PageRenderTime 287ms CodeModel.GetById 47ms RepoModel.GetById 0ms app.codeStats 0ms

/bedita-app/controllers/frontend_controller.php

http://bedita.googlecode.com/
PHP | 2522 lines | 1710 code | 223 blank | 589 comment | 348 complexity | 1370bca25b21f13322c9b2e00f70b1a1 MD5 | raw file
Possible License(s): AGPL-1.0, AGPL-3.0, LGPL-3.0, LGPL-2.1, GPL-3.0
  1. <?php
  2. /*-----8<--------------------------------------------------------------------
  3. *
  4. * BEdita - a semantic content management framework
  5. *
  6. * Copyright 2008 ChannelWeb Srl, Chialab Srl
  7. *
  8. * This file is part of BEdita: you can redistribute it and/or modify
  9. * it under the terms of the GNU Lesser General Public License as published
  10. * by the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. * BEdita is distributed WITHOUT ANY WARRANTY; without even the implied
  13. * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14. * See the GNU Lesser General Public License for more details.
  15. * You should have received a copy of the GNU Lesser General Public License
  16. * version 3 along with BEdita (see LICENSE.LGPL).
  17. * If not, see <http://gnu.org/licenses/lgpl-3.0.html>.
  18. *
  19. *------------------------------------------------------------------->8-----
  20. */
  21. /**
  22. * Frontend base class (Frontend API)
  23. *
  24. *
  25. * @version $Revision: 3921 $
  26. * @modifiedby $LastChangedBy: bato $
  27. * @lastmodified $LastChangedDate: 2012-09-03 16:14:14 +0200 (Mon, 03 Sep 2012) $
  28. *
  29. * $Id: frontend_controller.php 3921 2012-09-03 14:14:14Z bato $
  30. */
  31. if(defined('BEDITA_CORE_PATH')) {
  32. require_once (BEDITA_CORE_PATH . DS . 'bedita_exception.php');
  33. }
  34. abstract class FrontendController extends AppController {
  35. /**
  36. * object status used to filter
  37. *
  38. * @var array
  39. */
  40. private $status = array('on');
  41. /**
  42. * define which publication date has to be checked
  43. * "start" = true to check Content.start_date <= now
  44. * "end" = true to check Content.end_date >= now
  45. *
  46. * @var array
  47. */
  48. protected $checkPubDate = array("start" => true, "end" => true);
  49. /**
  50. * true to load objects in base level mode (BEObject without relations and LangText model are loaded)
  51. *
  52. * @var boolean
  53. */
  54. protected $baseLevel = false;
  55. /**
  56. * default options used to find sections' children
  57. * "showAllContents" => true to get all sections' children when a content is selected
  58. * false to get only content selected
  59. * "itemsByType" => true to divide children by type (i.e. Document, Event,....)
  60. * false to put all content type children in 'childContents' array and section type children in 'sectionChilds'
  61. * "childrenParams" => array to define special filters ('filter' array) and pagination options ("order", "dir", "dim", "page")
  62. * detail level ("detailed" => true, default false used only if "showAllContents" => true)
  63. *
  64. * @var array
  65. */
  66. protected $sectionOptions = array("showAllContents" => true, "itemsByType" => false, "childrenParams" => array());
  67. /**
  68. * set the XML format to display, possible values "tags", "attributes"
  69. *
  70. * @var string
  71. */
  72. protected $xmlFormat = "attributes";
  73. /**
  74. * current publication
  75. *
  76. * @var string
  77. */
  78. protected $publication = "";
  79. /**
  80. * default defined in captcha component
  81. *
  82. * @var array
  83. */
  84. protected $captchaOptions = array();
  85. /**
  86. * annotation options
  87. * object type => find options (filter and pagination)
  88. *
  89. * @var array
  90. */
  91. protected $annotationOptions = array("comment" => array());
  92. /**
  93. * BE obj internal cache
  94. * id => array(...)
  95. *
  96. * @var array
  97. */
  98. protected $objectCache = array();
  99. /**
  100. * tag and category options
  101. *
  102. * @var array
  103. */
  104. protected $tagOptions = array();
  105. /**
  106. * search options, attribute used on search
  107. *
  108. * @var array
  109. */
  110. protected $searchOptions = array("order" => false, "dir" => 1, "dim" => 50, "page" => 1, "filter" => false);
  111. /**
  112. * user logged in or not
  113. *
  114. * @var bool
  115. */
  116. protected $logged = false;
  117. /**
  118. * path to redirect after login action
  119. *
  120. * @var string
  121. */
  122. protected $loginRedirect = "/";
  123. /**
  124. * path to redirect after logout action
  125. *
  126. * @var string
  127. */
  128. protected $logoutRedirect = "/";
  129. /**
  130. * if it's true show unauthorized objects for user in list setting "authorized" => false in object array
  131. * else the unauthorized objects aren't in list (default)
  132. *
  133. * main objects requested are always blocked if user is not authorized to see them
  134. *
  135. * @var bool
  136. */
  137. protected $showUnauthorized = false;
  138. const UNLOGGED = "unlogged";
  139. const UNAUTHORIZED = "unauthorized";
  140. /**
  141. * set FrontendController::logged private attribute
  142. *
  143. * check if there's an active session and try to login if user not logged
  144. * - if "authorizedGroups" array defined in frontend.ini.php, user has to be in one of those groups
  145. * - if "staging" is defined only backend authorized groups are permitted
  146. * - otherwise any group is accepted
  147. *
  148. * @return mixed
  149. */
  150. protected function checkLogin() {
  151. if ($this->skipCheck) {
  152. return;
  153. }
  154. if(!$this->BeAuth->isLogged()) {
  155. if(Configure::read("staging") === true) {
  156. $frontendGroupsCanLogin = array(); // only backend authorized groups
  157. } else {
  158. // frontend only authorized groups (default empty)
  159. $confGroups = Configure::read("authorizedGroups");
  160. // which groups? authorized groups if defined, or any group
  161. $frontendGroupsCanLogin = (!empty($confGroups))? $confGroups :
  162. ClassRegistry::init("Group")->getList(array("backend_auth" => 0));
  163. }
  164. // try to login user if POST data are corrected
  165. if (!empty($this->params["form"]["login"])) {
  166. $userid = (isset($this->params["form"]["login"]["userid"])) ? $this->params["form"]["login"]["userid"] : "" ;
  167. $password = (isset($this->params["form"]["login"]["passwd"])) ? $this->params["form"]["login"]["passwd"] : "" ;
  168. if(!$this->BeAuth->login($userid, $password, null, $frontendGroupsCanLogin)) {
  169. //$this->loginEvent('warn', $userid, "login not authorized");
  170. $this->userErrorMessage(__("Wrong username/password or session expired", true));
  171. $this->logged = false;
  172. } else {
  173. $this->eventInfo("FRONTEND logged in publication");
  174. }
  175. $redirect = (!empty($this->params["form"]["backURL"]))? $this->params["form"]["backURL"] : $this->loginRedirect;
  176. $this->redirect($redirect);
  177. return true;
  178. }
  179. $this->logged = false;
  180. } else {
  181. $this->logged = true;
  182. }
  183. /*
  184. * if user is unlogged and it's a staging site OR
  185. * if user hasn't permissions to access at the publication
  186. * throws exception
  187. */
  188. if ( (!$this->logged && Configure::read("staging") === true) || ((empty($this->params["pass"][0]) || $this->params["pass"][0] != "logout") && !$this->publication["authorized"])) {
  189. $errorType = (!$this->logged)? self::UNLOGGED : self::UNAUTHORIZED;
  190. $this->accessDenied($errorType);
  191. }
  192. }
  193. /**
  194. * convienience method to do operations before login check
  195. * for example you could set FrontendController::skipCheck to true avoiding user session check
  196. */
  197. protected function beforeCheckLogin() {}
  198. /**
  199. * show login form or redirect if user is already logged
  200. *
  201. * @param string $backName nickname or id of section to go after login
  202. */
  203. protected function login($backName=null) {
  204. $urlToGo = (!empty($backName))? Router::url('/'. $backName, true) : $this->loginRedirect;
  205. if ($this->isLogged()) {
  206. $this->redirect($urlToGo);
  207. }
  208. $this->accessDenied(self::UNLOGGED);
  209. }
  210. /**
  211. * perform logout operation
  212. *
  213. * @param boolean $autoRedirect
  214. */
  215. protected function logout($autoRedirect=true) {
  216. $this->BeAuth->logout();
  217. $this->eventInfo("FRONTEND logged out: publication " . $this->publication["title"]);
  218. if ($autoRedirect) {
  219. $this->redirect($this->logoutRedirect);
  220. }
  221. }
  222. /**
  223. * manage access denied. If you want another behavior override it in pages_controller
  224. *
  225. * user unlogged: render login view (if user doesn't arrive from login page set info message)
  226. * user unauthorized to access that item: render unauthorized view (set error message)
  227. *
  228. * for other custom type will try to render a view with $type name
  229. * (i.e. $type="access_denied" render views/pages/access_denied.[tpl|ctp] template)
  230. *
  231. * @param string $type, which type of access denied
  232. * @throws BeditaFrontAccessException
  233. */
  234. protected function accessDenied($type) {
  235. if ($type == self::UNLOGGED && !strstr($this->here,"/login")) {
  236. $message = __("You have to be logged to access that item",true);
  237. $this->userInfoMessage($message);
  238. } elseif ($type == self::UNAUTHORIZED) {
  239. $message = __("You aren't authorized to access that item",true);
  240. $this->userErrorMessage($message);
  241. }
  242. throw new BeditaFrontAccessException(null, array("errorType" => $type));
  243. }
  244. /**
  245. * get private logged var
  246. *
  247. * @return boolean
  248. */
  249. protected function isLogged() {
  250. return $this->logged;
  251. }
  252. /**
  253. * called before action to initialize
  254. * $uses & $components array don't work... (abstract class ??)
  255. *
  256. * @throws BeditaPublicationException
  257. * @see bedita-app/AppController#initAttributes()
  258. */
  259. final protected function initAttributes() {
  260. if(!isset($this->BEObject)) {
  261. $this->BEObject = $this->loadModelByType('BEObject');
  262. }
  263. if(!isset($this->Section)) {
  264. $this->Section = $this->loadModelByType('Section');
  265. }
  266. if(!isset($this->Stream)) {
  267. $this->Stream = $this->loadModelByType('Stream');
  268. }
  269. if(!isset($this->BeLangText)) {
  270. App::import('Component', 'BeLangText');
  271. $this->BeLangText = new BeLangTextComponent();
  272. }
  273. if(!isset($this->Tree)) {
  274. $this->Tree = $this->loadModelByType('Tree');
  275. }
  276. $conf = Configure::getInstance() ;
  277. if (!empty($conf->draft))
  278. $this->status[] = "draft";
  279. // check publication status
  280. $pubStatus = $this->BEObject->field("status", array("id" => Configure::read("frontendAreaId")));
  281. if ($pubStatus != "on") {
  282. $statusSaved = $this->status;
  283. $this->status = array('on', 'off', 'draft');
  284. $this->publication = $this->loadObj(Configure::read("frontendAreaId"), false);
  285. $this->status = $statusSaved;
  286. $this->set('publication', $this->publication);
  287. if (Configure::read("draft") == false or ($pubStatus == "off")) {
  288. throw new BeditaPublicationException("Publication not ON", array("layout" => $pubStatus));
  289. }
  290. }
  291. $this->publication = $this->loadObj(Configure::read("frontendAreaId"),false);
  292. // set publication data for template
  293. $this->set('publication', $this->publication);
  294. // set filterPublicationDate
  295. $filterPubDate = Configure::read("filterPublicationDate");
  296. if (isset($filterPubDate)) {
  297. if (is_array($filterPubDate)) {
  298. $this->checkPubDate = $filterPubDate;
  299. } elseif ($filterPubDate === true) {
  300. $this->checkPubDate = array("start" => true, "end" => true);
  301. } elseif ($filterPubDate === false) {
  302. $this->checkPubDate = array("start" => false, "end" => false);
  303. }
  304. }
  305. $this->historyItem["area_id"] = $this->publication["id"];
  306. $this->beforeCheckLogin();
  307. }
  308. /**
  309. * override AppController::setupLocale. Used setup specific locale
  310. *
  311. * @see bedita-app/AppController#setupLocale()
  312. */
  313. protected function setupLocale() {
  314. $this->currLang = $this->Session->read('Config.language');
  315. $conf = Configure::getInstance();
  316. if($this->currLang === null || empty($this->currLang)) {
  317. if (isset($conf->cookieName["langSelect"])) {
  318. $lang = $this->Cookie->read($conf->cookieName["langSelect"]);
  319. }
  320. if(!empty($lang) && array_key_exists($lang, $conf->frontendLangs)) {
  321. $this->currLang = $lang;
  322. } else {
  323. // HTTP autodetect
  324. $l10n = new L10n();
  325. $l10n->get();
  326. $lang = $l10n->lang;
  327. if(!empty($lang)) {
  328. if(array_key_exists($lang, $conf->frontendLangs)) {
  329. $this->currLang = $lang;
  330. } else if (!empty($conf->frontendLangsMap[$lang])) {
  331. $lang = $conf->frontendLangsMap[$lang];
  332. if(array_key_exists($lang, $conf->frontendLangs)) {
  333. $this->currLang = $lang;
  334. }
  335. }
  336. }
  337. if(empty($this->currLang)) {
  338. $this->currLang = $conf->frontendLang;
  339. }
  340. }
  341. $this->Session->write('Config.language', $this->currLang);
  342. Configure::write('Config.language', $this->currLang);
  343. }
  344. $this->set('currLang', $this->currLang);
  345. if(isset( $conf->locales[$this->currLang])) {
  346. $this->currLocale = setlocale(LC_ALL, $conf->locales[$this->currLang]);
  347. } else {
  348. $this->currLocale = setlocale(LC_ALL, '');
  349. }
  350. $this->set('currLocale', $this->currLocale);
  351. if(isset( $conf->datePatternLocale[$this->currLang])) {
  352. Configure::write('datePattern', $conf->datePatternLocale[$this->currLang]);
  353. }
  354. if(isset( $conf->dateTimePatternLocale[$this->currLang])) {
  355. Configure::write('dateTimePattern', $conf->dateTimePatternLocale[$this->currLang]);
  356. }
  357. $dateFormatValidation = $conf->datePattern;
  358. $dateFormatValidation = preg_replace(array("/%d/", "/%m/", "/%Y/"), array("dd","mm","yyyy"), $dateFormatValidation);
  359. Configure::write('dateFormatValidation', $dateFormatValidation);
  360. }
  361. /**
  362. * change language
  363. *
  364. * @param string $lang
  365. * @param string $forward redirect action after changing language. If it's null redirect to refere
  366. * @return string
  367. * @throws BeditaException
  368. */
  369. public function lang($lang, $forward = null) {
  370. if (empty($lang)) {
  371. throw new BeditaException("No lang selected");
  372. }
  373. $conf = Configure::getInstance();
  374. if (!array_key_exists($lang, $conf->frontendLangs)) {
  375. throw new BeditaException("wrong lang selected: ".$lang);
  376. }
  377. $this->Session->write('Config.language', $lang);
  378. $this->Cookie->write($conf->cookieName["langSelect"], $lang, false, '+350 day');
  379. $this->currLang = $lang;
  380. if(!empty($forward)) {
  381. if (substr($forward, 0, 5) != "http:") {
  382. if (strpos("/", $forward) != 1)
  383. $forward = "/" . $forward;
  384. if (!empty($this->params["pass"][2])) {
  385. $forward .= "/" . implode("/", array_slice($this->params["pass"],2));
  386. }
  387. }
  388. $this->redirect($forward);
  389. } else {
  390. $this->redirect($this->referer());
  391. }
  392. }
  393. /**
  394. * check if current date is compatible with required pubblication dates (start/end date)
  395. *
  396. * @param array $obj
  397. * @return boolean, true if content may be published, false otherwise
  398. */
  399. protected function checkPubblicationDate(array $obj) {
  400. $currDate = strftime("%Y-%m-%d");
  401. if(isset($obj["start_date"]) && $this->checkPubDate["start"]) {
  402. if(strncmp($currDate, $obj["start_date"], 10) < 0)
  403. return false;
  404. }
  405. if(isset($obj["end_date"]) && $this->checkPubDate["end"]) {
  406. if(strncmp($currDate, $obj["end_date"], 10) > 0)
  407. return false;
  408. }
  409. return true;
  410. }
  411. /**
  412. * handle Exceptions
  413. *
  414. * @param Exception $ex
  415. * @return AppError
  416. */
  417. public static function handleExceptions(Exception $ex) {
  418. if ($ex instanceof BeditaPublicationException) {
  419. $currentController = AppController::currentController();
  420. echo $currentController->render(false, $ex->getLayout());
  421. } elseif ($ex instanceof BeditaFrontAccessException) {
  422. $errorType = $ex->getErrorType();
  423. $params = array(
  424. 'details' => $ex->getDetails(),
  425. 'msg' => $ex->getMessage(),
  426. 'result' => $ex->result,
  427. 'errorType' => $ex->getErrorType()
  428. );
  429. include_once (APP . 'app_error.php');
  430. return new AppError('handleExceptionFrontAccess', $params, $ex->errorTrace());
  431. } elseif ($ex instanceof BeditaRuntimeException) {
  432. include_once (APP . 'app_error.php');
  433. return new AppError('handleExceptionRuntime',
  434. array('details' => $ex->getDetails(), 'msg' => $ex->getMessage(),
  435. 'result' => $ex->result), $ex->errorTrace());
  436. } elseif ($ex instanceof SmartyException) {
  437. include_once (APP . 'app_error.php');
  438. $trace = $ex->getFile()." - line: ". $ex->getLine()." \nTrace:\n". $ex->getTraceAsString();
  439. return new AppError('handleExceptionRuntime', array('msg' => $ex->getMessage(), 'details' => ''), $trace);
  440. } else {
  441. if($ex instanceof BeditaException) {
  442. $errTrace = $ex->errorTrace();
  443. $details = $ex->getDetails();
  444. $result = $ex->result;
  445. } else {
  446. $errTrace = get_class($ex)." - ". $ex->getMessage().
  447. "\nFile: ".$ex->getFile()." - line: ".$ex->getLine()."\nTrace:\n".$ex->getTraceAsString();
  448. $details = "";
  449. $result = "";
  450. }
  451. include_once (APP . 'app_error.php');
  452. return new AppError('handleExceptionFrontend',
  453. array('details' => $details, 'msg' => $ex->getMessage(),
  454. 'result' => $result), $errTrace);
  455. }
  456. }
  457. /**
  458. * handle error, logging if log level is 'debug'
  459. *
  460. * @see bedita-app/AppController#handleError()
  461. */
  462. public function handleError($eventMsg, $userMsg, $errTrace) {
  463. if(Configure::read('debug') > 0) {
  464. $this->log($errTrace);
  465. }
  466. }
  467. /**
  468. * Get tree starting from specified section or area
  469. *
  470. * @param string|int $parentName parent nickname or id
  471. * @param boolean $loadContents if it's true load all contents too. Default false
  472. * @param array $exclude_nicknames list exclude sections
  473. * @param int $depth tree's depth level (default=null => all levels)
  474. * @return array
  475. * */
  476. protected function loadSectionsTree($parentName, $loadContents = false, array $exclude_nicknames = array(), $depth = null, $flatMode = false) {
  477. $conf = Configure::getInstance();
  478. $parent_id = is_numeric($parentName) ? $parentName: $this->BEObject->getIdFromNickname($parentName);
  479. $result = array();
  480. $filter["object_type_id"] = $conf->objectTypes['section']["id"];
  481. if (empty($parent_id)) {
  482. throw new BeditaException(__("Error loading sections tree. Missing parent" . ": " . $parentName, true));
  483. }
  484. $sections = $this->BeTree->getChildren($parent_id, $this->status, $filter, "priority");
  485. foreach ($sections['items'] as $s) {
  486. if(!empty($exclude_nicknames) && in_array($s['nickname'], $exclude_nicknames)) {
  487. continue;
  488. }
  489. $sectionObject = $this->loadObj($s['id']);
  490. if ($sectionObject !== self::UNLOGGED && $sectionObject !== self::UNAUTHORIZED) {
  491. $resultSections = array();
  492. $resultObjects = array();
  493. $this->setCanonicalPath($sectionObject);
  494. if($loadContents) {
  495. $option = array("filter" => array("object_type_id" => Configure::read("objectTypes.leafs.id")));
  496. $objs = $this->loadSectionObjects($s['id'], $option);
  497. $resultObjects = (!$this->sectionOptions["itemsByType"] && !empty($objs["childContents"]))? $objs["childContents"] : $objs;
  498. }
  499. if ($depth === null || $depth > 1) {
  500. $innerDepth = ($depth === null) ? null : $depth-1;
  501. $resultSections = $this->loadSectionsTree($s['id'], $loadContents, $exclude_nicknames, $innerDepth, $flatMode);
  502. }
  503. if(!$flatMode) {
  504. if(!empty($resultObjects)) {
  505. $sectionObject['objects'] = $resultObjects;
  506. }
  507. if(!empty($resultSections)) {
  508. $sectionObject['sections'] = $resultSections;
  509. }
  510. $result[] = $sectionObject;
  511. } else {
  512. $result[] = $sectionObject;
  513. if(!empty($resultSections)) {
  514. $result = array_merge($result, $resultSections);
  515. }
  516. if(!empty($resultObjects)) {
  517. $result = array_merge($result, $resultObjects);
  518. }
  519. }
  520. }
  521. }
  522. return $result;
  523. }
  524. /**
  525. * Set canonical path in object data array, check parent authorization
  526. * - $obj["canonicalPath"] will contain new caclulated canonical path
  527. * - update $this->objectCache
  528. * - setup $obj["parentAuthorized"]
  529. * - setup $obj["pathSection"] for sections
  530. *
  531. * @param array $obj, containing at least "id" and "nickname"
  532. */
  533. protected function setCanonicalPath(array &$obj) {
  534. $objectId = $obj["id"];
  535. if(isset($this->objectCache[$objectId]["canonicalPath"]) &&
  536. isset($this->objectCache[$objectId]["parentAuthorized"])) {
  537. $obj["canonicalPath"] = $this->objectCache[$objectId]["canonicalPath"];
  538. $obj["parentAuthorized"] = $this->objectCache[$objectId]["parentAuthorized"];
  539. return;
  540. }
  541. if($obj["object_type_id"] == Configure::read("objectTypes.area.id")) {
  542. $obj["canonicalPath"] = "/";
  543. $obj["parentAuthorized"] = $obj["authorized"];
  544. return;
  545. }
  546. $pathSection = $this->getPath($objectId);
  547. if($obj["object_type_id"] == Configure::read("objectTypes.section.id")) {
  548. $obj["pathSection"] = $pathSection;
  549. }
  550. $parentAuthorized = true;
  551. foreach ($pathSection as $ps) {
  552. if ($parentAuthorized && !empty($ps["authorized"]) && !$ps["authorized"]) {
  553. $parentAuthorized = false;
  554. }
  555. }
  556. $obj["parentAuthorized"] = $parentAuthorized;
  557. $canPath = "";
  558. if(!empty($pathSection)) {
  559. $parentSec = end($pathSection);
  560. $canPath = $parentSec["canonicalPath"];
  561. }
  562. $canPath .= (($canPath === "/") ? "" : "/");
  563. $menu = true;
  564. if($obj["object_type_id"] == Configure::read("objectTypes.section.id")) {
  565. $menu = !isset($obj["menu"]) ? true : (($obj["menu"] === '0') ? false : true);
  566. }
  567. if($menu) {
  568. $canPath .= $obj["nickname"];
  569. }
  570. $obj["canonicalPath"] = $canPath;
  571. if(isset($this->objectCache[$objectId])) {
  572. $this->objectCache[$objectId]["canonicalPath"] = $canPath;
  573. $this->objectCache[$objectId]["parentAuthorized"] = $parentAuthorized;
  574. }
  575. return;
  576. }
  577. /**
  578. * Get sections levels
  579. *
  580. * Find all ancestors from secName and build an array of levels
  581. * Each key in array returned is a level:
  582. * 0 is the first level
  583. * 1 is the second level
  584. * etc...
  585. *
  586. * set selected = true in a section if it's an ancestor (parent) of $secName
  587. *
  588. * @param string $secName nickname or section id
  589. * @param boolean $loadContents true meaning it loads all contents of each section
  590. * @param array $exclude_nicknames list exclude sections
  591. *
  592. * @return array of level selected
  593. */
  594. protected function loadSectionsLevels($secName, $loadContents=false, array $exclude_nicknames=null) {
  595. $conf = Configure::getInstance();
  596. $result = array();
  597. $section_id = is_numeric($secName) ? $secName : $this->BEObject->getIdFromNickname($secName);
  598. $path = $this->Tree->field("object_path", array("id" => $section_id));
  599. $parents = explode("/", trim($path,"/"));
  600. $level = 0;
  601. $filter["object_type_id"] = $conf->objectTypes['section']["id"];
  602. foreach ($parents as $p_id) {
  603. $sections = $this->BeTree->getChildren($p_id, $this->status, $filter, "priority");
  604. foreach ($sections["items"] as $s) {
  605. if(!empty($exclude_nicknames) && in_array($s['nickname'], $exclude_nicknames))
  606. continue ;
  607. $sectionObject = $this->loadObj($s['id']);
  608. if ($sectionObject !== self::UNLOGGED && $sectionObject !== self::UNAUTHORIZED) {
  609. if (in_array($s["id"], $parents)) {
  610. $sectionObject["selected"] = true;
  611. }
  612. if($loadContents) {
  613. $option = array("filter" => array("object_type_id" => Configure::read("objectTypes.leafs.id")));
  614. $objs = $this->loadSectionObjects($s['id'], $option);
  615. $sectionObject['objects'] = (!$this->sectionOptions["itemsByType"] && !empty($objs["childContents"]))? $objs["childContents"] : $objs;
  616. }
  617. $result[$level][] = $sectionObject;
  618. }
  619. }
  620. $level++;
  621. }
  622. return $result;
  623. }
  624. /**
  625. * load all publications
  626. *
  627. * @param string $tplVar, var name for template.
  628. * If not defined result will be set to "publicationsList" var
  629. *
  630. */
  631. protected function loadPublications($tplVar=null) {
  632. $publications = array();
  633. $filter = array("object_type_id" => Configure::read("objectTypes.area.id"));
  634. $res = $this->BEObject->findObjects(null, null, $this->status, $filter);
  635. if (!empty($res["items"])) {
  636. foreach ($res["items"] as $pub) {
  637. $obj = $this->loadObj($pub["id"]);
  638. if ($obj !== self::UNLOGGED && $obj !== self::UNAUTHORIZED)
  639. $publications[] = $obj;
  640. }
  641. }
  642. $tplVar = (!empty($tplVar))? $tplVar : "publicationsList";
  643. $this->set($tplVar, $publications);
  644. }
  645. /**
  646. * find first active section and load it as home page section
  647. * if any section was found load publication as home page section
  648. */
  649. public function homePage() {
  650. $filter = array("object_type_id" => Configure::read("objectTypes.section.id"));
  651. $child = $this->BeTree->getChildren($this->publication["id"], $this->getStatus(), $filter, null, true, 1, 1);
  652. $homePageSectionId = (empty($child["items"]))? $this->publication["id"] : $child["items"][0]["id"];
  653. $this->action = 'section';
  654. $this->section($homePageSectionId);
  655. }
  656. /**
  657. * prepare an XML containing sitemap specification
  658. * view in bedita-app/views/pages/sitemap_xml.tpl
  659. */
  660. public function sitemapXml() {
  661. $this->sitemap(true);
  662. $this->layout = null;
  663. $this->view = "Smarty";
  664. header("Content-type: text/xml; charset=utf-8");
  665. }
  666. /**
  667. * build array for sitemap
  668. *
  669. * @param boolean $xml_out
  670. * @return array
  671. */
  672. public function sitemap($xml_out = false) {
  673. $conf = Configure::getInstance() ;
  674. $extract_all = (!empty($conf->sitemapAllContent)) ? $conf->sitemapAllContent : false;
  675. $itemsByType = $this->sectionOptions["itemsByType"];
  676. $this->sectionOptions["itemsByType"] = false;
  677. $flatMode = $xml_out? true : false;
  678. $sectionsTree = $this->loadSectionsTree($conf->frontendAreaId,$extract_all, null, 10000, $flatMode) ;
  679. $this->sectionOptions["itemsByType"] = $itemsByType;
  680. if($xml_out) {
  681. $pubMap = array();
  682. $pubMap['loc'] = $this->publication["public_url"];
  683. $pubMap['lastmod'] = !empty($this->publication['last_modified']) ?
  684. substr($this->publication["last_modified"], 0, 10) : substr($this->publication["modified"], 0, 10);
  685. $pubMap['priority'] = !empty($this->publication['map_priority']) ?
  686. $this->publication['map_priority'] : null;
  687. $pubMap['changefreq'] = !empty($this->publication['map_changefreq']) ?
  688. $this->publication['map_changefreq'] : null;
  689. $urlset[] = $pubMap;
  690. if($extract_all) {
  691. $option = array("filter" => array("object_type_id" => Configure::read("objectTypes.leafs.id")),
  692. "sectionPath" => "");
  693. $objs = $this->loadSectionObjects($conf->frontendAreaId, $option);
  694. $resultObjects = (!$this->sectionOptions["itemsByType"] && !empty($objs["childContents"]))? $objs["childContents"] : $objs;
  695. if(!empty($resultObjects)) {
  696. $sectionsTree = array_merge($resultObjects, $sectionsTree);
  697. }
  698. }
  699. $i = count($urlset);
  700. $sectionModel = ClassRegistry::init("Section");
  701. foreach($sectionsTree as $v) {
  702. $urlset[$i] = array();
  703. $urlset[$i]['loc'] = $this->publication["public_url"]. $v['canonicalPath'];
  704. if($v['object_type_id'] == $conf->objectTypes["section"]["id"]) {
  705. $secFields = $sectionModel->find("first",
  706. array("conditions" => array("id"=>$v["id"]), "contain" => array()));
  707. if(!empty($secFields['last_modified'])) {
  708. $urlset[$i]['lastmod'] = substr($secFields['last_modified'], 0, 10);
  709. } else {
  710. $urlset[$i]['lastmod'] = substr($v["modified"], 0, 10);
  711. }
  712. if(!empty($secFields['map_priority'])) {
  713. $urlset[$i]['priority'] = $secFields['map_priority'];
  714. }
  715. if(!empty($secFields['map_changefreq'])) {
  716. $urlset[$i]['changefreq'] = $secFields['map_changefreq'];
  717. }
  718. } else {
  719. $urlset[$i]['lastmod'] = substr($v["modified"], 0, 10);
  720. }
  721. $urlset[$i]['menu'] = $v["menu"];
  722. $i++;
  723. }
  724. $this->set('urlset',$urlset);
  725. } else {
  726. if(!in_array('BeTree', $this->helpers)) {
  727. $this->helpers[] = 'BeTree';
  728. }
  729. }
  730. $this->set('sections_tree',$sectionsTree);
  731. }
  732. /**
  733. * Publish RSS feed with contents inside section $sectionName
  734. * Use callback controller methods (if defined):
  735. * - $sectionName."RssChannel" to fetch channel data
  736. * - $sectionName."RssItems" to fetch rss items
  737. *
  738. * If callbacks methods are not defined (default) load section and object data
  739. * and build rss data with defaults
  740. *
  741. * @param string $sectionName, section's nickname/unique name
  742. */
  743. public function rss($sectionName) {
  744. $channel = array();
  745. $s = array();
  746. // fetch channel data, use $sectionName."RssChannel" method if exists
  747. $methodName = $sectionName . "RssChannel";
  748. if (method_exists($this, $methodName)) {
  749. $channel = $this->{$methodName}();
  750. } else {
  751. // build channel data from section
  752. $s = $this->loadObjByNick($sectionName);
  753. if ($s === self::UNLOGGED || $s === self::UNAUTHORIZED) {
  754. $this->accessDenied($s);
  755. }
  756. if($s['syndicate'] === "off") {
  757. throw new BeditaException(__("Content not found", true));
  758. }
  759. $this->setCanonicalPath($s);
  760. App::import("Sanitize");
  761. $title = Sanitize::html($this->publication["public_name"] . " - " . $s['title']);
  762. $channel = array( 'title' => $title,
  763. 'link' => $s["canonicalPath"],
  764. 'description' => Sanitize::html($s['description']),
  765. 'language' => $s['lang'],
  766. );
  767. }
  768. $this->set('channelData', $channel);
  769. // fetch rss items, use
  770. $rssItems = array();
  771. $methodName = $sectionName . "RssItems";
  772. // check before filter method
  773. if (method_exists($this, $methodName)) {
  774. $rssItems = $this->{$methodName}();
  775. } else {
  776. if(empty($s)) { // if channel data has been redefined
  777. $s = $this->loadObjByNick($sectionName);
  778. $this->setCanonicalPath($s);
  779. }
  780. $items = $this->BeTree->getChildren($s['id'], $this->status, false, "priority", ($s['priority_order']=="asc"), 1, 40);
  781. if(!empty($items) && !empty($items['items'])) {
  782. foreach($items['items'] as $index => $item) {
  783. $obj = $this->loadObj($item['id']);
  784. if ($obj !== self::UNLOGGED && $obj !== self::UNAUTHORIZED) {
  785. $rssItems[] = $this->buildRssItem($obj, $s['canonicalPath']);
  786. }
  787. }
  788. }
  789. }
  790. $this->set('items', $rssItems);
  791. $this->view = 'View';
  792. // add RSS helper if not present
  793. if(!in_array('Rss', $this->helpers)) {
  794. $this->helpers[] = 'Rss';
  795. }
  796. $this->layout = NULL;
  797. header("Content-type: text/xml; charset=utf-8");
  798. }
  799. /**
  800. * Build a single RSS item from a BE object array
  801. * If section "canonicalPath" is set, links are created with it
  802. * If not: use object canonicalPath if present, otherwise object unique name (nickname)
  803. *
  804. * @param array $obj
  805. * @param string $canonicalPath
  806. * @return array
  807. */
  808. protected function buildRssItem(array &$obj, $canonicalPath = null) {
  809. $description = $obj['description'];
  810. if (!empty($obj['abstract'])) {
  811. $description .= "<hr/>" . $obj['abstract'];
  812. }
  813. if (!empty($obj['body'])) {
  814. $description .= "<hr/>" . $obj['body'];
  815. }
  816. $link = !empty($canonicalPath) ? ($canonicalPath ."/". $obj['nickname']) :
  817. (!empty($obj['canonicalPath']) ? $obj['canonicalPath'] : $obj['nickname']);
  818. return array( 'title' => $obj['title'], 'description' => $description,
  819. 'pubDate' => $obj['created'], 'link' => $link);
  820. }
  821. /**
  822. * output a kml defined by a section
  823. * @param string $sectionName
  824. */
  825. public function kml($sectionName) {
  826. $this->section($sectionName);
  827. $this->layout = 'ajax';
  828. header("Content-type: application/vnd.google-earth.kml+xml");
  829. header("Content-Disposition: attachment; filename=" . $sectionName . ".kml");
  830. }
  831. /**
  832. * output a georss atom representation of section
  833. * @param string $sectionName
  834. */
  835. public function georssatom($sectionName) {
  836. $this->section($sectionName);
  837. $this->layout = 'ajax';
  838. header("Content-type: application/atom+xml; charset=utf-8");
  839. }
  840. /**
  841. * output a georss representation of section
  842. * @param string $sectionName
  843. */
  844. public function georss($sectionName) {
  845. $gml = (!empty($this->params['named']['gml']));
  846. $this->section($sectionName);
  847. $s = $this->viewVars["section"];
  848. $channel = array( 'title' => $this->publication["public_name"] . " - " . $s['title'] ,
  849. 'link' => "/section/".$sectionName,
  850. 'description' => $s['description'],
  851. 'language' => $s['lang'],
  852. );
  853. $this->set('channelData', $channel);
  854. $rssItems = array();
  855. $items = $s['childContents'];
  856. if(!empty($items)) {
  857. foreach($items as $index => $obj) {
  858. $description = $obj['description'];
  859. $description .= (!empty($obj['abstract']) && !empty($description))? "<hr/>" . $obj['abstract'] : $obj['abstract'];
  860. $description .= (!empty($obj['body']) && !empty($description))? "<hr/>" . $obj['body'] : $obj['body'];
  861. if(!empty($obj['GeoTag'][0]['latitude']) && !empty($obj['GeoTag'][0]['longitude'])) {
  862. $position = $obj['GeoTag'][0]['latitude'] . ' ' . $obj['GeoTag'][0]['longitude'];
  863. $item = array('item' => array(
  864. 'title' => $obj['title'],
  865. 'description' => $description,
  866. 'pubDate' => $obj['created'],
  867. 'link' => $s['canonicalPath']."/".$obj['nickname']
  868. ));
  869. if($gml) { // geoRss GML
  870. $item['georss:where'] = array(
  871. 0 => array(
  872. 'gml:Point' => array(
  873. 0 => array(
  874. 'gml:pos' => $position
  875. )
  876. )
  877. )
  878. );
  879. } else { // geoRss simple
  880. $item['georss:point'] = $position;
  881. }
  882. $rssItems[] = $item;
  883. }
  884. }
  885. }
  886. $this->set('items', array($rssItems));
  887. $attrib = array(
  888. "version" => "2.0",
  889. "xmlns:georss" => "http://www.georss.org/georss",
  890. "xmlns:gml" => "http://www.opengis.net/gml"
  891. );
  892. $this->set('attrib',$attrib);
  893. $this->view = 'View';
  894. // add RSS helper if not present
  895. if(!in_array('Rss', $this->helpers)) {
  896. $this->helpers[] = 'Rss';
  897. }
  898. $this->layout = NULL;
  899. header("Content-type: application/text+xml; charset=utf-8");
  900. }
  901. /**
  902. * output a json object of returned array by section or content method
  903. * @param string $name
  904. * @return string|int $name, nickname or id
  905. */
  906. public function json($name) {
  907. $this->route($name);
  908. header("Content-Type: application/json");
  909. $this->view = 'View';
  910. $this->layout = null;
  911. $this->action = "json";
  912. $this->set("data", $this->viewVars["section"]);
  913. }
  914. /**
  915. * output an xml of returned array by section or content method
  916. *
  917. * passing a "format" named parameters in the url obtain an xml "attributes" format or an xml "tags" format
  918. * i.e. http://www.example.com/xml/nickname/format:tags output a tag style xml
  919. * default is defined by class attribute xmlFormat
  920. *
  921. * @param string|int $name, nickname or id
  922. */
  923. public function xml($name) {
  924. $this->route($name);
  925. $this->outputXML(array("section" => $this->viewVars["section"]));
  926. }
  927. /**
  928. * output an xml of returned array by loadObj/loadObjByNick method
  929. *
  930. * passing a "format" named parameters in the url obtain an xml "attributes" format or an xml "tags" format
  931. * i.e. http://www.example.com/xmlobject/nickname/format:tags output a tag style xml
  932. * default is defined by class attribute xmlFormat
  933. *
  934. * @param string|int $name, nickname or id
  935. */
  936. public function xmlobject($name) {
  937. $object = (is_numeric($name))? $this->loadObj($name) : $this->loadObjByNick($name);
  938. if ($object === self::UNLOGGED || $object === self::UNAUTHORIZED) {
  939. $this->accessDenied($object);
  940. }
  941. $this->outputXML(array("object" => $object));
  942. }
  943. /**
  944. * prepare to XML output
  945. *
  946. * @param array $data
  947. */
  948. private function outputXML($data) {
  949. $this->RequestHandler->setContent("xml", "text/xml");
  950. $this->RequestHandler->respondAs("xml", array("charset" => "utf-8"));
  951. $this->RequestHandler->renderAs($this, "xml");
  952. $availableFormat = array("attributes", "tags");
  953. if (!empty($this->passedArgs["format"]) && in_array($this->passedArgs["format"],$availableFormat)) {
  954. $options = array("format" => $this->passedArgs["format"]);
  955. } else {
  956. $options = array("format" => $this->xmlFormat);
  957. }
  958. $this->set("options", $options);
  959. $this->set("data", $data);
  960. $this->action = "xml";
  961. $this->view = 'View';
  962. }
  963. /**
  964. * Like loadObj using nickname
  965. *
  966. * @param string $obj_nick
  967. * @param boolean $blockAccess see FrontendController::loadObj()
  968. * @return array
  969. */
  970. protected function loadObjByNick($obj_nick, $blockAccess = true) {
  971. return $this->loadObj($this->BEObject->getIdFromNickname($obj_nick), $blockAccess);
  972. }
  973. /**
  974. * Like loadAndSetObj using nickname
  975. *
  976. * @param string $obj_nick
  977. * @param string $var_name view var name
  978. * @param boolean $blockAccess see FrontendController::loadObj()
  979. * @return array
  980. */
  981. protected function loadAndSetObjByNick($obj_nick, $var_name = null, $blockAccess = true) {
  982. return $this->loadAndSetObj($this->BEObject->getIdFromNickname($obj_nick) , $var_name, $blockAccess);
  983. }
  984. /**
  985. * Load bedita Object and set view var with $var_name or object type (e.g. "Document", "Event"..)
  986. * Returns object loaded
  987. * Throws Exception on errors
  988. *
  989. * @param int $obj_id
  990. * @param string $var_name view var name
  991. * @param boolean $blockAccess see FrontendController::loadObj()
  992. * @return array
  993. */
  994. protected function loadAndSetObj($obj_id, $var_name = null, $blockAccess = true) {
  995. $obj = $this->loadObj($obj_id, $blockAccess);
  996. if ($obj === self::UNLOGGED || $obj == self::UNAUTHORIZED) {
  997. return $obj;
  998. }
  999. $this->set((isset($var_name)? $var_name: $obj['object_type']),$obj);
  1000. return true;
  1001. }
  1002. /**
  1003. * set model bindings for BEdita object
  1004. *
  1005. * @param string $modelType model name of BE object
  1006. * @return array that contains:
  1007. * "bindings_used" => multidimensional array of bindings used,
  1008. * "bindings_list" => one dimensional array with the simple list of bindings ordered using a "natural order" algorithm
  1009. *
  1010. */
  1011. protected function setObjectBindings($modelType) {
  1012. if(!isset($this->{$modelType})) {
  1013. $this->{$modelType} = $this->loadModelByType($modelType);
  1014. }
  1015. if (!$this->baseLevel) {
  1016. $bindingsUsed = $this->modelBindings($this->{$modelType}, "frontend");
  1017. } else {
  1018. $bindingsUsed = array("BEObject" => array("LangText"));
  1019. if ($modelType == "Section") {
  1020. $bindingsUsed[] = "Tree";
  1021. }
  1022. $this->{$modelType}->contain($bindingsUsed);
  1023. }
  1024. $listOfBindings = BeLib::getInstance()->arrayValues($bindingsUsed, true);
  1025. natsort($listOfBindings);
  1026. return array("bindings_used" => $bindingsUsed, "bindings_list" => $listOfBindings);
  1027. }
  1028. /**
  1029. * Returns bedita Object
  1030. * Throws Exception on errors
  1031. *
  1032. * @param int $obj_id
  1033. * @param boolean $blockAccess
  1034. * if it's set a "frontend_access_without_block" permission on the object this param is ignored
  1035. * and the object returned (array) will have a key named "authorized" set to true or false
  1036. * depending on whether the user has permission to access at the object
  1037. * else if it's set a "frontend_access_with_block" permission on the object
  1038. * true => if user is unlogged return UNLOGGED constant
  1039. * if user is logged and he hasn't permission to access at the object return UNAUTHORIZED constant
  1040. * false => if user unlogged dosen't block the action and the object returned (array)
  1041. * will have a key named "authorized" set to false
  1042. * if user is logged but not authorized the object returned (array)
  1043. *
  1044. * note: if FrontendController::showUnauthorized is set to true and the user is logged
  1045. * then all unauthorized object will have set "authorized" to false regardless object permission
  1046. *
  1047. * @return array object detail
  1048. */
  1049. protected function loadObj($obj_id, $blockAccess=true) {
  1050. if($obj_id === null) {
  1051. throw new BeditaException(__("Content not found", true));
  1052. }
  1053. // use object cache
  1054. if(isset($this->objectCache[$obj_id])) {
  1055. $modelType = $this->objectCache[$obj_id]["object_type"];
  1056. $bindings = $this->setObjectBindings($modelType);
  1057. $bindingsDiff = array_diff($this->objectCache[$obj_id]["bindings"]["bindings_list"], $bindings["bindings_list"]);
  1058. // cached object is used only if its bindings contain more data or equal than those of the request
  1059. if (!empty($bindingsDiff) || ($this->objectCache[$obj_id]["bindings"]["bindings_list"] == $bindings["bindings_list"])) {
  1060. return $this->objectCache[$obj_id];
  1061. }
  1062. }
  1063. // check permissions and set $authorized true/false
  1064. if (!$this->skipCheck) {
  1065. // get permissions set on this object
  1066. $permissionModel = ClassRegistry::init("Permission");
  1067. $perms = $permissionModel->isPermissionSetted($obj_id, array(
  1068. Configure::read("objectPermissions.frontend_access_with_block"),
  1069. Configure::read("objectPermissions.frontend_access_without_block")
  1070. ));
  1071. // authorization defaults to false
  1072. $authorized = false;
  1073. if (!$perms) {
  1074. // even with check no perms found, set auth true
  1075. $authorized = true;
  1076. } else {
  1077. // divide perms by type (blocking or not)
  1078. $permsWithBlock = array();
  1079. $permsWithoutBlock = array();
  1080. foreach ($perms as $p) {
  1081. if ($p["Permission"]["flag"] == Configure::read("objectPermissions.frontend_access_without_block")) {
  1082. $permsWithoutBlock[] = $p;
  1083. } else {
  1084. $permsWithBlock[] = $p;
  1085. }
  1086. }
  1087. // if user is not logged
  1088. if (!$this->logged) {
  1089. if (!empty($permsWithBlock)) {
  1090. if (!$this->showUnauthorized) {
  1091. if($blockAccess) {
  1092. return self::UNLOGGED;
  1093. }
  1094. }
  1095. }
  1096. } else {
  1097. if ($permissionModel->checkPermissionByUser($perms, $this->BeAuth->user)) {
  1098. $authorized = true;
  1099. } else {
  1100. if (!empty($permsWithBlock)) {
  1101. if (!$this->showUnauthorized) {
  1102. if($blockAccess) {
  1103. return self::UNAUTHORIZED;
  1104. }
  1105. }
  1106. }
  1107. }
  1108. }
  1109. }
  1110. } else {
  1111. $authorized = true;
  1112. }
  1113. if (!isset($this->objectCache[$obj_id])) {
  1114. $modelType = $this->BEObject->getType($obj_id);
  1115. $bindings = $this->setObjectBindings($modelType);
  1116. }
  1117. $obj = $this->{$modelType}->find("first", array(
  1118. "conditions" => array(
  1119. "BEObject.id" => $obj_id,
  1120. "BEObject.status" => $this->status
  1121. )
  1122. )
  1123. );
  1124. if(empty($obj)) {
  1125. throw new BeditaException(__("Content not found", true));
  1126. }
  1127. // #304 status filter for Category and Tag
  1128. if(!empty($obj['Category'])) {
  1129. $cc = array();
  1130. foreach($obj['Category'] as $k => $v) {
  1131. if(in_array($v['status'],$this->status)) {
  1132. $cc[] = $v;
  1133. }
  1134. }
  1135. unset($obj['Category']);
  1136. $obj['Category'] = $cc;
  1137. }
  1138. if(!empty($obj['Tag'])) {
  1139. $tt = array();
  1140. foreach($obj['Tag'] as $k => $v) {
  1141. if(in_array($v['status'],$this->status)) {
  1142. $tt[] = $v;
  1143. }
  1144. }
  1145. unset($obj['Tag']);
  1146. $obj['Tag'] = $tt;
  1147. }
  1148. if(!$this->checkPubblicationDate($obj)) {
  1149. throw new BeditaException(__("Content not found", true));
  1150. }
  1151. $obj["publication_date"] = (!empty($obj["start_date"]))? $obj["start_date"] : $obj["created"];
  1152. $this->BeLangText->setObjectLang($obj, $this->currLang, $this->status);
  1153. if(!empty($obj["RelatedObject"])) {
  1154. $obj['relations'] = $this->objectRelationArray($obj['RelatedObject'], $this->status, array("mainLanguage" => $this->currLang));
  1155. unset($obj["RelatedObject"]);
  1156. $obj['relations_count'] = array();
  1157. foreach ($obj["relations"] as $k=>$v) {
  1158. $obj['relations_count'][$k] = count($v);
  1159. }
  1160. }
  1161. if (!empty($obj['Annotation'])) {
  1162. $this->setupAnnotations($obj, $this->status);
  1163. }
  1164. unset($obj['Annotation']);
  1165. if (!empty($obj['ObjectProperty'])) {
  1166. foreach ($obj['ObjectProperty'] as $prop) {
  1167. $properties[$prop["name"]] = $prop;
  1168. }
  1169. $obj['ObjectProperty'] = $properties;
  1170. }
  1171. $obj['object_type'] = $modelType;
  1172. $obj['authorized'] = $authorized;
  1173. // add bindings used
  1174. $obj['bindings'] = $bindings;
  1175. $this->objectCache[$obj_id] = $obj;
  1176. return $obj;
  1177. }
  1178. /**
  1179. * Load objects in section $parent_id and set in view vars an array for each object type
  1180. * (e.g. in view you will have
  1181. * $Document => array(0 => ..., 1 => ...)
  1182. * $Event" => array(0 => ..., 1 => ...)
  1183. * )
  1184. *
  1185. * @param int $parent_id
  1186. * @param array $options, filter and pagination options
  1187. */
  1188. protected function loadAndSetSectionObjects($parent_id, $options=array()) {
  1189. $sectionItems = $this->loadSectionObjects($parent_id);
  1190. foreach($sectionItems as $key => $objs) {
  1191. $this->set($key, $objs);
  1192. }
  1193. }
  1194. /**
  1195. * Load objects in section $parentNick and set in view vars an array for each object type
  1196. *
  1197. * @param string $parentNick
  1198. * @param array $options, filter and pagination options
  1199. */
  1200. protected function loadAndSetSectionObjectsByNick($parentNick, $options=array()) {
  1201. $sectionItems = $this->loadSectionObjectsByNick($parentNick, $options);
  1202. foreach($sectionItems as $key => $objs) {
  1203. $this->set($key, $objs);
  1204. }
  1205. }
  1206. /**
  1207. * Load objects in section $parentNick
  1208. *
  1209. * @param string $parentNick
  1210. * @param array $options, filter and pagination options
  1211. *
  1212. * @return array
  1213. */
  1214. protected function loadSectionObjectsByNick($parentNick, $options=array()) {
  1215. return $this->loadSectionObjects($this->BEObject->getIdFromNickname($parentNick), $options);
  1216. }
  1217. /**
  1218. * Load objects in section $parent_id
  1219. *
  1220. * @param int $parent_id
  1221. * @param array $options, filter, pagination and other options
  1222. *
  1223. * @return array
  1224. */
  1225. protected function loadSectionObjects($parent_id, $options=array()) {
  1226. if(empty($parent_id)) {
  1227. throw new BeditaException("Bad data");
  1228. }
  1229. $this->checkParentStatus($parent_id);
  1230. if(isset($this->objectCache[$parent_id]["menu"])){
  1231. $menu = $this->objectCache[$parent_id]["menu"];
  1232. $priorityOrder = $this->objectCache[$parent_id]["priority_order"];
  1233. } else {
  1234. $menu = $this->Tree->field("menu", array("id" => $parent_id));
  1235. $priorityOrder = $this->Section->field("priority_order", array("id" => $parent_id));
  1236. if(isset($this->objectCache[$parent_id])) {
  1237. $this->objectCache[$parent_id]["menu"] = $menu;
  1238. $this->objectCache[$parent_id]["priority_order"] = $priorityOrder;
  1239. }
  1240. }
  1241. $findAltPath = ($menu === '0');
  1242. if(empty($priorityOrder)) {
  1243. $priorityOrder = "asc";
  1244. }
  1245. $sectionItems = array();
  1246. $filter = (!empty($options["filter"]))? $options["filter"] : false;
  1247. $order = (!empty($options["order"]))? $options["order"] : "priority";
  1248. $dir = (isset($options["dir"]))? $options["dir"] : ($priorityOrder == "asc");
  1249. $page = (!empty($options["page"]))? $options["page"] : 1;
  1250. $dim = (!empty($options["dim"]))? $options["dim"] : 100000;
  1251. $s = $this->BEObject->getStartQuote();
  1252. $e = $this->BEObject->getEndQuote();
  1253. // add rules for start and end pubblication date
  1254. if ($this->checkPubDate["start"] == true && empty($filter["Content.start_date"])) {
  1255. $filter["Content.start_date"] = "<= '" . date("Y-m-d") . "' OR {$s}Content{$e}.{$s}start_date{$e} IS NULL";
  1256. }
  1257. if ($this->checkPubDate["end"] == true && empty($filter["Content.end_date"])) {
  1258. $filter["Content.end_date"] = ">= '" . date("Y-m-d") . "' OR {$s}Content{$e}.{$s}end_date{$e} IS NULL";
  1259. }
  1260. $items = $this->BeTree->getChildren($parent_id, $this->status, $filter, $order, $dir, $page, $dim);
  1261. if(!empty($items) && !empty($items['items'])) {
  1262. foreach($items['items'] as $index => $item) {
  1263. $obj = $this->loadObj($item['id']);
  1264. if ($obj !== self::UNAUTHORIZED && $obj !== self::UNLOGGED) {
  1265. if(empty($obj["canonicalPath"])) {
  1266. if(empty($options["sectionPath"])) {
  1267. if($findAltPath) {
  1268. $this->setCanonicalPath($obj);
  1269. } else {
  1270. $s = $this->loadObj($parent_id);
  1271. if ($s === self::UNAUTHORIZED || $s === self::UNLOGGED) {
  1272. return array();
  1273. }
  1274. $this->setCanonicalPath($s);
  1275. $obj["canonicalPath"] = (($s["canonicalPath"] != "/") ? $s["canonicalPath"] : "")
  1276. . "/" . $obj["nickname"];
  1277. }
  1278. } else {
  1279. $obj["canonicalPath"] = (($options["sectionPath"] != "/") ? $options["sectionPath"] : "")
  1280. . "/" . $obj["nickname"];
  1281. }
  1282. }
  1283. if (isset($options["setAuthorizedTo"])) {
  1284. $obj["authorized"] = $options["setAuthorizedTo"];
  1285. }
  1286. if ($this->sectionOptions["itemsByType"]) {
  1287. $sectionItems[$obj['object_type']][] = $obj;
  1288. } else {
  1289. if ($obj["object_type"] == Configure::read("objectTypes.section.model"))
  1290. $sectionItems["childSections"][] = $obj;
  1291. else
  1292. $sectionItems["childContents"][] = $obj;
  1293. }
  1294. }
  1295. }
  1296. $sectionItems["toolbar"] = $items['toolbar'];
  1297. }
  1298. return $sectionItems;
  1299. }
  1300. /**
  1301. * find first section that contain content ($name) then call section method
  1302. *
  1303. * @param string|id $name, id or content nickname
  1304. */
  1305. public function content($name) {
  1306. if(empty($name))
  1307. throw new BeditaException(__("Content not found", true));
  1308. $content_id = is_numeric($name) ? $name : $this->BEObject->getIdFromNickname($name);
  1309. // if it's defined frontend publication id then search content inside that publication else in all BEdita
  1310. $conditions = (!empty($this->publication["id"]))? "id = $content_id AND object_path LIKE '/" . $this->publication["id"] . "/%'" : "id = $content_id" ;
  1311. $section_id = $this->Tree->field('parent_id',$conditions, "priority");
  1312. if($section_id === false) {
  1313. throw new BeditaException(__("Content not found", true));
  1314. }
  1315. $this->action = 'section';
  1316. $this->section($section_id, $content_id);
  1317. }
  1318. /**
  1319. * find section and contents from section nick or section id and set template vars
  1320. *
  1321. * Set section and:
  1322. * if $contentName=null set all contents in section
  1323. * if $contentName is defined set single content
  1324. * if $contentName is defined and $this->showAllContents=true set content and other contents too (default)
  1325. *
  1326. * Execute 'sectionNickname'BeforeFilter and/or 'sectionNickName'BeforeRender
  1327. * if they're set in the controller (i.e. pages_controller.php)
  1328. *
  1329. * @param string/int $secName: section nick or section id
  1330. * @param string/int $contentName: content nick or content id
  1331. */
  1332. public function section($secName, $contentName = null) {
  1333. if (is_numeric($secName)) {
  1334. $sectionId = $secName;
  1335. $secName = $this->BEObject->getNicknameFromId($sectionId);
  1336. } else {
  1337. $sectionId = $this->BEObject->getIdFromNickname($secName);
  1338. }
  1339. $content_id = null;
  1340. if(!empty($contentName)) {
  1341. if (is_numeric($contentName)) {
  1342. $content_id = $contentName;
  1343. $contentName = $this->BEObject->getNicknameFromId($content_id);
  1344. } else {
  1345. $content_id = $this->BEObject->getIdFromNickname($contentName);
  1346. }
  1347. $contentType = $this->BEObject->getType($content_id);
  1348. if($contentType === "Section") {
  1349. $args = func_get_args();
  1350. array_shift($args);
  1351. return call_user_func_array(array($this, "section"), $args);
  1352. // check that contentName is a child of secName
  1353. } elseif ( $this->Tree->find('count',array("conditions" => array("id" => $content_id, "parent_id" => $sectionId))) == 0 ) {
  1354. throw new BeditaException(__("Content " . $contentName . " doesn't belong to " . $secName, true));
  1355. }
  1356. $contentNameFilter = BeLib::getInstance()->variableFromNickname($contentName);
  1357. }
  1358. $secNameFilter = BeLib::getInstance()->variableFromNickname($secName);
  1359. // section before filter
  1360. if (method_exists($this, $secNameFilter . "BeforeFilter")) {
  1361. $this->{$secNameFilter . "BeforeFilter"}($contentName);
  1362. }
  1363. // content before filter
  1364. if ($contentName && method_exists($this, $contentNameFilter . "BeforeFilter")) {
  1365. $this->{$contentNameFilter . "BeforeFilter"}($secName);
  1366. }
  1367. $section = $this->loadObj($sectionId);
  1368. if ($section === self::UNLOGGED || $section === self::UNAUTHORIZED) {
  1369. $this->accessDenied($section);
  1370. }
  1371. $this->setCanonicalPath($section);
  1372. $this->sectionOptions["childrenParams"] = array_merge($this->sectionOptions["childrenParams"], $this->params["named"]);
  1373. if (!isset($section["menu"]) || $section["menu"] !== "0") {
  1374. $this->sectionOptions["childrenParams"]["sectionPath"] = $section["canonicalPath"];
  1375. }
  1376. if (!$section["parentAuthorized"] || !$section["authorized"]) {
  1377. $section["authorized"] = false;
  1378. $this->sectionOptions["childrenParams"]["setAuthorizedTo"] = false;
  1379. }
  1380. if(!empty($content_id)) {
  1381. $section['currentContent'] = $this->loadObj($content_id);
  1382. if ($section['currentContent'] === self::UNLOGGED || $section['currentContent'] === self::UNAUTHORIZED) {
  1383. $this->accessDenied($section['currentContent']);
  1384. }
  1385. $section["contentRequested"] = true;
  1386. $section["contentPath"] = ($section["canonicalPath"] !== "/") ? $section["canonicalPath"] : "";
  1387. if(empty($section['currentContent']['canonicalPath'])) {
  1388. $section['currentContent']['canonicalPath'] = $section["contentPath"] .= "/" . $section['currentContent']['nickname'];
  1389. }
  1390. $this->historyItem["object_id"] = $content_id;
  1391. if(!empty($section['currentContent']['title'])) $this->historyItem["title"] = $section['currentContent']['title'];
  1392. else $this->historyItem["title"] = $section['title'];
  1393. if ($this->sectionOptions["showAllContents"]) {
  1394. if(empty($this->sectionOptions["childrenParams"]["detailed"])
  1395. || $this->sectionOptions["childrenParams"]["detailed"] === false) {
  1396. $this->baseLevel = true;
  1397. }
  1398. $checkPubDate = $this->checkPubDate;
  1399. $this->checkPubDate = array("start" => false, "end" => false);
  1400. $tmp = $this->loadSectionObjects($sectionId, $this->sectionOptions["childrenParams"]);
  1401. if (!$this->sectionOptions["itemsByType"]) {
  1402. $section = array_merge($section, $tmp);
  1403. } else {
  1404. $section = array_merge($section, array("children" => $tmp));
  1405. }
  1406. $this->baseLevel = false;
  1407. $this->checkPubDate = $checkPubDate;
  1408. }
  1409. } else {
  1410. $tmp = $this->loadSectionObjects($sectionId, $this->sectionOptions["childrenParams"]);
  1411. if (!$this->sectionOptions["itemsByType"]) {
  1412. $tmp['currentContent'] = (!empty($tmp['childContents']))? $tmp['childContents'][0] : array();
  1413. $section = array_merge($section, $tmp);
  1414. } else {
  1415. if(empty($tmp)) {
  1416. $section = array_merge($section, array("currentContent" => array(), "children" => array()));
  1417. } else {
  1418. $toolbar = $tmp["toolbar"];
  1419. unset($tmp["toolbar"]);
  1420. $current = current($tmp);
  1421. $section = array_merge($section, array("currentContent" => $current[0], "children" => $tmp, "toolbar" => $toolbar));
  1422. }
  1423. }
  1424. $this->historyItem["object_id"] = $sectionId;
  1425. $this->historyItem["title"] = $section['title'];
  1426. }
  1427. $this->set('section', $section);
  1428. // section before render
  1429. if (method_exists($this, $secNameFilter . "BeforeRender")) {
  1430. $this->{$secNameFilter . "BeforeRender"}();
  1431. }
  1432. // content before render
  1433. if ($contentName && method_exists($this, $contentNameFilter . "BeforeRender")) {
  1434. $this->{$contentNameFilter . "BeforeRender"}();
  1435. }
  1436. }
  1437. /**
  1438. * Set section canonical path and set parent array in $section array
  1439. *
  1440. * @param array $section
  1441. * @param int $sectionId
  1442. * @return boolean false if some parent sections is unauthorized for user
  1443. */
  1444. /*
  1445. protected function setSectionPath(array &$section, $sectionId) {
  1446. $section["pathSection"] = $this->getPath($sectionId);
  1447. $sectionPath = "";
  1448. $parentAuthorized = true;
  1449. foreach ($section["pathSection"] as $ps) {
  1450. if ($parentAuthorized && !empty($ps["authorized"]) && !$ps["authorized"]) {
  1451. $parentAuthorized = false;
  1452. }
  1453. }
  1454. if($section["object_type_id"] == Configure::read("objectTypes.area.id")) {
  1455. $currPath = "/";
  1456. } else {
  1457. $currPath = (!empty($section["menu"]) && $section["menu"] === '0') ? "" : "/" . $section["nickname"];
  1458. }
  1459. $parentPath = "";
  1460. if(!empty($section["pathSection"])) {
  1461. $parentSec = end($section["pathSection"]);
  1462. $parentPath = !empty($parentSec['canonicalPath']) ? $parentSec['canonicalPath'] : "";
  1463. }
  1464. $section["canonicalPath"] = $parentPath . $currPath;
  1465. return $parentAuthorized;
  1466. }
  1467. */
  1468. /**
  1469. * route to section, content or another method following the below rules
  1470. *
  1471. * 1. if there aren't url arguments (i.e. /) => uses homePage reserved word
  1472. * 2. if first url argument is a reserved words defined in configuration var 'defaultReservedWords'
  1473. * and 'cfgReservedWords' => try to call the method itself
  1474. * 3. if first url argument is a method of current controller => try to call the method itself
  1475. * 4. if first url argugment is a valid nickname
  1476. * and there is a method of current Controller with the name defined in BeLib::variableFromNickname => try to call the method itself
  1477. * example: www.example.com/my-nickname => calls PagesController::myNickname() method if it exists
  1478. * 5. if first url argument is a valid nickname => calls the appropriate FrontendController::section() or FrontendController::content() method
  1479. * 6. throw exception and 404 http error
  1480. * @throws BeditaException
  1481. */
  1482. public function route() {
  1483. $args = func_get_args();
  1484. if(count($args) === 0 || empty($args[0])) {
  1485. $args[0] = "homePage";
  1486. }
  1487. $name = $args[0];
  1488. // generic methodName
  1489. $methodName = str_replace(".", "_", $name); // example: sitemap.xml => sitemap_xml
  1490. $methodName = BeLib::getInstance()->variableFromNickname($methodName);
  1491. $reflectionClass = new ReflectionClass($this);
  1492. $id = (is_numeric($name))? $name : $this->BEObject->getIdFromNickname($name);
  1493. // setup args: look if $name is reserved
  1494. if (in_array($name, Configure::read("defaultReservedWords")) || in_array($name, Configure::read("cfgReservedWords"))) {
  1495. // load object with nickname $methodName if exists
  1496. if(!empty($id)) {
  1497. $this->loadAndSetObj($id, "object");
  1498. }
  1499. array_shift($args);
  1500. } else {
  1501. $currentClassName = $reflectionClass->getName();
  1502. $methods = array($name, $methodName);
  1503. // try to use current Controller method (PagesController::$name or PagesController::$methodName)
  1504. while (count($methods) > 0) {
  1505. $m = array_shift($methods);
  1506. try {
  1507. // check method belongs to PagesController to avoid to call AppController, FrontendController public methods
  1508. $methodClassName = $reflectionClass->getMethod($m)->class;
  1509. if ($currentClassName == $methodClassName) {
  1510. array_shift($args);
  1511. $methodName = $m;
  1512. if (is_string($name) && !empty($id)) {
  1513. // load object with nickname $name if exists
  1514. if(!empty($id)) {
  1515. $this->loadAndSetObj($id, "object");
  1516. }
  1517. }
  1518. $methods = array();
  1519. }
  1520. } catch (ReflectionException $ex) {
  1521. $methodName = null;
  1522. }
  1523. }
  1524. // try to use self::section or self::content methods
  1525. if ($methodName === null && is_string($name) && !empty($id)) {
  1526. $object_type_id = $this->BEObject->findObjectTypeId($id);
  1527. if ($object_type_id == Configure::read("objectTypes.section.id") || $object_type_id == Configure::read("objectTypes.area.id")) {
  1528. $methodName = "section";
  1529. } else {
  1530. $methodName = "content";
  1531. }
  1532. }
  1533. }
  1534. $this->action = $methodName;
  1535. try {
  1536. // check before filter method
  1537. if ($reflectionClass->hasMethod($methodName . "BeforeFilter")) {
  1538. $this->{$methodName . "BeforeFilter"}();
  1539. }
  1540. // call method
  1541. $reflectionMethod = $reflectionClass->getMethod($methodName);
  1542. $reflectionMethod->invokeArgs($this, $args);
  1543. // check before render method
  1544. if ($reflectionClass->hasMethod($methodName . "BeforeRender")) {
  1545. $this->{$methodName . "BeforeRender"}();
  1546. }
  1547. } catch (ReflectionException $ex) {
  1548. // launch 404 error
  1549. throw new BeditaException(__("Content not found", true), $ex->getMessage());
  1550. }
  1551. }
  1552. /**
  1553. * search inside history
  1554. */
  1555. public function search() {
  1556. $this->historyItem = null;
  1557. if(!in_array('BeToolbar', $this->helpers)) {
  1558. $this->helpers[] = 'BeToolbar';
  1559. }
  1560. $this->searchOptions = array_merge($this->searchOptions, $this->params["named"]);
  1561. $s = $this->BEObject->getStartQuote();
  1562. $e = $this->BEObject->getEndQuote();
  1563. // add rules for start and end pubblication date
  1564. if ($this->checkPubDate["start"] == true && empty($this->searchOptions["filter"]["Content.start_date"])) {
  1565. $this->searchOptions["filter"]["Content.start_date"] = "<= '" . date("Y-m-d") . "' OR {$s}Content{$e}.{$s}start_date{$e} IS NULL";
  1566. }
  1567. if ($this->checkPubDate["end"] == true && empty($this->searchOptions["filter"]["Content.end_date"])) {
  1568. $this->searchOptions["filter"]["Content.end_date"] = ">= '" . date("Y-m-d") . "' OR {$s}Content{$e}.{$s}end_date{$e} IS NULL";
  1569. }
  1570. $result = $this->BeTree->getDescendants($this->publication["id"], $this->status, $this->searchOptions["filter"], $this->searchOptions["order"], $this->searchOptions["dir"], $this->searchOptions["page"], $this->searchOptions["dim"]);
  1571. $this->set("searchResult", $result);
  1572. }
  1573. /**
  1574. * public subscribe page, used for newsletter/frontend subscribe/unsubscribe
  1575. *
  1576. * @param string $what
  1577. */
  1578. public function subscribe($what="newsletter") {
  1579. $this->historyItem = null;
  1580. if ($what == "newsletter") {
  1581. $mailGroupModel = ClassRegistry::init("MailGroup");
  1582. $mailgroups = $mailGroupModel->find("all", array(
  1583. "conditions" => array(
  1584. "area_id" => $this->publication["id"],
  1585. "visible" => 1
  1586. ),
  1587. "contain" => array()
  1588. )
  1589. );
  1590. $this->set("mailgroups", $mailgroups);
  1591. }
  1592. $this->set("what", $what);
  1593. }
  1594. /**
  1595. * manage hash request like newsletter/frontend subscribe/unsubscribe
  1596. *
  1597. * @param string $service_type
  1598. * @param string $hash
  1599. * @return void
  1600. * @throws BeditaRuntimeException
  1601. */
  1602. public function hashjob($service_type=null, $hash=null) {
  1603. try {
  1604. $this->Transaction->begin();
  1605. $this->BeHash->handleHash($service_type, $hash);
  1606. $this->Transaction->commit();
  1607. } catch (BeditaHashException $ex) {
  1608. $this->Transaction->rollback();
  1609. $this->userErrorMessage($ex->getMessage());
  1610. $this->eventError($ex->getDetails());
  1611. } catch (BeditaException $ex) {
  1612. $this->Transaction->rollback();
  1613. throw new BeditaRuntimeException($ex->getMessage(), $ex->getDetails());
  1614. }
  1615. }
  1616. /**
  1617. * find parent path of $object_id (excluded publication)
  1618. *
  1619. * @param int $object_id
  1620. * @return array (the keys are object's id)
  1621. */
  1622. protected function getPath($object_id) {
  1623. $pathArr = array();
  1624. $path = "";
  1625. if(isset($this->objectCache[$object_id]["parent_path"])){
  1626. $path = $this->objectCache[$object_id]["parent_path"];
  1627. } else {
  1628. $row = $this->Tree->find("first", array(
  1629. "conditions" => array("id" => $object_id, "area_id" => $this->publication["id"]),
  1630. "limit" => 1,
  1631. "order" => array("menu" => "desc", "parent_path" => "desc")
  1632. ));
  1633. if (!empty($row["Tree"]["parent_path"])) {
  1634. $path = $row["Tree"]["parent_path"];
  1635. if(!empty($this->objectCache[$object_id])) {
  1636. $this->objectCache[$object_id] = array_merge($this->objectCache[$object_id], $row["Tree"]);
  1637. }
  1638. }
  1639. }
  1640. $parents = explode("/", trim($path,"/"));
  1641. if (!empty($parents[0])) {
  1642. if($parents[0] != $this->publication["id"]) {
  1643. throw new BeditaException("Wrong publication: " . $parents[0]);
  1644. }
  1645. $oldSectionBindings = null;
  1646. if(!empty($this->modelBindings["Section"])) {
  1647. $oldSectionBindings = $this->modelBindings["Section"];
  1648. }
  1649. $this->modelBindings["Section"] = array("BEObject" => array("LangText", "ObjectProperty"), "Tree");
  1650. $currPath = "";
  1651. $parentPath = "";
  1652. foreach ($parents as $p) {
  1653. if ($p != $this->publication["id"]) {
  1654. if(isset($this->objectCache[$p])) {
  1655. $pathArr[$p] = $this->objectCache[$p];
  1656. } else {
  1657. $pathArr[$p] = $this->loadObj($p);
  1658. }
  1659. if(!empty($pathArr[$p]["canonicalPath"])) {
  1660. $currPath = $pathArr[$p]["canonicalPath"];
  1661. } else {
  1662. if($pathArr[$p]["menu"] !== '0') {
  1663. $currPath .= (($currPath === "/") ? "" : "/") . $pathArr[$p]["nickname"];
  1664. }
  1665. $pathArr[$p]["canonicalPath"] = empty($currPath) ? "/" : $currPath;
  1666. $this->objectCache[$p]["canonicalPath"] = $pathArr[$p]["canonicalPath"];
  1667. }
  1668. if ($pathArr[$p] === self::UNLOGGED || $pathArr[$p] === self::UNAUTHORIZED) {
  1669. $this->accessDenied($pathArr[$p]);
  1670. }
  1671. }
  1672. }
  1673. if(empty($oldSectionBindings)) {
  1674. unset($this->modelBindings["Section"]);
  1675. } else {
  1676. $this->modelBindings["Section"] = $oldSectionBindings;
  1677. }
  1678. }
  1679. return $pathArr;
  1680. }
  1681. /**
  1682. * get array of parents that contain the object specified by $object_id
  1683. *
  1684. * @param integer $object_id
  1685. * @return array
  1686. */
  1687. protected function getParentsObject($object_id) {
  1688. $parents_id = $this->BeTree->getParents($object_id, $this->publication["id"]);
  1689. $parents = array();
  1690. foreach ($parents_id as $id) {
  1691. $parents[] = $this->loadObj($id, false);
  1692. }
  1693. return $parents;
  1694. }
  1695. /**
  1696. * build archive tree
  1697. *
  1698. * Array(
  1699. * "Document" => Array(
  1700. * "2008" => Array(
  1701. * "01" => Array(
  1702. * 0 => document,
  1703. * 1 => document,
  1704. * ...
  1705. * "monthName" => month name
  1706. * "total" => number of document in january
  1707. * ),
  1708. * "02" => Array(...),
  1709. * ....
  1710. * "total" => numeber of document in 2008
  1711. * ),
  1712. * "2007" => Array(...),
  1713. * "ShortNews" => ....
  1714. * )
  1715. *
  1716. * @param string $secName section id or section nickname
  1717. * @return array
  1718. */
  1719. protected function loadArchiveTree($secName, $options=array()) {
  1720. $section_id = (is_numeric($secName))? $secName : $this->BEObject->getIdFromNickname($secName);
  1721. $monthName = array("01" => "January", "02" => "February", "03" => "March", "04" => "April", "05" => "May",
  1722. "06" => "June", "07" => "July", "08" => "August", "09" => "September", "10" => "October",
  1723. "11" => "November", "12" => "December");
  1724. $this->modelBindings['Document'] = array("BEObject" => array("LangText"));
  1725. $this->modelBindings['ShortNews'] = array("BEObject" => array("LangText"));
  1726. $this->modelBindings['Event'] = array("BEObject" => array("LangText"),"DateItem");
  1727. $oldItemsByType = $this->sectionOptions['itemsByType'];
  1728. $this->sectionOptions['itemsByType'] = true;
  1729. $items = $this->loadSectionObjects($section_id,$options);
  1730. unset($this->modelBindings);
  1731. $this->sectionOptions['itemsByType'] = $oldItemsByType;
  1732. $archive = array();
  1733. foreach ($items as $type => $itemGroup) {
  1734. if($type != "toolbar") {
  1735. foreach ($itemGroup as $item) {
  1736. // DateItem, pubblication or creation date
  1737. if(!empty($item["DateItem"][0]["start_date"]))
  1738. $refDate = $item["DateItem"][0]["start_date"];
  1739. else
  1740. $refDate = isset($item["start_date"])? $item["start_date"] : $item["created"];
  1741. $data = explode("-", $refDate);
  1742. $year = $data[0];
  1743. $id = $item["id"];
  1744. $item["title"] = (!empty($item["LangText"]["title"][$this->currLang]))? $item["LangText"]["title"][$this->currLang] : $item["title"];
  1745. $archive[$type][$year][$data[1]][] = $item;
  1746. }
  1747. // sort archive
  1748. $sortFunction = "ksort";
  1749. if (!empty($options["archiveSort"]) && $options["archiveSort"] == "desc")
  1750. $sortFunction = "krsort";
  1751. $sortFunction($archive[$type]);
  1752. foreach ($archive[$type] as $year => $month) {
  1753. $sortFunction($archive[$type][$year]);
  1754. }
  1755. // add number of items for month and year
  1756. $countYear = 0;
  1757. foreach ($archive[$type] as $year => $month) {
  1758. $countYear = 0;
  1759. foreach ($month as $key => $i) {
  1760. $countItem = count($i);
  1761. $countYear += $countItem;
  1762. $archive[$type][$year][$key]["total"] = $countItem;
  1763. $archive[$type][$year][$key]["monthName"] = __($monthName[$key],true);
  1764. }
  1765. $archive[$type][$year]["total"] = $countYear;
  1766. }
  1767. }
  1768. }
  1769. return $archive;
  1770. }
  1771. /**
  1772. * load all tag
  1773. *
  1774. * @param string $tplVar
  1775. * @param boolean $cloud, if true set 'class' key
  1776. * (possible value: smallestTag, largestTag, largeTag, mediumTag, smallTag)
  1777. * @param boolean $shuffle, if true shuffle the tags else order by label
  1778. * @param int $tagShowed, define how much tags have to be returned (null = all tags)
  1779. */
  1780. public function loadTags($tplVar=null, $cloud=true, $shuffle=false, $tagShowed=null) {
  1781. $tplVar = (empty($tplVar))? "listTags" : $tplVar;
  1782. $category = ClassRegistry::init("Category");
  1783. $tags = $category->getTags(array(
  1784. "showOrphans" => false,
  1785. "status" => $this->status,
  1786. "cloud" => $cloud,
  1787. "area_id" => $this->publication["id"]
  1788. ));
  1789. if ($shuffle) {
  1790. shuffle($tags);
  1791. }
  1792. if (!empty($tagShowed)) {
  1793. $tags = array_slice($tags,0,$tagShowed);
  1794. }
  1795. $this->set($tplVar, $tags);
  1796. }
  1797. /**
  1798. * find all objects tagged by $name and set results for view
  1799. *
  1800. * @param string $name
  1801. */
  1802. public function tag($name) {
  1803. $this->set("tag",$this->loadObjectsByTag($name));
  1804. }
  1805. /**
  1806. * find all objects for category $name and set results for view
  1807. *
  1808. * @param string $name
  1809. */
  1810. public function category($name) {
  1811. $this->set("category",$this->loadObjectsByCategory($name));
  1812. }
  1813. /**
  1814. * return objects for a specific category
  1815. *
  1816. * @param string $category category name (friendly url/unique name)
  1817. * @param array $options search options
  1818. * "section" => name or id section
  1819. * "filter" => particular filter
  1820. * "order", "dir", "dim", "page" used like pagination parameters,
  1821. * "baseLevel" => true to use $this->baseLevel = true for model bindings
  1822. * @return array
  1823. */
  1824. protected function loadObjectsByCategory($categoryName, $options=array()) {
  1825. return $this->loadObjectsByTagCategory($categoryName, $options, "category");
  1826. }
  1827. /**
  1828. * Internal method for loadObjectsByCategory loadObjectsByTag
  1829. *
  1830. * @param string $name category/tag name (friendly url/unique name)
  1831. * @param array $options search options (see loadObjectsByCategory)
  1832. * @param string $type, "tag" (default), or "category"
  1833. * @return array
  1834. * @throws BeditaException
  1835. */
  1836. private function loadObjectsByTagCategory($name, $options=array(), $type = "tag") {
  1837. $section_id = null;
  1838. if (!empty($options["section"])) {
  1839. $section_id = (is_numeric($options["section"]))? $options["section"] : $this->BEObject->getIdFromNickname($options["section"]);
  1840. $this->checkParentStatus($section_id);
  1841. $searchMethod = "getChildren";
  1842. } else {
  1843. $section_id = $this->publication["id"];
  1844. $searchMethod = "getDescendants";
  1845. }
  1846. $filter = (!empty($options["filter"]))? $options["filter"] : false;
  1847. if($type === "tag") {
  1848. $detail = ClassRegistry::init("Category")->find("first", array(
  1849. "conditions" => array("name" => $name, "object_type_id IS NULL", "status" => $this->status)
  1850. )
  1851. );
  1852. if (empty($detail))
  1853. throw new BeditaException(__("No tag found", true). " - $name");
  1854. $options = array_merge($this->tagOptions, $options, $this->params["named"]);
  1855. $filter["tag"] = $name;
  1856. } else if ($type === "category"){
  1857. $detail = ClassRegistry::init("Category")->find("first", array(
  1858. "conditions" => array("name" => $name, "object_type_id IS NOT NULL", "status" => $this->status)
  1859. )
  1860. );
  1861. if (empty($detail))
  1862. throw new BeditaException(__("No category found", true) . " - $name");
  1863. $options = array_merge($this->tagOptions, $options, $this->params["named"]);
  1864. $filter["category"] = $name;
  1865. } else {
  1866. throw new BeditaException(__("Unsupported type", true). " - $type");
  1867. }
  1868. $s = $this->BEObject->getStartQuote();
  1869. $e = $this->BEObject->getEndQuote();
  1870. $order = "";
  1871. if (!empty($options["order"])) {
  1872. $order = $options["order"];
  1873. } elseif (!empty($section_id)) {
  1874. $order = "{$s}Tree{$e}.{$s}priority{$e}";
  1875. }
  1876. $dir = (isset($options["dir"]))? $options["dir"] : 1;
  1877. $page = (!empty($options["page"]))? $options["page"] : 1;
  1878. $dim = (!empty($options["dim"]))? $options["dim"] : 100000;
  1879. // add rules for start and end pubblication date
  1880. if ($this->checkPubDate["start"] == true && empty($filter["Content.start_date"])) {
  1881. $filter["Content.start_date"] = "<= '" . date("Y-m-d") . "' OR {$s}Content{$e}.{$s}start_date{$e} IS NULL";
  1882. }
  1883. if ($this->checkPubDate["end"] == true && empty($filter["Content.end_date"])) {
  1884. $filter["Content.end_date"] = ">= '" . date("Y-m-d") . "' OR {$s}Content{$e}.{$s}end_date{$e} IS NULL";
  1885. }
  1886. $contents = $this->BeTree->{$searchMethod}($section_id, $this->status, $filter, $order, $dir, $page, $dim);
  1887. $result = $detail;
  1888. if (!empty($options['baseLevel'])) {
  1889. $oldBaseLevel = $this->baseLevel;
  1890. $this->baseLevel = true;
  1891. }
  1892. foreach ($contents["items"] as $c) {
  1893. $object = $this->loadObj($c["id"]);
  1894. if ($object !== self::UNLOGGED && $object !== self::UNAUTHORIZED) {
  1895. try {
  1896. $this->setCanonicalPath($object);
  1897. if ($this->sectionOptions["itemsByType"]) {
  1898. $result[$object['object_type']][] = $object;
  1899. } else {
  1900. $result["items"][] = $object;
  1901. }
  1902. } catch (BeditaException $ex) {
  1903. // do nothing, just esclude object from final result if no canonical path was found
  1904. if (Configure::read('debug') > 0) {
  1905. $this->log("Valid canonicalPath isn't found for object with nickname " . $object["nickname"], LOG_DEBUG);
  1906. }
  1907. }
  1908. }
  1909. }
  1910. if (!empty($options['baseLevel'])) {
  1911. $this->baseLevel = $oldBaseLevel;
  1912. }
  1913. return array_merge($result, array("toolbar" => $contents["toolbar"]));
  1914. }
  1915. /**
  1916. * return objects for a specific tag
  1917. *
  1918. * @param string $tag tag name (friendly url/unique name)
  1919. * @param array $options search options
  1920. * "section" => name or id section
  1921. * "filter" => particular filter
  1922. * "order", "dir", "dim", "page" used like pagination parameters
  1923. * "baseLevel" => true to use $this->baseLevel = true for model bindings
  1924. * @return array
  1925. */
  1926. protected function loadObjectsByTag($tag, $options=array()) {
  1927. return $this->loadObjectsByTagCategory($tag, $options, "tag");
  1928. }
  1929. /**
  1930. * load annotation referenced to some object
  1931. *
  1932. * @param string $annotationType, object type of the annotation e.g. "comment"
  1933. * @param string $objectName, reference object nickname or id
  1934. * @param array $options, specific options (pagination, filter) that override annotationOptions attribute
  1935. * @return array of annotations
  1936. */
  1937. protected function loadAnnotations($annotationType, $objectName, $options=array()) {
  1938. if (empty($annotationType) || empty($objectName))
  1939. throw new BeditaException(__("Annotation type or object_id missing", true));
  1940. $object_id = (is_numeric($objectName))? $objectName : $this->BEObject->getIdFromNickname($objectName);
  1941. $options = array_merge($this->annotationOptions[$annotationType], $options, $this->params["named"]);
  1942. $filter = (!empty($options["filter"]))? $options["filter"] : array();
  1943. $filter["object_type_id"] = Configure::read("objectTypes." . $annotationType . ".id");
  1944. $filter[Configure::read("objectTypes." . $annotationType . ".model") . ".object_id"] = $object_id;
  1945. $order = (!empty($options["order"]))? $options["order"] : "BEObject.created";
  1946. $dir = (isset($options["dir"]))? $options["dir"] : 1;
  1947. $page = (!empty($options["page"]))? $options["page"] : 1;
  1948. $dim = (!empty($options["dim"]))? $options["dim"] : 100000;
  1949. $annotations = $this->BeTree->getChildren(null, $this->status, $filter, $order, $dir, $page, $dim);
  1950. $result = array();
  1951. foreach ($annotations["items"] as $a) {
  1952. $object = $this->loadObj($a["id"]);
  1953. if ($object !== self::UNLOGGED && $object !== self::UNAUTHORIZED)
  1954. $result[Configure::read("objectTypes." . $annotationType . ".model")][] = $object;
  1955. }
  1956. return array_merge($result, array("toolbar" => $annotations["toolbar"]));
  1957. }
  1958. /**
  1959. * force download of media object
  1960. *
  1961. * @param $name id or object nickname
  1962. * @throws BeditaException
  1963. */
  1964. public function download($name) {
  1965. if(empty($name))
  1966. throw new BeditaException(__("Content not found", true));
  1967. $id = is_numeric($name) ? $name : $this->BEObject->getIdFromNickname($name);
  1968. $object_type_id = $this->BEObject->findObjectTypeId($id);
  1969. // verify type
  1970. $conf = Configure::getInstance();
  1971. if(($object_type_id === false) || !in_array($object_type_id, $conf->objectTypes['multimedia']['id']))
  1972. throw new BeditaException(__("Content not found", true));
  1973. $obj = $this->loadObj($id);
  1974. if ($obj === self::UNLOGGED || $obj === self::UNAUTHORIZED) {
  1975. $this->accessDenied($obj);
  1976. }
  1977. // check 'download' or 'attach' relation
  1978. // TODO: check relatedObject status and position on tree????
  1979. $objRel = ClassRegistry::init("ObjectRelation");
  1980. $relatedObjectId = $objRel->find('first', array(
  1981. 'conditions' => array(
  1982. "ObjectRelation.id" => $id,
  1983. "ObjectRelation.switch" => array("download", "attach")
  1984. ),
  1985. 'fields' => array('object_id')));
  1986. // check if multimedia is on the tree
  1987. $isOnTree = ClassRegistry::init("Tree")->isOnTree($id, $this->publication["id"]);
  1988. if($relatedObjectId === false && $isOnTree === false) {
  1989. throw new BeditaException(__("Content not found", true));
  1990. }
  1991. // media with provider or file on filesystem? TODO: use DS??
  1992. if(!empty($obj['provider']) || $obj['uri'][0] !== "/") {
  1993. $this->redirect($obj['uri']);
  1994. }
  1995. // TODO: for some extensions or mime-types redirect to media URL
  1996. if(isset($conf->redirectMimeTypesDownload) &&
  1997. in_array($obj['mime_type'], $conf->redirectMimeTypesDownload)) {
  1998. $this->redirect($conf->mediaUrl.$obj['uri']);
  1999. }
  2000. $path = ($conf->mediaRoot).$obj['uri'];
  2001. $f = new File($path);
  2002. $info = $f->info();
  2003. if(isset($conf->redirectExtensionsDownload) &&
  2004. in_array($info['extension'], $conf->redirectExtensionsDownload)) {
  2005. $this->redirect($conf->mediaUrl.$obj['uri']);
  2006. }
  2007. Configure::write('debug', 0);
  2008. // use readfile
  2009. // TODO: optimizations! use X-Sendfile ?
  2010. header('Content-Description: File Transfer');
  2011. header('Content-Type: '.$obj['mime_type']);
  2012. header('Content-Disposition: attachment; filename='.$obj['name']);
  2013. header('Content-Transfer-Encoding: binary');
  2014. header('Expires: 0');
  2015. header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
  2016. header('Pragma: public');
  2017. header('Content-Length: ' . $obj['file_size']);
  2018. ob_clean();
  2019. flush();
  2020. readfile($path);
  2021. exit();
  2022. }
  2023. /**
  2024. * show image for captcha
  2025. *
  2026. */
  2027. public function captchaImage() {
  2028. $this->historyItem = null;
  2029. if(!isset($this->Captcha)) {
  2030. App::import('Component', 'Captcha');
  2031. $this->Captcha = new CaptchaComponent();
  2032. $this->Captcha->startup($this);
  2033. }
  2034. $this->autoRender = false;
  2035. $this->Captcha->image($this->captchaOptions);
  2036. }
  2037. /**
  2038. * save comment relative to an object, set 'info' flash message
  2039. * throw Exception in case of error and set 'error' flash message
  2040. *
  2041. * If it's ajax request and if not empty $this->params["form"]["render"] renders it
  2042. *
  2043. * elseif it's not ajax request then redirect to referer
  2044. *
  2045. * @throws BeditaException
  2046. */
  2047. public function saveComment() {
  2048. $this->historyItem = null;
  2049. if (!empty($this->data)) {
  2050. // sanitize from scripts
  2051. $this->data = BeLib::getInstance()->stripData($this->data);
  2052. if(!isset($this->Comment)) {
  2053. $this->Comment = $this->loadModelByType("Comment");
  2054. }
  2055. $this->data["title"] = substr($this->data["description"],0,30) . "...";
  2056. // for comment status check contents.comments
  2057. $beObject = ClassRegistry::init("BEObject");
  2058. $commentsFlag = $beObject->field("comments", array("id" => $this->data['object_id']));
  2059. if($commentsFlag == 'moderated') {
  2060. $this->data["status"] = "draft";
  2061. $userMsgOK = "Your message has been sent and it's waiting approval.";
  2062. } else if ($commentsFlag == 'on'){
  2063. $this->data["status"] = 'on';
  2064. $userMsgOK = "Your message has been saved.";
  2065. } else {
  2066. throw new BeditaException(__("Post comment disabled", true));
  2067. }
  2068. try {
  2069. // check IP
  2070. $bannedIP = ClassRegistry::init("BannedIp");
  2071. if($bannedIP->isBanned($_SERVER['REMOTE_ADDR'])) {
  2072. throw new BeditaException(__("Error saving comment", true));
  2073. }
  2074. // check captcha if not logged
  2075. if (!$this->logged) {
  2076. if(!isset($this->Captcha)) {
  2077. App::import('Component', 'Captcha');
  2078. $this->Captcha = new CaptchaComponent();
  2079. $this->Captcha->startup($this);
  2080. }
  2081. $this->Captcha->checkCaptcha();
  2082. // set User data
  2083. } else {
  2084. $userdata = $this->Session->read($this->BeAuth->sessionKey);
  2085. $this->data["user_created"] = $userdata["id"];
  2086. $this->data["user_modified"] = $userdata["id"];
  2087. $this->data["author"] = (!empty($userdata["realname"]))? $userdata["realname"] : $userdata["userid"];
  2088. if ( (trim($userdata["email"]) != "") ) {
  2089. $this->data["email"] = $userdata["email"];
  2090. }
  2091. }
  2092. // build thread path
  2093. if (!empty($this->params["form"]["thread_parent_id"])) {
  2094. $thread_path = $this->Comment->field("thread_path", array("id" => $this->params["form"]["thread_parent_id"]));
  2095. $this->data["thread_path"] = (!empty($thread_path))? $thread_path . "/" . $this->params["form"]["thread_parent_id"] : "/" . $this->params["form"]["thread_parent_id"];
  2096. }
  2097. // content url shown in notification
  2098. if (empty($this->data["notification_content_url"])) {
  2099. $this->data["notification_content_url"] = $this->publication["public_url"] . $this->referer();
  2100. }
  2101. $this->Transaction->begin();
  2102. if (!$this->Comment->save($this->data)) {
  2103. throw new BeditaException(__("Error saving comment", true), $this->Comment->validationErrors);
  2104. }
  2105. $this->Transaction->commit();
  2106. $this->userInfoMessage(__($userMsgOK, true));
  2107. } catch (BeditaException $ex) {
  2108. $this->Transaction->rollback();
  2109. $this->log($ex->errorTrace());
  2110. $this->userErrorMessage($ex->getMessage());
  2111. $error = true;
  2112. }
  2113. }
  2114. // if it's ajax call no redirect by referer
  2115. if($this->RequestHandler->isAjax()) {
  2116. $this->layout = "ajax";
  2117. $this->set('comment',$this->loadObj($this->Comment->id));
  2118. if (!empty($this->params["form"]["render"])) {
  2119. $this->render(null, null, $this->params["form"]["render"]);
  2120. }
  2121. } else {
  2122. // saveCommentBeforeFilter
  2123. if (method_exists($this, "saveCommentBeforeRender")) {
  2124. $this->saveCommentBeforeRender();
  2125. }
  2126. $urlToRedirect = $this->referer();
  2127. if (!empty($error))
  2128. $urlToRedirect .= "/#error";
  2129. elseif ($commentsFlag == 'on')
  2130. $urlToRedirect .= "/#comment-".$this->Comment->id;
  2131. $this->redirect($urlToRedirect);
  2132. }
  2133. }
  2134. /**
  2135. * show an object in print mode with specific layout and view
  2136. * CakePHP layout: print (if dosen't exists in frontend app use backend print layout)
  2137. * use print view if not set a specific $printLayout
  2138. *
  2139. * @param int $id
  2140. * @param string $printLayout, the view template to use
  2141. */
  2142. public function printme($id=null, $printLayout=null) {
  2143. if (!empty($this->params["form"]["id"]))
  2144. $id = $this->params["form"]["id"];
  2145. if (!empty($this->params["form"]["printLayout"]))
  2146. $id = $this->params["form"]["printLayout"];
  2147. $objectData = $this->loadObj($id);
  2148. if ($objectData == self::UNLOGGED || $objectData === self::UNAUTHORIZED) {
  2149. $this->accessDenied($objectData);
  2150. }
  2151. $this->layout = "print";
  2152. $this->set("printLayout", $printLayout);
  2153. $this->set("object", $objectData);
  2154. if (file_exists(APP."views".DS."pages".DS.$printLayout.".tpl"))
  2155. $this->render($printLayout);
  2156. else
  2157. $this->render("print");
  2158. }
  2159. /**
  2160. * save a BEdita object. User has to be logged
  2161. *
  2162. * @param string $modelName (Document, Event, ....).
  2163. * If undefined get object type from $this->data["object_type_id"]
  2164. * @return mixed int|boolean, false on error, object_id saved on success
  2165. */
  2166. protected function save($modelName=null) {
  2167. if (!$this->logged) {
  2168. $this->accessDenied(self::UNLOGGED);
  2169. }
  2170. try {
  2171. if (empty($modelName) && empty($this->data["object_type_id"])) {
  2172. throw new BeditaException(__("no object type defined",true));
  2173. }
  2174. $modelName = (empty($modelName))? Configure::read("objectTypes.".$this->data["object_type_id"].".model") : $modelName;
  2175. $objectModel = ClassRegistry::init($modelName);
  2176. // content url shown in notification
  2177. if (empty($this->data["notification_content_url"])) {
  2178. $this->data["notification_content_url"] = $this->publication["public_url"] . $this->referer();
  2179. }
  2180. // sanitize from scripts
  2181. $this->data = BeLib::getInstance()->stripData($this->data);
  2182. $this->Transaction->begin();
  2183. $this->saveObject($objectModel);
  2184. $this->Transaction->commit();
  2185. $this->userInfoMessage(__($modelName . " saved",true));
  2186. $this->eventInfo("object [". $objectModel->id ."] saved");
  2187. return $objectModel->id;
  2188. } catch (BeditaException $ex) {
  2189. $this->Transaction->rollback();
  2190. $this->log($ex->errorTrace());
  2191. $this->userErrorMessage($ex->getMessage());
  2192. return false;
  2193. }
  2194. }
  2195. /**
  2196. * delete a BEdita object. User has to be logged
  2197. *
  2198. * @return boolean
  2199. */
  2200. protected function delete() {
  2201. if (!$this->logged) {
  2202. $this->accessDenied(self::UNLOGGED);
  2203. }
  2204. try {
  2205. if (!empty($this->data["object_type_id"])) {
  2206. $object_type_id = $this->data["object_type_id"];
  2207. } elseif (!empty($this->data["id"])) {
  2208. $object_type_id = $this->BEObject->findObjectTypeId($this->data["id"]);
  2209. } else {
  2210. throw new BeditaException(__("no object type defined",true));
  2211. }
  2212. $modelName = Configure::read("objectTypes.".$object_type_id.".model");
  2213. $this->{$modelName} = ClassRegistry::init($modelName);
  2214. $objectsDeleted = $this->deleteObjects($modelName);
  2215. $this->userInfoMessage(__($objectsDeleted . " deleted",true));
  2216. return true;
  2217. } catch (BeditaException $ex) {
  2218. $this->log($ex->errorTrace());
  2219. $this->userErrorMessage($ex->getMessage());
  2220. return false;
  2221. }
  2222. }
  2223. /**
  2224. * check parents status of $section_id
  2225. *
  2226. * if one or more parents haven't status IN $this->status array throw a BeditaException
  2227. *
  2228. * @param int $section_id
  2229. * @throws BeditaException
  2230. */
  2231. private function checkParentStatus($section_id) {
  2232. $parent_path = $this->Tree->field("parent_path", array("id" => $section_id));
  2233. $parent_array = explode("/", trim($parent_path,"/"));
  2234. if (!empty($parent_array[0])) {
  2235. $countParent = count($parent_array);
  2236. $countParentStatus = $this->BEObject->find("count", array(
  2237. "conditions" => array(
  2238. "status" => $this->status,
  2239. "id" => $parent_array
  2240. ),
  2241. "contain" => array()
  2242. )
  2243. );
  2244. if ($countParent != $countParentStatus)
  2245. throw new BeditaException(__("Content not found", true));
  2246. }
  2247. }
  2248. /**
  2249. * add "draft" status to class attribute $status
  2250. */
  2251. protected function showDraft() {
  2252. $this->status[] = "draft";
  2253. }
  2254. /**
  2255. * return class attribute $status
  2256. * @return array
  2257. */
  2258. public function getStatus() {
  2259. return $this->status;
  2260. }
  2261. }