CWIS Developer Documentation
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
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(
110  foreach ($TimestampFields as $Field)
111  {
112  if ($Field->UpdateMethod() ==
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  {
347  if (isset($this->DBFields[$DBFieldName]))
348  {
349  $ReturnValue = $this->DBFields[$DBFieldName];
350  }
351  else
352  {
353  $ReturnValue = NULL;
354  }
355  break;
356 
358  $ReturnValue = array("X" => $this->DBFields[$DBFieldName."X"],
359  "Y" => $this->DBFields[$DBFieldName."Y"]);
360  break;
361 
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 
377  $ReturnValue = $this->DBFields[$DBFieldName];
378  break;
379 
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 
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 
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 
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 
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 $this->GetQualifierByField($Field, $ReturnObject);
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  {
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  {
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 
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 
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 
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 
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))
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 
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 
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 
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 
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 
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 
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) { return $this->Set($Field, $NewValue); }
1217 
1218  # set value by field ID
1219  function SetByFieldId($FieldId, $NewValue)
1220  {
1221  $Field = $this->Schema->GetField($FieldId);
1222  return $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  return $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  return $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  return $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  return $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  {
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 
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 
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 
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 
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 
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 
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)
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  $ValueToCheck = $this->Get($this->Schema->GetField(
1868  $Field->ViewingUserValue()));
1869  if (($UserIsLogic == MetadataField::USERISVALUE_AND) && $CanView)
1870  {
1871  if ($ValueToCheck != $User->Name())
1872  {
1873  $CanView = FALSE;
1874  }
1875  }
1876  elseif ($UserIsLogic == MetadataField::USERISVALUE_OR)
1877  {
1878  if ($ValueToCheck == $User->Name())
1879  {
1880  $CanView = TRUE;
1881  }
1882  }
1883  }
1884  }
1885 
1886  # allow plugins to modify result of permission check
1887  $SignalResult = $GLOBALS["AF"]->SignalEvent(
1888  "EVENT_FIELD_VIEW_PERMISSION_CHECK", array(
1889  "Field" => $Field,
1890  "Resource" => $this,
1891  "User" => $User,
1892  "CanView" => $CanView));
1893  $CanView = $SignalResult["CanView"];
1894 
1895  # report back to caller whether user can view field
1896  return $CanView;
1897  }
1898 
1905  function UserCanAuthorField($User, $FieldOrFieldName)
1906  {
1907  # fields hard-coded to not be authorable
1908  $UnauthorableFields = array(
1909  "Cumulative Rating",
1910  "Date Of Record Creation",
1911  "Date Of Record Release",
1912  "Date Last Modified",
1913  "Last Modified By Id");
1914 
1915  # get field object (if not supplied)
1916  if (is_object($FieldOrFieldName)
1917  && ($FieldOrFieldName instanceof MetadataField))
1918  {
1919  $Field = $FieldOrFieldName;
1920  }
1921  elseif (strlen(trim($FieldOrFieldName)))
1922  {
1923  if ($this->Schema->FieldExists($FieldOrFieldName))
1924  {
1925  $Field = $this->Schema->GetFieldByName($FieldOrFieldName);
1926  }
1927  }
1928 
1929  # field absolutely cannot be authored if it is not valid
1930  if (!isset($Field)) { return FALSE; }
1931 
1932  # field should not be authored if it is disabled or is on "no author" list
1933  # or user is not the resource creator
1934  if (!$Field->Enabled()
1935  || in_array($Field->Name(), $UnauthorableFields)
1936  || ($User->Name() != $this->Get("Added By Id")))
1937  {
1938  $CanAuthor = FALSE;
1939  }
1940  else
1941  {
1942  # assume user can author field
1943  $CanAuthor = TRUE;
1944 
1945  # check authoring privilege constraint
1946  if ($Field->AuthoringPrivilege() != 0)
1947  {
1948  if (!$User->HasPriv($Field->AuthoringPrivilege()))
1949  {
1950  $CanAuthor = FALSE;
1951  }
1952  }
1953 
1954  # check "user is value of field" constraint
1955  $UserIsLogic = $Field->AuthoringUserIsValue();
1956  $ValueToCheck = $this->Get($this->Schema->GetField(
1957  $Field->AuthoringUserValue()));
1958  if (($UserIsLogic == MetadataField::USERISVALUE_AND) && $CanAuthor)
1959  {
1960  if ($ValueToCheck != $User->Name())
1961  {
1962  $CanAuthor = FALSE;
1963  }
1964  }
1965  elseif ($UserIsLogic == MetadataField::USERISVALUE_OR)
1966  {
1967  if ($ValueToCheck == $User->Name())
1968  {
1969  $CanAuthor = TRUE;
1970  }
1971  }
1972  }
1973 
1974  # allow plugins to modify result of permission check
1975  $SignalResult = $GLOBALS["AF"]->SignalEvent(
1976  "EVENT_FIELD_AUTHOR_PERMISSION_CHECK", array(
1977  "Field" => $Field,
1978  "Resource" => $this,
1979  "User" => $User,
1980  "CanAuthor" => $CanAuthor));
1981  $CanAuthor = $SignalResult["CanAuthor"];
1982 
1983  # report back to caller whether user can author field
1984  return $CanAuthor;
1985  }
1986 
1993  function UserCanEditField($User, $FieldOrFieldName)
1994  {
1995  # fields hard-coded to not be editable
1996  $UneditableFields = array(
1997  "Cumulative Rating",
1998  "Date Of Record Creation",
1999  "Date Of Record Release",
2000  "Date Last Modified",
2001  "Last Modified By Id");
2002 
2003  # get field object (if not supplied)
2004  if (is_object($FieldOrFieldName)
2005  && ($FieldOrFieldName instanceof MetadataField))
2006  {
2007  $Field = $FieldOrFieldName;
2008  }
2009  elseif (strlen(trim($FieldOrFieldName)))
2010  {
2011  if ($this->Schema->FieldExists($FieldOrFieldName))
2012  {
2013  $Field = $this->Schema->GetFieldByName($FieldOrFieldName);
2014  }
2015  }
2016 
2017  # field absolutely cannot be edited if it is not valid
2018  if (!isset($Field)) { return FALSE; }
2019 
2020  # field should not be edited if it is disabled or is on "no edit" list
2021  if (!$Field->Enabled() || in_array($Field->Name(), $UneditableFields))
2022  {
2023  $CanEdit = FALSE;
2024  }
2025  else
2026  {
2027  # if the user can author the field they can also edit it
2028  if ($this->UserCanAuthorField($User, $Field))
2029  {
2030  $CanEdit = TRUE;
2031  }
2032  else
2033  {
2034  # assume user can edit field
2035  $CanEdit = TRUE;
2036 
2037  # check editing privilege constraint
2038  if ($Field->EditingPrivilege() != 0)
2039  {
2040  if (!$User->HasPriv($Field->EditingPrivilege()))
2041  {
2042  $CanEdit = FALSE;
2043  }
2044  }
2045 
2046  # check "user is value of field" constraint
2047  $UserIsLogic = $Field->EditingUserIsValue();
2048  $ValueToCheck = $this->Get($this->Schema->GetField(
2049  $Field->EditingUserValue()));
2050  if (($UserIsLogic == MetadataField::USERISVALUE_AND) && $CanEdit)
2051  {
2052  if ($ValueToCheck != $User->Name())
2053  {
2054  $CanEdit = FALSE;
2055  }
2056  }
2057  elseif ($UserIsLogic == MetadataField::USERISVALUE_OR)
2058  {
2059  if ($ValueToCheck == $User->Name())
2060  {
2061  $CanEdit = TRUE;
2062  }
2063  }
2064  }
2065  }
2066 
2067  # allow plugins to modify result of permission check
2068  $SignalResult = $GLOBALS["AF"]->SignalEvent(
2069  "EVENT_FIELD_EDIT_PERMISSION_CHECK", array(
2070  "Field" => $Field,
2071  "Resource" => $this,
2072  "User" => $User,
2073  "CanEdit" => $CanEdit));
2074  $CanEdit = $SignalResult["CanEdit"];
2075 
2076  # report back to caller whether user can edit field
2077  return $CanEdit;
2078  }
2079 
2080  # ---- PRIVATE INTERFACE -------------------------------------------------
2081 
2082  private $DB;
2083  private $Schema;
2084  private $DBFields;
2085  private $Id;
2086  private $NumberOfRatings;
2087  private $CumulativeRating;
2088  private $NumberOfComments;
2089  private $Comments;
2090  private $LastStatus;
2091  private $ControlledNameCache;
2092  private $ControlledNameVariantCache;
2093  private $ClassificationCache;
2094 
2095  # recalculate and save cumulative rating value for resource
2096  private function UpdateCumulativeRating()
2097  {
2098  # grab totals from DB
2099  $this->DB->Query("SELECT COUNT(Rating) AS Count, "
2100  ."SUM(Rating) AS Total FROM ResourceRatings "
2101  ."WHERE ResourceId = ".$this->Id);
2102  $Record = $this->DB->FetchRow();
2103 
2104  # calculate new cumulative rating
2105  $this->CumulativeRating = round($Record["Total"] / $Record["Count"]);
2106 
2107  # save new cumulative rating in DB
2108  $this->DB->Query("UPDATE Resources "
2109  ."SET CumulativeRating = ".$this->CumulativeRating." "
2110  ."WHERE ResourceId = ".$this->Id);
2111  }
2112 
2113  # add intersection if not already present
2114  private function AddAssociation($TableName, $TargetFieldName, $TargetValue)
2115  {
2116  # if target not already associated with resource
2117  if ($this->DB->Query("SELECT COUNT(*) AS RecordCount FROM ".$TableName
2118  ." WHERE ResourceId = ".$this->Id
2119  ." AND ".$TargetFieldName." = '".$TargetValue."'",
2120  "RecordCount") == 0)
2121  {
2122  # associate target with resource
2123  $this->DB->Query("INSERT INTO ".$TableName." SET"
2124  ." ResourceId = ".$this->Id
2125  .", ".$TargetFieldName." = '".$TargetValue."'");
2126  }
2127  }
2128 
2129  # remove intersections (if any)
2130  private function RemoveAssociation($TableName, $TargetFieldName, $TargetValue)
2131  {
2132  # remove any intersections with target ID from DB
2133  $this->DB->Query("DELETE FROM ".$TableName
2134  ." WHERE ResourceId = ".$this->Id
2135  ." AND ".$TargetFieldName." = '".$TargetValue."'");
2136  }
2137 
2138  # remove all intersections for resource and field (if any)
2139  private function RemoveAllAssociations($TableName, $TargetFieldName, $Field)
2140  {
2141  # retrieve list of entries for this field and resource
2142  $Entries = $this->Get($Field);
2143 
2144  # for each entry
2145  foreach ($Entries as $EntryId => $EntryName)
2146  {
2147  # remove intersection
2148  $this->RemoveAssociation($TableName, $TargetFieldName, $EntryId);
2149  }
2150  }
2151 }