/**
	 * @return null|int
	 */
	public function pageinsertables_update(){
		$request = $this->getPageRequest();
		$view = $this->getView();
		$view->mode = View::MODE_AJAX;
		$view->contenttype = View::CTYPE_JSON;
		$view->record = false;

		// This is an ajax-only request.
		if(!$request->isAjax()){
			return View::ERROR_BADREQUEST;
		}

		$formid = $request->getPost('___formid');
		if(!$formid){
			return View::ERROR_NOTFOUND;
		}

		// Lookup that form!
		if(\Core\Session::Get('FormData/' . $formid) === null){
			return View::ERROR_NOTFOUND;
		}

		/** @var $form Form */
		$form = unserialize(\Core\Session::Get('FormData/' . $formid));

		if(!$form){
			return View::ERROR_NOTFOUND;
		}

		// Run though each element submitted and try to validate it.
		if (strtoupper($form->get('method')) == 'POST') $src =& $_POST;
		else $src =& $_GET;

		$form->loadFrom($src, true);

		// Now that the form has been loaded with the data, reinitialize the page's insertable elements.
		foreach($form->getModels() as $prefix => $model){
			if($model instanceof PageModel && $form->getElement($prefix . '[page_template]')){
				$pagetemplate = $form->getElement($prefix . '[page_template]');

				// Get all insertables currently present and remove them from the form.
				// (They will be added back shortly)
				foreach($form->getElements(true, false) as $el){
					$name = $el->get('name');
					if(strpos($name, $prefix . '[insertables]') === 0){
						$form->removeElement($name);
					}
				}

				// Now that the previous insertables are removed, update the value on the model and add the new insertables.
				// This block of logic is required because the template systems look at the last template set in the database.
				// Since what's in the databse isn't what we want here, we need to spoof it so the correct template is
				// used for retrieving form elements.
				$model->set('page_template', $pagetemplate->get('value'));

				// Set the last_template so that the traditional queries to getTemplate work without reverting back to the default template.
				$t = $model->getBaseTemplateName();

				// Allow the specific template to be overridden.
				if (($override = $model->get('page_template'))){
					$t = substr($t, 0, -4) . '/' . $override;
					$model->set('last_template', $t);
				}
				else{
					$model->set('last_template', null);
				}

				$tpl = Core\Templates\Template::Factory($model->getTemplateName());
				if($tpl){
					// My counter for which element was added last... I need this because I have "addElementAfter"...
					// so if I just kept adding the stack after a single element, they'd be in reverse order.
					// ie: stack: [a, b, c] -> {ref_el}, c, b, a
					$lastelementadded = $pagetemplate;
					$insertables = $tpl->getInsertables();
					foreach($insertables as $key => $dat){
						$type = $dat['type'];
						$dat['name'] = $prefix . '[insertables][' . $key . ']';

						// This insertable may already have content from the database... if so I want to pull that!
						$i = InsertableModel::Construct($model->get('baseurl'), $key);
						if ($i->get('value') !== null){
							$dat['value'] = $i->get('value');
						}

						$dat['class'] = 'insertable';

						$insertableelement = FormElement::Factory($type, $dat);
						$form->addElementAfter($insertableelement, $lastelementadded);
						$lastelementadded = $insertableelement;
					}
				}

				// Since there are new elements here, there may be old values that correspond to the new elements too.
				$form->loadFrom($src, true);

				// Don't forget to re-save these form updates back to the session!
				$form->persistent = true;
				$form->saveToSession();

				$view->jsondata = array(
					'status' => '1',
					'message' => 'Switched templatename successfully',
					'formid' => $form->get('uniqueid'),
				);
				return null;
			} // if($model instanceof PageModel && $form->getElement($prefix . '[page_template]'))
		} // foreach($form->getModels() as $prefix => $model)

		// Ummmm.....
		$view->jsondata = array(
			'status' => '0',
			'message' => 'No page found :/',
			'formid' => null,
		);
	}
