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 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 {
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
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
654 $FieldId = MetadataSchema::GetCanonicalFieldIdentifier(
655 $FieldId, $this->SchemaId);
656
657 # check that provided operator is sane
658 switch ($Fields[$FieldId]->Type())
659 {
660 case MetadataSchema::MDFTYPE_TEXT:
661 case MetadataSchema::MDFTYPE_PARAGRAPH:
662 case MetadataSchema::MDFTYPE_POINT:
663 case MetadataSchema::MDFTYPE_USER:
664 case MetadataSchema::MDFTYPE_URL:
665 $ValidOps = array("=");
666 break;
667
668 case MetadataSchema::MDFTYPE_FLAG:
669 $ValidOps = array("=", "!=");
670 break;
671
672 case MetadataSchema::MDFTYPE_NUMBER:
673 case MetadataSchema::MDFTYPE_DATE:
674 case MetadataSchema::MDFTYPE_TIMESTAMP:
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 {
691 case MetadataSchema::MDFTYPE_TEXT :
692 case MetadataSchema::MDFTYPE_PARAGRAPH :
693 case MetadataSchema::MDFTYPE_NUMBER :
694 case MetadataSchema::MDFTYPE_DATE :
695 case MetadataSchema::MDFTYPE_TIMESTAMP :
696 case MetadataSchema::MDFTYPE_FLAG :
697 case MetadataSchema::MDFTYPE_URL :
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
712 case MetadataSchema::MDFTYPE_POINT:
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
732 case MetadataSchema::MDFTYPE_USER:
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
814 public function AssociatedVisibleResourceCount(
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
845 $this->UpdateAssociatedVisibleResourceCount(
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
873 public function UpdateAssociatedVisibleResourceCount(
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(
945 MetadataSchema::MDFTYPE_OPTION |
946 MetadataSchema::MDFTYPE_CONTROLLEDNAME);
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
956 $this->ClearVisibleResourceCountForValues(
957 array_keys($Values));
958 }
959
964 public function GetReleasedResourceTotal()
965 {
966 return $this->GetVisibleResourceCount(
967 CWUser::GetAnonymousUser());
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 }
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:626
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:1039
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:814
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:977
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:996
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:1565
ResourceFactory\ClearViewingPermsCache
ClearViewingPermsCache()
Clear the cache of viewable resources.
Definition: ResourceFactory.php:588
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:873
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
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:948
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:927
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:599
ResourceFactory\GetReleasedResourceTotal
GetReleasedResourceTotal()
Get the total number of released resources in the collection.
Definition: ResourceFactory.php:964
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:941
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:1017
ResourceFactory\GetVisibleResourceCount
GetVisibleResourceCount(CWUser $User)
Get the total number of resources visible to a specified user.
Definition: ResourceFactory.php:910
ResourceFactory\DuplicateResource
DuplicateResource($ResourceId)
Duplicate the specified resource and return to caller.
Definition: ResourceFactory.php:47