<?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\module\content
 * @copyright Copyright (c) 2012 - 2016 clearFusionCMS. All rights reserved.
 * @link http://clearfusioncms.com
 */

/**
 * contentSettings Class
 *
 * Class to implement settings for clearFusionCMS.
 *
 * @package clearFusionCMS\module\content
 */
class contentSettings implements cfModuleSettingsInterface {
	/**
	 * Get a list of settings groups.
	 *
	 * @return array Array of setting groups, name => func name
	 */
	function getSettingsGroups() {
		if(!clearFusionCMS::getUser()->hasPermission('core.syssettings'))
			return array();

		return array(
			__('Content Editing', 'cfBackend') => 'ctntediting',
			__('Security', 'cfBackend') => 'security',
			__('Audit Trail', 'cfBackend') => 'audit',
			__('Email Setting', 'cfBackend') => 'email',
			__('Password Email', 'cfBackend') => 'pwdemail',
			__('File Storage', 'cfBackend') => 'filestore',
			__('Manager Theme', 'cfBackend') => 'template',
			__('Manager Locale', 'cfBackend') => 'locale',
			__('System Timezone', 'cfBackend') => 'timezone',
			__('Dashboard', 'cfBackend') => 'dashboard',
			__('Rebuild Search', 'cfBackend') => 'rebuildsearch',
			__('Cron', 'cfBackend') => 'cron',
			__('reCAPTCHA', 'cfBackend') => 'recaptcha',
			__('Facebook API', 'cfBackend') => 'facebook',
			__('Google API', 'cfBackend') => 'google',
			__('Twitter API', 'cfBackend') => 'twitterapi',
			__('System Information', 'cfBackend') => 'sysinfo'
		);
	}

	/**
	 * Implement editing of email settings.
	 *
	 * @param flForm $form The form object to use.
	 * @param flDb $db The database object.
	 * @return bool true if the settings were updated, false if failed to update or null if no change.
	 */
	function emailSetting($form, $db) {
		if(!clearFusionCMS::getUser()->hasPermission('core.syssettings'))
			return null;

		$cfg = clearFusionCMS::getSiteConfig('cfSystemEmail');

		$sysemail = new flFormElementText($form, 'sysemail', $cfg->sysEmail);
		$sysemail->setLabel(__('System Email', 'cfBackend'))
				->setRequired()
				->setDescription(__('The email address used to send system emails from e.g. password reset emails.', 'cfBackend'))
				->addValidator(new flFormValidatorRequired())
				->addValidator(new flFormValidatorEmail());

		$smtpserver = new flFormElementText($form, 'smtpserver', $cfg->smtpServer);
		$smtpserver->setLabel(__('SMTP Server', 'cfBackend'))
				->setDescription(__('The SMTP server or leave blank to use PHP mail to send emails.', 'cfBackend'));

		$smtpport = new flFormElementText($form, 'smtpport', $cfg->smtpPort);
		$smtpport->setLabel(__('SMTP Port', 'cfBackend'))
				->setDescription(__('The SMTP server port to use, default is 25.', 'cfBackend'))
				->addValidator(new flFormValidatorInteger());

		$smtpuser = new flFormElementText($form, 'smtpuser', $cfg->smtpUser);
		$smtpuser->setLabel(__('SMTP User', 'cfBackend'))
				->setDescription(__('Optional user to connect to the SMTP server with.', 'cfBackend'));

		$smtppwd = new flFormElementPassword($form, 'smtppwd', $cfg->smtpPass);
		$smtppwd->setLabel(__('SMTP Password', 'cfBackend'))
				->setDescription(__('Password for user used to connect to the SMTP server with.', 'cfBackend'));

		$submit = new flFormElementSubmit($form, 'update');
		$submit->setCaption(__('Update Settings', 'cfBackend'))->setCSS('primary');

		$form->groupElements('system', array($sysemail))
				->setGroupLabel('system', __('System Email Sender', 'cfBackend'))
				->groupElements('smtp', array($smtpserver, $smtpport, $smtpuser, $smtppwd))
				->setGroupLabel('smtp', __('SMTP Server Settings (optional)', 'cfBackend'))
				->groupElements('submit', array($submit), new flFormDecoratorList(new flFormDecoratorDl()));

		$form->focusElement($sysemail);

		if($form->isSubmitted() && $form->isValid()) {
			cfSettingsAudit::update(
				__('CMS', 'cfBackend'),
				__('Email Setting', 'cfBackend'),
				[
					'sysEmail' => $cfg->get('sysEmail'),
					'smtpServer' => $cfg->get('smtpServer'),
					'smtpPort' => $cfg->get('smtpPort'),
					'smtpUser' => $cfg->get('smtpUser')
				]
			);

			$cfg->sysEmail = trim($sysemail->getValue());
			$cfg->smtpServer = trim($smtpserver->getValue());
			$cfg->smtpPort = trim($smtpport->getValue());
			$cfg->smtpUser = trim($smtpuser->getValue());
			$cfg->smtpPass = $smtppwd->getValue();

			return true;
		}

		return null;
	}

