<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss version="2.0">
	<channel>
		<title>Predvoditelev.RU</title>
		<link>http://predvoditelev.ru/</link>
		<description>Личный сайт Предводителева Сергея</description>
		<pubDate>Wed, 02 Sep 2009 12:32:42 +0000</pubDate>
		<language>en-ru</language>
		<generator>MaxSite CMS (http://max-3000.com/)</generator>
		<copyright>Copyright 2010, http://predvoditelev.ru/</copyright>
				<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/predvoditelev" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="predvoditelev" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
			<title><![CDATA[Изображение с фиксированной высотой и 100% шириной]]></title>
			<link>http://predvoditelev.ru/page/img_100p_fix_height</link>
			<guid>http://predvoditelev.ru/page/img_100p_fix_height</guid>
			<pubDate>Wed, 02 Sep 2009 12:32:42 +0000</pubDate>
			<category><![CDATA[CSS и Верстка]]></category>
			<description><![CDATA[<p>
	Бывают случаи, когда необходимо показать изображение, которое тянется по ширине на <tt>100%</tt>, но при этом имеет
	фиксированную высоту. Также изображение должно сохранять свои пропорции (то есть всё, что не влазит по высоте - обрезается).
	Если написать вот так:
</p>

<a name="cut"></a>

<pre class="brush: html;">&lt;img src=&quot;image.jpg&quot; width=&quot;100%&quot; height=&quot;300&quot; alt=&quot;&quot;&gt;</pre>

<p>
	... то картинка будет растягиваться не пропорционально. Для того, чтобы это исправить используем слой-обертку:
</p>

<pre class="brush: html;">&lt;div class=&quot;b-img-wrap&quot;&gt;
	&lt;img src=&quot;image.jpg&quot; width=&quot;100%&quot; alt=&quot;&quot;&gt;
&lt;/div&gt;</pre>

<p>
	Стили:
</p>

<pre class="brush: css;">.b-img-wrap {
	height: 300px;
	overflow: hidden;
}</pre>

<p>
	Как вы заметили, у изображение убрали параметр <tt>height</tt>, те высота изображения будет высчитываться
	браузером автоматически и изображение будет пропорционально изменять свои размеры. Для того, чтобы
	сохранилась фиксированная высота - задаём её слою-обёртке и присваиваем ему свойство <tt>overflow: hidden;</tt>.
	Таким образом всё, что выходит за пределы слоя будет скрываться.
</p>

<h2>Метод масштабирования изобажения</h2>

<p>
	Во всех популярных браузерах кроме Internet Explorer ниже 8й версии используется качественный
	<strong>бикубический метод</strong> масштабирования изображения. А вот IE6 и IE7 используют <strong>метод копирования
	ближайшего пикселя</strong> - поэтому масштабированное изображение в этих браузерах получается менее качественным.
	С IE6 мы ничего	не сделаем, а вот <strong>в IE7 мы можем решить проблему</strong> с помощью CSS-свойства
	<tt>-ms-interpolation-mode</tt>. Это свойство определяет каким способом масштабировать изображения и может
	принимать одно из следующих значений:
</p>

<ul>
	<li><tt>nearest-neighbor</tt> (метод копирования ближайшего пикселя) – по умолчанию</li>
	<li><tt>bicubic</tt> (бикубический метод)</li>
</ul>

<p>
	Соответственно используем CSS-хак для IE7:
</p>

<pre class="brush: css;">*+html .b-img-wrap IMG { -ms-interpolation-mode: bicubic; }</pre>


<h2>Пример к статье</h2>
<p>
	<a href="http://predvoditelev.ru/dc/L2V4YW1wbGVzL2ltZ18xMDBwX2ZpeF9oZWlnaHQvaW1nXzEwMHBfZml4X2hlaWdodC56aXA=" title="/examples/img_100p_fix_height/img_100p_fix_height.zip" >Скачать пример</a> <sup title="Количество переходов">11</sup> (img_100p_fix_height.zip, ~352Kb) |
	<a href="/examples/img_100p_fix_height/" target="_blank">Посмотреть пример</a>
</p>

<p>Работоспособность была проверена в следующих браузерах:</p>

<ul>
	<li>Mozilla Firefox 3.5</li>
	<li>Opera 9.64</li>
	<li>Safari 4 (win)</li>
	<li>Internet Explorer 6, 7, 8</li>
</ul><a href="http://predvoditelev.ru/page/img_100p_fix_height#comments"> Обсудить</a>]]></description>
		</item>
				<item>
			<title><![CDATA[Создание тянущейся карусели с помощью jQuery + jCarousel]]></title>
			<link>http://predvoditelev.ru/page/jcarousel_100p</link>
			<guid>http://predvoditelev.ru/page/jcarousel_100p</guid>
			<pubDate>Sat, 29 Aug 2009 00:23:03 +0000</pubDate>
			<category><![CDATA[CSS и Верстка]]></category>
			<description><![CDATA[<p>
	В этой статье хотелось бы рассмотреть создание карусели изображений (и не только) со 100% шириной.
	Для примера будем делать карусель новостей. Каждая новость &mdash; отдельный блок фиксированный
	как по вертикали, так и по горизонтали, содержащий картинку, заголовок и краткий анонс.
	Для реализации используем популярный js-фреймворк <strong>jQuery</strong> и плагин для создания каруселей &mdash; <strong>jCarousel</strong>.
</p>

<a name="cut"></a>

<p>
	То, что в итоге у нас получится можно посмотреть <a href="/examples/jcarousel_100p/" target="_blank">здесь</a>.
</p>

<p>
	Для начала скачиваем последнюю версию jQuery (<a href="/go.php?http://jquery.com/" target="_blank">http://jquery.com/</a>)
	и плагин jCarousel (<a href="/go.php?http://sorgalla.com/jcarousel/" target="_blank">http://sorgalla.com/jcarousel/</a>).
	На момент написания статьи последние версии jQuery 1.3.2 и jCarousel 0.2.3.
</p>

<p>
	Подключаем jQuery и jCarousel к нашей странице. Из jCarousel нам понадобится сам файл плагина (<tt>jquery.jcarousel.pack.js</tt>)
	и файл со стилями (<tt>jquery.jcarousel.css</tt>). Теперь все необходимые инструменты у нас есть. Создаём список новостей:
</p>

<pre class="brush: html;">&lt;div class=&quot;b-news&quot;&gt;
	&lt;ul&gt;
		&lt;li&gt;
			&lt;div class=&quot;picture&quot;&gt;&lt;img src=&quot;images/news1.jpg&quot; width=&quot;300&quot; height=&quot;200&quot; alt=&quot;&quot;&gt;&lt;/div&gt;
			&lt;div class=&quot;title&quot;&gt;Новость номер 1&lt;/div&gt;
			&lt;div class=&quot;desc&quot;&gt;Краткий анонс первой новости&lt;/div&gt;
		&lt;/li&gt;
		...
		&lt;li&gt;
			&lt;div class=&quot;picture&quot;&gt;&lt;img src=&quot;images/news8.jpg&quot; width=&quot;300&quot; height=&quot;200&quot; alt=&quot;&quot;&gt;&lt;/div&gt;
			&lt;div class=&quot;title&quot;&gt;Новость номер 8&lt;/div&gt;
			&lt;div class=&quot;desc&quot;&gt;Краткий анонс восьмой новости&lt;/div&gt;
		&lt;/li&gt;
	&lt;/ul&gt;
&lt;/div&gt;</pre>

<p>
	Инициализация карусели довольно простая:
</p>

<pre class="brush: javascript;">$(document).ready(function(){
	$('.b-news UL').jcarousel({ scroll: 1 });
});</pre>

<p>
	Параметр <tt>scroll</tt> определяет сколько блоков прокручивать за раз.
</p>

<p>
	Теперь пропишем стили для карусели. Нам нужно задать <tt>width: 100%</tt> для контейнера, определить размеры
	элементов, а также местоположение и внешний вид кнопок управления (прокрутка вперед/назад).
	В коде даны комментарии:
</p>

<pre class="brush: css;">/* Растягиваем контейнер на 100% по ширине */
.b-news .jcarousel-container-horizontal { width: 100%; }
.b-news .jcarousel-clip-horizontal { width: 100%; }

/* Задаем размер одного блока */
.b-news .jcarousel-item, .b-news .jcarousel-list LI { width: 310px; height: 250px; }

/* Задаем внешний вид и расположение кнопок управления */
.b-news .jcarousel-next-horizontal {
	position: absolute;
	top: 80px;
	right: 15px;
	width: 30px;
	height: 30px;
	cursor: pointer;
	background: transparent url(../images/btn_next.gif) no-repeat 0 0;
}
.b-news .jcarousel-next-disabled-horizontal,
.b-news .jcarousel-next-disabled-horizontal:hover,
.b-news .jcarousel-next-disabled-horizontal:active { cursor: default; background-position: -30px 0; }
.b-news .jcarousel-prev-horizontal {
	position: absolute;
	top: 80px;
	left: 15px;
	width: 30px;
	height: 30px;
	cursor: pointer;
	background: transparent url(../images/btn_prev.gif) no-repeat 0 0;
}
.b-news .jcarousel-prev-disabled-horizontal,
.b-news .jcarousel-prev-disabled-horizontal:hover,
.b-news .jcarousel-prev-disabled-horizontal:active { cursor: default; background-position: -30px 0; }</pre>

<p>
	Осталось навести красивости и получим отличную карусель для новостей.
</p>


<h2>Пример к статье</h2>
<p>
	<a href="http://predvoditelev.ru/dc/L2V4YW1wbGVzL2pjYXJvdXNlbF8xMDBwL2pjYXJvdXNlbF8xMDBwLnppcA==" title="/examples/jcarousel_100p/jcarousel_100p.zip" >Скачать пример</a> <sup title="Количество переходов">172</sup> (jcarousel_100p.zip, ~160Kb) |
	<a href="/examples/jcarousel_100p/" target="_blank">Посмотреть пример</a>
</p>

<p>Работоспособность была проверена в следующих браузерах:</p>

<ul>
	<li>Mozilla Firefox 3.5</li>
	<li>Opera 9.64</li>
	<li>Safari 4 (win)</li>
	<li>Internet Explorer 6, 7, 8</li>
</ul><a href="http://predvoditelev.ru/page/jcarousel_100p#comments"> Обсудить</a>]]></description>
		</item>
				<item>
			<title><![CDATA[Рекурсивная смена прав доступа на PHP]]></title>
			<link>http://predvoditelev.ru/page/php_recursion_chmod</link>
			<guid>http://predvoditelev.ru/page/php_recursion_chmod</guid>
			<pubDate>Mon, 27 Apr 2009 21:28:08 +0000</pubDate>
			<category><![CDATA[PHP, Web-мастеринг]]></category>
			<description><![CDATA[<p>
	Возникла тут задачка. Необходимо <strong>сменить права доступа</strong> на все файлы, папки и подпапки в
	определенной директории. Я наивный думал, что такое должно быть реализовано в каждом
	FTP-браузере, но в моём любимом Total Commander'е такой функции не обнаружилось.
	Пришлось писать скрипт.
</p>

<a name="cut"></a>

<p>
	Как обычно сначала немного погуглил - готового решения не нашел.
	В общем по кусочкам собрал вот такой вот простенький скриптик:
</p>

<pre class="brush: php;">&lt;?
 
/*                                                      */
/*  СКРИПТ ДЛЯ РЕКУРСИВНОЙ СМЕНЫ ПРАВ НА ФАЙЛЫ И ПАПКИ  */
/*                                                      */
/*   Автор: Предводителев Сергей (Niko)                 */
/*   http://predvoditelev.ru                            */
/*                                                      */
/*   27.04.2009                                         */
/*                                                      */
 
 
// Папка, в которой находятся файлы и папки для изменения прав
$PARENT_DIR = '/home/*****';
 
$FILE_MODE = 0644; // права на файлы
$DIR_MODE = 0755; // права на папки
 
function chmodex($file) {
global $PARENT_DIR;
global $FILE_MODE;
global $DIR_MODE;
    
    if ( file_exists($file) ) {
        if ( is_dir($file) ) {
            if ( !($file==$PARENT_DIR) ) { chmod( $file, $DIR_MODE ); }
            $handle = opendir( $file );
            while( $filename = readdir($handle) )
            if ( $filename != &quot;.&quot; &amp;&amp; $filename != &quot;..&quot; ) chmodex( $file.&quot;/&quot;.$filename );
                closedir($handle);
        } else {
            chmod( $file, $FILE_MODE );
        }
    }
}
 
chmodex($PARENT_DIR);
 
?&gt;</pre>

<p>Для использования скрипта необходимо:</p>

<ul>
	<li>Открыть файл <tt>chmodex.php</tt> (см. приложенный архив).</li>
	<li>Указать <tt>$PARENT_DIR</tt> - это путь к папке, в которой необходимо изменить права доступа.</li>
	<li>Указать <tt>$FILE_MODE</tt> и <tt>$DIR_MODE</tt> - это права доступа, которые необходимо установить для файлов и папок соответственно.</li>
	<li>Залить скрипт <tt>chmodex.php</tt> в папку, в которой необходимо поменять права 
	доступа, те в папку, которую указали в <tt>$PARENT_DIR</tt>. Права доступа самой 
	папки <tt>$PARENT_DIR</tt> измененены не будут.</li>
	<li>Через браузер запустить <tt>chmodex.php</tt>.</li>
	<li>Если после запуска скрипта - белый экран, то значит всё прошло успешно :).</li>
</ul>

<p style="text-align: right;"><a href="http://predvoditelev.ru/dc/L2V4YW1wbGVzL2NobW9kZXguemlw" title="/examples/chmodex.zip" >Скачать скрипт</a> <sup title="Количество переходов">374</sup> (chmodex.zip, ~ 0.6 kb)</p><a href="http://predvoditelev.ru/page/php_recursion_chmod#comments"> Обсудить</a>]]></description>
		</item>
				<item>
			<title><![CDATA[Установка плагина Popularity Contest на WordPress 2.7 ]]></title>
			<link>http://predvoditelev.ru/page/wordpress_install_popularity_contest</link>
			<guid>http://predvoditelev.ru/page/wordpress_install_popularity_contest</guid>
			<pubDate>Wed, 04 Feb 2009 14:37:59 +0000</pubDate>
			<category><![CDATA[Web-мастеринг]]></category>
			<description><![CDATA[<p>
	Во время разработки очередного шаблона для <strong>WordPress 2.7</strong> столкнулся с проблемой
	при установке плагина <strong>Popularity Contest</strong>.
	Этот плагин предназначен для вывода самых популярных постов на сайте.
</p>

<a name="cut"></a>

<p>
	Скачал плагин Popularity Contest c <a href="http://alexking.org/projects/wordpress">оффициального сайта</a>.
	На момент написания поста последняя версия - 1.3b3 от 16 декабря 2007 года.
	Как видим плагин давно уже не обновлялся. Установка по идее довольно простая:
</p>

<ol>
	<li>Копируем файл <tt>popularity-contest.php</tt> в папку <tt>\wp-content\plugins\</tt>.</li>
	<li>Активировать плагин в разделе <i>Плагины</i> панели управления WordPress.</li>
</ol>

<p>
	Но при активации плагина WordPress выдавал следующую ошибку: 
	<i>&ldquo;Плагин не может быть активирован, поскольку он вызвал <b>фатальную ошибку</b>&ldquo;</i>.
</p>

<p>Немного погуглив нашел решение:</p>

<p>1) Открываем <tt>popularity-contest.php</tt> и заменяем строчку 59:</p>
<p><i>require('../../wp-blog-header.php');</i></p>
<p>на</p>
<p><i>require('../wp-blog-header.php');</i></p>

<p>2) Необходимо создать таблицы в базе данных. Вот код:</p>
<pre class="brush: sql;">CREATE TABLE IF NOT EXISTS `wp_ak_popularity` (
`post_id` int(11) NOT NULL,
`total` int(11) NOT NULL,
`feed_views` int(11) NOT NULL,
`home_views` int(11) NOT NULL,
`archive_views` int(11) NOT NULL,
`category_views` int(11) NOT NULL,
`single_views` int(11) NOT NULL,
`comments` int(11) NOT NULL,
`pingbacks` int(11) NOT NULL,
`trackbacks` int(11) NOT NULL,
`last_modified` datetime,
KEY `post_id` (`post_id`)
) ENGINE=MyISAM;

CREATE TABLE IF NOT EXISTS `wp_ak_popularity_options` (
`option_name` varchar(50) NOT NULL,
`option_value` varchar(50) NOT NULL
) ENGINE=MyISAM;</pre>

<p class="note"><b>Примечание.</b> Если вы используете в БД префикс для таблиц отличный от <tt>wp_</tt>, то при создании таблиц замените префиксы на используемые вами.</p>
<p>После этих действий плагин должен активироваться и работать без проблем.</p><a href="http://predvoditelev.ru/page/wordpress_install_popularity_contest#comments"> Обсудить</a>]]></description>
		</item>
				<item>
			<title><![CDATA[Проверка на максимальную длину названия темы в Vanilla]]></title>
			<link>http://predvoditelev.ru/page/vanilla_max_length_theme</link>
			<guid>http://predvoditelev.ru/page/vanilla_max_length_theme</guid>
			<pubDate>Wed, 12 Nov 2008 18:04:01 +0000</pubDate>
			<category><![CDATA[Web-мастеринг]]></category>
			<description><![CDATA[<img src="/images/vanilla_max_length_theme.gif" alt="Проверка на максимальную длину названия темы в Vanilla" width="200" height="55" class="img-left">

<p>
	<strong>Форум Vanilla</strong>, конечно, довольно интересный и очень удобный, но ещё достаточно сыроват.
	Поэтому приходится некоторые ошибки по ходу находить и самому править. Собственно обнаружилась
	ошибка в <strong>проверке на максимальную длину названия темы в Vanilla</strong>.
	Описываемая ошибка у меня проявилась в версии 1.1.5a.
</p>

<a name="cut"></a>

<p>
	Итак в файле <tt>[путь к форуму]/appg/settings.php</tt> есть такой параметр
	<tt>$Configuration['MAX_TOPIC_WORD_LENGTH']</tt>, который вероятно и должен отвечать за
	максимальную длину названия темы. Но как не менял я этот параметр - результат был один
	и тот же: ограничение всегда было в 100 символов.
</p>

<p>
	Стоит сказать, что определил-то я это не сразу :). Ошибка показывается, не как обычно что-то
	вроде <i>&ldquo;Превышено ограничение в N символов&rdquo;</i>, а показывается на сколько
	символов длина заголовка превышает ограничение, поэтому у меня число было всегда разным,
	а перевод был не правильным: "Заголовок темы превышает N символов".
	Я перевел для себя так (см. файл <tt>[путь к форуму]/languages/[название языка]/definitions.php</tt>):
</p>

<pre class="brush: php;">$Context-&gt;Dictionary['ErrInputLength'] = '//1 слишком длинный. Сократите его на //2 символ(а, ов).';</pre>

<p>
	Но собственно к ошибке с проверкой длинны. Раз ограничение всегда было одно - в 100 символов,
	значит оно где-то жестко прописано, вместо того, чтобы использовать наш параметр. Порывшись в
	исходниках, обнаружилось проблемное место. Итак, чтобы исправить этот глюк открываем файл
	<tt>[путь к форуму]/library/Vanilla/Vanilla.Class.DiscussionManager.php</tt>
	и заменяем в нём строку 448:
</p>

<pre class="brush: php;">Validate($this-&gt;Context-&gt;GetDefinition('DiscussionTopicLower'), 1, $Name, 100, '', $this-&gt;Context);</pre>

<p>на строку:</p>

<pre class="brush: php;">Validate($this-&gt;Context-&gt;GetDefinition('DiscussionTopicLower'), 1, $Name, $this-&gt;Context-&gt;Configuration['MAX_TOPIC_WORD_LENGTH'], '', $this-&gt;Context);</pre>

<p>Теперь проверка на длину строки будет работать корректно.</p>

<p>
В шаблоне также ограничение на длину прописано жестко, то есть параметр <tt>maxlength=&rdquo;100&Prime;</tt>. Заменим его.
Открываем файл <tt>[путь к форуму]/themes/discussion_form.php</tt> и заменяем в нем:
</p>

<pre class="brush: php;">&lt;input id=&quot;txtTopic&quot; type=&quot;text&quot; name=&quot;Name&quot; class=&quot;DiscussionBox&quot; maxlength=&quot;100&quot; value=&quot;'.$Discussion-&gt;Name.'&quot; /&gt;</pre>

<p>на</p>

<pre class="brush: php;">&lt;input id=&quot;txtTopic&quot; type=&quot;text&quot; name=&quot;Name&quot; class=&quot;DiscussionBox&quot; maxlength=&quot;'.$this-&gt;Context-&gt;Configuration['MAX_TOPIC_WORD_LENGTH'].'&quot; value=&quot;'.$Discussion-&gt;Name.'&quot; /&gt;</pre>

<p>Вот теперь вроде бы всё. Ошибка исправлена полностью.</p>

<p><b>PS</b> Много информациии по Vanilla вы найдете здесь: <a href="http://www.vanillain.ru/">Русскоязычное сообщество Vanilla forum</a>.</p><a href="http://predvoditelev.ru/page/vanilla_max_length_theme#comments"> Обсудить</a>]]></description>
		</item>
				<item>
			<title><![CDATA[Список пользователей на форуме Vanilla]]></title>
			<link>http://predvoditelev.ru/page/member_list_in_vanilla</link>
			<guid>http://predvoditelev.ru/page/member_list_in_vanilla</guid>
			<pubDate>Tue, 11 Nov 2008 11:58:17 +0000</pubDate>
			<category><![CDATA[Web-мастеринг]]></category>
			<description><![CDATA[<img src="/images/member_list_in_vanilla.gif" alt="Список пользователей на форуме Vanilla" width="220" height="50" class="img-left">

<p>
	Продолжаем расширять функциональность форума <strong>Vanilla</strong>.
	Изучая админку форума я так и не нашел - где же посмотреть список зарегестрированных
	пользователей. К сожалению, авторы не посчитали эту возможность настолько важной,
	чтобы включить её в базовую версию форума. Раз нет в базовой версии, значит должно
	быть расширение дающее такую возможность. И оно нашлось - <strong>MembersList</strong>&hellip;
</p>

<a name="cut"></a>

<p>
	Данное разрешение добавляет вкладку <i>&ldquo;Список пользователей&rdquo;</i> в основное меню
	и соответственно позволяет в удобном виде просмотреть список зарегистрированных пользователей.
</p>

<p>
	<strong>Скачать расширение MembersList</strong> можно отсюда: <a href="http://lussumo.com/addons/index.php?PostBackAction=AddOn&amp;AddOnID=355">http://lussumo.com/addons/index.php?PostBackAction=AddOn&amp;AddOnID=355</a> (на момент написания заметки актуальная версия - 1.2).
</p>

<p>
	Скаченный архив разархивируем и перед тем как закачать расширение на сервер,
	переведем его на русский язык. Для этого открываем файл <tt>MembersList/conf/language.php</tt>
	и переводим на русский язык соответствующие значения. У меня получилось вот так:
</p>

<pre class="brush: php;">&lt;?php
// Language Dictionary
 
$Context-&gt;Dictionary['Members'] = 'Список пользователей'; // Tab title
$Context-&gt;Dictionary['AvatarDefinition'] = 'Аватар'; // Avatar Cell Title
$Context-&gt;Dictionary['Username'] = 'Ник'; // Username Cell Title
$Context-&gt;Dictionary['NameTag'] = 'Имя, фамилия'; // Name &amp; Lastname Cell Title
$Context-&gt;Dictionary['Visit'] = 'Посещения'; // Total Visit Count Title
$Context-&gt;Dictionary['Role'] = 'Роль'; // Role Description Cell Title
$Context-&gt;Dictionary['Email'] = 'E-mail'; // Email Cell Title
$Context-&gt;Dictionary['Posts'] = 'Сообщения'; // Total Posts Title
$Context-&gt;Dictionary['Registered'] = 'Дата регистрации'; // Registration Date Title
 
$Context-&gt;Dictionary['Gizli'] = 'Secret'; // Secret Fields
$Context-&gt;Dictionary['notAvail'] = 'N/A'; // Not Available Fields (empty fields)
 
// denfinitions for Admin Panel
$Context-&gt;Dictionary['permission_view'] = 'Показывать дополнительные вкладки (Список пользователей)';
$Context-&gt;Dictionary['permission_view_secret'] ='Разрешить смотреть скрытые данные пользователей (Имя и e-mail)';
 
?&gt;</pre>

<p>Не забываем, что сохранять файл надо в кодировке <tt>UTF-8</tt>.</p>

<p>
	Теперь закачиваем папку <tt>MembersList</tt> на сервер в директорию <tt>[путь к форуму]/extensions/</tt>.
	Далее на форуме заходим в <i>настройки - дополнения</i> и ставим галочку напротив расширения <tt>MembersList</tt>.
</p>

<p>
	Расширение мы активировали, осталось только указать, какие группы пользователей могут смотреть
	списки зарегистрированных пользователей. Для этого служат параметры
	<i>Показывать дополнительные вкладки (Список пользователей)</i> и
	<i>Разрешить смотреть скрытые данные пользователей (Имя и e-mail)</i>
	(см. <i>настройки - полномочия</i>).
</p>

<p><strong>PS</strong> Описанные в посте действия производились над форумом Vanilla версии 1.1.5a.</p><a href="http://predvoditelev.ru/page/member_list_in_vanilla#comments"> Обсудить</a>]]></description>
		</item>
				<item>
			<title><![CDATA[Установка редактора TinyMCE на форум Vanilla]]></title>
			<link>http://predvoditelev.ru/page/install_tinymce_on_vanilla</link>
			<guid>http://predvoditelev.ru/page/install_tinymce_on_vanilla</guid>
			<pubDate>Mon, 10 Nov 2008 12:25:52 +0000</pubDate>
			<category><![CDATA[Web-мастеринг]]></category>
			<description><![CDATA[<img src="/images/install_tinymce_on_vanilla.gif" alt="Установка редактора TinyMCE на форум Vanilla" width="200" height="181" class="img-left">
<p>
	На одном из своих проектов решил установить форум. После непродолжительных раздумий остановил
	свой выбор на легковесном и жутко удобном форуме <strong><a href="http://getvanilla.com/">Vanilla</a></strong>
	(на момент написания поста последняя версия 1.1.5a).
</p>

<p>
	В базовой версии форума - только самое необходимое.
	Дополнительная функциональность делается с помощью расширения для форума.
</p>

<p>
	Первым расширением, которое мне понадобилось - был визуальный редактор для написания сообщения
	в форум. Выбор был сделан в пользу старого доброго <strong>TinyMCE</strong>. Собственно далее
	следует <strong>инструкция по установке TinyMCE на форум Vanilla</strong>.
</p>

<a name="cut"></a>

<p>
	Во-первых, качаем сам TinyMCE отсюда: <a href="http://tinymce.moxiecode.com/download.php">http://tinymce.moxiecode.com/download.php</a>
	(нам необходим <tt>Main package</tt>, на момент написания поста последняя версия - 3.2.1).
	В архиве есть папка <tt>jscripts</tt> - только она нужна нам из архива, документацию
	и примеры можно смело удалить :). Закачиваем папку <tt>jscripts</tt> на сервер в директорию
	<tt>[путь к форуму]/js/tinymce/</tt> (папку <tt>tinymce</tt> необходимо создать).
</p>

<p>
	Теперь скачиваем расширение для Vanilla, позволяющее установить TinyMCE в качестве редактора,
	отсюда: <a href="http://lussumo.com/addons/index.php?PostBackAction=AddOn&amp;AddOnID=179">http://lussumo.com/addons/index.php?PostBackAction=AddOn&amp;AddOnID=179</a>
	(на момент написания поста последняя версия - 1.4.1). Распаковываем содержимое архива на сервер
	в директорию <tt>[путь к форуму]/extensions/</tt>.
</p>

<p>
	Всё, осталось только активировать дополнение. Для этого заходим в
	<i>настройки форума - дополнения</i> и ставим галочку напротив расширения <i>Tinymce</i>.
</p><a href="http://predvoditelev.ru/page/install_tinymce_on_vanilla#comments"> Обсудить</a>]]></description>
		</item>
				<item>
			<title><![CDATA[ MODx: Вывод информации только для администратора ]]></title>
			<link>http://predvoditelev.ru/page/modx_info_only_for_admin</link>
			<guid>http://predvoditelev.ru/page/modx_info_only_for_admin</guid>
			<pubDate>Thu, 06 Nov 2008 12:12:08 +0000</pubDate>
			<category><![CDATA[MODx]]></category>
			<description><![CDATA[<p><strong>&hellip; или зачем админу смотреть на рекламу?</strong></p>

<p>
	Просматривая свой сайт, подумалось мне - а зачем мне смотреть рекламу на своём сайте?
	Пускай посетители видят рекламу, а я что-нибудь полезное посмотрю :).
	Например, какую-нибудь статистику. Что я и сделал.
</p>

<p><strong>Задача:</strong> если администратор залогинился на сайте, то при просмотре вместо чанка с рекламой показывать чанк со статистикой.</p>
<a name="cut"></a>

<p>Создаем простой сниппет <tt>ad_or_stat</tt>:</p>

<pre class="brush: php;">&lt;?php
    if ($_SESSION['mgrShortname'] == 'admin') {
        echo $modx-&gt;getChunk(&quot;tpl_admin-stat&quot;);
    } else {
        echo $modx-&gt;getChunk(&quot;tpl_begun-code&quot;);
    }
?&gt;</pre>

<p>
	В переменной <tt>$_SESSION['mgrShortname']</tt> содержится логин пользователя.
	Соответственно смотрим, если логин - <i>&ldquo;admin&rdquo;</i>, то выводим чанк
	со статистикой <tt>tpl_admin-stat</tt>; если же логин какой-то другой или это
	просто посетитель - выводим чанк с рекламой <tt>tpl_begun-code</tt>
	(в моем случае - это блок бегуна).
</p>

<p>Чанк <tt>tpl_admin-stat</tt> у меня выглядит следующим образом:</p>

<pre class="brush: html;">&lt;div class=&quot;admin-stat&quot;&gt;
	&lt;div class=&quot;title&quot;&gt;Статистика&lt;/div&gt;
	&lt;div class=&quot;stat&quot;&gt;
		Запросы к БД: &lt;b&gt;[^q^] ([^qt^])&lt;/b&gt;&lt;br /&gt;
		Время работы скриптов: &lt;b&gt;[^p^]&lt;/b&gt;&lt;br /&gt;
		Время генерации страницы: &lt;b&gt;[^t^]&lt;/b&gt;&lt;br /&gt;
		Источник: &lt;b&gt;[^s^]&lt;/b&gt;
	&lt;/div&gt;
&lt;/div&gt;</pre>

<p>Теперь просто вместо вызова чанка с рекламой в шаблоне - вызываем наш сниппет: <tt>[!ad_or_stat!]</tt> и вместо рекламы - наблюдаем статистику.</p><a href="http://predvoditelev.ru/page/modx_info_only_for_admin#comments"> Обсудить</a>]]></description>
		</item>
				<item>
			<title><![CDATA[MODx: Проблема с использованием регулярных выражений в Jot]]></title>
			<link>http://predvoditelev.ru/page/modx_jot_trable_with_regex</link>
			<guid>http://predvoditelev.ru/page/modx_jot_trable_with_regex</guid>
			<pubDate>Sun, 02 Nov 2008 20:37:26 +0000</pubDate>
			<category><![CDATA[MODx]]></category>
			<description><![CDATA[<img src="/images/modx_jot_trable_with_regex.jpg" alt="MODx: Проблема с использованием регулярных выражений в Jot" width="115" height="80" class="img-left">
<p>
	Захотелось вот на одном сайте сделать форму, чтобы пользователи могли оставлять комментарии.
	Сайт, как понятно из названия, на <strong>MODx</strong>. Для комментирования решил использовать
	сниппет <strong>Jot</strong>. На данный момент самая свежая версия - <em>1.1.4</em>.
	Скачал, установил&hellip; вроде работает :). Но в дальнейшем появился один глюк, не позволяющий в
	полной мере использовать регулярные выражения для проверки на правильность ввода данных, но обо всём по порядку&hellip;
</p>

<a name="cut"></a>

<p>
	Для поля <em>&ldquo;имя пользователя&rdquo;</em> необходимо было сделать проверку на длину строки (чтобы не менее 5 символов).
	Не проблема - в <strong>Jot</strong> можно использовать регулярные выражения для проверки с помощью параметра <tt>validate</tt>.
	Синтаксис следующий:
</p>

<p><em>fieldname:message(:validation),fieldname:message(:validation),&hellip;</em></p>

<p>где:</p>
<ul>
	<li><tt>fieldname</tt> - имя поля;</li>
	<li><tt>message</tt> - сообщение, показываемое при ошибке;</li>
	<li><tt>validation</tt> - правило проверки (<i>number</i>, <i>email</i>, регулярное выражение).</li>
</ul>
<p>Можно задать правила проверки для всех полей, разделяя их запятой.</p>

<p>
	Регулярное выражение для моего случая (длина строки не более 5 символов) следующее:
	<tt>/^.{5,}$/</tt>. В итоге получаем такой параметр:
</p>

<p><tt>&amp;validate=`Name:Имя должно содержать не менее 5 символов:/^.{5,}$/`</tt></p>

<p>
	Вроде бы всё правильно, но это не работает - вылазит ошибка: неверный синтаксис регулярного выражения.
	А проблема на самом деле в том, что в регулярном выражении мы используем запятую, но запятая используется
	в синтаксисе <tt>validate</tt> для разделения правил для полей. Соответственно строка парсится не правильно.
</p>

<p>
	Для исправления этого глюка я решил заменить разделение полей в <tt>validate</tt> с запятой на <tt>||</tt>.
	Для этого открываем файл <tt>/assets/snippets/jot/jot.class.inc.php</tt> и переходим на строку 171, она выглядит следующим образом:
</p>

<pre class="brush: php;">$valStrings = explode(&quot;,&quot;,$this-&gt;config[&quot;validate&quot;]);</pre>

<p>И заменяем запятую на на наш разделитель <i>"||"</i>:</p>

<pre class="brush: php;">$valStrings = explode(&quot;||&quot;,$this-&gt;config[&quot;validate&quot;]);</pre>

<p>
	Всё&hellip; теперь <strong>регулярные выражения в Jot</strong> работают корректно, но надо не забывать, что теперь разделителем в
	<tt>validate</tt> является <i>"||"</i> вместо запятой.
</p><a href="http://predvoditelev.ru/page/modx_jot_trable_with_regex#comments"> Обсудить</a>]]></description>
		</item>
				<item>
			<title><![CDATA[MODx: Делаем удобную форму обратной связи ]]></title>
			<link>http://predvoditelev.ru/page/modx_feedback</link>
			<guid>http://predvoditelev.ru/page/modx_feedback</guid>
			<pubDate>Thu, 30 Oct 2008 12:37:12 +0000</pubDate>
			<category><![CDATA[jQuery и JavaScript, MODx]]></category>
			<description><![CDATA[<p>
	На каждом нормальном сайте у пользователя должна быть возможность связаться с владельцем сайта.
	Я думаю, это всем понятно :). Для обратной связи можно указать просто адрес электронной почты,
	но не всегда хочется показывать всем подряд свой адрес, да и боты по сбору email&rsquo;ов не спят.
	В таких случаях нам на помощь придет форма обратной связи. Пользователь заполняет её прямо на сайте,
	жмет кнопку отправить и всё &mdash; письмо уже у нас на почте.
</p>

<p>
	Сейчас я делаю сайты на <strong>CMS MODx</strong> и соответсвенно
	<strong>форму обратной связи</strong> буду писать для неё. Что необходимо получить:
</p>

<a name="cut"></a>

<ul>
	<li>поля проверяются &laquo;на лету&raquo; (по мере заполнения пользователем формы);</li>
	<li>использование каптчи; </li>
	<li>возможность отправить сообщение, только когда все поля заполнены верно.</li>
</ul>

<p>
	Для работы с формой воспользуемся сниппетом <tt>eForm</tt>,
	для проверки полей на лету будем использовать <strong>JavaScript</strong>.
	Итак, приступим.
</p>

<p>
	Для начала создадим новый документ, в котором у нас будет отображаться форма обратной связи.
	Назовем его <i>&laquo;Обратная связь&raquo;</i> и в содержимое запишем следующее:
</p>

<pre class="brush: html;">&lt;h1&gt;Обратная связь&lt;/h1&gt;

[!nikoFeedBack!]</pre>

<p>
	Последняя строчка &mdash; некэшируемый вызов сниппета  <tt>nikoFeedBack</tt>,
	который и отвечает за вывод нашей формы.
</p>

<p>
	Теперь создаем новый сниппет: название &mdash; <i>&laquo;nikoFeedBack&raquo;</i>, описание &mdash; <i>&laquo;Форма обратной связи&raquo;</i>.
</p>

<p>Код сниппета:</p>

<pre class="brush: php;">&lt;?php
function fbValidateName($value) { return strlen($value)&gt;=5; }
function fbValidateText($value) { return strlen($value)&gt;=15; }
 
$modx-&gt;regClientStartupScript($modx-&gt;config['site_url'].'assets/snippets/nikoFeedBack/validate.js');
 
$outForm = $modx-&gt;runSnippet(
	&quot;eForm&quot;,
	array(
		&quot;formid&quot; =&gt; &quot;FeedBackForm&quot;,
		&quot;to&quot; =&gt; &quot;admin@site.ru&quot;,
		&quot;tpl&quot; =&gt; &quot;eFBForm&quot;,
		&quot;report&quot; =&gt; &quot;eFBReport&quot;,
		&quot;thankyou&quot; =&gt; &quot;eFBThanks&quot;,
		&quot;from&quot; =&gt; &quot;[+fbEMail+]&quot;,
		&quot;fromname&quot; =&gt; &quot;site.ru&quot;,
		&quot;subject&quot; =&gt; &quot;Посетители сайта пишут&quot;,
		&quot;vericode&quot; =&gt; &quot;1&quot;
	)
);
 
echo $outForm;
?&gt;</pre>

<p>
	В начале сниппета задается две функции:  <tt>fbValidateName</tt> и <tt>fbValidateText</tt>.
	Они отвечают за проверку корректности введенных данных полей <i>&laquo;Имя&raquo;</i> и
	<i>&laquo;Текст сообщения&raquo;</i>. Откуда эти функции вызываются &mdash; опишу позднее.
</p>

<p>
	Затем с помощью стандартной функции в MODx <tt>regClientStartupScript</tt> добавляем скрипт 
	<tt>validate.js</tt> (в нем будут храниться скрипты, отвечающие за проверку введенных
	значений на лету) для загрузки внутрь тега <tt>&lt;head&gt;</tt>.
</p>

<p>
	Далее вызывается сниппет <tt>eForm</tt>, который используется для работы с формами в MODx.
	При вызове сниппета передаем ему следующие параметры:
</p>

<ul>
	<li><tt>formid</tt> &ndash; Идентификатор формы, который используется при отправке данных (<i>FeedBackForm</i>).</li>
	<li><tt>to</tt> - Адрес Email на который будет послана информация. Можно использовать несколько
	адресов разделяя их запятой (,). Если не указано, то будет использован системный адрес.
	В данном случае информация будет отправлена на адрес <i>admin@site.ru</i>.</li>
	<li><tt>tpl</tt> - Название чанка (не число) или идентификатор документа (число)
	для используемого шаблона. Наш чанк с формой будет называться <i>&laquo;eFBForm&raquo;</i>.</li>
	<li><tt>report</tt> - Название чанка (не число) или идентификатор документа (число)
	для шаблона отчета. Это шаблон для письма, которое будет отправлено с сайта к нам
	на почту, у нас он называется <i>&laquo;eFBReport&raquo;</i>.</li>
	<li><tt>thankyou</tt> - Название чанка (не число) или идентификатор документа
	(число) для используемого шаблона сообщения выводимого пользователю после отправки
	данных формы. Наш чанк с благодарностью пользователю за оставленное сообщение будет
	называться <i>&laquo;eFBThanks&raquo;</i>.</li>
	<li><tt>from</tt> - Устанавливает адрес отправителя письма. В качестве адреса
	устанавливаем плейсхолдер <tt>[+fbEMail+]</tt>, который будет
	содержать адрес электронной почты пользователя.</li>
	<li><tt>fromname</tt> - Устанавливает имя отправителя письма. Пишем название нашего
	сайта, чтобы видеть сразу откуда письмо.</li>
	<li><tt>subject</tt> - Тема письма. Для примера поставим <i>&laquo;Посетители сайта пишут&raquo;</i>.</li>
	<li><tt>vericode</tt> - Включает использование каптчи в форме.</li>
</ul>

<p>Перейдем к чанкам. Для начала создадим чанк <tt>eFBForm</tt>, содержащий форму.</p>

<pre class="brush: html;">&lt;p&gt;Пишите сообщения и не сомневайтесь - вы будете услышаны!&lt;/p&gt;

&lt;div class=&quot;fbForm&quot;&gt;
	
	&lt;p&gt;&lt;span style=&quot;color:#900;&quot;&gt;[+validationmessage+]&lt;/span&gt;&lt;/p&gt;
	
	&lt;form method=&quot;post&quot; action=&quot;[~[*id*]~]&quot; id=&quot;FeedBackForm&quot;&gt;
	
		&lt;div class=&quot;form&quot;&gt;
		
			&lt;div class=&quot;name&quot;&gt;Ваше имя и фамилия&lt;/div&gt;
			&lt;div class=&quot;error&quot; id=&quot;fbNameError&quot;&gt;&lt;/div&gt;
			&lt;div class=&quot;element&quot;&gt;&lt;input type=&quot;text&quot; name=&quot;fbName&quot; id=&quot;fbName&quot; eform=&quot;Имя::1:Должно содержать не менее 5 символов.:#FUNCTION fbValidateName&quot; value=&quot;[+fbName+]&quot;&gt;&lt;/div&gt;
		
			&lt;div class=&quot;name&quot;&gt;Адрес электронной почты&lt;/div&gt;
			&lt;div class=&quot;error&quot; id=&quot;fbEMailError&quot;&gt;&lt;/div&gt;
			&lt;div class=&quot;element&quot;&gt;&lt;input type=&quot;text&quot; name=&quot;fbEMail&quot; id=&quot;fbEMail&quot; eform=&quot;EMail:email:1:EMail &amp;raquo; Некорректный адрес электронной почты.&quot; value=&quot;[+fbEMail+]&quot;&gt;&lt;/div&gt;
		
			&lt;div class=&quot;name&quot;&gt;Текст сообщения&lt;/div&gt;
			&lt;div class=&quot;error&quot; id=&quot;fbTextError&quot;&gt;&lt;/div&gt;
			&lt;div class=&quot;element&quot;&gt;&lt;textarea name=&quot;fbText&quot; id=&quot;fbText&quot; eform=&quot;Текст сообщения:html:1:Должен содержать не менее 15 символов.:#FUNCTION fbValidateText&quot;  cols=&quot;40&quot; rows=&quot;5&quot;&gt;[+fbText+]&lt;/textarea&gt;&lt;/div&gt;
		
			&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;captcha-test&quot;&gt;
				&lt;tr&gt;
					&lt;td&gt;
						&lt;div class=&quot;name&quot;&gt;Защита от спама&lt;/div&gt;
						&lt;div class=&quot;captcha&quot;&gt;&lt;img src=&quot;[+verimageurl+]&quot; alt=&quot;Проверочный код&quot; border=&quot;1&quot;&gt;&lt;/div&gt;
					&lt;/td&gt;
					&lt;td&gt;
						&lt;div class=&quot;name&quot;&gt;Код на картинке:&lt;/div&gt;
						&lt;div class=&quot;element&quot;&gt;&lt;input type=&quot;text&quot; name=&quot;vericode&quot; id=&quot;vericode&quot; value=&quot;&quot;&gt;&lt;/div&gt;
					&lt;/td&gt;
				&lt;/tr&gt;
			&lt;/table&gt;
		
			&lt;div class=&quot;submit&quot;&gt;&lt;input type=&quot;submit&quot; name=&quot;fbSubmit&quot; id=&quot;fbSubmit&quot; value=&quot;Отправить&quot;&gt;&lt;/div&gt;
		
		&lt;/div&gt;
	
	&lt;/form&gt;
	
&lt;/div&gt;</pre>

<p>
	<tt>[+validationmessage+]</tt> - этот плэйсхолдер выводит ошибки, если данные, отправленные
	на сервер, были некорректными или отправка сообщения не удалась.
</p>

<p>
	<tt>[+verimageurl+]</tt> - плэйсхолдер содержит адрес картинки с каптчей. Для проверки каптчи
	&mdash; необходимо использовать INPUT c именем <i>&laquo;vericode&raquo;</i>.
</p>

<p>
	Пустые слои с идентификаторами <tt>&lt;имя поля&gt;Error</tt> &mdash; используются для вывода
	ошибок при заполнении полей, когда проверка данных проводится &laquo;на лету&raquo;.
</p>

<p>
	Как вы заметили поля формы имеют дополнительный параметр <tt>efrom</tt>. Он используется для
	настройки встроенного в <tt>eForm</tt> анализатора, определяющего формат и тип данных с
	соответствующей проверкой каждого поля. Базовый вид параметра <tt>efrom</tt> следующий:
</p>

<p>
	<i>[description/title]:[datatype]:[required]:[validation message]:[validation rule]</i>
</p>

<p>
	<tt>description/title</tt> &mdash; Название (описание) поля.<br>
	<tt>datatype</tt> &mdash; Тип данных.<br>
	<tt>required</tt> &ndash; Обязательно ли заполнение этого поля.<br>
	<tt>validation message</tt> &mdash; Сообщение при неудачной проверке значения поля.<br>
	<tt>validation rule</tt> &mdash; Правило проверки значения поля.
</p>

<p>
	Вот здесь мы и используем функции <tt>fbValidateName</tt> и <tt>fbValidateText</tt>,
	описанные в сниппете <tt>nikoFeedBack</tt>, для проверки корректности значений 
	полей <i>&laquo;Имя&raquo;</i> и <i>&laquo;Текст сообщения&raquo;</i>.
</p>

<p>Стили используемые в форме:</p>

<pre class="brush: css;">* { padding : 0px; margin : 0px; border: none; }
BODY {
	color: #252525;
	font-family: Tahoma, Arial, Helvetica, sans-serif;
	font-size: 10pt;
}
.fbForm .form { padding-left: 40px; }
.fbForm .name { padding-top: 12px; font-size: 8pt; color: #7d7d7d; }
.fbForm .element { padding-top: 2px; }
.fbForm .element INPUT, .fbForm .element TEXTAREA {
	border: 1px solid #ccc;
	background-color: #E6EFF6;
	font-size: 10pt;       
	color: #2C5883;
}
.fbForm INPUT#fbName,
.fbForm INPUT#fbEMail {
	background-image: url(images/icon_form.gif);
	background-repeat: no-repeat;
	width: 250px;
	padding: 4px 0px 4px 25px;
}
.fbForm INPUT#fbName    { background-position:3px -3px; }
.fbForm INPUT#fbEMail { background-position:3px -33px; }
.fbForm TEXTAREA#fbText {
	padding: 4px 4px 4px 25px;
	width: 425px;
	font-family: Tahoma;
	background: #E6EFF6 url(images/icon_form.gif) no-repeat 3px -93px;
}
.fbForm .captcha-test TD { vertical-align: top; padding-right: 15px; }
.fbForm .captcha { padding-top: 3px; }
.fbForm .captcha IMG { border: 1px solid #ccc; }
.fbForm INPUT#vericode { width: 130px; padding: 4px 0px 4px 25px; background: #E6EFF6 url(images/icon_form.gif) no-repeat 3px -63px; }
.fbForm .submit { padding-top: 15px; }
.fbForm INPUT#fbSubmit {
	border: 1px solid #ccc;
	background-color: #f7f7f7;
	cursor: pointer;
	padding: 2px 15px 2px 15px;
}
.fbForm .error { font-size: 8pt; color: red; display: none; }</pre>

