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: PageDefinitionHints.php 866 2013-08-25 16:22:35Z geert $
 30   */
 31  
 32  /**
 33   * Implementation of the \Scrivo\PageDefinitionHints class.
 34   */
 35  
 36  namespace Scrivo;
 37  
 38  /**
 39   * The PageDefinitionHints class is used to prevent page definition selection
 40   * in the Scrivo user interface.
 41   *
 42   * In principle it is possible to create pages of any page definition
 43   * underneath any other page. But from the viewpoint of the site designer this
 44   * is not always desirable. The site might require specific rules such as that
 45   * there are only 'main menu' pages allowed underneath the home.
 46   *
 47   * Likewise, the site editor will not be facilitated when offered long lists
 48   * of page definitions (with possible many irrelevant entries) each time he or
 49   * she wants to add a page.
 50   *
 51   * To guide the user in page definition selection the PageDefinitionHints class
 52   * lists the number of occurances of pages of a specific page definition that
 53   * is allowed under a page of a given page definition. For these lists it can
 54   * be decided if a page definition is selectable when creating a page.
 55   *
 56   * Suppose we have three page definitions: Home, Standard and Contact. Then a
 57   * likely scenario is that we don't want the the editor the select the 'Home'
 58   * page definition at any time, the 'Standard' page definition for as many
 59   * times as desired but only as a child of a page of page definition 'Home' or
 60   * 'Standard' and the 'Contact' page only once as child of the home page (the
 61   * page of page definition 'Home').
 62   *
 63   * Again these are merely hints. In the super-interfaces 'admin' and 'config'
 64   * your still allowed to make all combinations you want. These hints are only
 65   * used as a guide in the editor interface.
 66   */
 67  class PageDefinitionHints implements \Iterator, \ArrayAccess {
 68  
 69      /**
 70       * Constant to denote that we want to retrieve the list of how many times
 71       * a page of this page definition may occur underneath pages of other
 72       * page definitions.
 73       */
 74      const PARENT_PAGE_DEFINITION_COUNT 1;
 75  
 76      /**
 77       * Constant to denote we want to retrieve the list of how many times pages
 78       * of other page definitions (total count) may occur underneath the current
 79       * page.
 80       */
 81      const CHILD_PAGE_DEFINITION_COUNT 2;
 82  
 83      /**
 84       * Constant to denote we want to retrieve the list of how many times pages
 85       * of other page definitions (remaining count) may occur underneath the
 86       * current page.
 87       */
 88      const CHILD_PAGE_DEFINITION_REMAINING 3;
 89  
 90      /**
 91       * The id of the page definition to retrieve the list.
 92       * @var int
 93       */
 94      private $pageDefinitionId 0;
 95  
 96      /**
 97       * The type of the list:
 98       * \Scrivo\PageDefinitionHints::PARENT_PAGE_DEFINITION_COUNT,
 99       * \Scrivo\PageDefinitionHints::CHILD_PAGE_DEFINITION_COUNT or
100       * \Scrivo\PageDefinitionHints::CHILD_PAGE_DEFINITION_REMAINING.
101       * @var int
102       */
103      private $type 0;
104  
105      /**
106       * The hints array.
107       * @var object[]
108       */
109      private $hints null;
110  
111      /**
112       * A Scrivo context
113       * @var \Scrivo\Context
114       */
115      private $context null;
116  
117      /**
118       * Construct a pageDefinition hints object. Depending on the context where
119       * you want to use these hints for a number of different lists can be
120       * constructed. For instance, when defining the user interface one need
121       * lists that contain the hints as stored in the database
122       * (PARENT_PAGE_DEFINITION_COUNT or CHILD_PAGE_DEFINITION_COUNT). But in
123       * the user interface itself you'll want to to use the rules as defined but
124       * corrected for the pages already created.
125       *
126       * Suppose there are three main menus are allowed under the a home page.
127       * The types PARENT_PAGE_DEFINITION_COUNT or CHILD_PAGE_DEFINITION_COUNT
128       * will give you that information. But if there are alreay two main menus
129       * are created under the home CHILD_PAGE_DEFINITION_REMAINING will give
130       * you the corrected result of just one main menu allowed under the a home
131       * page.
132       *      *
133       * @param \Scrivo\Context $context A valid Scrivo context.
134       * @param int $pageDefinitionId The id of the pageDefinition to create the
135       *   hints for, or the page id in the case of
136       *   CHILD_PAGE_DEFINITION_REMAINING.
137       * @param int $listType The list type to create: either
138       *   \Scrivo\PageDefinitionHints::PARENT_PAGE_DEFINITION_COUNT,
139       *   \Scrivo\PageDefinitionHints::CHILD_PAGE_DEFINITION_COUNT or
140       *   \Scrivo\PageDefinitionHints::CHILD_PAGE_DEFINITION_REMAINING.
141       * @param int $listType If the listType parameter was set to
142       *   \Scrivo\PageDefinitionHints::CHILD_PAGE_DEFINITION_REMAINING then the
143       *   id for the page for which to correct the result needs to be supplied.
144       */
145      function __construct(
146              \Scrivo\Context $context$pageDefinitionId$listType) {
147  
148          $this->context $context;
149          $this->type $listType;
150  
151          if (self::CHILD_PAGE_DEFINITION_COUNT == $listType) {
152              $this->pageDefinitionId $pageDefinitionId;
153              $this->load(true);
154          } else if (self::CHILD_PAGE_DEFINITION_REMAINING == $listType) {
155              $this->loadCorrected($pageDefinitionId);
156          } else if (self::PARENT_PAGE_DEFINITION_COUNT == $listType) {
157              $this->pageDefinitionId $pageDefinitionId;
158              $this->load(false);
159          } else {
160              throw new \Scrivo\SystemException("invalid list type");
161          }
162      }
163  
164      /**
165       * Get the page definition hint for a given page definition (id).
166       *
167       * @param int $key A page definition id used as key in the hints array.
168       *
169       * @return object The hint for the given page definition. The hint has the
170       *   following fields: pageDefinitionId, title and maxNoOfChilds.
171       *
172       * @throws \Scrivo\SystemException If the requested offset was out of
173       *   range.
174       */
175      public function offsetGet($key) {
176          if (!isset($this->hints[$key])) {
177              throw new \Scrivo\SystemException(
178                  "PageDefinitionHints invalid index");
179          }
180          return $this->hints[$key];
181      }
182  
183      /**
184       * Part of the implementation of \ArrayAccess. Not applicable for
185       * PageDefinitionHints.
186       *
187       * @param int $key
188       * @param string $value
189       *
190       * @throws \Scrivo\SystemException If this method is called.
191       */
192      public function offsetSet($key$value) {
193          throw new \Scrivo\SystemException(
194              "offsetSet can't be called on PageDefinitionHints objects");
195      }
196  
197      /**
198       * Test if a hint exists at the requested index location.
199       *
200       * @param int $key A page definition id used as key in the hints array.
201       */
202      public function offsetExists($key) {
203          return isset($this->hints[$key]);
204      }
205  
206      /**
207       * Part of the implementation of \ArrayAccess. Not applicable for
208       * PageDefinitionHints.
209       *
210       * @param int $key
211       *
212       * @throws \Exception If this method is called.
213       */
214      public function offsetUnset($key) {
215          throw new \Scrivo\SystemException(
216              "offsetUnset can't be called on PageDefinitionHints objects");
217      }
218  
219      /**
220       * Rewind the hints array so iterating will start at the beginning again.
221       */
222      function rewind() {
223          reset($this->hints);
224      }
225  
226      /**
227       * Get the current page definition hint when iterating.
228       */
229      function current() {
230          return current($this->hints);
231      }
232  
233      /**
234       * Get the key of the current page definition hint when iterating.
235       */
236      function key() {
237          return key($this->hints);
238      }
239  
240      /**
241       * Get the next page definition hint when iterating.
242       */
243      function next() {
244          next($this->hints);
245      }
246  
247      /**
248       * Check if the current key is valid.
249       */
250      function valid() {
251          return key($this->hints) ? true false;
252      }
253  
254      /**
255       * Load the page definition hints as defined in the database. This list
256       * can be generated from two different viewpoints:
257       * 1) pages of this page definition can be used x times under pages of
258       *    some other page definition
259       *    ($children==false/PARENT_PAGE_DEFINITION_COUNT), or
260       * 2) pages of some page definition can occur x times under a page using
261       *    this page definition ($children==true/CHILD_PAGE_DEFINITION_COUNT)
262       *
263       * @param boolean $children False (default) if you want to select how
264       *    many times a page using this page definition may occur under pages
265       *    of some other page definition, True if you want to select how many
266       *    times a pages of some other page definition may occur under a page
267       *    using this page definition.
268       */
269      private function load($children=false) {
270          try {
271              $this->context->checkPermission(AccessController::READ_ACCESS);
272  
273              $sth $this->context->connection->prepare(
274                  "SELECT    page_definition_id, title FROM page_definition
275                  WHERE instance_id = :instId ORDER BY title");
276  
277              $this->context->connection->bindInstance($sth);
278  
279              $sth->execute();
280  
281              $res = array();
282              while ($rd $sth->fetch(\PDO::FETCH_ASSOC)) {
283                  $k intval($rd["page_definition_id"]);
284                  $res[$k] = (object)array(
285                      "pageDefinitionId" => $k,
286                      "title" => $rd["title"],
287                      "maxNoOfChildren" => NULL
288                  );
289              }
290  
291              if ($children) {
292                  // CHILD_PAGE_DEFINITION_COUNT
293                  $sth $this->context->connection->prepare(
294                      "SELECT    page_definition_id parent_page_definition_id, max_no_of_children
295                      FROM page_definition_hints WHERE instance_id = :instId AND
296                          parent_page_definition_id = :templId");
297              } else {
298                  // PARENT_PAGE_DEFINITION_COUNT
299                  $sth $this->context->connection->prepare(
300                      "SELECT    parent_page_definition_id, max_no_of_children
301                      FROM page_definition_hints    WHERE instance_id = :instId AND
302                          page_definition_id = :templId");
303              }
304  
305              $this->context->connection->bindInstance($sth);
306              $sth->bindValue(
307                  ":templId"$this->pageDefinitionId, \PDO::PARAM_INT);
308  
309              $sth->execute();
310  
311              while ($rd $sth->fetch(\PDO::FETCH_ASSOC)) {
312                  $k intval($rd["parent_page_definition_id"]);
313                  if (isset($res[$k])) {
314                      $res[$k]->maxNoOfChildren =
315                          intval($rd["max_no_of_children"]);
316                  }
317              }
318  
319              $this->hints $res;
320  
321          } catch(\PDOException $e) {
322              throw new \Scrivo\ResourceException($e);
323          }
324      }
325  
326      /**
327       * Load the page definition hints as defined in the database, but corrected
328       * for the currently created pages. This is basically an extion of
329       * PageDefinitionHints::load(true)/pages of some page definition can occur
330       * x times under a page using this page
331       * definition/CHILD_PAGE_DEFINITION_COUNT and a correction for pages that
332       * are made already.
333       *
334       * The parent page is given as an argument, the list generated is a list
335       * of page definitions and how many times new pages of each page definition
336       * are still allowed underneath the given page.
337       *
338       * @param int $parentId The id of the parent page.
339       */
340      private function loadCorrected($parentId) {
341          try {
342              $this->context->checkPermission(AccessController::READ_ACCESS);
343  
344              $sth $this->context->connection->prepare(
345                  "SELECT page_definition_id FROM page WHERE instance_id = :instId
346                  AND (has_staging+version) = 0 AND page_id = :docPid");
347  
348              $this->context->connection->bindInstance($sth);
349              $sth->bindValue(":docPid"$parentId, \PDO::PARAM_INT);
350  
351              $sth->execute();
352  
353              if ($sth->rowCount() != 1) {
354                  throw new \Scrivo\SystemException("Failed to load page");
355              }
356  
357              $rd $sth->fetch(\PDO::FETCH_ASSOC);
358  
359              $this->pageDefinitionId $rd["page_definition_id"];
360  
361              $this->load(true);
362  
363              $sth $this->context->connection->prepare(
364                  "SELECT page_definition_id FROM page WHERE instance_id = :instId
365                  AND (has_staging+version) = 0 AND parent_id = :docPid");
366  
367              $this->context->connection->bindInstance($sth);
368              $sth->bindValue(":docPid"$parentId, \PDO::PARAM_INT);
369  
370              $sth->execute();
371  
372              $res = array();
373              while ($rd $sth->fetch(\PDO::FETCH_ASSOC)) {
374                  $k intval($rd["page_definition_id"]);
375                  if (!isset($res[$k])) {
376                      $res[$k] = 1;
377                  } else {
378                      $res[$k]++;
379                  }
380              }
381  
382              foreach ($res as $used => $count) {
383                  if (isset($this->hints[$used]->maxNoOfChildren)) {
384                      $this->hints[$used]->maxNoOfChildren -= $count;
385                      if ($this->hints[$used]->maxNoOfChildren 0) {
386                          $this->hints[$used]->maxNoOfChildren 0;
387                      }
388                  }
389              }
390  
391          } catch(\PDOException $e) {
392              throw new \Scrivo\ResourceException($e);
393          }
394      }
395  
396      /**
397       * Update a set of page definition hints. Note: It is assumed that you're
398       * updating a list of type PARENT_PAGE_DEFINITION_COUNT otherwise a
399       * \Scrivo\SystemException is raised.
400       */
401      public function update() {
402          try {
403  
404              if ($this->type != self::PARENT_PAGE_DEFINITION_COUNT) {
405                  throw new \Scrivo\SystemException("Only PageDefinitionHints of".
406                      " type PARENT_PAGE_DEFINITION_COUNT can be updated");
407              }
408  
409              $this->context->checkPermission(AccessController::WRITE_ACCESS);
410  
411              $sth $this->context->connection->prepare(
412                  "DELETE FROM page_definition_hints    WHERE instance_id = :instId
413                  AND page_definition_id = :templId");
414  
415              $this->context->connection->bindInstance($sth);
416              $sth->bindValue(
417                  ":templId"$this->pageDefinitionId, \PDO::PARAM_INT);
418  
419              $sth->execute();
420  
421              foreach ($this->hints as $k=>$hint) {
422                  if (!is_null($hint->maxNoOfChildren)) {
423  
424                      $sth $this->context->connection->prepare(
425                          "INSERT    INTO page_definition_hints (
426                              instance_id, parent_page_definition_id,
427                              page_definition_id, max_no_of_children
428                          ) VALUES (
429                              :instId, :templPid,
430                              :templId, :maxChld
431                          )"
432                      );
433  
434                      $this->context->connection->bindInstance($sth);
435                      $sth->bindValue(":templPid"$k, \PDO::PARAM_INT);
436                      $sth->bindValue(
437                          ":templId"$this->pageDefinitionId, \PDO::PARAM_INT);
438                      $sth->bindValue(
439                          ":maxChld"$hint->maxNoOfChildren, \PDO::PARAM_INT);
440  
441                      $sth->execute();
442                  }
443              }
444  
445          } catch(\PDOException $e) {
446              throw new \Scrivo\ResourceException($e);
447          }
448      }
449  }
450  

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