	/**
	 * Implement security settings.
	 *
	 * @param flForm $form The form object to use.
	 * @param flDb $db The database object.
	 * @return bool true if the settings were updated, false if failed to update or null if no change.
	 */
	function securitySetting($form, $db) {
		if(!clearFusionCMS::getUser()->hasPermission('core.syssettings'))
			return null;

		$cfg = new flConfigDb('cfSystemSecurity', $db);

		$twoFactor = new flFormElementCheckbox($form, 'twofactor', $cfg->get('twoFactor', false));
		$twoFactor->setLabel(__('Enable two factor authentication'))
			->setDescription(__('If enabled then CMS manager users will be able to enable two factor authentication using Google Authenticator to help secure access.'));

		$twoFactorName = new flFormElementText($form, 'twofactorname', $cfg->get('twoFactorName', clearFusionCMS::getSiteName()));
		$twoFactorName->setLabel(__('Account Name'))
			->setDescription(__('The account name to associate with keys.'));

		$submit = new flFormElementSubmit($form, 'update');
		$submit->setCaption(__('Update Settings', 'cfBackend'))->setCSS('primary');

		$form->groupElements('tfa', array($twoFactor, $twoFactorName))
				->setGroupLabel('tfa', __('Two Factor Authentication', 'cfBackend'))
				->groupElements('submit', array($submit), new flFormDecoratorList(new flFormDecoratorDl()));

		$form->focusElement($twoFactor);

		if($form->isSubmitted() && $form->isValid()) {
			cfSettingsAudit::update(
				__('CMS', 'cfBackend'),
				__('Security', 'cfBackend'),
				[
					'twoFactor' => $cfg->get('twoFactor'),
					'twoFactorName' => $cfg->get('twoFactorName')
				]
			);

			$cfg->twoFactor = $twoFactor->getValue();
			$cfg->twoFactorName = $twoFactorName->getValue();

			return true;
		}

		return null;
	}

	/**
	 * Implement audit trail settings.
	 *
	 * @param flForm $form The form object to use.
	 * @param flDb $db The database object.
	 * @return bool true if the settings were updated, false if failed to update or null if no change.
	 */
	function auditSetting($form, $db) {
		if(!clearFusionCMS::getUser()->hasPermission('core.syssettings'))
			return null;

		$cfg = new flConfigDb('cfSystemAudit', $db);

		$days = new flFormElementInteger($form, 'days', 0, null, $cfg->get('days', 30));
		$days->setLabel(__('Audit Log Retention in Days', 'cfBackend'))
			->setDescription(__('Settings days to 0 disables the audit log.', 'cfBackend'))
			->setRequired()
			->addValidator(new flFormValidatorRequired());

		$submit = new flFormElementSubmit($form, 'update');
		$submit->setCaption(__('Update Settings', 'cfBackend'))->setCSS('primary');

		$form->groupElements('flds', array($days))
				->groupElements('submit', array($submit), new flFormDecoratorList(new flFormDecoratorDl()));

		$form->focusElement($days);

		if($form->isSubmitted() && $form->isValid()) {
			cfSettingsAudit::update(
				__('CMS', 'cfBackend'),
				__('Audit Trail', 'cfBackend'),
				[
					'days' => $cfg->get('days')
				]
			);

			$cfg->days = $days->getValue();

			return true;
		}

		return null;
	}

	/**
	 * Implement editing of timezone settings.
	 *
	 * @param flForm $form The form object to use.
	 * @param flDb $db The database object.
	 * @return bool true if the settings were updated, false if failed to update or null if no change.
	 */
	function timezoneSetting($form, $db) {
		if(!clearFusionCMS::getUser()->hasPermission('core.syssettings'))
			return null;

		$cfg = clearFusionCMS::getSiteConfig('cfSystemTZ');

		$systz = new flFormElementSelect($form, 'systz', $cfg->timezone, flDateTzMenu::get());
		$systz->setLabel(__('System Timezone', 'cfBackend'));

		$submit = new flFormElementSubmit($form, 'update');
		$submit->setCaption(__('Update Settings', 'cfBackend'))->setCSS('primary');

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

		if($form->isSubmitted() && $form->isValid()) {
			cfSettingsAudit::update(
				__('CMS', 'cfBackend'),
				__('System Timezone', 'cfBackend'),
				[
					'timezone' => $cfg->get('timezone')
				]
			);

			$cfg->timezone = $systz->getValue();
			return true;
		}

		return null;
	}

	/**
	 * Implement editing of admin template.
	 *
	 * @param flForm $form The form object to use.
	 * @param flDb $db The database object.
	 * @return bool true if the settings were updated, false if failed to update or null if no change.
	 */
	function templateSetting($form, $db) {
		if(!clearFusionCMS::getUser()->hasPermission('core.syssettings'))
			return null;

		$themeMgr = new flTemplateManager($db);
		$templates = array('' => __('[default]', 'cfBackend')) + $themeMgr->getThemeList(true);

		$cfg = clearFusionCMS::getSiteConfig('cfSystemManager');

		$template = new flFormElementSelect($form, 'template', $cfg->template, $templates);
		$template->setLabel(__('Manager Theme', 'cfBackend'));

		$submit = new flFormElementSubmit($form, 'update');
		$submit->setCaption(__('Update Settings', 'cfBackend'))->setCSS('primary');

		$form->groupElements('submit', array($submit), new flFormDecoratorList(new flFormDecoratorDl()));
		$form->focusElement($template);

		if($form->isSubmitted() && $form->isValid()) {
			cfSettingsAudit::update(
				__('CMS', 'cfBackend'),
				__('Manager Theme', 'cfBackend'),
				[
					'template' => $cfg->get('template')
				]
			);

			$cfg->template = $template->getValue();
			return true;
		}

		return null;
	}

