PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/concrete/core/libraries/view.php

https://bitbucket.org/selfeky/xclusivescardwebsite
PHP | 1000 lines | 548 code | 139 blank | 313 comment | 167 complexity | 5650c19522ff844b2edbccb020c2d133 MD5 | raw file
  1. <?php
  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 Concrete5_Library_View extends Object {
  21. /**
  22. * @var string
  23. */
  24. private $viewPath;
  25. /**
  26. * @var string
  27. */
  28. protected $pkgHandle;
  29. /**
  30. * @var bool
  31. */
  32. protected $disableContentInclude = false;
  33. /**
  34. * controller used by this particular view
  35. * @access public
  36. * @var object
  37. */
  38. public $controller;
  39. /**
  40. * An array of items that get loaded into a page's header
  41. * @var array
  42. */
  43. private $headerItems = array();
  44. /**
  45. * An array of items that get loaded into just before body close
  46. * @var array
  47. */
  48. private $footerItems = array();
  49. /**
  50. * themePaths holds the various hard coded paths to themes
  51. * @access private
  52. * @var array
  53. */
  54. private $themePaths = array();
  55. /**
  56. * @var bool
  57. */
  58. private $areLinksDisabled = false;
  59. /**
  60. * editing mode is enabled or not
  61. * @access private
  62. * @var boolean
  63. */
  64. private $isEditingEnabled = true;
  65. /**
  66. * getInstance() grabs one instance of the view w/the singleton pattern
  67. * @return View
  68. */
  69. public function getInstance() {
  70. static $instance;
  71. if (!isset($instance)) {
  72. $instance = new View();
  73. }
  74. return $instance;
  75. }
  76. /**
  77. * This grabs the theme for a particular path, if one exists in the themePaths array
  78. * @access private
  79. * @param string $path
  80. * @return string $theme
  81. */
  82. private function getThemeFromPath($path) {
  83. // there's probably a more efficient way to do this
  84. $theme = false;
  85. $txt = Loader::helper('text');
  86. foreach($this->themePaths as $lp => $layout) {
  87. if ($txt->fnmatch($lp, $path)) {
  88. $theme = $layout;
  89. break;
  90. }
  91. }
  92. return $theme;
  93. }
  94. /**
  95. * Returns a stylesheet found in a themes directory - but FIRST passes it through the tools CSS handler
  96. * in order to make certain style attributes found inside editable
  97. * @param string $stylesheet
  98. */
  99. public function getStyleSheet($stylesheet) {
  100. if ($this->isPreview()) {
  101. return REL_DIR_FILES_TOOLS . '/css/' . DIRNAME_THEMES . '/' . $this->getThemeHandle() . '/' . $stylesheet . '?mode=preview&time=' . time();
  102. }
  103. $pt = PageTheme::getByHandle($this->getThemeHandle());
  104. $file = $this->getThemePath() . '/' . $stylesheet;
  105. $cacheFile = DIR_FILES_CACHE . '/' . DIRNAME_CSS . '/' . $this->getThemeHandle() . '/' . $stylesheet;
  106. $env = Environment::get();
  107. $themeRec = $env->getUncachedRecord(DIRNAME_THEMES . '/' . $this->getThemeHandle() . '/' . $stylesheet, $pt->getPackageHandle());
  108. if (file_exists($cacheFile) && $themeRec->exists()) {
  109. if (filemtime($cacheFile) > filemtime($themeRec->file)) {
  110. return REL_DIR_FILES_CACHE . '/' . DIRNAME_CSS . '/' . $this->getThemeHandle() . '/' . $stylesheet;
  111. }
  112. }
  113. if ($themeRec->exists()) {
  114. $themeFile = $themeRec->file;
  115. if (!file_exists(DIR_FILES_CACHE . '/' . DIRNAME_CSS)) {
  116. @mkdir(DIR_FILES_CACHE . '/' . DIRNAME_CSS);
  117. }
  118. if (!file_exists(DIR_FILES_CACHE . '/' . DIRNAME_CSS . '/' . $this->getThemeHandle())) {
  119. @mkdir(DIR_FILES_CACHE . '/' . DIRNAME_CSS . '/' . $this->getThemeHandle());
  120. }
  121. $fh = Loader::helper('file');
  122. $stat = filemtime($themeFile);
  123. if (!file_exists(dirname($cacheFile))) {
  124. @mkdir(dirname($cacheFile), DIRECTORY_PERMISSIONS_MODE, true);
  125. }
  126. $style = $pt->parseStyleSheet($stylesheet);
  127. $r = @file_put_contents($cacheFile, $style);
  128. if ($r) {
  129. return REL_DIR_FILES_CACHE . '/' . DIRNAME_CSS . '/' . $this->getThemeHandle() . '/' . $stylesheet;
  130. } else {
  131. return $this->getThemePath() . '/' . $stylesheet;
  132. }
  133. }
  134. }
  135. /**
  136. * Function responsible for adding header items within the context of a view.
  137. * @access private
  138. */
  139. public function addHeaderItem($item, $namespace = 'VIEW') {
  140. if ($this->resolveItemConflicts($item)) {
  141. $this->headerItems[$namespace][] = $item;
  142. }
  143. }
  144. /**
  145. * Function responsible for adding footer items within the context of a view.
  146. * @access private
  147. */
  148. public function addFooterItem($item, $namespace = 'VIEW') {
  149. if ($this->resolveItemConflicts($item)) {
  150. $this->footerItems[$namespace][] = $item;
  151. }
  152. }
  153. /**
  154. * Internal helper function for addHeaderItem() and addFooterItem().
  155. * Looks through header and footer items for anything of the same type
  156. * and having the same "unique handle" as the given item.
  157. *
  158. * HOW TO USE THIS FUNCTION:
  159. * When calling this function, just pass the first $item argument
  160. * (the second optional argument is only for our own recursive use).
  161. * If we return FALSE, that means the given item should NOT be added to headerItems/footerItems.
  162. * If we return TRUE, then go ahead and add the item to headerItems/footerItems.
  163. *
  164. * NOTE: THIS FUNCTION HAS POTENTIAL SIDE-EFFECTS (IN ADDITION TO RETURN VALUE)...
  165. * ~If no duplicate is found, we return TRUE (with no side-effects).
  166. * ~If a duplicate is found and the given item has a HIGHER version than the found item,
  167. * we return TRUE **AND** we remove the found duplicate from headerItems or footerItems!!
  168. * ~If a duplicate is found and the given item does NOT have a higher version than
  169. * the found item, we return FALSE (with no side-effects).
  170. */
  171. private function resolveItemConflicts($checkItem, &$againstItems = null) {
  172. //Only check items that have "unique handles"
  173. if (empty($checkItem->handle)) {
  174. return true;
  175. }
  176. //Recursively check header items AND footer items
  177. if (is_null($againstItems)) {
  178. return ($this->resolveItemConflicts($checkItem, $this->headerItems) && $this->resolveItemConflicts($checkItem, $this->footerItems));
  179. }
  180. //Loop through all items and check for duplicates
  181. foreach ($againstItems as $itemNamespace => $namespaceItems) {
  182. foreach ($namespaceItems as $itemKey => $againstItem) {
  183. //Check the "unique handles"
  184. if (!empty($againstItem->handle) && (strtolower($checkItem->handle['handle']) == strtolower($againstItem->handle['handle']))) {
  185. //Check the item types (so js and css items can have the same handle without conflicting)
  186. //Note that we consider both the JavaScript and InlineScript items to be the same "type".
  187. $checkClass = get_class($checkItem);
  188. $againstClass = get_class($againstItem);
  189. if (($checkClass == $againstClass) || (!array_diff(array($checkClass, $againstClass), array('JavaScriptOutputObject', 'InlineScriptOutputObject')))) {
  190. //Does the given item have a higher version than the existing found item?
  191. if (version_compare($checkItem->handle['version'], $againstItem->handle['version'], '>')) {
  192. //Yes (new item is higher) so remove old item
  193. // and return true to indicate that the new item should be added.
  194. unset($againstItems[$itemNamespace][$itemKey]); // bug note: if we didn't return in the next line, this would cause problems the next time the loop iterated!
  195. return true;
  196. } else {
  197. //No (new item is not higher) so leave old item where it is
  198. // and return false to indicate that the new item should *not* be added.
  199. return false;
  200. }
  201. }
  202. }
  203. }
  204. }
  205. //No duplicates found, so return true to indicate that it's okay to add the item.
  206. return true;
  207. }
  208. /**
  209. * returns an array of string header items, typically inserted into the html <head> of a page through the header_required element
  210. * @return array
  211. */
  212. public function getHeaderItems() {
  213. //Combine items from all namespaces into one list
  214. $a1 = (is_array($this->headerItems['CORE'])) ? $this->headerItems['CORE'] : array();
  215. $a2 = (is_array($this->headerItems['VIEW'])) ? $this->headerItems['VIEW'] : array();
  216. $a3 = (is_array($this->headerItems['CONTROLLER'])) ? $this->headerItems['CONTROLLER'] : array();
  217. $items = array_merge($a1, $a2, $a3);
  218. //Remove exact string duplicates (items whose string representations are equal)
  219. if (version_compare(PHP_VERSION, '5.2.9', '<')) {
  220. $items = array_unique($items);
  221. } else {
  222. // stupid PHP (see http://php.net/array_unique#refsect1-function.array-unique-changelog )
  223. $items = array_unique($items, SORT_STRING);
  224. }
  225. return $items;
  226. }
  227. /**
  228. * returns an array of string footer items, typically inserted into the html before the close of the </body> tag of a page through the footer_required element
  229. * @return array
  230. */
  231. public function getFooterItems() {
  232. //Combine items from all namespaces into one list
  233. $a1 = (is_array($this->footerItems['CORE'])) ? $this->footerItems['CORE'] : array();
  234. $a2 = (is_array($this->footerItems['VIEW'])) ? $this->footerItems['VIEW'] : array();
  235. $a3 = (is_array($this->footerItems['CONTROLLER'])) ? $this->footerItems['CONTROLLER'] : array();
  236. $a4 = (is_array($this->footerItems['SCRIPT'])) ? $this->footerItems['SCRIPT'] : array();
  237. $items = array_merge($a1, $a2, $a3, $a4);
  238. //Remove exact string duplicates (items whose string representations are equal)
  239. if (version_compare(PHP_VERSION, '5.2.9', '<')) {
  240. $items = array_unique($items);
  241. } else {
  242. // stupid PHP (see http://php.net/array_unique#refsect1-function.array-unique-changelog )
  243. $items = array_unique($items, SORT_STRING);
  244. }
  245. //Also remove items having exact string duplicates in the header
  246. $headerItems = $this->getHeaderItems();
  247. $retItems = array();
  248. foreach($items as $it) {
  249. if (!in_array($it, $headerItems)) {
  250. $retItems[] = $it;
  251. }
  252. }
  253. return $retItems;
  254. }
  255. /**
  256. * Function responsible for outputting header items
  257. * @access private
  258. */
  259. public function outputHeaderItems() {
  260. $items = $this->getHeaderItems();
  261. // Loop through all items
  262. // If it is a header output object, place each item in a separate array for its container directory
  263. // Otherwise, put it in the outputPost array
  264. $outputPost = array();
  265. $output = array();
  266. foreach($items as $hi) {
  267. print $hi; // caled on two seperate lines because of pre php 5.2 __toString issues
  268. print "\n";
  269. }
  270. }
  271. /**
  272. * Function responsible for outputting footer items
  273. * @access private
  274. */
  275. public function outputFooterItems() {
  276. $items = $this->getFooterItems();
  277. foreach($items as $hi) {
  278. print $hi; // caled on two seperate lines because of pre php 5.2 __toString issues
  279. print "\n";
  280. }
  281. }
  282. /**
  283. * @param string
  284. * @return mixed
  285. */
  286. public function field($fieldName) {
  287. return $this->controller->field($fieldName);
  288. }
  289. /**
  290. * @access private
  291. * @return void
  292. */
  293. public function enablePreview() {
  294. $this->isPreview = true;
  295. }
  296. /**
  297. * @access private
  298. * @return bool
  299. */
  300. public function isPreview() {
  301. return $this->isPreview;
  302. }
  303. /**
  304. * @access private
  305. * @return void
  306. */
  307. public function disableLinks() {
  308. $this->areLinksDisabled = true;
  309. }
  310. /**
  311. * @access private
  312. * @return void
  313. */
  314. public function enableLinks() {
  315. $this->areLinksDisabled = false;
  316. }
  317. /**
  318. * @access private
  319. * @return bool
  320. */
  321. public function areLinksDisabled() {
  322. return $this->areLinksDisabled;
  323. }
  324. /**
  325. * Returns the path used to access this view
  326. * @return string
  327. */
  328. private function getViewPath() {
  329. return $this->viewPath;
  330. }
  331. /**
  332. * Returns the handle of the currently active theme
  333. * @return string
  334. */
  335. public function getThemeHandle() { return $this->ptHandle;}
  336. /**
  337. * gets the theme include file for this particular view
  338. * @access public
  339. * @return string $theme
  340. */
  341. public function getTheme() { return $this->theme;}
  342. /**
  343. * gets the relative theme path for use in templates
  344. * @access public
  345. * @return string $themePath
  346. */
  347. public function getThemePath() { return $this->themePath; }
  348. /**
  349. * set directory of current theme for use when loading an element
  350. * @access public
  351. * @param string $path
  352. */
  353. public function setThemeDirectory($path) { $this->themeDir=$path; }
  354. /**
  355. * get directory of current theme for use when loading an element
  356. * @access public
  357. * @return string $themeDir
  358. */
  359. public function getThemeDirectory() {return $this->themeDir;}
  360. /**
  361. * used by the theme_paths and site_theme_paths files in config/ to hard coded certain paths to various themes
  362. * @access public
  363. * @param $path string
  364. * @param $theme object, if null site theme is default
  365. * @return void
  366. */
  367. public function setThemeByPath($path, $theme = NULL) {
  368. if ($theme != VIEW_CORE_THEME && $theme != 'dashboard') { // this is a hack until we figure this code out.
  369. if (is_string($theme)) {
  370. $pageTheme = PageTheme::getByHandle($theme);
  371. if(is_object($pageTheme) && $pageTheme->getThemeHandle() == $theme) { // is it the theme that's been requested?
  372. $theme = $pageTheme;
  373. }
  374. }
  375. }
  376. $this->themePaths[$path] = $theme;
  377. }
  378. /**
  379. * Returns the value of the item in the POST array.
  380. * @access public
  381. * @param $key
  382. * @return void
  383. */
  384. public function post($key) {
  385. return $this->controller->post($key);
  386. }
  387. /**
  388. * gets the collection object for the current view
  389. * @access public
  390. * @return Collection Object $c
  391. */
  392. public function getCollectionObject() {
  393. return $this->c;
  394. }
  395. /**
  396. * sets the collection object for the current view
  397. * @access public
  398. * @return void
  399. */
  400. public function setCollectionObject($c) {
  401. $this->c = $c;
  402. }
  403. /**
  404. * Includes file from the current theme path. Similar to php's include().
  405. * Files included with this function will have all variables set using $this->controller->set() in their local scope,
  406. * As well as access to all that controller's helper objects.
  407. * @access public
  408. * @param string $file
  409. * @param array $args
  410. * @return void
  411. */
  412. public function inc($file, $args = array()) {
  413. extract($args);
  414. if (isset($this->c)) {
  415. $c = $this->c;
  416. }
  417. extract($this->controller->getSets());
  418. extract($this->controller->getHelperObjects());
  419. $env = Environment::get();
  420. include($env->getPath(DIRNAME_THEMES . '/' . $this->getThemeHandle() . '/' . $file, $this->pkgHandle));
  421. }
  422. /**
  423. * editing is enabled true | false
  424. * @access private
  425. * @return boolean
  426. */
  427. public function editingEnabled() {
  428. return $this->isEditingEnabled;
  429. }
  430. /**
  431. * set's editing to disabled
  432. * @access private
  433. * @return void
  434. */
  435. public function disableEditing() {
  436. $this->isEditingEnabled = false;
  437. }
  438. /**
  439. * sets editing to enabled
  440. * @access private
  441. * @return void
  442. */
  443. public function enableEditing() {
  444. $this->isEditingEnabled = true;
  445. }
  446. /**
  447. * This is rarely used. We want to render another view
  448. * but keep the current controller. Views should probably not
  449. * auto-grab the controller anyway but whatever
  450. * @access private
  451. * @param object $cnt
  452. * @return void
  453. */
  454. public function setController($cnt) {
  455. $this->controller = $cnt;
  456. }
  457. /**
  458. * checks the current view to see if you're in that page's "section" (top level)
  459. * (with one exception: passing in the home page url ('' or '/') will always return false)
  460. * @access public
  461. * @param string $url
  462. * @return boolean | void
  463. */
  464. public function section($url) {
  465. $cPath = Page::getCurrentPage()->getCollectionPath();
  466. if (!empty($cPath)) {
  467. $url = '/' . trim($url, '/');
  468. if (strpos($cPath, $url) !== false && strpos($cPath, $url) == 0) {
  469. return true;
  470. }
  471. }
  472. }
  473. /**
  474. * url is a utility function that is used inside a view to setup urls w/tasks and parameters
  475. * @access public
  476. * @param string $action
  477. * @param string $task
  478. * @return string $url
  479. */
  480. public function url($action, $task = null) {
  481. $dispatcher = '';
  482. if ((!URL_REWRITING_ALL) || !defined('URL_REWRITING_ALL')) {
  483. $dispatcher = '/' . DISPATCHER_FILENAME;
  484. }
  485. $action = trim($action, '/');
  486. if ($action == '') {
  487. return DIR_REL . '/';
  488. }
  489. // if a query string appears in this variable, then we just pass it through as is
  490. if (strpos($action, '?') > -1) {
  491. return DIR_REL . $dispatcher. '/' . $action;
  492. } else {
  493. $_action = DIR_REL . $dispatcher. '/' . $action . '/';
  494. }
  495. if ($task != null) {
  496. if (ENABLE_LEGACY_CONTROLLER_URLS) {
  497. $_action .= '-/' . $task;
  498. } else {
  499. $_action .= $task;
  500. }
  501. $args = func_get_args();
  502. if (count($args) > 2) {
  503. for ($i = 2; $i < count($args); $i++){
  504. $_action .= '/' . $args[$i];
  505. }
  506. }
  507. if (strpos($_action, '?') === false) {
  508. $_action .= '/';
  509. }
  510. }
  511. return $_action;
  512. }
  513. public function checkMobileView() {
  514. if(isset($_COOKIE['ccmDisableMobileView']) && $_COOKIE['ccmDisableMobileView'] == true) {
  515. define('MOBILE_THEME_IS_ACTIVE', false);
  516. return false; // break out if we've said we don't want the mobile theme
  517. }
  518. $page = Page::getCurrentPage();
  519. if($page instanceof Page && $page->isAdminArea()) {
  520. define('MOBILE_THEME_IS_ACTIVE', false);
  521. return false; // no mobile theme for the dashboard
  522. }
  523. Loader::library('3rdparty/mobile_detect');
  524. $md = new Mobile_Detect();
  525. if ($md->isMobile()) {
  526. $themeId = Config::get('MOBILE_THEME_ID');
  527. if ($themeId > 0) {
  528. $mobileTheme = PageTheme::getByID($themeId);
  529. if($mobileTheme instanceof PageTheme) {
  530. define('MOBILE_THEME_IS_ACTIVE',true);
  531. // we have to grab the instance of the view
  532. // since on_page_view doesn't give it to us
  533. $this->setTheme($mobileTheme);
  534. }
  535. }
  536. }
  537. if (!defined('MOBILE_THEME_IS_ACTIVE')) {
  538. define('MOBILE_THEME_IS_ACTIVE', false);
  539. }
  540. }
  541. /**
  542. * A shortcut to posting back to the current page with a task and optional parameters. Only works in the context of
  543. * @param string $action
  544. * @param string $task
  545. * @return string $url
  546. */
  547. public function action($action, $task = null) {
  548. $a = func_get_args();
  549. array_unshift($a, $this->viewPath);
  550. $ret = call_user_func_array(array($this, 'url'), $a);
  551. return $ret;
  552. }
  553. /**
  554. * render's a fata error using the built-in view. This is currently only
  555. * used when the database connection fails
  556. * @access public
  557. * @param string $title
  558. * @param string $error
  559. * @return void
  560. */
  561. public function renderError($title, $error, $errorObj = null) {
  562. $innerContent = $error;
  563. $titleContent = $title;
  564. header('HTTP/1.1 500 Internal Server Error');
  565. if (!isset($this) || (!$this)) {
  566. $v = new View();
  567. $v->setThemeForView(DIRNAME_THEMES_CORE, FILENAME_THEMES_ERROR . '.php', true);
  568. include($v->getTheme());
  569. exit;
  570. }
  571. if (!isset($this->theme) || (!$this->theme) || (!file_exists($this->theme))) {
  572. $this->setThemeForView(DIRNAME_THEMES_CORE, FILENAME_THEMES_ERROR . '.php', true);
  573. include($this->theme);
  574. exit;
  575. } else {
  576. Loader::element('error_fatal', array('innerContent' => $innerContent,
  577. 'titleContent' => $titleContent));
  578. }
  579. }
  580. /**
  581. * sets the current theme
  582. * @access public
  583. * @param string $theme
  584. * @return void
  585. */
  586. public function setTheme($theme) {
  587. $this->themeOverride = $theme;
  588. }
  589. /**
  590. * set theme takes either a text-based theme ("concrete" or "dashboard" or something)
  591. * or a PageTheme object and sets information in the view about that theme. This is called internally
  592. * and is always passed the correct item based on context
  593. *
  594. * @access protected
  595. * @param PageTheme object $pl
  596. * @param string $filename
  597. * @param boolean $wrapTemplateInTheme
  598. * @return void
  599. */
  600. protected function setThemeForView($pl, $filename, $wrapTemplateInTheme = false) {
  601. // wrapTemplateInTheme gets set to true if we're passing the filename of a single page or page type file through
  602. $pkgID = 0;
  603. $env = Environment::get();
  604. if ($pl instanceof PageTheme) {
  605. $this->ptHandle = $pl->getThemeHandle();
  606. if ($pl->getPackageID() > 0) {
  607. $pkgID = $pl->getPackageID();
  608. $this->pkgHandle = $pl->getPackageHandle();
  609. }
  610. $rec = $env->getRecord(DIRNAME_THEMES . '/' . $pl->getThemeHandle() . '/' . $filename, $this->pkgHandle);
  611. if (!$rec->exists()) {
  612. if ($wrapTemplateInTheme) {
  613. $theme = $env->getPath(DIRNAME_THEMES . '/' . $pl->getThemeHandle() . '/' . FILENAME_THEMES_VIEW, $this->pkgHandle);
  614. } else {
  615. $theme = $env->getPath(DIRNAME_THEMES . '/' . $pl->getThemeHandle() . '/' . FILENAME_THEMES_DEFAULT, $this->pkgHandle);
  616. }
  617. } else {
  618. $theme = $rec->file;
  619. $this->disableContentInclude = true;
  620. }
  621. $themeDir = str_replace('/' . FILENAME_THEMES_DEFAULT, '', $env->getPath(DIRNAME_THEMES . '/' . $pl->getThemeHandle() . '/' . FILENAME_THEMES_DEFAULT, $this->pkgHandle));
  622. $themePath = str_replace('/' . FILENAME_THEMES_DEFAULT, '', $env->getURL(DIRNAME_THEMES . '/' . $pl->getThemeHandle() . '/' . FILENAME_THEMES_DEFAULT, $this->pkgHandle));
  623. } else {
  624. $this->ptHandle = $pl;
  625. if (file_exists(DIR_FILES_THEMES . '/' . $pl . '/' . $filename)) {
  626. $themePath = DIR_REL . '/' . DIRNAME_THEMES . '/' . $pl;
  627. $theme = DIR_FILES_THEMES . "/" . $pl . '/' . $filename;
  628. $themeDir = DIR_FILES_THEMES . "/" . $pl;
  629. } else if (file_exists(DIR_FILES_THEMES . '/' . $pl . '/' . FILENAME_THEMES_VIEW)) {
  630. $themePath = DIR_REL . '/' . DIRNAME_THEMES . '/' . $pl;
  631. $theme = DIR_FILES_THEMES . "/" . $pl . '/' . FILENAME_THEMES_VIEW;
  632. $themeDir = DIR_FILES_THEMES . "/" . $pl;
  633. } else if (file_exists(DIR_FILES_THEMES . '/' . DIRNAME_THEMES_CORE . '/' . $pl . '.php')) {
  634. $theme = DIR_FILES_THEMES . '/' . DIRNAME_THEMES_CORE . "/" . $pl . '.php';
  635. $themeDir = DIR_FILES_THEMES . '/' . DIRNAME_THEMES_CORE;
  636. } else if (file_exists(DIR_FILES_THEMES_CORE . "/" . $pl . '/' . $filename)) {
  637. $themePath = ASSETS_URL . '/' . DIRNAME_THEMES . '/' . DIRNAME_THEMES_CORE . '/' . $pl;
  638. $theme = DIR_FILES_THEMES_CORE . "/" . $pl . '/' . $filename;
  639. $themeDir = DIR_FILES_THEMES_CORE . "/" . $pl;
  640. } else if (file_exists(DIR_FILES_THEMES_CORE . "/" . $pl . '/' . FILENAME_THEMES_VIEW)) {
  641. $themePath = ASSETS_URL . '/' . DIRNAME_THEMES . '/' . DIRNAME_THEMES_CORE . '/' . $pl;
  642. $theme = DIR_FILES_THEMES_CORE . "/" . $pl . '/' . FILENAME_THEMES_VIEW;
  643. $themeDir = DIR_FILES_THEMES_CORE . "/" . $pl;
  644. } else if (file_exists(DIR_FILES_THEMES_CORE_ADMIN . "/" . $pl . '.php')) {
  645. $theme = DIR_FILES_THEMES_CORE_ADMIN . "/" . $pl . '.php';
  646. $themeDir = DIR_FILES_THEMES_CORE_ADMIN;
  647. }
  648. }
  649. $this->theme = $theme;
  650. $this->themePath = $themePath;
  651. $this->themeDir = $themeDir;
  652. $this->themePkgID = $pkgID;
  653. }
  654. public function escape($text){
  655. Loader::helper('text');
  656. return TextHelper::sanitize($text);
  657. }
  658. /**
  659. * render takes one argument - the item being rendered - and it can either be a path or a page object
  660. * @access public
  661. * @param string $view
  662. * @param array $args
  663. * @return void
  664. */
  665. public function render($view, $args = null) {
  666. if (is_array($args)) {
  667. extract($args);
  668. }
  669. // strip off a slash if there is one at the end
  670. if (is_string($view)) {
  671. if (substr($view, strlen($view) - 1) == '/') {
  672. $view = substr($view, 0, strlen($view) - 1);
  673. }
  674. }
  675. $dsh = Loader::helper('concrete/dashboard');
  676. $wrapTemplateInTheme = false;
  677. $this->checkMobileView();
  678. if (defined('DB_DATABASE') && ($view !== '/upgrade')) {
  679. Events::fire('on_start', $this);
  680. }
  681. // Extract controller information from the view, and put it in the current context
  682. if (!isset($this->controller)) {
  683. $this->controller = Loader::controller($view);
  684. $this->controller->setupAndRun();
  685. }
  686. if ($this->controller->getRenderOverride() != '') {
  687. $view = $this->controller->getRenderOverride();
  688. }
  689. // Determine which inner item to load, load it, and stick it in $innerContent
  690. $content = false;
  691. ob_start();
  692. if ($view instanceof Page) {
  693. $_pageBlocks = $view->getBlocks();
  694. if (!$dsh->inDashboard()) {
  695. $_pageBlocksGlobal = $view->getGlobalBlocks();
  696. $_pageBlocks = array_merge($_pageBlocks, $_pageBlocksGlobal);
  697. }
  698. // do we have any custom menu plugins?
  699. $cp = new Permissions($view);
  700. if ($cp->canViewToolbar()) {
  701. $ih = Loader::helper('concrete/interface/menu');
  702. $_interfaceItems = $ih->getPageHeaderMenuItems();
  703. foreach($_interfaceItems as $_im) {
  704. $_controller = $_im->getController();
  705. $_controller->outputAutoHeaderItems();
  706. }
  707. unset($_interfaceItems);
  708. unset($_im);
  709. unset($_controller);
  710. }
  711. unset($_interfaceItems);
  712. unset($_im);
  713. unset($_controller);
  714. // now, we output all the custom style records for the design tab in blocks/areas on the page
  715. $c = $this->getCollectionObject();
  716. $view->outputCustomStyleHeaderItems();
  717. $viewPath = $view->getCollectionPath();
  718. $this->viewPath = $viewPath;
  719. $cFilename = $view->getCollectionFilename();
  720. $ctHandle = $view->getCollectionTypeHandle();
  721. $editMode = $view->isEditMode();
  722. $c = $view;
  723. $this->c = $c;
  724. $env = Environment::get();
  725. // $view is a page. It can either be a SinglePage or just a Page, but we're not sure at this point, unfortunately
  726. if ($view->getCollectionTypeID() == 0 && $cFilename) {
  727. $wrapTemplateInTheme = true;
  728. $cFilename = trim($cFilename, '/');
  729. $content = $env->getPath(DIRNAME_PAGES . '/' . $cFilename, $view->getPackageHandle());
  730. $themeFilename = $c->getCollectionHandle() . '.php';
  731. } else {
  732. $rec = $env->getRecord(DIRNAME_PAGE_TYPES . '/' . $ctHandle . '.php', $view->getPackageHandle());
  733. if ($rec->exists()) {
  734. $wrapTemplateInTheme = true;
  735. $content = $rec->file;
  736. }
  737. $themeFilename = $ctHandle . '.php';
  738. }
  739. } else if (is_string($view)) {
  740. // if we're passing a view but our render override is not null, that means that we're passing
  741. // a new view from within a controller. If that's the case, then we DON'T override the viewPath, we want to keep it
  742. // In order to enable editable 404 pages, other editable pages that we render without actually visiting
  743. if (defined('DB_DATABASE') && $view == '/page_not_found') {
  744. $pp = Page::getByPath($view);
  745. if (!$pp->isError()) {
  746. $this->c = $pp;
  747. }
  748. }
  749. $viewPath = $view;
  750. if ($this->controller->getRenderOverride() != '' && $this->getCollectionObject() != null) {
  751. // we are INSIDE a collection renderring a view. Which means we want to keep the viewPath that of the collection
  752. $this->viewPath = $this->getCollectionObject()->getCollectionPath();
  753. }
  754. // we're just passing something like "/login" or whatever. This will typically just be
  755. // internal Concrete stuff, but we also prepare for potentially having something in DIR_FILES_CONTENT (ie: the webroot)
  756. if (file_exists(DIR_FILES_CONTENT . "/{$view}/" . FILENAME_COLLECTION_VIEW)) {
  757. $content = DIR_FILES_CONTENT . "/{$view}/" . FILENAME_COLLECTION_VIEW;
  758. } else if (file_exists(DIR_FILES_CONTENT . "/{$view}.php")) {
  759. $content = DIR_FILES_CONTENT . "/{$view}.php";
  760. } else if (file_exists(DIR_FILES_CONTENT_REQUIRED . "/{$view}/" . FILENAME_COLLECTION_VIEW)) {
  761. $content = DIR_FILES_CONTENT_REQUIRED . "/{$view}/" . FILENAME_COLLECTION_VIEW;
  762. } else if (file_exists(DIR_FILES_CONTENT_REQUIRED . "/{$view}.php")) {
  763. $content = DIR_FILES_CONTENT_REQUIRED . "/{$view}.php";
  764. } else if ($this->getCollectionObject() != null && $this->getCollectionObject()->isGeneratedCollection() && $this->getCollectionObject()->getPackageID() > 0) {
  765. //This is a single_page associated with a package, so check the package views as well
  766. $pagePkgPath = Package::getByID($this->getCollectionObject()->getPackageID())->getPackagePath();
  767. if (file_exists($pagePkgPath . "/single_pages/{$view}/" . FILENAME_COLLECTION_VIEW)) {
  768. $content = $pagePkgPath . "/single_pages/{$view}/" . FILENAME_COLLECTION_VIEW;
  769. } else if (file_exists($pagePkgPath . "/single_pages/{$view}.php")) {
  770. $content = $pagePkgPath . "/single_pages/{$view}.php";
  771. }
  772. }
  773. $wrapTemplateInTheme = true;
  774. $themeFilename = $view . '.php';
  775. }
  776. if (is_object($this->c)) {
  777. $c = $this->c;
  778. if (defined('DB_DATABASE') && ($view == '/page_not_found' || $view == '/login')) {
  779. $view = $c;
  780. $req = Request::get();
  781. $req->setCurrentPage($c);
  782. $_pageBlocks = $view->getBlocks();
  783. $_pageBlocksGlobal = $view->getGlobalBlocks();
  784. $_pageBlocks = array_merge($_pageBlocks, $_pageBlocksGlobal);
  785. }
  786. }
  787. if (is_array($_pageBlocks)) {
  788. foreach($_pageBlocks as $b1) {
  789. $b1p = new Permissions($b1);
  790. if ($b1p->canRead()) {
  791. $btc = $b1->getInstance();
  792. // now we inject any custom template CSS and JavaScript into the header
  793. if('Controller' != get_class($btc)){
  794. $btc->outputAutoHeaderItems();
  795. }
  796. $btc->runTask('on_page_view', array($view));
  797. }
  798. }
  799. }
  800. // Determine which outer item/theme to load
  801. // obtain theme information for this collection
  802. if (isset($this->themeOverride)) {
  803. $theme = $this->themeOverride;
  804. } else if ($this->controller->theme != false) {
  805. $theme = $this->controller->theme;
  806. } else if (($tmpTheme = $this->getThemeFromPath($viewPath)) != false) {
  807. $theme = $tmpTheme;
  808. } else if (is_object($this->c) && ($tmpTheme = $this->c->getCollectionThemeObject()) != false) {
  809. $theme = $tmpTheme;
  810. } else {
  811. $theme = FILENAME_COLLECTION_DEFAULT_THEME;
  812. }
  813. $this->setThemeForView($theme, $themeFilename, $wrapTemplateInTheme);
  814. // finally, we include the theme (which was set by setTheme and will automatically include innerContent)
  815. // disconnect from our db and exit
  816. $this->controller->on_before_render();
  817. extract($this->controller->getSets());
  818. extract($this->controller->getHelperObjects());
  819. if ($content != false && (!$this->disableContentInclude)) {
  820. include($content);
  821. }
  822. $innerContent = ob_get_contents();
  823. if (ob_get_level() > OB_INITIAL_LEVEL) {
  824. ob_end_clean();
  825. }
  826. Events::fire('on_before_render', $this);
  827. if (defined('APP_CHARSET')) {
  828. header("Content-Type: text/html; charset=" . APP_CHARSET);
  829. }
  830. if (file_exists($this->theme)) {
  831. $cache = PageCache::getLibrary();
  832. $shouldAddToCache = $cache->shouldAddToCache($this);
  833. if ($shouldAddToCache) {
  834. $cache->outputCacheHeaders($c);
  835. }
  836. ob_start();
  837. include($this->theme);
  838. $pageContent = ob_get_contents();
  839. ob_end_clean();
  840. $ret = Events::fire('on_page_output', $pageContent);
  841. if($ret != '') {
  842. print $ret;
  843. $pageContent = $ret;
  844. } else {
  845. print $pageContent;
  846. }
  847. $cache = PageCache::getLibrary();
  848. if ($shouldAddToCache) {
  849. $cache->set($c, $pageContent);
  850. }
  851. } else {
  852. 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));
  853. }
  854. Events::fire('on_render_complete', $this);
  855. if (ob_get_level() == OB_INITIAL_LEVEL) {
  856. require(DIR_BASE_CORE . '/startup/jobs.php');
  857. require(DIR_BASE_CORE . '/startup/shutdown.php');
  858. exit;
  859. }
  860. }
  861. }