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

/**
 * formsBackendController Class
 *
 * Backend controller for editing & managing forms.
 *
 * @package clearFusionCMS\module\forms
 */
class formsBackendController extends cfBackendController {
	/**
	 * Editor action.
	 */
	function editorAction() {
		$this->testPermission('cfForms.edit');
		$this->breadcrumbTrail->addCrumb(__('Forms', 'formsBackend'), $this->assemble('forms', 'editor'));

		$content = '';
		$active = 0;
		$msg = $this->session->flashGet('cfManagerOpSuccess') ? $this->session->flashGet('cfManagerOpSuccess') : '';

		$route = new flRouterRoute(':cmd/:id', array('id' => '0'), array('cmd' => 'add|edit|delete', 'id' => '\d+'));
		if($params = $route->match($this->request->getParam('*', array()))) {
			switch($params['cmd']) {
				case 'add': // Fall through to edit
					$data = array('formid' => '0', 'displayname' => '', 'recipient' => '', 'subject' => '', 'intro' => '', 'thankyou' => '', 'htmlemail' => '', 'textemail' => '', 'formdata' => '', 'submitdelay' => 0, 'mentions' => '', 'storeemails' => 0);
				case 'edit':
					if($params['id']) {
						$active = $params['id'];
						$data = $this->db->fetchRow('SELECT * FROM cf_forms WHERE formid=?', $params['id']);
						if(($content = $this->lockObject('forms', $params['id'], true)) === true) $content = '';
					}

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

						$form = new flForm($this->request, 'editform', $this->assemble('forms', 'editor') . $route->assemble($params), true, null, new flFormDecoratorList(new flFormDecoratorDl()));
						$form->setCSS('metaSSubmit');

						$name = new flFormElementText($form, 'name', $data['displayname']);
						$name->setLabel(__('Display Name', 'formsBackend'))
							->setRequired()
							->addValidator(new flFormValidatorRequired())
							->addValidator(new flFormValidatorMaxLength(64));

						$subject = new flFormElementText($form, 'subject', $data['subject']);
						$subject->setLabel(__('Subject', 'formsBackend'))
							->setRequired()
							->addValidator(new flFormValidatorRequired())
							->addValidator(new flFormValidatorMaxLength(255));

						$recipient = new flFormElementText($form, 'recipient', $data['recipient']);
						$recipient->setLabel(__('Recipient', 'formsBackend'))
							->setDescription(__('Multiple recipient email addresses can be specified by separating each with a comma.', 'formsBackend'))
							->setRequired()
							->addValidator(new flFormValidatorRequired())
							->addValidator(new flFormValidatorMultiEmail())
							->addValidator(new flFormValidatorMaxLength(255));

						$mention = new flFormElementText($form, 'mentions', $data['mentions']);
						$mention->setLabel(__('Notification Service Mentions', 'formsBackend'))
							->addValidator(new flFormValidatorMaxLength(255))
							->setDescription(__('A list of usernames prefixed with @ to mention in notifications, usernames are defined by the notification service being used.', 'formsBackend'));

						$delay = new flFormElementNumber($form, 'delay', 0, null, $data['submitdelay']);
						$delay->setLabel(__('Submission Delay', 'formsBackend'))
							->setDescription(__('The minimum number of seconds that must elapse before the form will be accepted, 0 to disable delay.', 'formsBackend'))
							->setRequired()
							->addValidator(new flFormValidatorRequired());

						$store = new flFormElementCheckbox($form, 'store', $data['storeemails']);
						$store->setLabel(__('Store emails submitted via this form'));

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

						$intro = cfEditor::create(
							$this->getEditorModule($cfgForms->get('htmlEditor', '')),
							$form,
							'intro',
							$data['intro']
						);
						$intro->setLabel(__('Form Intro Text', 'formsBackend'))
							->addValidator(new flFormValidatorMaxLength(8192));

						$thankyou = cfEditor::create(
							$this->getEditorModule($cfgForms->get('htmlEditor', '')),
							$form,
							'thankyou',
							$data['thankyou']
						);
						$thankyou->setLabel(__('Thank You Text', 'formsBackend'))
							->addValidator(new flFormValidatorMaxLength(8192));

						$emailhtml = cfEditor::create(
							$this->getEditorModule($cfgForms->get('emailEditor', '')),
							$form,
							'emailhtml',
							$data['htmlemail']
						);
						$emailhtml->setLabel(__('Email Body (HTML)', 'formsBackend'))
							->setRequired()
							->addValidator(new flFormValidatorRequired())
							->addValidator(new flFormValidatorMaxLength(8192));

						$emailtext = cfEditor::create(
							$this->getCodeEditorModule(),
							$form,
							'emailtext',
							$data['textemail']
						);
						$emailtext->setLabel(__('Email Body (Text)', 'formsBackend'))
							->setRequired()
							->addValidator(new flFormValidatorRequired())
							->addValidator(new flFormValidatorMaxLength(8192))
							->editorContentType('text');

						$definition = cfEditor::create(
							$this->getCodeEditorModule(),
							$form,
							'definition',
							$data['formdata']
						);
						$definition->setLabel(__('Form Definition', 'formsBackend'))
							->setRequired()
							->addValidator(new flFormValidatorRequired())
							->addValidator(new flFormValidatorMaxLength(65536))
							->editorContentType('xml');

						$update = new flFormElementSubmit($form, 'update');
						$update->setCaption($params['id'] ? __('Update Form', 'formsBackend') : __('Create Form', 'formsBackend'))->setCSS('primary');

						$form->groupElements('gen', array($name, $subject, $recipient, $mention, $delay, $store))
							->groupElements('txt', array($intro, $thankyou))
							->groupElements('etpl', array($emailtext, $emailhtml))
							->groupElements('def', array($definition))
							->addTab(__('General', 'formsBackend'), array('gen'))
							->addTab(__('Text', 'formsBackend'), array('txt'))
							->addTab(__('Email Templates', 'formsBackend'), array('etpl'))
							->addTab(__('Definition', 'formsBackend'), array('def'));

						if($params['id']) {
							$del = new flFormElementSubmit($form, 'delete');
							$del->setCaption(__('Delete Form', 'formsBackend'))
								->setCSS('danger');
							$form->groupElements('submit', array($update, $del), new flFormDecoratorList(new flFormDecoratorDl()));
						}
						else
							$form->groupElements('submit', array($update), new flFormDecoratorList(new flFormDecoratorDl()));

						$form->focusElement($name);

						if($form->isSubmitted() && $form->isValid()) {
							$d = array(
								'displayname' => $name->getValue(),
								'subject' => $subject->getValue(),
								'recipient' => $recipient->getValue(),
								'intro' => $intro->getValue(),
								'thankyou' => $thankyou->getValue(),
								'htmlemail' => $emailhtml->getValue(),
								'textemail' => $emailtext->getValue(),
								'formdata' => $definition->getValue(),
								'submitdelay' => $delay->getValue(),
								'mentions' => $mention->getValue(),
								'storeemails' => $store->getValue()
							);

							// If update form
							if($params['id']) {
								if($del->getValue()) {
									$this->response->redirectTo($this->assemble('forms', 'editor', array(), true) . $route->assemble(array('cmd' => 'delete', 'id' => $params['id'])));
								}
								else {
									if($this->db->update('cf_forms', $d, 'formid=?', $params['id'])) {
										formsAudit::update($params['id'], $name->getValue(), $data);
										$this->session->flashSet('cfManagerOpSuccess', $this->alertMessage('Success', __('The form has been updated.', 'formsBackend')));
										$this->cache->flush();
										$this->response->redirectTo($this->assemble('forms', 'editor', array(), true) . $route->assemble(array('cmd' => 'edit', 'id' => $params['id'])));
									}
									else
										$msg = $this->alertMessage('Error', __('Unable to update the form, please try again.', 'formsBackend'));
								}
							}
							// Create a form
							else {
								if($this->db->insert('cf_forms', $d)) {
									formsAudit::create($this->db->insertID(), $name->getValue());
									$this->session->flashSet('cfManagerOpSuccess', $this->alertMessage('Success', __('The form has been created.', 'formsBackend')));
									$this->response->redirectTo($this->assemble('forms', 'editor', array(), true) . $route->assemble(array('cmd' => 'edit', 'id' => $this->db->insertID())));
								}
								else
									$msg = $this->alertMessage('Error', __('Unable to create the form, please try again.', 'formsBackend'));
							}
						}

						$content = $this->renderBlock(__('Form', 'formsBackend'), $form->render($this->tpl->getCodeBuffer()), '', $msg);
					}
					break;

				case 'delete':
					$active = $params['id'];
					$data = $this->db->fetchRow('SELECT * FROM cf_forms WHERE formid=?', $params['id']);

					if(($content = $this->lockObject('forms', $params['id'], true)) === true) {
						$form = new flForm($this->request, 'deleteform', $this->assemble('forms', 'editor', array('delete', $active)), true);

						$name = new flFormElementText($form, 'name', $data['displayname']);
						$name->setLabel('Delete Form')
							->setDisabled()
							->setReadonly();

						$delSubmit = new flFormElementSubmit($form, 'deletesubmit');
						$delSubmit->setCaption(__('Permanently Delete The Form', 'formsBackend'))
							->setCSS('danger')
							->setDescription($this->alertMessage('Warning', __('This operation cannot be undone and will delete the form permanently.', 'formsBackend')));

						if($form->isSubmitted() && $form->isValid()) {
							$this->db->delete('cf_forms', 'formid=?', $active);
							$this->db->delete('cf_formssent', 'formid=?', $active);
							formsAudit::delete($active, $data);
							$this->response->redirectTo($this->assemble('forms', 'editor', array(), true));
						}

						$content = $this->renderBlock(__('Delete Form', 'formsBackend'), $form->render($this->tpl->getCodeBuffer()), '', $msg);
					}
					break;
			}
		}

