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