<p>
	Создадим чанк <tt>eFBThanks</tt>, который используется после успешной отправки сообщения. Тут всё просто:
</p>

<pre class="brush: html;">&lt;p&gt;Cообщение отправлено.&lt;/p&gt;
&lt;p&gt;Благодаря вашей помощи проект может стать лучше!&lt;/p&gt;</pre>

<p>
	Теперь чанк <tt>eFBReport</tt>, который является шаблоном для отправляемого письма:
</p>

<pre class="brush: html;">&lt;p&gt;Поступило сообщение через обратную связь сайта &lt;b&gt;site.ru&lt;/b&gt;.&lt;/p&gt;
&lt;p&gt;
[+postdate+]
&lt;br&gt;
[+fbName+] ([+fbEMail+]) пишет:
&lt;/p&gt;
----------------------------------------------
&lt;p&gt;
[+fbText+]
&lt;/p&gt;
----------------------------------------------
&lt;p&gt;Для ответа можно использовать эту ссылку: &lt;a href=&quot;mailto:[+fbEMail+]?subject=RE: site.ru&quot;&gt;[+fbEMail+]&lt;/a&gt;&lt;/p&gt;</pre>

<p>
	В чанке используются следующие плейсхолдеры: <tt>[+postdate+]</tt> - дата отправки сообщения;
	<tt>[+fbName+]</tt>, <tt>[+fbEMail+]</tt>, <tt>[+fbText+]</tt> - соответствующие поля формы.