	/**
	 * Implement setting of the manager locale.
	 *
	 * @param flForm $form The form object to use.
	 * @param flDb $db The database object.
	 * @return bool true if the settings were updated, false if failed to update or null if no change.
	 */
	function localeSetting($form, $db) {
		if(!clearFusionCMS::getUser()->hasPermission('core.syssettings'))
			return null;

		$folders = glob(FLAPPPATH . 'lang/*', GLOB_ONLYDIR);
		$locales = array('en_US' => 'en_US');
		foreach($folders as $path) {
			$path = basename($path);
			$locales[$path] = $path;
		}

		$cfg = clearFusionCMS::getSiteConfig('cfSystemManager');

		$locale = new flFormElementSelect($form, 'locale', $cfg->get('locale', 'en_US'), $locales);
		$locale->setLabel(__('Manager Locale', 'cfBackend'))
			->setDescription(__('After setting a new locale the change will not take effect until you navigate away from this page.', 'cfBackend'));

		$submit = new flFormElementSubmit($form, 'update');
		$submit->setCaption(__('Update Settings', 'cfBackend'))->setCSS('primary');

		$form->groupElements('submit', array($submit), new flFormDecoratorList(new flFormDecoratorDl()));
		$form->focusElement($locale);

		if($form->isSubmitted() && $form->isValid()) {
			cfSettingsAudit::update(
				__('CMS', 'cfBackend'),
				__('Manager Locale', 'cfBackend'),
				[
					'locale' => $cfg->get('locale')
				]
			);

			$cfg->locale = $locale->getValue();
			return true;
		}

		return null;
	}

	/**
	 * Implement content editing settings.
	 *
	 * @param flForm $form The form object to use.
	 * @param flDb $db The database object.
	 * @return bool true if the settings were updated, false if failed to update or null if no change.
	 */
	function ctnteditingSetting($form, $db) {
		if(!clearFusionCMS::getUser()->hasPermission('core.syssettings'))
			return null;

		$cfg = clearFusionCMS::getSiteConfig('cfSystemCtntEdit');

		// Content type
		$ct = array();
		$result = $db->query('SELECT id,typename FROM cf_contenttype ORDER BY typename');
		while($row = $result->fetch())
			$ct[$row['id']] = $row['typename'];
		$contType = new flFormElementSelect($form, 'contenttype', $cfg->defContentType, $ct);
		$contType->setLabel(__('Default Content Type', 'cfBackend'));

		// Template
		$mgr = new flTemplateManager($db);
		$siteCfg = clearFusionCMS::getSiteConfig('cfSystemSite');
		$template = new flFormElementSelect($form, 'template', $cfg->defTemplate, $mgr->getTemplateList($siteCfg->siteTheme));
		$template->setLabel(__('Default Template', 'cfBackend'));

		// WYSIWYG Editor
		$el = cfEditor::getAvailable(false, true);

		$editor = new flFormElementSelect($form, 'editor', $cfg->editor, $el);
		$editor->setLabel(__('WYSIWYG Editor', 'cfBackend'));

		$codeEditor = new flFormElementSelect($form, 'codeEditor', $cfg->get('codeEditor', ''), $el);
		$codeEditor->setLabel(__('Code Editor', 'cfBackend'));

		$css = new flFormElementText($form, 'css', $cfg->editorCSS);
		$css->setLabel(__('Content CCS File', 'cfBackend'))
			->setDescription(__('Address of a CSS file which holds styles for use in your content, the styles are made available in the editor.', 'cfBackend'))
			->addValidator(new flFormValidatorMaxLength(250));

		$useSlugs = new flFormElementCheckbox($form, 'useslugs', $cfg->get('useSlugs', false));
		$useSlugs->setLabel(__('Use Slug Names for Document Tree', 'cfBackend'))
			->setDescription(__('If ticked the documents slug will be used in the document tree, otherwise the document title will be used.', 'cfBackend'));

		$submit = new flFormElementSubmit($form, 'update');
		$submit->setCaption(__('Update Settings', 'cfBackend'))->setCSS('primary');

		$form->groupElements('submit', array($submit), new flFormDecoratorList(new flFormDecoratorDl()));
		$form->focusElement($contType);

		if($form->isSubmitted() && $form->isValid()) {
			cfSettingsAudit::update(
				__('CMS', 'cfBackend'),
				__('Content Editing', 'cfBackend'),
				[
					'defContentType' => $cfg->get('defContentType'),
					'defTemplate' => $cfg->get('defTemplate'),
					'editor' => $cfg->get('editor'),
					'codeEditor' => $cfg->get('codeEditor'),
					'editorCSS' => $cfg->get('editorCSS'),
					'useSlugs' => $cfg->get('useSlugs')
				]
			);

			$cfg->defContentType = $contType->getValue();
			$cfg->defTemplate = $template->getValue();
			$cfg->editor = $editor->getValue();
			$cfg->codeEditor = $codeEditor->getValue();
			$cfg->editorCSS = $css->getValue();
			$cfg->useSlugs = $useSlugs->getValue();

			return true;
		}

		return null;
	}