		// Build the tree of existing forms
		$tree = new flWidgetTreeView();

		$result = $this->db->query('SELECT formid,displayname FROM cf_forms ORDER BY displayname');
		while($row = $result->fetch()) {
			$tree->addNode(new flWidgetTreeLinkNode($row['displayname'] . ' [' . $row['formid'] . ']', $this->assemble('forms', 'editor') . $route->assemble(array('cmd' => 'edit', 'id' => $row['formid'])), flWidgetTreeNode::FIELD))
				->setLastNodeActive($row['formid'] == $active);
		}

		$rootTree = new flWidgetTreeView();
		$rootTree->addNode($result->numRows() ? new flWidgetTreeChildNode(__('Forms', 'formsBackend'), $tree, null) : new flWidgetTreeNode(__('Forms', 'formsBackend'), flWidgetTreeNode::CONTAINER));

		$fam = new flWidgetFloatingActionMenu();
		$fam->setCSS('[[+css]]');

		$fam->addAction('add', __('Add Form', 'formsBackend'), $this->assemble('forms', 'editor', array('add')));
		if($this->user->hasPermission('cfForms.view'))
			$fam->addAction('mail_outline', __('View Form Emails', 'formsBackend'), $this->assemble('forms', 'view'));

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

		$this->renderPage(__('Forms', 'formsBackend'), $content, $this->renderBlock('', $rootTree->render()));
	}

	/**
	 * Editor action.
	 */
	function viewAction() {
		$this->testPermission('cfForms.view');
		$this->breadcrumbTrail->addCrumb(__('View Form Emails', 'formsBackend'), $this->assemble('forms', 'view'));

		$active = 0;
		$content = '';
		$paginationRoute = new flRouterRoute(str_replace(array(':module', ':action', '*'), array('forms', 'view', ':formid/:page'), (string)$this->router->getRoute('cfManagerModule')), array('page' => '0'), array('formid' => '\d+', 'page' => '\d+'), $this->request);
		$route = new flRouterRoute(':formid/:page', array('page' => '0'), array('formid' => '\d+', 'page' => '\d+'));
		$msgRoute = new flRouterRoute('msg/:id', array(), array('id' => '\d+'));
		if($params = $route->match($this->request->getParam('*', array()))) {
			$active = $params['formid'];
			$numItems = $this->db->fetchOne('SELECT COUNT(*) FROM cf_formssent WHERE formid=?', $params['formid']);
			$pagination = new flWidgetPagination($params['page'], $numItems);
			$pagination->setRoute($paginationRoute, array('formid' => $params['formid']), 'page');

			$results = $this->db->fetchAll('SELECT * FROM cf_formssent WHERE formid=? ORDER BY emailid DESC LIMIT ' . $pagination->getStart() . ',' . $pagination->getRows(), $params['formid']);

			if(count($results)) {
				$table = new flWidgetTable();
				$table->setClass('tablewidget bordered striped rowhover responsive')
					->setHeadings(__('Email From', 'formsBackend'), __('Sent', 'formsBackend'), __('Operation', 'galleryBackend'))
					->setColData('email', 'sentat', 'ops')
					->skipEncode('ops', 'sentat');

				foreach($results as $item) {
					$f = new flDate($item['sentat'] . ' UTC', clearFusionCMS::getSysTimezone());
					$item['sentat'] = $f->toString('%d %M %Y<br />%H:%i:%s %O');
					$item['ops'] = "<a href=\"" . $this->assemble('forms', 'view', array('msg', $item['emailid'])) . "\">" . __('Read', 'formsBackend') . "</a>";
					$table->addRow($item);
				}

				$content = $this->renderBlock(__('Emails', 'formsBackend'),
					$table->render() .
					sprintf(__('<p>Found: %d emails for selected form</p>', 'formsBackend'), $numItems) .
					$pagination->render());
			}
		}
		else if($params = $msgRoute->match($this->request->getParam('*', array()))) {
			if($item = $this->db->fetchRow('SELECT * FROM cf_formssent WHERE emailid=?', $params['id'])) {
				$active = $item['formid'];

				$form = new flForm($this->request, 'msg', $this->assemble('forms', 'view'));

				$from = new flFormElementText($form, 'from', $item['email']);
				$from->setLabel(__('Email From', 'formsBackend'))
					->setReadonly()
					->setDisabled();

				$msg = unserialize($item['msg']);
				$emailtext = new flFormElementTextArea($form, 'emailtext', $msg['content']);
				$emailtext->setLabel(__('Email Contents', 'formsBackend'))
					->setReadonly()
					->setDisabled()
					->setRows(15);

				$f = new flDate($item['sentat'] . ' UTC', clearFusionCMS::getSysTimezone());
				$sent = new flFormElementText($form, 'sentat', $f->toString('%d %M %Y %H:%i:%s %O'));
				$sent->setLabel(__('Sent', 'formsBackend'))
					->setReadonly()
					->setDisabled();

				$content = $this->renderBlock(__('Email Contents', 'formsBackend'), $form->render($this->tpl->getCodeBuffer()));
			}
		}

		// Build the tree of existing forms
		$tree = new flWidgetTreeView();

		$result = $this->db->query('SELECT formid,displayname FROM cf_forms ORDER BY displayname');
		while($row = $result->fetch()) {
			$tree->addNode(new flWidgetTreeLinkNode($row['displayname'] . ' [' . $row['formid'] . ']', $this->assemble('forms', 'view') . $route->assemble(array('formid' => $row['formid'])), flWidgetTreeNode::FIELD))
				->setLastNodeActive($row['formid'] == $active);
		}

		$rootTree = new flWidgetTreeView();
		$rootTree->addNode($result->numRows() ? new flWidgetTreeChildNode(__('Forms', 'formsBackend'), $tree, null) : new flWidgetTreeNode(__('Forms', 'formsBackend'), flWidgetTreeNode::CONTAINER));

		if($active) {
			$fam = new flWidgetFloatingActionMenu();
			$fam->setCSS('[[+css]]');

			$fam->addAction('file_download', __('Download Form Data', 'formsBackend'), $this->assemble('forms', 'download', array($active)));

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

			$this->renderPage(__('View Form Emails', 'formsBackend'), $content, $this->renderBlock('', $rootTree->render()));
		}
		else
			$this->renderPage(__('View Form Emails', 'formsBackend'), $content, $this->renderBlock(__('View Form Emails', 'formsBackend'), $rootTree->render()));
	}

	/**
	 * Download the form data as a CSV
	 */
	function downloadAction() {
		$this->testPermission('cfForms.view');
		$this->breadcrumbTrail->addCrumb(__('View Form Emails', 'formsBackend'), $this->assemble('forms', 'view'));
		$this->breadcrumbTrail->addCrumb(__('Download', 'formsBackend'), $this->assemble('forms', 'download'));

		$content = '';
		$active = 0;
		$route = new flRouterRoute(':formid', array(), array('formid' => '\d+'));
		if($params = $route->match($this->request->getParam('*', array()))) {
			$active = $params['formid'];

			$form = new flForm($this->request, 'download', $this->request->getSelf());

			$start = new flFormElementText($form, 'start');
			$start->setLabel(__('Start Date (yyyy-mm-dd)', 'formsBackend'))
				->setRequired()
				->addValidator(new flFormValidatorRequired())
				->addValidator(new flFormValidatorRegex('/^\d{4}-\d{1,2}-\d{1,2}$/'));

			$end = new flFormElementText($form, 'end');
			$end->setLabel(__('End Date (yyyy-mm-dd)', 'formsBackend'))
				->setRequired()
				->addValidator(new flFormValidatorRequired())
				->addValidator(new flFormValidatorRegex('/^\d{4}-\d{1,2}-\d{1,2}$/'));

			$download = new flFormElementSubmit($form, 'downloadcsv');
			$download->setCaption(__('Download CSV', 'formsBackend'))->setCSS('primary');

			$form->groupElements('leftcoldld', array($start))
				->groupElements('rightcoldld', array($end))
				->groupElements('leftcolgo', array($download));

			if($form->isSubmitted() && $form->isValid()) {
				$fieldNames = array();

				// Get the field names
				$formInfo = $this->db->fetchRow('SELECT formdata, displayname FROM cf_forms WHERE formid=?', $active);
				$definition = @simplexml_load_string($formInfo['formdata']);
				if($definition && isset($definition->field)) {
					foreach($definition->field as $field) {
						$attrs = $field->attributes();
						if(isset($attrs['id']))
							$fieldNames[] = (string)$attrs['id'];
					}
				}

				$fileName = preg_replace('/-+/', '-', preg_replace('/[^a-z0-9_-]/', '-', strtolower($formInfo['displayname'])));
				$this->response->setContentType('text/csv')
					->setCharset('utf-8')
					->noCache()
					->setHeader('Content-Disposition', 'attachment; filename=' . $fileName . '-' . $start->getValue() . '-' . $end->getValue() . '.csv')
					->sendHeaders();

				$output = fopen('php://output', 'w');
				fputcsv($output, array_merge(array('sentat'), $fieldNames));

				$results = $this->db->query('SELECT sentat,msg FROM cf_formssent WHERE formid=? && sentat BETWEEN ? AND ?', $active, $start->getValue(). ' 00:00:00', $end->getValue() . ' 23:59:59');
				while($row = $results->fetch()) {
					$f = new flDate($row['sentat'] . ' UTC', clearFusionCMS::getSysTimezone());
					$r = unserialize($row['msg']);
					$d = array($f->toString('%d %M %Y %H:%i:%s %O'));
					foreach($fieldNames as $n)
						$d[] = isset($r['data'][$n]) ? $r['data'][$n] : '';
					fputcsv($output, $d);
				}

				// Stop the script
				die;
			}

			$content = $this->renderBlock(__('Download Email Contents', 'formsBackend'), $form->render($this->tpl->getCodeBuffer()));
		}

		// Build the tree of existing forms
		$tree = new flWidgetTreeView();

		$result = $this->db->query('SELECT formid,displayname FROM cf_forms ORDER BY displayname');
		while($row = $result->fetch()) {
			$tree->addNode(new flWidgetTreeLinkNode($row['displayname'] . ' [' . $row['formid'] . ']', $this->assemble('forms', 'view') . $route->assemble(array('formid' => $row['formid'])), flWidgetTreeNode::FIELD))
				->setLastNodeActive($row['formid'] == $active);
		}

		$rootTree = new flWidgetTreeView();
		$rootTree->addNode($result->numRows() ? new flWidgetTreeChildNode(__('Forms', 'formsBackend'), $tree, null) : new flWidgetTreeNode(__('Forms', 'formsBackend'), flWidgetTreeNode::CONTAINER));

		$fam = new flWidgetFloatingActionMenu();
		$fam->setCSS('[[+css]]');

		$fam->addAction('file_download', __('Download Form Data', 'formsBackend'), $this->assemble('forms', 'download', array($active)));

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

		$this->renderPage(__('View Form Emails', 'formsBackend'), $content, $this->renderBlock('', $rootTree->render()));
	}
}