</p>

<p>
	На этом основную часть нашей формы мы сделали. Она уже вполне работоспособна.
	Корректность введенных данных проверяется на сервере.
	Теперь добавим в нашу форму немного динамичности. Что мы сделаем:
</p>

<ul>
	<li>проверка корректности введенных значений &laquo;на лету&raquo;;</li>
	<li>кнопка отправить будет доступна только когда все поля формы заполнены
	верно (за исключением каптчи &mdash; это проверяется на сервере всегда).</li>
</ul>

<p>
	Создаем папку <tt>nikoFeedBack</tt> в папке <tt>assets/snippets/</tt>.
	Теперь в созданной папке создаем файл <tt>validate.js</tt>.
	Содержание файла следующее:
</p>

<pre class="brush: javascript;">var fbNowShowError; // Выводить ли при проверки поля ошибки
 
function fbIsFormValid() {
       
        var formFeedBack = document.getElementById(&quot;FeedBackForm&quot;);
        var result = true;
       
        for (var i=0; i &lt; formFeedBack.elements.length; i++) {
                if (typeof formFeedBack.elements[i].valid == &quot;boolean&quot;) {
                        result = result &amp;&amp; formFeedBack.elements[i].valid;
                }
        }
       
        return result;
       
}
 
function fbValidateField(txtField) {
       
        var fbNameError = document.getElementById(&quot;fbNameError&quot;);
        var fbEMailError = document.getElementById(&quot;fbEMailError&quot;);
        var fbTextError = document.getElementById(&quot;fbTextError&quot;);
       
        // Проверка имени
        if (txtField.name == &quot;fbName&quot;) {
                if (txtField.value.length &lt; 5) {
                        if (fbNowShowError) {
                                fbNameError.style.display = &quot;block&quot;;
                                fbNameError.innerHTML = &quot;Имя должно содержать не менее 5 символов&quot;;
                        }
                        txtField.valid = false;
                } else {
                        if (fbNowShowError) {
                                fbNameError.style.display = &quot;none&quot;;
                        }
                        txtField.valid = true;
                }
        }
       
        // Проверка email
        if (txtField.name == &quot;fbEMail&quot;) {
                var re = /^[\.\-_A-Za-z0-9]+?@[\.\-A-Za-z0-9]+?\.[A-Za-z0-9]{2,6}$/;
                if (!re.test(txtField.value)) {
                        if (fbNowShowError) {
                                fbEMailError.style.display = &quot;block&quot;;
                                fbEMailError.innerHTML = &quot;Некорректный адрес электронной почты&quot;;
                        }
                        txtField.valid = false;
                } else {
                        if (fbNowShowError) {
                                fbEMailError.style.display = &quot;none&quot;;
                        }
                        txtField.valid = true;
                }
        }
       
        // Проверка текста сообщения
        if (txtField.name == &quot;fbText&quot;) {
                if (txtField.value.length &lt; 15) {
                        if (fbNowShowError) {
                                fbTextError.style.display = &quot;block&quot;;
                                fbTextError.innerHTML = &quot;Текст сообщения должен содержать не менее 15 символов&quot;;
                        }
                        txtField.valid = false;
                } else {
                        if (fbNowShowError) {
                                fbTextError.style.display = &quot;none&quot;;
                        }
                        txtField.valid = true;
                }
        }
       
        var fbSubmit = document.getElementById(&quot;fbSubmit&quot;);
       
        if (fbIsFormValid()) {
                fbSubmit.disabled = false;
                fbSubmit.style.cursor = 'pointer';
        } else {
                fbSubmit.disabled = true;
                fbSubmit.style.cursor = 'default';
        }
       
}
 
