CWIS Developer Documentation
Folder.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: Folder.php
4 #
5 # Part of the Collection Workflow Information System (CWIS)
6 # Copyright 2012-2013 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu/cwis/
8 #
9 
17 class Folder
18 {
19 
20  # ---- PUBLIC INTERFACE --------------------------------------------------
21  /*@(*/
23 
29  public function __construct($FolderId)
30  {
31  # create our own DB handle
32  $this->DB = new Database();
33 
34  # store folder ID
35  $this->Id = intval($FolderId);
36 
37  # attempt to load in folder info
38  $this->DB->Query("SELECT * FROM Folders WHERE FolderId = ".$this->Id);
39  $Record = $this->DB->FetchRow();
40 
41  # if folder was not found
42  if ($Record === FALSE)
43  {
44  # bail out with exception
45  throw new Exception("Unknown Folder ID (".$FolderId.").");
46  }
47 
48  # save folder info
49  $this->OwnerId = $Record["OwnerId"];
50  $this->FolderName = $Record["FolderName"];
51  $this->NormalizedName = $Record["NormalizedName"];
52  $this->FolderNote = $Record["FolderNote"];
53  $this->IsShared = $Record["IsShared"];
54  $this->ContentType = $Record["ContentType"];
55  $this->UpdateValueCache = $Record;
56 
57  # load list of resources in folder from database
58  $this->DB->Query("SELECT ItemId, ItemTypeId, ItemNote FROM FolderItemInts"
59  ." WHERE FolderId = ".$this->Id);
60 
61  # create internal cache for item notes
62  $this->ItemNoteCache = array();
63  while ($Record = $this->DB->FetchRow())
64  {
65  $Index = self::GetCacheIndex($Record["ItemId"], $Record["ItemTypeId"]);
66  $this->ItemNoteCache[$Index] = $Record["ItemNote"];
67  }
68 
69  # load item ordering
70  if ($this->ContentType == self::MIXEDCONTENT)
71  {
72  $this->OrderList = new PersistentDoublyLinkedList(
73  "FolderItemInts", "ItemId", "FolderId = ".$this->Id, "ItemTypeId");
74  }
75  else
76  {
77  $this->OrderList = new PersistentDoublyLinkedList(
78  "FolderItemInts", "ItemId", "FolderId = ".$this->Id);
79  }
80 
81  # be sure that a NormalizedName is set
82  $this->NormalizedName();
83  }
84 
88  public function Delete()
89  {
90  # take folder out of global folder order
91  $Factory = new FolderFactory();
92  $Factory->RemoveItemFromOrder($this->Id);
93 
94  # remove resource listings from DB
95  $this->DB->Query("DELETE FROM FolderItemInts WHERE FolderId = ".$this->Id);
96 
97  # remove folder listing from DB
98  $this->DB->Query("DELETE FROM Folders WHERE FolderId = ".$this->Id);
99  }
100 
101  /*@)*/ /* Setup/Initialization */
102  # ------------------------------------------------------------------------
103  /*@(*/
104 
109  public function Id()
110  {
111  return $this->Id;
112  }
113 
120  public function Name($NewValue = DB_NOVALUE)
121  {
122  if ($NewValue != DB_NOVALUE)
123  {
124  $this->NormalizedName(self::NormalizeFolderName($NewValue));
125  }
126  return $this->UpdateValue("FolderName", $NewValue);
127  }
128 
136  public function NormalizedName($NewValue = DB_NOVALUE)
137  {
138  $Name = $this->UpdateValue("NormalizedName", $NewValue);
139  # attempt to generate and set new normalized name if none found
140  if (!strlen($Name))
141  {
142  $Name = $this->UpdateValue("NormalizedName",
143  self::NormalizeFolderName($this->Name()));
144  }
145  return $Name;
146  }
147 
153  public static function NormalizeFolderName($Name)
154  {
155  return preg_replace("/[^a-z0-9]/", "", strtolower($Name));
156  }
157 
164  public function IsShared($NewValue = DB_NOVALUE)
165  {
166  return $this->UpdateValue("IsShared", $NewValue);
167  }
168 
174  public function OwnerId($NewValue = DB_NOVALUE)
175  {
176  if ($NewValue !== DB_NOVALUE) { unset($this->OwnerId); }
177  return intval($this->UpdateValue("OwnerId", $NewValue));
178  }
179 
185  public function Note($NewValue = DB_NOVALUE)
186  {
187  return $this->UpdateValue("FolderNote", $NewValue);
188  }
189 
190  /*@)*/ /* Attribute Setting/Retrieval */
191  # ------------------------------------------------------------------------
192  /*@(*/
193 
206  public function InsertItemBefore($TargetItemOrItemId, $NewItemOrItemId,
207  $TargetItemType = NULL, $NewItemType = NULL)
208  {
209  $this->AddItem($NewItemOrItemId, $NewItemType);
210  $this->OrderList->InsertBefore($TargetItemOrItemId, $NewItemOrItemId,
211  self::GetItemTypeId($TargetItemType),
212  self::GetItemTypeId($NewItemType));
213  }
214 
226  public function InsertItemAfter($TargetItemOrItemId, $NewItemOrItemId,
227  $TargetItemType = NULL, $NewItemType = NULL)
228  {
229  $this->AddItem($NewItemOrItemId, $NewItemType);
230  $this->OrderList->InsertAfter($TargetItemOrItemId, $NewItemOrItemId,
231  self::GetItemTypeId($TargetItemType),
232  self::GetItemTypeId($NewItemType));
233  }
234 
242  public function PrependItem($ItemOrItemId, $ItemType = NULL)
243  {
244  $this->AddItem($ItemOrItemId, $ItemType);
245  $this->OrderList->Prepend($ItemOrItemId, self::GetItemTypeId($ItemType));
246  }
247 
255  public function AppendItem($ItemOrItemId, $ItemType = NULL)
256  {
257  $this->AddItem($ItemOrItemId, $ItemType);
258  $this->OrderList->Append($ItemOrItemId, self::GetItemTypeId($ItemType));
259  }
260 
267  public function AppendItems($ItemsOrItemIds, $ItemTypes = NULL)
268  {
269  # convert ItemTypes to an array if it wasn't one
270  if (!is_array($ItemTypes))
271  {
272  $NewItemTypes = array();
273  foreach ($ItemsOrItemIds as $Id)
274  {
275  $NewItemTypes[] = $ItemTypes;
276  }
277  $ItemTypes = $NewItemTypes;
278  }
279 
280  # get items ids
281  $ItemIds = array();
282  foreach ($ItemsOrItemIds as $ItemOrId)
283  {
284  $ItemIds[] = is_object($ItemOrId) ? $ItemOrId->Id() : $ItemOrId;
285  }
286 
287  # add all the itmes to our folder
288  foreach ($ItemIds as $Index => $ItemId)
289  {
290  $this->AddItem($ItemId, $ItemTypes[$Index]);
291  }
292 
293  # and add them to our ordering list
294  if ($this->ContentType == self::MIXEDCONTENT)
295  {
296  $ItemTypeIds = array();
297  foreach ($ItemTypes as $ItemType)
298  {
299  $ItemTypeIds[] = self::GetItemTypeId($ItemType);
300  }
301 
302  $this->OrderList->Append($ItemIds, $ItemTypeIds);
303  }
304  else
305  {
306  $this->OrderList->Append($ItemIds);
307  }
308  }
309 
322  public function GetItemIds($Offset = NULL, $Length = NULL)
323  {
324  # retrieve item ordered list of type IDs
325  $ItemIds = $this->OrderList->GetIds();
326 
327  # if this is a mixed-item-type folder
328  if ($this->ContentType == self::MIXEDCONTENT)
329  {
330  # convert item type IDs to corresponding type names
331  $NewItemIds = array();
332  foreach ($ItemIds as $ItemInfo)
333  {
334  $NewItemIds[] = array(
335  "ID" => $ItemInfo["ID"],
336  "Type" => self::GetItemTypeName($ItemInfo["Type"]),
337  );
338  }
339  $ItemIds = $NewItemIds;
340  }
341 
342  # prune results to subset if requested
343  if ($Offset !== NULL)
344  {
345  if ($Length !== NULL)
346  {
347  $ItemIds = array_slice($ItemIds, $Offset, $Length);
348  }
349  else
350  {
351  $ItemIds = array_slice($ItemIds, $Offset);
352  }
353  }
354 
355  # return list of item type IDs (and possibly types) to caller
356  return $ItemIds;
357  }
358 
365  public function GetItemCount()
366  {
367  return $this->OrderList->GetCount();
368  }
369 
376  public function RemoveItem($ItemId, $ItemType = NULL)
377  {
378  # if resource is in folder
379  if ($this->ContainsItem($ItemId, $ItemType))
380  {
381  # remove item from item order
382  $ItemTypeId = self::GetItemTypeId($ItemType);
383  $this->OrderList->Remove($ItemId, $ItemTypeId);
384 
385  # remove resource from folder locally
386  unset($this->ItemNoteCache[self::GetCacheIndex($ItemId, $ItemTypeId)]);
387 
388  # remove resource from folder in DB
389  $this->DB->Query("DELETE FROM FolderItemInts"
390  ." WHERE FolderId = ".intval($this->Id)
391  ." AND ItemId = ".intval($ItemId)
392  ." AND ItemTypeId = "
393  .($ItemTypeId === NULL ? -1 : intval($ItemTypeId)));
394  }
395  }
396 
404  public function NoteForItem($ItemId, $NewValue = DB_NOVALUE, $ItemType = NULL)
405  {
406  $ItemTypeId = self::GetItemTypeId($ItemType);
407  $Index = self::GetCacheIndex($ItemId, $ItemTypeId);
408  $DummyCache = array("ItemNote" => $this->ItemNoteCache[$Index]);
409 
410  $Value = $this->DB->UpdateValue("FolderItemInts", "ItemNote", $NewValue,
411  "FolderId = ".intval($this->Id)
412  ." AND ItemId = ".intval($ItemId)
413  ." AND ItemTypeId = "
414  .($ItemTypeId === NULL ? -1 : intval($ItemTypeId)),
415  $DummyCache);
416 
417  $this->ItemNoteCache[self::GetCacheIndex($ItemId, $ItemTypeId)] = $Value;
418 
419  return $Value;
420  }
421 
429  public function ContainsItem($ItemId, $ItemType = NULL)
430  {
431  $ItemTypeId = self::GetItemTypeId($ItemType);
432  return array_key_exists(self::GetCacheIndex($ItemId, $ItemTypeId),
433  $this->ItemNoteCache) ? TRUE : FALSE;
434  }
435 
436  /*@)*/ /* Item Operations */
437 
438  # ---- PRIVATE INTERFACE -------------------------------------------------
439 
440  private $DB;
441  private $Id;
442 
443  # folder attributes - these much match field names in Folders DB table
444  private $OwnerId;
445  private $FolderName;
446  private $NormalizedName;
447  private $FolderNote;
448  private $IsShared;
449  private $ContentType;
450 
451  private $ItemNoteCache;
452  private $OrderList;
453  private $UpdateValueCache;
454 
455  # item type IDs (indexed by normalized type name)
456  static private $ItemTypeIds;
457  # item type names (indexed by type ID)
458  static private $ItemTypeNames;
459 
460  # content type that indicates folder contains mixed content types
461  const MIXEDCONTENT = -1;
462 
470  public static function GetItemTypeId($TypeName)
471  {
472  # return NULL if TypeName is NULL.
473  if ($TypeName === NULL) { return NULL; }
474 
475  # make sure item type map is loaded
476  self::LoadItemTypeMap();
477 
478  # normalize item type name
479  $NormalizedTypeName = strtoupper(
480  preg_replace("/[^a-zA-Z0-9]/", "", $TypeName));
481 
482  # if name not already mapped
483  if (!array_key_exists($NormalizedTypeName, self::$ItemTypeIds))
484  {
485  # add name to database
486  static $DB;
487  if (!isset($DB)) { $DB = new Database(); }
488  $DB->Query("INSERT INTO FolderContentTypes SET"
489  ." TypeName = '".addslashes($TypeName)."',"
490  ." NormalizedTypeName = '".addslashes($NormalizedTypeName)."'");
491 
492  # add name to cached mappings
493  $NewTypeId = $DB->LastInsertId();
494  self::$ItemTypeIds[$NormalizedTypeName] = $NewTypeId;
495  self::$ItemTypeNames[$NewTypeId] = $TypeName;
496  }
497 
498  # return item type ID to caller
499  return self::$ItemTypeIds[$NormalizedTypeName];
500  }
508  private static function GetItemTypeName($TypeId)
509  {
510  # make sure item type map is loaded
511  self::LoadItemTypeMap();
512 
513  # if ID not present in mappings
514  if (!array_key_exists($TypeId, self::$ItemTypeNames))
515  {
516  # return null value
517  return NULL;
518  }
519  else
520  {
521  # return item type name to caller
522  return self::$ItemTypeNames[$TypeId];
523  }
524  }
525 
530  private static function LoadItemTypeMap()
531  {
532  # if name-to-number item type map not already loaded
533  if (!isset(self::$ItemTypeIds))
534  {
535  # load item type map from database
536  $DB = new Database();
537  $DB->Query("SELECT * FROM FolderContentTypes");
538  self::$ItemTypeIds = array();
539  self::$ItemTypeNames = array();
540  while ($Row = $DB->FetchRow())
541  {
542  self::$ItemTypeIds[$Row["NormalizedTypeName"]] = $Row["TypeId"];
543  self::$ItemTypeNames[$Row["TypeId"]] = $Row["TypeName"];
544  }
545  }
546  }
547 
553  private function AddItem($ItemOrItemId, $ItemType)
554  {
555  # convert item to ID if necessary
556  $ItemId = is_object($ItemOrItemId)
557  ? $ItemOrItemId->Id() : $ItemOrItemId;
558 
559  # convert item type to item type ID
560  $ItemTypeId = self::GetItemTypeId($ItemType);
561 
562  # convert null item type to "no type" value used in database
563  if ($ItemTypeId === NULL) { $ItemTypeId = -1; }
564 
565  # if resource is not already in folder
566  if (!$this->ContainsItem($ItemId, $ItemType))
567  {
568  # add resource to folder locally
569  $this->ItemNoteCache[self::GetCacheIndex($ItemId, $ItemTypeId)] = NULL;
570 
571  # add resource to folder in DB
572  $this->DB->Query("INSERT INTO FolderItemInts SET"
573  ." FolderId = ".intval($this->Id)
574  .", ItemId = ".intval($ItemId)
575  .", ItemTypeId = "
576  .($ItemTypeId === NULL ? -1 : intval($ItemTypeId)));
577  }
578  }
579 
586  private static function GetCacheIndex($ItemId, $ItemTypeId)
587  {
588  $ItemTypeId = ($ItemTypeId === NULL) ? -1 : $ItemTypeId;
589  return intval($ItemTypeId).":".intval($ItemId);
590  }
591 
599  private function UpdateValue($FieldName, $NewValue)
600  {
601  $this->$FieldName = $this->DB->UpdateValue("Folders", $FieldName, $NewValue,
602  "FolderId = ".$this->Id, $this->UpdateValueCache);
603  return $this->$FieldName;
604  }
605 }
NormalizedName($NewValue=DB_NOVALUE)
Get/set normalized version of folder name.
Definition: Folder.php:136
const MIXEDCONTENT
Definition: Folder.php:461
AppendItem($ItemOrItemId, $ItemType=NULL)
Add item to folder as the last item.
Definition: Folder.php:255
NoteForItem($ItemId, $NewValue=DB_NOVALUE, $ItemType=NULL)
Get/set note text for specific item within folder.
Definition: Folder.php:404
Id()
Get folder ID.
Definition: Folder.php:109
SQL database abstraction object with smart query caching.
Definition: Database.php:22
IsShared($NewValue=DB_NOVALUE)
Get/set whether folder is publically-viewable.
Definition: Folder.php:164
Factory object for Folder class, used to retrieve and manage Folders and groups of Folders...
Folder object used to create and manage groups of items.
Definition: Folder.php:17
RemoveItem($ItemId, $ItemType=NULL)
Remove item from folder, if present.
Definition: Folder.php:376
static NormalizeFolderName($Name)
Convert folder name to normalized form (lower-case alphanumeric only).
Definition: Folder.php:153
InsertItemAfter($TargetItemOrItemId, $NewItemOrItemId, $TargetItemType=NULL, $NewItemType=NULL)
Insert item into folder after specified item.
Definition: Folder.php:226
Note($NewValue=DB_NOVALUE)
Get/set note text for folder.
Definition: Folder.php:185
const DB_NOVALUE
Definition: Database.php:1738
GetItemCount()
Get number of items in folder.
Definition: Folder.php:365
AppendItems($ItemsOrItemIds, $ItemTypes=NULL)
Add multiple items to the folder at the end.
Definition: Folder.php:267
static GetItemTypeId($TypeName)
Definition: Folder.php:470
ContainsItem($ItemId, $ItemType=NULL)
Check whether specified item is contained in folder.
Definition: Folder.php:429
InsertItemBefore($TargetItemOrItemId, $NewItemOrItemId, $TargetItemType=NULL, $NewItemType=NULL)
Insert item into folder before specified item.
Definition: Folder.php:206
Persistent doubly-linked-list data structure, with its data stored in a specified database table...
OwnerId($NewValue=DB_NOVALUE)
Get/set user ID of folder owner.
Definition: Folder.php:174
Delete()
Delete folder.
Definition: Folder.php:88
__construct($FolderId)
Object constructor – load an existing folder.
Definition: Folder.php:29
PrependItem($ItemOrItemId, $ItemType=NULL)
Add item to folder as the first item.
Definition: Folder.php:242
GetItemIds($Offset=NULL, $Length=NULL)
Retrieve array of IDs of items in folder, in the order that they appear in the folder.
Definition: Folder.php:322
Name($NewValue=DB_NOVALUE)
Get/set folder name.
Definition: Folder.php:120