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 = new ControlledName(
184 NULL, $Value, $Field->Id());
185 }
186 $Resource->Set($Field, $CName);
187 break;
188
189 case MetadataSchema::MDFTYPE_TREE:
190 if (!isset($CFacts[$Field->Id()]))
191 {
192 $CFacts[$Field->Id()] = $Field->GetFactory();
193 }
194
195 $Class = $CFacts[$Field->Id()]->GetItemByName($Value);
196 if ($Class === NULL)
197 {
198 $CFacts[$Field->Id()]->ClearCaches();
199 $Class = Classification::Create($Value, $Field->Id());
200 }
201 $Resource->Set($Field, $Class);
202 break;
203
204 case MetadataSchema::MDFTYPE_POINT:
205 list($Point["X"], $Point["Y"]) = explode(",", $Value);
206 $Resource->Set($Field, $Point);
207 break;
208
209 case MetadataSchema::MDFTYPE_USER:
210 if (preg_match("/^[0-9]+\$/", $Value))
211 {
212 $Value = intval($Value);
213 }
214 $Resource->Set($Field, $Value);
215 break;
216
217 case MetadataSchema::MDFTYPE_IMAGE:
218 case MetadataSchema::MDFTYPE_FILE:
219 case MetadataSchema::MDFTYPE_REFERENCE:
220 break;
221
222 default:
223 break;
224 }
225 }
226 }
227 }
228 }
229
230 # make final resource (if any) non-temporary
231 if (isset($Resource))
232 {
233 $Resource->IsTempResource(FALSE);
234 $NewResourceIds[] = $Resource->Id();
235 }
236
237 # close file
238 $In->close();
239
240 # report to caller what resources were added
241 return $NewResourceIds;
242 }
243
250 public function ClearQualifier($ObjectOrId, $NewObjectOrId = NULL)
251 {
252 # sanitize qualifier ID or retrieve from object
253 $QualifierId = is_object($ObjectOrId)
254 ? $ObjectOrId->Id() : intval($ObjectOrId);
255
256 # if new qualifier passed in
257 if ($NewObjectOrId !== NULL)
258 {
259 # sanitize qualifier ID to change to or retrieve it from object
260 $NewQualifierIdVal = is_object($NewObjectOrId)
261 ? $NewObjectOrId->Id() : intval($NewObjectOrId);
262 }
263 else
264 {
265 # qualifier should be cleared
266 $NewQualifierIdVal = "NULL";
267 }
268
269 # for each metadata field
270 $Fields = $this->Schema->GetFields();
271 foreach ($Fields as $Field)
272 {
273 # if field uses qualifiers and uses item-level qualifiers
274 $QualColName = $Field->DBFieldName()."Qualifier";
275 if ($Field->UsesQualifiers()
276 && $Field->HasItemLevelQualifiers()
277 && $this->DB->FieldExists("Resources", $QualColName))
278 {
279 # set all occurrences to new qualifier value
280 $this->DB->Query("UPDATE Resources"
281 ." SET ".$QualColName." = ".$NewQualifierIdVal.""
282 ." WHERE ".$QualColName." = '".$QualifierId."'"
283 ." AND SchemaId = ".intval($this->SchemaId));
284 }
285 }
286
287 # clear or change qualifier association with controlled names
288 # (NOTE: this should probably be done in a controlled name factory object)
289 $this->DB->Query("UPDATE ControlledNames"
290 ." SET QualifierId = ".$NewQualifierIdVal
291 ." WHERE QualifierId = '".$QualifierId."'");
292
293 # clear or change qualifier association with classifications
294 # (NOTE: this should probably be done in a classification factory object)
295 $this->DB->Query("UPDATE Classifications"
296 ." SET QualifierId = ".$NewQualifierIdVal
297 ." WHERE QualifierId = '".$QualifierId."'");
298 }
299
304 public function GetRatedResourceCount()
305 {
306 return $this->DB->Query(
307 "SELECT COUNT(DISTINCT ResourceId) AS ResourceCount"
308 ." FROM ResourceRatings",
309 "ResourceCount");
310 }
311
316 public function GetRatedResourceUserCount()
317 {
318 return $this->DB->Query(
319 "SELECT COUNT(DISTINCT UserId) AS UserCount"
320 ." FROM ResourceRatings",
321 "UserCount");
322 }
323
333 public function GetRecentlyReleasedResources(
334 $Count = 10, $Offset = 0, $MaxDaysToGoBack = 90)
335 {
336 # assume that no resources will be found
337 $Resources = array();
338
339 # calculate cutoff date for resources
340 $CutoffDate = date("Y-m-d H:i:s", strtotime($MaxDaysToGoBack." days ago"));
341
342 # query for resource IDs
343 $this->DB->Query("SELECT ResourceId FROM Resources WHERE"
344 ." DateOfRecordRelease > '".$CutoffDate."'"
345 ." AND ResourceId >= 0"
346 ." AND SchemaId = ".intval($this->SchemaId)
347 ." ORDER BY DateOfRecordRelease DESC, DateOfRecordCreation DESC");
348 $ResourceIds = $this->DB->FetchColumn("ResourceId");
349
350 # filter out resources that aren't viewable to the public
351 $ResourceIds = $this->FilterNonViewableResources(
352 $ResourceIds, CWUser::GetAnonymousUser() );
353
354 # subset the results as requested
355 $ResourceIds = array_slice(
356 $ResourceIds, $Offset, $Count);
357
358 # for each resource ID found
359 foreach ($ResourceIds as $ResourceId)
360 {
361 # load resource and add to list of found resources
362 $Resources[$ResourceId] = new Resource($ResourceId);
363 }
364
365 # return found resources to caller
366 return $Resources;
367 }
368
377 public function GetResourceIdsSortedBy($FieldId, $Ascending = TRUE, $Limit = NULL)
378 {
379 # assume no resources will be found
380 $ResourceIds = array();
381
382 # if field was found
383 if ($this->Schema->FieldExists($FieldId))
384 {
385 $Field = $this->Schema->GetField($FieldId);
386 # construct query based on field type
387 switch ($Field->Type())
388 {
389 case MetadataSchema::MDFTYPE_TEXT:
390 case MetadataSchema::MDFTYPE_PARAGRAPH:
391 case MetadataSchema::MDFTYPE_URL:
392 $Count = $this->DB->Query("SELECT COUNT(*) AS ResourceCount"
393 ." FROM Resources WHERE "
394 .$Field->DBFieldName()." IS NOT NULL"
395 ." AND LENGTH(LTRIM(RTRIM(".$Field->DBFieldName()."))) > 0"
396 ." AND SchemaId = ".intval($this->SchemaId),
397 "ResourceCount");
398 if ($Count > 0)
399 {
400 $Query = "SELECT ResourceId FROM Resources"
401 ." WHERE SchemaId = ".intval($this->SchemaId)
402 ." ORDER BY ".$Field->DBFieldName()
403 .($Ascending ? " ASC" : " DESC");
404 }
405 break;
406
407 case MetadataSchema::MDFTYPE_NUMBER:
408 case MetadataSchema::MDFTYPE_TIMESTAMP:
409 $Count = $this->DB->Query("SELECT COUNT(*) AS ResourceCount"
410 ." FROM Resources WHERE "
411 .$Field->DBFieldName()." IS NOT NULL"
412 ." AND SchemaId = ".intval($this->SchemaId),
413 "ResourceCount");
414 if ($Count > 0)
415 {
416 $Query = "SELECT ResourceId FROM Resources"
417 ." WHERE SchemaId = ".intval($this->SchemaId)
418 ." ORDER BY ".$Field->DBFieldName()
419 .($Ascending ? " ASC" : " DESC");
420 }
421 break;
422
423 case MetadataSchema::MDFTYPE_DATE:
424 $Count = $this->DB->Query("SELECT COUNT(*) AS ResourceCount"
425 ." FROM Resources WHERE "
426 .$Field->DBFieldName()."Begin IS NOT NULL"
427 ." AND SchemaId = ".intval($this->SchemaId),
428 "ResourceCount");
429 if ($Count > 0)
430 {
431 $Query = "SELECT ResourceId FROM Resources"
432 ." WHERE SchemaId = ".intval($this->SchemaId)
433 ." ORDER BY ".$Field->DBFieldName()."Begin"
434 .($Ascending ? " ASC" : " DESC");
435 }
436 break;
437 }
438
439 # if appropriate query was found
440 if (isset($Query))
441 {
442 # if limited number of results were requested
443 if ($Limit !== NULL)
444 {
445 # add limit to query
446 $Query .= " LIMIT ".intval($Limit);
447 }
448
449 # perform query and retrieve resource IDs
450 $this->DB->Query($Query);
451 $ResourceIds = $this->DB->FetchColumn("ResourceId");
452 }
453 }
454
455 # return resource IDs to caller
456 return $ResourceIds;
457 }
458
465 public function FilterNonViewableResources($ResourceIds, $User)
466 {
467 # compute this user's class
468 $UserClass = $this->ComputeUserClass($User);
469
470 # generate an array where the keys are ResourceIds affected by
471 # user comparisons for the current user
472 $UserComparisonsRIDs = array_flip(
473 $this->ResourcesWhereUserComparisonsMatterForViewing($User));
474
475 # (Note: We can use the $UserClass without a schema prefix as
476 # a cache key even though User Classes are schema specific
477 # because the values we're caching are ResourceIds. Since the
478 # ResourceIds already imply a schema, there's no ambiguity
479 # regarding which schema was involved when the stored UserClass
480 # was computed.)
481 if (!isset(self::$UserClassPermissionsCache[$UserClass]))
482 {
483 # grab all the ResourceIds for this user class
484 $this->DB->Query("SELECT ResourceId, CanView FROM UserPermsCache WHERE"
485 ." UserClass='".$UserClass."'");
486
487 self::$UserClassPermissionsCache[$UserClass] = $this->DB->FetchColumn(
488 "CanView", "ResourceId");
489 }
490
491 # filter out those not requested
492 $Cache = array_intersect_key(
493 self::$UserClassPermissionsCache[$UserClass],
494 array_flip($ResourceIds) );
495
496 # figure out which resources we didn't have cached values for
497 # and iterate over those
498 $MissingIds = array_diff($ResourceIds, array_keys($Cache));
499
500 $PerUserKey = $this->SchemaId.".UID_".$User->Id();
501
502 # batch inserts up into not more than 1000 resources per query
503 $ChunkSize = 1000;
504 $QueryValues = array();
505 foreach ($MissingIds as $Id)
506 {
507 if (isset(self::$PerUserPermissionsCache[$PerUserKey]))
508 {
509 $CanView = self::$PerUserPermissionsCache[$PerUserKey];
510 }
511 else
512 {
513 # evaluate perms for this resource
514 if (Resource::ItemExists($Id))
515 {
516 $Resource = new Resource($Id);
517 $CanView = $Resource->UserCanView($User, FALSE);
518 }
519 else
520 {
521 $CanView = FALSE;
522 }
523
524 # if this is a result we can cache persistently
525 # (i.e. not affected by user comparisons), do so
526 if (!isset($UserComparisonsRIDs[$Id]))
527 {
528 self::$UserClassPermissionsCache[$UserClass][$Id] = $CanView;
529
530 # add this to our queue of inserts
531 $QueryValues[]= "(".$Id.",'".$UserClass."',".($CanView?"1":"0").")" ;
532
533 # if this chunk is full, insert it into the db and clear our queue
534 if (count($QueryValues)>=$ChunkSize)
535 {
536 $this->DB->Query(
537 "INSERT INTO UserPermsCache (ResourceId, UserClass, CanView) "
538 ."VALUES ".implode(",", $QueryValues) );
539 $QueryValues = array();
540 }
541 }
542 else
543 {
544 # this isn't a result we should cache persistently
545 # in the database, but we still want to cache it
546 # within this page load
547 self::$PerUserPermissionsCache[$PerUserKey] = $CanView;
548 }
549
550 }
551 $Cache[$Id] = $CanView;
552 }
553
554 # if we have values left to insert, do so
555 if (count($QueryValues))
556 {
557 $this->DB->Query(
558 "INSERT INTO UserPermsCache (ResourceId, UserClass, CanView) "
559 ."VALUES ".implode(",", $QueryValues) );
560 }
561
562 # if resource view permission check has any handlers that may
563 # modify our cached values
564 if ($GLOBALS["AF"]->IsHookedEvent("EVENT_RESOURCE_VIEW_PERMISSION_CHECK"))
565 {
566 # apply hooked functions to each value
567 foreach (array_keys($Cache) as $Id)
568 {
569 $SignalResult = $GLOBALS["AF"]->SignalEvent(
570 "EVENT_RESOURCE_VIEW_PERMISSION_CHECK",
571 array(
572 "Resource" => $Id,
573 "User" => $User,
574 "CanView" => $Cache[$Id],
575 "Schema" => $this->Schema, ));
576 $Cache[$Id] = $SignalResult["CanView"];
577 }
578 }
579
580 # filter out the non-viewable resources, preserving the order
581 # of resources
582 return array_intersect($ResourceIds,
583 array_keys(array_filter($Cache)) );
584 }
585
589 public function ClearViewingPermsCache()
590 {
591 $this->DB->Query("DELETE FROM UserPermsCache");
592 }
593
594
600 public function GetPossibleFieldNames()
601 {
602 # retrieve field names from schema
603 $FieldNames = array();
604 $Fields = $this->Schema->GetFields();
605 foreach ($Fields as $Field)
606 {
607 $FieldNames[$Field->Id()] = $Field->Name();
608 }
609
610 # return field names to caller
611 return $FieldNames;
612 }
613
627 public function GetMatchingResources(
628 $ValuesToMatch, $AllRequired=TRUE, $ReturnObjects=TRUE,
629 $Operator = "==")
630 {
631 # start out assuming we won't find any resources
632 $Resources = array();
633
634 # fix up equality operator
635 if ($Operator == "==")
636 {
637 $Operator = "=";
638 }
639
640 $LinkingTerm = "";
641 $Condition = "";
642
643 # for each value
644 $Fields = $this->Schema->GetFields();
645 foreach ($ValuesToMatch as $FieldId => $Value)
646 {
647 # only equality supported for NULL
648 if ($Operator != "=" && $Value == "NULL")
649 {
650 throw new Exception(
651 "Invalid operator, ".$Operator." not supported for NULL");
652 }
653
654 # convert supplied FieldId to canonical identifier
655 $FieldId = MetadataSchema::GetCanonicalFieldIdentifier(
656 $FieldId, $this->SchemaId);
657
658 # check that provided operator is sane
659 switch ($Fields[$FieldId]->Type())
660 {
661 case MetadataSchema::MDFTYPE_TEXT:
662 case MetadataSchema::MDFTYPE_PARAGRAPH:
663 case MetadataSchema::MDFTYPE_POINT:
664 case MetadataSchema::MDFTYPE_USER:
665 case MetadataSchema::MDFTYPE_URL:
666 $ValidOps = array("=");
667 break;
668
669 case MetadataSchema::MDFTYPE_FLAG:
670 $ValidOps = array("=", "!=");
671 break;
672
673 case MetadataSchema::MDFTYPE_NUMBER:
674 case MetadataSchema::MDFTYPE_DATE:
675 case MetadataSchema::MDFTYPE_TIMESTAMP:
676 $ValidOps = array("=", "!=", "<", "<=", ">", ">=");
677 break;
678
679 default:
680 $ValidOps = array();
681 }
682
683 if (!in_array($Operator, $ValidOps))
684 {
685 throw new Exception("Operator ".$Operator." not supported for "
686 .$Field->TypeAsName()." fields");
687 }
688
689 # add SQL fragments to Condition as needed
690 switch ($Fields[$FieldId]->Type())
691 {
692 case MetadataSchema::MDFTYPE_TEXT :
693 case MetadataSchema::MDFTYPE_PARAGRAPH :
694 case MetadataSchema::MDFTYPE_NUMBER :
695 case MetadataSchema::MDFTYPE_DATE :
696 case MetadataSchema::MDFTYPE_TIMESTAMP :
697 case MetadataSchema::MDFTYPE_FLAG :
698 case MetadataSchema::MDFTYPE_URL :
699 $DBFname = $Fields[$FieldId]->DBFieldName();
700 # add comparison to condition
701 if ($Value == "NULL")
702 {
703 $Condition .= $LinkingTerm."("
704 .$DBFname." IS NULL OR ".$DBFname." = '')";
705 }
706 else
707 {
708 $Condition .= $LinkingTerm.$DBFname." "
709 .$Operator." '".addslashes($Value)."'";
710 }
711 break;
712
713 case MetadataSchema::MDFTYPE_POINT:
714 $DBFname = $Fields[$FieldId]->DBFieldName();
715
716 if ($Value == "NULL")
717 {
718 $Condition .= $LinkingTerm."("
719 .$DBFname."X IS NULL AND "
720 .$DBFname."Y IS NULL)";
721 }
722 else
723 {
724 $Vx = addslashes($Value["X"]);
725 $Vy = addslashes($value["Y"]);
726
727 $Condition .= $LinkingTerm."("
728 .$DBFname."X = '".$Vx."' AND "
729 .$DBFname."Y = '".$Vy."')";
730 }
731 break;
732
733 case MetadataSchema::MDFTYPE_USER:
734 $TgtValues = array();
735 if (is_object($Value))
736 {
737 $TgtValues[]= $Value->Id();
738 }
739 elseif (is_numeric($Value))
740 {
741 $TgtValues[]= $Value;
742 }
743 elseif (is_array($Value))
744 {
745 foreach ($Value as $UserId => $UserNameOrObject)
746 {
747 $TgtValues[]= $UserId;
748 }
749 }
750
751 # if no users were specified
752 if (!count($TgtValues))
753 {
754 # return no results (nothing matches nothing)
755 return array();
756 }
757 else
758 {
759 # add conditional to match specified users
760 $Condition .= $LinkingTerm."("
761 ."ResourceId IN (SELECT ResourceId FROM "
762 ."ResourceUserInts WHERE FieldId=".intval($FieldId)
763 ." AND UserId IN ("
764 .implode(",", $TgtValues).")) )";
765 }
766 break;
767
768 default:
769 throw new Exception("Unsupported field type");
770 }
771
772 $LinkingTerm = $AllRequired ? " AND " : " OR ";
773 }
774
775 # if there were valid conditions
776 if (strlen($Condition))
777 {
778 # build query statment
779 $Query = "SELECT ResourceId FROM Resources WHERE (".$Condition
780 .") AND SchemaId = ".intval($this->SchemaId);
781
782 # execute query to retrieve matching resource IDs
783 $this->DB->Query($Query);
784 $ResourceIds = $this->DB->FetchColumn("ResourceId");
785
786 if ($ReturnObjects)
787 {
788 # retrieve resource objects
789 foreach ($ResourceIds as $Id)
790 {
791 $Resources[$Id] = new Resource($Id);
792 }
793 }
794 else
795 {
796 $Resources = $ResourceIds;
797 }
798 }
799
800 # return any resources found to caller
801 return $Resources;
802 }
803
815 public function AssociatedVisibleResourceCount(
816 $ValueId, $User, $ForegroundUpdate=FALSE)
817 {
818 # if the specified user is matched by any UserIs or UserIsNot
819 # privset conditions for any resources, then put them in a class
820 # by themselves
821 $UserClass = count($this->ResourcesWhereUserComparisonsMatterForViewing($User))
822 ? "UID_".$User->Id() :
823 $this->ComputeUserClass($User);
824
825 $CacheKey = $this->SchemaId.".".$UserClass;
826 # if we haven't loaded any cached values, do so now
827 if (!isset(self::$VisibleResourceCountCache[$CacheKey]))
828 {
829 $this->DB->Query(
830 "SELECT ResourceCount, ValueId FROM "
831 ."VisibleResourceCounts WHERE "
832 ."SchemaId=".intval($this->SchemaId)
833 ." AND UserClass='".addslashes($UserClass)."'");
834
835 self::$VisibleResourceCountCache[$CacheKey] = $this->DB->FetchColumn(
836 "ResourceCount", "ValueId");
837 }
838
839 # if we don't have a cached value for this class
840 if (!isset(self::$VisibleResourceCountCache[$CacheKey][$ValueId]))
841 {
842 # if we're doing a foreground update
843 if ($ForegroundUpdate)
844 {
845 # run the update callback
846 $this->UpdateAssociatedVisibleResourceCount(
847 $ValueId, $User->Id());
848
849 # grab the newly generated value
850 $NewValue = $this->DB->Query(
851 "SELECT ResourceCount FROM "
852 ."VisibleResourceCounts WHERE "
853 ."SchemaId=".intval($this->SchemaId)
854 ." AND UserClass='".addslashes($UserClass)."' "
855 ." AND ValueId=".intval($ValueId), "ResourceCount");
856
857 # load it into our local cache
858 self::$VisibleResourceCountCache[$CacheKey][$ValueId] = $NewValue;
859 }
860 else
861 {
862 # otherwise (for background update), queue the update
863 # callback and return -1
864 $GLOBALS["AF"]->QueueUniqueTask(
865 array($this, "UpdateAssociatedVisibleResourceCount"),
866 array($ValueId, $User->Id() ) );
867 return -1;
868 }
869 }
870
871 # owtherwise, return the cached data
872 return self::$VisibleResourceCountCache[$CacheKey][$ValueId];
873 }
874
881 public function UpdateAssociatedVisibleResourceCount(
882 $ValueId, $UserId)
883 {
884 $User = new CWUser($UserId);
885
886 # if the specified user is matched by any UserIs or UserIsNot
887 # privset conditions for any resources, then put them in a class
888 # by themselves
889 $UserClass = count($this->ResourcesWhereUserComparisonsMatterForViewing($User))
890 ? "UID_".$User->Id() :
891 $this->ComputeUserClass($User);
892
893 $this->DB->Query(
894 "SELECT ResourceId FROM ResourceNameInts "
895 ."WHERE ControlledNameId=".intval($ValueId) );
896 $ResourceIds = $this->DB->FetchColumn("ResourceId");
897
898 $ResourceIds = $this->FilterNonViewableResources(
899 $ResourceIds, $User);
900
901 $ResourceCount = count($ResourceIds);
902
903 $this->DB->Query(
904 "INSERT INTO VisibleResourceCounts "
905 ."(SchemaId, UserClass, ValueId, ResourceCount) "
906 ."VALUES ("
907 .intval($this->SchemaId).","
908 ."'".addslashes($UserClass)."',"
909 .intval($ValueId).","
910 .$ResourceCount.")");
911 }
912
918 public function GetVisibleResourceCount(CWUser $User)
919 {
920 $ResourceIds = $this->DB->Query(
921 "SELECT ResourceId FROM Resources "
922 ."WHERE ResourceId > 0 AND SchemaId = ".intval($this->SchemaId));
923 $ResourceIds = $this->DB->FetchColumn("ResourceId");
924
925 $ResourceIds = $this->FilterNonViewableResources(
926 $ResourceIds, $User);
927
928 return count($ResourceIds);
929 }
930
931
936 public function GetReleasedResourceTotal()
937 {
938 return $this->GetVisibleResourceCount(
939 CWUser::GetAnonymousUser());
940 }
941
947 public function GetResourceTotal()
948 {
949 return $this->DB->Query("
950 SELECT COUNT(*) AS ResourceTotal
951 FROM Resources
952 WHERE ResourceId > 0
953 AND SchemaId = ".intval($this->SchemaId),
954 "ResourceTotal");
955 }
956
961 public function ClearCaches()
962 {
963 self::$VisibleResourceCountCache = array();
964 self::$UserClassPermissionsCache = array();
965 self::$PerUserPermissionsCache = array();
966 self::$UserClassCache = array();
967 self::$UserComparisonResourceCache = array();
968 self::$UserComparisonFieldCache = array();
969 }
970
971 # ---- PRIVATE INTERFACE -------------------------------------------------
972
973 private $Schema;
974 private $SchemaId;
975
976 # internal caches
977 private static $VisibleResourceCountCache;
978 private static $UserClassPermissionsCache;
979 private static $PerUserPermissionsCache;
980 private static $UserClassCache;
981 private static $UserComparisonResourceCache;
982 private static $UserComparisonFieldCache;
983
991 private function ComputeUserClass($User)
992 {
993 # put the anonymous user into their own user class, otherwise
994 # use the UserId for a key into the ClassCache
995 $UserId = $User->IsAnonymous() ? "XX-ANON-XX" : $User->Id();
996
997 $CacheKey = $this->SchemaId.".".$UserId;
998
999 # check if we have a cached UserClass for this User
1000 if (!isset($this->UserClassCache[$CacheKey]))
1001 {
1002 # assemble a list of the privilege flags (PRIV_SYSADMIN,
1003 # etc) that are checked when evaluating the UserCanView for
1004 # all fields in this schema
1005 $RelevantPerms = array();
1006
1007 foreach ($this->Schema->GetFields() as $Field)
1008 {
1009 $RelevantPerms = array_merge(
1010 $RelevantPerms,
1011 $Field->ViewingPrivileges()->PrivilegeFlagsChecked() );
1012 }
1013 $RelevantPerms = array_unique($RelevantPerms);
1014
1015 # whittle the list of all privs checked down to just the
1016 # list of privs that users in this class have
1017 $PermsInvolved = array();
1018 foreach ($RelevantPerms as $Perm)
1019 {
1020 if ($User->HasPriv($Perm))
1021 {
1022 $PermsInvolved[]= $Perm;
1023 }
1024 }
1025
1026 # generate a string by concatenating all the involved
1027 # permissions then hashing the result (hashing gives
1028 # a fixed-size string for storing in the database)
1029 self::$UserClassCache[$CacheKey] = md5(implode( "-", $PermsInvolved ));
1030 }
1031
1032 return self::$UserClassCache[$CacheKey];
1033 }
1034
1044 private function ResourcesWhereUserComparisonsMatterForViewing($User)
1045 {
1046 $ResourceIds = array();
1047
1048 # if we're checking the anonymous user, presume that
1049 # nothing will match
1050 if ($User->IsAnonymous())
1051 {
1052 return $ResourceIds;
1053 }
1054
1055 $CacheKey = $this->SchemaId.".".$User->Id();
1056 if (!isset(self::$UserComparisonResourceCache[$CacheKey]))
1057 {
1058 $Schema = new MetadataSchema($this->SchemaId);
1059
1060 # for each comparison type
1061 foreach (array("==", "!=") as $ComparisonType)
1062 {
1063 $UserComparisonFields = $this->GetUserComparisonFields(
1064 $ComparisonType);
1065
1066 # if we have any fields to check
1067 if (count($UserComparisonFields) > 0 )
1068 {
1069 # query the database for resources where one or more of the
1070 # user comparisons will be satisfied
1071 $SqlOp = ($ComparisonType == "==") ? "= " : "!= ";
1072 $DB = new Database();
1073 $DB->Query("SELECT R.ResourceId as ResourceId FROM ".
1074 "Resources R, ResourceUserInts RU WHERE ".
1075 "R.SchemaId = ".$this->SchemaId." AND ".
1076 "R.ResourceId = RU.ResourceId AND ".
1077 "RU.UserId ".$SqlOp.$User->Id()." AND ".
1078 "RU.FieldId IN (".implode(",", $UserComparisonFields).")");
1079 $Result = $DB->FetchColumn("ResourceId");
1080
1081 # merge those resources into our results
1082 $ResourceIds = array_merge(
1083 $ResourceIds,
1084 $Result);
1085 }
1086 }
1087
1088 self::$UserComparisonResourceCache[$CacheKey] = array_unique($ResourceIds);
1089 }
1090
1091 return self::$UserComparisonResourceCache[$CacheKey];
1092 }
1093
1094
1100 private function GetUserComparisonFields($ComparisonType)
1101 {
1102 $CacheKey = $this->SchemaId.".".$ComparisonType;
1103 if (!isset(self::$UserComparisonFieldCache[$CacheKey]))
1104 {
1105 # iterate through all the fields in the schema,
1106 # constructing a list of the User fields implicated
1107 # in comparisons of the desired type
1108 $UserComparisonFields = array();
1109 foreach ($this->Schema->GetFields() as $Field)
1110 {
1111 $UserComparisonFields = array_merge(
1112 $UserComparisonFields,
1113 $Field->ViewingPrivileges()->FieldsWithUserComparisons(
1114 $ComparisonType) );
1115 }
1116 self::$UserComparisonFieldCache[$CacheKey] =
1117 array_unique($UserComparisonFields);
1118 }
1119
1120 return self::$UserComparisonFieldCache[$CacheKey];
1121 }
1122 }
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:627
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:1038
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:815
MetadataSchema\MDFTYPE_FILE
const MDFTYPE_FILE
Definition: MetadataSchema.php:32
ResourceFactory\GetRatedResourceUserCount
GetRatedResourceUserCount()
Return number of users who have rated resources.
Definition: ResourceFactory.php:316
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:377
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:589
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:881
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:333
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
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:465
ResourceFactory\ClearQualifier
ClearQualifier($ObjectOrId, $NewObjectOrId=NULL)
Clear or change specific qualifier for all resources.
Definition: ResourceFactory.php:250
ResourceFactory\ClearCaches
ClearCaches()
Clear internal caches.
Definition: ResourceFactory.php:961
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:600
ResourceFactory\GetReleasedResourceTotal
GetReleasedResourceTotal()
Get the total number of released resources in the collection.
Definition: ResourceFactory.php:936
MetadataSchema\MDFTYPE_TEXT
const MDFTYPE_TEXT
Definition: MetadataSchema.php:21
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:304
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:947
ResourceFactory\GetVisibleResourceCount
GetVisibleResourceCount(CWUser $User)
Get the total number of resources visible to a specified user.
Definition: ResourceFactory.php:918
ResourceFactory\DuplicateResource
DuplicateResource($ResourceId)
Duplicate the specified resource and return to caller.
Definition: ResourceFactory.php:47