CWIS Developer Documentation

- Home
- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

Resource.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: Resource.php
4 #
5 # Part of the Collection Workflow Integration System (CWIS)
6 # Copyright 2011-2016 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu/cwis/
8 #
9
13 class Resource extends Item
14 {
15
16 # ---- PUBLIC INTERFACE --------------------------------------------------
17
24 public function __construct($ResourceId)
25 {
26 # call parent contstructor to load info from DB
27 parent::__construct($ResourceId);
28
29 # load local attributes from database value cache
30 $this->CumulativeRating = $this->ValueCache["CumulativeRating"];
31
32 # load our local metadata schema
33 $this->SchemaId = $this->ValueCache["SchemaId"];
34 if (!isset(self::$Schemas[$this->SchemaId]))
35 {
36 self::$Schemas[$this->SchemaId] =
37 new MetadataSchema($this->SchemaId);
38 }
39 }
40
47 public static function Create($SchemaId)
48 {
49 # clean out any temp resource records more than three days old
50 $RFactory = new ResourceFactory();
51 $RFactory->CleanOutStaleTempItems(60 * 24 * 3);
52
53 # lock DB tables to prevent next ID from being grabbed
54 $DB = new Database;
55 $DB->Query("LOCK TABLES Resources WRITE");
56
57 # find next temp resource ID
58 $Id = $RFactory->GetNextTempItemId();
59
60 # write out new resource record with temp resource ID
61 # Set DateLastModified = NOW() to avoid being pruned as a
62 # stale temp resource.
63 $DB->Query(
64 "INSERT INTO Resources
65 SET `ResourceId` = '".intval($Id)."',
66 `SchemaId` = '".intval($SchemaId)."',
67 `DateLastModified` = NOW() " );
68
69 # release DB tables
70 $DB->Query("UNLOCK TABLES");
71
72 # create new Resource object
73 $Resource = new Resource($Id);
74
75 # set some additional fields for default resources
76 if ($SchemaId == MetadataSchema::SCHEMAID_DEFAULT)
77 {
78 $Resource->Set("Added By Id", $GLOBALS["G_User"]->Id());
79 $Resource->Set("Last Modified By Id", $GLOBALS["G_User"]->Id());
80 $Resource->Set("Date Of Record Creation", date("Y-m-d H:i:s"));
81 $Resource->Set("Date Last Modified", date("Y-m-d H:i:s"));
82 }
83
84 # for each field that can have a default value
85 $Schema = new MetadataSchema($SchemaId);
86 $Fields = $Schema->GetFields(MetadataSchema::MDFTYPE_OPTION
87 |MetadataSchema::MDFTYPE_TEXT
88 |MetadataSchema::MDFTYPE_FLAG
89 |MetadataSchema::MDFTYPE_NUMBER
90 |MetadataSchema::MDFTYPE_POINT);
91 foreach ($Fields as $Field)
92 {
93 # if there is a default value available
94 $DefaultValue = $Field->DefaultValue();
95 if ($DefaultValue !== NULL)
96 {
97 # if the default value is an array
98 if (is_array($DefaultValue))
99 {
100 # if there are values in the array
101 if (!empty($DefaultValue))
102 {
103 # flip values for Set() if necessary
104 if ($Field->Type() == MetadataSchema::MDFTYPE_OPTION)
105 {
106 $DefaultValue = array_flip($DefaultValue);
107 }
108
109 # set default value
110 $Resource->Set($Field, $DefaultValue);
111 }
112 }
113 else
114 {
115 # set default value
116 $Resource->Set($Field, $DefaultValue);
117 }
118 }
119 }
120
121 $Resource->UpdateAutoupdateFields(
122 MetadataField::UPDATEMETHOD_ONRECORDCREATE,
123 $GLOBALS["G_User"]);
124
125 # signal resource creation
126 $GLOBALS["AF"]->SignalEvent("EVENT_RESOURCE_CREATE", array(
127 "Resource" => $Resource,
128 ));
129
130 # return new Resource object to caller
131 return $Resource;
132 }
133
138 public function Delete()
139 {
140 global $SysConfig;
141
142 # signal that resource deletion is about to occur
143 global $AF;
144 $AF->SignalEvent("EVENT_RESOURCE_DELETE", array(
145 "Resource" => $this,
146 ));
147
148 # grab list of classifications
149 $Classifications = $this->Classifications();
150
151 # delete resource/classification intersections
152 $DB = $this->DB;
153 $DB->Query("DELETE FROM ResourceClassInts WHERE ResourceId = ".$this->Id());
154
155 # for each classification type
156 foreach ($Classifications as $ClassType => $ClassesOfType)
157 {
158 # for each classification of that type
159 foreach ($ClassesOfType as $ClassId => $ClassName)
160 {
161 # recalculate resource count for classification
162 $Class = new Classification($ClassId);
163 $Class->RecalcResourceCount();
164 }
165 }
166
167 # delete resource references
168 $DB->Query("
169 DELETE FROM ReferenceInts
170 WHERE SrcResourceId = '".addslashes($this->Id())."'
171 OR DstResourceId = '".addslashes($this->Id())."'");
172
173 # delete resource/name intersections
174 $DB->Query("DELETE FROM ResourceNameInts WHERE ResourceId = ".$this->Id());
175
176 # delete resource/user intersections
177 $DB->Query("DELETE FROM ResourceUserInts WHERE ResourceId = ".$this->Id());
178
179 # get the list of all images associated with this resource
180 $DB->Query("SELECT ImageId FROM ResourceImageInts"
181 ." WHERE ResourceId = ".intval($this->Id()));
182 $ImageIds = $DB->FetchColumn("ImageId");
183
184 # disassociate this resource from all images
185 $DB->Query("DELETE FROM ResourceImageInts"
186 ." WHERE ResourceId = ".intval($this->Id()));
187
188 # delete any images that no longer belong to any resources
189 foreach ($ImageIds as $ImageId)
190 {
191 $DB->Query("SELECT ResourceId FROM ResourceImageInts"
192 ." WHERE ImageId = ".intval($ImageId) );
193 if ($DB->NumRowsSelected() == 0)
194 {
195 $Image = new SPTImage($ImageId);
196 $Image->Delete();
197 }
198 }
199
200 # delete any associated files
201 $Factory = new FileFactory(NULL);
202 $Files = $Factory->GetFilesForResource($this->Id());
203 foreach ($Files as $File)
204 {
205 $File->Delete();
206 }
207
208 # delete resource record from database
209 $DB->Query("DELETE FROM Resources WHERE ResourceId = ".$this->Id());
210
211 # drop item from search engine and recommender system
212 if ($SysConfig->SearchDBEnabled())
213 {
214 $SearchEngine = new SPTSearchEngine();
215 $SearchEngine->DropItem($this->Id());
216 }
217 if ($SysConfig->RecommenderDBEnabled())
218 {
219 $Recommender = new SPTRecommender();
220 $Recommender->DropItem($this->Id());
221 }
222
223 # get the folders containing the resource
224 $FolderFactory = new FolderFactory();
225 $Folders = $FolderFactory->GetFoldersContainingItem(
226 $this->Id,
227 "Resource");
228
229 # drop the resource from each folder it belongs to
230 foreach ($Folders as $Folder)
231 {
232 # mixed item type folder
233 if ($Folder->ContainsItem($this->Id, "Resource"))
234 {
235 $Folder->RemoveItem($this->Id, "Resource");
236 }
237
238 # single item type folder
239 else
240 {
241 $Folder->RemoveItem($this->Id);
242 }
243 }
244
245 # delete any resource comments
246 $DB->Query("DELETE FROM Messages WHERE ParentId = ".$this->Id);
247 }
248
255 public function UpdateAutoupdateFields($UpdateType, $User=NULL)
256 {
257 # update all the timestamp fields as required
258 $TimestampFields = $this->Schema()->GetFields(
259 MetadataSchema::MDFTYPE_TIMESTAMP);
260 foreach ($TimestampFields as $Field)
261 {
262 if ($Field->UpdateMethod() == $UpdateType)
263 {
264 $this->Set($Field, "now");
265 }
266 }
267
268 # if a user was provided, update the user fields as well
269 if (!is_null($User))
270 {
271 $UserFields = $this->Schema()->GetFields(
272 MetadataSchema::MDFTYPE_USER);
273 foreach ($UserFields as $Field)
274 {
275 if ($Field->UpdateMethod() == $UpdateType)
276 {
277 $this->Set($Field, $User);
278 }
279 }
280 }
281 }
282
287 public function Id()
288 {
289 return $this->Id;
290 }
291
296 public function SchemaId()
297 {
298 return $this->SchemaId;
299 }
300
305 public function Schema()
306 {
307 return self::$Schemas[$this->SchemaId];
308 }
309
316 public function IsTempResource($NewSetting = NULL)
317 {
318 # if new temp resource setting supplied
319 if (!is_null($NewSetting))
320 {
321 # if caller requested to switch
322 $DB = $this->DB;
323 if ((($this->Id() < 0) && ($NewSetting == FALSE))
324 || (($this->Id() >= 0) && ($NewSetting == TRUE)))
325 {
326 # lock DB tables to prevent next ID from being grabbed
327 $DB->Query("LOCK TABLES Resources write");
328
329 # get next resource ID as appropriate
330 $OldResourceId = $this->Id;
331 $Factory = new ResourceFactory($this->SchemaId);
332 if ($NewSetting == TRUE)
333 {
334 $this->Id = $Factory->GetNextTempItemId();
335 }
336 else
337 {
338 $this->Id = $Factory->GetNextItemId();
339 }
340
341 # change resource ID
342 $DB->Query("UPDATE Resources SET ResourceId = ".
343 $this->Id. " WHERE ResourceId = ".$OldResourceId);
344
345 # release DB tables
346 $DB->Query("UNLOCK TABLES");
347
348 # change associations
349 unset($this->ClassificationCache);
350 $DB->Query("UPDATE ResourceClassInts SET ResourceId = ".
351 $this->Id. " WHERE ResourceId = ".$OldResourceId);
352 unset($this->ControlledNameCache);
353 unset($this->ControlledNameVariantCache);
354 $DB->Query("UPDATE ResourceNameInts SET ResourceId = ".
355 $this->Id. " WHERE ResourceId = ".$OldResourceId);
356 $DB->Query("UPDATE Files SET ResourceId = ".
357 $this->Id. " WHERE ResourceId = ".$OldResourceId);
358 $DB->Query("UPDATE ReferenceInts SET SrcResourceId = ".
359 $this->Id. " WHERE SrcResourceId = ".$OldResourceId);
360 $DB->Query("UPDATE ResourceImageInts SET ResourceId = ".
361 $this->Id. " WHERE ResourceId = ".$OldResourceId);
362 $DB->Query("UPDATE ResourceUserInts SET ResourceId = ".
363 $this->Id. " WHERE ResourceId = ".$OldResourceId);
364
365 # signal event as appropriate
366 if ($NewSetting === FALSE)
367 {
368 $GLOBALS["AF"]->SignalEvent("EVENT_RESOURCE_ADD", array(
369 "Resource" => $this,
370 ));
371 }
372 }
373 }
374
375 # report to caller whether we are a temp resource
376 return ($this->Id() < 0) ? TRUE : FALSE;
377 }
378
379
380 # --- Generic Attribute Retrieval Methods -------------------------------
381
386 public function GetViewPageUrl()
387 {
388 # put our Id into the ViewPage from our schema
389 $Url = str_replace(
390 "\$ID", $this->Id(),
391 $this->Schema()->ViewPage());
392
393 # return clean url, if one is available
394 return $GLOBALS["AF"]->GetCleanUrlForPath($Url);
395 }
396
410 public function Get($Field, $ReturnObject = FALSE, $IncludeVariants = FALSE)
411 {
412 # load field object if not already supplied
413 $Field = is_object($Field) ? $Field
414 : (is_numeric($Field) ? $this->Schema()->GetField($Field)
415 : $this->Schema()->GetFieldByName($Field));
416
417 # return no value found if we don't have a valid field
418 if (!($Field instanceof MetadataField)) { return NULL; }
419
420 if ($Field->SchemaId() != $this->SchemaId())
421 {
422 throw new Exception("Attempt to get a value for a field"
423 ." from a different schema."
424 ." (Field: ".$Field->Name()." [".$Field->Id()
425 ."], Field Schema: ".$Field->SchemaId()
426 .", Resource Schema: ".$this->SchemaId()
427 .")");
428 }
429
430 # grab database field name
431 $DBFieldName = $Field->DBFieldName();
432
433 # format return value based on field type
434 switch ($Field->Type())
435 {
436 case MetadataSchema::MDFTYPE_TEXT:
437 case MetadataSchema::MDFTYPE_PARAGRAPH:
438 case MetadataSchema::MDFTYPE_URL:
439 $ReturnValue = isset($this->ValueCache[$DBFieldName])
440 ? (string)$this->ValueCache[$DBFieldName] : NULL;
441 break;
442
443 case MetadataSchema::MDFTYPE_NUMBER:
444 $ReturnValue = isset($this->ValueCache[$DBFieldName])
445 ? (int)$this->ValueCache[$DBFieldName] : NULL;
446 break;
447
448 case MetadataSchema::MDFTYPE_FLAG:
449 $ReturnValue = isset($this->ValueCache[$DBFieldName])
450 ? (bool)$this->ValueCache[$DBFieldName] : NULL;
451 break;
452
453 case MetadataSchema::MDFTYPE_POINT:
454 $ReturnValue = array("X" => (float)$this->ValueCache[$DBFieldName."X"],
455 "Y" => (float)$this->ValueCache[$DBFieldName."Y"]);
456 break;
457
458 case MetadataSchema::MDFTYPE_DATE:
459 $Date = new Date($this->ValueCache[$DBFieldName."Begin"],
460 $this->ValueCache[$DBFieldName."End"],
461 $this->ValueCache[$DBFieldName."Precision"]);
462 if ($ReturnObject)
463 {
464 $ReturnValue = $Date;
465 }
466 else
467 {
468 $ReturnValue = $Date->Formatted();
469 }
470 break;
471
472 case MetadataSchema::MDFTYPE_TIMESTAMP:
473 $ReturnValue = $this->ValueCache[$DBFieldName];
474 break;
475
476 case MetadataSchema::MDFTYPE_TREE:
477 # start with empty array
478 $ReturnValue = array();
479
480 # if classification cache has not been loaded
481 if (!isset($this->ClassificationCache))
482 {
483 # load all classifications associated with this resource into cache
484 $this->ClassificationCache = array();
485 $this->DB->Query(
486 "SELECT Classifications.ClassificationId,"
487 ." Classifications.FieldId,ClassificationName"
488 ." FROM ResourceClassInts, Classifications"
489 ." WHERE ResourceClassInts.ResourceId = ".$this->Id
490 ." AND ResourceClassInts.ClassificationId"
491 ." = Classifications.ClassificationId");
492 while ($Record = $this->DB->FetchRow())
493 {
494 $ClassId = $Record["ClassificationId"];
495 $this->ClassificationCache[$ClassId]["Name"]
496 = $Record["ClassificationName"];
497 $this->ClassificationCache[$ClassId]["FieldId"]
498 = $Record["FieldId"];
499 }
500 }
501 # for each entry in classification cache
502 foreach ($this->ClassificationCache as
503 $ClassificationId => $ClassificationInfo)
504 {
505 # if classification ID matches field we are looking for
506 if ($ClassificationInfo["FieldId"] == $Field->Id())
507 {
508 # add field to result
509 if ($ReturnObject)
510 {
511 $ReturnValue[$ClassificationId] =
512 new Classification($ClassificationId);
513 }
514 else
515 {
516 $ReturnValue[$ClassificationId] = $ClassificationInfo["Name"];
517 }
518 }
519 }
520 break;
521
522 case MetadataSchema::MDFTYPE_CONTROLLEDNAME:
523 case MetadataSchema::MDFTYPE_OPTION:
524 # start with empty array
525 $ReturnValue = array();
526
527 # if controlled name cache has not been loaded
528 if (!isset($this->ControlledNameCache))
529 {
530 # load all controlled names associated with this resource into cache
531 $this->ControlledNameCache = array();
532 $this->DB->Query(
533 "SELECT ControlledNames.ControlledNameId,"
534 ." ControlledNames.FieldId,ControlledName"
535 ." FROM ResourceNameInts, ControlledNames"
536 ." WHERE ResourceNameInts.ResourceId = ".$this->Id
537 ." AND ResourceNameInts.ControlledNameId"
538 ." = ControlledNames.ControlledNameId"
539 ." ORDER BY ControlledNames.ControlledName ASC");
540 while ($Record = $this->DB->FetchRow())
541 {
542 $CNameId = $Record["ControlledNameId"];
543 $this->ControlledNameCache[$CNameId]["Name"]
544 = $Record["ControlledName"];
545 $this->ControlledNameCache[$CNameId]["FieldId"]
546 = $Record["FieldId"];
547 }
548 }
549
550 # if variant names requested and variant name cache has not been loaded
551 if ($IncludeVariants && !isset($this->ControlledNameVariantCache))
552 {
553 # load all controlled names associated with this resource into cache
554 $this->ControlledNameVariantCache = array();
555 $this->DB->Query("SELECT ControlledNames.ControlledNameId,"
556 ." ControlledNames.FieldId,"
557 ." ControlledName, VariantName"
558 ." FROM ResourceNameInts, ControlledNames, VariantNames"
559 ." WHERE ResourceNameInts.ResourceId = ".$this->Id
560 ." AND ResourceNameInts.ControlledNameId"
561 ." = ControlledNames.ControlledNameId"
562 ." AND VariantNames.ControlledNameId"
563 ." = ControlledNames.ControlledNameId");
564 while ($Record = $this->DB->FetchRow())
565 {
566 $this->ControlledNameVariantCache[$Record["ControlledNameId"]][]
567 = $Record["VariantName"];
568 }
569 }
570
571 # for each entry in controlled name cache
572 foreach ($this->ControlledNameCache as
573 $CNameId => $ControlledNameInfo)
574 {
575 # if controlled name type matches field we are looking for
576 if ($ControlledNameInfo["FieldId"] == $Field->Id())
577 {
578 # if objects requested
579 if ($ReturnObject)
580 {
581 $ReturnValue[$CNameId] =
582 new ControlledName($CNameId);
583 }
584 else
585 {
586 # if variant names requested
587 if ($IncludeVariants)
588 {
589 # add field to result
590 $ReturnValue[] = $ControlledNameInfo["Name"];
591
592 # add any variant names to result
593 if (isset($this->ControlledNameVariantCache[$CNameId]))
594 {
595 $ReturnValue = array_merge(
596 $ReturnValue,
597 $this->ControlledNameVariantCache[$CNameId]);
598 }
599 }
600 else
601 {
602 # add field with index to result
603 $ReturnValue[$CNameId] =
604 $ControlledNameInfo["Name"];
605 }
606 }
607 }
608 }
609 break;
610
611 case MetadataSchema::MDFTYPE_USER:
612 # start out assuming no associated users
613 $ReturnValue = array();
614
615 # query the database to get the associated userids
616 $this->DB->Query(
617 "SELECT UserId FROM ResourceUserInts WHERE ".
618 "ResourceId=".intval($this->Id).
619 " AND FieldId=".intval($Field->Id()));
620 $UserIds = $this->DB->FetchColumn("UserId");
621
622 # convert each userid to either a name or a CWUser object
623 foreach ($UserIds as $UserId)
624 {
625 $User = new CWUser(intval($UserId));
626 if ($ReturnObject)
627 {
628 $ReturnValue[$UserId] = $User;
629 }
630 else
631 {
632 $ReturnValue[$UserId] = $User->Get("UserName");
633 }
634 }
635 break;
636
637 case MetadataSchema::MDFTYPE_IMAGE:
638 # start out assuming no images will be found
639 $ReturnValue = array();
640
641 # find all images associated with this resource
642 $this->DB->Query("SELECT ImageId FROM ResourceImageInts"
643 ." WHERE ResourceId = ".intval($this->Id())
644 ." AND FieldId = ".intval($Field->Id()));
645
646 # if images were found
647 if ($this->DB->NumRowsSelected())
648 {
649 # if we are to return an object
650 $ImageIds = $this->DB->FetchColumn("ImageId");
651 if ($ReturnObject)
652 {
653 # load array of Image objects for return value
654 foreach ($ImageIds as $ImageId)
655 {
656 $ReturnValue[$ImageId] = new SPTImage($ImageId);
657 }
658 }
659 else
660 {
661 # load array of Image ids for return value
662 $ReturnValue = $ImageIds;
663 }
664 }
665 break;
666
667 case MetadataSchema::MDFTYPE_FILE:
668 # retrieve files using factory
669 $Factory = new FileFactory($Field->Id());
670 $ReturnValue = $Factory->GetFilesForResource(
671 $this->Id, $ReturnObject);
672 break;
673
674 case MetadataSchema::MDFTYPE_REFERENCE:
675 # query for resource references
676 $this->DB->Query("
677 SELECT * FROM ReferenceInts
678 WHERE FieldId = '".addslashes($Field->Id())."'
679 AND SrcResourceId = '".addslashes($this->Id())."'");
680
681 $ReturnValue = array();
682
683 # return each reference as a Resource object
684 if ($ReturnObject)
685 {
686 $FoundErrors = FALSE;
687
688 while (FALSE !== ($Record = $this->DB->FetchRow()))
689 {
690 $ReferenceId = $Record["DstResourceId"];
691 $Reference = new Resource($ReferenceId);
692 $ReturnValue[$ReferenceId] = $Reference;
693 }
694 }
695
696 # return each reference as a resource ID
697 else
698 {
699 while (FALSE !== ($Record = $this->DB->FetchRow()))
700 {
701 $ReferenceId = $Record["DstResourceId"];
702 $ReturnValue[$ReferenceId] = $ReferenceId;
703 }
704 }
705 break;
706
707 default:
708 # ERROR OUT
709 exit("<br>SPT - ERROR: attempt to retrieve "
710 ."unknown resource field type (".$Field->Type().")<br>\n");
711 break;
712 }
713
714 # return formatted value to caller
715 return $ReturnValue;
716 }
717
737 public function GetForDisplay(
738 $FieldNameOrObject, $ReturnObject = TRUE, $IncludeVariants = FALSE)
739 {
740 # normalize metadata field for use by any hooked code
741 $Field = is_object($FieldNameOrObject) ? $FieldNameOrObject
742 : $this->Schema()->GetFieldByName($FieldNameOrObject);
743
744 # retrieve value
745 $Value = $this->Get($Field, $ReturnObject, $IncludeVariants);
746
747 # signal event to allowed hooked code to modify value
748 $SignalResult = $GLOBALS["AF"]->SignalEvent(
749 "EVENT_FIELD_DISPLAY_FILTER", array(
750 "Field" => $Field,
751 "Resource" => $this,
752 "Value" => $Value));
753
754 # return possibly modified value to caller
755 return $SignalResult["Value"];
756 }
757
773 public function GetByField($FieldNameOrObject,
774 $ReturnObject = FALSE, $IncludeVariants = FALSE)
775 {
776 return $this->Get($FieldNameOrObject, $ReturnObject, $IncludeVariants);
777 }
778
793 public function GetByFieldId(
794 $FieldId, $ReturnObject = FALSE, $IncludeVariants = FALSE)
795 {
796 return $this->Get($FieldId, $ReturnObject, $IncludeVariants);
797 }
798
811 public function GetAsArray($IncludeDisabledFields = FALSE, $ReturnObjects = TRUE)
812 {
813 # retrieve field info
814 $Fields = $this->Schema()->GetFields();
815
816 # for each field
817 foreach ($Fields as $Field)
818 {
819 # if field is enabled or caller requested disabled fields
820 if ($Field->Enabled() || $IncludeDisabledFields)
821 {
822 # retrieve info and add it to the array
823 $FieldStrings[$Field->Name()] = $this->Get($Field, $ReturnObjects);
824
825 # if field uses qualifiers
826 if ($Field->UsesQualifiers())
827 {
828 # get qualifier attributes and add to the array
829 $FieldStrings[$Field->Name()." Qualifier"] =
830 $this->GetQualifierByField($Field, $ReturnObjects);
831 }
832 }
833 }
834
835 # add in internal values
836 $FieldStrings["ResourceId"] = $this->Id();
837 $FieldStrings["CumulativeRating"] = $this->CumulativeRating();
838
839 # return array to caller
840 return $FieldStrings;
841 }
842
857 public function GetMapped(
858 $MappedName, $ReturnObject = FALSE, $IncludeVariants = FALSE)
859 {
860 $Schema = $this->Schema();
861 $FieldId = $Schema->StdNameToFieldMapping($MappedName);
862 return $FieldId
863 ? $this->Get($FieldId, $ReturnObject, $IncludeVariants)
864 : NULL;
865 }
866
875 public function GetQualifier($FieldName, $ReturnObject = TRUE)
876 {
877 $Field = $this->Schema()->GetFieldByName($FieldName);
878 return $this->GetQualifierByField($Field, $ReturnObject);
879 }
880
889 public function GetQualifierByFieldId($FieldId, $ReturnObject = TRUE)
890 {
891 $Field = $this->Schema()->GetField($FieldId);
892 return ($Field) ? $this->GetQualifierByField($Field, $ReturnObject) : NULL;
893 }
894
903 public function GetQualifierByField($Field, $ReturnObject = TRUE)
904 {
905 # return NULL if field is invalid
906 if (!($Field instanceof MetadataField)) { return NULL; }
907
908 # assume no qualifiers if not otherwise determined
909 $ReturnValue = NULL;
910
911 # if field uses qualifiers
912 if ($Field->UsesQualifiers())
913 {
914 # retrieve qualifiers based on field type
915 switch ($Field->Type())
916 {
917 case MetadataSchema::MDFTYPE_TREE:
918 case MetadataSchema::MDFTYPE_CONTROLLEDNAME:
919 case MetadataSchema::MDFTYPE_OPTION:
920 # retrieve list of items
921 $Items = $this->Get($Field);
922
923 # if field uses item-level qualifiers
924 if ($Field->HasItemLevelQualifiers())
925 {
926 # determine general item name in DB
927 $TableName = ($Field->Type() == MetadataSchema::MDFTYPE_TREE)
928 ? "Classification" : "ControlledName";
929
930 # for each item
931 foreach ($Items as $ItemId => $ItemName)
932 {
933 # look up qualifier for item
934 $QualId = $this->DB->Query(
935 "SELECT * FROM ".$TableName."s"
936 ." WHERE ".$TableName."Id = ".$ItemId,
937 "QualifierId");
938
939
940 if ($QualId > 0)
941 {
942 # if object was requested by caller
943 if ($ReturnObject)
944 {
945 # load qualifier and add to return value array
946 $ReturnValue[$ItemId] = new Qualifier($QualId);
947 }
948 else
949 {
950 # add qualifier ID to return value array
951 $ReturnValue[$ItemId] = $QualId;
952 }
953 }
954 else
955 {
956 # add NULL to return value array for this item
957 $ReturnValue[$ItemId] = NULL;
958 }
959 }
960 }
961 else
962 {
963 # for each item
964 foreach ($Items as $ItemId => $ItemName)
965 {
966 # if object was requested by caller
967 if ($ReturnObject)
968 {
969 # load default qualifier and add to return value array
970 $ReturnValue[$ItemId] = new Qualifier(
971 $Field->DefaultQualifier());
972 }
973 else
974 {
975 # add default qualifier ID to return value array
976 $ReturnValue[$ItemId] = $Field->DefaultQualifier();
977 }
978 }
979 }
980 break;
981
982 default:
983 # if field uses item-level qualifiers
984 if ($Field->HasItemLevelQualifiers())
985 {
986 # if qualifier available
987 if ($this->ValueCache[$Field->DBFieldName()."Qualifier"] > 0)
988 {
989 # if object was requested by caller
990 $QFieldName = $Field->DBFieldName()."Qualifier";
991 if ($ReturnObject)
992 {
993 # return qualifier for field
994 $ReturnValue = new Qualifier(
995 $this->ValueCache[$QFieldName]);
996 }
997 else
998 {
999 # return qualifier ID for field
1000 $ReturnValue = $this->ValueCache[$QFieldName];
1001 }
1002 }
1003 }
1004 else
1005 {
1006 # if default qualifier available
1007 if ($Field->DefaultQualifier() > 0)
1008 {
1009 # if object was requested by caller
1010 if ($ReturnObject)
1011 {
1012 # return default qualifier
1013 $ReturnValue = new Qualifier($Field->DefaultQualifier());
1014 }
1015 else
1016 {
1017 # return default qualifier ID
1018 $ReturnValue = $Field->DefaultQualifier();
1019 }
1020 }
1021 }
1022 break;
1023 }
1024 }
1025
1026 # return qualifier object or ID (or array of same) to caller
1027 return $ReturnValue;
1028 }
1029
1037 public function FieldIsSet($FieldNameOrObject, $IgnorePadding=FALSE)
1038 {
1039 # load field object if needed
1040 $Field = is_object($FieldNameOrObject) ? $FieldNameOrObject
1041 : $this->Schema()->GetFieldByName($FieldNameOrObject);
1042
1043 # return no value found if we don't have a valid field
1044 if (!($Field instanceof MetadataField)) { return FALSE; }
1045
1046 # get the value
1047 $Value = $this->Get($Field);
1048
1049 # checks depend on the field type
1050 switch ($Field->Type())
1051 {
1052 case MetadataSchema::MDFTYPE_TEXT:
1053 case MetadataSchema::MDFTYPE_PARAGRAPH:
1054 case MetadataSchema::MDFTYPE_NUMBER:
1055 case MetadataSchema::MDFTYPE_URL:
1056 return isset($Value)
1057 && strlen($Value)
1058 && (!$IgnorePadding || ($IgnorePadding && strlen(trim($Value))));
1059
1060 case MetadataSchema::MDFTYPE_FLAG:
1061 return isset($Value)
1062 && strlen($Value);
1063
1064 case MetadataSchema::MDFTYPE_POINT:
1065 return isset($Value["X"])
1066 && isset($Value["Y"])
1067 && strlen(trim($Value["X"]))
1068 && strlen(trim($Value["Y"]));
1069
1070 case MetadataSchema::MDFTYPE_DATE:
1071 return isset($Value)
1072 && strlen(trim($Value))
1073 && $Value != "0000-00-00";
1074
1075 case MetadataSchema::MDFTYPE_TIMESTAMP:
1076 return isset($Value)
1077 && strlen(trim($Value))
1078 && $Value != "0000-00-00 00:00:00";
1079
1080 case MetadataSchema::MDFTYPE_TREE:
1081 case MetadataSchema::MDFTYPE_CONTROLLEDNAME:
1082 case MetadataSchema::MDFTYPE_OPTION:
1083 case MetadataSchema::MDFTYPE_FILE:
1084 case MetadataSchema::MDFTYPE_IMAGE:
1085 case MetadataSchema::MDFTYPE_REFERENCE:
1086 return count($Value) > 0;
1087
1088 case MetadataSchema::MDFTYPE_USER:
1089 $Factory = new CWUserFactory();
1090 return isset($Value)
1091 && strlen($Value)
1092 && $Factory->UserNameExists($Value);
1093
1094 default:
1095 return FALSE;
1096 }
1097 }
1098
1107 public function GetImageUrls($FieldNameOrObject, $ImageSize=SPTImage::SIZE_FULL)
1108 {
1109 $Result = array();
1110
1111 # get our target field and extract its values
1112 $Field = is_object($FieldNameOrObject) ? $FieldNameOrObject
1113 : $this->Schema()->GetField($FieldNameOrObject);
1114 $Images = $this->Get($Field, TRUE);
1115
1116 # iterate over our images getting URLs for each
1117 $Index = 0;
1118 foreach ($Images as $Image)
1119 {
1120 $Result[$Image->Id()] = $Image->GetImageUrlForResource(
1121 $this->Id(), $Field->Id(), $Index, $ImageSize);
1122 $Index++;
1123 }
1124
1125 return $Result;
1126 }
1127
1128 # --- Generic Attribute Setting Methods ---------------------------------
1129
1142 public function Set($Field, $NewValue, $Reset=FALSE)
1143 {
1144 # load field object if not already supplied
1145 $Field = is_object($Field) ? $Field
1146 : (is_numeric($Field) ? $this->Schema()->GetField($Field)
1147 : $this->Schema()->GetFieldByName($Field));
1148
1149 # return if we don't have a valid field
1150 if (!($Field instanceof MetadataField)) { return; }
1151
1152 if ($Field->SchemaId() != $this->SchemaId())
1153 {
1154 throw new Exception("Attempt to set a value for a field "
1155 ."from a different schema.");
1156 }
1157
1158 # grab commonly-used values for local use
1159 $DB = $this->DB;
1160 $ResourceId = $this->Id;
1161
1162 # grab database field name
1163 $DBFieldName = $Field->DBFieldName();
1164
1165 # Flag to deterimine if we've actually changed anything.
1166 $UpdateModTime = FALSE;
1167
1168 # store value in DB based on field type
1169 switch ($Field->Type())
1170 {
1171 case MetadataSchema::MDFTYPE_TEXT:
1172 case MetadataSchema::MDFTYPE_PARAGRAPH:
1173 case MetadataSchema::MDFTYPE_URL:
1174 if ($this->ValueCache[$DBFieldName] != $NewValue)
1175 {
1176 # save value directly to DB
1177 $DB->Query("UPDATE Resources SET `"
1178 .$DBFieldName."` = '".addslashes($NewValue)."' "
1179 ."WHERE ResourceId = ".$ResourceId);
1180
1181 # save value locally
1182 $this->ValueCache[$DBFieldName] = $NewValue;
1183 $UpdateModTime=TRUE;
1184 }
1185 break;
1186
1187 case MetadataSchema::MDFTYPE_NUMBER:
1188 if ( $this->ValueCache[$DBFieldName] != $NewValue )
1189 {
1190 # save value directly to DB
1191 if (is_null($NewValue))
1192 {
1193 $DB->Query("UPDATE Resources SET `"
1194 .$DBFieldName."` = NULL"
1195 ." WHERE ResourceId = ".$ResourceId);
1196 }
1197 else
1198 {
1199 $DB->Query("UPDATE Resources SET `"
1200 .$DBFieldName."` = ".intval($NewValue)
1201 ." WHERE ResourceId = ".$ResourceId);
1202 }
1203
1204 # save value locally
1205 $this->ValueCache[$DBFieldName] = $NewValue;
1206 $UpdateModTime = TRUE;
1207 }
1208 break;
1209
1210
1211 case MetadataSchema::MDFTYPE_POINT:
1212 if ($this->ValueCache[$DBFieldName."X"] != $NewValue["X"] ||
1213 $this->ValueCache[$DBFieldName."Y"] != $NewValue["Y"] )
1214 {
1215 if (is_null($NewValue))
1216 {
1217 $DB->Query("UPDATE Resources SET "
1218 ."`".$DBFieldName."X` = NULL, "
1219 ."`".$DBFieldName."Y` = NULL "
1220 ."WHERE ResourceId = ".$ResourceId);
1221 $this->ValueCache[$DBFieldName."X"] = NULL;
1222 $this->ValueCache[$DBFieldName."Y"] = NULL;
1223 }
1224 else
1225 {
1226 $DB->Query("UPDATE Resources SET "
1227 ."`".$DBFieldName."X` = " .(strlen($NewValue["X"])
1228 ? "'".$NewValue["X"]."'" : "NULL").", "
1229 ."`".$DBFieldName."Y` = ".(strlen($NewValue["Y"])
1230 ? "'".$NewValue["Y"]."'" : "NULL")
1231 ." WHERE ResourceId = ".$ResourceId);
1232
1233 $Digits = $Field->PointDecimalDigits();
1234
1235 $this->ValueCache[$DBFieldName."X"] =
1236 strlen($NewValue["X"]) ?
1237 round($NewValue["X"], $Digits) : NULL;
1238 $this->ValueCache[$DBFieldName."Y"] =
1239 strlen($NewValue["Y"]) ?
1240 round($NewValue["Y"], $Digits) : NULL;
1241 }
1242 $UpdateModTime = TRUE;
1243 }
1244 break;
1245
1246 case MetadataSchema::MDFTYPE_FLAG:
1247 if ($this->ValueCache[$DBFieldName] != $NewValue)
1248 {
1249 # save value directly to DB
1250 if (is_null($NewValue))
1251 {
1252 $DB->Query("UPDATE Resources SET `"
1253 .$DBFieldName."` = NULL"
1254 ." WHERE ResourceId = ".$ResourceId);
1255 }
1256 else
1257 {
1258 $NewValue = $NewValue ? "1" : "0";
1259 $DB->Query("UPDATE Resources SET `"
1260 .$DBFieldName."` = ".$NewValue
1261 ." WHERE ResourceId = ".$ResourceId);
1262 }
1263
1264 $this->ValueCache[$DBFieldName] = $NewValue;
1265
1266 # recalculate counts for any associated classifications if necessary
1267 if ($DBFieldName == "ReleaseFlag")
1268 {
1269 $DB->Query("SELECT ClassificationId FROM ResourceClassInts"
1270 ." WHERE ResourceId = ".$ResourceId);
1271 while ($ClassId = $DB->FetchField("ClassificationId"))
1272 {
1273 $Class = new Classification($ClassId);
1274 $Class->RecalcResourceCount();
1275 }
1276 }
1277 $UpdateModTime = TRUE;
1278 }
1279 break;
1280
1281 case MetadataSchema::MDFTYPE_USER:
1282 $OldValue = $this->Get($Field);
1283 # value comes back as array (UserId => UserName), just get the Ids
1284 $OldValue = array_keys($OldValue);
1285
1286 # input to Set() for these fields is one of
1287 # 1. an int specifying a UserId
1288 if (is_numeric($NewValue))
1289 {
1290 $NewValue = array($NewValue);
1291 }
1292 # 2. a CWUser object
1293 elseif ($NewValue instanceof CWUser)
1294 {
1295 $NewValue = array($NewValue->Id());
1296 }
1297 # 3. an array keyed by UserId (don't care about the values)
1298 elseif (is_array($NewValue))
1299 {
1300 $NewValue = array_keys($NewValue);
1301 }
1302 else
1303 {
1304 throw new Exception("Unknown format for NewValue in a User field");
1305 }
1306
1307 # if this is a unique field, only accept the first of the options given
1308 if ($Field->AllowMultiple() == FALSE && count($NewValue) > 1)
1309 {
1310 $NewValue = array_slice($NewValue, 0, 1, TRUE);
1311 }
1312
1313 # sort new and old values so we can directly compare
1314 sort($OldValue);
1315 sort($NewValue);
1316
1317 # if the value has changed
1318 if ($OldValue != $NewValue)
1319 {
1320 if ($Reset || $Field->AllowMultiple() == FALSE )
1321 {
1322 $ToRemove = array_diff($OldValue, $NewValue);
1323 $this->RemoveAssociation(
1324 "ResourceUserInts", "UserId", $ToRemove, $Field);
1325 }
1326
1327 # associate with resource if not already associated
1328 $this->AddAssociation("ResourceUserInts",
1329 "UserId",
1330 $NewValue, $Field);
1331
1332 $UpdateModTime=TRUE;
1333 }
1334 break;
1335
1336 case MetadataSchema::MDFTYPE_DATE:
1337 # if we were given a date object
1338 if (is_object($NewValue))
1339 {
1340 # use supplied date object
1341 $Date = $NewValue;
1342 }
1343 else
1344 {
1345 # create date object
1346 $Date = new Date($NewValue);
1347 }
1348
1349 $OldDate = new Date(
1350 $this->ValueCache[$DBFieldName."Begin"],
1351 $this->ValueCache[$DBFieldName."End"]);
1352
1353 if ($OldDate->BeginDate() != $Date->BeginDate() ||
1354 $OldDate->EndDate() != $Date->EndDate() ||
1355 $OldDate->Precision() != $Date->Precision() )
1356 {
1357 # extract values from date object and store in DB
1358 $BeginDate = "'".$Date->BeginDate()."'";
1359 if (strlen($BeginDate) < 3) { $BeginDate = "NULL"; }
1360 $EndDate = "'".$Date->EndDate()."'";
1361 if (strlen($EndDate) < 3) { $EndDate = "NULL"; }
1362
1363 $DB->Query("UPDATE Resources SET "
1364 .$DBFieldName."Begin = ".$BeginDate.", "
1365 .$DBFieldName."End = ".$EndDate.", "
1366 .$DBFieldName."Precision = '".$Date->Precision()."' "
1367 ."WHERE ResourceId = ".$ResourceId);
1368
1369 # save values locally
1370 $this->ValueCache[$DBFieldName."Begin"] = $Date->BeginDate();
1371 $this->ValueCache[$DBFieldName."End"] = $Date->EndDate();
1372 $this->ValueCache[$DBFieldName."Precision"] = $Date->Precision();
1373 $UpdateModTime=TRUE;
1374 }
1375 break;
1376
1377 case MetadataSchema::MDFTYPE_TIMESTAMP:
1378 if (is_null($NewValue) || !strlen(trim($NewValue)))
1379 {
1380 $DateValue = $NewValue;
1381
1382 if (!is_null($this->ValueCache[$DBFieldName]))
1383 {
1384 # save value directly to DB
1385 $DB->Query("UPDATE Resources SET "
1386 ."`".$DBFieldName."` = NULL "
1387 ."WHERE ResourceId = ".$ResourceId);
1388 $UpdateModTime = TRUE;
1389 }
1390 }
1391 else
1392 {
1393 # assume value is date and use directly
1394 $TimestampValue = strtotime($NewValue);
1395
1396 # use the new value if the date is valid
1397 if ($TimestampValue !== FALSE && $TimestampValue >= 0)
1398 {
1399 $DateValue = date("Y-m-d H:i:s", $TimestampValue);
1400
1401 if ($this->ValueCache[$DBFieldName] != $DateValue)
1402 {
1403 # save value directly to DB
1404 $DB->Query("UPDATE Resources SET "
1405 ."`".$DBFieldName."` = '".addslashes($DateValue)."' "
1406 ."WHERE ResourceId = ".$ResourceId);
1407 $UpdateModTime=TRUE;
1408 }
1409 }
1410
1411 # continue using the old value if invalid
1412 else
1413 {
1414 $DateValue = $this->Get($Field);
1415 }
1416 }
1417
1418 # save value locally
1419 $this->ValueCache[$DBFieldName] = $DateValue;
1420 break;
1421
1422 case MetadataSchema::MDFTYPE_TREE:
1423 $OldValue = $this->Get($Field);
1424
1425 # if incoming value is array
1426 if (is_array($NewValue))
1427 {
1428 if ($OldValue != $NewValue)
1429 {
1430 if ($Reset)
1431 {
1432 # remove values that were in the old value
1433 # but not the new one
1434 $ToRemove = array_diff(array_keys($OldValue),
1435 array_keys($NewValue));
1436 foreach ($ToRemove as $ClassificationId)
1437 {
1438 $this->RemoveAssociation("ResourceClassInts",
1439 "ClassificationId",
1440 $ClassificationId);
1441 $Class = new Classification($ClassificationId);
1442 $Class->RecalcResourceCount();
1443 }
1444 }
1445
1446 # for each element of array
1447 foreach ($NewValue as
1448 $ClassificationId => $ClassificationName)
1449 {
1450 $Class = new Classification($ClassificationId);
1451 if ($Class->FieldId() == $Field->Id())
1452 {
1453 # associate with resource if not already associated
1454 if ($this->AddAssociation("ResourceClassInts",
1455 "ClassificationId", $ClassificationId))
1456 {
1457 $Class->UpdateLastAssigned();
1458 $Class->RecalcResourceCount();
1459 }
1460 }
1461 else
1462 {
1463 throw new Exception(
1464 "Attempting to store classification from "
1465 ."Field ".$Class->FieldId()." into Field "
1466 .$Field->Id() );
1467 }
1468
1469 }
1470
1471 $UpdateModTime=TRUE;
1472 }
1473 }
1474 else
1475 {
1476 # associate with resource if not already associated
1477 if (is_object($NewValue))
1478 {
1479 $Class = $NewValue;
1480 $NewValue = $Class->Id();
1481 }
1482 else
1483 {
1484 $Class = new Classification($NewValue);
1485 }
1486
1487 if (!array_key_exists($Class->Id(), $OldValue))
1488 {
1489
1490 $this->AddAssociation("ResourceClassInts",
1491 "ClassificationId",
1492 $NewValue);
1493 $Class->UpdateLastAssigned();
1494 $Class->RecalcResourceCount();
1495 $UpdateModTime=TRUE;
1496 }
1497 }
1498
1499 # clear our classification cache
1500 if ($UpdateModTime)
1501 {
1502 unset($this->ClassificationCache);
1503 }
1504 break;
1505
1506 case MetadataSchema::MDFTYPE_CONTROLLEDNAME:
1507 case MetadataSchema::MDFTYPE_OPTION:
1508 $OldValue = $this->Get($Field);
1509
1510 # input to Set() for these fields is one of
1511 # 1. an int specifying a ControlledNameId
1512 # 2. a ControlledName object
1513 # 3. an array with keys giving Ids and values giving ControlledNames
1514 #
1515 # normalize 1 and 2 into 3 for simplicity of processing
1516 if (is_object($NewValue) || !is_array($NewValue) )
1517 {
1518 if (!is_object($NewValue))
1519 {
1520 $NewValue = new ControlledName($NewValue);
1521 }
1522
1523 $TmpValue = array();
1524 $TmpValue[$NewValue->Id()] = $NewValue->Name();
1525
1526 $NewValue = $TmpValue;
1527 }
1528
1529 # if this is a unique field, only accept the first of the options given
1530 # NB: all ControlledNames implicitly AllowMultiple
1531 if ($Field->Type() == MetadataSchema::MDFTYPE_OPTION &&
1532 $Field->AllowMultiple() == FALSE && count($NewValue) > 1)
1533 {
1534 $NewValue = array_slice($NewValue, 0, 1, TRUE);
1535 }
1536
1537 # if the value has changed
1538 if ($OldValue != $NewValue)
1539 {
1540 if ($Reset || ($Field->Type() == MetadataSchema::MDFTYPE_OPTION
1541 && $Field->AllowMultiple() == FALSE ) )
1542 {
1543 $ToRemove = array_diff(array_keys($OldValue),
1544 array_keys($NewValue));
1545 foreach ($ToRemove as $CNId)
1546 {
1547 $this->RemoveAssociation("ResourceNameInts",
1548 "ControlledNameId",
1549 $CNId);
1550 }
1551 }
1552
1553 # for each element of array
1554 foreach ($NewValue as $ControlledNameId => $ControlledName)
1555 {
1556 # associate with resource if not already associated
1557 if ($this->AddAssociation("ResourceNameInts",
1558 "ControlledNameId",
1559 $ControlledNameId))
1560 {
1561 $CN = new ControlledName($ControlledNameId);
1562 if ($CN->Status() != ControlledName::STATUS_OK)
1563 {
1564 $this->RemoveAssociation("ResourceNameInts",
1565 "ControlledNameId", $ControlledNameId);
1566 throw new InvalidArgumentException(
1567 "Attempt to set controlled name with"
1568 ." invalid ID (".$ControlledNameId.").");
1569 }
1570 $CN->UpdateLastAssigned();
1571 }
1572 }
1573 $UpdateModTime = TRUE;
1574 }
1575
1576 if ($UpdateModTime)
1577 {
1578 # clear our controlled name cache
1579 unset($this->ControlledNameCache);
1580 unset($this->ControlledNameVariantCache);
1581
1582 # clear visible count cache for any affected resources
1583 $ValueIds = array_keys($OldValue) + array_keys($NewValue);
1584 $DB->Query(
1585 "DELETE FROM VisibleResourceCounts WHERE "
1586 ."SchemaId=".intval($this->SchemaId)." AND "
1587 ."ValueId IN (".implode(",", $ValueIds).")");
1588 }
1589
1590 break;
1591
1592 case MetadataSchema::MDFTYPE_IMAGE:
1593 # associate value(s) with resource
1594 $this->AddAssociation(
1595 "ResourceImageInts", "ImageId", $NewValue, $Field);
1596 # clear cached image mappings
1597 SPTImage::ClearImageSymlinksForResource($this->Id(), $Field->Id());
1598 break;
1599
1600 case MetadataSchema::MDFTYPE_FILE:
1601 # convert incoming value to array if necessary
1602 if (!is_array($NewValue)) { $NewValue = array($NewValue); }
1603
1604 # for each incoming file
1605 $Factory = new FileFactory($Field->Id());
1606 foreach ($NewValue as $File)
1607 {
1608 # make copy of file
1609 $NewFile = $Factory->Copy($File);
1610
1611 # associate copy with this resource and field
1612 $NewFile->ResourceId($this->Id);
1613 $NewFile->FieldId($Field->Id());
1614 }
1615 # Since we make a fresh copy of the File whenever Set is called,
1616 # we'll always update the modification time for this field.
1617 $UpdateModTime = TRUE;
1618 break;
1619
1620 case MetadataSchema::MDFTYPE_REFERENCE:
1621 # convert incoming value to array to simplify the workflow
1622 if (is_scalar($NewValue) || $NewValue instanceof Resource)
1623 {
1624 $NewValue = array($NewValue);
1625 }
1626
1627 # delete existing resource references
1628 $this->ClearByField($Field);
1629
1630 # add each reference
1631 foreach ($NewValue as $ReferenceOrId)
1632 {
1633 # initially issume it's a reference ID and not an object...
1634 $ReferenceId = $ReferenceOrId;
1635
1636 # ... but get the reference ID if it's an object
1637 if ($ReferenceOrId instanceof Resource)
1638 {
1639 $ReferenceId = $ReferenceOrId->Id();
1640 }
1641
1642 # skip blank reference IDs
1643 if (strlen(trim($ReferenceId)) < 1)
1644 {
1645 continue;
1646 }
1647
1648 # skip reference IDs that don't look right
1649 if (!is_numeric($ReferenceId))
1650 {
1651 continue;
1652 }
1653
1654 # skip references to the current resource
1655 if ($ReferenceId == $this->Id())
1656 {
1657 continue;
1658 }
1659
1660 # add the reference to the references table
1661 $DB->Query("
1662 INSERT INTO ReferenceInts (
1663 FieldId,
1664 SrcResourceId,
1665 DstResourceId)
1666 VALUES (
1667 ".addslashes($Field->Id()).",
1668 ".addslashes($this->Id()).",
1669 ".addslashes($ReferenceId).")");
1670 }
1671 break;
1672
1673 default:
1674 # ERROR OUT
1675 exit("<br>SPT - ERROR: attempt to set unknown resource field type<br>\n");
1676 break;
1677 }
1678
1679 if ($UpdateModTime && !$this->IsTempResource())
1680 {
1681 # update modification timestamps
1682 global $G_User;
1683 $UserId = $G_User->IsLoggedIn() ? $G_User->Get("UserId") : -1;
1684 $DB->Query("DELETE FROM ResourceFieldTimestamps "
1685 ."WHERE ResourceId=".$this->Id." AND "
1686 ."FieldId=".$Field->Id() );
1687 $DB->Query("INSERT INTO ResourceFieldTimestamps "
1688 ."(ResourceId,FieldId,ModifiedBy,Timestamp) VALUES ("
1689 .$this->Id.",".$Field->Id().","
1690 .$UserId.",NOW())");
1691
1692 # on resource modification, clear the UserPermsCache entry
1693 # so that stale permissions checks are not cached
1694 $DB->Query("DELETE FROM UserPermsCache WHERE ResourceId=".$this->Id);
1695 }
1696 }
1697
1705 public function SetByField($Field, $NewValue)
1706 {
1707 $this->Set($Field, $NewValue);
1708 }
1709
1717 public function SetByFieldId($FieldId, $NewValue)
1718 {
1719 $this->Set($FieldId, $NewValue);
1720 }
1721
1727 public function SetQualifier($FieldName, $NewValue)
1728 {
1729 $Field = $this->Schema()->GetFieldByName($FieldName);
1730 $this->SetQualifierByField($Field, $NewValue);
1731 }
1732
1738 public function SetQualifierByFieldId($FieldId, $NewValue)
1739 {
1740 $Field = $this->Schema()->GetField($FieldId);
1741 $this->SetQualifierByField($Field, $NewValue);
1742 }
1743
1749 public function SetQualifierByField($Field, $NewValue)
1750 {
1751 # if field uses qualifiers and uses item-level qualifiers
1752 if ($Field->UsesQualifiers() && $Field->HasItemLevelQualifiers())
1753 {
1754 # if qualifier object passed in
1755 if (is_object($NewValue))
1756 {
1757 # grab qualifier ID from object
1758 $QualifierId = $NewValue->Id();
1759 }
1760 else
1761 {
1762 # assume value passed in is qualifier ID
1763 $QualifierId = $NewValue;
1764 }
1765
1766 # update qualifier value in database
1767 $DBFieldName = $Field->DBFieldName();
1768 $this->DB->Query("UPDATE Resources SET "
1769 .$DBFieldName."Qualifier = '".$QualifierId."' "
1770 ."WHERE ResourceId = ".$this->Id);
1771
1772 # update local qualifier value
1773 $this->ValueCache[$DBFieldName."Qualifier"] = $QualifierId;
1774 }
1775 }
1776
1783 public function ClearByFieldId($FieldId, $ValueToClear = NULL)
1784 {
1785 $Field = $this->Schema()->GetField($FieldId);
1786 $this->Clear($Field, $ValueToClear);
1787 }
1788
1795 public function Clear($Field, $ValueToClear = NULL)
1796 {
1797 # convert field name to object if necessary
1798 if (!is_object($Field))
1799 {
1800 $Field = $this->Schema()->GetFieldByName($Field);
1801 }
1802
1803 # grab commonly-used values for local use
1804 $DB = $this->DB;
1805 $ResourceId = $this->Id;
1806
1807 # grab database field name
1808 $DBFieldName = $Field->DBFieldName();
1809
1810 $UpdateModTime=FALSE;
1811
1812 # store value in DB based on field type
1813 switch ($Field->Type())
1814 {
1815 case MetadataSchema::MDFTYPE_TEXT:
1816 case MetadataSchema::MDFTYPE_PARAGRAPH:
1817 case MetadataSchema::MDFTYPE_NUMBER:
1818 case MetadataSchema::MDFTYPE_FLAG:
1819 case MetadataSchema::MDFTYPE_TIMESTAMP:
1820 case MetadataSchema::MDFTYPE_URL:
1821 if (strlen($this->ValueCache[$DBFieldName])>0)
1822 {
1823 # clear value in DB
1824 $DB->Query("UPDATE Resources SET `"
1825 .$DBFieldName."` = NULL "
1826 ."WHERE ResourceId = ".$ResourceId);
1827
1828 # clear value locally
1829 $this->ValueCache[$DBFieldName] = NULL;
1830 $UpdateModTime=TRUE;
1831 }
1832 break;
1833
1834 case MetadataSchema::MDFTYPE_POINT:
1835 if (!is_null($this->ValueCache[$DBFieldName."X"]) ||
1836 !is_null($this->ValueCache[$DBFieldName."Y"]) )
1837 {
1838 # Clear DB Values
1839 $DB->Query("UPDATE Resources SET "
1840 ."`".$DBFieldName."X` = NULL ,"
1841 ."`".$DBFieldName."Y` = NULL "
1842 ."WHERE ResourceId = ".$ResourceId);
1843
1844 # Clear local values
1845 $this->ValueCache[$DBFieldName."X"] = NULL;
1846 $this->ValueCache[$DBFieldName."Y"] = NULL;
1847 $UpdateModTime=TRUE;
1848 }
1849 break;
1850
1851 case MetadataSchema::MDFTYPE_DATE:
1852 if (!is_null($this->ValueCache[$DBFieldName."Begin"]) ||
1853 !is_null($this->ValueCache[$DBFieldName."End"]) ||
1854 !is_null($this->ValueCache[$DBFieldName."Precision"]))
1855 {
1856 # clear date object values in DB
1857 $DB->Query("UPDATE Resources SET "
1858 .$DBFieldName."Begin = '', "
1859 .$DBFieldName."End = '', "
1860 .$DBFieldName."Precision = '' "
1861 ."WHERE ResourceId = ".$ResourceId);
1862
1863 # clear value locally
1864 $this->ValueCache[$DBFieldName."Begin"] = NULL;
1865 $this->ValueCache[$DBFieldName."End"] = NULL;
1866 $this->ValueCache[$DBFieldName."Precision"] = NULL;
1867 $UpdateModTime=TRUE;
1868 }
1869 break;
1870
1871 case MetadataSchema::MDFTYPE_TREE:
1872 $OldValue = $this->Get($Field);
1873
1874 # if value to clear supplied
1875 if ($ValueToClear !== NULL)
1876 {
1877 # if supplied value is array
1878 if (is_array($ValueToClear))
1879 {
1880 # for each element of array
1881 foreach ($ValueToClear as $ClassificationId => $Dummy)
1882 {
1883 if (array_key_exists($ClassificationId, $OldValue))
1884 {
1885 # remove association with resource (if any)
1886 $this->RemoveAssociation("ResourceClassInts",
1887 "ClassificationId",
1888 $ClassificationId);
1889 $Class = new Classification($ClassificationId);
1890 $Class->RecalcResourceCount();
1891 $UpdateModTime=TRUE;
1892 }
1893 }
1894 }
1895 else
1896 {
1897 if (array_key_exists($ValueToClear, $OldValue))
1898 {
1899 # remove association with resource (if any)
1900 $this->RemoveAssociation("ResourceClassInts",
1901 "ClassificationId",
1902 $ValueToClear);
1903 $Class = new Classification($ValueToClear);
1904 $Class->RecalcResourceCount();
1905 $UpdateModTime=TRUE;
1906 }
1907 }
1908 }
1909 else
1910 {
1911 if (count($OldValue)>0)
1912 {
1913 # remove all associations for resource and field
1914 $this->RemoveAllAssociations(
1915 "ResourceClassInts", "ClassificationId", $Field);
1916
1917 # recompute resource count
1918 $Values = $this->Get($Field);
1919 foreach ($Values as $ClassificationId => $Dummy)
1920 {
1921 $Class = new Classification($ClassificationId);
1922 $Class->RecalcResourceCount();
1923 }
1924 $UpdateModTime=TRUE;
1925 }
1926 }
1927
1928 # clear our classification cache
1929 if ($UpdateModTime)
1930 {
1931 unset($this->ClassificationCache);
1932 }
1933 break;
1934
1935 case MetadataSchema::MDFTYPE_CONTROLLEDNAME:
1936 case MetadataSchema::MDFTYPE_OPTION:
1937 $OldValue = $this->Get($Field);
1938 # if value to clear supplied
1939 if ($ValueToClear !== NULL)
1940 {
1941 # if incoming value is array
1942 if (is_array($ValueToClear))
1943 {
1944 # for each element of array
1945 foreach ($ValueToClear as $ControlledNameId =>
1946 $ControlledName)
1947 {
1948 if (array_key_exists($ControlledNameId, $OldValue))
1949 {
1950 # remove association with resource (if any)
1951 $this->RemoveAssociation("ResourceNameInts",
1952 "ControlledNameId",
1953 $ControlledNameId);
1954 $UpdateModTime=TRUE;
1955 }
1956 }
1957 }
1958 else
1959 {
1960 if (array_key_exists($ValueToClear, $OldValue))
1961 {
1962 # remove association with resource (if any)
1963 $this->RemoveAssociation("ResourceNameInts",
1964 "ControlledNameId",
1965 $ValueToClear);
1966 $UpdateModTime=TRUE;
1967 }
1968 }
1969 }
1970 else
1971 {
1972 if (count($OldValue)>0)
1973 {
1974 # remove all associations for resource and field
1975 $this->RemoveAllAssociations(
1976 "ResourceNameInts", "ControlledNameId", $Field);
1977 $UpdateModTime=TRUE;
1978 }
1979 }
1980
1981 if ($UpdateModTime)
1982 {
1983 # clear our controlled name cache
1984 unset($this->ControlledNameCache);
1985 unset($this->ControlledNameVariantCache);
1986 }
1987 break;
1988
1989 case MetadataSchema::MDFTYPE_USER:
1990 $OldValue = $this->Get($Field);
1991
1992 # if value to clear supplied
1993 if ($ValueToClear !== NULL)
1994 {
1995 # if incoming value is array
1996 if (is_array($ValueToClear))
1997 {
1998 # for each element of array
1999 foreach ($ValueToClear as $UserId => $User)
2000 {
2001 if (array_key_exists($UserId, $OldValue))
2002 {
2003 # remove association with resource (if any)
2004 $this->RemoveAssociation("ResourceUserInts",
2005 "UserId",
2006 $UserId,
2007 $Field);
2008 $UpdateModTime=TRUE;
2009 }
2010 }
2011 }
2012 else
2013 {
2014 if (array_key_exists($ValueToClear, $OldValue))
2015 {
2016 # remove association with resource (if any)
2017 $this->RemoveAssociation("ResourceUserInts",
2018 "UserId",
2019 $UserId,
2020 $Field);
2021 $UpdateModTime=TRUE;
2022 }
2023 }
2024 }
2025 else
2026 {
2027 if (count($OldValue)>0)
2028 {
2029 # remove all associations for resource and field
2030 $this->RemoveAllAssociations(
2031 "ResourceUserInts", "UserId", $Field);
2032 $UpdateModTime=TRUE;
2033 }
2034 }
2035
2036 break;
2037
2038 case MetadataSchema::MDFTYPE_FILE:
2039 # if value to clear supplied
2040 if ($ValueToClear !== NULL)
2041 {
2042 # convert value to array if necessary
2043 $Files = $ValueToClear;
2044 if (!is_array($Files)) { $Files = array($Files); }
2045
2046 # convert values to objects if necessary
2047 foreach ($Files as $Index => $File)
2048 {
2049 if (!is_object($File))
2050 {
2051 $Files[$Index] = new File($File);
2052 }
2053 }
2054 }
2055 else
2056 {
2057 # use all files associated with resource
2058 $Files = $this->Get($Field, TRUE);
2059 }
2060
2061 # delete files
2062 foreach ($Files as $File) { $File->Delete(); }
2063 break;
2064
2065 case MetadataSchema::MDFTYPE_IMAGE:
2066 # if value to clear supplied
2067 if ($ValueToClear !== NULL)
2068 {
2069 # convert value to array if necessary
2070 $Images = $ValueToClear;
2071 if (!is_array($Images)) { $Images = array($Images); }
2072
2073 # convert values to objects if necessary
2074 foreach ($Images as $Index => $Image)
2075 {
2076 if (!is_object($Image))
2077 {
2078 $Images[$Index] = new SPTImage($Image);
2079 }
2080 }
2081 }
2082 else
2083 {
2084 # use all images associated with resource
2085 $Images = $this->Get($Field, TRUE);
2086 }
2087
2088 # delete images if we are the last resource referencing
2089 # a particular image.
2090 foreach ($Images as $Image)
2091 {
2092 $Cnt = $this->DB->Query(
2093 "SELECT COUNT(*) AS Cnt FROM ResourceImageInts WHERE ".
2094 "ImageId=".$Image->Id(), "Cnt");
2095 if ($Cnt==1)
2096 {
2097 $Image->Delete();
2098 }
2099 }
2100
2101 # clear cached image mappings
2102 SPTImage::ClearImageSymlinksForResource($this->Id(), $Field->Id());
2103
2104 # remove connections to images
2105 $UpdateModTime = $this->RemoveAssociation(
2106 "ResourceImageInts", "ImageId", $Images, $Field);
2107 break;
2108
2109 case MetadataSchema::MDFTYPE_REFERENCE:
2110 # remove references from the references table
2111 $DB->Query("
2112 DELETE FROM ReferenceInts
2113 WHERE FieldId = '".addslashes($Field->Id())."'
2114 AND SrcResourceId = '".addslashes($this->Id())."'");
2115 break;
2116
2117 default:
2118 # ERROR OUT
2119 exit("<br>SPT - ERROR: attempt to clear "
2120 ."unknown resource field type<br>\n");
2121 break;
2122 }
2123
2124 if ($UpdateModTime && !$this->IsTempResource())
2125 {
2126 # update modification timestamps
2127 global $G_User;
2128 $UserId = $G_User->IsLoggedIn() ? $G_User->Get("UserId") : -1;
2129 $DB->Query("DELETE FROM ResourceFieldTimestamps "
2130 ."WHERE ResourceId=".$this->Id." AND "
2131 ."FieldId=".$Field->Id() );
2132 $DB->Query("INSERT INTO ResourceFieldTimestamps "
2133 ."(ResourceId,FieldId,ModifiedBy,Timestamp) VALUES ("
2134 .$this->Id.",".$Field->Id().","
2135 .$UserId.",NOW())");
2136 }
2137 }
2138
2147 public function ClearByField($Field, $ValueToClear = NULL)
2148 {
2149 $this->Clear($Field, $ValueToClear);
2150 }
2151
2152 # --- Field-Specific or Type-Specific Attribute Retrieval Methods -------
2153
2159 public function Classifications()
2160 {
2161 $DB = $this->DB;
2162
2163 # start with empty array
2164 $Names = array();
2165
2166 # for each controlled name
2167 $DB->Query("SELECT ClassificationName, MetadataFields.FieldName, "
2168 ."ResourceClassInts.ClassificationId FROM ResourceClassInts, "
2169 ."Classifications, MetadataFields "
2170 ."WHERE ResourceClassInts.ResourceId = ".$this->Id." "
2171 ."AND ResourceClassInts.ClassificationId = "
2172 ."Classifications.ClassificationId "
2173 ."AND Classifications.FieldId = MetadataFields.FieldId ");
2174 while ($Record = $DB->FetchRow())
2175 {
2176 # add name to array
2177 $Names[$Record["FieldName"]][$Record["ClassificationId"]] =
2178 $Record["ClassificationName"];
2179 }
2180
2181 # return array to caller
2182 return $Names;
2183 }
2184
2185
2186 # --- Ratings Methods ---------------------------------------------------
2187
2192 public function CumulativeRating()
2193 {
2194 return $this->CumulativeRating;
2195 }
2196
2201 public function ScaledCumulativeRating()
2202 {
2203 if ($this->CumulativeRating == NULL)
2204 {
2205 return NULL;
2206 }
2207 else
2208 {
2209 return intval(($this->CumulativeRating + 5) / 10);
2210 }
2211 }
2212
2217 public function NumberOfRatings()
2218 {
2219 # if number of ratings not already set
2220 if (!isset($this->NumberOfRatings))
2221 {
2222 # obtain number of ratings
2223 $this->NumberOfRatings =
2224 $this->DB->Query("SELECT Count(*) AS NumberOfRatings "
2225 ."FROM ResourceRatings "
2226 ."WHERE ResourceId = ".$this->Id,
2227 "NumberOfRatings"
2228 );
2229
2230 # recalculate cumulative rating if it looks erroneous
2231 if (($this->NumberOfRatings > 0) && !$this->CumulativeRating())
2232 {
2233 $this->UpdateCumulativeRating();
2234 }
2235 }
2236
2237 # return number of ratings to caller
2238 return $this->NumberOfRatings;
2239 }
2240
2248 public function Rating($NewRating = NULL, $UserId = NULL)
2249 {
2250 $DB = $this->DB;
2251
2252 # if user ID not supplied
2253 if ($UserId == NULL)
2254 {
2255 # if user is logged in
2256 global $User;
2257 if ($User->IsLoggedIn())
2258 {
2259 # use ID of current user
2260 $UserId = $User->Get("UserId");
2261 }
2262 else
2263 {
2264 # return NULL to caller
2265 return NULL;
2266 }
2267 }
2268
2269 # sanitize $NewRating
2270 if (!is_null($NewRating))
2271 {
2272 $NewRating = intval($NewRating);
2273 }
2274
2275 # if there is a rating for resource and user
2276 $DB->Query("SELECT Rating FROM ResourceRatings "
2277 ."WHERE UserId = ${UserId} AND ResourceId = ".$this->Id);
2278 if ($Record = $DB->FetchRow())
2279 {
2280 # if new rating was supplied
2281 if ($NewRating != NULL)
2282 {
2283 # update existing rating
2284 $DB->Query("UPDATE ResourceRatings "
2285 ."SET Rating = ${NewRating}, DateRated = NOW() "
2286 ."WHERE UserId = ${UserId} AND ResourceId = ".$this->Id);
2287
2288 # update cumulative rating value
2289 $this->UpdateCumulativeRating();
2290
2291 # return value is new rating
2292 $Rating = $NewRating;
2293 }
2294 else
2295 {
2296 # get rating value to return to caller
2297 $Rating = $Record["Rating"];
2298 }
2299 }
2300 else
2301 {
2302 # if new rating was supplied
2303 if ($NewRating != NULL)
2304 {
2305 # add new rating
2306 $DB->Query("INSERT INTO ResourceRatings "
2307 ."(ResourceId, UserId, DateRated, Rating) "
2308 ."VALUES ("
2309 .$this->Id.", "
2310 ."${UserId}, "
2311 ."NOW(), "
2312 ."${NewRating})");
2313
2314 # update cumulative rating value
2315 $this->UpdateCumulativeRating();
2316
2317 # return value is new rating
2318 $Rating = $NewRating;
2319 }
2320 else
2321 {
2322 # return value is NULL
2323 $Rating = NULL;
2324 }
2325 }
2326
2327 # return rating value to caller
2328 return $Rating;
2329 }
2330
2331
2332 # --- Resource Comment Methods ------------------------------------------
2333
2338 public function Comments()
2339 {
2340 # read in comments if not already loaded
2341 if (!isset($this->Comments))
2342 {
2343 $this->DB->Query("SELECT MessageId FROM Messages "
2344 ."WHERE ParentId = ".$this->Id
2345 ." AND ParentType = 2 "
2346 ."ORDER BY DatePosted DESC");
2347 while ($MessageId = $this->DB->FetchField("MessageId"))
2348 {
2349 $this->Comments[] = new Message($MessageId);
2350 }
2351 }
2352
2353 # return array of comments to caller
2354 return $this->Comments;
2355 }
2356
2361 public function NumberOfComments()
2362 {
2363 # obtain number of comments if not already set
2364 if (!isset($this->NumberOfComments))
2365 {
2366 $this->NumberOfComments =
2367 $this->DB->Query("SELECT Count(*) AS NumberOfComments "
2368 ."FROM Messages "
2369 ."WHERE ParentId = ".$this->Id
2370 ." AND ParentType = 2",
2371 "NumberOfComments"
2372 );
2373 }
2374
2375 # return number of comments to caller
2376 return $this->NumberOfComments;
2377 }
2378
2379
2380 # --- Permission Methods -------------------------------------------------
2381
2391 public function UserCanView(User $User, $AllowHooksToModify=TRUE)
2392 {
2393 return $this->CheckSchemaPermissions($User, "View", $AllowHooksToModify);
2394 }
2395
2402 public function UserCanEdit($User)
2403 {
2404 return $this->CheckSchemaPermissions($User, "Edit");
2405 }
2406
2413 public function UserCanAuthor($User)
2414 {
2415 return $this->CheckSchemaPermissions($User, "Author");
2416 }
2417
2424 public function UserCanModify($User)
2425 {
2426 $CheckFn = "UserCan".(($this->Id()<0) ? "Author" : "Edit");
2427 return $this->$CheckFn($User);
2428 }
2429
2436 public function UserCanViewField($User, $FieldOrFieldName)
2437 {
2438 return $this->CheckFieldPermissions( $User, $FieldOrFieldName, "View" );
2439 }
2440
2447 public function UserCanEditField($User, $FieldOrFieldName)
2448 {
2449 return $this->CheckFieldPermissions( $User, $FieldOrFieldName, "Edit" );
2450 }
2451
2458 public function UserCanAuthorField($User, $FieldOrFieldName)
2459 {
2460 return $this->CheckFieldPermissions( $User, $FieldOrFieldName, "Author" );
2461 }
2462
2470 public function UserCanModifyField($User, $FieldOrFieldName)
2471 {
2472 $CheckFn = "UserCan".(($this->Id()<0) ? "Author" : "Edit")."Field";
2473
2474 return $this->$CheckFn($User, $FieldOrFieldName);
2475 }
2476
2477 # --- Utility Methods ----------------------------------------------------
2478
2482 public function QueueSearchAndRecommenderUpdate()
2483 {
2484 if (!$this->IsTempResource())
2485 {
2486 $SearchEngine = new SPTSearchEngine();
2487 $SearchEngine->QueueUpdateForItem($this);
2488
2489 $Recommender = new SPTRecommender();
2490 $Recommender->QueueUpdateForItem($this);
2491 }
2492 }
2493
2499 public static function GetSchemaForResource($ResourceId)
2500 {
2501 # if schema IDs are not loaded
2502 if (!isset(self::$SchemaIdCache))
2503 {
2504 # load schema IDs
2505 $DB = new Database();
2506 $DB->Query("SELECT ResourceId, SchemaId FROM Resources");
2507 self::$SchemaIdCache = $DB->FetchColumn("SchemaId", "ResourceId");
2508 }
2509
2510 # if multiple resources specified
2511 if (is_array($ResourceId))
2512 {
2513 # find schema IDs for specified resources
2514 $SchemaIds = array_intersect_key(self::$SchemaIdCache,
2515 array_flip($ResourceId));
2516
2517 # check that specified resource IDs were all valid
2518 if (count($SchemaIds) < count($ResourceId))
2519 {
2520 $BadIds = array_diff($ResourceId, array_keys($SchemaIds));
2521 throw new InvalidArgumentException("Unknown resource IDs ("
2522 .implode(", ", $BadIds).").");
2523 }
2524
2525 # return schema IDs to caller
2526 return $SchemaIds;
2527 }
2528 else
2529 {
2530 # check that specified resource was valid
2531 if (!isset(self::$SchemaIdCache[$ResourceId]))
2532 {
2533 throw new InvalidArgumentException("Unknown resource ID ("
2534 .$ResourceId.").");
2535 }
2536
2537 # return schema IDs for specified resource
2538 return self::$SchemaIdCache[$ResourceId];
2539 }
2540 }
2541
2542
2543 # ---- PRIVATE INTERFACE -------------------------------------------------
2544
2545 private $ClassificationCache;
2546 private $Comments;
2547 private $ControlledNameCache;
2548 private $ControlledNameVariantCache;
2549 private $CumulativeRating;
2550 private $NumberOfComments;
2551 private $NumberOfRatings;
2552 private $PermissionCache;
2553 private $SchemaId;
2554
2555 static private $Schemas;
2556 static private $SchemaIdCache;
2557
2568 private function CheckSchemaPermissions($User, $CheckType, $AllowHooksToModify=TRUE)
2569 {
2570 # construct a key to use for our permissions cache
2571 $CacheKey = "UserCan".$CheckType.$User->Id();
2572
2573 # if we don't have a cached value for this perm, compute one
2574 if (!isset($this->PermissionCache[$CacheKey]))
2575 {
2576 # get privileges for schema
2577 $PermsFn = $CheckType."ingPrivileges";
2578 $SchemaPrivs = $this->Schema()->$PermsFn();
2579
2580 # check passes if user privileges are greater than resource set
2581 $CheckResult = $SchemaPrivs->MeetsRequirements($User, $this);
2582
2583 # save the result of this check in our cache
2584 $this->PermissionCache[$CacheKey] = $CheckResult;
2585 }
2586
2587 $Value = $this->PermissionCache[$CacheKey];
2588
2589 if ($AllowHooksToModify)
2590 {
2591 $SignalResult = $GLOBALS["AF"]->SignalEvent(
2592 "EVENT_RESOURCE_".strtoupper($CheckType)."_PERMISSION_CHECK",
2593 array(
2594 "Resource" => $this,
2595 "User" => $User,
2596 "Can".$CheckType => $Value,
2597 "Schema" => $this->Schema(), ));
2598
2599 $Value = $SignalResult["Can".$CheckType];
2600 }
2601
2602 return $Value;
2603 }
2604
2613 private function CheckFieldPermissions($User, $Field, $CheckType)
2614 {
2615 # get field object (if not supplied)
2616 if (!($Field instanceof MetadataField))
2617 {
2618 try
2619 {
2620 $Field = $this->Schema()->GetField($Field);
2621 }
2622 catch (InvalidArgumentException $Exception)
2623 {
2624 # (user cannot view/author/edit if field was invalid)
2625 return FALSE;
2626 }
2627 }
2628
2629 # construct a key to use for our permissions cache
2630 $CacheKey = "UserCan".$CheckType."Field".$Field->Id()."-".$User->Id();
2631
2632 # if we don't have a cahced value, compute one
2633 if (!isset($this->PermissionCache[$CacheKey]))
2634 {
2635 # if field is enabled and editable, do permission check
2636 if ($Field->Enabled() &&
2637 ($CheckType == "View" || $Field->Editable()))
2638 {
2639 # be sure schema privs allow View/Edit/Author for this resource
2640 $SchemaCheckFn = "UserCan".$CheckType;
2641 if ($this->$SchemaCheckFn($User))
2642 {
2643 # get appropriate privilege set for field
2644 $PermsFn = $CheckType."ingPrivileges";
2645 $FieldPrivs = $Field->$PermsFn();
2646
2647 # user can View/Edit/Author if privileges are greater than field set
2648 $CheckResult = $FieldPrivs->MeetsRequirements($User, $this);
2649 }
2650 else
2651 {
2652 $CheckResult = FALSE;
2653 }
2654 }
2655 else
2656 {
2657 $CheckResult = FALSE;
2658 }
2659
2660 # allow plugins to modify result of permission check
2661 $SignalResult = $GLOBALS["AF"]->SignalEvent(
2662 "EVENT_FIELD_".strtoupper($CheckType)."_PERMISSION_CHECK", array(
2663 "Field" => $Field,
2664 "Resource" => $this,
2665 "User" => $User,
2666 "Can".$CheckType => $CheckResult));
2667 $CheckResult = $SignalResult["Can".$CheckType];
2668
2669 # save the result of this check in our cache
2670 $this->PermissionCache[$CacheKey] = $CheckResult;
2671 }
2672
2673 # return cached permission value
2674 return $this->PermissionCache[$CacheKey];
2675 }
2676
2680 private function UpdateCumulativeRating()
2681 {
2682 # grab totals from DB
2683 $this->DB->Query("SELECT COUNT(Rating) AS Count, "
2684 ."SUM(Rating) AS Total FROM ResourceRatings "
2685 ."WHERE ResourceId = ".$this->Id);
2686 $Record = $this->DB->FetchRow();
2687
2688 # calculate new cumulative rating
2689 $this->CumulativeRating = round($Record["Total"] / $Record["Count"]);
2690
2691 # save new cumulative rating in DB
2692 $this->DB->Query("UPDATE Resources "
2693 ."SET CumulativeRating = ".$this->CumulativeRating." "
2694 ."WHERE ResourceId = ".$this->Id);
2695 }
2696
2708 private function AddAssociation($TableName, $FieldName, $Value, $Field = NULL)
2709 {
2710 # We should ignore duplicate key errors when doing inserts:
2711 $this->DB->SetQueryErrorsToIgnore( array(
2712 "/INSERT INTO ".$TableName."/" =>
2713 "/Duplicate entry '-?[0-9]+-[0-9]+(-[0-9]+)?' for key/"));
2714
2715 # start out assuming no association will be added
2716 $AssociationAdded = FALSE;
2717
2718 # convert new value to array if necessary
2719 $Values = is_array($Value) ? $Value : array($Value);
2720
2721 # for each new value
2722 foreach ($Values as $Value)
2723 {
2724 # retrieve ID from value if necessary
2725 if (is_object($Value)) { $Value = $Value->Id(); }
2726
2727 # Try to insert a new entry for this association.
2728 $this->DB->Query("INSERT INTO ".$TableName." SET"
2729 ." ResourceId = ".intval($this->Id)
2730 .", ".$FieldName." = ".intval($Value)
2731 .($Field ? ", FieldId = ".intval($Field->Id()) : ""));
2732
2733 # If the insert ran without a duplicate key error,
2734 # then we added an assocation:
2735 if ($this->DB->IgnoredError() === FALSE)
2736 {
2737 $AssociationAdded = TRUE;
2738 }
2739 }
2740
2741 # Clear ignored errors:
2742 $this->DB->SetQueryErrorsToIgnore( NULL );
2743
2744 # report to caller whether association was added
2745 return $AssociationAdded;
2746 }
2747
2759 private function RemoveAssociation($TableName, $FieldName, $Value, $Field = NULL)
2760 {
2761 # start out assuming no association will be removed
2762 $AssociationRemoved = FALSE;
2763
2764 # convert value to array if necessary
2765 $Values = is_array($Value) ? $Value : array($Value);
2766
2767 # for each value
2768 foreach ($Values as $Value)
2769 {
2770 # retrieve ID from value if necessary
2771 if (is_object($Value)) { $Value = $Value->Id(); }
2772
2773 # remove any intersections with target ID from DB
2774 $this->DB->Query("DELETE FROM ".$TableName
2775 ." WHERE ResourceId = ".intval($this->Id)
2776 .($Field ? " AND FieldId = ".intval($Field->Id()) : "")
2777 ." AND ".$FieldName." = ".intval($Value));
2778 if ($this->DB->NumRowsAffected()) { $AssociationRemoved = TRUE; }
2779 }
2780
2781 # report to caller whether association was added
2782 return $AssociationRemoved;
2783 }
2784
2791 private function RemoveAllAssociations($TableName, $TargetFieldName, $Field)
2792 {
2793 # retrieve list of entries for this field and resource
2794 $Entries = $this->Get($Field);
2795
2796 # divide them into chunks of not more than 100
2797 foreach (array_chunk($Entries, 100, TRUE) as $Chunk)
2798 {
2799 # remove assocations from this chunk
2800 $this->DB->Query("DELETE FROM ".$TableName
2801 ." WHERE ResourceId = ".intval($this->Id)
2802 ." AND ".$TargetFieldName." IN "
2803 ."(".implode(",", array_keys($Chunk)).")");
2804 }
2805 }
2806
2813 static protected function SetDatabaseAccessValues($ClassName)
2814 {
2815 if (!isset(self::$ItemIdColumnNames[$ClassName]))
2816 {
2817 self::$ItemIdColumnNames[$ClassName] = "ResourceId";
2818 self::$ItemNameColumnNames[$ClassName] = NULL;
2819 self::$ItemTableNames[$ClassName] = "Resources";
2820 }
2821 }
2822 }
MetadataSchema\MDFTYPE_IMAGE
const MDFTYPE_IMAGE
Definition: MetadataSchema.php:31
Resource\GetByField
GetByField($FieldNameOrObject, $ReturnObject=FALSE, $IncludeVariants=FALSE)
Old method for retrieving values, deprecated in favor of Get().
Definition: Resource.php:773
Resource\UserCanView
UserCanView(User $User, $AllowHooksToModify=TRUE)
Determine if the given user can view the resource, e.g., on the full record page. ...
Definition: Resource.php:2391
FileFactory\GetFilesForResource
GetFilesForResource($ResourceOrResourceId, $ReturnObjects=TRUE)
Retrieve all files (names or objects) for specified resource.
Definition: FileFactory.php:39
Resource\GetImageUrls
GetImageUrls($FieldNameOrObject, $ImageSize=SPTImage::SIZE_FULL)
Get URLs for images, returning CleanURLs when possible and direct paths to image files otherwise...
Definition: Resource.php:1107
Resource\SetQualifier
SetQualifier($FieldName, $NewValue)
Set qualifier using field name.
Definition: Resource.php:1727
MetadataSchema\MDFTYPE_TIMESTAMP
const MDFTYPE_TIMESTAMP
Definition: MetadataSchema.php:25
SPTSearchEngine
Definition: SPTSearchEngine.php:10
MetadataSchema\MDFTYPE_FILE
const MDFTYPE_FILE
Definition: MetadataSchema.php:32
MetadataSchema
Metadata schema (in effect a Factory class for MetadataField).
Definition: MetadataSchema.php:13
Message
Abstraction for forum messages and resource comments.
Definition: Message.php:14
Database
SQL database abstraction object with smart query caching.
Definition: Database.php:22
Resource\UserCanModifyField
UserCanModifyField($User, $FieldOrFieldName)
Check whether user is allowed to modify (Edit for perm resources, Author for temp) specified metadata...
Definition: Resource.php:2470
MetadataSchema\MDFTYPE_REFERENCE
const MDFTYPE_REFERENCE
Definition: MetadataSchema.php:35
Resource\QueueSearchAndRecommenderUpdate
QueueSearchAndRecommenderUpdate()
Update search and recommender system DBs.
Definition: Resource.php:2482
Resource\GetAsArray
GetAsArray($IncludeDisabledFields=FALSE, $ReturnObjects=TRUE)
Retrieve all resource values as an array.
Definition: Resource.php:811
Resource\Id
Id()
Retrieve numerical resource ID.
Definition: Resource.php:287
Item\$DB
$DB
Definition: Item.php:115
Resource\UserCanEditField
UserCanEditField($User, $FieldOrFieldName)
Check whether user is allowed to edit specified metadata field.
Definition: Resource.php:2447
Resource\SetQualifierByField
SetQualifierByField($Field, $NewValue)
Set qualifier using field object.
Definition: Resource.php:1749
MetadataSchema\MDFTYPE_NUMBER
const MDFTYPE_NUMBER
Definition: MetadataSchema.php:23
Resource\GetViewPageUrl
GetViewPageUrl()
Retrieve view page URL for this resource.
Definition: Resource.php:386
Resource\Rating
Rating($NewRating=NULL, $UserId=NULL)
Get/set rating by a specific user for resource.
Definition: Resource.php:2248
User
Definition: User.php:41
Resource\NumberOfComments
NumberOfComments()
Get current number of comments for resource.
Definition: Resource.php:2361
Resource\NumberOfRatings
NumberOfRatings()
Get current number of ratings for resource.
Definition: Resource.php:2217
Resource\GetQualifier
GetQualifier($FieldName, $ReturnObject=TRUE)
Retrieve qualifier by field name.
Definition: Resource.php:875
FolderFactory
Factory object for Folder class, used to retrieve and manage Folders and groups of Folders...
Definition: FolderFactory.php:14
Qualifier
Definition: Qualifier.php:10
FileFactory\Copy
Copy($FileToCopy)
Create copy of File and return to caller.
Definition: FileFactory.php:85
MetadataSchema\MDFTYPE_TREE
const MDFTYPE_TREE
Definition: MetadataSchema.php:27
SPTRecommender
Definition: SPTRecommender.php:10
Resource\Schema
Schema()
Get MetadataSchema for resource.
Definition: Resource.php:305
Date
Definition: Date.php:18
MetadataSchema\MDFTYPE_OPTION
const MDFTYPE_OPTION
Definition: MetadataSchema.php:29
ControlledName
Metadata type representing non-hierarchical controlled vocabulary values.
Definition: ControlledName.php:14
MetadataSchema\MDFTYPE_FLAG
const MDFTYPE_FLAG
Definition: MetadataSchema.php:26
Resource\UserCanEdit
UserCanEdit($User)
Determine if the given user can edit the resource.
Definition: Resource.php:2402
MetadataSchema\MDFTYPE_CONTROLLEDNAME
const MDFTYPE_CONTROLLEDNAME
Definition: MetadataSchema.php:28
Resource\GetForDisplay
GetForDisplay($FieldNameOrObject, $ReturnObject=TRUE, $IncludeVariants=FALSE)
Retrieve value using field name or field object, signaling EVENT_FIELD_DISPLAY_FILTER to allow other ...
Definition: Resource.php:737
Resource\Comments
Comments()
Get comments for resource.
Definition: Resource.php:2338
Resource\UpdateAutoupdateFields
UpdateAutoupdateFields($UpdateType, $User=NULL)
Update the auto-updated fields as necessary.
Definition: Resource.php:255
CWUserFactory
CWIS-specific user factory class.
Definition: CWUserFactory.php:13
Resource\Get
Get($Field, $ReturnObject=FALSE, $IncludeVariants=FALSE)
Retrieve value using field name or field object.
Definition: Resource.php:410
PHP
PHP
Definition: RSSClient--Test.php:12
Resource\GetSchemaForResource
static GetSchemaForResource($ResourceId)
Get schema ID for specified resource(s).
Definition: Resource.php:2499
MetadataSchema\SCHEMAID_DEFAULT
const SCHEMAID_DEFAULT
Definition: MetadataSchema.php:53
FileFactory
Factory for manipulating File objects.
Definition: FileFactory.php:13
Item
Common base class for persistent items store in database.
Definition: Item.php:13
Resource\GetQualifierByFieldId
GetQualifierByFieldId($FieldId, $ReturnObject=TRUE)
Retrieve qualifier by field ID.
Definition: Resource.php:889
SPTImage
Encapsulates a full-size, preview, and thumbnail image.
Definition: SPTImage.php:13
Resource\UserCanAuthorField
UserCanAuthorField($User, $FieldOrFieldName)
Check whether user is allowed to author specified metadata field.
Definition: Resource.php:2458
Resource\Clear
Clear($Field, $ValueToClear=NULL)
Clear field value.
Definition: Resource.php:1795
Resource\UserCanModify
UserCanModify($User)
Check if the user is allowed to modify (Edit for perm resources, Author for temp) a specified resourc...
Definition: Resource.php:2424
Resource\UserCanAuthor
UserCanAuthor($User)
Determine if the given user can edit the resource.
Definition: Resource.php:2413
Resource\GetByFieldId
GetByFieldId($FieldId, $ReturnObject=FALSE, $IncludeVariants=FALSE)
Retrieve value using field ID.
Definition: Resource.php:793
Resource\IsTempResource
IsTempResource($NewSetting=NULL)
Get/set whether resource is a temporary record.
Definition: Resource.php:316
Resource\SetByField
SetByField($Field, $NewValue)
Method replaced by Resource::Set(), preserved for backward compatibility.
Definition: Resource.php:1705
ControlledName\STATUS_OK
const STATUS_OK
Successful execution.
Definition: ControlledName.php:21
MetadataField
Object representing a locally-defined type of metadata field.
Definition: MetadataField.php:13
Resource\__construct
__construct($ResourceId)
Object constructor for loading an existing resource.
Definition: Resource.php:24
Resource\GetMapped
GetMapped($MappedName, $ReturnObject=FALSE, $IncludeVariants=FALSE)
Retrieve value using standard (mapped) field name.
Definition: Resource.php:857
MetadataSchema\MDFTYPE_PARAGRAPH
const MDFTYPE_PARAGRAPH
Definition: MetadataSchema.php:22
Item\$Id
$Id
Definition: Item.php:116
Resource\SetDatabaseAccessValues
static SetDatabaseAccessValues($ClassName)
Set the database access values (table name, ID column name, name column name) for specified class...
Definition: Resource.php:2813
MetadataSchema\MDFTYPE_DATE
const MDFTYPE_DATE
Definition: MetadataSchema.php:24
Resource
Represents a "resource" in CWIS.
Definition: Resource.php:13
MetadataSchema\MDFTYPE_URL
const MDFTYPE_URL
Definition: MetadataSchema.php:33
Resource\GetQualifierByField
GetQualifierByField($Field, $ReturnObject=TRUE)
Retrieve qualifier by Field object.
Definition: Resource.php:903
MetadataSchema\MDFTYPE_TEXT
const MDFTYPE_TEXT
Definition: MetadataSchema.php:21
Resource\ClearByFieldId
ClearByFieldId($FieldId, $ValueToClear=NULL)
Clear field value specified by field ID.
Definition: Resource.php:1783
Resource\SetQualifierByFieldId
SetQualifierByFieldId($FieldId, $NewValue)
Set qualifier using field ID.
Definition: Resource.php:1738
SPTImage\SIZE_FULL
const SIZE_FULL
Definition: SPTImage.php:21
MetadataSchema\MDFTYPE_USER
const MDFTYPE_USER
Definition: MetadataSchema.php:30
SPTImage\ClearImageSymlinksForResource
static ClearImageSymlinksForResource($ResourceId, $FieldId)
Remove symlinks used for to cache image mappings.
Definition: SPTImage.php:576
Resource\SchemaId
SchemaId()
Retrieve ID of schema for resource.
Definition: Resource.php:296
Resource\ScaledCumulativeRating
ScaledCumulativeRating()
Return cumulative rating scaled to 1/10th.
Definition: Resource.php:2201
Resource\Set
Set($Field, $NewValue, $Reset=FALSE)
Set value using field name or field object.
Definition: Resource.php:1142
MetadataField\UPDATEMETHOD_ONRECORDCREATE
const UPDATEMETHOD_ONRECORDCREATE
Definition: MetadataField.php:19
Resource\Create
static Create($SchemaId)
Create a new resource.
Definition: Resource.php:47
Resource\UserCanViewField
UserCanViewField($User, $FieldOrFieldName)
Check whether user is allowed to view specified metadata field.
Definition: Resource.php:2436
Classification
Metadata type representing hierarchical ("Tree") controlled vocabulary values.
Definition: Classification.php:13
Resource\SetByFieldId
SetByFieldId($FieldId, $NewValue)
Set field value using field ID.
Definition: Resource.php:1717
Resource\Classifications
Classifications()
Get 2D array of classifications associated with resource.
Definition: Resource.php:2159
Resource\ClearByField
ClearByField($Field, $ValueToClear=NULL)
Clear field value.
Definition: Resource.php:2147
MetadataSchema\MDFTYPE_POINT
const MDFTYPE_POINT
Definition: MetadataSchema.php:34
File
Class representing a stored (usually uploaded) file.
Definition: File.php:13
ResourceFactory
Factory for Resource objects.
Definition: ResourceFactory.php:13
CWUser
CWIS-specific user class.
Definition: CWUser.php:13
Resource\CumulativeRating
CumulativeRating()
Get cumulative rating (range is usually 0-100)
Definition: Resource.php:2192
Resource\FieldIsSet
FieldIsSet($FieldNameOrObject, $IgnorePadding=FALSE)
Determine if the value for a field is set.
Definition: Resource.php:1037
Resource\Delete
Delete()
Remove resource (and accompanying associations) from database and delete any associated files...
Definition: Resource.php:138
$Items
$Items
Definition: RSSClient--Test.php:26