function fbValidateFieldOnChange(oEvent) {
       
        fbNowShowError = true;
       
        oEvent = oEvent || window.event;
        var txtField = oEvent.target || oEvent.srcElement;
       
        fbValidateField(txtField);
}
 
function fbValidateFieldOnKeyUp(oEvent) {
       
        fbNowShowError = false;
       
        oEvent = oEvent || window.event;
        var txtField = oEvent.target || oEvent.srcElement;
       
        fbValidateField(txtField);
}
 
window.onload = function () {
       
        var fbName = document.getElementById(&quot;fbName&quot;);
        var fbEMail = document.getElementById(&quot;fbEMail&quot;);
        var fbText = document.getElementById(&quot;fbText&quot;);
        var fbVeriCode = document.getElementById(&quot;vericode&quot;);
        var fbSubmit = document.getElementById(&quot;fbSubmit&quot;);
       
        fbSubmit.disabled = true;
        fbSubmit.style.cursor = 'default';
       
        fbName.valid = false;
        fbEMail.valid = false;
        fbText.valid = false;
       
        fbName.onchange = fbValidateFieldOnChange;
        fbEMail.onchange = fbValidateFieldOnChange;
        fbText.onchange = fbValidateFieldOnChange;
       
        fbName.onkeyup = fbValidateFieldOnKeyUp;
        fbEMail.onkeyup = fbValidateFieldOnKeyUp;
        fbText.onkeyup = fbValidateFieldOnKeyUp;
       
        fbNowShowError = true;
        if (fbName.value != '') fbValidateField(fbName);
        if (fbEMail.value != '') fbValidateField(fbEMail);
        if (fbText.value != '') fbValidateField(fbText);
        fbNowShowError = false;
       
        fbVeriCode.value = '';
       
        fbValidateField;
       
};</pre>

