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 Edward Almasy and Internet Scout Project
7 # http://scout.wisc.edu/
8 #
9
13 class Resource {
14
15 # ---- PUBLIC INTERFACE --------------------------------------------------
16
22 function Resource($ResourceId = NULL)
23 {
24 $this->DB = new Database();
25 $DB = $this->DB;
26 $this->Schema = new MetadataSchema();
27
28 # if resource ID supplied
29 if ($ResourceId !== NULL)
30 {
31 # save resource ID
32 $this->Id = intval($ResourceId);
33
34 # locate resource in database
35 $DB->Query("SELECT * FROM Resources WHERE ResourceId = ".$this->Id);
36
37 # if unable to locate resource
38 $Record = $DB->FetchRow();
39 if ($Record == FALSE)
40 {
41 # set status to -1 to indicate that creation failed
42 $this->LastStatus = -1;
43 }
44 else
45 {
46 # load in attributes from database
47 $this->DBFields = $Record;
48 $this->CumulativeRating = $Record["CumulativeRating"];
49
50 # set status to 1 to indicate that creation succeeded
51 $this->LastStatus = 1;
52 }
53 }
54 else
55 {
56 # clean out any temp resource records more than three days old
57 $RFactory = new ResourceFactory();
58 $RFactory->CleanOutStaleTempItems(60 * 24 * 3);
59
60 # lock DB tables to prevent next ID from being grabbed
61 $DB->Query(
62 "LOCK TABLES Resources write, APUsers read, ".
63 "APSessions write, APSessionData write");
64
65 # find next temp resource ID
66 $this->Id = $RFactory->GetNextTempItemId();
67
68 # write out new resource record with temp resource ID and user ID
69 global $User;
70 $DB->Query("INSERT INTO Resources (ResourceId, AddedById,"
71 ." LastModifiedById, DateLastModified, DateOfRecordCreation)"
72 ." VALUES (".$this->Id.", "
73 .$User->Get("UserId").", "
74 .$User->Get("UserId").", "
75 ."NOW(), NOW())");
76
77 # release DB tables
78 $DB->Query("UNLOCK TABLES");
79
80 # load in attributes from database
81 $DB->Query("SELECT * FROM Resources WHERE ResourceId = ".$this->Id);
82 $this->DBFields = $DB->FetchRow();
83 $this->CumulativeRating = $this->DBFields["CumulativeRating"];
84
85 # set any default values
86 $Schema = new MetadataSchema();
87 $Fields = $Schema->GetFields(MetadataSchema::MDFTYPE_OPTION
88 |MetadataSchema::MDFTYPE_TEXT
89 |MetadataSchema::MDFTYPE_FLAG
90 |MetadataSchema::MDFTYPE_NUMBER
91 |MetadataSchema::MDFTYPE_POINT);
92 foreach ($Fields as $Field)
93 {
94 $DefaultValue = $Field->DefaultValue();
95
96 # flip option default values to get into the form that
97 # Resource::Set() expects
98 if ($Field->Type() == MetadataSchema::MDFTYPE_OPTION
99 && is_array($DefaultValue))
100 {
101 $DefaultValue = array_flip($DefaultValue);
102 }
103
104 $this->SetByField($Field, $DefaultValue);
105 }
106
107 # Update timestamps as required:
108 $TimestampFields = $Schema->GetFields(
109 MetadataSchema::MDFTYPE_TIMESTAMP);
110 foreach ($TimestampFields as $Field)
111 {
112 if ($Field->UpdateMethod() ==
113 MetadataField::UPDATEMETHOD_ONRECORDCREATE)
114 {
115 $this->SetByField($Field, "now");
116 }
117 }
118
119 # signal resource creation
120 $GLOBALS["AF"]->SignalEvent("EVENT_RESOURCE_CREATE", array(
121 "Resource" => $this,
122 ));
123
124 # set status to 1 to indicate that creation succeeded
125 $this->LastStatus = 1;
126 }
127 }
128
133 function Delete()
134 {
135 global $SysConfig;
136
137 # signal that resource deletion is about to occur
138 global $AF;
139 $AF->SignalEvent("EVENT_RESOURCE_DELETE", array(
140 "Resource" => $this,
141 ));
142
143 # grab list of classifications
144 $Classifications = $this->Classifications();
145
146 # delete resource/classification intersections
147 $DB = $this->DB;
148 $DB->Query("DELETE FROM ResourceClassInts WHERE ResourceId = ".$this->Id());
149
150 # for each classification type
151 foreach ($Classifications as $ClassType => $ClassesOfType)
152 {
153 # for each classification of that type
154 foreach ($ClassesOfType as $ClassId => $ClassName)
155 {
156 # recalculate resource count for classification
157 $Class = new Classification($ClassId);
158 $Class->RecalcResourceCount();
159 }
160 }
161
162 # delete resource/name intersections
163 $DB->Query("DELETE FROM ResourceNameInts WHERE ResourceId = ".$this->Id());
164
165 # delete any associated images not in use by other resources
166 $Fields = $this->Schema->GetFields(MetadataSchema::MDFTYPE_IMAGE);
167 foreach ($Fields as $Field)
168 {
169 $ImageId = $DB->Query("SELECT `".$Field->DBFieldName()
170 ."` FROM Resources WHERE ResourceId = ".$this->Id(),
171 $Field->DBFieldName());
172 if ($ImageId > 0)
173 {
174 $ImageCount = $DB->Query("SELECT COUNT(*) AS ImageCount FROM Resources"
175 ." WHERE ".$Field->DBFieldName()." = ".$ImageId,
176 "ImageCount");
177 if ($ImageCount < 2)
178 {
179 $Image = new SPTImage($ImageId);
180 $Image->Delete();
181 }
182 }
183 }
184
185 # delete any associated files
186 $Factory = new FileFactory(NULL);
187 $Files = $Factory->GetFilesForResource($this->Id());
188 foreach ($Files as $File)
189 {
190 $File->Delete();
191 }
192
193 # delete resource record from database
194 $DB->Query("DELETE FROM Resources WHERE ResourceId = ".$this->Id());
195
196 # drop item from search engine and recommender system
197 if ($SysConfig->SearchDBEnabled())
198 {
199 $SearchEngine = new SPTSearchEngine();
200 $SearchEngine->DropItem($this->Id());
201 }
202 if ($SysConfig->RecommenderDBEnabled())
203 {
204 $Recommender = new SPTRecommender();
205 $Recommender->DropItem($this->Id());
206 }
207
208 # get the folders containing the resource
209 $FolderFactory = new FolderFactory();
210 $Folders = $FolderFactory->GetFoldersContainingItem(
211 $this->Id,
212 "Resource");
213
214 # drop the resource from each folder it belongs to
215 foreach ($Folders as $Folder)
216 {
217 # mixed item type folder
218 if ($Folder->ContainsItem($this->Id, "Resource"))
219 {
220 $Folder->RemoveItem($this->Id, "Resource");
221 }
222
223 # single item type folder
224 else
225 {
226 $Folder->RemoveItem($this->Id);
227 }
228 }
229
230 # delete any resource comments
231 $DB->Query("DELETE FROM Messages WHERE ParentId = ".$this->Id);
232 }
233
238 function Status() { return $this->LastStatus; }
239
244 function Id() { return $this->Id; }
245
251 function IsTempResource($NewSetting = NULL)
252 {
253 # if new temp resource setting supplied
254 if (!is_null($NewSetting))
255 {
256 # if caller requested to switch
257 $DB = $this->DB;
258 if ((($this->Id() < 0) && ($NewSetting == FALSE))
259 || (($this->Id() >= 0) && ($NewSetting == TRUE)))
260 {
261 # lock DB tables to prevent next ID from being grabbed
262 $DB->Query("LOCK TABLES Resources write");
263
264 # get next resource ID as appropriate
265 $OldResourceId = $this->Id;
266 $Factory = new ResourceFactory();
267 if ($NewSetting == TRUE)
268 {
269 $this->Id = $Factory->GetNextTempItemId();
270 }
271 else
272 {
273 $this->Id = $Factory->GetNextItemId();
274 }
275
276 # change resource ID
277 $DB->Query("UPDATE Resources SET ResourceId = ".
278 $this->Id. " WHERE ResourceId = ".$OldResourceId);
279
280 # release DB tables
281 $DB->Query("UNLOCK TABLES");
282
283 # change associations
284 unset($this->ClassificationCache);
285 $DB->Query("UPDATE ResourceClassInts SET ResourceId = ".
286 $this->Id. " WHERE ResourceId = ".$OldResourceId);
287 unset($this->ControlledNameCache);
288 unset($this->ControlledNameVariantCache);
289 $DB->Query("UPDATE ResourceNameInts SET ResourceId = ".
290 $this->Id. " WHERE ResourceId = ".$OldResourceId);
291 $DB->Query("UPDATE Files SET ResourceId = ".
292 $this->Id. " WHERE ResourceId = ".$OldResourceId);
293
294 # signal event as appropriate
295 if ($NewSetting === FALSE)
296 {
297 $GLOBALS["AF"]->SignalEvent("EVENT_RESOURCE_ADD", array(
298 "Resource" => $this,
299 ));
300 }
301 }
302 }
303
304 # report to caller whether we are a temp resource
305 return ($this->Id() < 0) ? TRUE : FALSE;
306 }
307
308
309 # --- Generic Attribute Retrieval Methods -------------------------------
310
325 function Get($FieldNameOrObject, $ReturnObject = FALSE, $IncludeVariants = FALSE)
326 {
327 # load field object if needed
328 $Field = is_object($FieldNameOrObject) ? $FieldNameOrObject
329 : $this->Schema->GetFieldByName($FieldNameOrObject);
330
331 # return no value found if we don't have a valid field
332 if ((get_class($Field) != "MetadataField")
333 || ($Field->Status() != MetadataSchema::MDFSTAT_OK))
334 { return NULL; }
335
336 # grab database field name
337 $DBFieldName = $Field->DBFieldName();
338
339 # format return value based on field type
340 switch ($Field->Type())
341 {
342 case MetadataSchema::MDFTYPE_TEXT:
343 case MetadataSchema::MDFTYPE_PARAGRAPH:
344 case MetadataSchema::MDFTYPE_NUMBER:
345 case MetadataSchema::MDFTYPE_FLAG:
346 case MetadataSchema::MDFTYPE_URL:
347 if (isset($this->DBFields[$DBFieldName]))
348 {
349 $ReturnValue = $this->DBFields[$DBFieldName];
350 }
351 else
352 {
353 $ReturnValue = NULL;
354 }
355 break;
356
357 case MetadataSchema::MDFTYPE_POINT:
358 $ReturnValue = array("X" => $this->DBFields[$DBFieldName."X"],
359 "Y" => $this->DBFields[$DBFieldName."Y"]);
360 break;
361
362 case MetadataSchema::MDFTYPE_DATE:
363 $Date = new Date($this->DBFields[$DBFieldName."Begin"],
364 $this->DBFields[$DBFieldName."End"],
365 $this->DBFields[$DBFieldName."Precision"]);
366 if ($ReturnObject)
367 {
368 $ReturnValue = $Date;
369 }
370 else
371 {
372 $ReturnValue = $Date->Formatted();
373 }
374 break;
375
376 case MetadataSchema::MDFTYPE_TIMESTAMP:
377 $ReturnValue = $this->DBFields[$DBFieldName];
378 break;
379
380 case MetadataSchema::MDFTYPE_TREE:
381 # start with empty array
382 $ReturnValue = array();
383
384 # if classification cache has not been loaded
385 if (!isset($this->ClassificationCache))
386 {
387 # load all classifications associated with this resource into cache
388 $this->ClassificationCache = array();
389 $this->DB->Query("SELECT Classifications.ClassificationId,Classifications.FieldId,ClassificationName "
390 ."FROM ResourceClassInts, Classifications "
391 ."WHERE ResourceClassInts.ResourceId = ".$this->Id." "
392 ."AND ResourceClassInts.ClassificationId = Classifications.ClassificationId ");
393 while ($Record = $this->DB->FetchRow())
394 {
395 $this->ClassificationCache[$Record["ClassificationId"]]["Name"] =
396 $Record["ClassificationName"];
397 $this->ClassificationCache[$Record["ClassificationId"]]["FieldId"] =
398 $Record["FieldId"];
399 }
400 }
401
402 # for each entry in classification cache
403 foreach ($this->ClassificationCache as $ClassificationId => $ClassificationInfo)
404 {
405 # if classification ID matches field we are looking for
406 if ($ClassificationInfo["FieldId"] == $Field->Id())
407 {
408 # add field to result
409 if ($ReturnObject)
410 {
411 $ReturnValue[$ClassificationId] = new Classification($ClassificationId);
412 }
413 else
414 {
415 $ReturnValue[$ClassificationId] = $ClassificationInfo["Name"];
416 }
417 }
418 }
419 break;
420
421 case MetadataSchema::MDFTYPE_CONTROLLEDNAME:
422 case MetadataSchema::MDFTYPE_OPTION:
423 # start with empty array
424 $ReturnValue = array();
425
426 # if controlled name cache has not been loaded
427 if (!isset($this->ControlledNameCache))
428 {
429 # load all controlled names associated with this resource into cache
430 $this->ControlledNameCache = array();
431 $this->DB->Query("SELECT ControlledNames.ControlledNameId,ControlledNames.FieldId,ControlledName "
432 ."FROM ResourceNameInts, ControlledNames "
433 ."WHERE ResourceNameInts.ResourceId = ".$this->Id." "
434 ."AND ResourceNameInts.ControlledNameId = ControlledNames.ControlledNameId ");
435 while ($Record = $this->DB->FetchRow())
436 {
437 $this->ControlledNameCache[$Record["ControlledNameId"]]["Name"] = $Record["ControlledName"];
438 $this->ControlledNameCache[$Record["ControlledNameId"]]["FieldId"] = $Record["FieldId"];
439 }
440 }
441
442 # if variant names requested and variant name cache has not been loaded
443 if ($IncludeVariants && !isset($this->ControlledNameVariantCache))
444 {
445 # load all controlled names associated with this resource into cache
446 $this->ControlledNameVariantCache = array();
447 $this->DB->Query("SELECT ControlledNames.ControlledNameId,ControlledNames.FieldId,ControlledName,VariantName "
448 ."FROM ResourceNameInts, ControlledNames, VariantNames "
449 ."WHERE ResourceNameInts.ResourceId = ".$this->Id." "
450 ."AND ResourceNameInts.ControlledNameId = ControlledNames.ControlledNameId "
451 ."AND VariantNames.ControlledNameId = ControlledNames.ControlledNameId");
452 while ($Record = $this->DB->FetchRow())
453 {
454 $this->ControlledNameVariantCache[$Record["ControlledNameId"]][] = $Record["VariantName"];
455 }
456 }
457
458 # for each entry in controlled name cache
459 foreach ($this->ControlledNameCache as $ControlledNameId => $ControlledNameInfo)
460 {
461 # if controlled name type matches field we are looking for
462 if ($ControlledNameInfo["FieldId"] == $Field->Id())
463 {
464 # if objects requested
465 if ($ReturnObject)
466 {
467 $ReturnValue[$ControlledNameId] =
468 new ControlledName($ControlledNameId);
469 }
470 else
471 {
472 # if variant names requested
473 if ($IncludeVariants)
474 {
475 # add field to result
476 $ReturnValue[] = $ControlledNameInfo["Name"];
477
478 # add any variant names to result
479 if (isset($this->ControlledNameVariantCache[$ControlledNameId]))
480 {
481 $ReturnValue = array_merge($ReturnValue, $this->ControlledNameVariantCache[$ControlledNameId]);
482 }
483 }
484 else
485 {
486 # add field with index to result
487 $ReturnValue[$ControlledNameId] = $ControlledNameInfo["Name"];
488 }
489 }
490 }
491 }
492 break;
493
494 case MetadataSchema::MDFTYPE_USER:
495 $User = new User($this->DB, (int)$this->DBFields[$DBFieldName]);
496 if ($ReturnObject)
497 {
498 $ReturnValue = $User;
499 }
500 else
501 {
502 $ReturnValue = $User->Get("UserName");
503 }
504 break;
505
506 case MetadataSchema::MDFTYPE_IMAGE:
507 if ($this->DBFields[$DBFieldName] > 0)
508 {
509 $ImageObject = new SPTImage($this->DBFields[$DBFieldName]);
510 if ($ReturnObject)
511 {
512 $ReturnValue = $ImageObject;
513 }
514 else
515 {
516 $ReturnValue = $ImageObject->AltText();
517 }
518 }
519 else
520 {
521 $ReturnValue = NULL;
522 }
523 break;
524
525 case MetadataSchema::MDFTYPE_FILE:
526 # retrieve files using factory
527 $Factory = new FileFactory($Field->Id());
528 $ReturnValue = $Factory->GetFilesForResource(
529 $this->Id, $ReturnObject);
530 break;
531
532 default:
533 # ERROR OUT
534 exit("<br>SPT - ERROR: attempt to retrieve unknown resource field type (".$Field->Type().")<br>\n");
535 break;
536 }
537
538 # return formatted value to caller
539 return $ReturnValue;
540 }
545 function GetByField($FieldNameOrObject,
546 $ReturnObject = FALSE, $IncludeVariants = FALSE)
547 { return $this->Get($FieldNameOrObject, $ReturnObject, $IncludeVariants); }
548
562 function GetByFieldId($FieldId, $ReturnObject = FALSE, $IncludeVariants = FALSE)
563 {
564 $Field = $this->Schema->GetField($FieldId);
565 return ($Field) ? $this->Get($Field, $ReturnObject, $IncludeVariants) : NULL;
566 }
567
580 function GetAsArray($IncludeDisabledFields = FALSE, $ReturnObjects = TRUE)
581 {
582 # retrieve field info
583 $Fields = $this->Schema->GetFields();
584
585 # for each field
586 foreach ($Fields as $Field)
587 {
588 # if field is enabled or caller requested disabled fields
589 if ($Field->Enabled() || $IncludeDisabledFields)
590 {
591 # retrieve info and add it to the array
592 $FieldStrings[$Field->Name()] = $this->Get($Field, $ReturnObjects);
593
594 # if field uses qualifiers
595 if ($Field->UsesQualifiers())
596 {
597 # get qualifier attributes and add to the array
598 $FieldStrings[$Field->Name()." Qualifier"] =
599 $this->GetQualifierByField($Field, $ReturnObjects);
600 }
601 }
602 }
603
604 # add in internal values
605 $FieldStrings["ResourceId"] = $this->Id();
606 $FieldStrings["CumulativeRating"] = $this->CumulativeRating();
607
608 # return array to caller
609 return $FieldStrings;
610 }
611
626 function GetMapped($MappedName, $ReturnObject = FALSE, $IncludeVariants = FALSE)
627 {
628 return $this->Schema->StdNameToFieldMapping($MappedName)
629 ? $this->GetByFieldId($this->Schema->StdNameToFieldMapping($MappedName),
630 $ReturnObject, $IncludeVariants)
631 : NULL;
632 }
633
642 function GetQualifier($FieldName, $ReturnObject = TRUE)
643 {
644 $Field = $this->Schema->GetFieldByName($FieldName);
645 return $this->GetQualifierByField($Field, $ReturnObject);
646 }
647
656 function GetQualifierByFieldId($FieldId, $ReturnObject = TRUE)
657 {
658 $Field = $this->Schema->GetField($FieldId);
659 return ($Field) ? $this->GetQualifierByField($Field, $ReturnObject) : NULL;
660 }
661
670 function GetQualifierByField($Field, $ReturnObject = TRUE)
671 {
672 # return NULL if field is invalid
673 if ((get_class($Field) != "MetadataField")
674 || ($Field->Status() != MetadataSchema::MDFSTAT_OK))
675 { return NULL; }
676
677 # assume no qualifiers if not otherwise determined
678 $ReturnValue = NULL;
679
680 # if field uses qualifiers
681 if ($Field->UsesQualifiers())
682 {
683 # retrieve qualifiers based on field type
684 switch ($Field->Type())
685 {
686 case MetadataSchema::MDFTYPE_TREE:
687 case MetadataSchema::MDFTYPE_CONTROLLEDNAME:
688 case MetadataSchema::MDFTYPE_OPTION:
689 # retrieve list of items
690 $Items = $this->Get($Field);
691
692 # if field uses item-level qualifiers
693 if ($Field->HasItemLevelQualifiers())
694 {
695 # determine general item name in DB
696 $TableName = ($Field->Type() == MetadataSchema::MDFTYPE_TREE)
697 ? "Classification" : "ControlledName";
698
699 # for each item
700 foreach ($Items as $ItemId => $ItemName)
701 {
702 # look up qualifier for item
703 $QualId = $this->DB->Query(
704 "SELECT * FROM ".$TableName."s"
705 ." WHERE ".$TableName."Id = ".$ItemId
706 , "QualifierId");
707
708
709 if ($QualId > 0)
710 {
711 # if object was requested by caller
712 if ($ReturnObject)
713 {
714 # load qualifier and add to return value array
715 $ReturnValue[$ItemId] = new Qualifier($QualId);
716 }
717 else
718 {
719 # add qualifier ID to return value array
720 $ReturnValue[$ItemId] = $QualId;
721 }
722 }
723 else
724 {
725 # add NULL to return value array for this item
726 $ReturnValue[$ItemId] = NULL;
727 }
728 }
729 }
730 else
731 {
732 # for each item
733 foreach ($Items as $ItemId => $ItemName)
734 {
735 # if object was requested by caller
736 if ($ReturnObject)
737 {
738 # load default qualifier and add to return value array
739 $ReturnValue[$ItemId] = new Qualifier($Field->DefaultQualifier());
740 }
741 else
742 {
743 # add default qualifier ID to return value array
744 $ReturnValue[$ItemId] = $Field->DefaultQualifier();
745 }
746 }
747 }
748 break;
749
750 default:
751 # if field uses item-level qualifiers
752 if ($Field->HasItemLevelQualifiers())
753 {
754 # if qualifier available
755 if ($this->DBFields[$Field->DBFieldName()."Qualifier"] > 0)
756 {
757 # if object was requested by caller
758 if ($ReturnObject)
759 {
760 # return qualifier for field
761 $ReturnValue = new Qualifier($this->DBFields[$Field->DBFieldName()."Qualifier"]);
762 }
763 else
764 {
765 # return qualifier ID for field
766 $ReturnValue = $this->DBFields[$Field->DBFieldName()."Qualifier"];
767 }
768 }
769 }
770 else
771 {
772 # if default qualifier available
773 if ($Field->DefaultQualifier() > 0)
774 {
775 # if object was requested by caller
776 if ($ReturnObject)
777 {
778 # return default qualifier
779 $ReturnValue = new Qualifier($Field->DefaultQualifier());
780 }
781 else
782 {
783 # return default qualifier ID
784 $ReturnValue = $Field->DefaultQualifier();
785 }
786 }
787 }
788 break;
789 }
790 }
791
792 # return qualifier object or ID (or array of same) to caller
793 return $ReturnValue;
794 }
795
796
797 # --- Generic Attribute Setting Methods ---------------------------------
798
799 # set value using field name or field object
800 function Set($FieldNameOrObject, $NewValue)
801 {
802 # load field object if needed
803 $Field = is_object($FieldNameOrObject) ? $FieldNameOrObject
804 : $this->Schema->GetFieldByName($FieldNameOrObject);
805
806 # grab commonly-used values for local use
807 $DB = $this->DB;
808 $ResourceId = $this->Id;
809
810 # grab database field name
811 $DBFieldName = $Field->DBFieldName();
812
813 # Flag to deterimine if we've actually changed anything.
814 $UpdateModTime = FALSE;
815
816 # store value in DB based on field type
817 switch ($Field->Type())
818 {
819 case MetadataSchema::MDFTYPE_TEXT:
820 case MetadataSchema::MDFTYPE_PARAGRAPH:
821 case MetadataSchema::MDFTYPE_URL:
822 if ($this->DBFields[$DBFieldName] != $NewValue)
823 {
824 # save value directly to DB
825 $DB->Query("UPDATE Resources SET `"
826 .$DBFieldName."` = '".addslashes($NewValue)."' "
827 ."WHERE ResourceId = ".$ResourceId);
828
829 # save value locally
830 $this->DBFields[$DBFieldName] = $NewValue;
831 $UpdateModTime=TRUE;
832 }
833 break;
834
835 case MetadataSchema::MDFTYPE_NUMBER:
836 if ( $this->DBFields[$DBFieldName] != $NewValue )
837 {
838 # save value directly to DB
839 if (is_null($NewValue))
840 {
841 $DB->Query("UPDATE Resources SET `"
842 .$DBFieldName."` = NULL"
843 ." WHERE ResourceId = ".$ResourceId);
844 }
845 else
846 {
847 $DB->Query("UPDATE Resources SET `"
848 .$DBFieldName."` = ".intval($NewValue)
849 ." WHERE ResourceId = ".$ResourceId);
850 }
851
852 # save value locally
853 $this->DBFields[$DBFieldName] = $NewValue;
854 $UpdateModTime = TRUE;
855 }
856 break;
857
858
859 case MetadataSchema::MDFTYPE_POINT:
860 if ($this->DBFields[$DBFieldName."X"] != $NewValue["X"] ||
861 $this->DBFields[$DBFieldName."Y"] != $NewValue["Y"] )
862 {
863 if (is_null($NewValue))
864 {
865 $DB->Query("UPDATE Resources SET "
866 ."`".$DBFieldName."X` = NULL, "
867 ."`".$DBFieldName."Y` = NULL "
868 ."WHERE ResourceId = ".$ResourceId);
869 $this->DBFields[$DBFieldName."X"] = NULL;
870 $this->DBFields[$DBFieldName."Y"] = NULL;
871 }
872 else
873 {
874 $DB->Query("UPDATE Resources SET "
875 ."`".$DBFieldName."X` = ".(strlen($NewValue["X"])
876 ? "'".$NewValue["X"]."'" : "NULL").", "
877 ."`".$DBFieldName."Y` = ".(strlen($NewValue["Y"])
878 ? "'".$NewValue["Y"]."'" : "NULL")
879 ." WHERE ResourceId = ".$ResourceId);
880
881 $Digits = $Field->PointDecimalDigits();
882
883 $this->DBFields[$DBFieldName."X"] =
884 strlen($NewValue["X"]) ? round($NewValue["X"], $Digits) : NULL;
885 $this->DBFields[$DBFieldName."Y"] =
886 strlen($NewValue["Y"]) ? round($NewValue["Y"], $Digits) : NULL;
887 }
888 $UpdateModTime = TRUE;
889 }
890 break;
891
892 case MetadataSchema::MDFTYPE_FLAG:
893 if ($this->DBFields[$DBFieldName] != $NewValue)
894 {
895 # save value directly to DB
896 if (is_null($NewValue))
897 {
898 $DB->Query("UPDATE Resources SET `"
899 .$DBFieldName."` = NULL"
900 ." WHERE ResourceId = ".$ResourceId);
901 }
902 else
903 {
904 $DB->Query("UPDATE Resources SET `"
905 .$DBFieldName."` = ".$NewValue
906 ." WHERE ResourceId = ".$ResourceId);
907 }
908
909 $this->DBFields[$DBFieldName] = $NewValue;
910
911 # recalculate counts for any associated classifications if necessary
912 if ($DBFieldName == "ReleaseFlag")
913 {
914 $DB->Query("SELECT ClassificationId FROM ResourceClassInts"
915 ." WHERE ResourceId = ".$ResourceId);
916 while ($ClassId = $DB->FetchField("ClassificationId"))
917 {
918 $Class = new Classification($ClassId);
919 $Class->RecalcResourceCount();
920 }
921 }
922 $UpdateModTime = TRUE;
923 }
924 break;
925
926 case MetadataSchema::MDFTYPE_USER:
927 # if value passed in was object
928 if (is_object($NewValue))
929 {
930 # retrieve user ID from object
931 $UserId = $NewValue->Get("UserId");
932 }
933 # else if value passed in was user name
934 elseif (is_string($NewValue) && strlen($NewValue))
935 {
936 # create user object and retrieve user ID from there
937 $User = new User($this->DB, $NewValue);
938 $UserId = $User->Get("UserId");
939 }
940 else
941 {
942 # assume value is user ID and use value directly
943 $UserId = $NewValue;
944 }
945
946 if ($this->DBFields[$DBFieldName] != $UserId)
947 {
948 # save value directly to DB
949 $DB->Query("UPDATE Resources SET `"
950 .$DBFieldName."` = '".$UserId."' "
951 ."WHERE ResourceId = ".$ResourceId);
952
953 # save value locally
954 $this->DBFields[$DBFieldName] = $UserId;
955 $UpdateModTime = TRUE;
956 }
957 break;
958
959 case MetadataSchema::MDFTYPE_DATE:
960 # if we were given a date object
961 if (is_object($NewValue))
962 {
963 # use supplied date object
964 $Date = $NewValue;
965 }
966 else
967 {
968 # create date object
969 $Date = new Date($NewValue);
970 }
971
972 $OldDate = new Date(
973 $this->DBFields[$DBFieldName."Begin"],
974 $this->DBFields[$DBFieldName."End"]);
975
976 if ($OldDate->BeginDate() != $Date->BeginDate() ||
977 $OldDate->EndDate() != $Date->EndDate() ||
978 $OldDate->Precision() != $Date->Precision() )
979 {
980 # extract values from date object and store in DB
981 $BeginDate = "'".$Date->BeginDate()."'";
982 if (strlen($BeginDate) < 3) { $BeginDate = "NULL"; }
983 $EndDate = "'".$Date->EndDate()."'";
984 if (strlen($EndDate) < 3) { $EndDate = "NULL"; }
985
986 $DB->Query("UPDATE Resources SET "
987 .$DBFieldName."Begin = ".$BeginDate.", "
988 .$DBFieldName."End = ".$EndDate.", "
989 .$DBFieldName."Precision = '".$Date->Precision()."' "
990 ."WHERE ResourceId = ".$ResourceId);
991
992 # save values locally
993 $this->DBFields[$DBFieldName."Begin"] = $Date->BeginDate();
994 $this->DBFields[$DBFieldName."End"] = $Date->EndDate();
995 $this->DBFields[$DBFieldName."Precision"] = $Date->Precision();
996 $UpdateModTime=TRUE;
997 }
998 break;
999
1000 case MetadataSchema::MDFTYPE_TIMESTAMP:
1001 if (is_null($NewValue))
1002 {
1003 $DateValue = $NewValue;
1004
1005 if (!is_null($this->DBFields[$DBFieldName]))
1006 {
1007 # save value directly to DB
1008 $DB->Query("UPDATE Resources SET "
1009 ."`".$DBFieldName."` = NULL "
1010 ."WHERE ResourceId = ".$ResourceId);
1011 $UpdateModTime = TRUE;
1012 }
1013 }
1014 else
1015 {
1016 # assume value is date and use directly
1017 $DateValue = date("Y-m-d H:i:s", strtotime($NewValue));
1018
1019 if ($this->DBFields[$DBFieldName] != $DateValue)
1020 {
1021 # save value directly to DB
1022 $DB->Query("UPDATE Resources SET "
1023 ."`".$DBFieldName."` = '".addslashes($DateValue)."' "
1024 ."WHERE ResourceId = ".$ResourceId);
1025 $UpdateModTime=TRUE;
1026 }
1027 }
1028
1029 # save value locally
1030 $this->DBFields[$DBFieldName] = $DateValue;
1031 break;
1032
1033 case MetadataSchema::MDFTYPE_TREE:
1034 $OldValue = $this->Get($Field);
1035
1036 # if incoming value is array
1037 if (is_array($NewValue))
1038 {
1039 if ($OldValue != $NewValue)
1040 {
1041 # for each element of array
1042 foreach ($NewValue as
1043 $ClassificationId => $ClassificationName)
1044 {
1045 $Class = new Classification($ClassificationId);
1046 if ($Class->Status() == Classification::CLASSSTAT_OK)
1047 {
1048 # associate with resource if not already associated
1049 $this->AddAssociation("ResourceClassInts",
1050 "ClassificationId",
1051 $ClassificationId);
1052 $Class->RecalcResourceCount();
1053 }
1054 }
1055 $UpdateModTime=TRUE;
1056 }
1057 }
1058 else
1059 {
1060 # associate with resource if not already associated
1061 if (is_object($NewValue))
1062 {
1063 $Class = $NewValue;
1064 $NewValue = $Class->Id();
1065 }
1066 else
1067 {
1068 $Class = new Classification($NewValue);
1069 }
1070
1071 if (!array_key_exists($Class->Id(), $OldValue))
1072 {
1073
1074 $this->AddAssociation("ResourceClassInts",
1075 "ClassificationId",
1076 $NewValue);
1077 $Class->RecalcResourceCount();
1078 $UpdateModTime=TRUE;
1079 }
1080 }
1081
1082 # clear our classification cache
1083 if ($UpdateModTime)
1084 {
1085 unset($this->ClassificationCache);
1086 }
1087 break;
1088
1089 case MetadataSchema::MDFTYPE_CONTROLLEDNAME:
1090 case MetadataSchema::MDFTYPE_OPTION:
1091 $OldValue = $this->Get($Field);
1092
1093 # Clear other values if this field expects unique options
1094 if ($Field->AllowMultiple() === FALSE)
1095 {
1096 # If we're fed an array for a unique option,
1097 # just use the last element of the array
1098 if (is_array($NewValue)){
1099 $NewValue = array_pop($NewValue);
1100 }
1101
1102 if (!array_key_exists($NewValue, $OldValue))
1103 {
1104
1105 $this->RemoveAllAssociations("ResourceNameInts",
1106 "ControlledNameId",
1107 $Field );
1108 $UpdateModTime=TRUE;
1109 }
1110 }
1111
1112 # if incoming value is array
1113 if (is_array($NewValue) && ($Field->AllowMultiple() !== FALSE) )
1114 {
1115 if ($OldValue != $NewValue)
1116 {
1117 # for each element of array
1118 foreach ($NewValue as $ControlledNameId => $ControlledName)
1119 {
1120 # associate with resource if not already associated
1121 $this->AddAssociation("ResourceNameInts",
1122 "ControlledNameId",
1123 $ControlledNameId);
1124 }
1125 $UpdateModTime=TRUE;
1126 }
1127 }
1128 else
1129 {
1130 # associate with resource if not already associated
1131 if (is_object($NewValue)) { $NewValue = $NewValue->Id(); }
1132 if (!array_key_exists($NewValue, $OldValue))
1133 {
1134 $this->AddAssociation("ResourceNameInts",
1135 "ControlledNameId",
1136 $NewValue);
1137 $UpdateModTime=TRUE;
1138 }
1139 }
1140
1141 if ($UpdateModTime)
1142 {
1143 # clear our controlled name cache
1144 unset($this->ControlledNameCache);
1145 unset($this->ControlledNameVariantCache);
1146 }
1147
1148 break;
1149
1150 case MetadataSchema::MDFTYPE_IMAGE:
1151 # if we were given an image object
1152 if (is_object($NewValue))
1153 {
1154 # grab ID from object
1155 $ImageId = $NewValue->Id();
1156 }
1157 else
1158 {
1159 # assume incoming value is ID
1160 $ImageId = $NewValue;
1161 }
1162
1163 if ($this->DBFields[$DBFieldName] != $ImageId)
1164 {
1165 # store new image object ID in database
1166 $DB->Query("UPDATE Resources SET `"
1167 .$DBFieldName."` = '".$ImageId."'"
1168 ." WHERE ResourceId = ".$ResourceId);
1169 $UpdateModTime = TRUE;
1170 # save value locally
1171 $this->DBFields[$DBFieldName] = $ImageId;
1172 }
1173 break;
1174
1175 case MetadataSchema::MDFTYPE_FILE:
1176 # convert incoming value to array if necessary
1177 if (!is_array($NewValue)) { $NewValue = array($NewValue); }
1178
1179 # for each incoming file
1180 $Factory = new FileFactory($Field->Id());
1181 foreach ($NewValue as $File)
1182 {
1183 # make copy of file
1184 $NewFile = $Factory->Copy($File);
1185
1186 # associate copy with this resource and field
1187 $NewFile->ResourceId($this->Id);
1188 $NewFile->FieldId($Field->Id());
1189 }
1190 # Since we make a fresh copy of the File whenever Set is called,
1191 # we'll always update the modification time for this field.
1192 $UpdateModTime = TRUE;
1193 break;
1194
1195 default:
1196 # ERROR OUT
1197 exit("<br>SPT - ERROR: attempt to set unknown resource field type<br>\n");
1198 break;
1199 }
1200
1201 if ($UpdateModTime && !$this->IsTempResource())
1202 {
1203 # update modification timestamps
1204 global $G_User;
1205 $UserId = $G_User->IsLoggedIn() ? $G_User->Get("UserId") : -1;
1206 $DB->Query("DELETE FROM ResourceFieldTimestamps "
1207 ."WHERE ResourceId=".$this->Id." AND "
1208 ."FieldId=".$Field->Id() );
1209 $DB->Query("INSERT INTO ResourceFieldTimestamps "
1210 ."(ResourceId,FieldId,ModifiedBy,Timestamp) VALUES ("
1211 .$this->Id.",".$Field->Id().","
1212 .$UserId.",NOW())");
1213 }
1214 }
1215 # (for backward compatibility)
1216 function SetByField($Field, $NewValue) { $this->Set($Field, $NewValue); }
1217
1218 # set value by field ID
1219 function SetByFieldId($FieldId, $NewValue)
1220 {
1221 $Field = $this->Schema->GetField($FieldId);
1222 $this->Set($Field, $NewValue);
1223 }
1224
1225 # set qualifier by field name
1226 function SetQualifier($FieldName, $NewValue)
1227 {
1228 $Field = $this->Schema->GetFieldByName($FieldName);
1229 $this->SetQualifierByField($Field, $NewValue);
1230 }
1231
1232 # set qualifier by field ID
1233 function SetQualifierByFieldId($FieldId, $NewValue)
1234 {
1235 $Field = $this->Schema->GetField($FieldId);
1236 $this->SetQualifierByField($Field, $NewValue);
1237 }
1238
1239 # set qualifier using field object
1240 function SetQualifierByField($Field, $NewValue)
1241 {
1242 # if field uses qualifiers and uses item-level qualifiers
1243 if ($Field->UsesQualifiers() && $Field->HasItemLevelQualifiers())
1244 {
1245 # if qualifier object passed in
1246 if (is_object($NewValue))
1247 {
1248 # grab qualifier ID from object
1249 $QualifierId = $NewValue->Id();
1250 }
1251 else
1252 {
1253 # assume value passed in is qualifier ID
1254 $QualifierId = $NewValue;
1255 }
1256
1257 # update qualifier value in database
1258 $DBFieldName = $Field->DBFieldName();
1259 $this->DB->Query("UPDATE Resources SET "
1260 .$DBFieldName."Qualifier = '".$QualifierId."' "
1261 ."WHERE ResourceId = ".$this->Id);
1262
1263 # update local qualifier value
1264 $this->DBFields[$DBFieldName."Qualifier"] = $QualifierId;
1265 }
1266 }
1267
1268 # clear value by field name
1269 function Clear($FieldName, $ValueToClear = NULL)
1270 {
1271 $Field = $this->Schema->GetFieldByName($FieldName);
1272 $this->ClearByField($Field, $ValueToClear);
1273 }
1274
1275 # clear value by field ID
1276 function ClearByFieldId($FieldId, $ValueToClear = NULL)
1277 {
1278 $Field = $this->Schema->GetField($FieldId);
1279 $this->ClearByField($Field, $ValueToClear);
1280 }
1281
1282 # clear value using field object
1283 function ClearByField($Field, $ValueToClear = NULL)
1284 {
1285 # grab commonly-used values for local use
1286 $DB = $this->DB;
1287 $ResourceId = $this->Id;
1288
1289 # grab database field name
1290 $DBFieldName = $Field->DBFieldName();
1291
1292 $UpdateModTime=FALSE;
1293
1294 # store value in DB based on field type
1295 switch ($Field->Type())
1296 {
1297 case MetadataSchema::MDFTYPE_TEXT:
1298 case MetadataSchema::MDFTYPE_PARAGRAPH:
1299 case MetadataSchema::MDFTYPE_NUMBER:
1300 case MetadataSchema::MDFTYPE_FLAG:
1301 case MetadataSchema::MDFTYPE_USER:
1302 case MetadataSchema::MDFTYPE_TIMESTAMP:
1303 case MetadataSchema::MDFTYPE_URL:
1304 if (strlen($this->DBFields[$DBFieldName])>0)
1305 {
1306 # clear value in DB
1307 $DB->Query("UPDATE Resources SET `"
1308 .$DBFieldName."` = '' "
1309 ."WHERE ResourceId = ".$ResourceId);
1310
1311 # clear value locally
1312 $this->DBFields[$DBFieldName] = NULL;
1313 $UpdateModTime=TRUE;
1314 }
1315 break;
1316
1317 case MetadataSchema::MDFTYPE_POINT:
1318 if (!is_null($this->DBFields[$DBFieldName."X"]) ||
1319 !is_null($this->DBFields[$DBFieldName."Y"]) )
1320 {
1321 # Clear DB Values
1322 $DB->Query("UPDATE Resources SET "
1323 ."`".$DBFieldName."X` = NULL ,"
1324 ."`".$DBFieldName."Y` = NULL "
1325 ."WHERE ResourceId = ".$ResourceId);
1326
1327 # Clear local values
1328 $this->DBFields[$DBFieldName."X"] = NULL;
1329 $this->DBFields[$DBFieldName."Y"] = NULL;
1330 $UpdateModTime=TRUE;
1331 }
1332 break;
1333
1334 case MetadataSchema::MDFTYPE_DATE:
1335 if (!is_null($this->DBFields[$DBFieldName."Begin"]) ||
1336 !is_null($this->DBFields[$DBFieldName."End"]) ||
1337 !is_null($this->DBFields[$DBFieldName."Precision"]))
1338 {
1339 # clear date object values in DB
1340 $DB->Query("UPDATE Resources SET "
1341 .$DBFieldName."Begin = '', "
1342 .$DBFieldName."End = '', "
1343 .$DBFieldName."Precision = '' "
1344 ."WHERE ResourceId = ".$ResourceId);
1345
1346 # clear value locally
1347 $this->DBFields[$DBFieldName."Begin"] = NULL;
1348 $this->DBFields[$DBFieldName."End"] = NULL;
1349 $this->DBFields[$DBFieldName."Precision"] = NULL;
1350 $UpdateModTime=TRUE;
1351 }
1352 break;
1353
1354 case MetadataSchema::MDFTYPE_TREE:
1355 $OldValue = $this->Get($Field);
1356
1357 # if value to clear supplied
1358 if ($ValueToClear !== NULL)
1359 {
1360 # if supplied value is array
1361 if (is_array($ValueToClear))
1362 {
1363 # for each element of array
1364 foreach ($ValueToClear as $ClassificationId => $Dummy)
1365 {
1366 if (array_key_exists($ClassificationId, $OldValue))
1367 {
1368 # remove association with resource (if any)
1369 $this->RemoveAssociation("ResourceClassInts",
1370 "ClassificationId",
1371 $ClassificationId);
1372 $Class = new Classification($ClassificationId);
1373 $Class->RecalcResourceCount();
1374 $UpdateModTime=TRUE;
1375 }
1376 }
1377 }
1378 else
1379 {
1380 if (array_key_exists($ValueToClear, $OldValue))
1381 {
1382 # remove association with resource (if any)
1383 $this->RemoveAssociation("ResourceClassInts",
1384 "ClassificationId",
1385 $ValueToClear);
1386 $Class = new Classification($ValueToClear);
1387 $Class->RecalcResourceCount();
1388 $UpdateModTime=TRUE;
1389 }
1390 }
1391 }
1392 else
1393 {
1394 if (count($OldValue)>0)
1395 {
1396 # remove all associations for resource and field
1397 $this->RemoveAllAssociations("ResourceClassInts", "ClassificationId", $Field);
1398
1399 # recompute resource count
1400 $Values = $this->Get($Field);
1401 foreach ($Values as $ClassificationId => $Dummy)
1402 {
1403 $Class = new Classification($ClassificationId);
1404 $Class->RecalcResourceCount();
1405 }
1406 $UpdateModTime=TRUE;
1407 }
1408 }
1409
1410 # clear our classification cache
1411 if ($UpdateModTime)
1412 {
1413 unset($this->ClassificationCache);
1414 }
1415 break;
1416
1417 case MetadataSchema::MDFTYPE_CONTROLLEDNAME:
1418 case MetadataSchema::MDFTYPE_OPTION:
1419 $OldValue = $this->Get($Field);
1420 # if value to clear supplied
1421 if ($ValueToClear !== NULL)
1422 {
1423 # if incoming value is array
1424 if (is_array($ValueToClear))
1425 {
1426 # for each element of array
1427 foreach ($ValueToClear as $ControlledNameId =>
1428 $ControlledName)
1429 {
1430 if (array_key_exists($ControlledNameId, $OldValue))
1431 {
1432 # remove association with resource (if any)
1433 $this->RemoveAssociation("ResourceNameInts",
1434 "ControlledNameId",
1435 $ControlledNameId);
1436 $UpdateModTime=TRUE;
1437 }
1438 }
1439 }
1440 else
1441 {
1442 if (array_key_exists($ValueToClear, $OldValue))
1443 {
1444 # remove association with resource (if any)
1445 $this->RemoveAssociation("ResourceNameInts",
1446 "ControlledNameId",
1447 $ValueToClear);
1448 $UpdateModTime=TRUE;
1449 }
1450 }
1451 }
1452 else
1453 {
1454 if (count($OldValue)>0)
1455 {
1456 # remove all associations for resource and field
1457 $this->RemoveAllAssociations("ResourceNameInts", "ControlledNameId", $Field);
1458 $UpdateModTime=TRUE;
1459 }
1460 }
1461
1462 if ($UpdateModTime)
1463 {
1464 # clear our controlled name cache
1465 unset($this->ControlledNameCache);
1466 unset($this->ControlledNameVariantCache);
1467 }
1468 break;
1469
1470 case MetadataSchema::MDFTYPE_IMAGE:
1471 # delete image if no other resources are using it
1472 $ImageId = $DB->Query("SELECT `".$DBFieldName
1473 ."` FROM Resources WHERE ResourceId = ".$ResourceId,
1474 $DBFieldName);
1475 if ($ImageId > 0)
1476 {
1477 $ImageCount = $DB->Query("SELECT COUNT(*) AS ImageCount FROM Resources"
1478 ." WHERE `".$DBFieldName."` = ".$ImageId,
1479 "ImageCount");
1480 if ($ImageCount < 2)
1481 {
1482 $Image = new SPTImage($ImageId);
1483 $Image->Delete();
1484 }
1485 $UpdateModTime=TRUE;
1486 }
1487
1488 # clear stored ID
1489 $DB->Query("UPDATE Resources SET `"
1490 .$DBFieldName."` = '' "
1491 ."WHERE ResourceId = ".$ResourceId);
1492
1493 # clear value locally
1494 $this->DBFields[$DBFieldName] = NULL;
1495 break;
1496
1497 case MetadataSchema::MDFTYPE_FILE:
1498 # get array of Files associated with this resource
1499 $Files = $this->Get($Field, TRUE);
1500
1501 if (count($Files)>0){
1502 # for each File
1503 foreach ($Files as $File)
1504 {
1505 # delete file
1506 $File->Delete();
1507 }
1508 $UpdateModTime=TRUE;
1509 }
1510 break;
1511
1512 default:
1513 # ERROR OUT
1514 exit("<br>SPT - ERROR: attempt to clear unknown resource field type<br>\n");
1515 break;
1516 }
1517
1518 if ($UpdateModTime && !$this->IsTempResource())
1519 {
1520 # update modification timestamps
1521 global $G_User;
1522 $UserId = $G_User->IsLoggedIn() ? $G_User->Get("UserId") : -1;
1523 $DB->Query("DELETE FROM ResourceFieldTimestamps "
1524 ."WHERE ResourceId=".$this->Id." AND "
1525 ."FieldId=".$Field->Id() );
1526 $DB->Query("INSERT INTO ResourceFieldTimestamps "
1527 ."(ResourceId,FieldId,ModifiedBy,Timestamp) VALUES ("
1528 .$this->Id.",".$Field->Id().","
1529 .$UserId.",NOW())");
1530 }
1531 }
1532
1533
1534 # --- Field-Specific or Type-Specific Attribute Retrieval Methods -------
1535
1536 # return 2D array of classifications associated with resource
1537 # (first index is classification (field) name, second index is classification ID)
1538 function Classifications()
1539 {
1540 $DB = $this->DB;
1541
1542 # start with empty array
1543 $Names = array();
1544
1545 # for each controlled name
1546 $DB->Query("SELECT ClassificationName, MetadataFields.FieldName, "
1547 ."ResourceClassInts.ClassificationId FROM ResourceClassInts, "
1548 ."Classifications, MetadataFields "
1549 ."WHERE ResourceClassInts.ResourceId = ".$this->Id." "
1550 ."AND ResourceClassInts.ClassificationId = Classifications.ClassificationId "
1551 ."AND Classifications.FieldId = MetadataFields.FieldId ");
1552 while ($Record = $DB->FetchRow())
1553 {
1554 # add name to array
1555 $Names[$Record["FieldName"]][$Record["ClassificationId"]] =
1556 $Record["ClassificationName"];
1557 }
1558
1559 # return array to caller
1560 return $Names;
1561 }
1562
1563
1564 # --- Ratings Methods ---------------------------------------------------
1565
1566 # return cumulative rating (range is usually 0-100)
1567 function CumulativeRating() { return $this->CumulativeRating; }
1568
1569 # return cumulative rating scaled to 1/10th (range is usually 0-10)
1570 function ScaledCumulativeRating()
1571 {
1572 if ($this->CumulativeRating == NULL)
1573 {
1574 return NULL;
1575 }
1576 else
1577 {
1578 return intval(($this->CumulativeRating + 5) / 10);
1579 }
1580 }
1581
1582 # return current number of ratings for resource
1583 function NumberOfRatings()
1584 {
1585 # if number of ratings not already set
1586 if (!isset($this->NumberOfRatings))
1587 {
1588 # obtain number of ratings
1589 $this->NumberOfRatings =
1590 $this->DB->Query("SELECT Count(*) AS NumberOfRatings "
1591 ."FROM ResourceRatings "
1592 ."WHERE ResourceId = ".$this->Id,
1593 "NumberOfRatings"
1594 );
1595
1596 # recalculate cumulative rating if it looks erroneous
1597 if (($this->NumberOfRatings > 0) && !$this->CumulativeRating())
1598 {
1599 $this->UpdateCumulativeRating();
1600 }
1601 }
1602
1603 # return number of ratings to caller
1604 return $this->NumberOfRatings;
1605 }
1606
1607 # update individual rating for resource
1608 function Rating($NewRating = NULL, $UserId = NULL)
1609 {
1610 $DB = $this->DB;
1611
1612 # if user ID not supplied
1613 if ($UserId == NULL)
1614 {
1615 # if user is logged in
1616 global $User;
1617 if ($User->IsLoggedIn())
1618 {
1619 # use ID of current user
1620 $UserId = $User->Get("UserId");
1621 }
1622 else
1623 {
1624 # return NULL to caller
1625 return NULL;
1626 }
1627 }
1628
1629 # sanitize $NewRating
1630 if (!is_null($NewRating))
1631 {
1632 $NewRating = intval($NewRating);
1633 }
1634
1635 # if there is a rating for resource and user
1636 $DB->Query("SELECT Rating FROM ResourceRatings "
1637 ."WHERE UserId = ${UserId} AND ResourceId = ".$this->Id);
1638 if ($Record = $DB->FetchRow())
1639 {
1640 # if new rating was supplied
1641 if ($NewRating != NULL)
1642 {
1643 # update existing rating
1644 $DB->Query("UPDATE ResourceRatings "
1645 ."SET Rating = ${NewRating}, DateRated = NOW() "
1646 ."WHERE UserId = ${UserId} AND ResourceId = ".$this->Id);
1647
1648 # update cumulative rating value
1649 $this->UpdateCumulativeRating();
1650
1651 # return value is new rating
1652 $Rating = $NewRating;
1653 }
1654 else
1655 {
1656 # get rating value to return to caller
1657 $Rating = $Record["Rating"];
1658 }
1659 }
1660 else
1661 {
1662 # if new rating was supplied
1663 if ($NewRating != NULL)
1664 {
1665 # add new rating
1666 $DB->Query("INSERT INTO ResourceRatings "
1667 ."(ResourceId, UserId, DateRated, Rating) "
1668 ."VALUES ("
1669 .$this->Id.", "
1670 ."${UserId}, "
1671 ."NOW(), "
1672 ."${NewRating})");
1673
1674 # update cumulative rating value
1675 $this->UpdateCumulativeRating();
1676
1677 # return value is new rating
1678 $Rating = $NewRating;
1679 }
1680 else
1681 {
1682 # return value is NULL
1683 $Rating = NULL;
1684 }
1685 }
1686
1687 # return rating value to caller
1688 return $Rating;
1689 }
1690
1691
1692 # --- Resource Comment Methods ------------------------------------------
1693
1694 # return comments as array of Message objects
1695 function Comments()
1696 {
1697 # read in comments if not already loaded
1698 if (!isset($this->Comments))
1699 {
1700 $this->DB->Query("SELECT MessageId FROM Messages "
1701 ."WHERE ParentId = ".$this->Id
1702 ." AND ParentType = 2 "
1703 ."ORDER BY DatePosted DESC");
1704 while ($MessageId = $this->DB->FetchField("MessageId"))
1705 {
1706 $this->Comments[] = new Message($MessageId);
1707 }
1708 }
1709
1710 # return array of comments to caller
1711 return $this->Comments;
1712 }
1713
1714 # return current number of comments
1715 function NumberOfComments()
1716 {
1717 # obtain number of comments if not already set
1718 if (!isset($this->NumberOfComments))
1719 {
1720 $this->NumberOfComments =
1721 $this->DB->Query("SELECT Count(*) AS NumberOfComments "
1722 ."FROM Messages "
1723 ."WHERE ParentId = ".$this->Id
1724 ." AND ParentType = 2",
1725 "NumberOfComments"
1726 );
1727 }
1728
1729 # return number of comments to caller
1730 return $this->NumberOfComments;
1731 }
1732
1733
1734 # --- Permission Methods -------------------------------------------------
1735
1743 public function UserCanView(User $User)
1744 {
1745 $CanView = TRUE;
1746
1747 # cannot view a resource that is not valid
1748 if ($this->Status() !== 1)
1749 {
1750 $CanView = FALSE;
1751 }
1752
1753 # check authorization if the resource has not been released
1754 else if (!$this->Get("Release Flag"))
1755 {
1756 $IsCreator = $User->Name() == $this->Get("Added By Id");
1757
1758 # if the user is not a personal resource admin or is not the creator
1759 # then an additional admin check is necessary
1760 if (!$User->HasPriv(PRIV_MYRESOURCEADMIN) || !$IsCreator)
1761 {
1762 # if the user is not a resource admin or release admin
1763 if (!$User->HasPriv(PRIV_RESOURCEADMIN, PRIV_RELEASEADMIN))
1764 {
1765 $CanView = FALSE;
1766 }
1767 }
1768 }
1769
1770 # allow plugins to modify result of the permission check
1771 $SignalResult = $GLOBALS["AF"]->SignalEvent(
1772 "EVENT_RESOURCE_VIEW_PERMISSION_CHECK",
1773 array("Resource" => $this, "User" => $User, "CanView" => $CanView));
1774 $CanView = $SignalResult["CanView"];
1775
1776 return $CanView;
1777 }
1778
1779 # return whether user can edit this resource
1780 function UserCanEdit($User)
1781 {
1782 # determine whether user can edit resource
1783 if ($User->HasPriv(PRIV_RESOURCEADMIN)
1784 || $User->HasPriv(PRIV_RELEASEADMIN)
1785 || ($User->HasPriv(PRIV_MYRESOURCEADMIN)
1786 && ($User->Id() == $this->DBFields["AddedById"])))
1787 {
1788 $CanEdit = TRUE;
1789 }
1790 else
1791 {
1792 $CanEdit = FALSE;
1793
1794 $Fields = $this->Schema->GetFields();
1795 foreach ($Fields as $Field)
1796 {
1797 $CanEdit |= $this->UserCanEditField($User, $Field);
1798 }
1799
1800 }
1801
1802 # allow plugins to modify result of permission check
1803 $SignalResult = $GLOBALS["AF"]->SignalEvent(
1804 "EVENT_RESOURCE_EDIT_PERMISSION_CHECK", array(
1805 "Resource" => $this,
1806 "User" => $User,
1807 "CanEdit" => $CanEdit));
1808 $CanEdit = $SignalResult["CanEdit"];
1809
1810 # report back to caller whether user can edit field
1811 return $CanEdit;
1812 }
1813
1820 function UserCanViewField($User, $FieldOrFieldName)
1821 {
1822 # get field object (if not supplied)
1823 if (is_object($FieldOrFieldName)
1824 && ($FieldOrFieldName instanceof MetadataField))
1825 {
1826 $Field = $FieldOrFieldName;
1827 }
1828 elseif (strlen(trim($FieldOrFieldName)))
1829 {
1830 if ($this->Schema->FieldExists($FieldOrFieldName))
1831 {
1832 $Field = $this->Schema->GetFieldByName($FieldOrFieldName);
1833 }
1834 }
1835
1836 # field absolutely cannot be viewed if it is not valid
1837 if (!isset($Field)) { return FALSE; }
1838
1839 # field should not be viewed if it is disabled
1840 if (!$Field->Enabled())
1841 {
1842 $CanView = FALSE;
1843 }
1844 else
1845 {
1846 # if the user can edit the field they can also view it
1847 if ($this->UserCanEditField($User, $Field))
1848 {
1849 $CanView = TRUE;
1850 }
1851 else
1852 {
1853 # assume user can view field
1854 $CanView = TRUE;
1855
1856 # check viewing privilege constraint
1857 if ($Field->ViewingPrivilege() != 0)
1858 {
1859 if (!$User->HasPriv($Field->ViewingPrivilege()))
1860 {
1861 $CanView = FALSE;
1862 }
1863 }
1864
1865 # check "user is value of field" constraint
1866 $UserIsLogic = $Field->ViewingUserIsValue();
1867 if ($UserIsLogic != MetadataField::USERISVALUE_UNSET)
1868 {
1869 $ValueToCheck = $this->Get($this->Schema->GetField(
1870 $Field->ViewingUserValue()));
1871 $UserName = $User->Name();
1872 if (($UserIsLogic == MetadataField::USERISVALUE_AND) && $CanView)
1873 {
1874 if (!$UserName || ($ValueToCheck != $UserName))
1875 {
1876 $CanView = FALSE;
1877 }
1878 }
1879 elseif ($UserIsLogic == MetadataField::USERISVALUE_OR)
1880 {
1881 if (!$UserName && ($ValueToCheck != $UserName))
1882 {
1883 $CanView = TRUE;
1884 }
1885 }
1886 }
1887 }
1888 }
1889
1890 # allow plugins to modify result of permission check
1891 $SignalResult = $GLOBALS["AF"]->SignalEvent(
1892 "EVENT_FIELD_VIEW_PERMISSION_CHECK", array(
1893 "Field" => $Field,
1894 "Resource" => $this,
1895 "User" => $User,
1896 "CanView" => $CanView));
1897 $CanView = $SignalResult["CanView"];
1898
1899 # report back to caller whether user can view field
1900 return $CanView;
1901 }
1902
1909 function UserCanAuthorField($User, $FieldOrFieldName)
1910 {
1911 # fields hard-coded to not be authorable
1912 $UnauthorableFields = array(
1913 "Cumulative Rating",
1914 "Date Of Record Creation",
1915 "Date Of Record Release",
1916 "Date Last Modified",
1917 "Last Modified By Id");
1918
1919 # get field object (if not supplied)
1920 if (is_object($FieldOrFieldName)
1921 && ($FieldOrFieldName instanceof MetadataField))
1922 {
1923 $Field = $FieldOrFieldName;
1924 }
1925 elseif (strlen(trim($FieldOrFieldName)))
1926 {
1927 if ($this->Schema->FieldExists($FieldOrFieldName))
1928 {
1929 $Field = $this->Schema->GetFieldByName($FieldOrFieldName);
1930 }
1931 }
1932
1933 # field absolutely cannot be authored if it is not valid
1934 if (!isset($Field)) { return FALSE; }
1935
1936 # field should not be authored if it is disabled or is on "no author" list
1937 # or user is not the resource creator
1938 if (!$Field->Enabled()
1939 || in_array($Field->Name(), $UnauthorableFields)
1940 || ($User->Name() != $this->Get("Added By Id")))
1941 {
1942 $CanAuthor = FALSE;
1943 }
1944 else
1945 {
1946 # assume user can author field
1947 $CanAuthor = TRUE;
1948
1949 # check authoring privilege constraint
1950 if ($Field->AuthoringPrivilege() != 0)
1951 {
1952 if (!$User->HasPriv($Field->AuthoringPrivilege()))
1953 {
1954 $CanAuthor = FALSE;
1955 }
1956 }
1957
1958 # check "user is value of field" constraint
1959 $UserIsLogic = $Field->AuthoringUserIsValue();
1960 if ($UserIsLogic != MetadataField::USERISVALUE_UNSET)
1961 {
1962 $ValueToCheck = $this->Get($this->Schema->GetField(
1963 $Field->AuthoringUserValue()));
1964 $UserName = $User->Name();
1965 if (($UserIsLogic == MetadataField::USERISVALUE_AND) && $CanAuthor)
1966 {
1967 if (!$UserName || ($ValueToCheck != $UserName))
1968 {
1969 $CanAuthor = FALSE;
1970 }
1971 }
1972 elseif ($UserIsLogic == MetadataField::USERISVALUE_OR)
1973 {
1974 if (!$UserName && ($ValueToCheck != $UserName))
1975 {
1976 $CanAuthor = TRUE;
1977 }
1978 }
1979 }
1980 }
1981
1982 # allow plugins to modify result of permission check
1983 $SignalResult = $GLOBALS["AF"]->SignalEvent(
1984 "EVENT_FIELD_AUTHOR_PERMISSION_CHECK", array(
1985 "Field" => $Field,
1986 "Resource" => $this,
1987 "User" => $User,
1988 "CanAuthor" => $CanAuthor));
1989 $CanAuthor = $SignalResult["CanAuthor"];
1990
1991 # report back to caller whether user can author field
1992 return $CanAuthor;
1993 }
1994
2001 function UserCanEditField($User, $FieldOrFieldName)
2002 {
2003 # fields hard-coded to not be editable
2004 $UneditableFields = array(
2005 "Cumulative Rating",
2006 "Date Of Record Creation",
2007 "Date Of Record Release",
2008 "Date Last Modified",
2009 "Last Modified By Id");
2010
2011 # get field object (if not supplied)
2012 if (is_object($FieldOrFieldName)
2013 && ($FieldOrFieldName instanceof MetadataField))
2014 {
2015 $Field = $FieldOrFieldName;
2016 }
2017 elseif (strlen(trim($FieldOrFieldName)))
2018 {
2019 if ($this->Schema->FieldExists($FieldOrFieldName))
2020 {
2021 $Field = $this->Schema->GetFieldByName($FieldOrFieldName);
2022 }
2023 }
2024
2025 # field absolutely cannot be edited if it is not valid
2026 if (!isset($Field)) { return FALSE; }
2027
2028 # field should not be edited if it is disabled or is on "no edit" list
2029 if (!$Field->Enabled() || in_array($Field->Name(), $UneditableFields))
2030 {
2031 $CanEdit = FALSE;
2032 }
2033 else
2034 {
2035 # if the user can author the field they can also edit it
2036 if ($this->UserCanAuthorField($User, $Field))
2037 {
2038 $CanEdit = TRUE;
2039 }
2040 else
2041 {
2042 # assume user can edit field
2043 $CanEdit = TRUE;
2044
2045 # check editing privilege constraint
2046 if ($Field->EditingPrivilege() != 0)
2047 {
2048 if (!$User->HasPriv($Field->EditingPrivilege()))
2049 {
2050 $CanEdit = FALSE;
2051 }
2052 }
2053
2054 # check "user is value of field" constraint
2055 $UserIsLogic = $Field->EditingUserIsValue();
2056 if ($UserIsLogic != MetadataField::USERISVALUE_UNSET)
2057 {
2058 $ValueToCheck = $this->Get($this->Schema->GetField(
2059 $Field->EditingUserValue()));
2060 $UserName = $User->Name();
2061 if (($UserIsLogic == MetadataField::USERISVALUE_AND) && $CanEdit)
2062 {
2063 if (!$UserName || ($ValueToCheck != $UserName))
2064 {
2065 $CanEdit = FALSE;
2066 }
2067 }
2068 elseif ($UserIsLogic == MetadataField::USERISVALUE_OR)
2069 {
2070 if (!$UserName && ($ValueToCheck != $UserName))
2071 {
2072 $CanEdit = TRUE;
2073 }
2074 }
2075 }
2076 }
2077 }
2078
2079 # allow plugins to modify result of permission check
2080 $SignalResult = $GLOBALS["AF"]->SignalEvent(
2081 "EVENT_FIELD_EDIT_PERMISSION_CHECK", array(
2082 "Field" => $Field,
2083 "Resource" => $this,
2084 "User" => $User,
2085 "CanEdit" => $CanEdit));
2086 $CanEdit = $SignalResult["CanEdit"];
2087
2088 # report back to caller whether user can edit field
2089 return $CanEdit;
2090 }
2091
2092 # ---- PRIVATE INTERFACE -------------------------------------------------
2093
2094 private $DB;
2095 private $Schema;
2096 private $DBFields;
2097 private $Id;
2098 private $NumberOfRatings;
2099 private $CumulativeRating;
2100 private $NumberOfComments;
2101 private $Comments;
2102 private $LastStatus;
2103 private $ControlledNameCache;
2104 private $ControlledNameVariantCache;
2105 private $ClassificationCache;
2106
2107 # recalculate and save cumulative rating value for resource
2108 private function UpdateCumulativeRating()
2109 {
2110 # grab totals from DB
2111 $this->DB->Query("SELECT COUNT(Rating) AS Count, "
2112 ."SUM(Rating) AS Total FROM ResourceRatings "
2113 ."WHERE ResourceId = ".$this->Id);
2114 $Record = $this->DB->FetchRow();
2115
2116 # calculate new cumulative rating
2117 $this->CumulativeRating = round($Record["Total"] / $Record["Count"]);
2118
2119 # save new cumulative rating in DB
2120 $this->DB->Query("UPDATE Resources "
2121 ."SET CumulativeRating = ".$this->CumulativeRating." "
2122 ."WHERE ResourceId = ".$this->Id);
2123 }
2124
2125 # add intersection if not already present
2126 private function AddAssociation($TableName, $TargetFieldName, $TargetValue)
2127 {
2128 # if target not already associated with resource
2129 if ($this->DB->Query("SELECT COUNT(*) AS RecordCount FROM ".$TableName
2130 ." WHERE ResourceId = ".$this->Id
2131 ." AND ".$TargetFieldName." = '".$TargetValue."'",
2132 "RecordCount") == 0)
2133 {
2134 # associate target with resource
2135 $this->DB->Query("INSERT INTO ".$TableName." SET"
2136 ." ResourceId = ".$this->Id
2137 .", ".$TargetFieldName." = '".$TargetValue."'");
2138 }
2139 }
2140
2141 # remove intersections (if any)
2142 private function RemoveAssociation($TableName, $TargetFieldName, $TargetValue)
2143 {
2144 # remove any intersections with target ID from DB
2145 $this->DB->Query("DELETE FROM ".$TableName
2146 ." WHERE ResourceId = ".$this->Id
2147 ." AND ".$TargetFieldName." = '".$TargetValue."'");
2148 }
2149
2150 # remove all intersections for resource and field (if any)
2151 private function RemoveAllAssociations($TableName, $TargetFieldName, $Field)
2152 {
2153 # retrieve list of entries for this field and resource
2154 $Entries = $this->Get($Field);
2155
2156 # for each entry
2157 foreach ($Entries as $EntryId => $EntryName)
2158 {
2159 # remove intersection
2160 $this->RemoveAssociation($TableName, $TargetFieldName, $EntryId);
2161 }
2162 }
2163 }