Page 1 of 1

Тэг для формы

Posted: Tue Sep 28, 2010 11:33 pm
by von-hamster
Написал тэг который позволяет быстро создать форму для отправки на email. Может кому полезно будет.
Для тех, кто спросит - зачем? Отвечу: альтернативный вариант модулю, ну и постарался сделать как можно проще в использовании...
Текущий вариант поддерживает модуль капчи.
Текущая версия не охватывает все возможные случаи, но легко расширяется...

Чего нет:
1. разных валидаторов. Сейчас только капча и на заполненость.
2. не все варианты полей формы.
3. Не используется smarty как шаблонизатор.

Итак, поехали.
1. Создаем 2 тэга пользователя. Один - собственно базовый класс, второй - описание формы. Тэг класса я назвал тэг rzForm. В принципе можно вынести в файл и инклудить. Текст класса - ниже.
2. Настраиваем форму. Для этого создаем второй тэг (см ниже примеры). Я назвал тэги faq
3. Прописываем тэги в месте, где нужно вызвать форму ({rzForm} {faq})

примеры:

Код примера со своим шаблоном и капчей:

Code: Select all

// Это массив с данными для отправки
$email = array(
	// адрес, куда отправлять (обязательно)
	'email'	=> 'xxx@yyy.com',
	// тема (обязательно)
	'theme'	=> 'Новый вопрос с сайта',
	// отправитель
	'from'	=> 'robot@example.ru',
	// имя отправителя
	'fromName'	=> 'Mail Robot',
);

// массив описания формы. Здесь перечисляем поля и типы. На данный момент поддерживаются:
// text
// select
// textarea,
// checkbox
// capcha - собственно - капча - использует модуль капчи,
// title - разделитель, или пустое название
$data = array(
	// ключ будет участвовать в формировании названия поля и id
	'name' => array(
		// это название, которое будет выводиться на форме и в почте
		'title'		=> 'Ваше имя',
		// тип поля
		'type'		=> 'text',
		// если обязателен, иначе можно не указывать
		'required'	=> true,
	),
	'q' => array(
		'title'	=> 'Вопрос',
		'type'	=> 'textarea',
		'required'	=> true,
	),
	'title'	=> array(
		'title'	=> 'Какой-нть разделитель',
		'type'	=> 'text',
	),
	'category'	=> array(
		'title'	=> 'Раздел',
		'type'	=> 'select',
		// Здесь перечисляем все варианты селекта. При этом имя и значени будут совпадать - нам ведь нужно только отправить
		'items'	=> array(
			'делимый',
			'неделимый',
		),
	),
	'yes'	=> array(
		'title'	=> 'Уврены?',
		// у чекбокса в итоге будет значение либо "да" - если выбран, либо "нет" - если не выбран
		'type'	=> 'checkbox',
	),
	//
	'capcha' => array(
		'title'	=> 'Защита от роботов',
		'type'	=> 'capcha',
		'required'	=> true,
	),
);

// массив шаблонов. Вобщем-то можно и без него - в классе есть шаблоны по умолчанию - элементы формы в таблице. Однако, если используется - то нужно перечислить все поля.
// особенности - форма должна передаваться постом
// сабмит должен иметь имя. По умолчанию при создании объекта используется go, можно переопределить, например, если нужно несколько форм на одной странице.
// В шаблоне 3 вида замещений:
// 1. Тайтл - ##title_ключ_из_массива##
// 2. Элемент формы - ##field_ключ_из_массива##
// 3. Значение - ##value_ключ_из_массива## - используется в шаблоне письма
$templates = array();

// шаблон формы - здесь перечислены только 3 поля, остальные по аналогии. Если нет этого шаблона, то используется по умолчанию - там все поля в таблице.
$templates['form'] = <<< tpl
<form method="post">
	<div class="form_name">
		##title_name##:
	</div>
	<div class="text_input">
		##field_name##
	</div>
	<div class="form_name">
		##title_q##:
	</div>
	<div class="textarea">
		##field_q##
	</div>
	<div class="form_name">
		##title_capcha##:
	</div>
	<div class="textarea">
		##field_capcha##
	</div>
	<div class="submit">
		<input type="submit" value="Задать" name="go" />
	</div>
</form>
tpl;

// это шаблон ошибки. Пока незнает поля в которых произошла ошибка
$templates['error'] = <<< tpl
<p style="color: red">Не заполнены все поля или неверно введен код с картинки!</p>
tpl;

// Шаблон успешной отправки - если все нормально после сабмита, то форма не показывается
$templates['ok'] = <<< tpl
<p><strong>Ваш вопрос принят.</strong></p>
tpl;