Пример #2
0
	public function setTemplateName($templatename){

		$prefix = $this->get('name');
		if($templatename === null) $templatename = ''; // Reset back to default.

		// First of all, check and see if this is already set.
		// This will work initially because the initial value is null, and the incoming string will be "".
		if($this->_selector->get('value') === $templatename) return;

		$this->_selector->set('value', $templatename);

		// Ok, it's not.... guess I should remove any existing elements then.
		foreach($this->_elements as $k => $el){
			if($el == $this->_selector) continue; // I suppose I shouldn't remove the selector itself...

			unset($this->_elements[$k]);
		}


		if($templatename){
			// Don't forget to prepend the base template!
			$templatename = substr($this->_selector->get('templatename'), 0, -4) . '/' . $templatename;
		}
		else{
			// Default!  I still need to transpose "" to the default template afterall.
			$templatename = $this->_selector->get('templatename');
		}



		// Translate the filename to an absolute path.
		$tpl = Core\Templates\Template::ResolveFile($templatename);
		if (!$tpl) return null;

		// Scan through $tpl and find any {insertable} tag.
		$tplcontents = file_get_contents($tpl);
		preg_match_all('/\{insertable(.*)\}(.*)\{\/insertable\}/isU', $tplcontents, $matches);

		// Guess this page had no insertables.
		if (!sizeof($matches[0])){
			return;
		}
		foreach ($matches[0] as $k => $v) {
			// The contents of the {insertable ...} tag.
			$tag     = trim($matches[1][$k]);
			// The contents inside of the tags.
			$content = trim($matches[2][$k]);
			$default = $content;

			// To make this tag searchable easily, convert it to an xml element and get the attributes from that.
			$simple = new SimpleXMLElement('<insertable ' . $tag . '/>');
			$attributes = array();
			foreach($simple->attributes() as $k => $v){
				$attributes[$k] = (string)$v;
			}

			$name = $attributes['name'];
			$title = isset($attributes['title']) ? $attributes['title'] : $name;
			$type = isset($attributes['type']) ? $attributes['type'] : null; // null means an automatic type.
			if(isset($attributes['default'])) $default = $attributes['default'];


			// This insertable may already have content from the database... if so I want to pull that!
			$i = new InsertableModel($this->get('baseurl'), $name);
			if ($i->get('value') !== null) $content = $i->get('value');

			// These will be the default options for creating form elements, extend them as necessary.
			$elementoptions = array(
				'name'  => $prefix . "[$name]",
				'title' => $title,
				'value' => $content,
			);
			if(isset($attributes['description'])) $elementoptions['description'] = $attributes['description'];

			// If the type is null, try to determine the form type based on the content.
			if($type === null){
				if (strpos($default, "\n") === false && strpos($default, "<") === false) {
					$type = 'text';
				}
				elseif (preg_match('/<img(.*?)>/i', $default)) {
					$type = 'image';
				}
				else {
					$type = 'wysiwyg';
				}
			}



			// Some elements have specific options that need to be set.
			switch($type){
				case 'image':
					$type = 'file';
					$elementoptions['accept'] = 'image/*';
					$elementoptions['basedir'] = 'public/insertable';
					break;
				case 'file':
					$elementoptions['basedir'] = 'public/insertable';
					break;
				case 'select':
					$elementoptions['options'] = array_map('trim', explode('|', $attributes['options']));
					break;
			}

			// Add the actual elements now!
			$this->addElement($type, $elementoptions);
		}
	}
 /**
  * View to display a list of currently installed themes, their templates, and be able to manage
  * their templates and set them as default.
  *
  * @todo Implement an Add/Upload Theme link on this page.
  */
 public function index2()
 {
     $view = $this->getView();
     $default = ConfigHandler::Get('/theme/selected');
     $themes = array();
     $dir = ROOT_PDIR . 'themes';
     $dh = opendir($dir);
     if ($dh) {
         while (($file = readdir($dh)) !== false) {
             if ($file[0] == '.') {
                 continue;
             }
             if (!is_dir($dir . '/' . $file)) {
                 continue;
             }
             // Load up the templates for this theme.
             $templates = ThemeHandler::GetTheme($file)->getTemplates();
             $themes[] = array('name' => $file, 'default' => $default == $file, 'templates' => $templates);
         }
         closedir($dh);
     }
     // Get the page templates that have widgetareas defined within.
     $components = Core::GetComponents();
     $pagetemplates = array();
     foreach ($components as $c) {
         /** @var $c Component_2_1 */
         $dir = $c->getViewSearchDir();
         if (!$dir) {
             continue;
         }
         $dirlen = strlen($dir);
         $component = $c->getName();
         $dh = \Core\Filestore\Factory::Directory($dir);
         $pagetplfiles = $dh->ls('tpl', true);
         // not sure why getFilename(path) isn't working as expected, but this works too.
         foreach ($pagetplfiles as $obj) {
             /** @var $obj \Core\Filestore\File */
             $file = substr($obj->getFilename(), $dirlen);
             // Since this is a template, it may actually be in a different location than where the package maintainer put it.
             // ie: user template user/templates/pages/user/view.tpl may be installed to themes/myawesometheme/pages/user/view.tpl instead.
             $resolved = Core\Templates\Template::ResolveFile($file);
             $newobj = \Core\Filestore\Factory::File($resolved);
             // Check the contents of the file and see if there is a {widgetarea...} here.
             $contents = $newobj->getContents();
             if (strpos($contents, '{widgetarea') !== false) {
                 $haswidgets = true;
             } else {
                 $haswidgets = false;
             }
             $pagetemplates[] = array('file' => $file, 'resolved' => $resolved, 'obj' => $newobj, 'haswidgets' => $haswidgets, 'component' => $component);
         }
     }
     // The CSS files for the current theme and every component.
     // This is keyed by the filename so that duplicate entries don't show up more than once,
     // ie: if an asset exists in both the component and the theme.
     $cssfiles = array();
     // Give me the current theme!
     $dir = ROOT_PDIR . 'themes/' . ConfigHandler::Get('/theme/selected') . '/assets';
     $dirlen = strlen($dir);
     $component = 'Theme/' . ConfigHandler::Get('/theme/selected');
     $dh = \Core\Filestore\Factory::Directory($dir);
     $cssls = $dh->ls('css', true);
     foreach ($cssls as $obj) {
         /** @var $obj \Core\Filestore\Backends\FileLocal */
         $file = 'assets' . substr($obj->getFilename(), $dirlen);
         // Since this is a template, it may actually be in a different location than where the package maintainer put it.
         // ie: user template user/templates/pages/user/view.tpl may be installed to themes/myawesometheme/pages/user/view.tpl instead.
         $newobj = \Core\Filestore\Factory::File($file);
         $cssfiles[$file] = array('file' => $file, 'obj' => $newobj, 'component' => $component);
     }
     // And the rest of the components... I suppose
     foreach ($components as $c) {
         // Now, give me all this component's CSS files!
         $dir = $c->getAssetDir();
         if (!$dir) {
             continue;
         }
         $dirlen = strlen($dir);
         $component = $c->getName();
         $dh = \Core\Filestore\Factory::Directory($dir);
         $cssls = $dh->ls('css', true);
         // not sure why getFilename(path) isn't working as expected, but this works too.
         foreach ($cssls as $obj) {
             /** @var $obj \Core\Filestore\Backends\FileLocal */
             $file = 'assets' . substr($obj->getFilename(), $dirlen);
             // Since this is a template, it may actually be in a different location than where the package maintainer put it.
             // ie: user template user/templates/pages/user/view.tpl may be installed to themes/myawesometheme/pages/user/view.tpl instead.
             $newobj = \Core\Filestore\Factory::File($file);
             $cssfiles[$file] = array('file' => $file, 'obj' => $newobj, 'component' => $component);
         }
     }
     $view->assign('pages', $pagetemplates);
     $view->assign('css', $cssfiles);
     $view->assign('themes', $themes);
     $view->title = 'Theme Manager';
 }
