1  <?php
  2  /* Copyright (c) 2012, Geert Bergman (geert@scrivo.nl)
  3   * All rights reserved.
  4   *
  5   * Redistribution and use in source and binary forms, with or without
  6   * modification, are permitted provided that the following conditions are met:
  7   *
  8   * 1. Redistributions of source code must retain the above copyright notice,
  9   *    this list of conditions and the following disclaimer.
 10   * 2. Redistributions in binary form must reproduce the above copyright notice,
 11   *    this list of conditions and the following disclaimer in the documentation
 12   *    and/or other materials provided with the distribution.
 13   * 3. Neither the name of "Scrivo" nor the names of its contributors may be
 14   *    used to endorse or promote products derived from this software without
 15   *    specific prior written permission.
 16   *
 17   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 18   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 19   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 20   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 21   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 22   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 23   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 24   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 25   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 26   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 27   * POSSIBILITY OF SUCH DAMAGE.
 28   *
 29   * $Id: ItemList.php 866 2013-08-25 16:22:35Z geert $
 30   */
 31  
 32  /**
 33   * Implementation of the \Scrivo\ItemList class.
 34   */
 35  
 36  namespace Scrivo;
 37  
 38  /**
 39   * Item lists (or Scrivo list) are a very versitle way of adding list like
 40   * data to a page (faq, comments, news and even a forurm like)
 41   */
 42  class ItemList {
 43  
 44      /**
 45       * The list/application id (DB key).
 46       * @var int
 47       */
 48      private $id 0;
 49  
 50      /**
 51       * Id of the list definition.
 52       * @var int
 53       */
 54      private $pagePropertyDefinitionId 0;
 55  
 56      /**
 57       * Id of the application definition.
 58       * @var int
 59       */
 60      private $applicationDefinitionId 0;
 61  
 62      /**
 63       * Id of the page where this list instance is associated with.
 64       * @var int
 65       */
 66      private $pageId 0;
 67  
 68      /**
 69       * Optional reference to a page that is parent for pages linked to
 70       * list items.
 71       * @var int
 72       */
 73      private $folderId 0;
 74  
 75      /**
 76       * A list of list item pids.
 77       * @var array[]
 78       */
 79      private $listPids null;
 80  
 81      /**
 82       * The attached roles.
 83       * @var \Scrivo\RoleSet
 84       */
 85      private $roles;
 86  
 87      /**
 88       * A Scrivo context.
 89       * @var \Scrivo\Context
 90       */
 91      private $context null;
 92  
 93      /**
 94       * Construct an item list. Item lists are Scrivo application and therefore
 95       * linked to a page and an application definition.
 96       *
 97       * Scrivo lists are never created as explicitly, but assumed to exist if
 98       * a page with a list type application definition does exist. If not the
 99       * list is created on the fly based upon its defintion. Because actual
100       * list creation is an internal issue and not ment to do expicitly the
101       * constructor is not public.
102       *
103       * To create a list see 'fetch'.
104       *
105       * @param \Scrivo\Context $context A valid Scrivo context
106       * @param string $pageId The id of the page that hosts the application.
107       * @param string $pagePropertyDefinitionId The page property definition id.
108       */
109      protected function __construct(
110              \Scrivo\Context $context$pageId$pagePropertyDefinitionId) {
111          try {
112              $this->context $context;
113  
114              // Check if the list was create before (the list record exists).
115              $sth $context->connection->prepare(
116                  "SELECT L.item_list_id, L.page_definition_tab_id, L.page_id,
117                      L.folder_id, T.application_definition_id
118                  FROM item_list L, page_definition_tab T, page D
119                  WHERE L.instance_id = :instId AND D.instance_id = :instId AND
120                      T.instance_id = :instId AND D.page_definition_id = T.page_definition_id AND
121                      T.page_definition_tab_id = L.page_definition_tab_id AND
122                      L.version = D.version AND L.page_id = D.page_id AND
123                      (D.has_staging + D.version) = 0 AND
124                      L.page_definition_tab_id = :ppDefId AND
125                      L.page_id  = :pageId");
126  
127              $context->connection->bindInstance($sth);
128              $sth->bindValue(":pageId"$pageId, \PDO::PARAM_INT);
129              $sth->bindValue(
130                  ":ppDefId"$pagePropertyDefinitionId, \PDO::PARAM_INT);
131  
132              $sth->execute();
133  
134              if ($sth->rowCount() > 1) {
135                  throw new \Scrivo\SystemException(
136                      "Data corruption: failed to load list");
137              } else if ($sth->rowCount() === 0) {
138                  // If not create it
139                  // Is it a valid page and application definition
140                  // Yes create the list
141                  // else throw up
142                  $this->pageId $pageId;
143                  $this->pagePropertyDefinitionId $pagePropertyDefinitionId;
144                  $this->insert();
145              } else {
146                  $rd $sth->fetch(\PDO::FETCH_ASSOC);
147                  $this->id intval($rd["item_list_id"]);
148                  $this->pagePropertyDefinitionId =
149                      intval($rd["page_definition_tab_id"]);
150                  $this->pageId intval($rd["page_id"]);
151                  $this->folderId intval($rd["folder_id"]);
152                  $this->applicationDefinitionId intval($rd["application_definition_id"]);
153              }
154  
155              // Select it
156  
157              // TODO: when the list definition chances and a sub-folder is
158              // required mark this in the list item definition (HAS_FOLDER or
159              // something like that). Then is it is easliy decided if the sub
160              // folder needs to be created at this point.
161              // Or delegate it to list item creation.
162  
163              // "Borrow" the read access roles form the host page.
164              $this->roles = new \Scrivo\RoleSet();
165  
166              $sth $context->connection->prepare(
167                  "SELECT role_id FROM object_role
168                  WHERE instance_id = :instId AND page_id = :pageId");
169  
170              $context->connection->bindInstance($sth);
171              $sth->bindValue(":pageId"$pageId, \PDO::PARAM_INT);
172  
173              $sth->execute();
174  
175              while ($rd $sth->fetch(\PDO::FETCH_ASSOC)) {
176                  $this->roles[] = intval($rd["role_id"]);
177              }
178  
179              $this->listPids = array();
180  
181          } catch(\PDOException $e) {
182              throw new \Scrivo\ResourceException($e);
183          }
184      }
185  
186      /**
187       * Implementation of the readable properties using the PHP magic
188       * method __get().
189       *
190       * @param string $name The name of the property to get.
191       *
192       * @return mixed The value of the requested property.
193       */
194      public function __get($name) {
195          switch($name) {
196              case "items": return $this->getItems(0);
197          }
198          throw new \Scrivo\SystemException("No such get-property '$name'.");
199      }
200  
201      /**
202       * Insert new list object data into the database. A list record is assumed
203       * to exists if page exists and is created silently.
204       *
205       * @throws \Scrivo\ApplicationException If the data is not accessible or
206       *   one or more of the fields contain invalid data.
207       */
208      private function insert() {
209  
210          $this->id $this->context->connection->generateId();
211  
212          $sth $this->context->connection->prepare(
213              "INSERT INTO item_list (
214                  instance_id, item_list_id, page_definition_tab_id, page_id,
215                  version, folder_id
216              ) VALUES (
217                  :instId, :id, :pagePropDefId, :pageId,
218                  0, :folderId
219              )");
220  
221          $this->context->connection->bindInstance($sth);
222          $sth->bindValue(":id"$this->id, \PDO::PARAM_INT);
223          $sth->bindValue(
224              ":pagePropDefId"$this->pagePropertyDefinitionId, \PDO::PARAM_INT);
225          $sth->bindValue(":pageId"$this->pageId, \PDO::PARAM_INT);
226          $sth->bindValue(":folderId"$this->folderId, \PDO::PARAM_INT);
227  
228          $sth->execute();
229  
230      }
231  
232      /**
233       * Update list object data in the database. Only used to set the subfolder
234       * id.
235       *
236       * @throws \Scrivo\ApplicationException If the data is not accessible or
237       *   one or more of the fields contain invalid data.
238       */
239      private function update() {
240  
241          $sth $this->context->connection->prepare(
242              "UPDATE item_list SET folder_id = :folderId
243                  WHERE instance_id = :instId AND item_list_id = :listId");
244  
245          $this->context->connection->bindInstance($sth);
246          $sth->bindValue(":folderId"$this->folderId, \PDO::PARAM_INT);
247          $sth->bindValue(":listId"$this->id, \PDO::PARAM_INT);
248  
249          $sth->execute();
250  
251      }
252  
253      /**
254       * Retrieve an item list from the cache or database.
255       *
256       * @param \Scrivo\Context $context A Scrivo context.
257       * @param int $pageId The id of the page that hosts the list.
258       * @param int $defintionId The id of the page property definition.
259       *
260       * @throws \Scrivo\ApplicationException if the page was not readable for
261       *   the user defined in the context.
262       */
263      public static function fetch(\Scrivo\Context $context$pageId$defintionId) {
264          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(
265              null,
266              array(\Scrivo\ArgumentCheck::TYPE_INTEGER),
267              array(\Scrivo\ArgumentCheck::TYPE_INTEGER),
268          ));
269  
270          // Try to retieve form cache
271          $obj null;
272          $cId "$pageId.$defintionId";
273          if (isset($context->cache[$cId])) {
274              // Get the list from cache and set the context.
275              $obj $context->cache[$cId];
276              $obj->context $context;
277          } else {
278              // Load the list and set it in the cache.
279              $obj = new \Scrivo\ItemList($context$pageId$defintionId);
280              $context->cache[$cId] = $obj;
281          }
282          $obj->roles->checkReadPermission($context->principal);
283          return $obj;
284      }
285  
286      /**
287       * Retrieve all list items at root level.
288       *
289       * @return \Scrivo\ListItem[] An array if list items.
290      public function getItems() {
291          $res = $this->list[0];
292          return $res ? $res : array();
293      }
294       */
295  
296      /**
297       * Retrieve a sub list.
298       *
299       * @param int $parentId The id of the common parent for the list items.
300       *
301       * @return \Scrivo\ListItem[] An array if list items.
302       */
303      public function getItems($parentId) {
304          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(
305              array(\Scrivo\ArgumentCheck::TYPE_INTEGER)
306          ));
307  
308          \Scrivo\ArgumentCheck::assert(
309              $parentId, \Scrivo\ArgumentCheck::TYPE_INTEGER);
310          return $this->select($parentId);
311      }
312  
313      /**
314       * Select list items from the database.
315       *
316       * @param int $parentId An optional parent id is case of a sub-list.
317       *
318       * @return \Scrivo\ListItem[id] An array containing the selected list items.
319       */
320      private function select($parentId=0) {
321  
322          $cId $this->pageId "." $this->pagePropertyDefinitionId;
323  
324          if (!isset($this->listPids[$parentId])) {
325              $this->listPids[$parentId] = true;
326              $this->context->cache[$cId] = $this;
327  
328          }
329  
330          if (isset($this->context->cache[$cId "." $parentId])) {
331              return $this->context->cache[$cId "." $parentId];
332          }
333  
334          try {
335              $sth $this->context->connection->prepare(
336                  "SELECT I.list_item_id, I.item_list_id, I.link_id,
337                      I.version, I.parent_id, I.sequence_no, I.list_item_definition_id,
338                      I.page_id, I.title, I.date_created, I.date_modified, I.date_online,
339                      I.date_offline
340                  FROM list_item I, item_list L
341                  WHERE I.instance_id = :instId AND L.instance_id = :instId
342                      AND L.item_list_id = I.item_list_id
343                      AND I.parent_id = :parentId
344                      AND L.page_id = :pageId
345                      AND L.page_definition_tab_id = :defId
346                      AND L.version = 0 AND I.version = 0
347                  ORDER BY parent_id, sequence_no");
348  
349              $this->context->connection->bindInstance($sth);
350              $sth->bindValue(":pageId"$this->pageId, \PDO::PARAM_INT);
351              $sth->bindValue(":parentId"$parentId, \PDO::PARAM_INT);
352              $sth->bindValue(
353                  ":defId"$this->pagePropertyDefinitionId, \PDO::PARAM_INT);
354  
355              $sth->execute();
356  
357              $res = array();
358              $prps null;
359  
360              while ($rd $sth->fetch(\PDO::FETCH_ASSOC)) {
361  
362                  // TODO: get this out of the loop, its here because of the
363                  // list id
364                  if ($prps === null) {
365                      $prps self::selectProperties(
366                          $this->contextintval($rd["item_list_id"]));
367                  }
368  
369                  $id intval($rd["list_item_id"]);
370  
371                  $li = new \Scrivo\ListItem($rd,
372                      isset($prps[$id]) ? $prps[$id] : new \Scrivo\PropertySet());
373  
374                  $res[$li->id] = $li;
375              }
376  
377              $this->context->cache[$cId "." $parentId] = $res;
378  
379              return $res;
380  
381          } catch(\PDOException $e) {
382              throw new \Scrivo\ResourceException($e);
383          }
384      }
385  
386      /**
387       * Select list item properties from the database.
388       *
389       * @param \Scrivo\Context $context A Scrivo context.
390       * @param int $listId The id of the list for which to retrieve the
391       *   properties.
392       *
393       * @return ListItemProperty[type] An array containing the selected list
394       *   item properties.
395       */
396      private static function selectProperties(\Scrivo\Context $context,
397              $listId) {
398  
399          $sth $context->connection->prepare(
400              "SELECT I.list_item_id, I.page_id,
401                  T.list_item_property_definition_id ID_DEF, T.type, T.php_key,
402                  IFNULL(V.value, '') value
403              FROM
404                  list_item I
405                  JOIN list_item_property_definition T ON (
406                      I.instance_id = :instId AND T.instance_id = :instId
407                      AND I.list_item_definition_id = T.list_item_definition_id)
408                  LEFT JOIN list_item_property V ON (
409                      V.instance_id = :instId AND I.instance_id = :instId
410                      AND T.instance_id = :instId
411                      AND V.list_item_property_definition_id = T.list_item_property_definition_id
412                      AND V.list_item_id = I.list_item_id AND V.version = 0)
413              WHERE
414                  I.instance_id = :instId
415                  AND I.item_list_id = :listId    AND I.version = 0"
416  //                .($itemId ? " AND I.list_item_id = :itemId" : "")
417              );
418  
419          $context->connection->bindInstance($sth);
420          $sth->bindValue(":listId"$listId, \PDO::PARAM_INT);
421  /*
422          if ($itemId) {
423              $sth->bindValue(":itemId", $itemId, \PDO::PARAM_INT);
424          }
425  */
426          $sth->execute();
427  
428          $res = array();
429          $id = -1;
430  
431          while ($rd $sth->fetch(\PDO::FETCH_ASSOC)) {
432  
433              $lid intval($rd["list_item_id"]);
434  
435              if ($lid != $id) {
436                  $id $lid;
437                  $res[$id] = new \Scrivo\PropertySet();
438              }
439  
440              $li ListItemProperty::create($rd);
441  
442              if ($li && !$li->phpSelector->equals(new \Scrivo\String(""))) {
443                  $res[$id]->{$li->phpSelector} = $li;
444              }
445          }
446  
447          return $res;
448      }
449  
450      /**
451       * Create a linked page for a list item.
452       *
453       * @param \Scrivo\ListItem $item The list item for which to create the
454       *   linked page.
455       * @param \Scrivo\ListItemDefinition $def The list item defintion of the
456       *   list item for which to create the linked page.
457       *
458       * @return int The page id of the page to link.
459       */
460      private function createLinkedPage(
461              \Scrivo\ListItem $item, \Scrivo\ListItemDefinition $def) {
462  
463          // Load the reference page to copy the language and role settings.
464          $mp = \Scrivo\Page::fetch($this->context$this->pageId);
465  
466          // If no subfolder exists yet create one.
467          if (!$this->folderId) {
468  
469              $propDef = \Scrivo\PagePropertyDefinition::fetch(
470                  $this->context$this->pagePropertyDefinitionId);
471              $tab = \Scrivo\PageDefinitionTab::fetch(
472                  $this->context$propDef->pageDefinitionTabId);
473  
474              $folder = new \Scrivo\Page($this->context);
475              $folder->parentId $this->pageId;
476              $folder->type = \Scrivo\Page::TYPE_SUB_FOLDER;
477              $folder->title $tab->title;
478              $folder->insert();
479  
480              $this->folderId $folder->id;
481              $this->update();
482          }
483  
484          // Create the sub-page.
485          $sub = new \Scrivo\Page($this->context);
486          $sub->definitionId $def->pageDefinitionId;
487          $sub->parentId $this->folderId;
488          $sub->type = \Scrivo\Page::TYPE_NON_NAVIGABLE_PAGE;
489          $sub->title $item->title;
490          $sub->insert();
491  
492          return $sub->id;
493      }
494  
495      /**
496       * Get the list item definition id using the phpSelector of the list
497       * item definition.
498       *
499       * @param \Scrivo\String $phpSelector The phpSelector of a list
500       *   item definition.
501       *
502       * @return int The list item definition id.
503       */
504      private function getDefinitionId(\Scrivo\String $phpSelector) {
505  
506          $sth $this->context->connection->prepare(
507              "SELECT T.list_item_definition_id FROM list_item_definition T WHERE
508                  T.instance_id = :instId AND T.application_definition_id = :appDefId AND
509                  T.php_key = :sel");
510  
511          $this->context->connection->bindInstance($sth);
512          $sth->bindValue(
513              ":appDefId"$this->applicationDefinitionId, \PDO::PARAM_INT);
514          $sth->bindValue(":sel"$phpSelector, \PDO::PARAM_STR);
515  
516          $sth->execute();
517  
518          return intval($sth->fetchColumn());
519      }
520  
521      /**
522       * Create a set of blank properties for a given list item definition.
523       *
524       * @param int $definitionId
525       *
526       * @return \Scrivo\PropertySet
527       */
528      private function selectEmptyProperties($definitionId) {
529  
530          $sth $this->context->connection->prepare(
531              "SELECT
532                  0 list_item_id, null value, :pageId page_id,
533                  list_item_property_definition_id ID_DEF, type, php_key
534              FROM list_item_property_definition
535              WHERE instance_id = :instId AND list_item_definition_id = :sel");
536  
537          $this->context->connection->bindInstance($sth);
538          $sth->bindValue(":pageId"$this->pageId, \PDO::PARAM_INT);
539          $sth->bindValue(":sel"$definitionId, \PDO::PARAM_INT);
540  
541          $sth->execute();
542  
543          $res = new \Scrivo\PropertySet();
544  
545          while ($rd $sth->fetch(\PDO::FETCH_ASSOC)) {
546  
547              $li ListItemProperty::create($rd);
548  
549              if ($li && !$li->phpSelector->equals(new \Scrivo\String(""))) {
550                  $res->{$li->phpSelector} = $li;
551              }
552          }
553  
554          return $res;
555      }
556  
557      /**
558       * Update an existing list item property in the database.
559       *
560       * First the data fields of this user will be validated, then the data
561       * is updated in the database.
562       *
563       * @param \Scrivo\ListItemProperty $prp The list item property to update.
564       * @param int $itemId The id of the list item for which to update the
565       *    property.
566       *
567       * @throws \Scrivo\ApplicationException If one or more of the fields
568       *   contain invalid data.
569       */
570      private function updateProperty(\Scrivo\ListItemProperty $prp$itemId) {
571  
572          $sth $this->context->connection->prepare(
573              "DELETE FROM list_item_property WHERE instance_id = :instId AND
574              list_item_id = :listItemId AND list_item_property_definition_id = :idDef AND
575              version = 0");
576  
577          $this->context->connection->bindInstance($sth);
578          $sth->bindValue(":listItemId"$itemId, \PDO::PARAM_INT);
579          $sth->bindValue(":idDef"$prp->definitionId, \PDO::PARAM_STR);
580  
581          $sth->execute();
582  
583          $sth $this->context->connection->prepare(
584              "INSERT INTO list_item_property (
585                  instance_id, list_item_id, version, list_item_property_definition_id,
586                  page_id, value
587               ) VALUES (:instId, :listItemId, 0, :idDef, :pageId, :data)");
588  
589          $this->context->connection->bindInstance($sth);
590          $sth->bindValue(":listItemId"$itemId, \PDO::PARAM_INT);
591          $sth->bindValue(":idDef"$prp->definitionId, \PDO::PARAM_STR);
592          $sth->bindValue(":pageId"$prp->pageId, \PDO::PARAM_STR);
593          $sth->bindValue(":data"$prp->data, \PDO::PARAM_STR);
594  
595          $sth->execute();
596      }
597  
598      /**
599       * Insert new list item object data into the database.
600       *
601       * First it is checked if the data of this list item object can be inserted
602       * into the database, then the data is inserted into the database. If no id
603       * was set a new object id is generated.
604       *
605       * @param \Scrivo\ListItem $item The list item to insert into the database.
606       *
607       * @throws \Scrivo\ApplicationException If the data is not accessible or
608       *   one or more of the fields contain invalid data.
609       */
610      private function insertItem(\Scrivo\ListItem $item) {
611  
612          // Create a sub-page if necessary
613          $def = \Scrivo\ListItemDefinition::fetch(
614              $this->context$item->definitionId);
615          $linkedPageId 0;
616          if ($def->pageDefinitionId) {
617              $linkedPageId $this->createLinkedPage($item$def);
618          }
619  
620          $id $this->context->connection->generateId();
621  
622          $sth $this->context->connection->prepare(
623              "INSERT INTO list_item (
624                  instance_id, list_item_id, item_list_id, link_id,
625                  version, parent_id, sequence_no, list_item_definition_id,
626                  page_id, title, date_created, date_modified,
627                  date_online, date_offline
628              ) VALUES (
629                  :instId, :id, :listId, :pageId,
630                  :version, :parentId, :sequenceNo, :definitionId,
631                  :linkedPageId, :title, now(), now(),
632                  :dateOnline, :dateOffline
633              )");
634  
635          $this->context->connection->bindInstance($sth);
636  
637          $sth->bindValue(":id"$id, \PDO::PARAM_INT);
638          $sth->bindValue(":listId"$item->listId, \PDO::PARAM_INT);
639          $sth->bindValue(":pageId"$item->pageId, \PDO::PARAM_INT);
640          $sth->bindValue(":version"$item->version, \PDO::PARAM_INT);
641          $sth->bindValue(":parentId"$item->parentId, \PDO::PARAM_INT);
642          $sth->bindValue(":sequenceNo"0, \PDO::PARAM_INT);
643          $sth->bindValue(":definitionId",
644              $item->definitionId, \PDO::PARAM_INT);
645          // TODO: no item-> ???
646          $sth->bindValue(":linkedPageId"$linkedPageId, \PDO::PARAM_INT);
647          $sth->bindValue(":title"$item->title, \PDO::PARAM_STR);
648          $sth->bindValue(":dateOnline",
649              $item->dateOnline->format("Y-m-d H:i:s"), \PDO::PARAM_STR);
650          $sth->bindValue(":dateOffline"$item->dateOffline
651              $item->dateOffline->format("Y-m-d H:i:s")
652              : null, \PDO::PARAM_STR);
653  
654          $sth->execute();
655  
656          foreach ($item->properties as $prp) {
657              $this->updateProperty($prp$id);
658          }
659  
660          \Scrivo\SequenceNo::position($this->context"list_item",
661              array("parent_id""item_list_id"), $id, \Scrivo\SequenceNo::MOVE_FIRST);
662  
663      }
664  
665      /**
666       * Update existing list item object data in the database.
667       *
668       * First it is checked if the data of this list item object can be updated
669       * in the database, then the data is updated in the database.
670       *
671       * @param \Scrivo\ListItem $item The list item to update in the database.
672       *
673       * @throws \Scrivo\ApplicationException If the data is not accessible or
674       *   one or more of the fields contain invalid data.
675       */
676      private function updateItem(\Scrivo\ListItem $item) {
677  
678          // Create a sub-page if necessary
679          $def = \Scrivo\ListItemDefinition::fetch(
680              $this->context$item->definitionId);
681          $linkedPageId $item->linkedPageId;
682          if (!$linkedPageId && $def->pageDefinitionId) {
683              $linkedPageId $this->createLinkedPage($item$def);
684          }
685  
686          $sth $this->context->connection->prepare(
687              "UPDATE list_item SET
688                  parent_id = :parentId, title = :title, link_id = :linkedPage,
689                  date_online = :dateOnline, date_offline = :dateOffline
690              WHERE instance_id = :instId AND list_item_id = :id");
691  
692          $this->context->connection->bindInstance($sth);
693          $sth->bindValue(":id"$item->id, \PDO::PARAM_INT);
694  
695          $sth->bindValue(":parentId"$item->parentId, \PDO::PARAM_INT);
696          $sth->bindValue(":linkedPage"$linkedPageId, \PDO::PARAM_INT);
697          $sth->bindValue(":title"$item->title, \PDO::PARAM_STR);
698          $sth->bindValue(":dateOnline",
699              $item->dateOnline->format("Y-m-d H:i:s"), \PDO::PARAM_STR);
700          $sth->bindValue(":dateOffline"$item->dateOffline
701              $item->dateOffline->format("Y-m-d H:i:s")
702              : null, \PDO::PARAM_STR);
703  
704          $sth->execute();
705  
706          foreach ($item->properties as $prp) {
707              $this->updateProperty($prp$item->id);
708          }
709  
710      }
711  
712      /**
713       * Create a new list item to insert in the database.
714       *
715       * @param \Scrivo\String $phpSelector The selector of the list item
716       *   definition of the list item to create.
717       *
718       * @return \Scrivo\ListItem
719       */
720      public function newItem(\Scrivo\String $phpSelector) {
721          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(null));
722          try {
723              $defId $this->getDefinitionId($phpSelector);
724  
725              return new \Scrivo\ListItem(
726                  array(
727                      "list_item_id" => 0,
728                      "item_list_id" => $this->id,
729                      "page_id" => $this->pageId,
730                      "version" => 0,
731                      "parent_id" => 0,
732                      "sequence_no" => 0,
733                      "list_item_definition_id" => $defId,
734                      "link_id" => 0,
735                      "title" => new \Scrivo\String(""),
736                      "date_created" => "now",
737                      "date_modified" => "now",
738                      "date_online" => "now",
739                      "date_offline" => null
740                  ),
741                  self::selectEmptyProperties($defId)
742              );
743  
744          } catch(\PDOException $e) {
745              throw new \Scrivo\ResourceException($e);
746          }
747      }
748  
749      /**
750       * Save list item data to the database.
751       *
752       * @param \Scrivo\ListItem $item The list item to save, either an existing
753       *   item updated item or a new one craeted with the newItem method.
754       *
755       * @throws \Scrivo\ApplicationException if the data is not accessible or
756       *   it is not possible to insert or update the list item data.
757       */
758      public function saveItem(\Scrivo\ListItem $item) {
759          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(null));
760          try {
761              $this->context->checkPermission(
762                  \Scrivo\AccessController::WRITE_ACCESS$this->pageId);
763  
764              if ($item->id === 0) {
765                  $this->insertItem($item);
766              } else {
767                  $this->updateItem($item);
768              }
769  
770              $this->removeFromCache();
771  
772          } catch(\PDOException $e) {
773              throw new \Scrivo\ResourceException($e);
774          }
775      }
776  
777      /**
778       * Delete existing list item data from the database.
779       *
780       * First it is is checked if it's possible to delete list item data,
781       * then the list item data including its dependencies is deleted from
782       * the database.
783       *
784       * @param int $id The object id of the list item to delete.
785       *
786       * @throws \Scrivo\ApplicationException If the data is not accessible or
787       *   if it is not possible to delete the list item data.
788       */
789      public function deleteItem($id) {
790          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(
791              array(\Scrivo\ArgumentCheck::TYPE_INTEGER)
792          ));
793          try {
794              // TODO test delete when there are children
795              $this->context->checkPermission(
796                  \Scrivo\AccessController::WRITE_ACCESS$this->pageId);
797  
798              // TODO: delete sub page
799  
800              $sth $this->context->connection->prepare(
801                  "DELETE FROM list_item
802                      WHERE instance_id = :instId AND list_item_id = :id");
803  
804              $this->context->connection->bindInstance($sth);
805              $sth->bindValue(":id"$id, \PDO::PARAM_INT);
806  
807              $sth->execute();
808  
809              $sth $this->context->connection->prepare(
810                  "DELETE FROM list_item_property
811                      WHERE instance_id = :instId AND list_item_id = :id");
812  
813              $this->context->connection->bindInstance($sth);
814              $sth->bindValue(":id"$id, \PDO::PARAM_INT);
815  
816              $sth->execute();
817  
818              $this->removeFromCache();
819  
820          } catch(\PDOException $e) {
821              throw new \Scrivo\ResourceException($e);
822          }
823      }
824  
825      /**
826       * Move a list item to another position amongst its siblings.
827       *
828       * @param int $id The object id of the list item to move.
829       * @param int $dir Position or direction of the move,
830       *      see \Scrivo\SequenceNo:::MOVE_*
831       */
832      public function moveItem($id$dir=\Scrivo\SequenceNo::MOVE_DOWN) {
833          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(
834                  array(\Scrivo\ArgumentCheck::TYPE_INTEGER),
835                  array(\Scrivo\ArgumentCheck::TYPE_INTEGER)
836          ));
837  
838          $this->context->checkPermission(
839                  \Scrivo\AccessController::WRITE_ACCESS$this->pageId);
840  
841          \Scrivo\SequenceNo::position($this->context"list_item",
842              array("parent_id""item_list_id"), $id$dir);
843  
844          $this->removeFromCache();
845      }
846  
847      /**
848       * Remove this list (including all sub lists from the cache).
849       */
850      private function removeFromCache() {
851  
852          $cId $this->pageId "." $this->pagePropertyDefinitionId;
853          if ($this->listPids) {
854              $listIds array_keys($this->listPids);
855              foreach ($listIds as $id) {
856                  unset($this->context->cache[$cId "." $id]);
857              }
858          }
859          unset($this->context->cache[$cId]);
860      }
861  
862  
863  }
864  
865  ?>

Documentation generated by phpDocumentor 2.0.0a12 and ScrivoDocumentor on August 29, 2013