PageRenderTime 48ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/web/concrete/libraries/view.php

http://github.com/concrete5/concrete5
PHP | 905 lines | 523 code | 130 blank | 252 comment | 166 complexity | 5c4a687fab2dc11559521ebf2adf9439 MD5 | raw file
Possible License(s): MIT, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  1. <?
  2. defined('C5_EXECUTE') or die("Access Denied.");
  3. /**
  4. * @package Core
  5. * @category Concrete
  6. * @author Andrew Embler <andrew@concrete5.org>
  7. * @copyright Copyright (c) 2003-2008 Concrete5. (http://www.concrete5.org)
  8. * @license http://www.concrete5.org/license/ MIT License
  9. *
  10. */
  11. /**
  12. * A generic object that every front-end template (view) or page extends.
  13. * @package Core
  14. * @author Andrew Embler <andrew@concrete5.org>
  15. * @category Concrete
  16. * @copyright Copyright (c) 2003-2008 Concrete5. (http://www.concrete5.org)
  17. * @license http://www.concrete5.org/license/ MIT License
  18. *
  19. */
  20. class View extends Object {
  21. private $viewPath;
  22. /**
  23. * controller used by this particular view
  24. * @access public
  25. * @var object
  26. */
  27. public $controller;
  28. /**
  29. * An array of items that get loaded into a page's header
  30. */
  31. private $headerItems = array();
  32. /**
  33. * An array of items that get loaded into just before body close
  34. */
  35. private $footerItems = array();
  36. /**
  37. * themePaths holds the various hard coded paths to themes
  38. * @access private
  39. * @var array
  40. */
  41. private $themePaths = array();
  42. private $areLinksDisabled = false;
  43. /**
  44. * editing mode is enabled or not
  45. * @access private
  46. * @var boolean
  47. */
  48. private $isEditingEnabled = true;
  49. // getInstance() grabs one instance of the view w/the singleton pattern
  50. public function getInstance() {
  51. static $instance;
  52. if (!isset($instance)) {
  53. $v = __CLASS__;
  54. $instance = new $v;
  55. }
  56. return $instance;
  57. }
  58. /**
  59. * This grabs the theme for a particular path, if one exists in the themePaths array
  60. * @access private
  61. * @param string $path
  62. * @return string $theme
  63. */
  64. private function getThemeFromPath($path) {
  65. // there's probably a more efficient way to do this
  66. $theme = false;
  67. $txt = Loader::helper('text');
  68. foreach($this->themePaths as $lp => $layout) {
  69. if ($txt->fnmatch($lp, $path)) {
  70. $theme = $layout;
  71. break;
  72. }
  73. }
  74. return $theme;
  75. }
  76. /**
  77. * Returns a stylesheet found in a themes directory - but FIRST passes it through the tools CSS handler
  78. * in order to make certain style attributes found inside editable
  79. * @param string $stylesheet
  80. */
  81. public function getStyleSheet($stylesheet) {
  82. if ($this->isPreview()) {
  83. return REL_DIR_FILES_TOOLS . '/css/' . DIRNAME_THEMES . '/' . $this->getThemeHandle() . '/' . $stylesheet . '?mode=preview&time=' . time();
  84. } else {
  85. return REL_DIR_FILES_TOOLS . '/css/' . DIRNAME_THEMES . '/' . $this->getThemeHandle() . '/' . $stylesheet;
  86. }
  87. }
  88. /**
  89. * Function responsible for adding header items within the context of a view.
  90. * @access private
  91. */
  92. public function addHeaderItem($item, $namespace = 'VIEW') {
  93. $this->headerItems[$namespace][] = $item;
  94. }
  95. /**
  96. * Function responsible for adding footer items within the context of a view.
  97. * @access private
  98. */
  99. public function addFooterItem($item, $namespace = 'VIEW') {
  100. $this->footerItems[$namespace][] = $item;
  101. }
  102. public function getHeaderItems() {
  103. $a1 = (is_array($this->headerItems['CORE'])) ? $this->headerItems['CORE'] : array();
  104. $a2 = (is_array($this->headerItems['VIEW'])) ? $this->headerItems['VIEW'] : array();
  105. $a3 = (is_array($this->headerItems['CONTROLLER'])) ? $this->headerItems['CONTROLLER'] : array();
  106. $items = array_merge($a1, $a2, $a3);
  107. if (version_compare(PHP_VERSION, '5.2.9', '<')) {
  108. $items = array_unique($items);
  109. } else {
  110. // stupid PHP
  111. $items = array_unique($items, SORT_STRING);
  112. }
  113. return $items;
  114. }
  115. public function getFooterItems() {
  116. $a1 = (is_array($this->footerItems['CORE'])) ? $this->footerItems['CORE'] : array();
  117. $a2 = (is_array($this->footerItems['VIEW'])) ? $this->footerItems['VIEW'] : array();
  118. $a3 = (is_array($this->footerItems['CONTROLLER'])) ? $this->footerItems['CONTROLLER'] : array();
  119. $a4 = (is_array($this->footerItems['SCRIPT'])) ? $this->footerItems['SCRIPT'] : array();
  120. $items = array_merge($a1, $a2, $a3, $a4);
  121. if (version_compare(PHP_VERSION, '5.2.9', '<')) {
  122. $items = array_unique($items);
  123. } else {
  124. // stupid PHP
  125. $items = array_unique($items, SORT_STRING);
  126. }
  127. // also strip out anything that was in the header
  128. $headerItems = $this->getHeaderItems();
  129. $retitems = array();
  130. foreach($items as $it) {
  131. if (!in_array($it, $headerItems)) {
  132. $retitems[] = $it;
  133. }
  134. }
  135. return $retitems;
  136. }
  137. /**
  138. * Function responsible for outputting header items
  139. * @access private
  140. */
  141. public function outputHeaderItems() {
  142. $items = $this->getHeaderItems();
  143. // Loop through all items
  144. // If it is a header output object, place each item in a separate array for its container directory
  145. // Otherwise, put it in the outputPost array
  146. $outputPost = array();
  147. $output = array();
  148. foreach($items as $hi) {
  149. print $hi; // caled on two seperate lines because of pre php 5.2 __toString issues
  150. print "\n";
  151. }
  152. }
  153. /**
  154. * Function responsible for outputting footer items
  155. * @access private
  156. */
  157. public function outputFooterItems() {
  158. $items = $this->getFooterItems();
  159. foreach($items as $hi) {
  160. print $hi; // caled on two seperate lines because of pre php 5.2 __toString issues
  161. print "\n";
  162. }
  163. }
  164. public function field($fieldName) {
  165. return $this->controller->field($fieldName);
  166. }
  167. /**
  168. * @access private
  169. */
  170. public function enablePreview() {
  171. $this->isPreview = true;
  172. }
  173. /**
  174. * @access private
  175. */
  176. public function isPreview() {
  177. return $this->isPreview;
  178. }
  179. /**
  180. * @access private
  181. */
  182. public function disableLinks() {
  183. $this->areLinksDisabled = true;
  184. }
  185. /**
  186. * @access private
  187. */
  188. public function enableLinks() {
  189. $this->areLinksDisabled = false;
  190. }
  191. /**
  192. * @access private
  193. */
  194. public function areLinksDisabled() {
  195. return $this->areLinksDisabled;
  196. }
  197. /**
  198. * Returns the path used to access this view
  199. * @return string $viewPath
  200. */
  201. private function getViewPath() {
  202. return $this->viewPath;
  203. }
  204. /**
  205. * Returns the handle of the currently active theme
  206. */
  207. public function getThemeHandle() { return $this->ptHandle;}
  208. /**
  209. * gets the theme include file for this particular view
  210. * @access public
  211. * @return string $theme
  212. */
  213. public function getTheme() { return $this->theme;}
  214. /**
  215. * gets the relative theme path for use in templates
  216. * @access public
  217. * @return string $themePath
  218. */
  219. public function getThemePath() { return $this->themePath; }
  220. /**
  221. * set directory of current theme for use when loading an element
  222. * @access public
  223. * @param string $path
  224. */
  225. public function setThemeDirectory($path) { $this->themeDir=$path; }
  226. /**
  227. * get directory of current theme for use when loading an element
  228. * @access public
  229. * @return string $themeDir
  230. */
  231. public function getThemeDirectory() {return $this->themeDir;}
  232. /**
  233. * used by the theme_paths and site_theme_paths files in config/ to hard coded certain paths to various themes
  234. * @access public
  235. * @param $path string
  236. * @param $theme object, if null site theme is default
  237. * @return void
  238. */
  239. public function setThemeByPath($path, $theme = NULL) {
  240. if ($theme != VIEW_CORE_THEME && $theme != 'dashboard') { // this is a hack until we figure this code out.
  241. if (is_string($theme)) {
  242. $pageTheme = PageTheme::getByHandle($theme);
  243. if(is_object($pageTheme) && $pageTheme->getThemeHandle() == $theme) { // is it the theme that's been requested?
  244. $theme = $pageTheme;
  245. }
  246. }
  247. }
  248. $this->themePaths[$path] = $theme;
  249. }
  250. /**
  251. * Returns the value of the item in the POST array.
  252. * @access public
  253. * @param $key
  254. * @return void
  255. */
  256. public function post($key) {
  257. return $this->controller->post($key);
  258. }
  259. /**
  260. * gets the collection object for the current view
  261. * @access public
  262. * @return Collection Object $c
  263. */
  264. public function getCollectionObject() {
  265. return $this->c;
  266. }
  267. /**
  268. * sets the collection object for the current view
  269. * @access public
  270. * @return void
  271. */
  272. public function setCollectionObject($c) {
  273. $this->c = $c;
  274. }
  275. /**
  276. * Includes file from the current theme path. Similar to php's include().
  277. * Files included with this function will have all variables set using $this->controller->set() in their local scope,
  278. * As well as access to all that controller's helper objects.
  279. * @access public
  280. * @param string $file
  281. * @param array $args
  282. * @return void
  283. */
  284. public function inc($file, $args = array()) {
  285. extract($args);
  286. if (isset($this->c)) {
  287. $c = $this->c;
  288. }
  289. extract($this->controller->getSets());
  290. extract($this->controller->getHelperObjects());
  291. include($this->themeDir . '/' . $file);
  292. }
  293. /**
  294. * editing is enabled true | false
  295. * @access private
  296. * @return boolean
  297. */
  298. public function editingEnabled() {
  299. return $this->isEditingEnabled;
  300. }
  301. /**
  302. * set's editing to disabled
  303. * @access private
  304. * @return void
  305. */
  306. public function disableEditing() {
  307. $this->isEditingEnabled = false;
  308. }
  309. /**
  310. * sets editing to enabled
  311. * @access private
  312. * @return void
  313. */
  314. public function enableEditing() {
  315. $this->isEditingEnabled = true;
  316. }
  317. /**
  318. * This is rarely used. We want to render another view
  319. * but keep the current controller. Views should probably not
  320. * auto-grab the controller anyway but whatever
  321. * @access private
  322. * @param object $cnt
  323. * @return void
  324. */
  325. public function setController($cnt) {
  326. $this->controller = $cnt;
  327. }
  328. /**
  329. * checks the current view to see if you're in that page's "section" (top level)
  330. * (with one exception: passing in the home page url ('' or '/') will always return false)
  331. * @access public
  332. * @param string $url
  333. * @return boolean | void
  334. */
  335. public function section($url) {
  336. $cPath = Page::getCurrentPage()->getCollectionPath();
  337. if (!empty($cPath)) {
  338. $url = '/' . trim($url, '/');
  339. if (strpos($cPath, $url) !== false && strpos($cPath, $url) == 0) {
  340. return true;
  341. }
  342. }
  343. }
  344. /**
  345. * url is a utility function that is used inside a view to setup urls w/tasks and parameters
  346. * @access public
  347. * @param string $action
  348. * @param string $task
  349. * @return string $url
  350. */
  351. public function url($action, $task = null) {
  352. $dispatcher = '';
  353. if ((!URL_REWRITING_ALL) || !defined('URL_REWRITING_ALL')) {
  354. $dispatcher = '/' . DISPATCHER_FILENAME;
  355. }
  356. $action = trim($action, '/');
  357. if ($action == '') {
  358. return DIR_REL . '/';
  359. }
  360. // if a query string appears in this variable, then we just pass it through as is
  361. if (strpos($action, '?') > -1) {
  362. return DIR_REL . $dispatcher. '/' . $action;
  363. } else {
  364. $_action = DIR_REL . $dispatcher. '/' . $action . '/';
  365. }
  366. if ($task != null) {
  367. if (ENABLE_LEGACY_CONTROLLER_URLS) {
  368. $_action .= '-/' . $task;
  369. } else {
  370. $_action .= $task;
  371. }
  372. $args = func_get_args();
  373. if (count($args) > 2) {
  374. for ($i = 2; $i < count($args); $i++){
  375. $_action .= '/' . $args[$i];
  376. }
  377. }
  378. if (strpos($_action, '?') === false) {
  379. $_action .= '/';
  380. }
  381. }
  382. return $_action;
  383. }
  384. /**
  385. * A shortcut to posting back to the current page with a task and optional parameters. Only works in the context of
  386. * @param string $action
  387. * @param string $task
  388. * @return string $url
  389. */
  390. public function action($action, $task = null) {
  391. $a = func_get_args();
  392. array_unshift($a, $this->viewPath);
  393. $ret = call_user_func_array(array($this, 'url'), $a);
  394. return $ret;
  395. }
  396. /**
  397. * render's a fata error using the built-in view. This is currently only
  398. * used when the database connection fails
  399. * @access public
  400. * @param string $title
  401. * @param string $error
  402. * @return void
  403. */
  404. public function renderError($title, $error, $errorObj = null) {
  405. $innerContent = $error;
  406. $titleContent = $title;
  407. if (!isset($this) || (!$this)) {
  408. $v = new View();
  409. $v->setThemeForView(DIRNAME_THEMES_CORE, FILENAME_THEMES_ERROR . '.php', true);
  410. include($v->getTheme());
  411. exit;
  412. }
  413. if (!isset($this->theme) || (!$this->theme) || (!file_exists($this->theme))) {
  414. $this->setThemeForView(DIRNAME_THEMES_CORE, FILENAME_THEMES_ERROR . '.php', true);
  415. include($this->theme);
  416. exit;
  417. } else {
  418. Loader::element('error_fatal', array('innerContent' => $innerContent,
  419. 'titleContent' => $titleContent));
  420. }
  421. }
  422. /**
  423. * @private
  424. */
  425. public static function defaultExceptionHandler($e) {
  426. View::renderError(t('An unexpected error occurred.'), $e->getMessage(), $e);
  427. }
  428. /**
  429. * sets the current theme
  430. * @access public
  431. * @param string $theme
  432. * @return void
  433. */
  434. public function setTheme($theme) {
  435. $this->themeOverride = $theme;
  436. }
  437. /**
  438. * set theme takes either a text-based theme ("concrete" or "dashboard" or something)
  439. * or a PageTheme object and sets information in the view about that theme. This is called internally
  440. * and is always passed the correct item based on context
  441. *
  442. * @access public
  443. * @param PageTheme object $pl
  444. * @param string $filename
  445. * @param boolean $wrapTemplateInTheme
  446. * @return void
  447. */
  448. private function setThemeForView($pl, $filename, $wrapTemplateInTheme = false) {
  449. // wrapTemplateInTheme gets set to true if we're passing the filename of a single page or page type file through
  450. $pkgID = 0;
  451. if ($pl instanceof PageTheme) {
  452. $this->ptHandle = $pl->getThemeHandle();
  453. if ($pl->getPackageID() > 0) {
  454. if (is_dir(DIR_PACKAGES . '/' . $pl->getPackageHandle())) {
  455. $dirp = DIR_PACKAGES;
  456. $url = DIR_REL;
  457. } else {
  458. $dirp = DIR_PACKAGES_CORE;
  459. $url = ASSETS_URL;
  460. }
  461. $theme = $dirp . '/' . $pl->getPackageHandle() . '/' . DIRNAME_THEMES . '/' . $pl->getThemeHandle() . '/' . $filename;
  462. if (!file_exists($theme)) {
  463. if ($wrapTemplateInTheme) {
  464. $theme = $dirp . '/' . $pl->getPackageHandle() . '/' . DIRNAME_THEMES . '/' . $pl->getThemeHandle() . '/' . FILENAME_THEMES_VIEW;
  465. } else {
  466. $theme = $dirp . '/' . $pl->getPackageHandle() . '/' . DIRNAME_THEMES . '/' . $pl->getThemeHandle() . '/' . FILENAME_THEMES_DEFAULT;
  467. }
  468. }
  469. $themeDir = $dirp . '/' . $pl->getPackageHandle() . '/' . DIRNAME_THEMES . '/' . $pl->getThemeHandle();
  470. $themePath = $url . '/' . DIRNAME_PACKAGES . '/' . $pl->getPackageHandle() . '/' . DIRNAME_THEMES . '/' . $pl->getThemeHandle();
  471. $pkgID = $pl->getPackageID();
  472. } else {
  473. if (is_dir(DIR_FILES_THEMES . '/' . $pl->getThemeHandle())) {
  474. $dir = DIR_FILES_THEMES;
  475. $themePath = DIR_REL . '/' . DIRNAME_THEMES . '/' . $pl->getThemeHandle();
  476. } else {
  477. $dir = DIR_FILES_THEMES_CORE;
  478. $themePath = ASSETS_URL . '/' . DIRNAME_THEMES . '/' . $pl->getThemeHandle();
  479. }
  480. $theme = $dir . '/' . $pl->getThemeHandle() . '/' . $filename;
  481. if (!file_exists($theme)) {
  482. if ($wrapTemplateInTheme) {
  483. $theme = $dir . '/' . $pl->getThemeHandle() . '/' . FILENAME_THEMES_VIEW;
  484. } else {
  485. $theme = $dir . '/' . $pl->getThemeHandle() . '/' . FILENAME_THEMES_DEFAULT;
  486. }
  487. }
  488. $themeDir = $dir . '/' . $pl->getThemeHandle();
  489. }
  490. } else {
  491. $this->ptHandle = $pl;
  492. if (file_exists(DIR_FILES_THEMES . '/' . $pl . '/' . $filename)) {
  493. $themePath = DIR_REL . '/' . DIRNAME_THEMES . '/' . $pl;
  494. $theme = DIR_FILES_THEMES . "/" . $pl . '/' . $filename;
  495. $themeDir = DIR_FILES_THEMES . "/" . $pl;
  496. } else if (file_exists(DIR_FILES_THEMES . '/' . $pl . '/' . FILENAME_THEMES_VIEW)) {
  497. $themePath = DIR_REL . '/' . DIRNAME_THEMES . '/' . $pl;
  498. $theme = DIR_FILES_THEMES . "/" . $pl . '/' . FILENAME_THEMES_VIEW;
  499. $themeDir = DIR_FILES_THEMES . "/" . $pl;
  500. } else if (file_exists(DIR_FILES_THEMES . '/' . DIRNAME_THEMES_CORE . '/' . $pl . '.php')) {
  501. $theme = DIR_FILES_THEMES . '/' . DIRNAME_THEMES_CORE . "/" . $pl . '.php';
  502. $themeDir = DIR_FILES_THEMES . '/' . DIRNAME_THEMES_CORE;
  503. } else if (file_exists(DIR_FILES_THEMES_CORE . "/" . $pl . '/' . $filename)) {
  504. $themePath = ASSETS_URL . '/' . DIRNAME_THEMES . '/' . DIRNAME_THEMES_CORE . '/' . $pl;
  505. $theme = DIR_FILES_THEMES_CORE . "/" . $pl . '/' . $filename;
  506. $themeDir = DIR_FILES_THEMES_CORE . "/" . $pl;
  507. } else if (file_exists(DIR_FILES_THEMES_CORE . "/" . $pl . '/' . FILENAME_THEMES_VIEW)) {
  508. $themePath = ASSETS_URL . '/' . DIRNAME_THEMES . '/' . DIRNAME_THEMES_CORE . '/' . $pl;
  509. $theme = DIR_FILES_THEMES_CORE . "/" . $pl . '/' . FILENAME_THEMES_VIEW;
  510. $themeDir = DIR_FILES_THEMES_CORE . "/" . $pl;
  511. } else if (file_exists(DIR_FILES_THEMES_CORE_ADMIN . "/" . $pl . '.php')) {
  512. $theme = DIR_FILES_THEMES_CORE_ADMIN . "/" . $pl . '.php';
  513. $themeDir = DIR_FILES_THEMES_CORE_ADMIN;
  514. }
  515. }
  516. $this->theme = $theme;
  517. $this->themePath = $themePath;
  518. $this->themeDir = $themeDir;
  519. $this->themePkgID = $pkgID;
  520. }
  521. public function escape($text){
  522. Loader::helper('text');
  523. return TextHelper::sanitize($text);
  524. }
  525. /**
  526. * render takes one argument - the item being rendered - and it can either be a path or a page object
  527. * @access public
  528. * @param string $view
  529. * @param array $args
  530. * @return void
  531. */
  532. public function render($view, $args = null) {
  533. try {
  534. if (is_array($args)) {
  535. extract($args);
  536. }
  537. // strip off a slash if there is one at the end
  538. if (is_string($view)) {
  539. if (substr($view, strlen($view) - 1) == '/') {
  540. $view = substr($view, 0, strlen($view) - 1);
  541. }
  542. }
  543. $wrapTemplateInTheme = false;
  544. Events::fire('on_start', $this);
  545. // Extract controller information from the view, and put it in the current context
  546. if (!isset($this->controller)) {
  547. $this->controller = Loader::controller($view);
  548. $this->controller->setupAndRun();
  549. }
  550. if ($this->controller->getRenderOverride() != '') {
  551. $view = $this->controller->getRenderOverride();
  552. }
  553. // Determine which inner item to load, load it, and stick it in $innerContent
  554. $content = false;
  555. ob_start();
  556. if ($view instanceof Page) {
  557. $viewPath = $view->getCollectionPath();
  558. $this->viewPath = $viewPath;
  559. $cFilename = $view->getCollectionFilename();
  560. $ctHandle = $view->getCollectionTypeHandle();
  561. $editMode = $view->isEditMode();
  562. $c = $view;
  563. $this->c = $c;
  564. // $view is a page. It can either be a SinglePage or just a Page, but we're not sure at this point, unfortunately
  565. if ($view->getCollectionTypeID() == 0 && $cFilename) {
  566. $wrapTemplateInTheme = true;
  567. if (file_exists(DIR_FILES_CONTENT. "{$cFilename}")) {
  568. $content = DIR_FILES_CONTENT. "{$cFilename}";
  569. } else if ($view->getPackageID() > 0) {
  570. $file1 = DIR_PACKAGES . '/' . $view->getPackageHandle() . '/'. DIRNAME_PAGES . $cFilename;
  571. $file2 = DIR_PACKAGES_CORE . '/' . $view->getPackageHandle() . '/'. DIRNAME_PAGES . $cFilename;
  572. if (file_exists($file1)) {
  573. $content = $file1;
  574. } else if (file_exists($file2)) {
  575. $content = $file2;
  576. }
  577. } else if (file_exists(DIR_FILES_CONTENT_REQUIRED . "{$cFilename}")) {
  578. $content = DIR_FILES_CONTENT_REQUIRED. "{$cFilename}";
  579. }
  580. $themeFilename = $c->getCollectionHandle() . '.php';
  581. } else {
  582. if (file_exists(DIR_BASE . '/' . DIRNAME_PAGE_TYPES . '/' . $ctHandle . '.php')) {
  583. $content = DIR_BASE . '/' . DIRNAME_PAGE_TYPES . '/' . $ctHandle . '.php';
  584. $wrapTemplateInTheme = true;
  585. } else if (file_exists(DIR_BASE_CORE. '/' . DIRNAME_PAGE_TYPES . '/' . $ctHandle . '.php')) {
  586. $content = DIR_BASE_CORE . '/' . DIRNAME_PAGE_TYPES . '/' . $ctHandle . '.php';
  587. $wrapTemplateInTheme = true;
  588. } else if ($view->getPackageID() > 0) {
  589. $file1 = DIR_PACKAGES . '/' . $view->getPackageHandle() . '/'. DIRNAME_PAGE_TYPES . '/' . $ctHandle . '.php';
  590. $file2 = DIR_PACKAGES_CORE . '/' . $view->getPackageHandle() . '/'. DIRNAME_PAGE_TYPES . '/' . $ctHandle . '.php';
  591. if (file_exists($file1)) {
  592. $content = $file1;
  593. $wrapTemplateInTheme = true;
  594. } else if (file_exists($file2)) {
  595. $content = $file2;
  596. $wrapTemplateInTheme = true;
  597. }
  598. }
  599. $themeFilename = $ctHandle . '.php';
  600. }
  601. } else if (is_string($view)) {
  602. // if we're passing a view but our render override is not null, that means that we're passing
  603. // a new view from within a controller. If that's the case, then we DON'T override the viewPath, we want to keep it
  604. // In order to enable editable 404 pages, other editable pages that we render without actually visiting
  605. if (defined('DB_DATABASE') && $view == '/page_not_found') {
  606. $pp = Page::getByPath($view);
  607. if (!$pp->isError()) {
  608. $this->c = $pp;
  609. }
  610. }
  611. $viewPath = $view;
  612. if ($this->controller->getRenderOverride() != '' && $this->getCollectionObject() != null) {
  613. // we are INSIDE a collection renderring a view. Which means we want to keep the viewPath that of the collection
  614. $this->viewPath = $this->getCollectionObject()->getCollectionPath();
  615. }
  616. // we're just passing something like "/login" or whatever. This will typically just be
  617. // internal Concrete stuff, but we also prepare for potentially having something in DIR_FILES_CONTENT (ie: the webroot)
  618. if (file_exists(DIR_FILES_CONTENT . "/{$view}/" . FILENAME_COLLECTION_VIEW)) {
  619. $content = DIR_FILES_CONTENT . "/{$view}/" . FILENAME_COLLECTION_VIEW;
  620. } else if (file_exists(DIR_FILES_CONTENT . "/{$view}.php")) {
  621. $content = DIR_FILES_CONTENT . "/{$view}.php";
  622. } else if (file_exists(DIR_FILES_CONTENT_REQUIRED . "/{$view}/" . FILENAME_COLLECTION_VIEW)) {
  623. $content = DIR_FILES_CONTENT_REQUIRED . "/{$view}/" . FILENAME_COLLECTION_VIEW;
  624. } else if (file_exists(DIR_FILES_CONTENT_REQUIRED . "/{$view}.php")) {
  625. $content = DIR_FILES_CONTENT_REQUIRED . "/{$view}.php";
  626. } else if ($this->getCollectionObject() != null && $this->getCollectionObject()->isGeneratedCollection() && $this->getCollectionObject()->getPackageID() > 0) {
  627. //This is a single_page associated with a package, so check the package views as well
  628. $pagePkgPath = Package::getByID($this->getCollectionObject()->getPackageID())->getPackagePath();
  629. if (file_exists($pagePkgPath . "/single_pages/{$view}/" . FILENAME_COLLECTION_VIEW)) {
  630. $content = $pagePkgPath . "/single_pages/{$view}/" . FILENAME_COLLECTION_VIEW;
  631. } else if (file_exists($pagePkgPath . "/single_pages/{$view}.php")) {
  632. $content = $pagePkgPath . "/single_pages/{$view}.php";
  633. }
  634. }
  635. $wrapTemplateInTheme = true;
  636. $themeFilename = $view . '.php';
  637. }
  638. if (is_object($this->c)) {
  639. $c = $this->c;
  640. if (is_object($db) && $view == '/page_not_found') {
  641. $view = $c;
  642. }
  643. }
  644. // Determine which outer item/theme to load
  645. // obtain theme information for this collection
  646. if (isset($this->themeOverride)) {
  647. $theme = $this->themeOverride;
  648. } else if ($this->controller->theme != false) {
  649. $theme = $this->controller->theme;
  650. } else if (($tmpTheme = $this->getThemeFromPath($viewPath)) != false) {
  651. $theme = $tmpTheme;
  652. } else if (is_object($this->c) && ($tmpTheme = $this->c->getCollectionThemeObject()) != false) {
  653. $theme = $tmpTheme;
  654. } else {
  655. $theme = FILENAME_COLLECTION_DEFAULT_THEME;
  656. }
  657. $this->setThemeForView($theme, $themeFilename, $wrapTemplateInTheme);
  658. // Now, if we're on an actual page, we retrieve all the blocks on the page
  659. // and store their view states in the local cache (for the page). That way
  660. // we can add header items and have them show up in the header BEFORE
  661. // the block itself is actually loaded
  662. if ($view instanceof Page) {
  663. $_pageBlocks = $view->getBlocks();
  664. $_pageBlocksGlobal = $view->getGlobalBlocks();
  665. $_pageBlocks = array_merge($_pageBlocks, $_pageBlocksGlobal);
  666. if ($view->supportsPageCache($_pageBlocks, $this->controller)) {
  667. $pageContent = $view->getFromPageCache();
  668. if ($pageContent != false) {
  669. Events::fire('on_before_render', $this);
  670. if (defined('APP_CHARSET')) {
  671. header("Content-Type: text/html; charset=" . APP_CHARSET);
  672. }
  673. print($pageContent);
  674. Events::fire('on_render_complete', $this);
  675. if (ob_get_level() == OB_INITIAL_LEVEL) {
  676. require(DIR_BASE_CORE . '/startup/shutdown.php');
  677. exit;
  678. }
  679. return;
  680. }
  681. }
  682. foreach($_pageBlocks as $b1) {
  683. $btc = $b1->getInstance();
  684. // now we inject any custom template CSS and JavaScript into the header
  685. if('Controller' != get_class($btc)){
  686. $btc->outputAutoHeaderItems();
  687. }
  688. $btc->runTask('on_page_view', array($view));
  689. }
  690. // do we have any custom menu plugins?
  691. $cp = new Permissions($view);
  692. if ($cp->canWrite() || $cp->canAddSubContent() || $cp->canAdminPage() || $cp->canApproveCollection()) {
  693. $ih = Loader::helper('concrete/interface/menu');
  694. $_interfaceItems = $ih->getPageHeaderMenuItems();
  695. foreach($_interfaceItems as $_im) {
  696. $_controller = $_im->getController();
  697. $_controller->outputAutoHeaderItems();
  698. }
  699. unset($_interfaceItems);
  700. unset($_im);
  701. unset($_controller);
  702. }
  703. unset($_interfaceItems);
  704. unset($_im);
  705. unset($_controller);
  706. // now, we output all the custom style records for the design tab in blocks/areas on the page
  707. $c = $this->getCollectionObject();
  708. $view->outputCustomStyleHeaderItems();
  709. }
  710. // finally, we include the theme (which was set by setTheme and will automatically include innerContent)
  711. // disconnect from our db and exit
  712. $this->controller->on_before_render();
  713. extract($this->controller->getSets());
  714. extract($this->controller->getHelperObjects());
  715. if ($content != false) {
  716. include($content);
  717. }
  718. $innerContent = ob_get_contents();
  719. if (ob_get_level() > OB_INITIAL_LEVEL) {
  720. ob_end_clean();
  721. }
  722. Events::fire('on_before_render', $this);
  723. if (defined('APP_CHARSET')) {
  724. header("Content-Type: text/html; charset=" . APP_CHARSET);
  725. }
  726. if (file_exists($this->theme)) {
  727. ob_start();
  728. include($this->theme);
  729. $pageContent = ob_get_contents();
  730. ob_end_clean();
  731. $ret = Events::fire('on_page_output', $pageContent);
  732. if($ret != '') {
  733. print $ret;
  734. } else {
  735. print $pageContent;
  736. }
  737. if ($view instanceof Page) {
  738. if ($view->supportsPageCache($_pageBlocks, $this->controller)) {
  739. $view->addToPageCache($pageContent);
  740. }
  741. }
  742. } else {
  743. throw new Exception(t('File %s not found. All themes need default.php and view.php files in them. Consult concrete5 documentation on how to create these files.', $this->theme));
  744. }
  745. Events::fire('on_render_complete', $this);
  746. if (ob_get_level() == OB_INITIAL_LEVEL) {
  747. require(DIR_BASE_CORE . '/startup/shutdown.php');
  748. exit;
  749. }
  750. } catch(ADODB_Exception $e) {
  751. // if it's a database exception we go here.
  752. if (Config::get('SITE_DEBUG_LEVEL') == DEBUG_DISPLAY_ERRORS) {
  753. $this->renderError(t('An unexpected error occurred.'), $e->getMessage(), $e);
  754. } else {
  755. $this->renderError(t('An unexpected error occurred.'), t('A database error occurred while processing this request.'), $e);
  756. }
  757. // log if setup to do so
  758. if (ENABLE_LOG_ERRORS) {
  759. $l = new Log(LOG_TYPE_EXCEPTIONS, true, true);
  760. $l->write(t('Exception Occurred: ') . $e->getMessage());
  761. $l->write($e->getTraceAsString());
  762. $l->close();
  763. }
  764. } catch (Exception $e) {
  765. $this->renderError(t('An unexpected error occurred.'), $e->getMessage(), $e);
  766. // log if setup to do so
  767. if (ENABLE_LOG_ERRORS) {
  768. $l = new Log(LOG_TYPE_EXCEPTIONS, true, true);
  769. $l->write(t('Exception Occurred: ') . $e->getMessage());
  770. $l->write($e->getTraceAsString());
  771. $l->close();
  772. }
  773. }
  774. }
  775. }