1  <?php
  2  /* Copyright (c) 2013, 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: PageDefinition.php 866 2013-08-25 16:22:35Z geert $
 30   */
 31  
 32  /**
 33   * Implementation of the \Scrivo\PageDefinition class.
 34   */
 35  
 36  namespace Scrivo;
 37  
 38  /**
 39   * The PageDefinition class is used to create a definition for a page.
 40   *
 41   * In the Scrivo user interface the editor will select a page definition to
 42   * create a page from. The page definition holds a list of configurable
 43   * properties (text blocks, images, urls, colors, etc) that can be edited for
 44   * that page. See also the class PageDefinitionProperty that is used to define
 45   * the individual properties and the class PageDefinitionTab that is used to
 46   * distribute these properties over several tabs in the Scrivo user interface.
 47   *
 48   * Furthermore the page definition holds a reference to the PHP script that
 49   * should be executed when rendering pages with this page definition
 50   * definition.
 51   *
 52   * PageDefinitions can be suppressed in the user interface using the
 53   * 'configOnly'property: for instance, there is no need to let the editor
 54   * chose another page using the 'Home' page definition in a site. Or they can
 55   * be shown conditionally in the interface: you can only select pages of page
 56   * definition 'Main Menu' as sub pages of a page of page definition 'Home'.
 57   * See the 'PageDefinitionHints' class for more details.
 58   *
 59   * TODO: field type_set now is a semicolon seperated list (DB data), better
 60   *  to serialize this data.
 61   * TODO: field search_index_rule has no function anymore.
 62   *
 63   * @property-read int $id The page definition id (DB key).
 64   * @property-read object $properties The page definition properties.
 65   * @property-read \Scrivo\PageDefinitionTab[] $tabs The page definition tabs.
 66   * @property boolean $configOnly Setting to suppress this page definition in
 67   *   the user interface.
 68   * @property int $defaultTabId The id of the tab that should be shown in the
 69   *   user interface as the active tab when the editor selects a page using this
 70   *   page definition for editing.
 71   * @property \Scrivo\String $description An additional description for the
 72   *   page definition.
 73   * @property \Scrivo\String $action The location of the PHP script to
 74   *   execute when rendering pages using this page definition.
 75   * @property \Scrivo\String $title A descriptive title for the page definition.
 76   * @property int[] $typeSet The set of page types that the user can select
 77   *   in the user interface  when creating/modifing pages using this page
 78   *   definition.
 79   */
 80  class PageDefinition {
 81  
 82      /**
 83       * The page definition id (DB key).
 84       * @var int
 85       */
 86      private $id 0;
 87  
 88      /**
 89       * A descriptive title for the page definition.
 90       * @var \Scrivo\String
 91       */
 92      private $title null;
 93  
 94      /**
 95       * An additional description for the page definition.
 96       * @var \Scrivo\String
 97       */
 98      private $description null;
 99  
100      /**
101       * The location of the PHP script to execute when rendering pages using
102       * this page definition.
103       * @var \Scrivo\String
104       */
105      private $action null;
106  
107      /**
108       * Setting to suppress this page definition in the user interface.
109       * @var boolean
110       */
111      private $configOnly false;
112  
113      /**
114       * The set of page types that the user can select in the user interface
115       * when creating/modifing pages using this page definition.
116       * @var int[]
117       */
118      private $typeSet = array();
119  
120      /**
121       * The id of the tab that should be shown in the user interface as the
122       * active tab when the editor selects a page using this page definition
123       * for editing.
124       * @var int
125       */
126      private $defaultTabId 0;
127  
128      /**
129       * The page definition properties as a PHP object in which the members
130       * correspond with the PHP selector names.
131       * @var \stdClass
132       */
133      protected $properties null;
134  
135      /**
136       * The page definition tabs.
137       * @var \Scrivo\PageDefinitionTab[]
138       */
139      protected $tabs null;
140  
141      /**
142       * A Scrivo context.
143       * @var \Scrivo\Context
144       */
145      private $context null;
146  
147      /**
148       * Create an empty page definition object.
149       *
150       * @param \Scrivo\Context $context A Scrivo context.
151       */
152      public function __construct(\Scrivo\Context $context=null) {
153          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(null), 0);
154  
155          if ($context) {
156              $this->title = new \Scrivo\String();
157              $this->description = new \Scrivo\String();
158              $this->action = new \Scrivo\String();
159  
160              $this->context $context;
161          }
162      }
163  
164      /**
165       * Implementation of the readable properties using the PHP magic
166       * method __get().
167       *
168       * @param string $name The name of the property to get.
169       *
170       * @return mixed The value of the requested property.
171       */
172      public function __get($name) {
173          switch($name) {
174              case "id": return $this->id;
175              case "title": return $this->title;
176              case "description": return $this->description;
177              case "action": return $this->action;
178              case "configOnly": return $this->configOnly;
179              case "typeSet": return $this->typeSet;
180              case "defaultTabId": return $this->defaultTabId;
181              case "properties": return $this->getProperties();
182              case "tabs": return $this->getTabs();
183          }
184          throw new \Scrivo\SystemException("No such get-property '$name'.");
185      }
186  
187      /**
188       * Implementation of the writable properties using the PHP magic
189       * method __set().
190       *
191       * @param string $name The name of the property to set.
192       * @param mixed $value The value of the property to set.
193       */
194      public function __set($name$value) {
195          switch($name) {
196              case "title"$this->setTitle($value); return;
197              case "description"$this->setDescription($value); return;
198              case "action"$this->setFileName($value); return;
199              case "configOnly"$this->setConfigOnly($value); return;
200              case "typeSet"$this->setTypeSet($value); return;
201              case "defaultTabId"$this->setDefaultTabId($value); return;
202          }
203          throw new \Scrivo\SystemException("No such set-property '$name'.");
204      }
205  
206      /**
207       * Convenience method to set the fields of a page definition object from
208       * an array (result set row).
209       *
210       * @param \Scrivo\Context $context A Scrivo context.
211       * @param array $rd An array containing the field data using the database
212       *    field names as keys.
213       */
214      private function setFields(\Scrivo\Context $context, array $rd) {
215  
216          $this->id intval($rd["page_definition_id"]);
217          $this->title = new \Scrivo\String($rd["title"]);
218          $this->description = new \Scrivo\String($rd["description"]);
219          $this->action = new \Scrivo\String($rd["action"]);
220          $this->configOnly intval($rd["config_only"]) == true false;
221          $this->typeSet =
222              $this->convertTypeSet(new \Scrivo\String($rd["type_set"]));
223          $this->defaultTabId intval($rd["default_tab_id"]);
224  
225          $this->context $context;
226      }
227  
228      /**
229       * Get this page definition's property list.
230       *
231       * @return object This page definition's property list.
232       */
233      private function getProperties() {
234          if ($this->properties === null) {
235              $this->properties = (object)
236                  \Scrivo\PagePropertyDefinition::select($this->context$this->id);
237          }
238          return $this->properties;
239      }
240  
241      /**
242       * Get this page definition's tabs.
243       *
244       * @return \Scrivo\PageDefinitionTab[] This page definition's tabs.
245       */
246      private function getTabs() {
247          if ($this->tabs === null) {
248              $this->tabs =
249                  \Scrivo\PageDefinitionTab::select($this->context$this->id);
250          }
251          return $this->tabs;
252      }
253  
254      /**
255       * Set A descriptive title for the page definition.
256       *
257       * @param \Scrivo\String $title A descriptive title for the page
258       *    definition.
259       */
260      private function setTitle(\Scrivo\String $title) {
261          $this->title $title;
262      }
263  
264      /**
265       * Set the additional description for the page definition.
266       *
267       * @param \Scrivo\String $description An additional description for the
268       *    page definition.
269       */
270      private function setDescription(\Scrivo\String $description) {
271          $this->description $description;
272      }
273  
274      /**
275       * Set the location of the PHP script to execute when rendering pages
276       * using this page definition.
277       *
278       * @param \Scrivo\String $action The location of the PHP script to
279       *    execute when rendering pages using this page definition.
280       */
281      private function setFileName(\Scrivo\String $action) {
282          $this->action $action;
283      }
284  
285      /**
286       * Set the setting to suppress this page definition in the user interface.
287       *
288       * @param boolean $configOnly Setting to suppress this page definition in
289       *    the user interface.
290       */
291      private function setConfigOnly($configOnly) {
292          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(
293              array(\Scrivo\ArgumentCheck::TYPE_BOOLEAN)
294          ));
295  
296          $this->configOnly $configOnly;
297      }
298  
299      /**
300       * Set the set of page types that the user can select in the user interface
301       * when creating/modifing pages using this page definition.
302       *
303       * @param int[] $typeSet The set of page types that the user can select in
304       *    the user interface  when creating/modifing pages using this page
305       *    definition.
306       */
307      private function setTypeSet(array $typeSet) {
308          $this->typeSet $typeSet;
309      }
310  
311      /**
312       * Set the id of the tab that should be shown in the user interface as
313       * the active tab when the editor selects a page using this page definition
314       * for editing.
315       *
316       * @param int $defaultTabId The id of the tab that should be shown in the
317       *    user interface as the active tab when the editor selects a page using
318       *    this page definition for editing.
319       */
320      private function setDefaultTabId($defaultTabId) {
321          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(
322              array(\Scrivo\ArgumentCheck::TYPE_INTEGER)
323          ));
324  
325          $this->defaultTabId $defaultTabId;
326      }
327  
328      /**
329       * Convert the semicolon seperated value from the database to an array.
330       *
331       * @param \Scrivo\String typeSetString An semicolon seperated string.
332       * @return int[] The type set as an array of integers.
333       */
334      private function convertTypeSet(\Scrivo\String $typeSetString) {
335          $ts $typeSetString->split(new \Scrivo\String(";"));
336          $ts2 = array();
337          foreach ($ts as $pageId) {
338              $ts2[] = intval((string)$pageId);
339          }
340          return $ts2;
341      }
342  
343      /**
344       * Check if this page definition object can be inserted into the database.
345       *
346       * @throws \Scrivo\ApplicationException If the data is not accessible or
347       *   one or more of the fields contain invalid data.
348       */
349      private function validateInsert() {
350          $this->context->checkPermission(\Scrivo\AccessController::WRITE_ACCESS);
351      }
352  
353      /**
354       * Insert new page definition object data into the database.
355       *
356       * First it is checked if the data of this page definition object can be
357       * inserted into the database, then the data is inserted into the database.
358       * If no id was set a new object id is generated.
359       *
360       * @throws \Scrivo\ApplicationException If the data is not accessible or
361       *   one or more of the fields contain invalid data.
362       */
363      public function insert() {
364          try {
365              $this->validateInsert();
366  
367              if (!$this->id) {
368                  $this->id $this->context->connection->generateId();
369              }
370  
371              $sth $this->context->connection->prepare(
372                  "INSERT INTO page_definition (
373                      instance_id, page_definition_id, title, description, action,
374                      config_only, type_set, default_tab_id
375                  ) VALUES (
376                      :instId, :id, :title, :description, :action,
377                      :configOnly, :typeSet, :defaultTabId
378                  )");
379  
380              $this->context->connection->bindInstance($sth);
381              $sth->bindValue(":id"$this->id, \PDO::PARAM_INT);
382              $sth->bindValue(":title"$this->title, \PDO::PARAM_STR);
383              $sth->bindValue(
384                  ":description"$this->description, \PDO::PARAM_STR);
385              $sth->bindValue(":action"$this->action, \PDO::PARAM_STR);
386              $sth->bindValue(":configOnly",
387                  $this->configOnly 0, \PDO::PARAM_INT);
388              $sth->bindValue(":typeSet", new \Scrivo\String(
389                  implode(";"$this->typeSet)), \PDO::PARAM_INT);
390              $sth->bindValue(
391                  ":defaultTabId"$this->defaultTabId, \PDO::PARAM_INT);
392  
393              $sth->execute();
394  
395          } catch(\PDOException $e) {
396              throw new \Scrivo\ResourceException($e);
397          }
398      }
399  
400      /**
401       * Check if this page definition object can be updated in the database.
402       *
403       * @throws \Scrivo\ApplicationException If the data is not accessible or
404       *   one or more of the fields contain invalid data.
405       */
406      private function validateUpdate() {
407          $this->context->checkPermission(\Scrivo\AccessController::WRITE_ACCESS);
408      }
409  
410      /**
411       * Update existing page definition object data in the database.
412       *
413       * First it is checked if the data of this page definition object can be
414       * updated in the database, then the data is updated in the database.
415       *
416       * @throws \Scrivo\ApplicationException If the data is not accessible or
417       *   one or more of the fields contain invalid data.
418       */
419      public function update() {
420          try {
421              $this->validateUpdate();
422  
423              $sth $this->context->connection->prepare(
424                  "UPDATE page_definition SET
425                      title = :title, description = :description,
426                      action = :action, config_only = :configOnly,
427                      type_set = :typeSet, default_tab_id = :defaultTabId
428                  WHERE instance_id = :instId AND page_definition_id = :id");
429  
430              $this->context->connection->bindInstance($sth);
431              $sth->bindValue(":id"$this->id, \PDO::PARAM_INT);
432  
433              $sth->bindValue(":title"$this->title, \PDO::PARAM_STR);
434              $sth->bindValue(
435                  ":description"$this->description, \PDO::PARAM_STR);
436              $sth->bindValue(":action"$this->action, \PDO::PARAM_STR);
437              $sth->bindValue(":configOnly",
438                  $this->configOnly 0, \PDO::PARAM_INT);
439              $sth->bindValue(":typeSet", new \Scrivo\String(
440                  implode(";"$this->typeSet)), \PDO::PARAM_INT);
441              $sth->bindValue(
442                  ":defaultTabId"$this->defaultTabId, \PDO::PARAM_INT);
443  
444              $sth->execute();
445  
446              unset($this->context->cache[$this->id]);
447  
448          } catch(\PDOException $e) {
449              throw new \Scrivo\ResourceException($e);
450          }
451      }
452  
453      /**
454       * Check if deletion of page definition object data does not violate any
455       * business rules.
456       *
457       * @param \Scrivo\Context $context A Scrivo context.
458       * @param int $id The object id of the page definition to select.
459       *
460       * @throws \Scrivo\ApplicationException If the data is not accessible or
461       *   if it is not possible to delete the language data.
462       */
463      private static function validateDelete(\Scrivo\Context $context$id) {
464          $context->checkPermission(\Scrivo\AccessController::WRITE_ACCESS);
465      }
466  
467      /**
468       * Delete existing page definition data from the database.
469       *
470       * First it is is checked if it's possible to delete page definition data,
471       * then the page definition data including its dependencies is deleted from
472       * the database.
473       *
474       * @param \Scrivo\Context $context A Scrivo context.
475       * @param int $id The object id of the page definition to select.
476       *
477       * @throws \Scrivo\ApplicationException If the data is not accessible or
478       *   if it is not possible to delete the page definition data.
479       */
480      public static function delete(\Scrivo\Context $context$id) {
481          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(
482              null,
483              array(\Scrivo\ArgumentCheck::TYPE_INTEGER)
484          ));
485          try {
486              self::validateDelete($context$id);
487  
488              foreach (array("page_property_definition""page_definition_tab""page_definition")
489                  as $table) {
490  
491                  $sth $context->connection->prepare(
492                      "DELETE FROM $table
493                      WHERE instance_id = :instId AND page_definition_id = :id");
494  
495                  $context->connection->bindInstance($sth);
496                  $sth->bindValue(":id"$id, \PDO::PARAM_INT);
497  
498                  $sth->execute();
499              }
500  
501              unset($context->cache[$id]);
502  
503          } catch(\PDOException $e) {
504              throw new \Scrivo\ResourceException($e);
505          }
506      }
507  
508      /**
509       * Fetch a page definition object from the database using its object id.
510       *
511       * @param \Scrivo\Context $context A Scrivo context.
512       * @param int $id The object id of the page definition to select.
513       *
514       * @return \Scrivo\PageDefinition The requested page definition object.
515       */
516      public static function fetch(\Scrivo\Context $context$id) {
517          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(
518              null,
519              array(\Scrivo\ArgumentCheck::TYPE_INTEGER)
520          ));
521          try {
522              // Try to retieve the page definition from the cache ...
523              if (isset($context->cache[$id])) {
524                  // ... get it from the cache and set the context.
525                  $pageDefinition $context->cache[$id];
526                  $pageDefinition->context $context;
527              } else {
528                  // ... else retrieve it and set it in the cache.
529                  $sth $context->connection->prepare(
530                      "SELECT page_definition_id, title, description, action,
531                          config_only, type_set, default_tab_id
532                      FROM page_definition
533                      WHERE instance_id = :instId AND page_definition_id = :id");
534  
535                  $context->connection->bindInstance($sth);
536                  $sth->bindValue(":id"$id, \PDO::PARAM_INT);
537  
538                  $sth->execute();
539  
540                  if ($sth->rowCount() != 1) {
541                      throw new \Scrivo\SystemException(
542                          "Failed to load page definition $id");
543                  }
544  
545                  $pageDefinition = new \Scrivo\PageDefinition();
546                  $pageDefinition->setFields(
547                      $context$sth->fetch(\PDO::FETCH_ASSOC));
548  
549                  $context->cache[$id] = $pageDefinition;
550              }
551  
552              return $pageDefinition;
553  
554          } catch(\PDOException $e) {
555              throw new \Scrivo\ResourceException($e);
556          }
557      }
558  
559      /**
560       * Select page definitions from the database.
561       *
562       * @param \Scrivo\Context $context A Scrivo context.
563       *
564       * @return \Scrivo\PageDefinition[id] An array containing the selected
565       *    page definitions.
566       */
567      public static function select(\Scrivo\Context $context) {
568          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(null));
569          try {
570              $sth $context->connection->prepare(
571                  "SELECT page_definition_id, title, description, action,
572                      config_only, type_set, default_tab_id
573                  FROM page_definition
574                  WHERE instance_id = :instId
575                  ORDER BY title");
576  
577              $context->connection->bindInstance($sth);
578  
579              $sth->execute();
580  
581              $res = array();
582  
583              while ($rd $sth->fetch(\PDO::FETCH_ASSOC)) {
584  
585                  $li = new PageDefinition();
586                  $li->setFields($context$rd);
587  
588                  $res[$li->id] = $li;
589              }
590  
591              return $res;
592  
593          } catch(\PDOException $e) {
594              throw new \Scrivo\ResourceException($e);
595          }
596      }
597  
598      /**
599       * Select the list of page definitions that are selectable by a an editor.
600       *
601       * @param \Scrivo\Context $context A Scrivo context.
602       * @param int $parentId The id of the page where the editor wants to
603       *   create a page underneath.
604       *
605       * @return PageDefinition[id] An array containing the selected page
606       *   definitions.
607       */
608      public static function selectSelectable(
609              \Scrivo\Context $context$parentId) {
610          \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array(
611              null,
612              array(\Scrivo\ArgumentCheck::TYPE_INTEGER)
613          ));
614  
615          // Select all the page definitions ...
616          $list self::select($context);
617  
618          // ... and the hints ...
619          $hints = new \Scrivo\PageDefinitionHints($context$parentId,
620              \Scrivo\PageDefinitionHints::CHILD_PAGE_DEFINITION_REMAINING);
621  
622          // ... and create a new list of page definitions that are not config
623          // only and have a count left according the hints list.
624          $pageDefinitions = array();
625          foreach ($list as $k=>$pageDef) {
626              if (!$pageDef->configOnly && $hints[$k]->maxNoOfChildren !== 0) {
627                  $pageDefinitions[$k] = $pageDef;
628              }
629          }
630  
631          return $pageDefinitions;
632      }
633  
634  }
635  
636  ?>

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