<?php

/**
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @package clearFusionCMS
 * @copyright Copyright (c) 2012 - 2016 clearFusionCMS. All rights reserved.
 * @link http://clearfusioncms.com
 */

/**
 * cfBackendElementsAction Class
 *
 * The action class to manage page elements, chunks, snippets, templates.
 *
 * @package clearFusionCMS
 */
class cfBackendElementsAction extends cfBackendControllerAction {

	/**
	 * Track the active element type.
	 *
	 * @var string
	 */
	protected $activeType = '';

	/**
	 * Track the active item
	 *
	 * @var string
	 */
	protected $activeItem ='';

	/**
	 * Build the chunk tree.
	 *
	 * @param flWidgetTreeView $tree The treeview to add to.
	 * @param flTemplateManger $mgr Manager object.
	 * @param int $themeId The ID of the theme to build the tree for.
	 * @param bool $templates true for template chunks, false for normal chunks.
	 */
	function buildChunkTree($tree, $mgr, $themeId, $templates) {
		$active = $this->activeType == 'template' && $templates ? $this->activeItem :
			($this->activeType == 'chunk' && !$templates ? $this->activeItem : '');

		if($mgr->getChunkCount($themeId, $templates)) {
			$chunks = new flWidgetTreeView();

			// Build the categories with snippets
			$cats = $mgr->getCategories();
			foreach($cats as $catID => $catName) {
				$sl = $mgr->getChunksInCat($themeId, $catID, $templates);
				if(count($sl)) {
					$st = new flWidgetTreeView();
					foreach($sl as $id => $name)
						$st->addNode(new flWidgetTreeLinkNode($name, $this->assemble('', 'elements', array($templates ? 'template' : 'chunk', $id)), $templates ? flWidgetTreeLinkNode::TEMPLATE : flWidgetTreeNode::CHUNK))
						->setLastNodeActive($active == $id);
					$chunks->addNode(new flWidgetTreeChildNode($catName, $st, false));
				}
			}

			$sl = $mgr->getChunksInCat($themeId, 0, $templates);
			foreach($sl as $id => $name)
				$chunks->addNode(new flWidgetTreeLinkNode($name, $this->assemble('', 'elements', array($templates ? 'template' : 'chunk', $id)), $templates ? flWidgetTreeLinkNode::TEMPLATE : flWidgetTreeNode::CHUNK))
				->setLastNodeActive($active == $id);

			$tree->addNode(new flWidgetTreeChildNode($templates ? __('Templates', 'cfBackend') : __('Chunks', 'cfBackend'), $chunks, false));
		}
		else
			$tree->addNode(new flWidgetTreeNode($templates ? __('Templates', 'cfBackend') : __('Chunks', 'cfBackend'), flWidgetTreeNode::CONTAINER));
	}

