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  if ($Field->CopyOnResourceDuplication())
54  {
55  $NewValue = $SrcResource->GetByField($Field, TRUE);
56 
57  # clear default value from destination resource that is
58  # set when creating a new resource
59  $DstResource->ClearByField($Field);
60 
61  # copy value from source resource to destination resource
62  $DstResource->SetByField($Field, $NewValue);
63  }
64  }
65  }
66 
67  # return new resource to caller
68  return $DstResource;
69  }
70 
77  function ClearQualifier($ObjectOrId, $NewObjectOrId = NULL)
78  {
79  # sanitize qualifier ID or retrieve from object
80  $QualifierId = is_object($ObjectOrId)
81  ? $ObjectOrId->Id() : intval($ObjectOrId);
82 
83  # if new qualifier passed in
84  if ($NewObjectOrId !== NULL)
85  {
86  # sanitize qualifier ID to change to or retrieve it from object
87  $NewQualifierIdVal = is_object($NewObjectOrId)
88  ? $NewObjectOrId->Id() : intval($NewObjectOrId);
89  }
90  else
91  {
92  # qualifier should be cleared
93  $NewQualifierIdVal = "NULL";
94  }
95 
96  # for each metadata field
97  $Schema = new MetadataSchema($this->SchemaId);
98  $Fields = $Schema->GetFields();
99  foreach ($Fields as $Field)
100  {
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))
106  {
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));
112  }
113  }
114 
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."'");
120 
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."'");
126  }
127 
133  {
134  return $this->DB->Query(
135  "SELECT COUNT(DISTINCT ResourceId) AS ResourceCount"
136  ." FROM ResourceRatings",
137  "ResourceCount");
138  }
139 
145  {
146  return $this->DB->Query(
147  "SELECT COUNT(DISTINCT UserId) AS UserCount"
148  ." FROM ResourceRatings",
149  "UserCount");
150  }
151 
161  function GetRecentlyReleasedResources($Count = 10, $Offset = 0, $MaxDaysToGoBack = 90)
162  {
163  # assume that no resources will be found
164  $Resources = array();
165 
166  # calculate cutoff date for resources
167  $CutoffDate = date("Y-m-d H:i:s", strtotime($MaxDaysToGoBack." days ago"));
168 
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");
178 
179  # for each resource ID found
180  foreach ($ResourceIds as $ResourceId)
181  {
182  # load resource and add to list of found resources
183  $Resources[$ResourceId] = new Resource($ResourceId);
184  }
185 
186  # return found resources to caller
187  return $Resources;
188  }
189 
198  function GetResourceIdsSortedBy($FieldName, $Ascending = TRUE, $Limit = NULL)
199  {
200  # assume no resources will be found
201  $ResourceIds = array();
202 
203  # get field
204  $Schema = new MetadataSchema($this->SchemaId);
205  $Field = $Schema->GetFieldByName($FieldName);
206 
207  # if field was found
208  if ($Field != NULL)
209  {
210  # construct query based on field type
211  switch ($Field->Type())
212  {
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),
221  "ResourceCount");
222  if ($Count > 0)
223  {
224  $Query = "SELECT ResourceId FROM Resources"
225  ." WHERE SchemaId = ".intval($this->SchemaId)
226  ." ORDER BY ".$Field->DBFieldName()
227  .($Ascending ? " ASC" : " DESC");
228  }
229  break;
230 
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),
237  "ResourceCount");
238  if ($Count > 0)
239  {
240  $Query = "SELECT ResourceId FROM Resources"
241  ." WHERE SchemaId = ".intval($this->SchemaId)
242  ." ORDER BY ".$Field->DBFieldName()
243  .($Ascending ? " ASC" : " DESC");
244  }
245  break;
246 
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),
252  "ResourceCount");
253  if ($Count > 0)
254  {
255  $Query = "SELECT ResourceId FROM Resources"
256  ." WHERE SchemaId = ".intval($this->SchemaId)
257  ." ORDER BY ".$Field->DBFieldName()."Begin"
258  .($Ascending ? " ASC" : " DESC");
259  }
260  break;
261  }
262 
263  # if appropriate query was found
264  if (isset($Query))
265  {
266  # if limited number of results were requested
267  if ($Limit !== NULL)
268  {
269  # add limit to query
270  $Query .= " LIMIT ".intval($Limit);
271  }
272 
273  # perform query and retrieve resource IDs
274  $this->DB->Query($Query);
275  $ResourceIds = $this->DB->FetchColumn("ResourceId");
276  }
277  }
278 
279  # return resource IDs to caller
280  return $ResourceIds;
281  }
282 
289  function FilterNonViewableResources($ResourceIds, $User)
290  {
291  $DB = new Database();
292 
293  # compute this user's class
294  $UserClass = $this->ComputeUserClass($User);
295 
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));
299 
300  # grab all the ResourceIds for this user class
301  $DB->Query("SELECT ResourceId, CanView FROM UserPermsCache WHERE"
302  ." UserClass='".$UserClass."'");
303 
304  # filter out those not requested
305  $Cache = array_intersect_key(
306  $DB->FetchColumn("CanView", "ResourceId"),
307  array_flip($ResourceIds) );
308 
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)
313  {
314  # evaluate perms for this resource
315  $Resource = new Resource($Id);
316  $CanView = $Resource->UserCanView($User, FALSE);
317 
318  # if this is a result we can cache, do so
319  if ( !isset($UserComparisonRIDs[ $Id ]) )
320  {
321  $this->DB->Query(
322  "INSERT INTO UserPermsCache (ResourceId, UserClass, CanView) "
323  ."VALUES (".$Id.",'".$UserClass."',".($CanView?"1":"0").")");
324  }
325 
326  $Cache[$Id] = $CanView;
327  }
328 
329  # apply schema permissions hooks to all our values
330  foreach (array_keys($Cache) as $Id)
331  {
332  $SignalResult = $GLOBALS["AF"]->SignalEvent(
333  "EVENT_RESOURCE_VIEW_PERMISSION_CHECK",
334  array(
335  "Resource" => $Id,
336  "User" => $User,
337  "CanView" => $Cache[$Id]));
338  $Cache[$Id] = $SignalResult["CanView"];
339  }
340 
341  # return the viewable ResourceIds
342  return array_keys( array_filter($Cache) );
343 
344  }
345 
350  {
351  $DB = new Database();
352  $DB->Query("DELETE FROM UserPermsCache");
353  }
354 
361  function GetTimestampOfLastResourceModification($OnlyReleasedResources = TRUE)
362  {
363  $LastChangeDate = $this->DB->Query(
364  "SELECT MAX(DateLastModified) AS LastChangeDate"
365  ." FROM Resources"
366  ." WHERE SchemaId = ".intval($this->SchemaId)
367  .($OnlyReleasedResources ? " AND ReleaseFlag = 1" : ""),
368  "LastChangeDate");
369  return ($LastChangeDate ? strtotime($LastChangeDate) : NULL);
370  }
371 
378  {
379  # retrieve field names from schema
380  $FieldNames = array();
381  $Schema = new MetadataSchema($this->SchemaId);
382  $Fields = $Schema->GetFields();
383  foreach ($Fields as $Field)
384  {
385  $FieldNames[$Field->Id()] = $Field->Name();
386  }
387 
388  # return field names to caller
389  return $FieldNames;
390  }
391 
399  function GetMatchingResources($ValuesToMatch)
400  {
401  # start out assuming we won't find any resources
402  $Resources = array();
403 
404  # for each value
405  $Schema = new MetadataSchema($this->SchemaId);
406  $Fields = $Schema->GetFields(
414  $LinkingTerm = "";
415  $Condition = "";
416  foreach ($ValuesToMatch as $FieldId => $Value)
417  {
418  # if field can be used for comparison
419  if (isset($Fields[$FieldId]))
420  {
421  # add comparison to condition
422  $Condition .= $LinkingTerm.$Fields[$FieldId]->DBFieldName()
423  ." = '".addslashes($Value)."'";
424  $LinkingTerm = " AND ";
425  }
426  }
427 
428  # if there were valid conditions
429  if (strlen($Condition))
430  {
431  # build query statment
432  $Query = "SELECT ResourceId FROM Resources WHERE ".$Condition
433  ." AND SchemaId = ".intval($this->SchemaId);
434 
435  # execute query to retrieve matching resource IDs
436  $this->DB->Query($Query);
437  $ResourceIds = $this->DB->FetchColumn("ResourceId");
438 
439  # retrieve resource objects
440  foreach ($ResourceIds as $Id)
441  {
442  $Resources[$Id] = new Resource($Id);
443  }
444  }
445 
446  # return any resources found to caller
447  return $Resources;
448  }
449 
450  # Functions for keeping per-field resource counts updated:
451 
462  function GetResourceCount($FieldId, $Value, $CountType="All")
463  {
464  if ($FieldId<0)
465  {
466  return NULL;
467  }
468 
469  $Schema = new MetadataSchema($this->SchemaId);
470  $Field = $Schema->GetField($FieldId);
471  if ($Field === NULL)
472  {
473  return NULL;
474  }
475 
476  if ($this->ResourceCount === NULL)
477  {
478  $this->DB->Query(
479  "SELECT FieldId, ClassName, CountType, Count FROM ResourceCounts");
480 
481  while ($Row = $this->DB->FetchRow())
482  {
483  $R_FieldId = $Row["FieldId"];
484  $R_ClassName = $Row["ClassName"];
485  $R_CountType = $Row["CountType"];
486  $R_Count = $Row["Count"];
487 
488  $this->ResourceCount[$R_FieldId][$R_ClassName][$R_CountType] = $R_Count;
489  }
490  }
491 
492  if ($Field->Type() === MetadataSchema::MDFTYPE_OPTION
493  || $Field->Type() === MetadataSchema::MDFTYPE_CONTROLLEDNAME)
494  {
495  return isset($this->ResourceCount[$FieldId][$Value][$CountType]) ?
496  $this->ResourceCount[$FieldId][$Value][$CountType] :
497  0 ;
498  }
499  else
500  {
501  return NULL;
502  }
503  }
504 
509  public function GetReleasedResourceTotal()
510  {
511  return $this->DB->Query("
512  SELECT COUNT(*) AS ResourceTotal
513  FROM Resources
514  WHERE ResourceId > 0
515  AND ReleaseFlag = 1
516  AND SchemaId = ".intval($this->SchemaId),
517  "ResourceTotal");
518  }
519 
525  public function GetResourceTotal()
526  {
527  return $this->DB->Query("
528  SELECT COUNT(*) AS ResourceTotal
529  FROM Resources
530  WHERE ResourceId > 0
531  AND SchemaId = ".intval($this->SchemaId),
532  "ResourceTotal");
533  }
534 
543  {
544  global $AF;
545 
546  # be sure that we're not a gigantic object when the task is queued
547  $TmpResourceCount = $this->ResourceCount;
548  $this->ResourceCount = NULL;
549 
550  $AF->QueueUniqueTask(
551  array($this,"UpdateResourceCountCallback"), array());
552  $this->ResourceCount = $TmpResourceCount;
553  }
554 
562  {
563  $DB = new Database();
564 
565  $DB->Query(
566  "CREATE TABLE ResourceCountsNew "
567  ."(FieldId INT, ClassName TEXT, CountType TEXT, Count INT);");
568 
569  $Start = microtime(TRUE);
570 
571  foreach ($this->ResourceCountConditions as $CountType => $CountCondition)
572  {
573  $DB->Query(
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;" );
584  }
585 
586  $Stop = microtime(TRUE);
587 
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).");");
592  $DB->Query(
593  "RENAME TABLE ResourceCounts TO ResourceCountsOld,"
594  ." ResourceCountsNew TO ResourceCounts; ");
595  $DB->Query(
596  "DROP TABLE ResourceCountsOld; ");
597  }
598 
599  # ---- PRIVATE INTERFACE -------------------------------------------------
600 
601  private $ResourceCount = NULL;
602  private $ResourceCountConditions =
603  array("All" => NULL, "Released" => "ReleaseFlag=1");
604  private $SchemaId;
605 
611  private function ComputeUserClass( $User )
612  {
613  static $ClassCache;
614 
615  if (!isset($ClassCache))
616  {
617  $ClassCache = array();
618  }
619 
620  $UserId = $User->IsLoggedIn() ? $User->Id() : "XX-ANON-XX";
621 
622  if (!isset($ClassCache[$UserId]))
623  {
624  $RelevantPerms = array();
625 
626  $Schema = new MetadataSchema();
627  foreach ($Schema->GetFields() as $Field)
628  {
629  $RelevantPerms = array_merge(
630  $RelevantPerms,
631  $Field->ViewingPrivileges()->PrivilegeFlagsChecked() );
632  }
633  $RelevantPerms = array_unique($RelevantPerms);
634 
635  $PermsInvolved = array();
636  foreach ($RelevantPerms as $Perm)
637  {
638  if ($User->HasPriv($Perm))
639  {
640  $PermsInvolved []= $Perm;
641  }
642  }
643  $ClassCache[$UserId] = md5( implode( "-", $PermsInvolved ) );
644  }
645 
646  return $ClassCache[$UserId];
647  }
648 
649 
656  private function InUserComparisons( $User )
657  {
658  return array_unique(
659  array_merge(
660  $this->CheckUserComparisons( $User, "=="),
661  $this->CheckUserComparisons( $User, "!=") ) ) ;
662  }
663 
672  private function CheckUserComparisons( $User, $ComparisonType )
673  {
674  # assume no resources match
675  $Result = array();
676 
677  # if we're checking the anonymous user, presume that
678  # nothing will match
679  if ( !$User->IsLoggedIn() )
680  {
681  return $Result;
682  }
683 
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
687  $Schema = new MetadataSchema();
688  $UserComparisonFields = array();
689  foreach ($Schema->GetFields() as $Field)
690  {
691  $UserComparisonFields = array_merge(
692  $UserComparisonFields,
693  $Field->ViewingPrivileges()->FieldsWithUserComparisons($ComparisonType) );
694  }
695  $UserComparisonFields = array_unique($UserComparisonFields);
696 
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)
701  {
702  $Field = new MetadataField($FieldId);
703  $SqlConditions []= $Field->DBFieldName().
704  ($ComparisonType == "==" ? " = " : " != ")
705  .$User->Id();
706  }
707 
708  # use the list of SQL conditions to see if any resources
709  # will actually match this predicate
710  if (count($SqlConditions)>0)
711  {
712  $DB = new Database();
713  $Query = "SELECT ResourceId FROM Resources WHERE "
714  ."SchemaId=".$this->SchemaId." AND ("
715  .implode(" OR ", $SqlConditions).")";
716 
717  $Result = $DB->FetchColumn("ResourceId");
718  }
719 
720  return $Result;
721  }
722 
723 
724 
725 
726 }
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.
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
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.
__construct($SchemaId=MetadataSchema::SCHEMAID_DEFAULT)
Class constructor.
static Create($SchemaId)
Create a new resource.
Definition: Resource.php:60
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.