CWIS Developer Documentation

- Home
- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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
13 class ResourceFactory extends ItemFactory
14 {
15 # ---- PUBLIC INTERFACE --------------------------------------------------
16
22 public function __construct($SchemaId = MetadataSchema::SCHEMAID_DEFAULT)
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 {
158 case MetadataSchema::MDFTYPE_TEXT:
159 case MetadataSchema::MDFTYPE_PARAGRAPH:
160 case MetadataSchema::MDFTYPE_NUMBER:
161 case MetadataSchema::MDFTYPE_TIMESTAMP:
162 case MetadataSchema::MDFTYPE_URL:
163 case MetadataSchema::MDFTYPE_DATE:
164 $Resource->Set($Field, $Value);
165 break;
166
167 case MetadataSchema::MDFTYPE_FLAG:
168 $Resource->Set($Field,
169 (strtoupper($Value) == "TRUE") ? TRUE : FALSE);
170 break;
171
172 case MetadataSchema::MDFTYPE_OPTION:
173 case MetadataSchema::MDFTYPE_CONTROLLEDNAME:
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
188 case MetadataSchema::MDFTYPE_TREE:
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
203 case MetadataSchema::MDFTYPE_POINT:
204 list($Point["X"], $Point["Y"]) = explode(",", $Value);
205 $Resource->Set($Field, $Point);
206 break;
207
208 case MetadataSchema::MDFTYPE_USER:
209 if (preg_match("/^[0-9]+\$/", $Value))
210 {
211 $Value = intval($Value);
212 }
213 $Resource->Set($Field, $Value);
214 break;
215
216 case MetadataSchema::MDFTYPE_IMAGE:
217 case MetadataSchema::MDFTYPE_FILE:
218 case MetadataSchema::MDFTYPE_REFERENCE:
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
332 public function GetRecentlyReleasedResources(
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 current user
350 $ResourceIds = $this->FilterNonViewableResources(
351 $ResourceIds, $GLOBALS["G_User"] );
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 {
388 case MetadataSchema::MDFTYPE_TEXT:
389 case MetadataSchema::MDFTYPE_PARAGRAPH:
390 case MetadataSchema::MDFTYPE_URL:
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
406 case MetadataSchema::MDFTYPE_NUMBER:
407 case MetadataSchema::MDFTYPE_TIMESTAMP:
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
422 case MetadataSchema::MDFTYPE_DATE:
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
590 public function GetPossibleFieldNames()
591 {
592 # retrieve field names from schema
593 $FieldNames = array();
594 $Fields = $this->Schema->GetFields();
595 foreach ($Fields as $Field)
596 {
597 $FieldNames[$Field->Id()] = $Field->Name();
598 }
599
600 # return field names to caller
601 return $FieldNames;
602 }
603
617 public function GetMatchingResources(
618 $ValuesToMatch, $AllRequired=TRUE, $ReturnObjects=TRUE,
619 $Operator = "==")
620 {
621 # start out assuming we won't find any resources
622 $Resources = array();
623
624 # fix up equality operator
625 if ($Operator == "==")
626 {
627 $Operator = "=";
628 }
629
630 $LinkingTerm = "";
631 $Condition = "";
632
633 # for each value
634 $Fields = $this->Schema->GetFields();
635 foreach ($ValuesToMatch as $FieldId => $Value)
636 {
637 # only equality supported for NULL
638 if ($Operator != "=" && $Value == "NULL")
639 {
640 throw new Exception(
641 "Invalid operator, ".$Operator." not supported for NULL");
642 }
643
644 # convert supplied FieldId to canonical identifier
645 $FieldId = MetadataSchema::GetCanonicalFieldIdentifier(
646 $FieldId, $this->SchemaId);
647
648 # check that provided operator is sane
649 switch ($Fields[$FieldId]->Type())
650 {
651 case MetadataSchema::MDFTYPE_TEXT:
652 case MetadataSchema::MDFTYPE_PARAGRAPH:
653 case MetadataSchema::MDFTYPE_POINT:
654 case MetadataSchema::MDFTYPE_USER:
655 case MetadataSchema::MDFTYPE_URL:
656 $ValidOps = array("=");
657 break;
658
659 case MetadataSchema::MDFTYPE_FLAG:
660 $ValidOps = array("=", "!=");
661 break;
662
663 case MetadataSchema::MDFTYPE_NUMBER:
664 case MetadataSchema::MDFTYPE_DATE:
665 case MetadataSchema::MDFTYPE_TIMESTAMP:
666 $ValidOps = array("=", "!=", "<", "<=", ">", ">=");
667 break;
668
669 default:
670 $ValidOps = array();
671 }
672
673 if (!in_array($Operator, $ValidOps))
674 {
675 throw new Exception("Operator ".$Operator." not supported for "
676 .$Fields[$FieldId]->TypeAsName()." fields");
677 }
678
679 # add SQL fragments to Condition as needed
680 switch ($Fields[$FieldId]->Type())
681 {
682 case MetadataSchema::MDFTYPE_TEXT :
683 case MetadataSchema::MDFTYPE_PARAGRAPH :
684 case MetadataSchema::MDFTYPE_NUMBER :
685 case MetadataSchema::MDFTYPE_DATE :
686 case MetadataSchema::MDFTYPE_TIMESTAMP :
687 case MetadataSchema::MDFTYPE_FLAG :
688 case MetadataSchema::MDFTYPE_URL :
689 $DBFname = $Fields[$FieldId]->DBFieldName();
690 # add comparison to condition
691 if ($Value == "NULL")
692 {
693 $Condition .= $LinkingTerm."("
694 .$DBFname." IS NULL OR ".$DBFname." = '')";
695 }
696 else
697 {
698 $Condition .= $LinkingTerm.$DBFname." "
699 .$Operator." '".addslashes($Value)."'";
700 }
701 break;
702
703 case MetadataSchema::MDFTYPE_POINT:
704 $DBFname = $Fields[$FieldId]->DBFieldName();
705
706 if ($Value == "NULL")
707 {
708 $Condition .= $LinkingTerm."("
709 .$DBFname."X IS NULL AND "
710 .$DBFname."Y IS NULL)";
711 }
712 else
713 {
714 $Vx = addslashes($Value["X"]);
715 $Vy = addslashes($Value["Y"]);
716
717 $Condition .= $LinkingTerm."("
718 .$DBFname."X = '".$Vx."' AND "
719 .$DBFname."Y = '".$Vy."')";
720 }
721 break;
722
723 case MetadataSchema::MDFTYPE_USER:
724 $TgtValues = array();
725 if (is_object($Value))
726 {
727 $TgtValues[]= $Value->Id();
728 }
729 elseif (is_numeric($Value))
730 {
731 $TgtValues[]= $Value;
732 }
733 elseif (is_array($Value))
734 {
735 foreach ($Value as $UserId => $UserNameOrObject)
736 {
737 $TgtValues[]= $UserId;
738 }
739 }
740
741 # if no users were specified
742 if (!count($TgtValues))
743 {
744 # return no results (nothing matches nothing)
745 return array();
746 }
747 else
748 {
749 # add conditional to match specified users
750 $Condition .= $LinkingTerm."("
751 ."ResourceId IN (SELECT ResourceId FROM "
752 ."ResourceUserInts WHERE FieldId=".intval($FieldId)
753 ." AND UserId IN ("
754 .implode(",", $TgtValues).")) )";
755 }
756 break;
757
758 default:
759 throw new Exception("Unsupported field type");
760 }
761
762 $LinkingTerm = $AllRequired ? " AND " : " OR ";
763 }
764
765 # if there were valid conditions
766 if (strlen($Condition))
767 {
768 # build query statment
769 $Query = "SELECT ResourceId FROM Resources WHERE (".$Condition
770 .") AND SchemaId = ".intval($this->SchemaId);
771
772 # execute query to retrieve matching resource IDs
773 $this->DB->Query($Query);
774 $ResourceIds = $this->DB->FetchColumn("ResourceId");
775
776 if ($ReturnObjects)
777 {
778 # retrieve resource objects
779 foreach ($ResourceIds as $Id)
780 {
781 $Resources[$Id] = new Resource($Id);
782 }
783 }
784 else
785 {
786 $Resources = $ResourceIds;
787 }
788 }
789
790 # return any resources found to caller
791 return $Resources;
792 }
793
805 public function AssociatedVisibleResourceCount(
806 $ValueId, $User, $ForegroundUpdate=FALSE)
807 {
808 # if the specified user is matched by any UserIs or UserIsNot
809 # privset conditions for any resources, then put them in a class
810 # by themselves
811 $UserClass = count($this->ResourcesWhereUserComparisonsMatterForViewing($User))
812 ? "UID_".$User->Id() :
813 $this->ComputeUserClass($User);
814
815 $CacheKey = $this->SchemaId.".".$UserClass;
816 # if we haven't loaded any cached values, do so now
817 if (!isset(self::$VisibleResourceCountCache[$CacheKey]))
818 {
819 $this->DB->Query(
820 "SELECT ResourceCount, ValueId FROM "
821 ."VisibleResourceCounts WHERE "
822 ."SchemaId=".intval($this->SchemaId)
823 ." AND UserClass='".addslashes($UserClass)."'");
824
825 self::$VisibleResourceCountCache[$CacheKey] = $this->DB->FetchColumn(
826 "ResourceCount", "ValueId");
827 }
828
829 # if we don't have a cached value for this class
830 if (!isset(self::$VisibleResourceCountCache[$CacheKey][$ValueId]))
831 {
832 # if we're doing a foreground update
833 if ($ForegroundUpdate)
834 {
835 # run the update callback
836 $this->UpdateAssociatedVisibleResourceCount(
837 $ValueId, $User->Id());
838
839 # and call ourselves again
840 return $this->AssociatedVisibleResourceCount(
841 $ValueId, $User);
842 }
843 else
844 {
845 # otherwise (for background update), queue the update
846 # callback and return -1
847 $GLOBALS["AF"]->QueueUniqueTask(
848 array($this, "UpdateAssociatedVisibleResourceCount"),
849 array($ValueId, $User->Id() ) );
850 return -1;
851 }
852 }
853
854 # owtherwise, return the cached data
855 return self::$VisibleResourceCountCache[$CacheKey][$ValueId];
856 }
857
864 public function UpdateAssociatedVisibleResourceCount(
865 $ValueId, $UserId)
866 {
867 $User = new CWUser($UserId);
868
869 # if the specified user is matched by any UserIs or UserIsNot
870 # privset conditions for any resources, then put them in a class
871 # by themselves
872 $UserClass = count($this->ResourcesWhereUserComparisonsMatterForViewing($User))
873 ? "UID_".$User->Id() :
874 $this->ComputeUserClass($User);
875
876 $this->DB->Query(
877 "SELECT ResourceId FROM ResourceNameInts "
878 ."WHERE ControlledNameId=".intval($ValueId) );
879 $ResourceIds = $this->DB->FetchColumn("ResourceId");
880
881 $ResourceIds = $this->FilterNonViewableResources(
882 $ResourceIds, $User);
883
884 $ResourceCount = count($ResourceIds);
885
886 $this->DB->Query(
887 "INSERT INTO VisibleResourceCounts "
888 ."(SchemaId, UserClass, ValueId, ResourceCount) "
889 ."VALUES ("
890 .intval($this->SchemaId).","
891 ."'".addslashes($UserClass)."',"
892 .intval($ValueId).","
893 .$ResourceCount.")");
894 }
895
901 public function GetVisibleResourceCount(CWUser $User)
902 {
903 $ResourceIds = $this->DB->Query(
904 "SELECT ResourceId FROM Resources "
905 ."WHERE ResourceId > 0 AND SchemaId = ".intval($this->SchemaId));
906 $ResourceIds = $this->DB->FetchColumn("ResourceId");
907
908 $ResourceIds = $this->FilterNonViewableResources(
909 $ResourceIds, $User);
910
911 return count($ResourceIds);
912 }
913
918 public function ClearVisibleResourceCountForValues($ValueIds)
919 {
920 # and clear our visible resource count cache
921 $this->DB->Query(
922 "DELETE FROM VisibleResourceCounts WHERE "
923 ."SchemaId=".intval($this->SchemaId)." AND "
924 ."ValueId IN (".implode(",", $ValueIds).")");
925 }
926
932 public function ClearVisibleResourceCount($Resource)
933 {
934 # get all the CName and Option fields
935 $Fields = $this->Schema()->GetFields(
936 MetadataSchema::MDFTYPE_OPTION |
937 MetadataSchema::MDFTYPE_CONTROLLEDNAME);
938
939 # pull out the Values associated with those
940 $Values = [];
941 foreach ($Fields as $Field)
942 {
943 $Values += $Resource->Get($Field);
944 }
945
946 # and clear our visible resource count cache
947 $this->ClearVisibleResourceCountForValues(
948 array_keys($Values));
949 }
950
955 public function GetReleasedResourceTotal()
956 {
957 return $this->GetVisibleResourceCount(
958 CWUser::GetAnonymousUser());
959 }
960
968 public static function FlattenMultiSchemaResourceList($ResourcesPerSchema)
969 {
970 $Result = [];
971 foreach ($ResourcesPerSchema as $SchemaId => $ResourceIds)
972 {
973 $Result = array_merge($Result, $ResourceIds);
974 }
975
976 return $Result;
977 }
978
987 public static function BuildMultiSchemaResourceList($ResourceIds)
988 {
989 $DB = new Database();
990 $DB->Query("SELECT ResourceId, SchemaId FROM Resources");
991 $ResourceSchemas = $DB->FetchColumn("SchemaId", "ResourceId");
992
993 $Result = [];
994 foreach ($ResourceIds as $ResourceId)
995 {
996 $SchemaId = $ResourceSchemas[$ResourceId];
997 $Result[$SchemaId][]= $ResourceId;
998 }
999
1000 return $Result;
1001 }
1002
1008 public function GetResourceTotal()
1009 {
1010 return $this->DB->Query("
1011 SELECT COUNT(*) AS ResourceTotal
1012 FROM Resources
1013 WHERE ResourceId > 0
1014 AND SchemaId = ".intval($this->SchemaId),
1015 "ResourceTotal");
1016 }
1017
1021 public static function ClearViewingPermsCache()
1022 {
1023 $DB = new Database();
1024 $DB->Query("DELETE FROM UserPermsCache");
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 }
MetadataSchema\MDFTYPE_IMAGE
const MDFTYPE_IMAGE
Definition: MetadataSchema.php:31
ResourceFactory\GetMatchingResources
GetMatchingResources($ValuesToMatch, $AllRequired=TRUE, $ReturnObjects=TRUE, $Operator="==")
Find resources with values that match those specified.
Definition: ResourceFactory.php:617
User\GetAnonymousUser
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:948
MetadataSchema\MDFTYPE_TIMESTAMP
const MDFTYPE_TIMESTAMP
Definition: MetadataSchema.php:25
ResourceFactory\AssociatedVisibleResourceCount
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...
Definition: ResourceFactory.php:805
ResourceFactory\FlattenMultiSchemaResourceList
static FlattenMultiSchemaResourceList($ResourcesPerSchema)
Take an array keyed by SchemaId with elements giving arrays of ResourceIds and merge it to a flattene...
Definition: ResourceFactory.php:968
MetadataSchema\MDFTYPE_FILE
const MDFTYPE_FILE
Definition: MetadataSchema.php:32
ResourceFactory\GetRatedResourceUserCount
GetRatedResourceUserCount()
Return number of users who have rated resources.
Definition: ResourceFactory.php:315
MetadataSchema
Metadata schema (in effect a Factory class for MetadataField).
Definition: MetadataSchema.php:13
ControlledName\Create
static Create($Term, $FieldId)
Create a new empty ControlledName if it&#39;s not already present.
Definition: ControlledName.php:29
Database
SQL database abstraction object with smart query caching.
Definition: Database.php:22
Classification\Create
static Create($Name, $FieldId, $ParentId=NULL)
Add new classification to the hierarchy.
Definition: Classification.php:31
MetadataSchema\MDFTYPE_REFERENCE
const MDFTYPE_REFERENCE
Definition: MetadataSchema.php:35
ResourceFactory\BuildMultiSchemaResourceList
static BuildMultiSchemaResourceList($ResourceIds)
Take an array of ResourceIds and split it into an array keyed by SchemaId where the elements are arra...
Definition: ResourceFactory.php:987
ResourceFactory\GetResourceIdsSortedBy
GetResourceIdsSortedBy($FieldId, $Ascending=TRUE, $Limit=NULL)
Get resource IDs sorted by specified field.
Definition: ResourceFactory.php:376
MetadataSchema\GetCanonicalFieldIdentifier
static GetCanonicalFieldIdentifier($Field, $SchemaId=NULL)
Retrieve canonical identifier for field.
Definition: MetadataSchema.php:1581
MetadataSchema\MDFTYPE_NUMBER
const MDFTYPE_NUMBER
Definition: MetadataSchema.php:23
ResourceFactory\UpdateAssociatedVisibleResourceCount
UpdateAssociatedVisibleResourceCount($ValueId, $UserId)
Update the count of resources associated with a ControlledName that are visible to a specified user...
Definition: ResourceFactory.php:864
ResourceFactory\GetRecentlyReleasedResources
GetRecentlyReleasedResources($Count=10, $Offset=0, $MaxDaysToGoBack=90)
Get resources sorted by descending Date of Record Release, with Date of Record Creation as the second...
Definition: ResourceFactory.php:332
ResourceFactory\ImportResourcesFromXmlFile
ImportResourcesFromXmlFile($FileName)
Import resource records from XML file.
Definition: ResourceFactory.php:95
MetadataSchema\MDFTYPE_TREE
const MDFTYPE_TREE
Definition: MetadataSchema.php:27
MetadataSchema\MDFTYPE_OPTION
const MDFTYPE_OPTION
Definition: MetadataSchema.php:29
ResourceFactory\ClearViewingPermsCache
static ClearViewingPermsCache()
Clear the cache of viewable resources.
Definition: ResourceFactory.php:1021
MetadataSchema\MDFTYPE_FLAG
const MDFTYPE_FLAG
Definition: MetadataSchema.php:26
MetadataSchema\MDFTYPE_CONTROLLEDNAME
const MDFTYPE_CONTROLLEDNAME
Definition: MetadataSchema.php:28
ItemFactory\$DB
$DB
Definition: ItemFactory.php:1004
PHP
PHP
Definition: RSSClient--Test.php:12
ResourceFactory\$Schema
$Schema
Definition: ResourceFactory.php:1043
MetadataSchema\SCHEMAID_DEFAULT
const SCHEMAID_DEFAULT
Definition: MetadataSchema.php:53
ResourceFactory\ClearVisibleResourceCountForValues
ClearVisibleResourceCountForValues($ValueIds)
Clear cache of visible resources associated with a ControlledName.
Definition: ResourceFactory.php:918
ResourceFactory\FilterNonViewableResources
FilterNonViewableResources($ResourceIds, $User)
Filter a list of resources leaving only those viewable by a specified user.
Definition: ResourceFactory.php:464
ResourceFactory\ClearQualifier
ClearQualifier($ObjectOrId, $NewObjectOrId=NULL)
Clear or change specific qualifier for all resources.
Definition: ResourceFactory.php:249
ResourceFactory\ClearCaches
ClearCaches()
Clear internal caches.
Definition: ResourceFactory.php:1031
MetadataSchema\MDFTYPE_PARAGRAPH
const MDFTYPE_PARAGRAPH
Definition: MetadataSchema.php:22
MetadataSchema\MDFTYPE_DATE
const MDFTYPE_DATE
Definition: MetadataSchema.php:24
ResourceFactory\$SchemaId
$SchemaId
Definition: ResourceFactory.php:1044
Resource
Represents a "resource" in CWIS.
Definition: Resource.php:13
MetadataSchema\MDFTYPE_URL
const MDFTYPE_URL
Definition: MetadataSchema.php:33
ResourceFactory\GetPossibleFieldNames
GetPossibleFieldNames()
Get possible field names for resources.
Definition: ResourceFactory.php:590
ResourceFactory\GetReleasedResourceTotal
GetReleasedResourceTotal()
Get the total number of released resources in the collection.
Definition: ResourceFactory.php:955
MetadataSchema\MDFTYPE_TEXT
const MDFTYPE_TEXT
Definition: MetadataSchema.php:21
ResourceFactory\ClearVisibleResourceCount
ClearVisibleResourceCount($Resource)
Clear database visibility caches for all the CNames referenced by a specified resource.
Definition: ResourceFactory.php:932
MetadataSchema\MDFTYPE_USER
const MDFTYPE_USER
Definition: MetadataSchema.php:30
ResourceFactory\Schema
Schema()
Get metadata schema associated with this resource factory.
Definition: ResourceFactory.php:37
ResourceFactory\__construct
__construct($SchemaId=MetadataSchema::SCHEMAID_DEFAULT)
Class constructor.
Definition: ResourceFactory.php:22
Resource\Create
static Create($SchemaId)
Create a new resource.
Definition: Resource.php:48
ItemFactory
Common factory class for item manipulation.
Definition: ItemFactory.php:17
MetadataSchema\MDFTYPE_POINT
const MDFTYPE_POINT
Definition: MetadataSchema.php:34
Item\ItemExists
static ItemExists($Id)
Check whether an item exists with the specified ID.
Definition: Item.php:162
ResourceFactory\GetRatedResourceCount
GetRatedResourceCount()
Return number of resources that have ratings.
Definition: ResourceFactory.php:303
ResourceFactory
Factory for Resource objects.
Definition: ResourceFactory.php:13
CWUser
CWIS-specific user class.
Definition: CWUser.php:13
ResourceFactory\GetResourceTotal
GetResourceTotal()
Get the total number of resources in the collection, even if they are not released.
Definition: ResourceFactory.php:1008
ResourceFactory\GetVisibleResourceCount
GetVisibleResourceCount(CWUser $User)
Get the total number of resources visible to a specified user.
Definition: ResourceFactory.php:901
ResourceFactory\DuplicateResource
DuplicateResource($ResourceId)
Duplicate the specified resource and return to caller.
Definition: ResourceFactory.php:47