	/**
	 * Implement editing file storage locations for content.
	 *
	 * @param flForm $form The form object to use.
	 * @param flDb $db The database object.
	 * @return bool true if the settings were updated, false if failed to update or null if no change.
	 */
	function filestoreSetting($form, $db) {
		if(!clearFusionCMS::getUser()->hasPermission('core.syssettings'))
			return null;

		$cfg = clearFusionCMS::getSiteConfig('cfSystemFileStore');

		$pub = new flFormElementText($form, 'pub', $cfg->publicFiles);
		$pub->setLabel(__('Public File Storage Location', 'cfBackend'));

		$mod = new flFormElementText($form, 'mod', $cfg->publicStorage);
		$mod->setLabel(__('Public Module File Storage Location', 'cfBackend'));

		$priv = new flFormElementText($form, 'priv', $cfg->privateFiles);
		$priv->setLabel(__('Private File Storage Location', 'cfBackend'));

		$privmod = new flFormElementText($form, 'privmod', $cfg->privateStorage);
		$privmod->setLabel(__('Private Module File Storage Location', 'cfBackend'));

		$submit = new flFormElementSubmit($form, 'update');
		$submit->setCaption(__('Update Settings', 'cfBackend'))->setCSS('primary');

		$form->groupElements('submit', array($submit), new flFormDecoratorList(new flFormDecoratorDl()));
		$form->focusElement($pub);

		$r = null;
		if($form->isSubmitted() && $form->isValid()) {
			cfSettingsAudit::update(
				__('CMS', 'cfBackend'),
				__('File Storage', 'cfBackend'),
				[
					'publicFiles' => $cfg->get('publicFiles'),
					'privateFiles' => $cfg->get('privateFiles'),
					'publicStorage' => $cfg->get('publicStorage'),
					'privateStorage' => $cfg->get('privateStorage')
				]
			);

			$cfg->publicFiles = rtrim($pub->getValue(), '/');
			$cfg->privateFiles = rtrim($priv->getValue(), '/');
			$cfg->publicStorage = rtrim($mod->getValue(), '/');
			$cfg->privateStorage = rtrim($privmod->getValue(), '/');
			$r = true;
		}

		$pubWarn = !is_writable($cfg->publicFiles) || !is_readable($cfg->publicFiles) ? clearFusionCMS::getController()->alertMessage('Error', __('The folder you have chosen to store your public files in is not readable or writable, please ensure that the folder allows full access to the user this script runs as.', 'cfBackend')) : '';
		$privWarn = !is_writable($cfg->privateFiles) || !is_readable($cfg->privateFiles) ? clearFusionCMS::getController()->alertMessage('Error', __('The folder you have chosen to store your private files in is not readable or writable, please ensure that the folder allows full access to the user this script runs as.', 'cfBackend')) : '';
		$modWarn = !is_writable($cfg->publicStorage) || !is_readable($cfg->publicStorage) ? clearFusionCMS::getController()->alertMessage('Error', __('The folder you have chosen to store your public module files in is not readable or writable, please ensure that the folder allows full access to the user this script runs as.', 'cfBackend')) : '';
		$modPrivWarn = !is_writable($cfg->privateStorage) || !is_readable($cfg->privateStorage) ? clearFusionCMS::getController()->alertMessage('Error', __('The folder you have chosen to store your private module files in is not readable or writable, please ensure that the folder allows full access to the user this script runs as.', 'cfBackend')) : '';

		$pub->setDescription(__('The location on disk where public files are stored, this must be a folder within the web space.', 'cfBackend') . $pubWarn);
		$mod->setDescription(__('The location on disk where module asset files are stored, this must be a folder within the web space.', 'cfBackend') . $modWarn);
		$priv->setDescription(clearFusionCMS::getController()->alertMessage('Info', __('The location on disk where private files are stored, this should be a folder outside of your web space for maximum security. If you are hosting under Apache then the default location although not recommended should suffice.', 'cfBackend')) . $privWarn);
		$privmod->setDescription(clearFusionCMS::getController()->alertMessage('Info', __('The location on disk where private module files are stored, this should be a folder outside of your web space for maximum security. If you are hosting under Apache then the default location although not recommended should suffice.', 'cfBackend')) . $modPrivWarn);

		return $r;
	}

	/**
	 * Implement editing of dashboard groups.
	 *
	 * @param flForm $form The form object to use.
	 * @param flDb $db The database object.
	 * @return bool true if the settings were updated, false if failed to update or null if no change.
	 */
	function dashboardSetting($form, $db) {
		if(!clearFusionCMS::getUser()->hasPermission('core.syssettings'))
			return null;

		$elems = array();

		$mgr = new cfDashboardMgr($db);
		$grps = $mgr->getGroups();

		foreach($grps as $name => $dispName) {
			$e = new flFormElementText($form, "grp_$name", $mgr->getGroupOrder($name));
			$e->setLabel(sprintf(__('%s Display Order', 'cfBackend'), $dispName))
				->setRequired()
				->addValidator(new flFormValidatorRequired())
				->addValidator(new flFormValidatorInteger());
			$elems[$name] = $e;
		}

		$pane = new flFormElementPane($form, 'info', clearFusionCMS::getController()->alertMessage('Info', __('Groups with smaller numbers are shown before those with larger order numbers.', 'cfBackend')));

		$submit = new flFormElementSubmit($form, 'update');
		$submit->setCaption(__('Update Settings', 'cfBackend'))->setCSS('primary');

		$form->groupElements('submit', array($submit), new flFormDecoratorList(new flFormDecoratorDl()));
		$form->focusElement(reset($elems));

		$r = null;
		if($form->isSubmitted() && $form->isValid()) {
			cfSettingsAudit::update(
				__('CMS', 'cfBackend'),
				__('Dashboard', 'cfBackend'),
				$grps
			);

			foreach($grps as $name => $dispName)
				$mgr->setGroupOrder($name, $elems[$name]->getValue());
			$r = true;
		}

		return $r;
	}