	/**
	 * Process the page.
	 */
	function process() {

		$this->testPermission('cfContent.elements');
		$this->breadcrumbTrail->addCrumb(__('Elements', 'cfBackend'), $this->assemble('', 'elements'));

		$mgr = new flTemplateManager($this->db);
		$fields = new cfCustomFields($this->db);
		$tasks = new cfTaskSnippets($this->db);

		$content = '';

		// Build routes
		$edCat = new flRouterRoute(':cmd/:id', array('id' => '0'), array('cmd' => '(theme|category)', 'id' => '\d+'));
		$delCat = new flRouterRoute(':cmd/:id', array(), array('cmd' => '(deltheme|delcategory)', 'id' => '\d+'));
		$snippetRoute = new flRouterRoute('snippet/:id', array('id' => '0'), array('id' => '\d+'));
		$delSnippetRoute = new flRouterRoute('delsnippet/:id', array(), array('id' => '\d+'));
		$chunkRoute = new flRouterRoute(':cmd/:id', array('id' => '0'), array('cmd' => '(chunk|template)', 'id' => '\d+'));
		$delChunkRoute = new flRouterRoute('delchunk/:id', array(), array('id' => '\d+'));
		$fieldRoute = new flRouterRoute('field/:id/view', array('id' => '0'), array('id' => '[a-zA-Z0-9_\.]+'));
		$delFieldRoute = new flRouterRoute('field/:id/delete', array(), array('id' => '[a-zA-Z0-9_\.]+'));
		$taskRoute = new flRouterRoute('task/:id', array('id' => '0'), array('id' => '\d+'));
		$delTaskRoute = new flRouterRoute('deltask/:id', array(), array('id' => '\d+'));

		// Add/edit snippet
		if($params = $snippetRoute->match($this->request->getParam('*', array()))) {
			$this->testPermission('cfContent.elemsnippet');

			if($id = $params['id']) {
				$data = $mgr->getSnippet($id);
				$this->activeType = 'snippet';
				$this->activeItem = $id;
				if(($content = $this->lockObject('cfElementSnippet', $params['id'], true)) === true) $content = '';
			}
			else
				$data = array('snippetname' => '', 'description' => '', 'catid' => '0', 'content' => '');

			if(empty($content)) {
				$this->tpl->getCodeBuffer()->bufferReadyJS("
	\$fl('#name').bind('keyup', function(e) {
		\$fl('#nameview').html(\$fl(e.target).val());
	});
	\$fl('#nameview').html(\$fl('#name').val());
")->bufferHeadJS('system/app/js/cfMetaS.js');
				$this->confirmLeavePage();

				$form = new flForm($this->request, 'snippetform', $this->assemble('', 'elements', array('snippet', $id)), true, null, new flFormDecoratorList(new flFormDecoratorDl()));
				$form->setCSS('metaSSubmit');

				$name = new flFormElementText($form, 'name', $data['snippetname']);
				$name->setLabel(__('Snippet Name', 'cfBackend'))
					->setRequired()
					->setDescription('The snippet can be called as &#91;&#91;<span id="nameview"></span>&#93;&#93;')
					->addValidator(new flFormValidatorRequired())
					->addValidator(new flFormValidatorMinLength(3))
					->addValidator(new flFormValidatorMaxLength(64))
					->addValidator(new flFormValidatorRegex('/^[a-z0-9_.]+$/i', __('The snippet name can only contain the characters a-z, 0-9, _ and ..', 'cfBackend')));

				$cat = new flFormElementSelect($form, 'cat', $data['catid'], array('0' => '[none]') + $mgr->getCategories());
				$cat->setLabel(__('Category', 'cfBackend'));

				$desc = new flFormElementTextArea($form, 'desc', $data['description']);
				$desc->setLabel(__('Description', 'cfBackend'))
					->addValidator(new flFormValidatorMaxLength(255))
					->setRows(3);

				$content = new flFormElementWYSIWYG($form, 'snippet', $data['content']);
				$content->setLabel(__('Snippet (PHP)', 'cfBackend'))
					->addValidator(new flFormValidatorMaxLength(2097152))
					->setRows(20)
					->setDB($this->db)
					->editorModule($this->getCodeEditorModule())
					->editorContentType('snippet');

				if($id) {
					$update = new flFormElementSubmit($form, 'update');
					$update->setCaption(__('Update Snippet', 'cfBackend'))->setCSS('primary');

					$delSubmit = new flFormElementSubmit($form, 'delete');
					$delSubmit->setCaption(__('Delete Snippet', 'cfBackend'))
						->setCSS('danger');

					$form->groupElements('submit', array($update, $delSubmit), new flFormDecoratorList(new flFormDecoratorDl()));
				}
				else {
					$create = new flFormElementSubmit($form, 'create');
					$create->setCaption(__('Create Snippet', 'cfBackend'))->setCSS('primary');

					$form->groupElements('submit', array($create), new flFormDecoratorList(new flFormDecoratorDl()));
				}

				$msg = $this->session->flashGet('cfManagerOpSuccess') ? $this->alertMessage('Success', __('The new snippet has been created.', 'cfBackend')) : '';

				if($form->isSubmitted() && $form->isValid()) {
					if($id) {
						if($mgr->updateSnippet($id, $name->getValue(), $desc->getValue(), $content->getValue(), $cat->getValue())) {
							cfElementAudit::updateSnippet($id, $data);
							$msg = $this->alertMessage('Success', __('The snippet has been updated.', 'cfBackend'));
						}
						else
							$msg = $this->alertMessage('Error', __('Unable to update the snippet, check that the snippet name has not already been used.', 'cfBackend'));

						$this->cache->flush();
						if($delSubmit->getValue())
							$this->response->redirectTo($this->assemble('', 'elements', array('delsnippet', $id), true));
					}
					else {
						if($nid = $mgr->createSnippet($name->getValue(), $desc->getValue(), $content->getValue(), $cat->getValue())) {
							cfElementAudit::createSnippet($nid, $name->getValue());
							$this->cache->flush();
							$this->session->flashSet('cfManagerOpSuccess', true);
							$this->response->redirectTo($this->assemble('', 'elements', array('snippet', $nid)));
						}
						else
							$msg = $this->alertMessage('Error', __('Unable to create the new snippet, check that the snippet name has not already been used.', 'cfBackend'));
					}
				}

				$content = $this->renderBlock($id ? sprintf(__('Snippet: %s', 'cfBackend'), $data['snippetname']) : __('Create Snippet', 'cfBackend'), $form->render($this->tpl->getCodeBuffer()), '', $msg);
			}
		}
		// Delete snippet
		else if($params = $delSnippetRoute->match($this->request->getParam('*', array()))) {
			$this->testPermission('cfContent.elemsnippet');

			if(($content = $this->lockObject('cfElementSnippet', $params['id'], true)) === true) {
				$id = $params['id'];
				$this->activeType = 'snippet';
				$this->activeItem = $id;

				$data = $mgr->getSnippet($id);

				$form = new flForm($this->request, 'deleteform', $this->assemble('', 'elements', array('delsnippet', $id)), true);

				$name = new flFormElementText($form, 'name', $data['snippetname']);
				$name->setLabel(__('Snippet Name', 'cfBackend'))
					->setReadonly()
					->setDisabled();

				$delSubmit = new flFormElementSubmit($form, 'delete');
				$delSubmit->setCaption(__('Permanently Delete This Snippet', 'cfBackend'))
					->setCSS('danger')
					->setDescription($this->alertMessage('Warning', __('Permanently delete this item, this option cannot be undone!', 'cfBackend')));

				if($form->isSubmitted() && $form->isValid()) {
					cfElementAudit::deleteSnippet($id, $data);
					$mgr->deleteSnippet($id);
					$this->cache->flush();
					$this->response->redirectTo($this->assemble('', 'elements', array(), true));
				}

				$content = $this->renderBlock(sprintf(__('Delete Snippet: %s', 'cfBackend'), $data['snippetname']), $form->render($this->tpl->getCodeBuffer()));
			}
		}
		// Add/edit task
		else if($params = $taskRoute->match($this->request->getParam('*', array()))) {
			$this->testPermission('cfContent.elemtasks');

			if($id = $params['id']) {
				$data = $tasks->getTask($id);
				$this->activeType = 'task';
				$this->activeItem = $id;
				if(($content = $this->lockObject('cfElementTask', $params['id'], true)) === true) $content = '';
			}
			else
				$data = array('taskname' => '', 'description' => '', 'catid' => '0', 'updaterate' => '3600', 'content' => '', 'email' => '');

			if(empty($content)) {
				$this->tpl->getCodeBuffer()->bufferReadyJS("
	\$fl('#name').bind('keyup', function(e) {
		\$fl('#nameview').html(\$fl(e.target).val());
	});
	\$fl('#nameview').html(\$fl('#name').val());
")->bufferHeadJS('system/app/js/cfMetaS.js');
				$this->confirmLeavePage();

				$form = new flForm($this->request, 'taskform', $this->assemble('', 'elements', array('task', $id)), true, null, new flFormDecoratorList(new flFormDecoratorDl()));
				$form->setCSS('metaSSubmit');

				$name = new flFormElementText($form, 'name', $data['taskname']);
				$name->setLabel(__('Task Name', 'cfBackend'))
					->setDescription(__("Snippets can use clearFusionCMS::getTaskData('<span id=\"nameview\"></span>') to access the task data.", 'cfBackend'))
					->setRequired()
					->addValidator(new flFormValidatorRequired())
					->addValidator(new flFormValidatorMinLength(3))
					->addValidator(new flFormValidatorMaxLength(26))
					->addValidator(new flFormValidatorRegex('/^[a-z0-9_]+$/', __('The task name can only contain the characters a-z, 0-9 and _.', 'cfBackend')));

				$cat = new flFormElementSelect($form, 'cat', $data['catid'], array('0' => '[none]') + $mgr->getCategories());
				$cat->setLabel(__('Category', 'cfBackend'));

				$desc = new flFormElementTextArea($form, 'desc', $data['description']);
				$desc->setLabel(__('Description', 'cfBackend'))
					->addValidator(new flFormValidatorMaxLength(255))
					->setRows(3);

				$email = new flFormElementEmail($form, 'email', $data['email']);
				$email->setLabel(__('Email', 'cfBackend'))
					->setDescription(__('If set any output from the task will be emailed to this address.', 'cfBackend'))
					->addValidator(new flFormValidatorEmail());

				$rate = new flFormElementSelect($form, 'rate', $data['updaterate'], array(
					'300' => __('5 Minutes', 'cfBackend'),
					'1800' => __('30 Minutes', 'cfBackend'),
					'3600' => __('1 Hour', 'cfBackend'),
					'10800' => __('3 Hours', 'cfBackend'),
					'21600' => __('6 Hours', 'cfBackend'),
					'43200' => __('12 Hours', 'cfBackend'),
					'86400' => __('1 Day', 'cfBackend')
				));
				$rate->setLabel(__('Maximum Rate to Execute Task at', 'cfBackend'));

				$content = new flFormElementWYSIWYG($form, 'snippet', $data['content']);
				$content->setLabel(__('Task Snippet (PHP)', 'cfBackend'))
					->setDescription(__('$dataStore is configured to provide storage for this task.', 'cfBackend'))
					->addValidator(new flFormValidatorMaxLength(2097152))
					->setRows(20)
					->setDB($this->db)
					->editorModule($this->getCodeEditorModule())
					->editorContentType('snippet');

				if($id) {
					$update = new flFormElementSubmit($form, 'update');
					$update->setCaption(__('Update Task Snippet', 'cfBackend'))->setCSS('primary');

					$run = new flFormElementSubmit($form, 'run');
					$run->setCaption(__('Run Task Now', 'cfBackend'))->setCSS('primary');

					$delSubmit = new flFormElementSubmit($form, 'delete');
					$delSubmit->setCaption(__('Delete Task Snippet', 'cfBackend'))
						->setCSS('danger');

					$form->groupElements('submit', array($update, $run, $delSubmit), new flFormDecoratorList(new flFormDecoratorDl()));
				}
				else {
					$create = new flFormElementSubmit($form, 'create');
					$create->setCaption(__('Create Task Snippet', 'cfBackend'))->setCSS('primary');
					$form->groupElements('submit', array($create), new flFormDecoratorList(new flFormDecoratorDl()));
				}

				$msg = $this->session->flashGet('cfManagerOpSuccess') ? $this->alertMessage('Success', __('The new task has been created.', 'cfBackend')) : '';

				if($form->isSubmitted() && $form->isValid()) {
					if($id) {
						if($tasks->updateTask($id, $name->getValue(), $desc->getValue(), $content->getValue(), $cat->getValue(), $rate->getValue(), $email->getValue())) {
							cfElementAudit::updateTask($id, $data);
							if($run->getValue()) {
								$tasks->runTask($name->getValue());
								$msg = $this->alertMessage('Success', __('The task has been run, any output will be emailed to the tasks email address.', 'cfBackend'));
							}
							else
								$msg = $this->alertMessage('Success', __('The task has been updated.', 'cfBackend'));
						}
						else
							$msg = $this->alertMessage('Error', __('Unable to update the task, check that the task name has not already been used.', 'cfBackend'));

						if($delSubmit->getValue())
							$this->response->redirectTo($this->assemble('', 'elements', array('deltask', $id), true));
					}
					else {
						if($nid = $tasks->createTask($name->getValue(), $desc->getValue(), $content->getValue(), $cat->getValue(), $rate->getValue(), $email->getValue())) {
							cfElementAudit::createTask($nid, $name->getValue());
							$this->session->flashSet('cfManagerOpSuccess', true);
							$this->response->redirectTo($this->assemble('', 'elements', array('task', $nid)));
						}
						else
							$msg = $this->alertMessage('Error', __('Unable to create the new task, check that the task name has not already been used.', 'cfBackend'));
					}
				}

				$content = $this->renderBlock($id ? sprintf(__('Task: %s', 'cfBackend'), $data['taskname']) : __('Create Task', 'cfBackend'), $form->render($this->tpl->getCodeBuffer()), '', $msg);
			}
		}
		// Delete task
		else if($params = $delTaskRoute->match($this->request->getParam('*', array()))) {
			$this->testPermission('cfContent.elemtasks');

			if(($content = $this->lockObject('cfElementTask', $params['id'], true)) === true) {
				$id = $params['id'];
				$this->activeType = 'task';
				$this->activeItem = $id;

				$data = $tasks->getTask($id);

				$form = new flForm($this->request, 'deleteform', $this->assemble('', 'elements', array('deltask', $id)), true);

				$name = new flFormElementText($form, 'name', $data['taskname']);
				$name->setLabel(__('Task Name', 'cfBackend'))
					->setReadonly()
					->setDisabled();

				$delSubmit = new flFormElementSubmit($form, 'delete');
				$delSubmit->setCaption(__('Permanently Delete This Task', 'cfBackend'))
					->setCSS('danger')
					->setDescription($this->alertMessage('Warning', __('Permanently delete this item, this option cannot be undone!', 'cfBackend')));

				if($form->isSubmitted() && $form->isValid()) {
					cfElementAudit::deleteTask($id, $data);
					$tasks->deleteTask($id);
					$this->response->redirectTo($this->assemble('', 'elements', array(), true));
				}

				$content = $this->renderBlock(sprintf(__('Delete Task: %s', 'cfBackend'), $data['taskname']), $form->render($this->tpl->getCodeBuffer()));
			}
		}
		// Add/edit field
		else if($params = $fieldRoute->match($this->request->getParam('*', array()))) {
			$this->testPermission('cfContent.elemfields');

			if($id = $params['id']) {
				$data = $fields->getField($id);
				$this->activeType = 'field';
				$this->activeItem = $id;
				if(($content = $this->lockObject('cfElementField', $params['id'], true)) === true) $content = '';
			}
			else
				$data = array('ignoregroup' => '', 'placeholder' => '', 'fldtype' => 'text', 'caption' => '', 'description' => '', 'editperm' => '', 'validation' => '', 'catid' => '0', 'disporder' => 0, 'templates' => array(), 'editor' => '', 'snippet' => '');

			if(empty($content)) {
				$this->tpl->getCodeBuffer()->bufferReadyJS("
	\$fl('#name').bind('keyup', function(e) {
		\$fl('#nameview').html(\$fl(e.target).val());
	});
	\$fl('#nameview').html(\$fl('#name').val());

	var _firstUntick = true;
	\$fl('#templates input').bind('click', function(e) {
		if(_firstUntick && !\$fl(e.target).value()) {
			_firstUntick = false;
			if(!confirm('" . __('Removing fields from templates will delete all data stored for that field by documents using the removed templates. This operation cannot be undone!', 'cfBackend') . "')) {
				e.preventDefault();
				return false;
			}
		}
	});
	
	\$fl('#fldtype').bind('change', function() {
		var v = \$fl('#fldtype').val();
		if(v == 'htmlarea' || v == 'htmlareasmall' || v == 'textarea' || v == 'textareasmall') {
			\$fl('#editor-lblcontainer').css('display', 'block');
			\$fl('#editor-container').css('display', 'block');
		}
		else {
			\$fl('#editor-lblcontainer').css('display', 'none');
			\$fl('#editor-container').css('display', 'none');
		}
	}).trigger('change');
")->bufferHeadJS('system/app/js/cfMetaS.js');
				$this->confirmLeavePage();

				$form = new flForm($this->request, 'fieldform', $this->assemble('', 'elements', array('field', $id, 'view')), true, null, new flFormDecoratorList(new flFormDecoratorDl()));
				$form->setCSS('metaSSubmit');

				$name = new flFormElementText($form, 'name', $data['placeholder']);
				$name->setLabel(__('Field Name', 'cfBackend'))
					->setRequired()
					->setDescription(__('The field can be used in published content with the tag &#91;&#91;+<span id="nameview"></span>&#93;&#93;', 'cfBackend'))
					->addValidator(new flFormValidatorRequired())
					->addValidator(new flFormValidatorMinLength(3))
					->addValidator(new flFormValidatorMaxLength(32))
					->addValidator(new flFormValidatorRegex('/^[a-z0-9_]+$/i', __('The field name can only contain the characters a-z, 0-9, and _.', 'cfBackend')));

				$cat = new flFormElementSelect($form, 'cat', $data['catid'], array('0' => __('[none]', 'cfBackend')) + $mgr->getCategories());
				$cat->setLabel(__('Category', 'cfBackend'));

				$dispOrder = new flFormElementText($form, 'disporder', $data['disporder']);
				$dispOrder->setLabel(__('Display Order', 'cfBackend'))
					->setDescription(__('The order the fields are shown in when editing a document, smaller numbers first. Fields with a negative order value are shown before the content field and those with a positive value after.', 'cfBackend'))
					->setRequired()
					->addValidator(new flFormValidatorRequired())
					->addValidator(new flFormValidatorInteger());

				$ignore = new flFormElementCheckbox($form, 'ignore', $data['ignoregroup']);
				$ignore->setLabel(__('Ignore Grouping', 'cfBackend'))
					->setDescription(__('If ticked then the field will not be grouped by it\'s category.', 'cfBackend'));

				$desc = new flFormElementTextArea($form, 'desc', $data['description']);
				$desc->setLabel(__('Description', 'cfBackend'))
					->addValidator(new flFormValidatorMaxLength(255))
					->setRows(3)
					->setDescription(__('Description of field.', 'cfBackend'));

				$caption = new flFormElementText($form, 'caption', $data['caption']);
				$caption->setLabel(__('Caption', 'cfBackend'))
					->setDescription(__('The caption shown with the field when editing.', 'cfBackend'))
					->setRequired()
					->addValidator(new flFormValidatorRequired())
					->addValidator(new flFormValidatorMaxLength(64));

				$type = new flFormElementSelect($form, 'fldtype', $data['fldtype'], $fields->getTypes());
				$type->setLabel(__('Field Type', 'cfBackend'));

				$ac = new flAcMgr($this->db);
				$availEdPerms = array('' => __('[None]', 'cfBackend'));
				foreach($ac->getPermissionsInGroup('ctntedt') as $p => $d) {
					if($this->user->hasPermission($p))
						$availEdPerms[$p] = $d;
				}

				$required = new flFormElementCheckbox($form, 'required', isset($data['validation']['required']) ? $data['validation']['required'] : '');
				$required->setLabel(__('Field Is Required', 'cfBackend'))
					->setDescription(__('If ticked then the field cannot be left blank.', 'cfBackend'));

				$min = new flFormElementText($form, 'min', isset($data['validation']['min']) ? $data['validation']['min'] : '');
				$min->setLabel(__('Minimum Value or Minimum Number of Characters Allowed', 'cfBackend'))
					->setDescription(__('Leave blank for no minimum.', 'cfBackend'))
					->addValidator(new flFormValidatorInteger());

				$max = new flFormElementText($form, 'max', isset($data['validation']['max']) ? $data['validation']['max'] : '');
				$max->setLabel(__('Maximum Value or Maximum Number of Characters Allowed', 'cfBackend'))
					->setDescription(__('Leave blank for no maximum.', 'cfBackend'))
					->addValidator(new flFormValidatorInteger());

				$perms = new flFormElementSelect($form, 'editperm', $data['editperm'], $availEdPerms);
				$perms->setLabel(__('Edit Permission', 'cfBackend'))
					->setDescription(__('The permission required to be able to edit the field.', 'cfBackend'));

				$siteCfg = clearFusionCMS::getSiteConfig('cfSystemSite');
				$template = new flFormElementChecklist($form, 'templates', $data['templates'], $mgr->getTemplateList($siteCfg->siteTheme));
				$template->setLabel(__('Assign Field to Templates', 'cfBackend'))
					->setDescription(__('Select the templates that the field is used with.', 'cfBackend') . $this->alertMessage('Warning', __('Removing fields from templates will delete all data stored for that field by documents using the removed templates. This operation cannot be undone!', 'cfBackend')));

				$el = cfEditor::getAvailable(true);
				$editor = new flFormElementSelect($form, 'editor', $data['editor'], $el);
				$editor->setLabel(__('Editor', 'cfBackend'));

				$form->groupElements('info', array($name, $desc, $caption, $type, $editor))
					->groupElements('validation', array($required, $min, $max))
					->groupElements('tpls', array($template))
					->groupElements('permissions', array($perms))
					->groupElements('display', array($dispOrder, $cat, $ignore))
					->addTab(__('Definition', 'cfBackend'), array('info'))
					->addTab(__('Validation', 'cfBackend'), array('validation'))
					->addTab(__('Template Assignments', 'cfBackend'), array('tpls'))
					->addTab(__('Permissions', 'cfBackend'), array('permissions'))
					->addTab(__('Display Settings', 'cfBackend'), array('display'));

				if($id) {
					$update = new flFormElementSubmit($form, 'update');
					$update->setCaption(__('Update Field', 'cfBackend'))->setCSS('primary');

					$duplicate = new flFormElementSubmit($form, 'duplicate');
					$duplicate->setCaption(__('Duplicate Field', 'cfBackend'))->setCSS('primary');

					$delSubmit = new flFormElementSubmit($form, 'delete');
					$delSubmit->setCaption(__('Delete Field', 'cfBackend'))
						->setCSS('danger');

					$form->groupElements('submit', array($update, $duplicate, $delSubmit), new flFormDecoratorList(new flFormDecoratorDl()));
				}
				else {
					$create = new flFormElementSubmit($form, 'create');
					$create->setCaption(__('Create Field', 'cfBackend'))->setCSS('primary');
					$form->groupElements('submit', array($create), new flFormDecoratorList(new flFormDecoratorDl()));
				}

				$msg = $this->session->flashGet('cfManagerOpSuccess', '');

				if($form->isSubmitted() && $form->isValid()) {
					$validation = array(
						'required' => $required->getValue(),
						'min' => $min->getValue(),
						'max' => $max->getValue()
					);

					if($id) {
						// If duplicate
						if(isset($duplicate) && $duplicate->getValue()) {
							$cpycnt = 1;
							while($cpycnt < 10 && !($nid = $fields->createField($copyName = (($cpycnt >= 2 ? "copy_{$cpycnt}_of_" : 'copy_of_') . $name->getValue()), $type->getValue(), ($cpycnt >= 2 ? "Copy $cpycnt of " : 'Copy of ') . $caption->getValue(), $desc->getValue(), $perms->getValue(), $cat->getValue(), $dispOrder->getValue(), $template->getValue(), $validation, $ignore->getValue(), $editor->getValue(), cfEditor::getFilter($editor->getValue()))))
								$cpycnt++;

							if($nid) {
								cfElementAudit::createField($copyName);
								$this->cache->flush();
								$this->session->flashSet('cfManagerOpSuccess', $this->alertMessage('Success', __('The new field has been duplicated.', 'cfBackend')));
								$this->response->redirectTo($this->assemble('', 'elements', array('field', $copyName, 'view')));
							}
							else
								$msg = $this->alertMessage('Error', __('Unable to duplicate the field, check that the field name has not already been used.', 'cfBackend'));
						}
						// Update
						else {
							if($fields->updateField($id, $name->getValue(), $type->getValue(), $caption->getValue(), $desc->getValue(), $perms->getValue(), $cat->getValue(), $dispOrder->getValue(), $template->getValue(), $validation, $ignore->getValue(), $editor->getValue(), cfEditor::getFilter($editor->getValue()))) {
								cfElementAudit::updateField($name->getValue(), $data);
								$this->session->flashSet('cfManagerOpSuccess', $this->alertMessage('Success', __('The field has been updated.', 'cfBackend')));
								$this->cache->flush();
								if(!isset($delSubmit) || !$delSubmit->getValue()) {
									$this->response->redirectTo($this->assemble('', 'elements', array('field', $name->getValue(), 'view')));
								}
							}
							else
								$msg = $this->alertMessage('Error', __('Unable to update the field, check that the field name has not already been used.', 'cfBackend'));

							if(isset($delSubmit) && $delSubmit->getValue())
								$this->response->redirectTo($this->assemble('', 'elements', array('field', $id, 'delete'), true));
						}
					}
					else {
						if($nid = $fields->createField($name->getValue(), $type->getValue(), $caption->getValue(), $desc->getValue(), $perms->getValue(), $cat->getValue(), $dispOrder->getValue(), $template->getValue(), $validation, $ignore->getValue(), $editor->getValue(), cfEditor::getFilter($editor->getValue()))) {
							cfElementAudit::createField($name->getValue());
							$this->cache->flush();
							$this->session->flashSet('cfManagerOpSuccess', $this->alertMessage('Success', __('The new field has been created.', 'cfBackend')));
							$this->response->redirectTo($this->assemble('', 'elements', array('field', $name->getValue(), 'view')));
						}
						else
							$msg = $this->alertMessage('Error', __('Unable to create the new field, check that the field name has not already been used.', 'cfBackend'));
					}
				}

				$content = $this->renderBlock($id ? sprintf(__('Custom Field: %1$s (%2$s)', 'cfBackend'), $data['caption'], $data['placeholder']) : __('Create Custom Field', 'cfBackend'), $form->render($this->tpl->getCodeBuffer()), '', $msg);
			}
		}
		// Delete field
		else if($params = $delFieldRoute->match($this->request->getParam('*', array()))) {
			$this->testPermission('cfContent.elemfields');

			if(($content = $this->lockObject('cfElementField', $params['id'], true)) === true) {
				$id = $params['id'];
				$this->activeType = 'field';
				$this->activeItem = $id;

				$data = $fields->getField($id);

				$form = new flForm($this->request, 'deleteform', $this->assemble('', 'elements', array('field', $id, 'delete')), true);

				$name = new flFormElementText($form, 'name', $data['placeholder']);
				$name->setLabel(__('Field Name', 'cfBackend'))
					->setReadonly()
					->setDisabled();

				$name = new flFormElementText($form, 'caption', $data['caption']);
				$name->setLabel(__('Caption', 'cfBackend'))
					->setReadonly()
					->setDisabled();

				$delSubmit = new flFormElementSubmit($form, 'delete');
				$delSubmit->setCaption(__('Permanently Delete This Field', 'cfBackend'))
					->setCSS('danger')
					->setDescription($this->alertMessage('Warning', __('Permanently delete this item, this option cannot be undone and all data in this field will be lost!', 'cfBackend')));

				if($form->isSubmitted() && $form->isValid()) {
					$fields->deleteField($id);
					cfElementAudit::deleteField($data);
					$this->cache->flush();
					$this->response->redirectTo($this->assemble('', 'elements', array(), true));
				}

				$content = $this->renderBlock(sprintf(__('Delete Custom Field: %1$s (%2$s)', 'cfBackend'), $data['caption'], $data['placeholder']), $form->render($this->tpl->getCodeBuffer()));
			}
		}
		// Add/edit chunk
		else if($params = $chunkRoute->match($this->request->getParam('*', array()))) {
			$isTemplate = ($cmd = $params['cmd']) == 'template';

			$this->testPermission($isTemplate ? 'cfContent.elemtemplate' : 'cfContent.elemchunk');

			if($id = $params['id']) {
				$data = $mgr->getChunk($id);
				$cmd = ($isTemplate = $data['istemplate'] != '0') ? 'template' : 'chunk';
				$this->activeType = $cmd;
				$this->activeItem = $id;
				if(($content = $this->lockObject('cfElementChunk', $params['id'], true)) === true) $content = '';
			}
			else
				$data = array('chunkname' => '', 'themeid' => '0', 'catid' => '0', 'description' => '', 'content' => '');

			if(empty($content)) {
				$this->tpl->getCodeBuffer()->bufferHeadJS('system/app/js/cfMetaS.js');
				$this->confirmLeavePage();

				$form = new flForm($this->request, 'chunkform', $this->assemble('', 'elements', array($cmd, $id)), true, null, new flFormDecoratorList(new flFormDecoratorDl()));
				$form->setCSS('metaSSubmit');

				$name = new flFormElementText($form, 'name', $data['chunkname']);
				$name->setLabel($isTemplate ? __('Template Name', 'cfBackend') : __('Chunk Name', 'cfBackend'))
					->setRequired()
					->addValidator(new flFormValidatorRequired())
					->addValidator(new flFormValidatorMinLength(3))
					->addValidator(new flFormValidatorMaxLength(64))
					->addValidator(new flFormValidatorRegex('/^[a-z0-9_.]+$/i', $isTemplate ? __('The template name can only contain the characters a-z, 0-9, _ and ..', 'cfBackend') : __('The chunk name can only contain the characters a-z, 0-9, _ and ..', 'cfBackend')));

				if(!$isTemplate)
					$name->setDescription(__('The chunk can be called as &#91;&#91;$<span id="nameview"></span>&#93;&#93;', 'cfBackend'));

				$theme = new flFormElementSelect($form, 'theme', $data['themeid'], array('0' => __('[default]', 'cfBackend')) + $mgr->getChildThemesByID());
				$theme->setLabel(__('Theme', 'cfBackend'));

				$cat = new flFormElementSelect($form, 'cat', $data['catid'], array('0' => __('[none]', 'cfBackend')) + $mgr->getCategories());
				$cat->setLabel(__('Category', 'cfBackend'));

				$desc = new flFormElementTextArea($form, 'desc', $data['description']);
				$desc->setLabel(__('Description', 'cfBackend'))
					->addValidator(new flFormValidatorMaxLength(255))
					->setRows(3);

				$content = new flFormElementWYSIWYG($form, 'chunk', $data['content']);
				$content->setLabel($isTemplate ? __('Template (HTML)', 'cfBackend') : __('Chunk (HTML)', 'cfBackend'))
					->addValidator(new flFormValidatorMaxLength(2097152))
					->setRows(20)
					->setDB($this->db)
					->editorModule($this->getCodeEditorModule())
					->editorContentType('chunk');

				$delSubmit = null;
				if($id) {
					$update = new flFormElementSubmit($form, 'update');
					$update->setCaption($isTemplate ? __('Update Template', 'cfBackend') : __('Update Chunk', 'cfBackend'))->setCSS('primary');

					$duplicate = new flFormElementSubmit($form, 'duplicate');
					$duplicate->setCaption($isTemplate ? __('Duplicate Template', 'cfBackend') : __('Duplicate Chunk', 'cfBackend'))->setCSS('primary');

					$sites = new cfSiteManager($this->db);
					$count = 0;
					foreach($sites->listSites() as $site => $_desc)
						$count += $this->db->fetchOne("SELECT COUNT(*) FROM cf_content_{$site} WHERE template=?", $data['chunkname']);

					$elemGrp = array($update, $duplicate);
					if($cmd == 'chunk' || ($cmd == 'template' && ($data['themeid'] || !$count))) {
						$delSubmit = new flFormElementSubmit($form, 'delete');
						$delSubmit->setCaption($isTemplate ? __('Delete Template', 'cfBackend') : __('Delete Chunk', 'cfBackend'))
							->setCSS('danger');
						$elemGrp[] = $delSubmit;
					}
					else if($cmd == 'template' && !$data['themeid'] && $count) {
						$showUsage = new flFormElementSubmit($form, 'usage');
						$showUsage->setCaption(__('Show Usage', 'cfBackend'))->setCSS('primary');
						$usagePane = new flFormElementPane($form, 'tplusage', '');
						$elemGrp[] = $showUsage;
					}

					$form->groupElements('submit', $elemGrp, new flFormDecoratorList(new flFormDecoratorDl()));
				}
				else {
					$create = new flFormElementSubmit($form, 'create');
					$create->setCaption($isTemplate ? __('Create Template', 'cfBackend') : __('Create Chunk', 'cfBackend'))->setCSS('primary');
					$form->groupElements('submit', array($create), new flFormDecoratorList(new flFormDecoratorDl()));
				}

				$msg = $this->session->flashGet('cfManagerOpSuccess', '');

				if($form->isSubmitted() && $form->isValid()) {
					if($id) {
						// If duplicate
						if(isset($duplicate) && $duplicate->getValue()) {
							$cpycnt = 1;
							while($cpycnt < 10 && !($nid = $mgr->createChunk($copyName = (($cpycnt >= 2 ? "copy.{$cpycnt}.of." : 'copy.of.') . $name->getValue()), $desc->getValue(), $content->getValue(), $cat->getValue(), $theme->getValue(), $isTemplate)))
								$cpycnt++;

							if($nid) {
								cfElementAudit::createChunk($isTemplate, $nid, $copyName);

								// Copy custom fields to duplicate template
								if($isTemplate) {
									$flds = new cfCustomFields($this->db);
									$flds->copy($data['chunkname'], $copyName);
								}

								$this->cache->flush();
								$this->session->flashSet('cfManagerOpSuccess', $this->alertMessage('Success', $isTemplate ? __('The template has been duplicated.', 'cfBackend') : __('The chunk has been duplicated.', 'cfBackend')));
								$this->response->redirectTo($this->assemble('', 'elements', array('chunk', $nid)));
							}
							else
								$msg = $this->alertMessage('Error', $isTemplate ? __('Unable to duplicate the template, it may be that a template or chunk of the same name exists within the theme.', 'cfBackend') : __('Unable to duplicate the chunk, it may be that a chunk or template of the same name exists within the theme.', 'cfBackend'));
						}
						// Update
						else {
							if($mgr->updateChunk($id, $name->getValue(), $desc->getValue(), $content->getValue(), $cat->getValue(), $theme->getValue())) {

								cfElementAudit::updateChunk($isTemplate, $id, $data);

								// If not a themed template then update the content to use the new name
								if($cmd == 'template' && !$theme->getValue()) {
									$sites = new cfSiteManager($this->db);
									foreach($sites->listSites() as $site => $_desc)
										$this->db->update('cf_content_' . $site, array('template' => $name->getValue()), 'template=?', $data['chunkname']);

									// Notify fields of change
									$fields->templateChanged($data['chunkname'], $name->getValue());
								}

								$this->cache->flush();
								$msg = $this->alertMessage('Success', $isTemplate ? __('The template has been updated.', 'cfBackend') : __('The chunk has been updated.', 'cfBackend'));
							}
							else
								$msg = $this->alertMessage('Error', $isTemplate ? __('Unable to update the template, it may be that a template or chunk of the same name exists within the theme.', 'cfBackend') : __('Unable to update the chunk, it may be that a chunk or template of the same name exists within the theme.', 'cfBackend'));

							if($delSubmit !== null && $delSubmit->getValue())
								$this->response->redirectTo($this->assemble('', 'elements', array('delchunk', $id), true));

							// Show all the documents in this site using the template
							if(isset($showUsage) && $showUsage->getValue()) {
								$table = new flWidgetTable();
								$table->setClass('tabledata bordered striped rowhover')
									->setHeadings(__('Used in Document', 'cfBackend'), __('Used in Document', 'cfBackend'), __('Used in Document', 'cfBackend'))
									->setColData('col1', 'col2', 'col3')
									->skipEncode('col1', 'col2', 'col3');

								$results = $this->db->query('SELECT id,menutitle,title,subtitle FROM cf_content_' . clearFusionCMS::getSite() . ' WHERE template=?', $name->getValue());
								while($row = $results->fetch()) {
									$docList = array();

									for($i=1;$i<4;$i++) {
										if($i > 1)
											$row = $results->fetch();

										if($row) {
											$title = (empty($row['menutitle']) ? $row['title'] . (empty($row['subtitle']) ? '' : " - $row[subtitle]") : $row['menutitle']) . " [$row[id]]";
											$link = $this->assemble('', 'documents', array($row['id']));
											$docList['col' . $i] = "<a href=\"$link\">$title</a>";
										}
										else
											$docList['col' . $i] = '';
									}
									$table->addRow($docList);
								}

								$usagePane->setContent($table->render() . $this->alertMessage('Info', __('Only documents within this site are shown.', 'cfBackend')));
							}
						}
					}
					else {
						if($nid = $mgr->createChunk($name->getValue(), $desc->getValue(), $content->getValue(), $cat->getValue(), $theme->getValue(), $isTemplate)) {
							cfElementAudit::createChunk($isTemplate, $nid, $name->getValue());
							$this->cache->flush();
							$this->session->flashSet('cfManagerOpSuccess', $this->alertMessage('Success', $isTemplate ? __('The new template has been created.', 'cfBackend') : __('The new chunk has been created.', 'cfBackend')));
							$this->response->redirectTo($this->assemble('', 'elements', array('chunk', $nid)));
						}
						else
							$msg = $this->alertMessage('Error', $isTemplate ? __('Unable to create the new template, it may be that a template or chunk of the same name exists within the theme.', 'cfBackend') : __('Unable to create the new chunk, it may be that a chunk or template of the same name exists within the theme.', 'cfBackend'));
					}
				}

				if(!$isTemplate) {
					$this->tpl->getCodeBuffer()->bufferReadyJS("
	// Copy name to usage
	\$fl('#name').bind('keyup', function(e) {
		\$fl('#nameview').html(\$fl(e.target).val());
	});
	\$fl('#nameview').html(\$fl('#name').val());
");
				}
				$content = $this->renderBlock($isTemplate ? ($id ? sprintf(__('Template: %s', 'cfBackend'), $data['chunkname']) : __('Create Template', 'cfBackend')) : ($id ? sprintf(__('Chunk: %s', 'cfBackend'), $data['chunkname']) : __('Create Chunk', 'cfBackend')), $form->render($this->tpl->getCodeBuffer()), '', $msg);
			}
		}
		// Delete chunk
		else if($params = $delChunkRoute->match($this->request->getParam('*', array()))) {
			$id = $params['id'];

			if(($content = $this->lockObject('cfElementChunk', $params['id'], true)) === true) {
				$data = $mgr->getChunk($id);
				$isTemplate = $data['istemplate'] != 0;
				$this->activeType = $isTemplate ? 'template' : 'chunk';
				$this->activeItem = $id;

				$this->testPermission($isTemplate ? 'cfContent.elemtemplate' : 'cfContent.elemchunk');

				$form = new flForm($this->request, 'deleteform', $this->assemble('', 'elements', array('delchunk', $id)), true);

				$name = new flFormElementText($form, 'name', $data['chunkname']);
				$name->setLabel($isTemplate ? __('Template Name', 'cfBackend') : __('Chunk Name', 'cfBackend'))
					->setReadonly()
					->setDisabled();

				$delSubmit = new flFormElementSubmit($form, 'delete');
				$delSubmit->setCaption($isTemplate ? __('Permanently Delete This Template', 'cfBackend') : __('Permanently Delete This Chunk', 'cfBackend'))
					->setCSS('danger')
					->setDescription($this->alertMessage('Warning', __('Permanently delete this item, this option cannot be undone!', 'cfBackend')));

				if($form->isSubmitted() && $form->isValid()) {
					$sites = new cfSiteManager($this->db);
					$count = 0;
					foreach($sites->listSites() as $site => $desc)
						$count += $this->db->fetchOne("SELECT COUNT(*) FROM cf_content_{$site} WHERE template=?", $data['chunkname']);

					if(!$isTemplate || $data['themeid'] || !$count) {
						if($isTemplate)
							$fields->templateDeleted($data['chunkname']);
						$mgr->deleteChunk($id);
						cfElementAudit::deleteChunk($isTemplate, $id, $data);
						$this->cache->flush();
					}
					$this->response->redirectTo($this->assemble('', 'elements', array(), true));
				}

				$content = $this->renderBlock(sprintf($isTemplate ? __('Delete Template: %s', 'cfBackend') : __('Delete Chunk: %s', 'cfBackend'), $data['chunkname']), $form->render($this->tpl->getCodeBuffer()));
			}
		}
		// Add/edit theme/category
		else if($params = $edCat->match($this->request->getParam('*', array()))) {
			$id = $params['id'];
			$cmd = $params['cmd'];
			$this->activeType = $cmd;
			$this->activeItem = $id;

			if($id) {
				$l = $cmd == 'theme' ? $mgr->getChildThemesByID() : $mgr->getCategories();
				$tname = $l[$id];
				if(($content = $this->lockObject('cfElementCategory', $params['id'], true)) === true) $content = '';
			}
			else
				$tname = '';

			if(empty($content)) {

				if($cmd == 'theme')
					$this->testPermission('cfContent.elemtheme');

				$form = new flForm($this->request, 'themecatform', $this->assemble('', 'elements', $id ? array($cmd, $id) : array($cmd)), true, null, new flFormDecoratorList(new flFormDecoratorDl()));

				$name = new flFormElementText($form, 'name', $tname);
				$name->setLabel($cmd == 'theme' ? __('Child Theme Name', 'cfBackend') : __('Category Name', 'cfBackend'))
					->setRequired()
					->addValidator(new flFormValidatorRequired())
					->addValidator(new flFormValidatorMinLength(3))
					->addValidator(new flFormValidatorMaxLength(255));

				if($cmd == 'theme')
					$name->addValidator(new flFormValidatorRegex('/^[a-z0-9_]+$/', __('The theme name can only contain the characters a-z, 0-9 and _.', 'cfBackend')));
				else
					$name->addValidator(new flFormValidatorRegex('/^[a-z0-9_ .-]+$/i', __('The category name can only contain the characters a-z, 0-9, -, . and _.', 'cfBackend')));

				if($id) {
					$update = new flFormElementSubmit($form, 'update');
					$update->setCaption($cmd == 'theme' ? __('Update Child Theme', 'cfBackend') : __('Update Category', 'cfBackend'))->setCSS('primary');

					$delSubmit = new flFormElementSubmit($form, 'delete');
					$delSubmit->setCaption($cmd == 'theme' ? __('Delete Child Theme', 'cfBackend') : __('Delete Category', 'cfBackend'))
						->setCSS('danger');

					$form->groupElements('submit', array($update, $delSubmit), new flFormDecoratorList(new flFormDecoratorDl()));
				}
				else {
					$create = new flFormElementSubmit($form, 'create');
					$create->setCaption($cmd == 'theme' ? __('Create Child Theme', 'cfBackend') : __('Create Category', 'cfBackend'))->setCSS('primary');
					$form->groupElements('submit', array($create), new flFormDecoratorList(new flFormDecoratorDl()));
				}

				$msg = '';
				if($form->isSubmitted() && $form->isValid()) {

					// Editing
					if($id) {
						$worked = $cmd == 'theme' ? $mgr->updateChildTheme($id, $name->getValue()) : $mgr->updateCategory($id, $name->getValue());
						if($worked) {
							if($cmd == 'theme')
								cfElementAudit::updateTheme($id, ['themename' => $tname]);
							else
								cfElementAudit::updateCategory($id, ['catname' => $tname]);

							if($cmd == 'theme') {
								$cfg = clearFusionCMS::getSiteConfig('cfSystemSite');
								if($cfg->siteTheme == $tname) {
									$cfg->siteTheme = $name->getValue();
								}
							}
							$msg = $this->alertMessage('Success', $cmd == 'theme' ? __('The theme name has been updated.', 'cfBackend') : __('The category name has been updated.', 'cfBackend'));
							$this->cache->flush();
						}
						else
							$msg = $this->alertMessage('Error', $cmd == 'theme' ? __('Unable to update the theme name, please try again.', 'cfBackend') : __('Unable to update the category name, please try again.', 'cfBackend'));

						if($delSubmit->getValue())
							$this->response->redirectTo($this->assemble('', 'elements', array('del' . $cmd, $id), true));
					}
					// Creating new
					else {
						$worked = $cmd == 'theme' ? $mgr->addChildTheme($name->getValue()) : $mgr->addCategory($name->getValue());
						if($worked) {
							if($cmd == 'theme')
								cfElementAudit::createTheme($worked, $name->getValue());
							else
								cfElementAudit::createCategory($worked, $name->getValue());

							$msg = $this->alertMessage('Success', $cmd == 'theme' ? __('The new theme has been created.', 'cfBackend') : __('The new category has been created', 'cfBackend'));
						}
						else
							$msg = $this->alertMessage('Error', $cmd == 'theme' ? __('Unable to create the new theme, please try again.', 'cfBackend') : __('Unable to create the new category, please try again.', 'cfBackend'));
					}
				}

				$content = $this->renderBlock($cmd == 'theme' ? ($id ? __('Update Theme', 'cfBackend') : __('Create Child Theme', 'cfBackend')) : ($id ? __('Update Category', 'cfBackend') : __('Create Category', 'cfBackend')), $form->render($this->tpl->getCodeBuffer()), '', $msg);
			}
		}
		// Delete theme/category
		else if($params = $delCat->match($this->request->getParam('*', array()))) {
			$id = $params['id'];
			$cmd = $params['cmd'];
			$this->activeType = substr($cmd, 3);
			$this->activeItem = $id;

			if(($content = $this->lockObject('cfElementCategory', $params['id'], true)) === true) {
				if($cmd == 'deltheme')
					$this->testPermission('cfContent.elemtheme');

				$l = $cmd == 'deltheme' ? $mgr->getChildThemesByID() : $mgr->getCategories();
				$theme = $l[$id];

				$form = new flForm($this->request, 'deleteform', $this->assemble('', 'elements', array($cmd, $id)), true);

				$name = new flFormElementText($form, 'name', $theme);
				$name->setLabel($cmd == 'deltheme' ? __('Child Theme Name', 'cfBackend') : __('Category Name', 'cfBackend'))
					->setDescription(__('This action cannot be undone!', 'cfBackend'))
					->setReadonly()
					->setDisabled();

				$delSubmit = new flFormElementSubmit($form, 'delete');
				$delSubmit->setCaption($cmd == 'deltheme' ? __('Permanently Delete This Theme and All its Elements', 'cfBackend') : __('Permanently Delete This Category', 'cfBackend'))
					->setCSS('danger');

				if($cmd == 'deltheme')
					$delSubmit->setDescription($this->alertMessage('Warning', __('Deleting the theme will delete all chunks and templates associated with the theme, this operation cannot be undone.', 'cfBackend')));
				else
					$delSubmit->setDescription($this->alertMessage('Warning', __('Deleting the category will move all elements to the root, this operation cannot be undone.', 'cfBackend')));

				if($form->isSubmitted() && $form->isValid()) {
					if($cmd == 'deltheme') {
						cfElementAudit::deleteTheme($id, [ 'themename' => $theme ]);
						$cfg = clearFusionCMS::getSiteConfig('cfSystemSite');
						if($cfg->siteTheme == $theme)
							$cfg->siteTheme = '';
						$mgr->deleteChildTheme($id);
					}
					else {
						cfElementAudit::deleteCategory($id, [ 'catname' => $theme ]);
						$fields->categoryDeleted($id);
						$tasks->categoryDeleted($id);
						$mgr->deleteCategory($id);
					}
					$this->response->redirectTo($this->assemble('', 'elements', array(), true));
				}

				$content = $this->renderBlock(sprintf($cmd == 'deltheme' ? __('Delete Theme: %s', 'cfBackend') : __('Delete Category: %s', 'cfBackend'), $theme), $form->render($this->tpl->getCodeBuffer()));
			}
		}

		// Build the tree showing the elements
		$topTree = new flWidgetTreeView('cfElementsTreeView');

		// Build chunks tree
		if($this->user->hasPermission('cfContent.elemchunk'))
			$this->buildChunkTree($topTree, $mgr, 0, false);

		// Build snippet tree
		if($this->user->hasPermission('cfContent.elemsnippet')) {
			if($mgr->getSnippetCount()) {
				$active = $this->activeType == 'snippet' ? $this->activeItem : '';
				$snippets = new flWidgetTreeView();

				// Build the categories with snippets
				$cats = $mgr->getCategories();
				foreach($cats as $catID => $catName) {
					$sl = $mgr->getSnippetsInCat($catID);
					if(count($sl)) {
						$st = new flWidgetTreeView();
						foreach($sl as $id => $name)
							$st->addNode(new flWidgetTreeLinkNode($name, $this->assemble('', 'elements', array('snippet', $id)), flWidgetTreeNode::SNIPPET))
								->setLastNodeActive($active == $id);
						$snippets->addNode(new flWidgetTreeChildNode($catName, $st, false));
					}
				}

				$sl = $mgr->getSnippetsInCat(0);
				foreach($sl as $id => $name)
					$snippets->addNode(new flWidgetTreeLinkNode($name, $this->assemble('', 'elements', array('snippet', $id)), flWidgetTreeNode::SNIPPET))
						->setLastNodeActive($active == $id);

				$topTree->addNode(new flWidgetTreeChildNode(__('Snippets', 'cfBackend'), $snippets, false));
			}
			else
				$topTree->addNode(new flWidgetTreeNode(__('Snippets', 'cfBackend'), flWidgetTreeNode::CONTAINER));
		}

		// Build template tree
		if($this->user->hasPermission('cfContent.elemtemplate'))
			$this->buildChunkTree($topTree, $mgr, 0, true);

		// Build the task snippet tree
		if($this->user->hasPermission('cfContent.elemtasks')) {
			if($tasks->getCount()) {
				$active = $this->activeType == 'task' ? $this->activeItem : '';
				$fldTree = new flWidgetTreeView();

				// Build the categories with fields
				$cats = $mgr->getCategories();
				foreach($cats as $catID => $catName) {
					$fl = $tasks->getInCat($catID);
					if(count($fl)) {
						$ft = new flWidgetTreeView();
						foreach($fl as $id => $name)
							$ft->addNode(new flWidgetTreeLinkNode($name, $this->assemble('', 'elements', array('task', $id)), flWidgetTreeNode::TASK))
								->setLastNodeActive($active == $id);
						$fldTree->addNode(new flWidgetTreeChildNode($catName, $ft, false));
					}
				}

				$fl = $tasks->getInCat(0);
				foreach($fl as $id => $name)
					$fldTree->addNode(new flWidgetTreeLinkNode($name, $this->assemble('', 'elements', array('task', $id)), flWidgetTreeNode::TASK))
						->setLastNodeActive($active == $id);

				$topTree->addNode(new flWidgetTreeChildNode(__('Background Tasks', 'cfBackend'), $fldTree, false));
			}
			else
				$topTree->addNode(new flWidgetTreeNode(__('Background Tasks', 'cfBackend'), flWidgetTreeNode::CONTAINER));
		}

		// Build the custom field tree
		if($this->user->hasPermission('cfContent.elemfields')) {
			if($fields->getCount()) {
				$active = $this->activeType == 'field' ? $this->activeItem : '';
				$fldTree = new flWidgetTreeView();

				// Build the categories with fields
				$cats = $mgr->getCategories();
				foreach($cats as $catID => $catName) {
					$fl = $fields->getFieldsInCat($catID);
					if(count($fl)) {
						$ft = new flWidgetTreeView();
						foreach($fl as $id => $name)
							$ft->addNode(new flWidgetTreeLinkNode($name, $this->assemble('', 'elements', array('field', $id, 'view')), flWidgetTreeNode::FIELD))
								->setLastNodeActive($active == $id);
						$fldTree->addNode(new flWidgetTreeChildNode($catName, $ft, false));
					}
				}

				$fl = $fields->getFieldsInCat(0);
				foreach($fl as $id => $name)
					$fldTree->addNode(new flWidgetTreeLinkNode($name, $this->assemble('', 'elements', array('field', $id, 'view')), flWidgetTreeNode::FIELD))
						->setLastNodeActive($active == $id);

				$topTree->addNode(new flWidgetTreeChildNode(__('Custom Fields', 'cfBackend'), $fldTree, false));
			}
			else
				$topTree->addNode(new flWidgetTreeNode(__('Custom Fields', 'cfBackend'), flWidgetTreeNode::CONTAINER));
		}

		// Build the theme tree
		if($this->user->hasPermission('cfContent.elemtheme')) {
			if($mgr->getChildThemeCount()) {
				$active = $this->activeType == 'theme' ? $this->activeItem : '';
				$themes = new flWidgetTreeView();
				$tl = $mgr->getChildThemesByID();
				foreach($tl as $id => $name) {
					$tree = new flWidgetTreeView();
					$this->buildChunkTree($tree, $mgr, $id, false);
					$this->buildChunkTree($tree, $mgr, $id, true);
					$themes->addNode(new flWidgetTreeChildNode($name, $tree, false, $this->assemble('', 'elements', array('theme', $id))))
							->setLastNodeActive($active == $id);
				}
				$topTree->addNode(new flWidgetTreeChildNode(__('Child Themes', 'cfBackend'), $themes, false, $this->assemble('', 'elements', array('theme'))));
			}
			else
				$topTree->addNode(new flWidgetTreeLinkNode(__('Child Themes', 'cfBackend'), $this->assemble('', 'elements', array('theme')), flWidgetTreeNode::CONTAINER));
		}

		// Build the category tree
		$link = $this->assemble('', 'elements', array('category'));
		if($mgr->getCategoryCount()) {
			$active = $this->activeType == 'category' ? $this->activeItem : '';
			$cats = new flWidgetTreeView();
			$cl = $mgr->getCategories();
			foreach($cl as $id => $name)
				$cats->addNode(new flWidgetTreeLinkNode($name . ' (' . ($mgr->countInCategory($id) + $fields->getCount($id)) . ')', $this->assemble('', 'elements', array('category', $id)), flWidgetTreeNode::CONTAINER))
					->setLastNodeActive($active == $id);
			$topTree->addNode(new flWidgetTreeChildNode(__('Categories', 'cfBackend'), $cats, false, $link));
		}
		else
			$topTree->addNode(new flWidgetTreeLinkNode(__('Categories', 'cfBackend'), $link, flWidgetTreeNode::CONTAINER));

		// Build floating action menu
		$fam = new flWidgetFloatingActionMenu();
		$fam->setCSS('[[+css]]');

		if($this->user->hasPermission('cfContent.elemchunk'))
			$fam->addAction('view_module', __('New Chunk', 'cfBackend'), $this->assemble('', 'elements', array('chunk')));
		if($this->user->hasPermission('cfContent.elemsnippet'))
			$fam->addAction('note_add', __('New Snippet', 'cfBackend'), $this->assemble('', 'elements', array('snippet')));
		if($this->user->hasPermission('cfContent.elemtemplate'))
			$fam->addAction('view_quilt', __('New Template', 'cfBackend'), $this->assemble('', 'elements', array('template')));
		if($this->user->hasPermission('cfContent.elemfields'))
			$fam->addAction('input', __('New Custom Field', 'cfBackend'), $this->assemble('', 'elements', array('field', '0', 'view')));
		if($this->user->hasPermission('cfContent.elemtasks'))
			$fam->addAction('timer', __('New Background Task', 'cfBackend'), $this->assemble('', 'elements', array('task')));

		$this->tpl->registerChunk('fam', $fam->render());

		// Draw the page
		$this->renderPage(__('Elements', 'cfBackend'), $content, $this->renderBlock('', $topTree->render($this->tpl->getCodeBuffer())));
	}
}
