3
defined('C5_EXECUTE') or die(_("Access Denied."));
8
* @author Andrew Embler <andrew@concrete5.org>
9
* @copyright Copyright (c) 2003-2008 Concrete5. (http://www.concrete5.org)
10
* @license http://www.concrete5.org/license/ MIT License
15
* A generic object that every front-end template (view) or page extends.
17
* @author Andrew Embler <andrew@concrete5.org>
19
* @copyright Copyright (c) 2003-2008 Concrete5. (http://www.concrete5.org)
20
* @license http://www.concrete5.org/license/ MIT License
23
class View extends Object {
28
* controller used by this particular view
36
* An array of items that get loaded into a page's header
38
private $headerItems = array();
41
* themePaths holds the various hard coded paths to themes
45
private $themePaths = array();
47
private $areLinksDisabled = false;
50
* editing mode is enabled or not
54
private $isEditingEnabled = true;
56
// getInstance() grabs one instance of the view w/the singleton pattern
57
public function getInstance() {
59
if (!isset($instance)) {
68
* This grabs the theme for a particular path, if one exists in the themePaths array
71
* @return string $theme
73
private function getThemeFromPath($path) {
74
// there's probably a more efficient way to do this
76
foreach($this->themePaths as $lp => $layout) {
77
if (preg_match('/^\\' . $lp . '(.*)/', $path)) {
86
* Returns a stylesheet found in a themes directory - but FIRST passes it through the tools CSS handler
87
* in order to make certain style attributes found inside editable
88
* @param string $stylesheet
90
public function getStyleSheet($stylesheet) {
91
if ($this->isPreview()) {
92
return REL_DIR_FILES_TOOLS . '/css/' . DIRNAME_THEMES . '/' . $this->getThemeHandle() . '/' . $stylesheet . '?mode=preview';
94
return REL_DIR_FILES_TOOLS . '/css/' . DIRNAME_THEMES . '/' . $this->getThemeHandle() . '/' . $stylesheet;
98
public function addHeaderItem($item, $namespace = 'VIEW') {
99
$this->headerItems[$namespace][] = $item;
102
public function outputHeaderItems() {
104
if (is_array($this->headerItems['VIEW'])) {
105
foreach($this->headerItems['VIEW'] as $hi) {
106
if (!in_array($hi, $items)) {
112
if (is_array($this->headerItems['CONTROLLER'])) {
113
foreach($this->headerItems['CONTROLLER'] as $hi) {
114
if (!in_array($hi, $items)) {
122
public function enablePreview() {
123
$this->isPreview = true;
126
public function isPreview() {
127
return $this->isPreview;
130
public function disableLinks() {
131
$this->areLinksDisabled = true;
134
public function enableLinks() {
135
$this->areLinksDisabled = false;
138
public function areLinksDisabled() {
139
return $this->areLinksDisabled;
143
* Returns the path used to access this view
144
* @return string $viewPath
146
private function getViewPath() {
147
return $this->viewPath;
151
* Returns the handle of the currently active theme
153
public function getThemeHandle() { return $this->ptHandle;}
156
* gets the theme include file for this particular view
158
* @return string $theme
160
public function getTheme() { return $this->theme;}
164
* gets the relative theme path for use in templates
166
* @return string $themePath
168
public function getThemePath() { return $this->themePath; }
172
* set directory of current theme for use when loading an element
174
* @param string $path
176
public function setThemeDirectory($path) { $this->themeDir=$path; }
179
* get directory of current theme for use when loading an element
181
* @return string $themeDir
183
public function getThemeDirectory() {return $this->themeDir;}
187
* used by the theme_paths and site_theme_paths files in config/ to hard coded certain paths to various themes
189
* @param $path string
190
* @param $theme object
193
public function setThemeByPath($path, $theme) {
194
$this->themePaths[$path] = $theme;
203
public function post($key) {
204
return $this->controller->post($key);
209
* gets the collection object for the current view
211
* @return Collection Object $c
213
public function getCollectionObject() {
218
* sets the collection object for the current view
222
public function setCollectionObject($c) {
228
* includes file from the current theme path similar to php's include()
230
* @param string $file
234
public function inc($file, $args = array()) {
236
if (isset($this->c)) {
239
extract($this->controller->getSets());
240
extract($this->controller->getHelperObjects());
241
include($this->themeDir . '/' . $file);
246
* editing is enabled true | false
250
public function editingEnabled() {
251
return $this->isEditingEnabled;
256
* set's editing to disabled
260
public function disableEditing() {
261
$this->isEditingEnabled = false;
266
* sets editing to enabled
270
public function enableEditing() {
271
$this->isEditingEnabled = true;
278
* This is rarely used. We want to render another view
279
* but keep the current controller. Views should probably not
280
* auto-grab the controller anyway but whatever
285
public function setController($cnt) {
286
$this->controller = $cnt;
291
* checks the current view to see if you're in that page's "section" (top level)
294
* @return boolean | void
296
public function section($url) {
297
if (is_object($this->c)) {
298
$cPath = $this->c->getCollectionPath();
299
if (strpos($cPath, '/' . $url) !== false && strpos($cPath, '/' . $url) == 0) {
307
* url is a utility function that is used inside a view to setup urls w/tasks and parameters
309
* @param string $action
310
* @param string $task
311
* @return string $url
313
public function url($action, $task = null) {
315
if ((!URL_REWRITING_ALL) || !defined('URL_REWRITING_ALL')) {
316
$dispatcher = '/index.php';
318
if ($action == '/') {
319
return DIR_REL . '/';
321
$_action = DIR_REL . $dispatcher. $action;
322
// remove last / if it's on there
323
if (substr($_action, strlen($_action) - 1, 1) == '/') {
324
$_action = substr($_action, 0, strlen($_action) - 1);
328
$_action .= '/-/' . $task;
329
$args = func_get_args();
330
if (count($args) > 2) {
331
for ($i = 2; $i < count($args); $i++){
332
$_action .= '/' . $args[$i];
340
* A shortcut to posting back to the current page with a task and optional parameters. Only works in the context of
341
* @param string $action
342
* @param string $task
343
* @return string $url
345
public function action($action, $task = null) {
346
$a = func_get_args();
347
array_unshift($a, $this->viewPath);
348
$ret = call_user_func_array(array($this, 'url'), $a);
354
* render's a fata error using the built-in view. This is currently only
355
* used when the database connection fails
357
* @param string $title
358
* @param string $error
361
public function renderError($title, $error, $errorObj = null) {
362
$innerContent = $error;
363
$titleContent = $title;
364
if (!isset($this->theme) || (!$this->theme)) {
365
$this->setThemeForView(DIRNAME_THEMES_CORE, FILENAME_THEMES_ERROR . '.php', true);
366
include($this->theme);
368
Loader::element('error_fatal', array('innerContent' => $innerContent,
369
'titleContent' => $titleContent));
375
* sets the current theme
377
* @param string $theme
380
public function setTheme($theme) {
381
$this->themeOverride = $theme;
385
* set theme takes either a text-based theme ("concrete" or "dashboard" or something)
386
* or a PageTheme object and sets information in the view about that theme. This is called internally
387
* and is always passed the correct item based on context
390
* @param PageTheme object $pl
391
* @param string $filename
392
* @param boolean $wrapTemplateInTheme
395
private function setThemeForView($pl, $filename, $wrapTemplateInTheme = false) {
396
// wrapTemplateInTheme gets set to true if we're passing the filename of a single page or page type file through
398
if ($pl instanceof PageTheme) {
399
$this->ptHandle = $pl->getThemeHandle();
400
if ($pl->getPackageID() > 0) {
401
if (is_dir(DIR_PACKAGES . '/' . $pl->getPackageHandle())) {
402
$dirp = DIR_PACKAGES;
405
$dirp = DIR_PACKAGES_CORE;
408
$theme = $dirp . '/' . $pl->getPackageHandle() . '/' . DIRNAME_THEMES . '/' . $pl->getThemeHandle() . '/' . $filename;
409
if (!file_exists($theme)) {
410
if ($wrapTemplateInTheme) {
411
$theme = $dirp . '/' . $pl->getPackageHandle() . '/' . DIRNAME_THEMES . '/' . $pl->getThemeHandle() . '/' . FILENAME_THEMES_VIEW;
413
$theme = $dirp . '/' . $pl->getPackageHandle() . '/' . DIRNAME_THEMES . '/' . $pl->getThemeHandle() . '/' . FILENAME_THEMES_DEFAULT;
416
$themeDir = $dirp . '/' . $pl->getPackageHandle() . '/' . DIRNAME_THEMES . '/' . $pl->getThemeHandle();
417
$themePath = $url . '/' . DIRNAME_PACKAGES . '/' . $pl->getPackageHandle() . '/' . DIRNAME_THEMES . '/' . $pl->getThemeHandle();
418
$pkgID = $pl->getPackageID();
420
if (is_dir(DIR_FILES_THEMES . '/' . $pl->getThemeHandle())) {
421
$dir = DIR_FILES_THEMES;
422
$themePath = DIR_REL . '/' . DIRNAME_THEMES . '/' . $pl->getThemeHandle();
424
$dir = DIR_FILES_THEMES_CORE;
425
$themePath = ASSETS_URL . '/' . DIRNAME_THEMES . '/' . $pl->getThemeHandle();
427
$theme = $dir . '/' . $pl->getThemeHandle() . '/' . $filename;
428
if (!file_exists($theme)) {
429
if ($wrapTemplateInTheme) {
430
$theme = $dir . '/' . $pl->getThemeHandle() . '/' . FILENAME_THEMES_VIEW;
432
$theme = $dir . '/' . $pl->getThemeHandle() . '/' . FILENAME_THEMES_DEFAULT;
435
$themeDir = $dir . '/' . $pl->getThemeHandle();
438
$this->ptHandle = $pl;
439
if (file_exists(DIR_FILES_THEMES . '/' . $pl . '/' . $filename)) {
440
$themePath = DIR_REL . '/' . DIRNAME_THEMES . '/' . $pl;
441
$theme = DIR_FILES_THEMES . "/" . $pl . '/' . $filename;
442
$themeDir = DIR_FILES_THEMES . "/" . $pl;
443
} else if (file_exists(DIR_FILES_THEMES . '/' . $pl . '/' . FILENAME_THEMES_VIEW)) {
444
$themePath = DIR_REL . '/' . DIRNAME_THEMES . '/' . $pl;
445
$theme = DIR_FILES_THEMES . "/" . $pl . '/' . FILENAME_THEMES_VIEW;
446
$themeDir = DIR_FILES_THEMES . "/" . $pl;
447
} else if (file_exists(DIR_FILES_THEMES . '/' . DIRNAME_THEMES_CORE . '/' . $pl . '.php')) {
448
$theme = DIR_FILES_THEMES . '/' . DIRNAME_THEMES_CORE . "/" . $pl . '.php';
449
$themeDir = DIR_FILES_THEMES . '/' . DIRNAME_THEMES_CORE;
450
} else if (file_exists(DIR_FILES_THEMES_CORE . "/" . $pl . '/' . $filename)) {
451
$themePath = ASSETS_URL . '/' . DIRNAME_THEMES . '/' . DIRNAME_THEMES_CORE . '/' . $pl;
452
$theme = DIR_FILES_THEMES_CORE . "/" . $pl . '/' . $filename;
453
$themeDir = DIR_FILES_THEMES_CORE . "/" . $pl;
454
} else if (file_exists(DIR_FILES_THEMES_CORE . "/" . $pl . '/' . FILENAME_THEMES_VIEW)) {
455
$themePath = ASSETS_URL . '/' . DIRNAME_THEMES . '/' . DIRNAME_THEMES_CORE . '/' . $pl;
456
$theme = DIR_FILES_THEMES_CORE . "/" . $pl . '/' . FILENAME_THEMES_VIEW;
457
$themeDir = DIR_FILES_THEMES_CORE . "/" . $pl;
458
} else if (file_exists(DIR_FILES_THEMES_CORE_ADMIN . "/" . $pl . '.php')) {
459
$theme = DIR_FILES_THEMES_CORE_ADMIN . "/" . $pl . '.php';
460
$themeDir = DIR_FILES_THEMES_CORE_ADMIN;
464
$this->theme = $theme;
465
$this->themePath = $themePath;
466
$this->themeDir = $themeDir;
467
$this->themePkgID = $pkgID;
472
* render takes one argument - the item being rendered - and it can either be a path or a page object
474
* @param string $view
478
public function render($view, $args = null) {
481
if (is_array($args)) {
485
// strip off a slash if there is one at the end
486
if (is_string($view)) {
487
if (substr($view, strlen($view) - 1) == '/') {
488
$view = substr($view, 0, strlen($view) - 1);
492
$wrapTemplateInTheme = false;
494
// Extract controller information from the view, and put it in the current context
495
if (!isset($this->controller)) {
496
$this->controller = Loader::controller($view);
497
$this->controller->setupAndRun();
500
extract($this->controller->getSets());
501
extract($this->controller->getHelperObjects());
503
// Determine which inner item to load, load it, and stick it in $innerContent
507
if ($view instanceof Page) {
509
$viewPath = $view->getCollectionPath();
510
$this->viewPath = $viewPath;
512
$cFilename = $view->getCollectionFilename();
513
$ctHandle = $view->getCollectionTypeHandle();
514
$editMode = $view->isEditMode();
518
// $view is a page. It can either be a SinglePage or just a Page, but we're not sure at this point, unfortunately
519
if ($view->getCollectionTypeID() == 0 && $cFilename) {
520
$wrapTemplateInTheme = true;
521
if (file_exists(DIR_FILES_CONTENT. "{$cFilename}")) {
522
$content = DIR_FILES_CONTENT. "{$cFilename}";
523
} else if ($view->getPackageID() > 0) {
524
$file1 = DIR_PACKAGES . '/' . $view->getPackageHandle() . '/'. DIRNAME_PAGES . $cFilename;
525
$file2 = DIR_PACKAGES_CORE . '/' . $view->getPackageHandle() . '/'. DIRNAME_PAGES . $cFilename;
526
if (file_exists($file1)) {
528
} else if (file_exists($file2)) {
531
} else if (file_exists(DIR_FILES_CONTENT_REQUIRED . "{$cFilename}")) {
532
$content = DIR_FILES_CONTENT_REQUIRED. "{$cFilename}";
535
$themeFilename = $c->getCollectionHandle() . '.php';
539
if (file_exists(DIR_BASE . '/' . DIRNAME_PAGE_TYPES . '/' . $ctHandle . '.php')) {
540
$content = DIR_BASE . '/' . DIRNAME_PAGE_TYPES . '/' . $ctHandle . '.php';
541
$wrapTemplateInTheme = true;
542
} else if (file_exists(DIR_BASE_CORE. '/' . DIRNAME_PAGE_TYPES . '/' . $ctHandle . '.php')) {
543
$content = DIR_BASE_CORE . '/' . DIRNAME_PAGE_TYPES . '/' . $ctHandle . '.php';
544
$wrapTemplateInTheme = true;
545
} else if ($view->getPackageID() > 0) {
546
$file1 = DIR_PACKAGES . '/' . $view->getPackageHandle() . '/'. DIRNAME_PAGE_TYPES . '/' . $ctHandle . '.php';
547
$file2 = DIR_PACKAGES_CORE . '/' . $view->getPackageHandle() . '/'. DIRNAME_PAGE_TYPES . '/' . $ctHandle . '.php';
548
if (file_exists($file1)) {
550
$wrapTemplateInTheme = true;
551
} else if (file_exists($file2)) {
553
$wrapTemplateInTheme = true;
557
$themeFilename = $ctHandle . '.php';
561
} else if (is_string($view)) {
563
// if we're passing a view but our render override is not null, that means that we're passing
564
// a new view from within a controller. If that's the case, then we DON'T override the viewPath, we want to keep it
567
if ($this->controller->getRenderOverride() != '' && $this->getCollectionObject() != null) {
568
// we are INSIDE a collection renderring a view. Which means we want to keep the viewPath that of the collection
569
$this->viewPath = $this->getCollectionObject()->getCollectionPath();
572
// we're just passing something like "/login" or whatever. This will typically just be
573
// internal Concrete stuff, but we also prepare for potentially having something in DIR_FILES_CONTENT (ie: the webroot)
574
if (file_exists(DIR_FILES_CONTENT . "/{$view}/" . FILENAME_COLLECTION_VIEW)) {
575
$content = DIR_FILES_CONTENT . "/{$view}/" . FILENAME_COLLECTION_VIEW;
576
} else if (file_exists(DIR_FILES_CONTENT . "/{$view}.php")) {
577
$content = DIR_FILES_CONTENT . "/{$view}.php";
578
} else if (file_exists(DIR_FILES_CONTENT_REQUIRED . "/{$view}/" . FILENAME_COLLECTION_VIEW)) {
579
$content = DIR_FILES_CONTENT_REQUIRED . "/{$view}/" . FILENAME_COLLECTION_VIEW;
580
} else if (file_exists(DIR_FILES_CONTENT_REQUIRED . "/{$view}.php")) {
581
$content = DIR_FILES_CONTENT_REQUIRED . "/{$view}.php";
583
$wrapTemplateInTheme = true;
584
$themeFilename = $view . '.php';
588
if (is_object($this->c)) {
592
// Determine which outer item/theme to load
593
// obtain theme information for this collection
594
if (isset($this->themeOverride)) {
595
$theme = $this->themeOverride;
596
} else if ($this->controller->theme != false) {
597
$theme = $this->controller->theme;
598
} else if (($tmpTheme = $this->getThemeFromPath($viewPath)) != false) {
600
} else if (is_object($this->c) && ($tmpTheme = $this->c->getCollectionThemeObject()) != false) {
603
$theme = FILENAME_COLLECTION_DEFAULT_THEME;
606
$this->setThemeForView($theme, $themeFilename, $wrapTemplateInTheme);
608
// Now, if we're on an actual page, we retrieve all the blocks on the page
609
// and store their view states in the local cache (for the page). That way
610
// we can add header items and have them show up in the header BEFORE
611
// the block itself is actually loaded
612
if ($view instanceof Page) {
613
$blocks = $view->getBlocks();
614
foreach($blocks as $b1) {
615
$btc = Loader::controller($b1);
616
$btc->runTask('on_page_view', $view);
620
// finally, we include the theme (which was set by setTheme and will automatically include innerContent)
621
// disconnect from our db and exit
622
if ($content != false) {
626
$innerContent = ob_get_contents();
628
if (ob_get_level() == (OB_INITIAL_LEVEL + 1)) {
632
Events::fire('on_before_render', $this);
634
include($this->theme);
636
Events::fire('on_render_complete', $this);
638
if (ob_get_level() == OB_INITIAL_LEVEL) {
640
require(DIR_BASE_CORE . '/startup/shutdown.php');
645
} catch(ADODB_Exception $e) {
646
// if it's a database exception we go here.
647
if (Config::get('SITE_DEBUG_LEVEL') == DEBUG_DISPLAY_ERRORS) {
648
$this->renderError(t('An unexpected error occurred.'), $e->getMessage(), $e);
650
$this->renderError(t('An unexpected error occurred.'), t('A database error occurred while processing this request.'), $e);
653
// log if setup to do so
654
if (ENABLE_LOG_ERRORS) {
655
$l = new Log(LOG_TYPE_EXCEPTIONS, true, true);
656
$l->write(t('Exception Occurred: ') . $e->getMessage());
657
$l->write($e->getTraceAsString());
660
} catch (Exception $e) {
661
$this->renderError(t('An unexpected error occurred.'), $e->getMessage(), $e);
662
// log if setup to do so
663
if (ENABLE_LOG_ERRORS) {
664
$l = new Log(LOG_TYPE_EXCEPTIONS, true, true);
665
$l->write(t('Exception Occurred: ') . $e->getMessage());
666
$l->write($e->getTraceAsString());
b'\\ No newline at end of file'