	/**
	 * Implement system information.
	 *
	 * @param flForm $form The form object to use.
	 * @param flDb $db The database object.
	 * @return bool true if the settings were updated, false if failed to update or null if no change.
	 */
	function sysinfoSetting($form, $db) {
		$r = null;

		if(clearFusionCMS::getUser()->hasPermission('core.syssettings')) {
			$cfg = new flConfigDb('cfSystemLicence', $db);
			$cfInfo = new flModuleMgr($db);
			$info = $cfInfo->getInfo('content');

			$pane = new flFormElementPane($form, 'sysinfo', '');

			$licCode = new flFormElementText($form, 'licence', $cfg->get('code', ''));
			$licCode->setLabel(__('Your License Number', 'cfBackend'))
				->setRequired()
				->addValidator(new flFormValidatorRequired())
				->addValidator(new flFormValidatorRegex('/^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/i', __('The license code must be in the format XXXX-XXXX-XXXX-XXXX-XXXX-XXXX.', 'cfBackend')))
				->setDescription(__('A license code could not be found, please enter your license code above.', 'cfBackend'));

			$submit = new flFormElementSubmit($form, 'submit');
			$submit->setCaption(__('Save License Code', 'cfBackend'))->setCSS('primary');

			$form->groupElements('info', array($pane))
				->groupElements('lic', array($licCode))
				->addTab(__('System Info', 'cfBackend'), 'info')
				->addTab(__('License', 'cfBackend'), 'lic')
				->groupElements('submit', array($submit), new flFormDecoratorList(new flFormDecoratorDl()));
			$form->focusElement($licCode);

			if($form->isSubmitted() && $form->isValid()) {

				// Phone home
				$secret = 'RLV4T9U6AJ4CW8COK80400G8W8W4O0CH';
				$payload = array(
					'domain' => clearFusionCMS::getRequest()->getServerName(),
					'key' => strtoupper($licCode->getValue()),
					'subject' => function_exists('ioncube_file_is_encoded') && ioncube_file_is_encoded() ? 'clearFusionCMSFree' : 'clearFusionCMSPro',
					'version' => $info->version
				);
				$payload['hash'] = md5($secret . $payload['domain'] . $payload['key'] . $payload['subject'] . $payload['version']);

				$client = new flHttpClient('http://clearfusioncms.com/licence-remote-endpoint.json');
				$client->setPostData($payload);
				if($client->sendRequest() && $client->getResponseCode() == 200) {
					$res = json_decode($client->getBody());
					if($res->result != 'ok' && getenv('CLEARFUSIONCMS_ENV') !== 'development') {
						$licCode->forceError($res->message);
						$r = false;
					}
					else {
						cfSettingsAudit::update(
							__('CMS', 'cfBackend'),
							__('System Information', 'cfBackend'),
							[]
						);
						$cfg->code = strtoupper($licCode->getValue());
						$r = true;
					}
				}
				else
					$licCode->forceError('Unable to contact license server, please try again.');
			}

			$table = new flWidgetTable();
			$table->setClass('tablewidget bordered striped rowhover allleft')
				->setHeadings('', 'Version')
				->setColData('what', 'version')->skipEncode('version');

			$table->addRow(array('what' => __('CMS', 'cfBackEnd'), 'version' => $info->version));
			$table->addRow(array('what' => __('MySQL', 'cfBackEnd'), 'version' => $db->version()));
			$table->addRow(array('what' => __('PHP', 'cfBackEnd'), 'version' => phpversion()));
			$table->addRow(array('what' => __('Operating System', 'cfBackEnd'), 'version' => php_uname('s') . ' ' . php_uname('r') . ' ' . php_uname('v') . ' ' . php_uname('m')));
			$table->addRow(array('what' => __('Server Software', 'cfBackEnd'), 'version' => isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : ''));
			$table->addRow(array('what' => __('License Number', 'cfBackEnd'), 'version' => $cfg->code));

			$dbInfo = parse_url(clearFusionCMS::getConfig()->db);
			$table->addRow(array('what' => __('DB Name', 'cfBackEnd'), 'version' => trim($dbInfo['path'], '/')));

			$pane->setContent($table->render());
		}

		return $r;
	}