Пример #4
0
/**
 * @todo Finish documentation of smarty_function_widgetarea
 * @param array  $params  Associative (and/or indexed) array of smarty parameters passed in from the template
 * @param Smarty_Internal_Template $smarty  Parent Smarty template object
 *
 * @return string|void
 */
function smarty_function_widgetarea($params, $smarty) {
	// Get all widgets set to load in this area.

	$body     = '';
	$baseurl  = PageRequest::GetSystemRequest()->getBaseURL();
	$template = $smarty->template_resource;
	$tmpl     = $smarty->getTemplateVars('__core_template');
	$topview  = ($tmpl instanceof \Core\Templates\TemplateInterface) ? $tmpl->getView() : \Core\view();

	$parameters  = [];
	$name        = null;
	$installable = null;
	$assign      = null;
	foreach($params as $k => $v){
		switch($k){
			case 'name':
				$name = $v;
				break;
			case 'installable':
				$installable = $v;
				break;
			case 'assign':
				$assign = $v;
				break;
			default:
				$parameters[$k] = $v;
				break;
		}
	}

	// I need to resolve the page template down to the base version in order for the lookup to work.
	foreach(Core\Templates\Template::GetPaths() as $base){
		if(strpos($template, $base) === 0){
			$template = substr($template, strlen($base));
			break;
		}
	}

	// Given support for page-level widgets, this logic gets slightly more difficult...
	$factory = new ModelFactory('WidgetInstanceModel');
	$factory->order('weight');
	if(Core::IsComponentAvailable('multisite') && MultiSiteHelper::IsEnabled()){
		$factory->whereGroup('or', ['site = -1', 'site = ' . MultiSiteHelper::GetCurrentSiteID()]);
	}

	$subwhere = new Core\Datamodel\DatasetWhereClause();
	$subwhere->setSeparator('OR');

	// First, the skin-level where clause.
	$skinwhere = new Core\Datamodel\DatasetWhereClause();
	$skinwhere->setSeparator('AND');
	$skinwhere->addWhere('template = ' . $template);
	$skinwhere->addWhere('widgetarea = ' . $name);
	$subwhere->addWhere($skinwhere);

	// And second, the page-level where clause.
	if($baseurl){
		$pagewhere = new Core\Datamodel\DatasetWhereClause();
		$pagewhere->setSeparator('AND');
		$pagewhere->addWhere('page_baseurl = ' . $baseurl);
		$pagewhere->addWhere('widgetarea = ' . $name);
		$subwhere->addWhere($pagewhere);
	}

	$factory->where($subwhere);


	$widgetcount = 0;
	try{
		$widgets = $factory->get();
	}
	catch(Exception $e){
		if(DEVELOPMENT_MODE){
			$body .= '<p class="message-error">Exception while trying to load widget area ' . $name . '!</p>';
			$body .= '<pre class="xdebug-var-dump">' . $e->getMessage() . '</pre>';
		}
		else{
			\Core\ErrorManagement\exception_handler($e, false);
		}
		$widgets = [];
		++$widgetcount;
	}


	foreach ($widgets as $wi) {
		/** @var $wi WidgetInstanceModel */
		// User cannot access this widget? Don't display it...
		if(!\Core\user()){
			continue;
		}
		if (!\Core\user()->checkAccess($wi->get('access'))){
			continue;
		}

		if($installable){
			$wi->set('installable', $installable);
		}
		$view = $wi->execute($parameters);

		// Some widgets may return simply a blank string.  Those should just be ignored.
		if ($view == ''){
			continue;
		}

		// If it's just a string, return that.
		if (is_string($view)) {
			$contents = $view;
		}
		elseif($view->error == View::ERROR_NOERROR){
			// Ensure that the widget's View knows it's linked to a parent!
			$view->parent = $topview;

			$contents = $view->fetch();
		}
		else{
			$contents = 'Error displaying widget [' . $wi->get('baseurl') . '], returned error [' . $view->error . ']';
		}
		++$widgetcount;
		
		// Does this widget have controls attached to it?
		$widget = $wi->getWidget();
		if($widget->controls instanceof ViewControls && $widget->controls->hasLinks()){
			$contents = '<div class="widget-controls-wrapper">' .
				'<menu id="widget-controls-' . $wi->get('id') . '">' . 
				$widget->controls->fetch() . 
				'</menu>' . 
				'</div>' .
				$contents;
		}

		$body .= '<div class="widget">' . $contents . '</div>';
	}

	// Do some sanitizing for the css data
	$class = 'widgetarea-' . strtolower(str_replace(' ', '', $name));

	$html = '<div class="widgetarea ' . $class . '" widgetarea="' . $name . '">' . $body . '</div>';

	// No widgets, no inner content!
	if($widgetcount == 0){
		$html = '';
	}

	if($assign){
		$smarty->assign($assign, $html);
	}
	else{
		return $html;
	}
}
Пример #5
0
	/**
	 * Display a listing of all widgets registered in the system.
	 */
	public function admin(){
		$view = $this->getView();
		$request = $this->getPageRequest();

		$viewer = \Core\user()->checkAccess('p:/core/widgets/manage');
		$manager = \Core\user()->checkAccess('p:/core/widgets/manage');
		if(!($viewer || $manager)){
			return View::ERROR_ACCESSDENIED;
		}

		// Build a list of create pages for all registered components.
		$components    = Core::GetComponents();
		$pages         = [];
		$skins         = [];
		$selected      = null;
		$selectedtype  = null;
		$baseurl       = null;
		$selectoptions = [];
		$links         = [];
		$theme         = ThemeHandler::GetTheme();
		$formtheme     = null;
		$formskin      = null;
		$formtemplate  = null;

		foreach($components as $c){
			/** @var Component_2_1 $c */

			$viewdir = $c->getViewSearchDir();
			if($viewdir){
				$dirlen = strlen($viewdir);
				$component = $c->getName();

				$dh = \Core\Filestore\Factory::Directory($viewdir);
				//$pagetplfiles = $dh->ls('tpl', true);
				$pagetplfiles = $dh->ls(null, true);

				// not sure why getFilename(path) isn't working as expected, but this works too.
				foreach($pagetplfiles as $obj){

					// I don't want directories.
					if($obj instanceof \Core\Filestore\Directory) continue;

					/** @var $obj \Core\Filestore\File */
					$file = substr($obj->getFilename(), $dirlen);

					// Since this is a template, it may actually be in a different location than where the package maintainer put it.
					// ie: user template user/templates/pages/user/view.tpl may be installed to themes/myawesometheme/pages/user/view.tpl instead.
					$tpl = Core\Templates\Template::Factory($file);

					if($tpl->hasWidgetAreas()){
						$pagetitle = $file;
						if(strpos($pagetitle, 'pages/') === 0){
							$pagetitle = substr($pagetitle, 6);
						}
						// Replace directory slashes with a space
						$pagetitle = str_replace(['/', '-'], ' ', $pagetitle);
						// Capitalize them
						$pagetitle = ucwords($pagetitle);
						// And trim off the ".tpl" suffix.
						$pagetitle = substr($pagetitle, 0, -4);
						$pages[$file] = $pagetitle;
					}
				}
			}

			foreach($c->getXML()->getElements('/widgets/widgetcreate') as $node){
				/** @var DOMElement $node */

				if($node->getAttribute('baseurl')){
					$nodebaseurl = $node->getAttribute('baseurl');
					$image = '';
				}
				elseif($node->getAttribute('class')){
					/** @var Widget_2_1 $obj */
					$obj = Widget_2_1::Factory($node->getAttribute('class'));
					$nodebaseurl = '/widget/create?class=' . $node->getAttribute('class');
					if($obj){
						$image = $obj->getPreviewImage();
					}
					else{
						\Core\set_message('Invalid "widgetcreate" found in ' .$node->getAttribute('class') . ', ' . $node->getAttribute('title'), 'error');
						$image = '';
					}
				}
				else{
					\Core\set_message('Invalid "widgetcreate" found in ' . $c->getName() . ', ' . $node->getAttribute('title'), 'error');
					continue;
				}

				$links[] = [
					'baseurl' => $nodebaseurl,
					'title' => $node->getAttribute('title'),
				    'preview' => $image,
				];
			}
		}

		// Build the array of skins for the current theme
		$themeskins  = $theme->getSkins();
		$defaultskin = null;
		foreach($themeskins as $dat){

			$skins[ 'skins/' . $dat['file'] ] = $dat['title'];

			if($dat['default']){
				$defaultskin = 'skins/' . $dat['file'];
			}
		}

		// Now that the various templates have been loaded into a flat array, I need to sort them.
		asort($pages);
		asort($skins);

		foreach($skins as $k => $v){
			$selectoptions[ $k ] = 'Skin: ' . $v;
		}
		foreach($pages as $k => $v){
			$selectoptions[ $k ] = 'Page: ' . $v;
		}

		if($request->getParameter('baseurl')){
			// It's a URL-specific request, lookup which template that page used last.
			$baseurl  = $request->getParameter('baseurl');
			$page     = PageModel::Construct($baseurl);

			if(!isset($pages[ $page->get('last_template') ])){
				\Core\set_message('Requested page template does not seem to contain any widget areas.', 'error');
				\Core\go_back();
			}

			$selected = $page->get('last_template');
			$selectedtype = 'url';
			$formtemplate = $selected;
		}
		elseif($request->getParameter('template')){
			$selected = $request->getParameter('template');

			if(isset($pages[ $selected ])){
				$selectedtype = 'page';
				$formtemplate = $selected;
			}
			else{
				$selectedtype = 'skin';
				$formtheme = $theme->getKeyName();
				$formskin  = $selected;
			}
		}
		else{
			// Just use the default theme skin.
			$selected = $defaultskin;
			$selectedtype = 'skin';$formtheme = $theme->getKeyName();
			$formskin  = $selected;
		}

		$template     = \Core\Templates\Template::Factory($selected);
		$areas        = $template->getWidgetAreas();
		$installables = [0 => ''];

		foreach($areas as $k => $dat){
			// Ensure that each area has a widgets array, (even if it's empty)
			$areas[$k]['widgets'] = [];
			$installables[] = $dat['installable'];
		}
		$installables = array_unique($installables);

		$factory = new ModelFactory('WidgetInstanceModel');
		$factory->order('weight');
		if(Core::IsComponentAvailable('multisite') && MultiSiteHelper::IsEnabled()){
			$factory->whereGroup('or', ['site = -1', 'site = ' . MultiSiteHelper::GetCurrentSiteID()]);
		}

		if($selectedtype == 'skin'){
			// First, the skin-level where clause.
			$skinwhere = new Core\Datamodel\DatasetWhereClause();
			$skinwhere->setSeparator('AND');
			//$skinwhere->addWhere('theme = ' . $theme->getKeyName());
			$skinwhere->addWhere('template = ' . $selected);
			$factory->where($skinwhere);
		}
		elseif($selectedtype == 'page'){
			$factory->where('template = ' . $selected);
		}
		elseif($selectedtype == 'url'){
			$factory->where('page_baseurl = ' . $baseurl);
		}
		else{
			\Core\set_message('Invalid/unknown template type', 'error');
			\Core\go_back();
		}


		foreach($factory->get() as $wi){
			/** @var $wi WidgetInstanceModel */

			$a = $wi->get('widgetarea');
			$areas[$a]['widgets'][] = $wi;
		}

		$available = WidgetModel::Find(['installable IN ' . implode(', ', $installables)]);

		/*
		$table = new Core\ListingTable\Table();
		$table->setName('/admin/widgets');
		$table->setModelName('WidgetModel');
		// Add in all the columns for this listing table.
		$table->addColumn('Title', 'title');
		if(Core::IsComponentAvailable('enterprise') && MultiSiteHelper::IsEnabled() && \Core\user()->checkAccess('g:admin')){
			$table->addColumn('Site', 'site', false);
			$ms = true;
		}
		else{
			$ms = false;
		}
		$table->getModelFactory()->where('installable IN ' . implode(', ', $installables));
		$table->addColumn('Base URL', 'baseurl');
		$table->addColumn('Installable', 'installable');
		$table->addColumn('Created', 'created');

		$table->loadFiltersFromRequest();
		*/

		$view->mastertemplate = 'admin';
		$view->title = 'All Widgets';
		//$view->assign('table', $table);
		$view->assign('available_widgets', $available);
		$view->assign('links', $links);
		$view->assign('manager', $manager);
		$view->assign('theme', $formtheme);
		$view->assign('skin', $formskin);
		$view->assign('template', $selected);
		$view->assign('page_template', $formtemplate);
		$view->assign('page_baseurl', $baseurl);
		$view->assign('options', $selectoptions);
		$view->assign('selected', $selected);
		$view->assign('areas', $areas);
		//$view->assign('multisite', $ms);
	}