<p>Кратко опишу, что делает каждая их функций:</p>

<p>
	<tt>fbIsFormValid</tt> &mdash; проверяет все ли поля формы заполнены правильно.<br>
	<tt>fbValidateField</tt> &mdash; проверяет правильно ли заполнено поле.
	Автоматически определяется тип поля &mdash; и проводится соответствующая проверка.
	По окончании проверки поля проверяется &mdash; правильно ли заполнены остальные поля,
	и если правильно &mdash; то кнопка <i>&laquo;Отправить&raquo;</i> становится доступной,
	если не правильно &mdash; то недоступной.<br>
	<tt>fbValidateFieldOnChange</tt> &mdash; функция, вызываемая при изменении поля
	(после потери им фокуса). Сама функция &mdash; вызывает функцию проверки поля.<br>
	<tt>fbValidateFieldOnKeyUp</tt> &mdash; функция, вызываемая каждый раз при
	изменении поля. Сама функция &mdash; вызывает функцию проверки поля.
</p>

<p>
	Разные функции на события <tt>OnChange</tt> и <tt>OnKeyUp</tt> вызываются для того, чтобы задать
	- выдавать ли ошибку при проверке или нет. То есть, когда объект не потерял фокус,
	но произошло его изменение (событие <tt>OnKeyUp</tt>) &mdash; производится проверка
	данного поля и всех остальных полей для того, чтобы в случае правильности заполнения
	сделать активной кнопку <i>&laquo;Отправить&raquo;</i>, но при этом ошибки не выводятся,
	либо продолжают показываться. В случае же когда поле теряет фокус
	(событие <tt>OnChange</tt>) помимо проверки &mdash; показывать или нет кнопку
	<i>&laquo;Отправить&raquo;</i> - также показывается сообщение об ошибке в случае
	некорректных данных или исчезает ошибка, если данные введены верно.
	Довольно запутанно, но это необходимо, например, для того, чтобы при
	первоначальном заполнении полей &mdash; сразу не выскакивали ошибки
	(если разделения не будет, то как только мы начнем вводить к примеру Имя, то сразу вылезет ошибка).
