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