	/**
	 * Implement editing the password reset email.
	 *
	 * @param flForm $form The form object to use.
	 * @param flDb $db The database object.
	 * @return bool true if the settings were updated, false if failed to update or null if no change.
	 */
	function pwdemailSetting($form, $db) {
		if(!clearFusionCMS::getUser()->hasPermission('core.syssettings'))
			return null;

		$cfg = new flConfigDb('cfSystemEmails', $db);

		$subject = new flFormElementText($form, 'subject', $cfg->pwdResetSubject);
		$subject->setLabel(__('The Subject Line', 'cfBackend'))
			->setDescription(__('The subject line for password reset emails. Use &#91;&#91;+domain&#93;&#93; to include the domain name.', 'cfBackend'))
			->setRequired()
			->addValidator(new flFormValidatorRequired())
			->addValidator(new flFormValidatorMaxLength(255));

		$email = new flFormElementWYSIWYG($form, 'email', $cfg->pwdResetEmail);
		$email->setLabel(__('Email Content', 'cfBackend'))
			->setDescription(__('The contents of the password reset email. Use &#91;&#91;+domain&#93;&#93; to include the domain name and &#91;&#91;+hash&#93;&#93; where you want the authorization hash to show.', 'cfBackend'))
			->setRequired()
			->setRows(20)
			->editorSize('100%', '225')
			->addValidator(new flFormValidatorRequired())
			->addValidator(new flFormValidatorMaxLength(65000))
			->setDB($db)
			->editorModule(clearFusionCMS::getController()->getCodeEditorModule())
			->editorContentType('text');

		$submit = new flFormElementSubmit($form, 'update');
		$submit->setCaption(__('Update Settings', 'cfBackend'))->setCSS('primary');

		$form->groupElements('submit', array($submit), new flFormDecoratorList(new flFormDecoratorDl()));
		$form->focusElement($subject);

		$r = null;
		if($form->isSubmitted() && $form->isValid()) {
			cfSettingsAudit::update(
				__('CMS', 'cfBackend'),
				__('Password Email', 'cfBackend'),
				[
					'pwdResetSubject' => $cfg->get('pwdResetSubject'),
					'pwdResetEmail' => $cfg->get('pwdResetEmail')
				]
			);

			$cfg->pwdResetSubject = $subject->getValue();
			$cfg->pwdResetEmail = $email->getValue();
			$r = true;
		}

		return $r;
	}