</p>

<p>
	В конце с помощью конструкции <tt>window.onload = function () { &hellip; }</tt> после
	загрузки страницы задаем обработчики событий для полей и сразу проверяем все поля
	на валидность. Плюс данного способа в том, что форма остаётся рабочей даже в том случае,
	если у пользователя отключен <strong>JavaScript</strong>.
</p>

<p>
	Всё! Форма обратной связи с проверкой данных &laquo;на лету&raquo; готова. Пользуйтесь :).
</p>

<p>
	В приложенном архиве лежит файл стилей, изображения, используемые для оформления,
	файлы с чанками <i>eFBForm</i>, <i>eFBReport</i>, <i>eFBThanks</i>, файл со сниппетом
	<i>nikoFeedBack</i> и файл с JS-скриптами <i>validate.js</i>.
</p>

<p style="text-align: right;"><a href="http://predvoditelev.ru/dc/L2V4YW1wbGVzL21vZHhfZmVlZGJhY2suemlw" title="/examples/modx_feedback.zip" >Приложение к статье</a> <sup title="Количество переходов">57</sup> (modx_feedback.zip, ~5.5kb)</p>

<h2><strong>UPD</strong>: Отправление сообщения в CMS</h2>

<p>
	Для того, чтобы отправлять сообщения в кмс, нужно внести немного изменений в сниппет <tt>nikoFeedBack</tt>.