// Шаблон почты.
$templates['mail'] = <<< tpl
Вопрос от пользователя ##value_name## :

##value_q##
tpl;

// Ну а это собственно инициализация. Последним параметром можно передать имя сабмита, при необходимости.
$form = new rzForm($email, $data, $templates);
echo $form->run();


код класса

Code: Select all

if (!class_exists('rzForm')) {
	/**
	 * Класс для быстрой генерации форм для отправки
	 */
	class rzForm{
		/**
		 * @var array Массив описания полей
		 */
		private $_data		= array();

		/**
		 * @var string шаблон формы
		 */
		private $_tplForm	= '';

		/**
		 * @var string шаблон письма
		 */
		private $_tplMail	= '';

		/**
		 * @var string Шаблон ошибки
		 */
		private $_tplError	= '';

		/**
		 * @var string Шаблон сообщения успешной отправки
		 */
		private $_tplOk		= '';

		/**
		 * @var array Массив соответсвий переменных шаблона и их значений
		 */
		private $_tplData	= array();

		/**
		 * @var bool Все поля валидны
		 */
		private $_valid		= true;

		/**
		 * @var string Адрес получателя
		 */
		private	$_email			= '';

		/**
		 * @var string Тема письма
		 */
		private	$_emailTheme	= '';

		/**
		 * @var bool|string Адрес отправителя
		 */
		private	$_emailFrom		= '';

		/**
		 * @var bool|string Имя отправителя
		 */
		private	$_emailFromName	= '';

		private $_go = '';

		/**
		 * @param array $email Массив с адресом, темой и отправителем
		 * @param array $data Массив описания формы
		 * @param array $templates Массив со всеми шаблонами
		 * @return void
		 */
		public function __construct($email, $data, $templates, $go = 'go') {
			$this->_data			= $data;
			$this->_email			= $email['email'];
			$this->_emailTheme		= $email['theme'];
			$this->_emailFrom		= isset($email['from']) ? $email['from'] : false;
			$this->_emailFromName	= isset($email['fromName']) ? $email['fromName'] : false;

			$this->_tplForm		= $templates['form'];
			$this->_tplMail		= $templates['mail'];
			$this->_tplError	= $templates['error'];
			$this->_tplOk		= $templates['ok'];

			$this->_go			= $go;
		}

		/**
		 * Основной публичный метод, который вызывется после создания объекта
		 * @return string|void сгенерированый html
		 */
		public function run() {
			$showForm = true;
			if (isset($_POST[$this->_go]) && $_POST[$this->_go]) {
				$this->prepareFields($_POST['f']);
				if ($this->_valid) {
					$this->send();
					$showForm = false;
				}
			} else {
				$this->prepareFields();
				$this->_valid = true;
			}
			if ($showForm) {
				$ret = $this->showForm();
			} else {
				$ret = $this->processTpl($this->_tplOk, array());
			}
			return $ret;
		}

		/**
		 * Отобразить форму
		 * @return void
		 */
		private function showForm() {
			$ret = '';
			if ($this->_tplForm) {
				if (!$this->_valid) {
					$ret .= $this->processTpl($this->_tplError, $this->_tplData);
				}
				$ret .= $this->processTpl($this->_tplForm, $this->_tplData);
			} else {
				$ret = $this->quickForm();
			}
			return $ret;
		}

		/**
		 * Обработать шаблон
		 * @param  $tpl
		 * @param  $data
		 * @return string
		 */
		private function processTpl($tpl, $data) {
			return strtr($tpl, $data);
		}

		/**
		 * Обработать массив данных с учетом поста
		 * @param array $data
		 * @return void
		 */
		private function prepareFields($data = array()) {
			$data = array_map('htmlspecialchars', array_map('trim', $data));
			foreach ($this->_data as $field => &$options) {
				$options['value']	= isset($data[$field]) ? $data[$field] : '';
				$options['name']	= "f[{$field}]";
				$options['id']		= "id_{$field}";
				$options['valid']	= true;
				$options['required']	= isset($options['required']) && $options['required'];

				$html = ' name="' . $options['name'] . '" id="' . $options['id'] . '" ';
				switch ($options['type']) {
					case 'title':
						$options['html'] = '';
						break;
					case 'text':
						$options['html'] = '<input type="text" ' . $html . ' value="' . $options['value'] . '">';
						break;
					case 'checkbox':
						$options['value']	= $options['value'] ? 'да' : 'нет';
						$checked = $options['value'] == 'да' ? ' checked="checked" ' : '';
						$options['html'] = '<input type="checkbox" ' . $html . $checked . ' value="да">';
						break;
					case 'select':
						$options['html'] = '<select ' . $html . '>';
						foreach ($options['items'] as $item) {
							$item = htmlspecialchars(trim($item));
							$selected = $item == $options['value'] ? ' selected="selected" ' : '';
							$options['html'] .= '<option value="' . $item . '" ' . $selected . '>' . $item . '</option>';
						}
						$options['html'] .= '</select>';
						break;
					case 'textarea':
						$options['html'] = '<textarea ' . $html . '>' . $options['value'] . '</textarea>';
						break;
					case 'capcha':
						global $gCms;
						if (isset($gCms->modules['Captcha'])) {
						  $captcha =& $gCms->modules['Captcha']['object'];
						}
						$options['html'] = '<input type="text" ' . $html . ' value="">' . $captcha->getCaptcha();
						$options['valid'] = $captcha->checkCaptcha($options['value']);
						$this->_valid		= $options['valid'];
						break;
					default:
						break;
				}
				$this->_tplData["##title_{$field}##"]	= $options['title'];
				$this->_tplData["##field_{$field}##"]	= $options['html'];
				$this->_tplData["##value_{$field}##"]	= $options['value'];
				if ($options['required'] && !$options['value']) {
					$options['valid']	= false;
					$this->_valid		= false;
				}
			}
		}

		/**
		 * Отправить сообщение на почту
		 * @return void
		 */
		private function send() {
			$this->mail(
				$this->_email,
				$this->_emailTheme,
				$this->_tplMail ? $this->processTpl($this->_tplMail, $this->_tplData) : $this->quickEmail()
			);
		}

		/**
		 * Непосредственно отправка
		 * @param  $email
		 * @param  $theme
		 * @param  $message
		 * @param string $fileName
		 * @return void
		 */
		private function mail($email, $theme, $message, $fileName = '') {
			$enc_in = 'UTF-8';
			$enc_out = 'CP1251';

			$theme = iconv($enc_in, $enc_out, $theme);
			$message = iconv($enc_in, $enc_out, $message);

			$headers = array();
			if ($this->_emailFrom) {
				$headers[]	= $this->_emailFromName ?
								"From: {$this->_emailFromName} <{$this->_emailFrom}>" :
								"{$this->_emailFrom}";
			}

			if ($fileName && file_exists($fileName)) {
				$un        = strtoupper(uniqid(time()));
				$headers[] = 'Mime-Version: 1.0';
				$headers[] = 'Content-Type: multipart/mixed; boundary="----------'.$un.'"';
				$zag		= "------------".$un."\nContent-type: text/plain; charset=\"windows-1251\";\n";
				$zag		.= "Content-Transfer-Encoding: 8bit\n\n{$message}\n\n";
				$zag		.= "------------".$un."\n";
				$zag		.= "Content-Type: application/octet-stream;";
				$zag		.= "name=\"".basename($fileName)."\"\n";
				$zag		.= "Content-Transfer-Encoding:base64\n";
				$zag		.= "Content-Disposition:attachment;";
				$zag		.= "filename=\"".basename($fileName)."\"\n\n";
				$zag		.= chunk_split(base64_encode(file_get_contents($fileName)))."\n";
				$message	= $zag;
			} else {
				$headers[] = 'Content-type: text/plain; charset="windows-1251"';
			}

			mail("{$email}", $theme, $message, implode("\n", $headers), $this->_emailFrom ? "-f{$this->_emailFrom}" : null);
		}

		private function quickForm() {
			$text = '';
			if (!$this->_valid) {
				$text .= '<p style="color:red">Не заполнены все требуемые поля</p>';
			}
			$text .= '<form method="post"><table>';
			foreach ($this->_data as $f) {
				if ($f['type'] == 'title') {
					$text .= "<tr><th colspan=\"2\" align=\"center\">{$f['title']}</th>";
				} else {
					$required = $f['required'] ? ' *' : '';
					$text .= "<tr><th>{$f['title']} {$required} :</th><td>{$f['html']}</td>";
				}
			}
			$text .= '<tr><th colspan="2"><input type="submit" value="Отправить" name="' . $this->_go . '" /></th></tr>';
			$text .= '</table></form>';
			return $text;
		}

		private function quickEmail() {
			$text = '';
			foreach ($this->_data as $f) {
				$text .= "{$f['title']}: {$f['value']}\n";
			}
			$text .= "\n\n";
			return $text;
		}

	}
}

Re: Тэг для формы

Posted: Wed Sep 29, 2010 3:23 am
by Fenik17
$enc_in = 'UTF-8';
$enc_out = 'CP1251';
А зачем кодировку менять?

Re: Тэг для формы

Posted: Wed Sep 29, 2010 8:11 am
by von-hamster
по опыту - почтовые клиенты еще не совсем дружат с utf8