CWIS Developer Documentation
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 
14 
15  # ---- PUBLIC INTERFACE --------------------------------------------------
16 
23  {
24  # save schema ID
25  $this->SchemaId = $SchemaId;
26 
27  # set up item factory base class
28  $this->ItemFactory("Resource", "Resources", "ResourceId", NULL, FALSE,
29  "SchemaId = ".intval($this->SchemaId));
30  }
31 
37  function DuplicateResource($ResourceId)
38  {
39  # create new target resource
40  $DstResource = Resource::Create($this->SchemaId);
41 
42  # load up resource to duplicate
43  $SrcResource = new Resource($ResourceId);
44 
45  # if resource to duplicate was found
46  if ($SrcResource->Status() > 0)
47  {
48  # for each metadata field
49  $Schema = new MetadataSchema($this->SchemaId);
50  $Fields = $Schema->GetFields();
51  foreach ($Fields as $Field)
52  {
53  # skip the Cumulative Rating and Date Last Modified fields
54  if ($Field->Name() != "Cumulative Rating" &&
55  $Field->Name() != "Date Last Modified")
56  {
57  $NewValue = $SrcResource->GetByField($Field, TRUE);
58 
59  # clear default value from destination resource that is
60  # set when creating a new resource
61  $DstResource->ClearByField($Field);
62 
63  # copy value from source resource to destination resource
64  $DstResource->SetByField($Field, $NewValue);
65  }
66  }
67  }
68 
69  # return new resource to caller
70  return $DstResource;
71  }
72 
79  function ClearQualifier($ObjectOrId, $NewObjectOrId = NULL)
80  {
81  # sanitize qualifier ID or retrieve from object
82  $QualifierId = is_object($ObjectOrId)
83  ? $ObjectOrId->Id() : intval($ObjectOrId);
84 
85  # if new qualifier passed in
86  if ($NewObjectOrId !== NULL)
87  {
88  # sanitize qualifier ID to change to or retrieve it from object
89  $NewQualifierIdVal = is_object($NewObjectOrId)
90  ? $NewObjectOrId->Id() : intval($NewObjectOrId);
91  }
92  else
93  {
94  # qualifier should be cleared
95  $NewQualifierIdVal = "NULL";
96  }
97 
98  # for each metadata field
99  $Schema = new MetadataSchema($this->SchemaId);
100  $Fields = $Schema->GetFields();
101  foreach ($Fields as $Field)
102  {
103  # if field uses qualifiers and uses item-level qualifiers
104  $QualColName = $Field->DBFieldName()."Qualifier";
105  if ($Field->UsesQualifiers()
106  && $Field->HasItemLevelQualifiers()
107  && $this->DB->FieldExists("Resources", $QualColName))
108  {
109  # set all occurrences to new qualifier value
110  $this->DB->Query("UPDATE Resources"
111  ." SET ".$QualColName." = ".$NewQualifierIdVal.""
112  ." WHERE ".$QualColName." = '".$QualifierId."'"
113  ." AND SchemaId = ".intval($this->SchemaId));
114  }
115  }
116 
117  # clear or change qualifier association with controlled names
118  # (NOTE: this should probably be done in a controlled name factory object)
119  $this->DB->Query("UPDATE ControlledNames"
120  ." SET QualifierId = ".$NewQualifierIdVal
121  ." WHERE QualifierId = '".$QualifierId."'");
122 
123  # clear or change qualifier association with classifications
124  # (NOTE: this should probably be done in a classification factory object)
125  $this->DB->Query("UPDATE Classifications"
126  ." SET QualifierId = ".$NewQualifierIdVal
127  ." WHERE QualifierId = '".$QualifierId."'");
128  }
129 
135  {
136  return $this->DB->Query(
137  "SELECT COUNT(DISTINCT ResourceId) AS ResourceCount"
138  ." FROM ResourceRatings",
139  "ResourceCount");
140  }
141 
147  {
148  return $this->DB->Query(
149  "SELECT COUNT(DISTINCT UserId) AS UserCount"
150  ." FROM ResourceRatings",
151  "UserCount");
152  }
153 
163  function GetRecentlyReleasedResources($Count = 10, $Offset = 0, $MaxDaysToGoBack = 90)
164  {
165  # assume that no resources will be found
166  $Resources = array();
167 
168  # calculate cutoff date for resources
169  $CutoffDate = date("Y-m-d H:i:s", strtotime($MaxDaysToGoBack." days ago"));
170 
171  # query for resource IDs
172  $this->DB->Query("SELECT ResourceId FROM Resources WHERE"
173  ." DateOfRecordRelease > '".$CutoffDate."'"
174  ." AND ReleaseFlag = 1"
175  ." AND ResourceId >= 0"
176  ." AND SchemaId = ".intval($this->SchemaId)
177  ." ORDER BY DateOfRecordRelease DESC, DateOfRecordCreation DESC"
178  ." LIMIT ".intval($Offset).", ".intval($Count));
179  $ResourceIds = $this->DB->FetchColumn("ResourceId");
180 
181  # for each resource ID found
182  foreach ($ResourceIds as $ResourceId)
183  {
184  # load resource and add to list of found resources
185  $Resources[$ResourceId] = new Resource($ResourceId);
186  }
187 
188  # return found resources to caller
189  return $Resources;
190  }
191 
200  function GetResourceIdsSortedBy($FieldName, $Ascending = TRUE, $Limit = NULL)
201  {
202  # assume no resources will be found
203  $ResourceIds = array();
204 
205  # get field
206  $Schema = new MetadataSchema($this->SchemaId);
207  $Field = $Schema->GetFieldByName($FieldName);
208 
209  # if field was found
210  if ($Field != NULL)
211  {
212  # construct query based on field type
213  switch ($Field->Type())
214  {
218  $Count = $this->DB->Query("SELECT COUNT(*) AS ResourceCount"
219  ." FROM Resources WHERE "
220  .$Field->DBFieldName()." IS NOT NULL"
221  ." AND LENGTH(LTRIM(RTRIM(".$Field->DBFieldName()."))) > 0"
222  ." AND SchemaId = ".intval($this->SchemaId),
223  "ResourceCount");
224  if ($Count > 1)
225  {
226  $Query = "SELECT ResourceId FROM Resources"
227  ." WHERE SchemaId = ".intval($this->SchemaId)
228  ." ORDER BY ".$Field->DBFieldName()
229  .($Ascending ? " ASC" : " DESC");
230  }
231  break;
232 
235  $Count = $this->DB->Query("SELECT COUNT(*) AS ResourceCount"
236  ." FROM Resources WHERE "
237  .$Field->DBFieldName()." IS NOT NULL"
238  ." AND SchemaId = ".intval($this->SchemaId),
239  "ResourceCount");
240  if ($Count > 1)
241  {
242  $Query = "SELECT ResourceId FROM Resources"
243  ." WHERE SchemaId = ".intval($this->SchemaId)
244  ." ORDER BY ".$Field->DBFieldName()
245  .($Ascending ? " ASC" : " DESC");
246  }
247  break;
248 
250  $Count = $this->DB->Query("SELECT COUNT(*) AS ResourceCount"
251  ." FROM Resources WHERE "
252  .$Field->DBFieldName()."Begin IS NOT NULL"
253  ." AND SchemaId = ".intval($this->SchemaId),
254  "ResourceCount");
255  if ($Count > 1)
256  {
257  $Query = "SELECT ResourceId FROM Resources"
258  ." WHERE SchemaId = ".intval($this->SchemaId)
259  ." ORDER BY ".$Field->DBFieldName()."Begin"
260  .($Ascending ? " ASC" : " DESC");
261  }
262  break;
263  }
264 
265  # if appropriate query was found
266  if (isset($Query))
267  {
268  # if limited number of results were requested
269  if ($Limit !== NULL)
270  {
271  # add limit to query
272  $Query .= " LIMIT ".intval($Limit);
273  }
274 
275  # perform query and retrieve resource IDs
276  $this->DB->Query($Query);
277  $ResourceIds = $this->DB->FetchColumn("ResourceId");
278  }
279  }
280 
281  # return resource IDs to caller
282  return $ResourceIds;
283  }
284 
291  function FilterNonViewableResources($ResourceIds, $User)
292  {
293  $DB = new Database();
294 
295  # compute this user's class
296  $UserClass = $this->ComputeUserClass($User);
297 
298  # generate an array where the keys are ResourceIds affected by
299  # user comparions for the current user
300  $UserComparisonsRIDs = array_flip($this->InUserComparisons($User));
301 
302  # grab all the ResourceIds for this user class
303  $DB->Query("SELECT ResourceId, CanView FROM UserPermsCache WHERE"
304  ." UserClass='".$UserClass."'");
305 
306  # filter out those not requested
307  $Cache = array_intersect_key(
308  $DB->FetchColumn("CanView", "ResourceId"),
309  array_flip($ResourceIds) );
310 
311  # figure out which resources we didn't have cached values for
312  # and iterate over those
313  $MissingIds = array_diff( $ResourceIds, array_keys($Cache) );
314  foreach ($MissingIds as $Id)
315  {
316  # evaluate perms for this resource
317  $Resource = new Resource($Id);
318  $CanView = $Resource->UserCanView($User, FALSE);
319 
320  # if this is a result we can cache, do so
321  if ( !isset($UserComparisonRIDs[ $Id ]) )
322  {
323  $this->DB->Query("INSERT INTO UserPermsCache (ResourceId, UserClass, CanView) "
324  ."VALUES (".$Id.",'".$UserClass."',".($CanView?"1":"0").")");
325  }
326 
327  $Cache[$Id] = $CanView;
328  }
329 
330  # apply schema permissions hooks to all our values
331  foreach (array_keys($Cache) as $Id)
332  {
333  $SignalResult = $GLOBALS["AF"]->SignalEvent(
334  "EVENT_RESOURCE_VIEW_PERMISSION_CHECK",
335  array(
336  "Resource" => $Id,
337  "User" => $User,
338  "CanView" => $Cache[$Id]));
339  $Cache[$Id] = $SignalResult["CanView"];
340  }
341 
342  # return the viewable ResourceIds
343  return array_keys( array_filter($Cache) );
344 
345  }
346 
351  {
352  $DB = new Database();
353  $DB->Query("DELETE FROM UserPermsCache");
354  }
355 
362  function GetTimestampOfLastResourceModification($OnlyReleasedResources = TRUE)
363  {
364  $LastChangeDate = $this->DB->Query(
365  "SELECT MAX(DateLastModified) AS LastChangeDate"
366  ." FROM Resources"
367  ." WHERE SchemaId = ".intval($this->SchemaId)
368  .($OnlyReleasedResources ? " AND ReleaseFlag = 1" : ""),
369  "LastChangeDate");
370  return ($LastChangeDate ? strtotime($LastChangeDate) : NULL);
371  }
372 
379  {
380  # retrieve field names from schema
381  $FieldNames = array();
382  $Schema = new MetadataSchema($this->SchemaId);
383  $Fields = $Schema->GetFields();
384  foreach ($Fields as $Field)
385  {
386  $FieldNames[$Field->Id()] = $Field->Name();
387  }
388 
389  # return field names to caller
390  return $FieldNames;
391  }
392 
400  function GetMatchingResources($ValuesToMatch)
401  {
402  # start out assuming we won't find any resources
403  $Resources = array();
404 
405  # for each value
406  $Schema = new MetadataSchema($this->SchemaId);
407  $Fields = $Schema->GetFields(
415  $LinkingTerm = "";
416  $Condition = "";
417  foreach ($ValuesToMatch as $FieldId => $Value)
418  {
419  # if field can be used for comparison
420  if (isset($Fields[$FieldId]))
421  {
422  # add comparison to condition
423  $Condition .= $LinkingTerm.$Fields[$FieldId]->DBFieldName()
424  ." = '".addslashes($Value)."'";
425  $LinkingTerm = " AND ";
426  }
427  }
428 
429  # if there were valid conditions
430  if (strlen($Condition))
431  {
432  # build query statment
433  $Query = "SELECT ResourceId FROM Resources WHERE ".$Condition
434  ." AND SchemaId = ".intval($this->SchemaId);
435 
436  # execute query to retrieve matching resource IDs
437  $this->DB->Query($Query);
438  $ResourceIds = $this->DB->FetchColumn("ResourceId");
439 
440  # retrieve resource objects
441  foreach ($ResourceIds as $Id)
442  {
443  $Resources[$Id] = new Resource($Id);
444  }
445  }
446 
447  # return any resources found to caller
448  return $Resources;
449  }
450 
451  # Functions for keeping per-field resource counts updated:
452 
463  function GetResourceCount($FieldId, $Value, $CountType="All")
464  {
465  if ($FieldId<0)
466  return NULL;
467 
468  $Schema = new MetadataSchema($this->SchemaId);
469  $Field = $Schema->GetField($FieldId);
470  if ($Field === NULL)
471  return NULL;
472 
473  if ($this->ResourceCount === NULL)
474  {
475  $this->DB->Query(
476  "SELECT FieldId, ClassName, CountType, Count FROM ResourceCounts");
477 
478  while ($Row = $this->DB->FetchRow())
479  {
480  $R_FieldId = $Row["FieldId"];
481  $R_ClassName = $Row["ClassName"];
482  $R_CountType = $Row["CountType"];
483  $R_Count = $Row["Count"];
484 
485  $this->ResourceCount[$R_FieldId][$R_ClassName][$R_CountType] = $R_Count;
486  }
487  }
488 
489  if ($Field->Type() === MetadataSchema::MDFTYPE_OPTION
490  || $Field->Type() === MetadataSchema::MDFTYPE_CONTROLLEDNAME)
491  {
492  return isset($this->ResourceCount[$FieldId][$Value][$CountType]) ?
493  $this->ResourceCount[$FieldId][$Value][$CountType] :
494  0 ;
495  }
496  else
497  {
498  return NULL;
499  }
500  }
501 
506  public function GetReleasedResourceTotal()
507  {
508  return $this->DB->Query("
509  SELECT COUNT(*) AS ResourceTotal
510  FROM Resources
511  WHERE ResourceId > 0
512  AND ReleaseFlag = 1
513  AND SchemaId = ".intval($this->SchemaId),
514  "ResourceTotal");
515  }
516 
522  public function GetResourceTotal()
523  {
524  return $this->DB->Query("
525  SELECT COUNT(*) AS ResourceTotal
526  FROM Resources
527  WHERE ResourceId > 0
528  AND SchemaId = ".intval($this->SchemaId),
529  "ResourceTotal");
530  }
531 
540  {
541  global $AF;
542 
543  # be sure that we're not a gigantic object when the task is queued
544  $TmpResourceCount = $this->ResourceCount;
545  $this->ResourceCount = NULL;
546 
547  $AF->QueueUniqueTask(
548  array($this,"UpdateResourceCountCallback"), array());
549  $this->ResourceCount = $TmpResourceCount;
550  }
551 
559  {
560  $DB = new Database();
561 
562  $DB->Query(
563  "CREATE TABLE ResourceCountsNew (FieldId INT, ClassName TEXT, CountType TEXT, Count INT);");
564 
565  $Start = microtime(TRUE);
566 
567  foreach ($this->ResourceCountConditions as $CountType => $CountCondition)
568  {
569  $DB->Query(
570  "INSERT INTO ResourceCountsNew "
571  ."SELECT FieldId, ControlledName AS ClassName,"
572  ."'".$CountType."' AS CountType, Count(ResourceId) AS Count "
573  ."FROM (SELECT * FROM ResourceNameInts WHERE ResourceId IN "
574  ."(SELECT ResourceId FROM Resources "
575  ." WHERE SchemaId = ".intval($this->SchemaId)
576  .(($CountCondition!==NULL)
577  ?" AND ".$CountCondition:"").")) AS T0 "
578  ."JOIN ControlledNames USING(ControlledNameId) GROUP BY ControlledNameId;" );
579  }
580 
581  $Stop = microtime(TRUE);
582 
583  $DB->Query(
584  "INSERT INTO ResourceCountsNew VALUES (-1, '__LAST_UPDATED__', '', UNIX_TIMESTAMP()); ");
585  $DB->Query(
586  "INSERT INTO ResourceCountsNew VALUES (-2, '__UPDATE_RUNTIME__','',".($Stop-$Start).");");
587  $DB->Query(
588  "RENAME TABLE ResourceCounts TO ResourceCountsOld, ResourceCountsNew TO ResourceCounts; ");
589  $DB->Query(
590  "DROP TABLE ResourceCountsOld; ");
591  }
592 
593  # ---- PRIVATE INTERFACE -------------------------------------------------
594 
595  private $ResourceCount = NULL;
596  private $ResourceCountConditions = array("All" => NULL, "Released" => "ReleaseFlag=1");
597  private $SchemaId;
598 
604  private function ComputeUserClass( $User )
605  {
606  static $ClassCache;
607 
608  if (!isset($ClassCache))
609  {
610  $ClassCache = array();
611  }
612 
613  $UserId = $User->IsLoggedIn() ? $User->Id() : "XX-ANON-XX";
614 
615  if (!isset($ClassCache[$UserId]))
616  {
617  $RelevantPerms = array();
618 
619  $Schema = new MetadataSchema();
620  foreach ($Schema->GetFields() as $Field)
621  {
622  $RelevantPerms = array_merge(
623  $RelevantPerms,
624  $Field->ViewingPrivileges()->PrivilegeFlagsChecked() );
625  }
626  $RelevantPerms = array_unique($RelevantPerms);
627 
628  $PermsInvolved = array();
629  foreach ($RelevantPerms as $Perm)
630  {
631  if ($User->HasPriv($Perm))
632  $PermsInvolved []= $Perm;
633  }
634  $ClassCache[$UserId] = md5( implode( "-", $PermsInvolved ) );
635  }
636 
637  return $ClassCache[$UserId];
638  }
639 
640 
647  private function InUserComparisons( $User )
648  {
649  return array_unique(
650  array_merge(
651  $this->CheckUserComparisons( $User, "=="),
652  $this->CheckUserComparisons( $User, "!=") ) ) ;
653  }
654 
663  private function CheckUserComparisons( $User, $ComparisonType )
664  {
665  # assume no resources match
666  $Result = array();
667 
668  # if we're checking the anonymous user, presume that
669  # nothing will match
670  if ( !$User->IsLoggedIn() )
671  return $Result;
672 
673  # iterate through all the fields in the schema,
674  # constructing a list of the User fields implicated
675  # in "User is the value of" comparisions
676  $Schema = new MetadataSchema();
677  $UserComparisonFields = array();
678  foreach ($Schema->GetFields() as $Field)
679  {
680  $UserComparisonFields = array_merge(
681  $UserComparisonFields,
682  $Field->ViewingPrivileges()->FieldsWithUserComparisons($ComparisonType) );
683  }
684  $UserComparisonFields = array_unique($UserComparisonFields);
685 
686  # from the list of fields, construct an array of SQL conditions to
687  # count how many resources are implicated
688  $SqlConditions = array();
689  foreach ($UserComparisonFields as $FieldId)
690  {
691  $Field = new MetadataField($FieldId);
692  $SqlConditions []= $Field->DBFieldName().
693  ($ComparisonType == "==" ? " = " : " != ")
694  .$User->Id();
695  }
696 
697  # use the list of SQL conditions to see if any resources
698  # will actually match this predicate
699  if (count($SqlConditions)>0)
700  {
701  $DB = new Database();
702  $Query = "SELECT ResourceId FROM Resources WHERE "
703  ."SchemaId=".$this->SchemaId." AND ("
704  .implode(" OR ", $SqlConditions).")";
705 
706  $Result = $DB->FetchColumn("ResourceId");
707  }
708 
709  return $Result;
710  }
711 
712 
713 
714 
715 }
GetRatedResourceUserCount()
Return number of users who have rated resources.
Metadata schema (in effect a Factory class for MetadataField).
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.
ResourceFactory($SchemaId=MetadataSchema::SCHEMAID_DEFAULT)
Class constructor.
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.
const MDFTYPE_CONTROLLEDNAME
PHP
Definition: OAIClient.php:39
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.
Object representing a locally-defined type of metadata field.
Represents a "resource" in CWIS.
Definition: Resource.php:13
GetPossibleFieldNames()
Get possible field names for resources.
GetReleasedResourceTotal()
Get the total number of released resources in the collection.
static Create($SchemaId)
Create a new resource.
Definition: Resource.php:59
Common factory class for item manipulation.
Definition: ItemFactory.php:17
GetRatedResourceCount()
Return number of resources that have ratings.
Factory for Resource objects.
ItemFactory($ItemClassName, $ItemTableName, $ItemIdFieldName, $ItemNameFieldName=NULL, $OrderOpsAllowed=FALSE, $SqlCondition=NULL)
Class constructor.
Definition: ItemFactory.php:36
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.