3 # FILE: ResourceFactory.php
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/
15 # ---- PUBLIC INTERFACE --------------------------------------------------
25 $this->SchemaId = $SchemaId;
27 # set up item factory base class
28 $this->
ItemFactory(
"Resource",
"Resources",
"ResourceId", NULL, FALSE,
29 "SchemaId = ".intval($this->SchemaId));
39 # create new target resource
42 # load up resource to duplicate
43 $SrcResource =
new Resource($ResourceId);
45 # if resource to duplicate was found
46 if ($SrcResource->Status() > 0)
48 # for each metadata field
50 $Fields = $Schema->GetFields();
51 foreach ($Fields as $Field)
53 if ($Field->CopyOnResourceDuplication())
55 $NewValue = $SrcResource->GetByField($Field, TRUE);
57 # clear default value from destination resource that is
58 # set when creating a new resource
59 $DstResource->ClearByField($Field);
61 # copy value from source resource to destination resource
62 $DstResource->SetByField($Field, $NewValue);
67 # return new resource to caller
79 # sanitize qualifier ID or retrieve from object
80 $QualifierId = is_object($ObjectOrId)
81 ? $ObjectOrId->Id() : intval($ObjectOrId);
83 # if new qualifier passed in
84 if ($NewObjectOrId !== NULL)
86 # sanitize qualifier ID to change to or retrieve it from object
87 $NewQualifierIdVal = is_object($NewObjectOrId)
88 ? $NewObjectOrId->Id() : intval($NewObjectOrId);
92 # qualifier should be cleared
93 $NewQualifierIdVal =
"NULL";
96 # for each metadata field
98 $Fields = $Schema->GetFields();
99 foreach ($Fields as $Field)
101 # if field uses qualifiers and uses item-level qualifiers
102 $QualColName = $Field->DBFieldName().
"Qualifier";
103 if ($Field->UsesQualifiers()
104 && $Field->HasItemLevelQualifiers()
105 && $this->DB->FieldExists(
"Resources", $QualColName))
107 # set all occurrences to new qualifier value
108 $this->DB->Query(
"UPDATE Resources"
109 .
" SET ".$QualColName.
" = ".$NewQualifierIdVal.
""
110 .
" WHERE ".$QualColName.
" = '".$QualifierId.
"'"
111 .
" AND SchemaId = ".intval($this->SchemaId));
115 # clear or change qualifier association with controlled names
116 # (NOTE: this should probably be done in a controlled name factory object)
117 $this->DB->Query(
"UPDATE ControlledNames"
118 .
" SET QualifierId = ".$NewQualifierIdVal
119 .
" WHERE QualifierId = '".$QualifierId.
"'");
121 # clear or change qualifier association with classifications
122 # (NOTE: this should probably be done in a classification factory object)
123 $this->DB->Query(
"UPDATE Classifications"
124 .
" SET QualifierId = ".$NewQualifierIdVal
125 .
" WHERE QualifierId = '".$QualifierId.
"'");
134 return $this->DB->Query(
135 "SELECT COUNT(DISTINCT ResourceId) AS ResourceCount"
136 .
" FROM ResourceRatings",
146 return $this->DB->Query(
147 "SELECT COUNT(DISTINCT UserId) AS UserCount"
148 .
" FROM ResourceRatings",
163 # assume that no resources will be found
164 $Resources = array();
166 # calculate cutoff date for resources
167 $CutoffDate = date(
"Y-m-d H:i:s", strtotime($MaxDaysToGoBack.
" days ago"));
169 # query for resource IDs
170 $this->DB->Query(
"SELECT ResourceId FROM Resources WHERE"
171 .
" DateOfRecordRelease > '".$CutoffDate.
"'"
172 .
" AND ReleaseFlag = 1"
173 .
" AND ResourceId >= 0"
174 .
" AND SchemaId = ".intval($this->SchemaId)
175 .
" ORDER BY DateOfRecordRelease DESC, DateOfRecordCreation DESC"
176 .
" LIMIT ".intval($Offset).
", ".intval($Count));
177 $ResourceIds = $this->DB->FetchColumn(
"ResourceId");
179 # for each resource ID found
180 foreach ($ResourceIds as $ResourceId)
182 # load resource and add to list of found resources
183 $Resources[$ResourceId] =
new Resource($ResourceId);
186 # return found resources to caller
200 # assume no resources will be found
201 $ResourceIds = array();
205 $Field = $Schema->GetFieldByName($FieldName);
210 # construct query based on field type
211 switch ($Field->Type())
216 $Count = $this->DB->Query(
"SELECT COUNT(*) AS ResourceCount"
217 .
" FROM Resources WHERE "
218 .$Field->DBFieldName().
" IS NOT NULL"
219 .
" AND LENGTH(LTRIM(RTRIM(".$Field->DBFieldName().
"))) > 0"
220 .
" AND SchemaId = ".intval($this->SchemaId),
224 $Query =
"SELECT ResourceId FROM Resources"
225 .
" WHERE SchemaId = ".intval($this->SchemaId)
226 .
" ORDER BY ".$Field->DBFieldName()
227 .($Ascending ?
" ASC" :
" DESC");
233 $Count = $this->DB->Query(
"SELECT COUNT(*) AS ResourceCount"
234 .
" FROM Resources WHERE "
235 .$Field->DBFieldName().
" IS NOT NULL"
236 .
" AND SchemaId = ".intval($this->SchemaId),
240 $Query =
"SELECT ResourceId FROM Resources"
241 .
" WHERE SchemaId = ".intval($this->SchemaId)
242 .
" ORDER BY ".$Field->DBFieldName()
243 .($Ascending ?
" ASC" :
" DESC");
248 $Count = $this->DB->Query(
"SELECT COUNT(*) AS ResourceCount"
249 .
" FROM Resources WHERE "
250 .$Field->DBFieldName().
"Begin IS NOT NULL"
251 .
" AND SchemaId = ".intval($this->SchemaId),
255 $Query =
"SELECT ResourceId FROM Resources"
256 .
" WHERE SchemaId = ".intval($this->SchemaId)
257 .
" ORDER BY ".$Field->DBFieldName().
"Begin"
258 .($Ascending ?
" ASC" :
" DESC");
263 # if appropriate query was found
266 # if limited number of results were requested
270 $Query .=
" LIMIT ".intval($Limit);
273 # perform query and retrieve resource IDs
274 $this->DB->Query($Query);
275 $ResourceIds = $this->DB->FetchColumn(
"ResourceId");
279 # return resource IDs to caller
293 # compute this user's class
294 $UserClass = $this->ComputeUserClass($User);
296 # generate an array where the keys are ResourceIds affected by
297 # user comparions for the current user
298 $UserComparisonsRIDs = array_flip($this->InUserComparisons($User));
300 # grab all the ResourceIds for this user class
301 $DB->Query(
"SELECT ResourceId, CanView FROM UserPermsCache WHERE"
302 .
" UserClass='".$UserClass.
"'");
304 # filter out those not requested
305 $Cache = array_intersect_key(
306 $DB->FetchColumn(
"CanView",
"ResourceId"),
307 array_flip($ResourceIds) );
309 # figure out which resources we didn't have cached values for
310 # and iterate over those
311 $MissingIds = array_diff( $ResourceIds, array_keys($Cache) );
312 foreach ($MissingIds as $Id)
314 # evaluate perms for this resource
316 $CanView = $Resource->UserCanView($User, FALSE);
318 # if this is a result we can cache, do so
319 if ( !isset($UserComparisonRIDs[ $Id ]) )
322 "INSERT INTO UserPermsCache (ResourceId, UserClass, CanView) "
323 .
"VALUES (".$Id.
",'".$UserClass.
"',".($CanView?
"1":
"0").
")");
326 $Cache[$Id] = $CanView;
329 # apply schema permissions hooks to all our values
330 foreach (array_keys($Cache) as $Id)
332 $SignalResult = $GLOBALS[
"AF"]->SignalEvent(
333 "EVENT_RESOURCE_VIEW_PERMISSION_CHECK",
337 "CanView" => $Cache[$Id]));
338 $Cache[$Id] = $SignalResult[
"CanView"];
341 # return the viewable ResourceIds
342 return array_keys( array_filter($Cache) );
352 $DB->Query(
"DELETE FROM UserPermsCache");
363 $LastChangeDate = $this->DB->Query(
364 "SELECT MAX(DateLastModified) AS LastChangeDate"
366 .
" WHERE SchemaId = ".intval($this->SchemaId)
367 .($OnlyReleasedResources ?
" AND ReleaseFlag = 1" :
""),
369 return ($LastChangeDate ? strtotime($LastChangeDate) : NULL);
379 # retrieve field names from schema
380 $FieldNames = array();
382 $Fields = $Schema->GetFields();
383 foreach ($Fields as $Field)
385 $FieldNames[$Field->Id()] = $Field->Name();
388 # return field names to caller
401 # start out assuming we won't find any resources
402 $Resources = array();
406 $Fields = $Schema->GetFields(
416 foreach ($ValuesToMatch as $FieldId => $Value)
418 # if field can be used for comparison
419 if (isset($Fields[$FieldId]))
421 # add comparison to condition
422 $Condition .= $LinkingTerm.$Fields[$FieldId]->DBFieldName()
423 .
" = '".addslashes($Value).
"'";
424 $LinkingTerm =
" AND ";
428 # if there were valid conditions
429 if (strlen($Condition))
431 # build query statment
432 $Query =
"SELECT ResourceId FROM Resources WHERE ".$Condition
433 .
" AND SchemaId = ".intval($this->SchemaId);
435 # execute query to retrieve matching resource IDs
436 $this->DB->Query($Query);
437 $ResourceIds = $this->DB->FetchColumn(
"ResourceId");
439 # retrieve resource objects
440 foreach ($ResourceIds as $Id)
442 $Resources[$Id] =
new Resource($Id);
446 # return any resources found to caller
450 # Functions for keeping per-field resource counts updated:
470 $Field = $Schema->GetField($FieldId);
476 if ($this->ResourceCount === NULL)
479 "SELECT FieldId, ClassName, CountType, Count FROM ResourceCounts");
481 while ($Row = $this->DB->FetchRow())
483 $R_FieldId = $Row[
"FieldId"];
484 $R_ClassName = $Row[
"ClassName"];
485 $R_CountType = $Row[
"CountType"];
486 $R_Count = $Row[
"Count"];
488 $this->ResourceCount[$R_FieldId][$R_ClassName][$R_CountType] = $R_Count;
495 return isset($this->ResourceCount[$FieldId][$Value][$CountType]) ?
496 $this->ResourceCount[$FieldId][$Value][$CountType] :
511 return $this->DB->Query(
"
512 SELECT COUNT(*) AS ResourceTotal
516 AND SchemaId = ".intval($this->SchemaId),
527 return $this->DB->Query(
"
528 SELECT COUNT(*) AS ResourceTotal
531 AND SchemaId = ".intval($this->SchemaId),
546 # be sure that we're not a gigantic object when the task is queued
547 $TmpResourceCount = $this->ResourceCount;
548 $this->ResourceCount = NULL;
550 $AF->QueueUniqueTask(
551 array($this,
"UpdateResourceCountCallback"), array());
552 $this->ResourceCount = $TmpResourceCount;
566 "CREATE TABLE ResourceCountsNew "
567 .
"(FieldId INT, ClassName TEXT, CountType TEXT, Count INT);");
569 $Start = microtime(TRUE);
571 foreach ($this->ResourceCountConditions as $CountType => $CountCondition)
574 "INSERT INTO ResourceCountsNew "
575 .
"SELECT FieldId, ControlledName AS ClassName,"
576 .
"'".$CountType.
"' AS CountType, Count(ResourceId) AS Count "
577 .
"FROM (SELECT * FROM ResourceNameInts WHERE ResourceId IN "
578 .
"(SELECT ResourceId FROM Resources "
579 .
" WHERE SchemaId = ".intval($this->SchemaId)
580 .(($CountCondition!==NULL)
581 ?
" AND ".$CountCondition:
"").
")) AS T0 "
582 .
"JOIN ControlledNames USING(ControlledNameId) "
583 .
"GROUP BY ControlledNameId;" );
586 $Stop = microtime(TRUE);
588 $DB->Query(
"INSERT INTO ResourceCountsNew "
589 .
"VALUES (-1, '__LAST_UPDATED__', '', UNIX_TIMESTAMP()); ");
590 $DB->Query(
"INSERT INTO ResourceCountsNew "
591 .
"VALUES (-2, '__UPDATE_RUNTIME__','',".($Stop-$Start).
");");
593 "RENAME TABLE ResourceCounts TO ResourceCountsOld,"
594 .
" ResourceCountsNew TO ResourceCounts; ");
596 "DROP TABLE ResourceCountsOld; ");
599 # ---- PRIVATE INTERFACE -------------------------------------------------
601 private $ResourceCount = NULL;
602 private $ResourceCountConditions =
603 array(
"All" => NULL,
"Released" =>
"ReleaseFlag=1");
611 private function ComputeUserClass( $User )
615 if (!isset($ClassCache))
617 $ClassCache = array();
620 $UserId = $User->IsLoggedIn() ? $User->Id() :
"XX-ANON-XX";
622 if (!isset($ClassCache[$UserId]))
624 $RelevantPerms = array();
627 foreach ($Schema->GetFields() as $Field)
629 $RelevantPerms = array_merge(
631 $Field->ViewingPrivileges()->PrivilegeFlagsChecked() );
633 $RelevantPerms = array_unique($RelevantPerms);
635 $PermsInvolved = array();
636 foreach ($RelevantPerms as $Perm)
638 if ($User->HasPriv($Perm))
640 $PermsInvolved []= $Perm;
643 $ClassCache[$UserId] = md5( implode(
"-", $PermsInvolved ) );
646 return $ClassCache[$UserId];
656 private function InUserComparisons( $User )
660 $this->CheckUserComparisons( $User,
"=="),
661 $this->CheckUserComparisons( $User,
"!=") ) ) ;
672 private function CheckUserComparisons( $User, $ComparisonType )
674 # assume no resources match
677 # if we're checking the anonymous user, presume that
679 if ( !$User->IsLoggedIn() )
684 # iterate through all the fields in the schema,
685 # constructing a list of the User fields implicated
686 # in "User is the value of" comparisions
688 $UserComparisonFields = array();
689 foreach ($Schema->GetFields() as $Field)
691 $UserComparisonFields = array_merge(
692 $UserComparisonFields,
693 $Field->ViewingPrivileges()->FieldsWithUserComparisons($ComparisonType) );
695 $UserComparisonFields = array_unique($UserComparisonFields);
697 # from the list of fields, construct an array of SQL conditions to
698 # count how many resources are implicated
699 $SqlConditions = array();
700 foreach ($UserComparisonFields as $FieldId)
703 $SqlConditions []= $Field->DBFieldName().
704 ($ComparisonType ==
"==" ?
" = " :
" != ")
708 # use the list of SQL conditions to see if any resources
709 # will actually match this predicate
710 if (count($SqlConditions)>0)
713 $Query =
"SELECT ResourceId FROM Resources WHERE "
714 .
"SchemaId=".$this->SchemaId.
" AND ("
715 .implode(
" OR ", $SqlConditions).
")";
717 $Result =
$DB->FetchColumn(
"ResourceId");
GetRatedResourceUserCount()
Return number of users who have rated resources.
SQL database abstraction object with smart query caching.
GetTimestampOfLastResourceModification($OnlyReleasedResources=TRUE)
Get date/time of when last a resource was modified.
UpdateResourceCountCallback()
Update the stored counts of resources per controlled name, looking at the private var $ResourceCountC...
GetResourceCount($FieldId, $Value, $CountType="All")
Return the number of resources having a given value set for a specified ControlledName field...
ClearViewingPermsCache()
Clear the cache of viewable resources.
GetResourceIdsSortedBy($FieldName, $Ascending=TRUE, $Limit=NULL)
Get resource IDs sorted by specified field.
GetRecentlyReleasedResources($Count=10, $Offset=0, $MaxDaysToGoBack=90)
Get resources sorted by descending Date of Record Release, with Date of Record Creation as the second...
QueueResourceCountUpdate()
Add a task to the queue which will update the resource counts for ControlledNames.
GetMatchingResources($ValuesToMatch)
Find resources with values that match those specified.
FilterNonViewableResources($ResourceIds, $User)
Filter a list of resources leaving only those viewable by a specified user.
ClearQualifier($ObjectOrId, $NewObjectOrId=NULL)
Clear or change specific qualifier for all resources.
Represents a "resource" in CWIS.
GetPossibleFieldNames()
Get possible field names for resources.
GetReleasedResourceTotal()
Get the total number of released resources in the collection.
__construct($SchemaId=MetadataSchema::SCHEMAID_DEFAULT)
Class constructor.
static Create($SchemaId)
Create a new resource.
Common factory class for item manipulation.
GetRatedResourceCount()
Return number of resources that have ratings.
Factory for Resource objects.
ItemFactory($ItemClassName, $ItemTableName, $ItemIdFieldName, $ItemNameFieldName=NULL, $OrderOpsAllowed=FALSE, $SqlCondition=NULL)
Class constructor.
GetResourceTotal()
Get the total number of resources in the collection, even if they are not released.
DuplicateResource($ResourceId)
Duplicate the specified resource and return to caller.