</p>

<p>Добавить функцию:</p>

<pre class="brush: php;">function fbSubmitMessage($fields) {
global $modx;
	
	$txt = $modx-&gt;getChunk(’eFBReport’);
	foreach ($fields as $key =&gt; $val) {
		$txt = str_replace(’[+'.$key.'+]‘,$val,$txt);
	}
	
	$modx-&gt;sendAlert(’alert’,'admin’,1,$fields[subject].’ (’.$fields[fbName].’)',$txt,1);
	
	return true;
	
}</pre>

<p>
	И добавить параметры <tt>noemail=1</tt> (чтобы на почту не отправлялось) и
	<tt>eFormOnMailSent=fbSubmitMessage</tt> (чтобы запустилась наша функция отправки в кмс)
	при вызове <tt>eForm</tt>, те в итоге вот такой вызов:
</p>

<pre class="brush: php;">$outForm = $modx-&gt;runSnippet(
	&quot;eForm&quot;,
	array(
		&quot;formid&quot; =&gt; &quot;FeedBackForm&quot;,
		&quot;to&quot; =&gt; &quot;admin@site.ru&quot;,
		&quot;tpl&quot; =&gt; &quot;eFBForm&quot;,
		&quot;report&quot; =&gt; &quot;eFBReport,
		&quot;thankyou&quot; =&gt; &quot;eFBThanks&quot;,
		&quot;from&quot; =&gt; &quot;[+fbEMail+]&quot;,
		&quot;fromname&quot; =&gt; &quot;site.ru&quot;,
		&quot;subject&quot; =&gt; &quot;Посетители сайта пишут&quot;,
		&quot;vericode&quot; =&gt; &quot;1&quot;,
		&quot;eFormOnMailSent&quot; =&gt; &quot;fbSubmitMessage&quot;,
		&quot;noemail&quot; =&gt; &quot;1&quot;
	)
);</pre><a href="http://predvoditelev.ru/page/modx_feedback#comments"> Обсудить</a>]]></description>
		</item>
			</channel>
</rss>