Пример #6
0
	/**
	 * Fetch this view as an HTML string.
	 * @return mixed|null|string
	 */
	public function fetch() {

		if($this->_fetchCache !== null){
			// w00t ;)
			return $this->_fetchCache;
		}

		try{
			$body = $this->fetchBody();
			\Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record(
				'Fetched application content from within View->fetch() for ' . $this->templatename
			);
		}
		catch(Exception $e){
			$this->error = View::ERROR_SERVERERROR;
			\Core\ErrorManagement\exception_handler($e, ($this->mode == View::MODE_PAGE));
			$body = '';
		}


		// If there's no template, I have nothing to even do!
		if ($this->mastertemplate === false) {
			return $body;
		}
		// Else if it's null, it's just not set yet :p
		// @deprecated here!
		elseif ($this->mastertemplate === null) {
			$this->mastertemplate = ConfigHandler::Get('/theme/default_template');
		}

		// Whee!
		//var_dump($this->templatename, Core\Templates\Template::ResolveFile($this->templatename));
		// Content types take priority on controlling the master template.
		if ($this->contenttype == View::CTYPE_JSON) {
			$mastertpl = false;
		}
		else {
			// Master template depends on the render mode.
			switch ($this->mode) {
				case View::MODE_PAGEORAJAX:
					if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'){
						$mastertpl = false;
						$this->mode = View::MODE_AJAX;
					}
					else{
						$mastertpl = ROOT_PDIR . 'themes/' . ConfigHandler::Get('/theme/selected') . '/skins/' . $this->mastertemplate;
						$this->mode = View::MODE_PAGE;
					}
					break;
				case View::MODE_NOOUTPUT:
				case View::MODE_AJAX:
					$mastertpl = false;
					break;
				case View::MODE_PAGE:
				case View::MODE_EMAILORPRINT:
					$mastertpl = Core\Templates\Template::ResolveFile('skins/' . $this->mastertemplate);
					//$mastertpl = ROOT_PDIR . 'themes/' . ConfigHandler::Get('/theme/selected') . '/skins/' . $this->mastertemplate;
					break;
				case View::MODE_WIDGET:
					$mastertpl = Core\Templates\Template::ResolveFile('widgetcontainers/' . $this->mastertemplate);
					break;
			}
		}

		// If there's *still* no template, I still have nothing to do.
		if (!$mastertpl) return $body;


		$template = \Core\Templates\Template::Factory($mastertpl);
		// Ensure that the template is linked to this View correctly.
		$template->setView($this);
		//$template = new Core\Templates\Template();
		//$template->setBaseURL('/');
		// Page-level views have some special variables.
		if ($this->mode == View::MODE_PAGE) {
			$template->assign('breadcrumbs', $this->getBreadcrumbs());
			$template->assign('controls', $this->controls);
			$template->assign('messages', Core::GetMessages());

			// Tack on the pre and post body variables from the current page.
			//$body = CurrentPage::GetBodyPre() . $body . CurrentPage::GetBodyPost();
		}
		// Widgets need some special variables too.
		//if($this->mode == View::MODE_WIDGET){
		//	//var_dump($this->getVariable('widget')); die();
		//	$template->assign('widget', $this->getVariable('widget'));
		//}


		// This logic is needed for the SEO title, since that's usually completely human unfriendly.
		if(isset($this->meta['title']) && $this->meta['title']){
			$template->assign('seotitle', $this->meta['title']);
		}
		else{
			$template->assign('seotitle', $this->getTitle());
		}
		$template->assign('title', $this->getTitle());
		$template->assign('body', $body);

		// The body needs some custom classes for assisting the designers.
		// These are mainly pulled from the UA.
		$ua = \Core\UserAgent::Construct();

		$this->bodyclasses = array_merge($this->bodyclasses, $ua->getPseudoIdentifier(true));

		// Provide a way for stylesheets to target this page specifically.
		switch ($this->error) {
			case View::ERROR_BADREQUEST:
			case View::ERROR_PAYMENTREQUIRED:
			case View::ERROR_ACCESSDENIED:
			case View::ERROR_NOTFOUND:
			case View::ERROR_METHODNOTALLOWED:
			case View::ERROR_NOTACCEPTABLE:
			case View::ERROR_PROXYAUTHENTICATIONREQUIRED:
			case View::ERROR_REQUESTTIMEOUT:
			case View::ERROR_CONFLICT:
			case View::ERROR_GONE:
			case View::ERROR_LENGTHREQUIRED:
			case View::ERROR_PRECONDITIONFAILED:
			case View::ERROR_ENTITYTOOLARGE:
			case View::ERROR_URITOOLARGE:
			case View::ERROR_UNSUPPORTEDMEDIATYPE:
			case View::ERROR_RANGENOTSATISFIABLE:
			case View::ERROR_EXPECTATIONFAILED:
			case View::ERROR_UNAUTHORIZED:
				$url = 'error-' . $this->error;
				break;
			case 403:
				$url = "error-403 page-user-login";
				break;
			default:
				$url  = strtolower(trim(preg_replace('/[^a-z0-9\-]*/i', '', str_replace('/', '-', $this->baseurl)), '-'));
		}

		while($url != ''){
			$this->bodyclasses[] = 'page-' . $url;
			$url = substr($url, 0, strrpos($url, '-'));
		}


		$bodyclasses = strtolower(implode(' ', $this->bodyclasses));
		$template->assign('body_classes', $bodyclasses);

		try{
			$data = $template->fetch();
		}
		catch(SmartyException $e){
			$this->error = View::ERROR_SERVERERROR;
			error_log('[view error]');
			error_log('Template name: [' . $mastertpl . ']');
			\Core\ErrorManagement\exception_handler($e);
			require(ROOT_PDIR . 'core/templates/halt_pages/fatal_error.inc.html');
			die();
		}
		catch(TemplateException $e){
			$this->error = View::ERROR_SERVERERROR;
			error_log('[view error]');
			error_log('Template name: [' . $mastertpl . ']');
			\Core\ErrorManagement\exception_handler($e);
			require(ROOT_PDIR . 'core/templates/halt_pages/fatal_error.inc.html');
			die();
		}

		if($this->mode == View::MODE_EMAILORPRINT && $this->contenttype == View::CTYPE_HTML){
			// Inform other elements that the page is just about to be rendered.
			HookHandler::DispatchHook('/core/page/rendering', $this);

			// Replace the </head> tag with the head data from the current page
			// and the </body> with the foot data from the current page.
			// This is needed to be done at this stage because some element in
			// the template after rendering may add additional script to the head.
			// Also tack on any attributes for the <html> tag.
			if(preg_match('#</head>#i', $data)){
				// I need to do preg_replace because I only want to replace the FIRST instance of </head>
				$data = preg_replace('#</head>#i', $this->getHeadContent() . "\n" . '</head>', $data, 1);
			}
		}
		elseif ($this->mode == View::MODE_PAGE && $this->contenttype == View::CTYPE_HTML) {
			// Inform other elements that the page is just about to be rendered.
			HookHandler::DispatchHook('/core/page/rendering', $this);

			// Metadata!  w00t

			// Replace the </head> tag with the head data from the current page
			// and the </body> with the foot data from the current page.
			// This is needed to be done at this stage because some element in
			// the template after rendering may add additional script to the head.
			// Also tack on any attributes for the <html> tag.
			if(preg_match('#</head>#i', $data)){
				// I need to do preg_replace because I only want to replace the FIRST instance of </head>
				$data = preg_replace('#</head>#i', $this->getHeadContent() . "\n" . '</head>', $data, 1);
			}
			if(preg_match('#</body>#i', $data)){
				// I need to use strrpos because I only want the LAST instance of </body>
				$match = strrpos($data, '</body>');

				$foot = $this->getFootContent();

				if(defined('ENABLE_XHPROF') && function_exists('xhprof_disable')){
					require_once('xhprof_lib/utils/xhprof_lib.php'); #SKIPCOMPILER
					require_once('xhprof_lib/utils/xhprof_runs.php'); #SKIPCOMPILER
					$xhprof_data = xhprof_disable();
					$namespace = trim(str_replace(['.', '/'], '-', HOST . REL_REQUEST_PATH), '-');
					$xhprof_runs = new XHProfRuns_Default();
					$run_id = $xhprof_runs->save_run($xhprof_data, $namespace);

					define('XHPROF_RUN', $run_id);
					define('XHPROF_SOURCE', $namespace);

					$xhprof_link = sprintf(
						'<a href="' . SERVERNAME . '/xhprof/index.php?run=%s&source=%s" target="_blank">View XHprof Profiler Report</a>' . "\n",
						$run_id,
						$namespace
					);
				}
				else{
					$xhprof_link = '';
				}

				// If the viewmode is regular and DEVELOPMENT_MODE is enabled, show some possibly useful information now that everything's said and done.
				if (DEVELOPMENT_MODE) {
					$legend = '<div class="fieldset-title">%s<i class="icon-chevron-down expandable-hint"></i><i class="icon-chevron-up collapsible-hint"></i></div>' . "\n";

					$debug = '';
					$debug .= '<pre class="xdebug-var-dump screen">';
					$debug .= '<fieldset class="debug-section collapsible" id="debug-section-template-information">';
					$debug .= sprintf($legend, 'Template Information');
					$debug .= "<span>";
					$debug .= 'Base URL: ' . $this->baseurl . "\n";
					$debug .= 'Template Requested: ' . $this->templatename . "\n";
					$debug .= 'Template Actually Used: ' . \Core\Templates\Template::ResolveFile($this->templatename) . "\n";
					$debug .= 'Master Skin: ' . $this->mastertemplate . "\n";
					$debug .= "</span>";
					$debug .= '</fieldset>';

					$debug .= '<fieldset class="debug-section collapsible" id="debug-section-performance-information">';
					$debug .= sprintf($legend, 'Performance Information');
					$debug .= "<span>";
					$debug .= $xhprof_link;
					$debug .= "Database Reads: " . \Core\Utilities\Profiler\DatamodelProfiler::GetDefaultProfiler()->readCount() . "\n";
					$debug .= "Database Writes: " . \Core\Utilities\Profiler\DatamodelProfiler::GetDefaultProfiler()->writeCount() . "\n";
					//$debug .= "Number of queries: " . DB::Singleton()->counter . "\n";
					//$debug .= "Amount of memory used by PHP: " . \Core\Filestore\format_size(memory_get_usage()) . "\n";
					$debug .= "Amount of memory used by PHP: " . \Core\Filestore\format_size(memory_get_peak_usage(true)) . "\n";
					$profiler = Core\Utilities\Profiler\Profiler::GetDefaultProfiler();
					$debug .= "Total processing time: " . $profiler->getTimeFormatted() . "\n";
					$debug .= "</span>";
					$debug .= '</fieldset>';

					$debug .= '<fieldset class="debug-section collapsible" id="debug-section-profiler-information">';
					$debug .= sprintf($legend, 'Core Profiler');
					$debug .= "<span>";
					$debug .= $profiler->getEventTimesFormatted();
					$debug .= "</span>";
					$debug .= '</fieldset>';

					$debug .= '<fieldset class="debug-section collapsible collapsed" id="debug-section-components-information">';
					// Tack on what components are currently installed.
					$debug .= sprintf($legend, 'Available Components');
					$debugcomponents = array_merge(Core::GetComponents(), Core::GetDisabledComponents());
					$debug .= "<span>";
					// Give me sorting!
					ksort($debugcomponents);
					foreach ($debugcomponents as $l => $v) {
						if($v->isEnabled() && $v->isReady()){
							$debug .= '[<span style="color:green;">Enabled</span>]';
						}
						elseif($v->isEnabled() && !$v->isReady()){
							$debug .= '[<span style="color:red;">!ERROR!</span>]';
						}
						else{
							$debug .= '[<span style="color:red;">Disabled</span>]';
						}


						$debug .= $v->getName() . ' ' . $v->getVersion() . "<br/>";
					}
					$debug .= "</span>";
					$debug .= '</fieldset>';

					$debug .= '<fieldset class="debug-section collapsible collapsed" id="debug-section-hooks-information">';
					// I wanna see what hooks are registered too!
					$debug .= sprintf($legend, 'Registered Hooks');
					foreach(HookHandler::GetAllHooks() as $hook){
						$debug .= "<span>";
						/** @var $hook Hook */
						$debug .= $hook->name;
						if($hook->description) $debug .= ' <em> - ' . $hook->description . '</em>';
						$debug .= "\n" . '<span style="color:#999;">Return expected: ' . $hook->returnType . '</span>';
						$debug .= "\n" . '<span style="color:#999;">Attached by ' . $hook->getBindingCount() . ' binding(s).</span>';
						foreach($hook->getBindings() as $b){
							$debug .= "\n" . ' * ' . $b['call'];
						}
						$debug .= "\n\n";
						$debug .= "</span>";
					}
					$debug .= '</fieldset>';

					// Display the licensed content on this application
					$debug .= '<fieldset class="debug-section collapsible collapsed" id="debug-section-licenser-information">';
					$debug .= sprintf($legend, 'Licensed Information');
					$lic = \Core\Licenser::GetRaw();
					$debug .= '<div>';
					foreach($lic as $dat){
						$debug .= $dat['url'] . '::' . $dat['feature'] . ' => ' . $dat['value'] . "\n";
					}
					$debug .= '</div></fieldset>';

					$debug .= '<fieldset class="debug-section collapsible collapsed" id="debug-section-includes-information">';
					// I want to see how many files were included.
					$debug .= sprintf($legend, 'Included Files');
					$debug .= '<span>Number: ' . sizeof(get_included_files()) . "</span>";
					$debug .= '<span>'. implode("<br/>", get_included_files()) . "</span>";
					$debug .= '</fieldset>';

					$debug .= '<fieldset class="debug-section collapsible collapsed" id="debug-section-query-information">';
					$debug .= sprintf($legend, 'Query Log');
					$profiler = \Core\Utilities\Profiler\DatamodelProfiler::GetDefaultProfiler();
					$debug .= '<div>' . $profiler->getEventTimesFormatted() . '</div>';
					$debug .= '</fieldset>';

					// Display all the i18n strings available on the system.
					$debug .= '<fieldset class="debug-section collapsible collapsed" id="debug-section-i18nstrings-information">';
					$debug .= sprintf($legend, 'I18N Strings Available');
					$strings = \Core\i18n\I18NLoader::GetAllStrings();
					$debug .= '<ul>';
					foreach($strings as &$s){
						$debug .= '<li>' . $s['key'] . '</li>';
					}
					$debug .= '</ul>';
					$debug .= '</fieldset>';
					$debug .= '</pre>';

					// And append!
					$foot .= "\n" . $debug;
				}
				$data = substr_replace($data, $foot . "\n" . '</body>', $match, 7);
			}
			$data = preg_replace('#<html#', '<html ' . $this->getHTMLAttributes(), $data, 1);


			// This logic has been migrated to the {$body_classes} variable.
			/*
			if(preg_match('/<body[^>]*>/', $data, $matches)){
				// body is $matches[0].
				$fullbody = $matches[0];
				if($fullbody == '<body>'){
					$body = '<body class="' . $bodyclass . '">';
				}
				elseif(strpos($fullbody, 'class=') === false){
					// Almost as easy, other elements but no class.
					$body = substr($fullbody, 0, -1) . ' class="' . $bodyclass . '">';
				}
				else{
					// parsing HTML is far easier with XML objects.
					$node = new SimpleXMLElement($fullbody . '</body>');
					$body = '<body';
					foreach($node->attributes() as $k => $v){
						if($k == 'class'){
							$body .= ' ' . $k . '="' . $bodyclass . ' ' . $v . '"';
						}
						else{
							$body .= ' ' . $k . '="' . $v . '"';
						}
					}
					$body .= '>';
				}

				// And replace!
				$data = preg_replace('#<body[^>]*>#', $body, $data, 1);
			}
			*/
		}

		$this->_fetchCache = $data;

		return $data;
	}