	/**
	 * Implement search reindexing.
	 *
	 * @param flForm $form The form object to use.
	 * @param flDb $db The database object.
	 * @return bool true if the settings were updated, false if failed to update or null if no change.
	 */
	function rebuildsearchSetting($form, $db) {
		if(!clearFusionCMS::getUser()->hasPermission('core.syssettings'))
			return null;

		$toIndex = array();

		$table = new flWidgetTable();
		$table->setClass('tablewidget bordered striped')
			->setHeadings(__('Module', 'cfBackend'), __('Number of Content Items', 'cfBackend'))
			->setColData('module', 'items');

		$modMgr = new flModuleMgr($db);
		$modules = $modMgr->getInstalled('SearchHelper');
		$modules['content'] = '0';

		$helpers = array();

		foreach($modules as $module => $ver) {
			$info = $modMgr->getInfo($module);

			$cls = "{$module}SearchHelper";
			$helpers[$module] = new $cls($db);
			$items = $helpers[$module]->getContentCount();

			$table->addRow(array('module' => $info->name, 'items' => $items));
			$toIndex[$module] = $items;
		}

		// If AJAX request then process it
		if(clearFusionCMS::getRequest()->isXmlHttpRequest()) {

			// If session info available
			$done = false;
			if(isset(clearFusionCMS::getSession()->getNamespace('cfManagerSession')->searchReindex)) {

				$total = array_sum($toIndex);
				$worker = clearFusionCMS::getSession()->getNamespace('cfManagerSession')->searchReindex;
				$left = array_sum($worker);

				// If elements left to process
				if($left) {
					// Get module to work on
					$w = array_keys($worker);
					$k = array_pop($w);
					$v = array_pop($worker);

					// Call the module to reindex
					$helpers[$k]->reindex($toIndex[$k] - $v, 100);

					// Adjust the amount left
					$d = min(100, $v);
					$left -= $d;
					if($v -= $d) // Still work to do on this module
						$worker[$k] = $v;
					clearFusionCMS::getSession()->getNamespace('cfManagerSession')->searchReindex = $worker;
					$percent = 100 - (int)(($left / $total) * 100);
				}

				if(!$left) {
					$percent = 100;
					$done = true;
					unset(clearFusionCMS::getSession()->getNamespace('cfManagerSession')->searchReindex);
		 			clearFusionCMS::getEventManager()->notify(new flEvent('system.afterSearchReindex'));
					clearFusionCMS::getCache()->flush();
				}
			}
			else {
				$percent = 100;
				$done = true;
			}

			clearFusionCMS::getResponse()->setContentType('application/json')
				->noCache()
				->setBody(json_encode(array('done' => $done, 'progress' => $percent)))
				->sendResponse();
			die;
		}
		// Show stats and wait for go
		else {
			// If session info available
			if(isset(clearFusionCMS::getSession()->getNamespace('cfManagerSession')->searchReindex)) {
				$pane = new flFormElementPane($form, 'status', '<div id="completedbar" aria-atomic="true" aria-live="polite"><strong style="width: 0%;">0%</strong></div>');
				$pane->setLabel(__('Rebuilding Search Index', 'cfBackend'))
					->setDescription(__('Please be patient as this may take some time to complete.', 'cfBackend'));

				clearFusionCMS::getCodeBuffer()->bufferReadyJS("
function updateReindex() {
	\$fl.ajax('" . clearFusionCMS::getRequest()->getSelf() . "', { type: 'post', success: function(d) {
		d = typeof d === 'object' ? d : window.JSON.parse(d);
		\$fl('#completedbar strong').html(d.progress + '%').css('width', d.progress + '%');
		if(!d.done)
			\$fl('#completedbar').timerOnce(10, updateReindex);
		else
			\$fl('#completedbar strong').html('" . __('Rebuild Completed', 'cfBackend') . "');
	}, async: false});
}
\$fl('#completedbar').timerOnce(10, updateReindex);
");

				// Issue before reindex event
				clearFusionCMS::getEventManager()->notify(new flEvent('system.beforeSearchReindex'));

				cfSettingsAudit::update(
					__('CMS', 'cfBackend'),
					__('Rebuild Search', 'cfBackend'),
					[]
				);
			}
			else {
				$pane = new flFormElementPane($form, 'info', $table->render());

				$submit = new flFormElementSubmit($form, 'reindex');
				$submit->setCaption(__('Reindex Content', 'cfBackend'))->setCSS('primary');

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

				if(count($toIndex) && $form->isSubmitted() && $form->isValid()) {
					clearFusionCMS::getSession()->getNamespace('cfManagerSession')->searchReindex = $toIndex;
					clearFusionCMS::getResponse()->redirectTo(clearFusionCMS::getRequest()->getSelf());
				}
			}
		}

		return null;
	}

	/**
	 * Implement settings for cron.
	 *
	 * @param flForm $form The form object to use.
	 * @param flDb $db The database object.
	 * @return bool true if the settings were updated, false if failed to update or null if no change.
	 */
	function cronSetting($form, $db) {
		if(!clearFusionCMS::getUser()->hasPermission('core.syssettings'))
			return null;

		$cfg = new flConfigDb('cfSystemCron', $db);
		$var = new flVariables('cfSystemCron', clearFusionCMS::getDb());

		$uri = clearFusionCMS::getRouter()->assemble('cfManagerCron', array(), true) . "?key=$var->key";

		$pseudo = new flFormElementCheckbox($form, 'usepseudo', $cfg->usePseudoCron);
		$pseudo->setLabel(__('Use pseudo cron', 'cfBackend'));

		$pane = new flFormElementPane($form, 'info', __("
<p>If not using the pseudo cron then you need to configure a cron job to run every 5 minutes, add <strong>one</strong> of the following to your systems crontab:</p>
<p><code>*/5 * * * * /usr/bin/wget -O - -q -t 1 $uri</code></p>
<p><code>*/5 * * * * curl --silent $uri</code></p>
<p><code>*/5 * * * *  /usr/bin/lynx -source $uri</code></p>
<p>If using cPanel select Cron jobs then from the first Common Settings drop down select Every 5 minutes, then in the command box enter one of:</p>
<p><code>/usr/bin/wget -O - -q -t 1 $uri</code></p>
<p><code>curl --silent $uri</code></p>
<p><code>/usr/bin/lynx -source $uri</code></p>", 'cfBackend') .
				clearFusionCMS::getController()->alertMessage('Warning', __('The value of key must be kept secret to maintain security.', 'cfBackend')));

		$submit = new flFormElementSubmit($form, 'update');
		$submit->setCSS('primary');

		$form->groupElements('submit', array($submit), new flFormDecoratorList(new flFormDecoratorDl()));
		$form->focusElement($pseudo);

		$r = null;
		if($form->isSubmitted() && $form->isValid()) {
			cfSettingsAudit::update(
				__('CMS', 'cfBackend'),
				__('Cron', 'cfBackend'),
				[
					'usePseudoCron' => $cfg->get('usePseudoCron')
				]
			);

			$cfg->usePseudoCron = $pseudo->getValue();
			if(!$cfg->usePseudoCron) {
				clearFusionCMS::getEventManager()->removeListener('system.beforePageRender', 'cfPseudoCron', 'addJS')
					->removeListener('system.beforeAdminPageRender', 'cfPseudoCron', 'addJS');
			}
			else {
				clearFusionCMS::getEventManager()->addListener('system.beforePageRender', 'cfPseudoCron', 'addJS')
					->addListener('system.beforeAdminPageRender', 'cfPseudoCron', 'addJS');
			}
			clearFusionCMS::getCache()->flush();
			$r = true;
		}

		return $r;
	}

	/**
	 * Implement editing of facebook API settings.
	 *
	 * @param flForm $form The form object to use.
	 * @param flDb $db The database object.
	 * @return bool true if the settings were updated, false if failed to update or null if no change.
	 */
	function facebookSetting($form, $db) {
		if(!clearFusionCMS::getUser()->hasPermission('core.syssettings'))
			return null;

		$cfg = clearFusionCMS::getSiteConfig('cfSystemSocialAPI');

		$appid = new flFormElementText($form, 'fbappid', $cfg->get('facebook_appid'));
		$appid->setLabel(__('Facebook App ID', 'cfBackend'));
		$appsecret = new flFormElementPassword($form, 'fbappsecret', $cfg->get('facebook_appsecret'));
		$appsecret->setLabel(__('Facebook App Secret', 'cfBackend'));

		if($cfg->get('facebook_appsecret')) {
			$appsecret->setPlaceholder('****************')->setCSS('keepPlaceholder');
		}

		new flFormElementPane($form, 'fbinfo',
			__('<p>You can create an application ID at <a href="https://developers.facebook.com/apps/">https://developers.facebook.com/apps/</a></p>', 'cfBackend') .
			sprintf(__('<ol>
<li>Click the Add a New App button.</li>
<li>Create an app name and enter your email address.</li>
<li>Click on Settings in the left menu.</li>
<li>Click Add Platform and select Website.</li>
<li>Under both App Domains and Site URL fields enter the domain name of your site: <strong>%s</strong></li>
<li>Click Save Changes</li>
<li>Select Show to see your App Secret, then copy both the App ID and App Secret to this page.</li>
</ol>', 'cfBackend'), clearFusionCMS::getRequest()->getBasePath(true))
		);

		$submit = new flFormElementSubmit($form, 'update');
		$submit->setCaption(__('Update Settings', 'cfBackend'))->setCSS('primary');

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

		if($form->isSubmitted() && $form->isValid()) {
			cfSettingsAudit::update(
				__('CMS', 'cfBackend'),
				__('Facebook API', 'cfBackend'),
				[]
			);

			$cfg->facebook_appid = $appid->getValue();
			$secret = $appsecret->getValue();
			if(!empty($secret))
				$cfg->facebook_appsecret = $secret;
			return true;
		}

		return null;
	}

	/**
	 * Implement editing of reCAPTCHA settings.
	 *
	 * @param flForm $form The form object to use.
	 * @param flDb $db The database object.
	 * @return bool true if the settings were updated, false if failed to update or null if no change.
	 */
	function recaptchaSetting($form, $db) {
		if(!clearFusionCMS::getUser()->hasPermission('core.syssettings'))
			return null;

		$cfg = clearFusionCMS::getSiteConfig('cfSystemSocialAPI');

		$siteKey = new flFormElementText($form, 'sitekey', $cfg->get('recaptcha_sitekey'));
		$siteKey->setLabel(__('Site Key', 'cfBackend'));

		$secretKey = new flFormElementText($form, 'secretkey', $cfg->get('recaptcha_secretkey'));
		$secretKey->setLabel(__('Secret Key', 'cfBackend'));

		$submit = new flFormElementSubmit($form, 'update');
		$submit->setCaption(__('Update Settings', 'cfBackend'))->setCSS('primary');

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

		if($form->isSubmitted() && $form->isValid()) {
			cfSettingsAudit::update(
				__('CMS', 'cfBackend'),
				__('reCAPTCHA', 'cfBackend'),
				[]
			);

			$cfg->recaptcha_sitekey = $siteKey->getValue();
			$cfg->recaptcha_secretkey = $secretKey->getValue();
			return true;
		}

		return null;
	}

	/**
	 * Implement Google API key.
	 *
	 * @param flForm $form The form object to use.
	 * @param flDb $db The database object.
	 * @return bool true if the settings were updated, false if failed to update or null if no change.
	 */
	function googleSetting($form, $db) {
		if(!clearFusionCMS::getUser()->hasPermission('core.syssettings'))
			return null;

		$cfg = clearFusionCMS::getSiteConfig('cfSystemSocialAPI');

		$apikey = new flFormElementText($form, 'apikey', $cfg->get('google_mapsapi', ''));
		$apikey->setLabel(__('Google Maps API Key', 'cfBackend'))
			->addValidator(new flFormValidatorMaxLength(200))
			->setDescription(__('Your Google Maps API key, you can obtain your own key from <a href="https://developers.google.com/maps/documentation/javascript/get-api-key">Google</a>', 'cfBackend'));

		$submit = new flFormElementSubmit($form, 'update');
		$submit->setCaption(__('Update Settings', 'cfBackend'))->setCSS('primary');

		$form->groupElements('submit', array($submit), new flFormDecoratorList(new flFormDecoratorDl()));
		$form->focusElement($apikey);

		if($form->isSubmitted() && $form->isValid()) {
			cfSettingsAudit::update(
				__('CMS', 'cfBackend'),
				__('Google API', 'cfBackend'),
				[]
			);

			$cfg->google_mapsapi = $apikey->getValue();

			clearFusionCMS::getCache()->flush();

			return true;
		}

		return null;
	}

	/**
	 * Implement twitter API settings.
	 *
	 * @param flForm $form The form object to use.
	 * @param flDb $db The database object.
	 * @return bool true if the settings were updated, false if failed to update or null if no change.
	 */
	function twitterapiSetting($form, $db) {
		if(!clearFusionCMS::getUser()->hasPermission('core.syssettings'))
			return null;

		$cfg = clearFusionCMS::getSiteConfig('cfSystemSocialAPI');

		$key = new flFormElementText($form, 'key', $origKey = $cfg->get('twitter_consumer', ''));
		$key->setLabel(__('Consumer Key', 'cfBackend'))
			->setRequired()
			->addValidator(new flFormValidatorRequired())
			->addValidator(new flFormValidatorMaxLength(200));

		$secret = new flFormElementText($form, 'secret', $cfg->get('twitter_secret', ''));
		$secret->setLabel(__('Consumer Secret', 'cfBackend'))
			->setRequired()
			->addValidator(new flFormValidatorRequired())
			->addValidator(new flFormValidatorMaxLength(200))
			->setDescription(clearFusionCMS::getController()->alertMessage('Info', __('To get a key and secret you must go to https://dev.twitter.com/apps/ and register an application for your site, once registered you will be provided with the Consumer key and Consumer secret which you will be able to paste in above.', 'cfBackend')));

		$submit = new flFormElementSubmit($form, 'update');
		$submit->setCaption(__('Update Settings', 'socialBackend'))->setCSS('primary');

		$form->groupElements('submit', array($submit), new flFormDecoratorList(new flFormDecoratorDl()));
		$form->focusElement($key);

		if($form->isSubmitted() && $form->isValid()) {
			cfSettingsAudit::update(
				__('CMS', 'cfBackend'),
				__('Twitter API', 'cfBackend'),
				[]
			);

			$cfg->twitter_consumer = $key->getValue();
			$cfg->twitter_secret = $secret->getValue();
			return true;
		}

		return null;
	}
}
