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
38 public function DuplicateResource($ResourceId)
39 {
40 # check that resource to be duplicated exists
41 if (!Resource::ItemExists($ResourceId))
42 {
43 throw new InvalidArgumentException(
44 "No resource found with specified ID (".$ResourceId.").");
45 }
46
47 # create new target resource
48 $DstResource = Resource::Create($this->SchemaId);
49
50 # load up resource to duplicate
51 $SrcResource = new Resource($ResourceId);
52
53 # for each metadata field
54 $Fields = $this->Schema->GetFields();
55 foreach ($Fields as $Field)
56 {
57 if ($Field->CopyOnResourceDuplication())
58 {
59 $NewValue = $SrcResource->GetByField($Field, TRUE);
60
61 # clear default value from destination resource that is
62 # set when creating a new resource
63 $DstResource->ClearByField($Field);
64
65 # copy value from source resource to destination resource
66 $DstResource->SetByField($Field, $NewValue);
67 }
68 }
69
70 # return new resource to caller
71 return $DstResource;
72 }
73
86 public function ImportResourcesFromXmlFile($FileName)
87 {
88 # open file
89 $In = new XMLReader();
90 $Result = $In->open($FileName);
91
92 # throw exception if file could not be opened
93 if ($Result === FALSE)
94 {
95 throw new Exception("Unable to open file: ".$FileName);
96 }
97
98 # load possible tag names
99 $PossibleTags = array();
100
101 $Fields = $this->Schema->GetFields();
102 foreach ($Fields as $FieldId => $Field)
103 {
104 $NormalizedName = preg_replace(
105 "/[^A-Za-z0-9]/", "", $Field->Name());
106 $PossibleTags[$NormalizedName] = $Field;
107 }
108
109 # arrays to hold ControlledName and Classification factories
110 $CNFacts = array();
111 $CFacts = array();
112
113 # while XML left to read
114 $NewResourceIds = array();
115 while ($In->read())
116 {
117 # if new element
118 if ($In->nodeType == XMLReader::ELEMENT)
119 {
120 # if node indicates start of resource
121 if ($In->name === "Resource")
122 {
123 # if we already had a resource make it non-temporary
124 if (isset($Resource))
125 {
126 $Resource->IsTempResource(FALSE);
127 $NewResourceIds[] = $Resource->Id();
128 }
129
130 # create a new resource
131 $Resource = Resource::Create($this->SchemaId);
132 }
133 # else if node is in list of possible tags
134 elseif (array_key_exists($In->name, $PossibleTags))
135 {
136 # if we have a current resource
137 if (isset($Resource))
138 {
139 # retrieve field and value
140 $DBFieldName = $In->name;
141 $Field = $PossibleTags[$DBFieldName];
142 $In->read();
143 $Value = $In->value;
144 $In->read();
145
146 # set value in resource based on field type
147 switch ($Field->Type())
148 {
149 case MetadataSchema::MDFTYPE_TEXT:
150 case MetadataSchema::MDFTYPE_PARAGRAPH:
151 case MetadataSchema::MDFTYPE_NUMBER:
152 case MetadataSchema::MDFTYPE_TIMESTAMP:
153 case MetadataSchema::MDFTYPE_URL:
154 case MetadataSchema::MDFTYPE_DATE:
155 $Resource->Set($Field, $Value);
156 break;
157
158 case MetadataSchema::MDFTYPE_FLAG:
159 $Resource->Set($Field,
160 (strtoupper($Value) == "TRUE") ? TRUE : FALSE);
161 break;
162
163 case MetadataSchema::MDFTYPE_OPTION:
164 case MetadataSchema::MDFTYPE_CONTROLLEDNAME:
165 if (!isset($CNFacts[$Field->Id()]))
166 {
167 $CNFacts[$Field->Id()] = $Field->GetFactory();
168 }
169
170 $CName = $CNFacts[$Field->Id()]->GetItemByName($Value);
171 if ($CName === NULL)
172 {
173 $CNFacts[$Field->Id()]->ClearCaches();
174 $CName = new ControlledName(
175 NULL, $Value, $Field->Id());
176 }
177 $Resource->Set($Field, $CName);
178 break;
179
180 case MetadataSchema::MDFTYPE_TREE:
181 if (!isset($CFacts[$Field->Id()]))
182 {
183 $CFacts[$Field->Id()] = $Field->GetFactory();
184 }
185
186 $Class = $CFacts[$Field->Id()]->GetItemByName($Value);
187 if ($Class === NULL)
188 {
189 $CFacts[$Field->Id()]->ClearCaches();
190 $Class = Classification::Create($Value, $Field->Id());
191 }
192 $Resource->Set($Field, $Class);
193 break;
194
195 case MetadataSchema::MDFTYPE_POINT:
196 list($Point["X"], $Point["Y"]) = explode(",", $Value);
197 $Resource->Set($Field, $Point);
198 break;
199
200 case MetadataSchema::MDFTYPE_USER:
201 if (preg_match("/^[0-9]+\$/", $Value))
202 {
203 $Value = intval($Value);
204 }
205 $Resource->Set($Field, $Value);
206 break;
207
208 case MetadataSchema::MDFTYPE_IMAGE:
209 case MetadataSchema::MDFTYPE_FILE:
210 case MetadataSchema::MDFTYPE_REFERENCE:
211 break;
212
213 default:
214 break;
215 }
216 }
217 }
218 }
219 }
220
221 # make final resource (if any) non-temporary
222 if (isset($Resource))
223 {
224 $Resource->IsTempResource(FALSE);
225 $NewResourceIds[] = $Resource->Id();
226 }
227
228 # close file
229 $In->close();
230
231 # report to caller what resources were added
232 return $NewResourceIds;
233 }
234
241 public function ClearQualifier($ObjectOrId, $NewObjectOrId = NULL)
242 {
243 # sanitize qualifier ID or retrieve from object
244 $QualifierId = is_object($ObjectOrId)
245 ? $ObjectOrId->Id() : intval($ObjectOrId);
246
247 # if new qualifier passed in
248 if ($NewObjectOrId !== NULL)
249 {
250 # sanitize qualifier ID to change to or retrieve it from object
251 $NewQualifierIdVal = is_object($NewObjectOrId)
252 ? $NewObjectOrId->Id() : intval($NewObjectOrId);
253 }
254 else
255 {
256 # qualifier should be cleared
257 $NewQualifierIdVal = "NULL";
258 }
259
260 # for each metadata field
261 $Fields = $this->Schema->GetFields();
262 foreach ($Fields as $Field)
263 {
264 # if field uses qualifiers and uses item-level qualifiers
265 $QualColName = $Field->DBFieldName()."Qualifier";
266 if ($Field->UsesQualifiers()
267 && $Field->HasItemLevelQualifiers()
268 && $this->DB->FieldExists("Resources", $QualColName))
269 {
270 # set all occurrences to new qualifier value
271 $this->DB->Query("UPDATE Resources"
272 ." SET ".$QualColName." = ".$NewQualifierIdVal.""
273 ." WHERE ".$QualColName." = '".$QualifierId."'"
274 ." AND SchemaId = ".intval($this->SchemaId));
275 }
276 }
277
278 # clear or change qualifier association with controlled names
279 # (NOTE: this should probably be done in a controlled name factory object)
280 $this->DB->Query("UPDATE ControlledNames"
281 ." SET QualifierId = ".$NewQualifierIdVal
282 ." WHERE QualifierId = '".$QualifierId."'");
283
284 # clear or change qualifier association with classifications
285 # (NOTE: this should probably be done in a classification factory object)
286 $this->DB->Query("UPDATE Classifications"
287 ." SET QualifierId = ".$NewQualifierIdVal
288 ." WHERE QualifierId = '".$QualifierId."'");
289 }
290
295 public function GetRatedResourceCount()
296 {
297 return $this->DB->Query(
298 "SELECT COUNT(DISTINCT ResourceId) AS ResourceCount"
299 ." FROM ResourceRatings",
300 "ResourceCount");
301 }
302
307 public function GetRatedResourceUserCount()
308 {
309 return $this->DB->Query(
310 "SELECT COUNT(DISTINCT UserId) AS UserCount"
311 ." FROM ResourceRatings",
312 "UserCount");
313 }
314
324 public function GetRecentlyReleasedResources(
325 $Count = 10, $Offset = 0, $MaxDaysToGoBack = 90)
326 {
327 # assume that no resources will be found
328 $Resources = array();
329
330 # calculate cutoff date for resources
331 $CutoffDate = date("Y-m-d H:i:s", strtotime($MaxDaysToGoBack." days ago"));
332
333 # query for resource IDs
334 $this->DB->Query("SELECT ResourceId FROM Resources WHERE"
335 ." DateOfRecordRelease > '".$CutoffDate."'"
336 ." AND ResourceId >= 0"
337 ." AND SchemaId = ".intval($this->SchemaId)
338 ." ORDER BY DateOfRecordRelease DESC, DateOfRecordCreation DESC");
339 $ResourceIds = $this->DB->FetchColumn("ResourceId");
340
341 # filter out resources that aren't viewable to the public
342 $ResourceIds = $this->FilterNonViewableResources(
343 $ResourceIds, CWUser::GetAnonymousUser() );
344
345 # subset the results as requested
346 $ResourceIds = array_slice(
347 $ResourceIds, $Offset, $Count);
348
349 # for each resource ID found
350 foreach ($ResourceIds as $ResourceId)
351 {
352 # load resource and add to list of found resources
353 $Resources[$ResourceId] = new Resource($ResourceId);
354 }
355
356 # return found resources to caller
357 return $Resources;
358 }
359
368 public function GetResourceIdsSortedBy($FieldId, $Ascending = TRUE, $Limit = NULL)
369 {
370 # assume no resources will be found
371 $ResourceIds = array();
372
373 # if field was found
374 if ($this->Schema->FieldExists($FieldId))
375 {
376 $Field = $this->Schema->GetField($FieldId);
377 # construct query based on field type
378 switch ($Field->Type())
379 {
380 case MetadataSchema::MDFTYPE_TEXT:
381 case MetadataSchema::MDFTYPE_PARAGRAPH:
382 case MetadataSchema::MDFTYPE_URL:
383 $Count = $this->DB->Query("SELECT COUNT(*) AS ResourceCount"
384 ." FROM Resources WHERE "
385 .$Field->DBFieldName()." IS NOT NULL"
386 ." AND LENGTH(LTRIM(RTRIM(".$Field->DBFieldName()."))) > 0"
387 ." AND SchemaId = ".intval($this->SchemaId),
388 "ResourceCount");
389 if ($Count > 0)
390 {
391 $Query = "SELECT ResourceId FROM Resources"
392 ." WHERE SchemaId = ".intval($this->SchemaId)
393 ." ORDER BY ".$Field->DBFieldName()
394 .($Ascending ? " ASC" : " DESC");
395 }
396 break;
397
398 case MetadataSchema::MDFTYPE_NUMBER:
399 case MetadataSchema::MDFTYPE_TIMESTAMP:
400 $Count = $this->DB->Query("SELECT COUNT(*) AS ResourceCount"
401 ." FROM Resources WHERE "
402 .$Field->DBFieldName()." IS NOT NULL"
403 ." AND SchemaId = ".intval($this->SchemaId),
404 "ResourceCount");
405 if ($Count > 0)
406 {
407 $Query = "SELECT ResourceId FROM Resources"
408 ." WHERE SchemaId = ".intval($this->SchemaId)
409 ." ORDER BY ".$Field->DBFieldName()
410 .($Ascending ? " ASC" : " DESC");
411 }
412 break;
413
414 case MetadataSchema::MDFTYPE_DATE:
415 $Count = $this->DB->Query("SELECT COUNT(*) AS ResourceCount"
416 ." FROM Resources WHERE "
417 .$Field->DBFieldName()."Begin IS NOT NULL"
418 ." AND SchemaId = ".intval($this->SchemaId),
419 "ResourceCount");
420 if ($Count > 0)
421 {
422 $Query = "SELECT ResourceId FROM Resources"
423 ." WHERE SchemaId = ".intval($this->SchemaId)
424 ." ORDER BY ".$Field->DBFieldName()."Begin"
425 .($Ascending ? " ASC" : " DESC");
426 }
427 break;
428 }
429
430 # if appropriate query was found
431 if (isset($Query))
432 {
433 # if limited number of results were requested
434 if ($Limit !== NULL)
435 {
436 # add limit to query
437 $Query .= " LIMIT ".intval($Limit);
438 }
439
440 # perform query and retrieve resource IDs
441 $this->DB->Query($Query);
442 $ResourceIds = $this->DB->FetchColumn("ResourceId");
443 }
444 }
445
446 # return resource IDs to caller
447 return $ResourceIds;
448 }
449
456 public function FilterNonViewableResources($ResourceIds, $User)
457 {
458 # compute this user's class
459 $UserClass = $this->ComputeUserClass($User);
460
461 # generate an array where the keys are ResourceIds affected by
462 # user comparisons for the current user
463 $UserComparisonsRIDs = array_flip(
464 $this->ResourcesWhereUserComparisonsMatterForViewing($User));
465
466 # (Note: We can use the $UserClass without a schema prefix as
467 # a cache key even though User Classes are schema specific
468 # because the values we're caching are ResourceIds. Since the
469 # ResourceIds already imply a schema, there's no ambiguity
470 # regarding which schema was involved when the stored UserClass
471 # was computed.)
472 if (!isset(self::$UserClassPermissionsCache[$UserClass]))
473 {
474 # grab all the ResourceIds for this user class
475 $this->DB->Query("SELECT ResourceId, CanView FROM UserPermsCache WHERE"
476 ." UserClass='".$UserClass."'");
477
478 self::$UserClassPermissionsCache[$UserClass] = $this->DB->FetchColumn(
479 "CanView", "ResourceId");
480 }
481
482 # filter out those not requested
483 $Cache = array_intersect_key(
484 self::$UserClassPermissionsCache[$UserClass],
485 array_flip($ResourceIds) );
486
487 # figure out which resources we didn't have cached values for
488 # and iterate over those
489 $MissingIds = array_diff($ResourceIds, array_keys($Cache));
490
491 $PerUserKey = $this->SchemaId.".UID_".$User->Id();
492
493 # batch inserts up into not more than 1000 resources per query
494 $ChunkSize = 1000;
495 $QueryValues = array();
496 foreach ($MissingIds as $Id)
497 {
498 if (isset(self::$PerUserPermissionsCache[$PerUserKey]))
499 {
500 $CanView = self::$PerUserPermissionsCache[$PerUserKey];
501 }
502 else
503 {
504 # evaluate perms for this resource
505 if (Resource::ItemExists($Id))
506 {
507 $Resource = new Resource($Id);
508 $CanView = $Resource->UserCanView($User, FALSE);
509 }
510 else
511 {
512 $CanView = FALSE;
513 }
514
515 # if this is a result we can cache persistently
516 # (i.e. not affected by user comparisons), do so
517 if (!isset($UserComparisonsRIDs[$Id]))
518 {
519 self::$UserClassPermissionsCache[$UserClass][$Id] = $CanView;
520
521 # add this to our queue of inserts
522 $QueryValues[]= "(".$Id.",'".$UserClass."',".($CanView?"1":"0").")" ;
523
524 # if this chunk is full, insert it into the db and clear our queue
525 if (count($QueryValues)>=$ChunkSize)
526 {
527 $this->DB->Query(
528 "INSERT INTO UserPermsCache (ResourceId, UserClass, CanView) "
529 ."VALUES ".implode(",", $QueryValues) );
530 $QueryValues = array();
531 }
532 }
533 else
534 {
535 # this isn't a result we should cache persistently
536 # in the database, but we still want to cache it
537 # within this page load
538 self::$PerUserPermissionsCache[$PerUserKey] = $CanView;
539 }
540
541 }
542 $Cache[$Id] = $CanView;
543 }
544
545 # if we have values left to insert, do so
546 if (count($QueryValues))
547 {
548 $this->DB->Query(
549 "INSERT INTO UserPermsCache (ResourceId, UserClass, CanView) "
550 ."VALUES ".implode(",", $QueryValues) );
551 }
552
553 # if resource view permission check has any handlers that may
554 # modify our cached values
555 if ($GLOBALS["AF"]->IsHookedEvent("EVENT_RESOURCE_VIEW_PERMISSION_CHECK"))
556 {
557 # apply hooked functions to each value
558 foreach (array_keys($Cache) as $Id)
559 {
560 $SignalResult = $GLOBALS["AF"]->SignalEvent(
561 "EVENT_RESOURCE_VIEW_PERMISSION_CHECK",
562 array(
563 "Resource" => $Id,
564 "User" => $User,
565 "CanView" => $Cache[$Id],
566 "Schema" => $this->Schema, ));
567 $Cache[$Id] = $SignalResult["CanView"];
568 }
569 }
570
571 # filter out the non-viewable resources, preserving the order
572 # of resources
573 return array_intersect($ResourceIds,
574 array_keys(array_filter($Cache)) );
575 }
576
580 public function ClearViewingPermsCache()
581 {
582 $this->DB->Query("DELETE FROM UserPermsCache");
583 }
584
585
591 public function GetPossibleFieldNames()
592 {
593 # retrieve field names from schema
594 $FieldNames = array();
595 $Fields = $this->Schema->GetFields();
596 foreach ($Fields as $Field)
597 {
598 $FieldNames[$Field->Id()] = $Field->Name();
599 }
600
601 # return field names to caller
602 return $FieldNames;
603 }
604
618 public function GetMatchingResources(
619 $ValuesToMatch, $AllRequired=TRUE, $ReturnObjects=TRUE,
620 $Operator = "==")
621 {
622 # start out assuming we won't find any resources
623 $Resources = array();
624
625 # fix up equality operator
626 if ($Operator == "==")
627 {
628 $Operator = "=";
629 }
630
631 $LinkingTerm = "";
632 $Condition = "";
633
634 # for each value
635 $Fields = $this->Schema->GetFields();
636 foreach ($ValuesToMatch as $FieldId => $Value)
637 {
638 # only equality supported for NULL
639 if ($Operator != "=" && $Value == "NULL")
640 {
641 throw new Exception(
642 "Invalid operator, ".$Operator." not supported for NULL");
643 }
644
645 # retrieve metadata field ID if not supplied
646 if (!is_numeric($FieldId))
647 {
648 $FieldId = $this->Schema->GetFieldIdByName($FieldId);
649 }
650
651 # if we're attempting to search a field that isn't in our schema,
652 # throw an exception
653 if (!isset($Fields[$FieldId]))
654 {
655 throw new Exception(
656 "Attempt to match values against a field "
657 ."that doesn't exist in this schema");
658 }
659
660 # check that provided operator is sane
661 switch ($Fields[$FieldId]->Type())
662 {
663 case MetadataSchema::MDFTYPE_TEXT:
664 case MetadataSchema::MDFTYPE_PARAGRAPH:
665 case MetadataSchema::MDFTYPE_POINT:
666 case MetadataSchema::MDFTYPE_USER:
667 case MetadataSchema::MDFTYPE_URL:
668 $ValidOps = array("=");
669 break;
670
671 case MetadataSchema::MDFTYPE_FLAG:
672 $ValidOps = array("=", "!=");
673 break;
674
675 case MetadataSchema::MDFTYPE_NUMBER:
676 case MetadataSchema::MDFTYPE_DATE:
677 case MetadataSchema::MDFTYPE_TIMESTAMP:
678 $ValidOps = array("=", "!=", "<", "<=", ">", ">=");
679 break;
680
681 default:
682 $ValidOps = array();
683 }
684
685 if (!in_array($Operator, $ValidOps))
686 {
687 throw new Exception("Operator ".$Operator." not supported for "
688 .$Field->TypeAsName()." fields");
689 }
690
691 # add SQL fragments to Condition as needed
692 switch ($Fields[$FieldId]->Type())
693 {
694 case MetadataSchema::MDFTYPE_TEXT :
695 case MetadataSchema::MDFTYPE_PARAGRAPH :
696 case MetadataSchema::MDFTYPE_NUMBER :
697 case MetadataSchema::MDFTYPE_DATE :
698 case MetadataSchema::MDFTYPE_TIMESTAMP :
699 case MetadataSchema::MDFTYPE_FLAG :
700 case MetadataSchema::MDFTYPE_URL :
701 $DBFname = $Fields[$FieldId]->DBFieldName();
702 # add comparison to condition
703 if ($Value == "NULL")
704 {
705 $Condition .= $LinkingTerm."("
706 .$DBFname." IS NULL OR ".$DBFname." = '')";
707 }
708 else
709 {
710 $Condition .= $LinkingTerm.$DBFname." "
711 .$Operator." '".addslashes($Value)."'";
712 }
713 break;
714
715 case MetadataSchema::MDFTYPE_POINT:
716 $DBFname = $Fields[$FieldId]->DBFieldName();
717
718 if ($Value == "NULL")
719 {
720 $Condition .= $LinkingTerm."("
721 .$DBFname."X IS NULL AND "
722 .$DBFname."Y IS NULL)";
723 }
724 else
725 {
726 $Vx = addslashes($Value["X"]);
727 $Vy = addslashes($value["Y"]);
728
729 $Condition .= $LinkingTerm."("
730 .$DBFname."X = '".$Vx."' AND "
731 .$DBFname."Y = '".$Vy."')";
732 }
733 break;
734
735 case MetadataSchema::MDFTYPE_USER:
736 $TgtValues = array();
737 if (is_object($Value))
738 {
739 $TgtValues[]= $Value->Id();
740 }
741 elseif (is_numeric($Value))
742 {
743 $TgtValues[]= $Value;
744 }
745 elseif (is_array($Value))
746 {
747 foreach ($Value as $UserId => $UserNameOrObject)
748 {
749 $TgtValues[]= $UserId;
750 }
751 }
752
753 # if no users were specified
754 if (!count($TgtValues))
755 {
756 # return no results (nothing matches nothing)
757 return array();
758 }
759 else
760 {
761 # add conditional to match specified users
762 $Condition .= $LinkingTerm."("
763 ."ResourceId IN (SELECT ResourceId FROM "
764 ."ResourceUserInts WHERE FieldId=".intval($FieldId)
765 ." AND UserId IN ("
766 .implode(",", $TgtValues).")) )";
767 }
768 break;
769
770 default:
771 throw new Exception("Unsupported field type");
772 }
773
774 $LinkingTerm = $AllRequired ? " AND " : " OR ";
775 }
776
777 # if there were valid conditions
778 if (strlen($Condition))
779 {
780 # build query statment
781 $Query = "SELECT ResourceId FROM Resources WHERE (".$Condition
782 .") AND SchemaId = ".intval($this->SchemaId);
783
784 # execute query to retrieve matching resource IDs
785 $this->DB->Query($Query);
786 $ResourceIds = $this->DB->FetchColumn("ResourceId");
787
788 if ($ReturnObjects)
789 {
790 # retrieve resource objects
791 foreach ($ResourceIds as $Id)
792 {
793 $Resources[$Id] = new Resource($Id);
794 }
795 }
796 else
797 {
798 $Resources = $ResourceIds;
799 }
800 }
801
802 # return any resources found to caller
803 return $Resources;
804 }
805
817 public function AssociatedVisibleResourceCount(
818 $ValueId, $User, $ForegroundUpdate=FALSE)
819 {
820 # if the specified user is matched by any UserIs or UserIsNot
821 # privset conditions for any resources, then put them in a class
822 # by themselves
823 $UserClass = count($this->ResourcesWhereUserComparisonsMatterForViewing($User))
824 ? "UID_".$User->Id() :
825 $this->ComputeUserClass($User);
826
827 $CacheKey = $this->SchemaId.".".$UserClass;
828 # if we haven't loaded any cached values, do so now
829 if (!isset(self::$VisibleResourceCountCache[$CacheKey]))
830 {
831 $this->DB->Query(
832 "SELECT ResourceCount, ValueId FROM "
833 ."VisibleResourceCounts WHERE "
834 ."SchemaId=".intval($this->SchemaId)
835 ." AND UserClass='".addslashes($UserClass)."'");
836
837 self::$VisibleResourceCountCache[$CacheKey] = $this->DB->FetchColumn(
838 "ResourceCount", "ValueId");
839 }
840
841 # if we don't have a cached value for this class
842 if (!isset(self::$VisibleResourceCountCache[$CacheKey][$ValueId]))
843 {
844 # if we're doing a foreground update
845 if ($ForegroundUpdate)
846 {
847 # run the update callback
848 $this->UpdateAssociatedVisibleResourceCount(
849 $ValueId, $User->Id());
850
851 # grab the newly generated value
852 $NewValue = $this->DB->Query(
853 "SELECT ResourceCount FROM "
854 ."VisibleResourceCounts WHERE "
855 ."SchemaId=".intval($this->SchemaId)
856 ." AND UserClass='".addslashes($UserClass)."' "
857 ." AND ValueId=".intval($ValueId), "ResourceCount");
858
859 # load it into our local cache
860 self::$VisibleResourceCountCache[$CacheKey][$ValueId] = $NewValue;
861 }
862 else
863 {
864 # otherwise (for background update), queue the update
865 # callback and return -1
866 $GLOBALS["AF"]->QueueUniqueTask(
867 array($this, "UpdateAssociatedVisibleResourceCount"),
868 array($ValueId, $User->Id() ) );
869 return -1;
870 }
871 }
872
873 # owtherwise, return the cached data
874 return self::$VisibleResourceCountCache[$CacheKey][$ValueId];
875 }
876
883 public function UpdateAssociatedVisibleResourceCount(
884 $ValueId, $UserId)
885 {
886 $User = new CWUser($UserId);
887
888 # if the specified user is matched by any UserIs or UserIsNot
889 # privset conditions for any resources, then put them in a class
890 # by themselves
891 $UserClass = count($this->ResourcesWhereUserComparisonsMatterForViewing($User))
892 ? "UID_".$User->Id() :
893 $this->ComputeUserClass($User);
894
895 $this->DB->Query(
896 "SELECT ResourceId FROM ResourceNameInts "
897 ."WHERE ControlledNameId=".intval($ValueId) );
898 $ResourceIds = $this->DB->FetchColumn("ResourceId");
899
900 $ResourceIds = $this->FilterNonViewableResources(
901 $ResourceIds, $User);
902
903 $ResourceCount = count($ResourceIds);
904
905 $this->DB->Query(
906 "INSERT INTO VisibleResourceCounts "
907 ."(SchemaId, UserClass, ValueId, ResourceCount) "
908 ."VALUES ("
909 .intval($this->SchemaId).","
910 ."'".addslashes($UserClass)."',"
911 .intval($ValueId).","
912 .$ResourceCount.")");
913 }
914
920 public function GetVisibleResourceCount(CWUser $User)
921 {
922 $ResourceIds = $this->DB->Query(
923 "SELECT ResourceId FROM Resources "
924 ."WHERE ResourceId > 0 AND SchemaId = ".intval($this->SchemaId));
925 $ResourceIds = $this->DB->FetchColumn("ResourceId");
926
927 $ResourceIds = $this->FilterNonViewableResources(
928 $ResourceIds, $User);
929
930 return count($ResourceIds);
931 }
932
933
938 public function GetReleasedResourceTotal()
939 {
940 return $this->GetVisibleResourceCount(
941 CWUser::GetAnonymousUser());
942 }
943
949 public function GetResourceTotal()
950 {
951 return $this->DB->Query("
952 SELECT COUNT(*) AS ResourceTotal
953 FROM Resources
954 WHERE ResourceId > 0
955 AND SchemaId = ".intval($this->SchemaId),
956 "ResourceTotal");
957 }
958
963 public function ClearCaches()
964 {
965 self::$VisibleResourceCountCache = array();
966 self::$UserClassPermissionsCache = array();
967 self::$PerUserPermissionsCache = array();
968 self::$UserClassCache = array();
969 self::$UserComparisonResourceCache = array();
970 self::$UserComparisonFieldCache = array();
971 }
972
973 # ---- PRIVATE INTERFACE -------------------------------------------------
974
975 private $Schema;
976 private $SchemaId;
977
978 # internal caches
979 private static $VisibleResourceCountCache;
980 private static $UserClassPermissionsCache;
981 private static $PerUserPermissionsCache;
982 private static $UserClassCache;
983 private static $UserComparisonResourceCache;
984 private static $UserComparisonFieldCache;
985
993 private function ComputeUserClass($User)
994 {
995 # put the anonymous user into their own user class, otherwise
996 # use the UserId for a key into the ClassCache
997 $UserId = $User->IsAnonymous() ? "XX-ANON-XX" : $User->Id();
998
999 $CacheKey = $this->SchemaId.".".$UserId;
1000
1001 # check if we have a cached UserClass for this User
1002 if (!isset($this->UserClassCache[$CacheKey]))
1003 {
1004 # assemble a list of the privilege flags (PRIV_SYSADMIN,
1005 # etc) that are checked when evaluating the UserCanView for
1006 # all fields in this schema
1007 $RelevantPerms = array();
1008
1009 foreach ($this->Schema->GetFields() as $Field)
1010 {
1011 $RelevantPerms = array_merge(
1012 $RelevantPerms,
1013 $Field->ViewingPrivileges()->PrivilegeFlagsChecked() );
1014 }
1015 $RelevantPerms = array_unique($RelevantPerms);
1016
1017 # whittle the list of all privs checked down to just the
1018 # list of privs that users in this class have
1019 $PermsInvolved = array();
1020 foreach ($RelevantPerms as $Perm)
1021 {
1022 if ($User->HasPriv($Perm))
1023 {
1024 $PermsInvolved[]= $Perm;
1025 }
1026 }
1027
1028 # generate a string by concatenating all the involved
1029 # permissions then hashing the result (hashing gives
1030 # a fixed-size string for storing in the database)
1031 self::$UserClassCache[$CacheKey] = md5(implode( "-", $PermsInvolved ));
1032 }
1033
1034 return self::$UserClassCache[$CacheKey];
1035 }
1036
1046 private function ResourcesWhereUserComparisonsMatterForViewing($User)
1047 {
1048 $ResourceIds = array();
1049
1050 # if we're checking the anonymous user, presume that
1051 # nothing will match
1052 if ($User->IsAnonymous())
1053 {
1054 return $ResourceIds;
1055 }
1056
1057 $CacheKey = $this->SchemaId.".".$User->Id();
1058 if (!isset(self::$UserComparisonResourceCache[$CacheKey]))
1059 {
1060 $Schema = new MetadataSchema($this->SchemaId);
1061
1062 # for each comparison type
1063 foreach (array("==", "!=") as $ComparisonType)
1064 {
1065 $UserComparisonFields = $this->GetUserComparisonFields(
1066 $ComparisonType);
1067
1068 # if we have any fields to check
1069 if (count($UserComparisonFields) > 0 )
1070 {
1071 # query the database for resources where one or more of the
1072 # user comparisons will be satisfied
1073 $SqlOp = ($ComparisonType == "==") ? "= " : "!= ";
1074 $DB = new Database();
1075 $DB->Query("SELECT R.ResourceId as ResourceId FROM ".
1076 "Resources R, ResourceUserInts RU WHERE ".
1077 "R.SchemaId = ".$this->SchemaId." AND ".
1078 "R.ResourceId = RU.ResourceId AND ".
1079 "RU.UserId ".$SqlOp.$User->Id()." AND ".
1080 "RU.FieldId IN (".implode(",", $UserComparisonFields).")");
1081 $Result = $DB->FetchColumn("ResourceId");
1082
1083 # merge those resources into our results
1084 $ResourceIds = array_merge(
1085 $ResourceIds,
1086 $Result);
1087 }
1088 }
1089
1090 self::$UserComparisonResourceCache[$CacheKey] = array_unique($ResourceIds);
1091 }
1092
1093 return self::$UserComparisonResourceCache[$CacheKey];
1094 }
1095
1096
1102 private function GetUserComparisonFields($ComparisonType)
1103 {
1104 $CacheKey = $this->SchemaId.".".$ComparisonType;
1105 if (!isset(self::$UserComparisonFieldCache[$CacheKey]))
1106 {
1107 # iterate through all the fields in the schema,
1108 # constructing a list of the User fields implicated
1109 # in comparisons of the desired type
1110 $UserComparisonFields = array();
1111 foreach ($this->Schema->GetFields() as $Field)
1112 {
1113 $UserComparisonFields = array_merge(
1114 $UserComparisonFields,
1115 $Field->ViewingPrivileges()->FieldsWithUserComparisons(
1116 $ComparisonType) );
1117 }
1118 self::$UserComparisonFieldCache[$CacheKey] =
1119 array_unique($UserComparisonFields);
1120 }
1121
1122 return self::$UserComparisonFieldCache[$CacheKey];
1123 }
1124 }
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:618
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:1034
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:817
MetadataSchema\MDFTYPE_FILE
const MDFTYPE_FILE
Definition: MetadataSchema.php:32
ResourceFactory\GetRatedResourceUserCount
GetRatedResourceUserCount()
Return number of users who have rated resources.
Definition: ResourceFactory.php:307
MetadataSchema
Metadata schema (in effect a Factory class for MetadataField).
Definition: MetadataSchema.php:13
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\GetResourceIdsSortedBy
GetResourceIdsSortedBy($FieldId, $Ascending=TRUE, $Limit=NULL)
Get resource IDs sorted by specified field.
Definition: ResourceFactory.php:368
ResourceFactory\ClearViewingPermsCache
ClearViewingPermsCache()
Clear the cache of viewable resources.
Definition: ResourceFactory.php:580
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:883
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:324
ResourceFactory\ImportResourcesFromXmlFile
ImportResourcesFromXmlFile($FileName)
Import resource records from XML file.
Definition: ResourceFactory.php:86
MetadataSchema\MDFTYPE_TREE
const MDFTYPE_TREE
Definition: MetadataSchema.php:27
MetadataSchema\MDFTYPE_OPTION
const MDFTYPE_OPTION
Definition: MetadataSchema.php:29
ControlledName
Metadata type representing non-hierarchical controlled vocabulary values.
Definition: ControlledName.php:14
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:927
PHP
PHP
Definition: RSSClient--Test.php:12
MetadataSchema\SCHEMAID_DEFAULT
const SCHEMAID_DEFAULT
Definition: MetadataSchema.php:53
ResourceFactory\FilterNonViewableResources
FilterNonViewableResources($ResourceIds, $User)
Filter a list of resources leaving only those viewable by a specified user.
Definition: ResourceFactory.php:456
ResourceFactory\ClearQualifier
ClearQualifier($ObjectOrId, $NewObjectOrId=NULL)
Clear or change specific qualifier for all resources.
Definition: ResourceFactory.php:241
ResourceFactory\ClearCaches
ClearCaches()
Clear internal caches.
Definition: ResourceFactory.php:963
MetadataSchema\MDFTYPE_PARAGRAPH
const MDFTYPE_PARAGRAPH
Definition: MetadataSchema.php:22
MetadataSchema\MDFTYPE_DATE
const MDFTYPE_DATE
Definition: MetadataSchema.php:24
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:591
ResourceFactory\GetReleasedResourceTotal
GetReleasedResourceTotal()
Get the total number of released resources in the collection.
Definition: ResourceFactory.php:938
MetadataSchema\MDFTYPE_TEXT
const MDFTYPE_TEXT
Definition: MetadataSchema.php:21
MetadataSchema\MDFTYPE_USER
const MDFTYPE_USER
Definition: MetadataSchema.php:30
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:155
ResourceFactory\GetRatedResourceCount
GetRatedResourceCount()
Return number of resources that have ratings.
Definition: ResourceFactory.php:295
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:949
ResourceFactory\GetVisibleResourceCount
GetVisibleResourceCount(CWUser $User)
Get the total number of resources visible to a specified user.
Definition: ResourceFactory.php:920
ResourceFactory\DuplicateResource
DuplicateResource($ResourceId)
Duplicate the specified resource and return to caller.
Definition: ResourceFactory.php:38