CWIS Developer Documentation
ResourceFactory.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: ResourceFactory.php
4 #
5 # Part of the Collection Workflow Integration System (CWIS)
6 # Copyright 2011-2013 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu/cwis/
8 #
9 
14 {
15  # ---- PUBLIC INTERFACE --------------------------------------------------
16 
23  {
24  # save schema
25  $this->SchemaId = $SchemaId;
26  $this->Schema = new MetadataSchema($this->SchemaId);
27 
28  # set up item factory base class
29  parent::__construct("Resource", "Resources", "ResourceId", NULL, FALSE,
30  "SchemaId = ".intval($this->SchemaId));
31  }
32 
37  public function Schema()
38  {
39  return $this->Schema;
40  }
41 
47  public function DuplicateResource($ResourceId)
48  {
49  # check that resource to be duplicated exists
50  if (!Resource::ItemExists($ResourceId))
51  {
52  throw new InvalidArgumentException(
53  "No resource found with specified ID (".$ResourceId.").");
54  }
55 
56  # create new target resource
57  $DstResource = Resource::Create($this->SchemaId);
58 
59  # load up resource to duplicate
60  $SrcResource = new Resource($ResourceId);
61 
62  # for each metadata field
63  $Fields = $this->Schema->GetFields();
64  foreach ($Fields as $Field)
65  {
66  if ($Field->CopyOnResourceDuplication())
67  {
68  $NewValue = $SrcResource->GetByField($Field, TRUE);
69 
70  # clear default value from destination resource that is
71  # set when creating a new resource
72  $DstResource->ClearByField($Field);
73 
74  # copy value from source resource to destination resource
75  $DstResource->SetByField($Field, $NewValue);
76  }
77  }
78 
79  # return new resource to caller
80  return $DstResource;
81  }
82 
95  public function ImportResourcesFromXmlFile($FileName)
96  {
97  # open file
98  $In = new XMLReader();
99  $Result = $In->open($FileName);
100 
101  # throw exception if file could not be opened
102  if ($Result === FALSE)
103  {
104  throw new Exception("Unable to open file: ".$FileName);
105  }
106 
107  # load possible tag names
108  $PossibleTags = array();
109 
110  $Fields = $this->Schema->GetFields();
111  foreach ($Fields as $FieldId => $Field)
112  {
113  $NormalizedName = preg_replace(
114  "/[^A-Za-z0-9]/", "", $Field->Name());
115  $PossibleTags[$NormalizedName] = $Field;
116  }
117 
118  # arrays to hold ControlledName and Classification factories
119  $CNFacts = array();
120  $CFacts = array();
121 
122  # while XML left to read
123  $NewResourceIds = array();
124  while ($In->read())
125  {
126  # if new element
127  if ($In->nodeType == XMLReader::ELEMENT)
128  {
129  # if node indicates start of resource
130  if ($In->name === "Resource")
131  {
132  # if we already had a resource make it non-temporary
133  if (isset($Resource))
134  {
135  $Resource->IsTempResource(FALSE);
136  $NewResourceIds[] = $Resource->Id();
137  }
138 
139  # create a new resource
140  $Resource = Resource::Create($this->SchemaId);
141  }
142  # else if node is in list of possible tags
143  elseif (array_key_exists($In->name, $PossibleTags))
144  {
145  # if we have a current resource
146  if (isset($Resource))
147  {
148  # retrieve field and value
149  $DBFieldName = $In->name;
150  $Field = $PossibleTags[$DBFieldName];
151  $In->read();
152  $Value = $In->value;
153  $In->read();
154 
155  # set value in resource based on field type
156  switch ($Field->Type())
157  {
164  $Resource->Set($Field, $Value);
165  break;
166 
168  $Resource->Set($Field,
169  (strtoupper($Value) == "TRUE") ? TRUE : FALSE);
170  break;
171 
174  if (!isset($CNFacts[$Field->Id()]))
175  {
176  $CNFacts[$Field->Id()] = $Field->GetFactory();
177  }
178 
179  $CName = $CNFacts[$Field->Id()]->GetItemByName($Value);
180  if ($CName === NULL)
181  {
182  $CNFacts[$Field->Id()]->ClearCaches();
183  $CName = ControlledName::Create($Value, $Field->Id());
184  }
185  $Resource->Set($Field, $CName);
186  break;
187 
189  if (!isset($CFacts[$Field->Id()]))
190  {
191  $CFacts[$Field->Id()] = $Field->GetFactory();
192  }
193 
194  $Class = $CFacts[$Field->Id()]->GetItemByName($Value);
195  if ($Class === NULL)
196  {
197  $CFacts[$Field->Id()]->ClearCaches();
198  $Class = Classification::Create($Value, $Field->Id());
199  }
200  $Resource->Set($Field, $Class);
201  break;
202 
204  list($Point["X"], $Point["Y"]) = explode(",", $Value);
205  $Resource->Set($Field, $Point);
206  break;
207 
209  if (preg_match("/^[0-9]+\$/", $Value))
210  {
211  $Value = intval($Value);
212  }
213  $Resource->Set($Field, $Value);
214  break;
215 
219  break;
220 
221  default:
222  break;
223  }
224  }
225  }
226  }
227  }
228 
229  # make final resource (if any) non-temporary
230  if (isset($Resource))
231  {
232  $Resource->IsTempResource(FALSE);
233  $NewResourceIds[] = $Resource->Id();
234  }
235 
236  # close file
237  $In->close();
238 
239  # report to caller what resources were added
240  return $NewResourceIds;
241  }
242 
249  public function ClearQualifier($ObjectOrId, $NewObjectOrId = NULL)
250  {
251  # sanitize qualifier ID or retrieve from object
252  $QualifierId = is_object($ObjectOrId)
253  ? $ObjectOrId->Id() : intval($ObjectOrId);
254 
255  # if new qualifier passed in
256  if ($NewObjectOrId !== NULL)
257  {
258  # sanitize qualifier ID to change to or retrieve it from object
259  $NewQualifierIdVal = is_object($NewObjectOrId)
260  ? $NewObjectOrId->Id() : intval($NewObjectOrId);
261  }
262  else
263  {
264  # qualifier should be cleared
265  $NewQualifierIdVal = "NULL";
266  }
267 
268  # for each metadata field
269  $Fields = $this->Schema->GetFields();
270  foreach ($Fields as $Field)
271  {
272  # if field uses qualifiers and uses item-level qualifiers
273  $QualColName = $Field->DBFieldName()."Qualifier";
274  if ($Field->UsesQualifiers()
275  && $Field->HasItemLevelQualifiers()
276  && $this->DB->FieldExists("Resources", $QualColName))
277  {
278  # set all occurrences to new qualifier value
279  $this->DB->Query("UPDATE Resources"
280  ." SET ".$QualColName." = ".$NewQualifierIdVal.""
281  ." WHERE ".$QualColName." = '".$QualifierId."'"
282  ." AND SchemaId = ".intval($this->SchemaId));
283  }
284  }
285 
286  # clear or change qualifier association with controlled names
287  # (NOTE: this should probably be done in a controlled name factory object)
288  $this->DB->Query("UPDATE ControlledNames"
289  ." SET QualifierId = ".$NewQualifierIdVal
290  ." WHERE QualifierId = '".$QualifierId."'");
291 
292  # clear or change qualifier association with classifications
293  # (NOTE: this should probably be done in a classification factory object)
294  $this->DB->Query("UPDATE Classifications"
295  ." SET QualifierId = ".$NewQualifierIdVal
296  ." WHERE QualifierId = '".$QualifierId."'");
297  }
298 
303  public function GetRatedResourceCount()
304  {
305  return $this->DB->Query(
306  "SELECT COUNT(DISTINCT ResourceId) AS ResourceCount"
307  ." FROM ResourceRatings",
308  "ResourceCount");
309  }
310 
315  public function GetRatedResourceUserCount()
316  {
317  return $this->DB->Query(
318  "SELECT COUNT(DISTINCT UserId) AS UserCount"
319  ." FROM ResourceRatings",
320  "UserCount");
321  }
322 
333  $Count = 10, $Offset = 0, $MaxDaysToGoBack = 90)
334  {
335  # assume that no resources will be found
336  $Resources = array();
337 
338  # calculate cutoff date for resources
339  $CutoffDate = date("Y-m-d H:i:s", strtotime($MaxDaysToGoBack." days ago"));
340 
341  # query for resource IDs
342  $this->DB->Query("SELECT ResourceId FROM Resources WHERE"
343  ." DateOfRecordRelease > '".$CutoffDate."'"
344  ." AND ResourceId >= 0"
345  ." AND SchemaId = ".intval($this->SchemaId)
346  ." ORDER BY DateOfRecordRelease DESC, DateOfRecordCreation DESC");
347  $ResourceIds = $this->DB->FetchColumn("ResourceId");
348 
349  # filter out resources that aren't viewable to the public
350  $ResourceIds = $this->FilterNonViewableResources(
351  $ResourceIds, CWUser::GetAnonymousUser() );
352 
353  # subset the results as requested
354  $ResourceIds = array_slice(
355  $ResourceIds, $Offset, $Count);
356 
357  # for each resource ID found
358  foreach ($ResourceIds as $ResourceId)
359  {
360  # load resource and add to list of found resources
361  $Resources[$ResourceId] = new Resource($ResourceId);
362  }
363 
364  # return found resources to caller
365  return $Resources;
366  }
367 
376  public function GetResourceIdsSortedBy($FieldId, $Ascending = TRUE, $Limit = NULL)
377  {
378  # assume no resources will be found
379  $ResourceIds = array();
380 
381  # if field was found
382  if ($this->Schema->FieldExists($FieldId))
383  {
384  $Field = $this->Schema->GetField($FieldId);
385  # construct query based on field type
386  switch ($Field->Type())
387  {
391  $Count = $this->DB->Query("SELECT COUNT(*) AS ResourceCount"
392  ." FROM Resources WHERE "
393  .$Field->DBFieldName()." IS NOT NULL"
394  ." AND LENGTH(LTRIM(RTRIM(".$Field->DBFieldName()."))) > 0"
395  ." AND SchemaId = ".intval($this->SchemaId),
396  "ResourceCount");
397  if ($Count > 0)
398  {
399  $Query = "SELECT ResourceId FROM Resources"
400  ." WHERE SchemaId = ".intval($this->SchemaId)
401  ." ORDER BY ".$Field->DBFieldName()
402  .($Ascending ? " ASC" : " DESC");
403  }
404  break;
405 
408  $Count = $this->DB->Query("SELECT COUNT(*) AS ResourceCount"
409  ." FROM Resources WHERE "
410  .$Field->DBFieldName()." IS NOT NULL"
411  ." AND SchemaId = ".intval($this->SchemaId),
412  "ResourceCount");
413  if ($Count > 0)
414  {
415  $Query = "SELECT ResourceId FROM Resources"
416  ." WHERE SchemaId = ".intval($this->SchemaId)
417  ." ORDER BY ".$Field->DBFieldName()
418  .($Ascending ? " ASC" : " DESC");
419  }
420  break;
421 
423  $Count = $this->DB->Query("SELECT COUNT(*) AS ResourceCount"
424  ." FROM Resources WHERE "
425  .$Field->DBFieldName()."Begin IS NOT NULL"
426  ." AND SchemaId = ".intval($this->SchemaId),
427  "ResourceCount");
428  if ($Count > 0)
429  {
430  $Query = "SELECT ResourceId FROM Resources"
431  ." WHERE SchemaId = ".intval($this->SchemaId)
432  ." ORDER BY ".$Field->DBFieldName()."Begin"
433  .($Ascending ? " ASC" : " DESC");
434  }
435  break;
436  }
437 
438  # if appropriate query was found
439  if (isset($Query))
440  {
441  # if limited number of results were requested
442  if ($Limit !== NULL)
443  {
444  # add limit to query
445  $Query .= " LIMIT ".intval($Limit);
446  }
447 
448  # perform query and retrieve resource IDs
449  $this->DB->Query($Query);
450  $ResourceIds = $this->DB->FetchColumn("ResourceId");
451  }
452  }
453 
454  # return resource IDs to caller
455  return $ResourceIds;
456  }
457 
464  public function FilterNonViewableResources($ResourceIds, $User)
465  {
466  # compute this user's class
467  $UserClass = $this->ComputeUserClass($User);
468 
469  # generate an array where the keys are ResourceIds affected by
470  # user comparisons for the current user
471  $UserComparisonsRIDs = array_flip(
472  $this->ResourcesWhereUserComparisonsMatterForViewing($User));
473 
474  # (Note: We can use the $UserClass without a schema prefix as
475  # a cache key even though User Classes are schema specific
476  # because the values we're caching are ResourceIds. Since the
477  # ResourceIds already imply a schema, there's no ambiguity
478  # regarding which schema was involved when the stored UserClass
479  # was computed.)
480  if (!isset(self::$UserClassPermissionsCache[$UserClass]))
481  {
482  # grab all the ResourceIds for this user class
483  $this->DB->Query("SELECT ResourceId, CanView FROM UserPermsCache WHERE"
484  ." UserClass='".$UserClass."'");
485 
486  self::$UserClassPermissionsCache[$UserClass] = $this->DB->FetchColumn(
487  "CanView", "ResourceId");
488  }
489 
490  # filter out those not requested
491  $Cache = array_intersect_key(
492  self::$UserClassPermissionsCache[$UserClass],
493  array_flip($ResourceIds) );
494 
495  # figure out which resources we didn't have cached values for
496  # and iterate over those
497  $MissingIds = array_diff($ResourceIds, array_keys($Cache));
498 
499  $PerUserKey = $this->SchemaId.".UID_".$User->Id();
500 
501  # batch inserts up into not more than 1000 resources per query
502  $ChunkSize = 1000;
503  $QueryValues = array();
504  foreach ($MissingIds as $Id)
505  {
506  if (isset(self::$PerUserPermissionsCache[$PerUserKey]))
507  {
508  $CanView = self::$PerUserPermissionsCache[$PerUserKey];
509  }
510  else
511  {
512  # evaluate perms for this resource
513  if (Resource::ItemExists($Id))
514  {
515  $Resource = new Resource($Id);
516  $CanView = $Resource->UserCanView($User, FALSE);
517  }
518  else
519  {
520  $CanView = FALSE;
521  }
522 
523  # if this is a result we can cache persistently
524  # (i.e. not affected by user comparisons), do so
525  if (!isset($UserComparisonsRIDs[$Id]))
526  {
527  self::$UserClassPermissionsCache[$UserClass][$Id] = $CanView;
528 
529  # add this to our queue of inserts
530  $QueryValues[]= "(".$Id.",'".$UserClass."',".($CanView?"1":"0").")" ;
531 
532  # if this chunk is full, insert it into the db and clear our queue
533  if (count($QueryValues)>=$ChunkSize)
534  {
535  $this->DB->Query(
536  "INSERT INTO UserPermsCache (ResourceId, UserClass, CanView) "
537  ."VALUES ".implode(",", $QueryValues) );
538  $QueryValues = array();
539  }
540  }
541  else
542  {
543  # this isn't a result we should cache persistently
544  # in the database, but we still want to cache it
545  # within this page load
546  self::$PerUserPermissionsCache[$PerUserKey] = $CanView;
547  }
548 
549  }
550  $Cache[$Id] = $CanView;
551  }
552 
553  # if we have values left to insert, do so
554  if (count($QueryValues))
555  {
556  $this->DB->Query(
557  "INSERT INTO UserPermsCache (ResourceId, UserClass, CanView) "
558  ."VALUES ".implode(",", $QueryValues) );
559  }
560 
561  # if resource view permission check has any handlers that may
562  # modify our cached values
563  if ($GLOBALS["AF"]->IsHookedEvent("EVENT_RESOURCE_VIEW_PERMISSION_CHECK"))
564  {
565  # apply hooked functions to each value
566  foreach (array_keys($Cache) as $Id)
567  {
568  $SignalResult = $GLOBALS["AF"]->SignalEvent(
569  "EVENT_RESOURCE_VIEW_PERMISSION_CHECK",
570  array(
571  "Resource" => $Id,
572  "User" => $User,
573  "CanView" => $Cache[$Id],
574  "Schema" => $this->Schema, ));
575  $Cache[$Id] = $SignalResult["CanView"];
576  }
577  }
578 
579  # filter out the non-viewable resources, preserving the order
580  # of resources
581  return array_intersect($ResourceIds,
582  array_keys(array_filter($Cache)) );
583  }
584 
588  public function ClearViewingPermsCache()
589  {
590  $this->DB->Query("DELETE FROM UserPermsCache");
591  }
592 
593 
599  public function GetPossibleFieldNames()
600  {
601  # retrieve field names from schema
602  $FieldNames = array();
603  $Fields = $this->Schema->GetFields();
604  foreach ($Fields as $Field)
605  {
606  $FieldNames[$Field->Id()] = $Field->Name();
607  }
608 
609  # return field names to caller
610  return $FieldNames;
611  }
612 
626  public function GetMatchingResources(
627  $ValuesToMatch, $AllRequired=TRUE, $ReturnObjects=TRUE,
628  $Operator = "==")
629  {
630  # start out assuming we won't find any resources
631  $Resources = array();
632 
633  # fix up equality operator
634  if ($Operator == "==")
635  {
636  $Operator = "=";
637  }
638 
639  $LinkingTerm = "";
640  $Condition = "";
641 
642  # for each value
643  $Fields = $this->Schema->GetFields();
644  foreach ($ValuesToMatch as $FieldId => $Value)
645  {
646  # only equality supported for NULL
647  if ($Operator != "=" && $Value == "NULL")
648  {
649  throw new Exception(
650  "Invalid operator, ".$Operator." not supported for NULL");
651  }
652 
653  # convert supplied FieldId to canonical identifier
655  $FieldId, $this->SchemaId);
656 
657  # check that provided operator is sane
658  switch ($Fields[$FieldId]->Type())
659  {
665  $ValidOps = array("=");
666  break;
667 
669  $ValidOps = array("=", "!=");
670  break;
671 
675  $ValidOps = array("=", "!=", "<", "<=", ">", ">=");
676  break;
677 
678  default:
679  $ValidOps = array();
680  }
681 
682  if (!in_array($Operator, $ValidOps))
683  {
684  throw new Exception("Operator ".$Operator." not supported for "
685  .$Fields[$FieldId]->TypeAsName()." fields");
686  }
687 
688  # add SQL fragments to Condition as needed
689  switch ($Fields[$FieldId]->Type())
690  {
698  $DBFname = $Fields[$FieldId]->DBFieldName();
699  # add comparison to condition
700  if ($Value == "NULL")
701  {
702  $Condition .= $LinkingTerm."("
703  .$DBFname." IS NULL OR ".$DBFname." = '')";
704  }
705  else
706  {
707  $Condition .= $LinkingTerm.$DBFname." "
708  .$Operator." '".addslashes($Value)."'";
709  }
710  break;
711 
713  $DBFname = $Fields[$FieldId]->DBFieldName();
714 
715  if ($Value == "NULL")
716  {
717  $Condition .= $LinkingTerm."("
718  .$DBFname."X IS NULL AND "
719  .$DBFname."Y IS NULL)";
720  }
721  else
722  {
723  $Vx = addslashes($Value["X"]);
724  $Vy = addslashes($Value["Y"]);
725 
726  $Condition .= $LinkingTerm."("
727  .$DBFname."X = '".$Vx."' AND "
728  .$DBFname."Y = '".$Vy."')";
729  }
730  break;
731 
733  $TgtValues = array();
734  if (is_object($Value))
735  {
736  $TgtValues[]= $Value->Id();
737  }
738  elseif (is_numeric($Value))
739  {
740  $TgtValues[]= $Value;
741  }
742  elseif (is_array($Value))
743  {
744  foreach ($Value as $UserId => $UserNameOrObject)
745  {
746  $TgtValues[]= $UserId;
747  }
748  }
749 
750  # if no users were specified
751  if (!count($TgtValues))
752  {
753  # return no results (nothing matches nothing)
754  return array();
755  }
756  else
757  {
758  # add conditional to match specified users
759  $Condition .= $LinkingTerm."("
760  ."ResourceId IN (SELECT ResourceId FROM "
761  ."ResourceUserInts WHERE FieldId=".intval($FieldId)
762  ." AND UserId IN ("
763  .implode(",", $TgtValues).")) )";
764  }
765  break;
766 
767  default:
768  throw new Exception("Unsupported field type");
769  }
770 
771  $LinkingTerm = $AllRequired ? " AND " : " OR ";
772  }
773 
774  # if there were valid conditions
775  if (strlen($Condition))
776  {
777  # build query statment
778  $Query = "SELECT ResourceId FROM Resources WHERE (".$Condition
779  .") AND SchemaId = ".intval($this->SchemaId);
780 
781  # execute query to retrieve matching resource IDs
782  $this->DB->Query($Query);
783  $ResourceIds = $this->DB->FetchColumn("ResourceId");
784 
785  if ($ReturnObjects)
786  {
787  # retrieve resource objects
788  foreach ($ResourceIds as $Id)
789  {
790  $Resources[$Id] = new Resource($Id);
791  }
792  }
793  else
794  {
795  $Resources = $ResourceIds;
796  }
797  }
798 
799  # return any resources found to caller
800  return $Resources;
801  }
802 
815  $ValueId, $User, $ForegroundUpdate=FALSE)
816  {
817  # if the specified user is matched by any UserIs or UserIsNot
818  # privset conditions for any resources, then put them in a class
819  # by themselves
820  $UserClass = count($this->ResourcesWhereUserComparisonsMatterForViewing($User))
821  ? "UID_".$User->Id() :
822  $this->ComputeUserClass($User);
823 
824  $CacheKey = $this->SchemaId.".".$UserClass;
825  # if we haven't loaded any cached values, do so now
826  if (!isset(self::$VisibleResourceCountCache[$CacheKey]))
827  {
828  $this->DB->Query(
829  "SELECT ResourceCount, ValueId FROM "
830  ."VisibleResourceCounts WHERE "
831  ."SchemaId=".intval($this->SchemaId)
832  ." AND UserClass='".addslashes($UserClass)."'");
833 
834  self::$VisibleResourceCountCache[$CacheKey] = $this->DB->FetchColumn(
835  "ResourceCount", "ValueId");
836  }
837 
838  # if we don't have a cached value for this class
839  if (!isset(self::$VisibleResourceCountCache[$CacheKey][$ValueId]))
840  {
841  # if we're doing a foreground update
842  if ($ForegroundUpdate)
843  {
844  # run the update callback
846  $ValueId, $User->Id());
847 
848  # and call ourselves again
849  return $this->AssociatedVisibleResourceCount(
850  $ValueId, $User);
851  }
852  else
853  {
854  # otherwise (for background update), queue the update
855  # callback and return -1
856  $GLOBALS["AF"]->QueueUniqueTask(
857  array($this, "UpdateAssociatedVisibleResourceCount"),
858  array($ValueId, $User->Id() ) );
859  return -1;
860  }
861  }
862 
863  # owtherwise, return the cached data
864  return self::$VisibleResourceCountCache[$CacheKey][$ValueId];
865  }
866 
874  $ValueId, $UserId)
875  {
876  $User = new CWUser($UserId);
877 
878  # if the specified user is matched by any UserIs or UserIsNot
879  # privset conditions for any resources, then put them in a class
880  # by themselves
881  $UserClass = count($this->ResourcesWhereUserComparisonsMatterForViewing($User))
882  ? "UID_".$User->Id() :
883  $this->ComputeUserClass($User);
884 
885  $this->DB->Query(
886  "SELECT ResourceId FROM ResourceNameInts "
887  ."WHERE ControlledNameId=".intval($ValueId) );
888  $ResourceIds = $this->DB->FetchColumn("ResourceId");
889 
890  $ResourceIds = $this->FilterNonViewableResources(
891  $ResourceIds, $User);
892 
893  $ResourceCount = count($ResourceIds);
894 
895  $this->DB->Query(
896  "INSERT INTO VisibleResourceCounts "
897  ."(SchemaId, UserClass, ValueId, ResourceCount) "
898  ."VALUES ("
899  .intval($this->SchemaId).","
900  ."'".addslashes($UserClass)."',"
901  .intval($ValueId).","
902  .$ResourceCount.")");
903  }
904 
910  public function GetVisibleResourceCount(CWUser $User)
911  {
912  $ResourceIds = $this->DB->Query(
913  "SELECT ResourceId FROM Resources "
914  ."WHERE ResourceId > 0 AND SchemaId = ".intval($this->SchemaId));
915  $ResourceIds = $this->DB->FetchColumn("ResourceId");
916 
917  $ResourceIds = $this->FilterNonViewableResources(
918  $ResourceIds, $User);
919 
920  return count($ResourceIds);
921  }
922 
927  public function ClearVisibleResourceCountForValues($ValueIds)
928  {
929  # and clear our visible resource count cache
930  $this->DB->Query(
931  "DELETE FROM VisibleResourceCounts WHERE "
932  ."SchemaId=".intval($this->SchemaId)." AND "
933  ."ValueId IN (".implode(",", $ValueIds).")");
934  }
935 
941  public function ClearVisibleResourceCount($Resource)
942  {
943  # get all the CName and Option fields
944  $Fields = $this->Schema()->GetFields(
947 
948  # pull out the Values associated with those
949  $Values = [];
950  foreach ($Fields as $Field)
951  {
952  $Values += $Resource->Get($Field);
953  }
954 
955  # and clear our visible resource count cache
957  array_keys($Values));
958  }
959 
964  public function GetReleasedResourceTotal()
965  {
966  return $this->GetVisibleResourceCount(
968  }
969 
977  public static function FlattenMultiSchemaResourceList($ResourcesPerSchema)
978  {
979  $Result = [];
980  foreach ($ResourcesPerSchema as $SchemaId => $ResourceIds)
981  {
982  $Result = array_merge($Result, $ResourceIds);
983  }
984 
985  return $Result;
986  }
987 
996  public static function BuildMultiSchemaResourceList($ResourceIds)
997  {
998  $DB = new Database();
999  $DB->Query("SELECT ResourceId, SchemaId FROM Resources");
1000  $ResourceSchemas = $DB->FetchColumn("SchemaId", "ResourceId");
1001 
1002  $Result = [];
1003  foreach ($ResourceIds as $ResourceId)
1004  {
1005  $SchemaId = $ResourceSchemas[$ResourceId];
1006  $Result[$SchemaId][]= $ResourceId;
1007  }
1008 
1009  return $Result;
1010  }
1011 
1017  public function GetResourceTotal()
1018  {
1019  return $this->DB->Query("
1020  SELECT COUNT(*) AS ResourceTotal
1021  FROM Resources
1022  WHERE ResourceId > 0
1023  AND SchemaId = ".intval($this->SchemaId),
1024  "ResourceTotal");
1025  }
1026 
1031  public function ClearCaches()
1032  {
1033  self::$VisibleResourceCountCache = array();
1034  self::$UserClassPermissionsCache = array();
1035  self::$PerUserPermissionsCache = array();
1036  self::$UserClassCache = array();
1037  self::$UserComparisonResourceCache = array();
1038  self::$UserComparisonFieldCache = array();
1039  }
1040 
1041  # ---- PRIVATE INTERFACE -------------------------------------------------
1042 
1043  protected $Schema;
1044  protected $SchemaId;
1045 
1046  # internal caches
1047  private static $VisibleResourceCountCache;
1048  private static $UserClassPermissionsCache;
1049  private static $PerUserPermissionsCache;
1050  private static $UserClassCache;
1051  private static $UserComparisonResourceCache;
1052  private static $UserComparisonFieldCache;
1053 
1061  private function ComputeUserClass($User)
1062  {
1063  # put the anonymous user into their own user class, otherwise
1064  # use the UserId for a key into the ClassCache
1065  $UserId = $User->IsAnonymous() ? "XX-ANON-XX" : $User->Id();
1066 
1067  $CacheKey = $this->SchemaId.".".$UserId;
1068 
1069  # check if we have a cached UserClass for this User
1070  if (!isset($this->UserClassCache[$CacheKey]))
1071  {
1072  # assemble a list of the privilege flags (PRIV_SYSADMIN,
1073  # etc) that are checked when evaluating the UserCanView for
1074  # all fields in this schema
1075  $RelevantPerms = array();
1076 
1077  foreach ($this->Schema->GetFields() as $Field)
1078  {
1079  $RelevantPerms = array_merge(
1080  $RelevantPerms,
1081  $Field->ViewingPrivileges()->PrivilegeFlagsChecked() );
1082  }
1083  $RelevantPerms = array_unique($RelevantPerms);
1084 
1085  # whittle the list of all privs checked down to just the
1086  # list of privs that users in this class have
1087  $PermsInvolved = array();
1088  foreach ($RelevantPerms as $Perm)
1089  {
1090  if ($User->HasPriv($Perm))
1091  {
1092  $PermsInvolved[]= $Perm;
1093  }
1094  }
1095 
1096  # generate a string by concatenating all the involved
1097  # permissions then hashing the result (hashing gives
1098  # a fixed-size string for storing in the database)
1099  self::$UserClassCache[$CacheKey] = md5(implode( "-", $PermsInvolved ));
1100  }
1101 
1102  return self::$UserClassCache[$CacheKey];
1103  }
1104 
1114  private function ResourcesWhereUserComparisonsMatterForViewing($User)
1115  {
1116  $ResourceIds = array();
1117 
1118  # if we're checking the anonymous user, presume that
1119  # nothing will match
1120  if ($User->IsAnonymous())
1121  {
1122  return $ResourceIds;
1123  }
1124 
1125  $CacheKey = $this->SchemaId.".".$User->Id();
1126  if (!isset(self::$UserComparisonResourceCache[$CacheKey]))
1127  {
1128  $Schema = new MetadataSchema($this->SchemaId);
1129 
1130  # for each comparison type
1131  foreach (array("==", "!=") as $ComparisonType)
1132  {
1133  $UserComparisonFields = $this->GetUserComparisonFields(
1134  $ComparisonType);
1135 
1136  # if we have any fields to check
1137  if (count($UserComparisonFields) > 0 )
1138  {
1139  # query the database for resources where one or more of the
1140  # user comparisons will be satisfied
1141  $SqlOp = ($ComparisonType == "==") ? "= " : "!= ";
1142  $DB = new Database();
1143  $DB->Query("SELECT R.ResourceId as ResourceId FROM ".
1144  "Resources R, ResourceUserInts RU WHERE ".
1145  "R.SchemaId = ".$this->SchemaId." AND ".
1146  "R.ResourceId = RU.ResourceId AND ".
1147  "RU.UserId ".$SqlOp.$User->Id()." AND ".
1148  "RU.FieldId IN (".implode(",", $UserComparisonFields).")");
1149  $Result = $DB->FetchColumn("ResourceId");
1150 
1151  # merge those resources into our results
1152  $ResourceIds = array_merge(
1153  $ResourceIds,
1154  $Result);
1155  }
1156  }
1157 
1158  self::$UserComparisonResourceCache[$CacheKey] = array_unique($ResourceIds);
1159  }
1160 
1161  return self::$UserComparisonResourceCache[$CacheKey];
1162  }
1163 
1164 
1170  private function GetUserComparisonFields($ComparisonType)
1171  {
1172  $CacheKey = $this->SchemaId.".".$ComparisonType;
1173  if (!isset(self::$UserComparisonFieldCache[$CacheKey]))
1174  {
1175  # iterate through all the fields in the schema,
1176  # constructing a list of the User fields implicated
1177  # in comparisons of the desired type
1178  $UserComparisonFields = array();
1179  foreach ($this->Schema->GetFields() as $Field)
1180  {
1181  $UserComparisonFields = array_merge(
1182  $UserComparisonFields,
1183  $Field->ViewingPrivileges()->FieldsWithUserComparisons(
1184  $ComparisonType) );
1185  }
1186  self::$UserComparisonFieldCache[$CacheKey] =
1187  array_unique($UserComparisonFields);
1188  }
1189 
1190  return self::$UserComparisonFieldCache[$CacheKey];
1191  }
1192 }
GetMatchingResources($ValuesToMatch, $AllRequired=TRUE, $ReturnObjects=TRUE, $Operator="==")
Find resources with values that match those specified.
static GetAnonymousUser()
Get the anonymous user (i.e., the User object that exists when no user is logged in), useful when a permission check needs to know if something should be visible to the general public.
Definition: User.php:1039
AssociatedVisibleResourceCount($ValueId, $User, $ForegroundUpdate=FALSE)
Return the number of resources in this schema that are visible to a specified user and that have a gi...
static FlattenMultiSchemaResourceList($ResourcesPerSchema)
Take an array keyed by SchemaId with elements giving arrays of ResourceIds and merge it to a flattene...
GetRatedResourceUserCount()
Return number of users who have rated resources.
Metadata schema (in effect a Factory class for MetadataField).
static Create($Term, $FieldId)
Create a new empty ControlledName if it&#39;s not already present.
SQL database abstraction object with smart query caching.
Definition: Database.php:22
static Create($Name, $FieldId, $ParentId=NULL)
Add new classification to the hierarchy.
static BuildMultiSchemaResourceList($ResourceIds)
Take an array of ResourceIds and split it into an array keyed by SchemaId where the elements are arra...
GetResourceIdsSortedBy($FieldId, $Ascending=TRUE, $Limit=NULL)
Get resource IDs sorted by specified field.
static GetCanonicalFieldIdentifier($Field, $SchemaId=NULL)
Retrieve canonical identifier for field.
ClearViewingPermsCache()
Clear the cache of viewable resources.
UpdateAssociatedVisibleResourceCount($ValueId, $UserId)
Update the count of resources associated with a ControlledName that are visible to a specified user...
GetRecentlyReleasedResources($Count=10, $Offset=0, $MaxDaysToGoBack=90)
Get resources sorted by descending Date of Record Release, with Date of Record Creation as the second...
ImportResourcesFromXmlFile($FileName)
Import resource records from XML file.
const MDFTYPE_CONTROLLEDNAME
ClearVisibleResourceCountForValues($ValueIds)
Clear cache of visible resources associated with a ControlledName.
FilterNonViewableResources($ResourceIds, $User)
Filter a list of resources leaving only those viewable by a specified user.
ClearQualifier($ObjectOrId, $NewObjectOrId=NULL)
Clear or change specific qualifier for all resources.
ClearCaches()
Clear internal caches.
Represents a "resource" in CWIS.
Definition: Resource.php:13
GetPossibleFieldNames()
Get possible field names for resources.
GetReleasedResourceTotal()
Get the total number of released resources in the collection.
ClearVisibleResourceCount($Resource)
Clear database visibility caches for all the CNames referenced by a specified resource.
Schema()
Get metadata schema associated with this resource factory.
__construct($SchemaId=MetadataSchema::SCHEMAID_DEFAULT)
Class constructor.
static Create($SchemaId)
Create a new resource.
Definition: Resource.php:48
Common factory class for item manipulation.
Definition: ItemFactory.php:17
static ItemExists($Id)
Check whether an item exists with the specified ID.
Definition: Item.php:162
GetRatedResourceCount()
Return number of resources that have ratings.
Factory for Resource objects.
CWIS-specific user class.
Definition: CWUser.php:13
GetResourceTotal()
Get the total number of resources in the collection, even if they are not released.
GetVisibleResourceCount(CWUser $User)
Get the total number of resources visible to a specified user.
DuplicateResource($ResourceId)
Duplicate the specified resource and return to caller.