jQuery: решение проблемы с куками на WordPress при использовании MaxCache

Этот пост адресован пользователям WordPress, у которых стоит кэширование с помощь MaxCache.

WordPress имеет в своем функционале две очень хорошие особенности, которые повышают юзабилити сайта для посетителей, оставляющих комментарии:

  1. Однажды оставив одобренный администратором сайта комментарий, посетитель избавляется от необходимости при каждом следующем комментарии вновь вводить свои данные, такие, как имя, e-mail, и адрес сайта.
  2. Если дoбaвляeмый кoммeнтapий уходит на модерацию, то комментирующий все равно видит его на сайте, обычно с пометкой «коммент ожидает модерации». Это дает посетителю понять, что комментарий успешно добавлен и не попал в спам-фильтр.

Проблема

Не так давно известный программист Максим создал очень полезный и нужный скрипт — кэш для WordPress (MaxCache), который существенно снижает нагрузку на сервер хостинга. Для моего тормознутого хостинга, на котором в данный момент мой блог dimox.name вынужден, так сказать, волочить свое существование, этот скрипт оказался спасением.

Но речь сейчас не о хостинге и не о скрипте MaxCache. Суть поста заключается в нижеследующем.

При использовании кэша MaxCache WordPress-сайт теряет те самые 2 замечательные функции, о которых я написал выше. Это связано с особенностью работы MaxCache, и, к сожалению, на уровне скрипта ничего сделать нельзя.

После установки кэша я неоднократно видел комментарии, в которых мои постоянные читатели с сожалением сообщали о необходимости каждый раз снова вводить свои данные.

Я всегда стараюсь идти на встречу посетителям своих сайтов, поэтому я нашел выход из данной ситуации — написал на jQuery скрипт, который выполняет 2 утерянные после установки кэша функции.

Далее я подробненько опишу код скрипта. Если у вас нет желания все это читать, то можете сразу «перескочить» на конечный код скрипта.

Функция №1 — «вспоминаем» комментирующего

Чтобы избежать возможных конфликтов jQuery с другими фреймворками, создадим вот такую переменную, которую в дальнейшем и будем использовать:

var $j = jQuery.noConflict();

Ну а все содержимое скрипта будем размещать вот в такую функцию, которая выполнит находящийся в ней код после загрузки веб-страницы:

$j(function() { ... })

Для того, чтобы «вспомнить» нашего посетителя, который уже оставлял комментарий, мы создадим куки, в которых запишем его имя, e-mail и адрес сайта. Работать мы будем, соответственно, с формой комментирования WordPress. Ее HTML-код выглядит примерно следующим образом (показываю только теги, необходимы нам):

<form action="/wp-comments-post.php" method="post" id="commentform">
	<input type="text" name="author" />
	<input type="text" name="email" />
	<input type="text" name="url" />
	...
</form>

Чтобы скрипт, который мы получим в результате, у вас работал, убедитесь, что в вашем шаблоне WordPress у тега form используется точно такой же идентификатор (id="commentform").

У каждого input-тега в этой форме есть неизменный параметр name="", за которые мы и будем цепляться при написании скрипта.

Куки мы будет создавать в тот момент, когда комментирующий нажимает кнопку «Дoбaвить кoммeнтapий», для этого воспользуемся вот такой функцией:

$j('#commentform').submit(function() { ... });

В ней мы считаем значения полей input[name="author"], input[name="email"], input[name="url"] и с помощью функции createCookie() (ее код я приведу в конце, дабы не раздувать размер статьи) поместим их в куки браузера:

$j('#commentform').submit(function() {

	createCookie('wp_commenter_author', $j('input[name="author"]').val(), 365);
	createCookie('wp_commenter_email', $j('input[name="email"]').val(), 365);
	createCookie('wp_commenter_url', $j('input[name="url"]').val(), 365);

});

В результате в браузере пользователя появляются куки со следующими названиями: wp_commenter_author, wp_commenter_email и wp_commenter_url. Цифра 365 указывает на количество дней хранения этих куков.

Можно было так и оставить вышеуказанный код создания куков, но в ходе тестирования обнаружился следующий момент. Как вы знаете, если вы залогинены на своем блоге, то при дoбaвлeнии кoммeнтapия поля имя/е-mail/сайт за ненадобностью не отображаются. Поэтому, поскольку этих полей нет, то в 3-х создаваемых куках записывается значение «undifined» (т.е. значение не определено). А, поскольку, далее мы будем эти куки считывать, то нам таких неопределенностей необходимо избежать. Поэтому мы делаем это следующим образом:

$j('#commentform').submit(function() {

	if ($j('input[name="author"]').length) createCookie('wp_commenter_author', $j('input[name="author"]').val(), 365);
	if ($j('input[name="email"]').length) createCookie('wp_commenter_email', $j('input[name="email"]').val(), 365);
	if ($j('input[name="url"]').length) createCookie('wp_commenter_url', $j('input[name="url"]').val(), 365);

});

Т.е. условием вида if ($j('input[name="author"]').length) мы проверяем существования 3-х полей, и, если они есть на странице (т.е. это не залогиненный пользователь), то создаем куки со значениями данных полей.

Хорошо, куки создали. Теперь нужно их считать для последующий подстановки в эти поля. Создадим переменные, в которые сразу же заносим значения наших кук (код функции readCookie() будет приведен также в конце):

var author = readCookie('wp_commenter_author');
var email = readCookie('wp_commenter_email');
var url = readCookie('wp_commenter_url');

Куки прочитали, теперь нужно заполнить ими 3 наших поля (имя/е-mail/сайт):

if (author) $j('input[name="author"]').val(author);
if (email) $j('input[name="email"]').val(email);
if (url) $j('input[name="url"]').val(url);

Условие вида if (author) говорит нам, что, если кука существует в браузере пользователя, то мы вставляем ее значение в соответствующее поле. Эта проверка необходима, чтобы избежать ошибки, если кук нет.

В результате мы получаем вот такой конечный скрипт, который реализует 1-ю функцию — запоминает данные, введенные комментирующим, и в последствии автоматически подставляет их в поля.

var $j = jQuery.noConflict();

$j(function() {

	var author = readCookie('wp_commenter_author');
	var email = readCookie('wp_commenter_email');
	var url = readCookie('wp_commenter_url');

	if (author) $j('input[name="author"]').val(author);
	if (email) $j('input[name="email"]').val(email);
	if (url) $j('input[name="url"]').val(url);

	$j('#commentform').submit(function() {
		if ($j('input[name="author"]').length) createCookie('wp_commenter_author', $j('input[name="author"]').val(), 365);
		if ($j('input[name="email"]').length) createCookie('wp_commenter_email', $j('input[name="email"]').val(), 365);
		if ($j('input[name="url"]').length) createCookie('wp_commenter_url', $j('input[name="url"]').val(), 365);
	});

})

Можно было бы и остановится на таком скрипте. Но, ведь у нас еще есть и вторая задача. Дело в том, что при использовании MaxCache, если комментарий уходит на модерацию, то комментирующий не видит своего комментария на странице, (как это бывает, если кэш не используется). В результате некоторые товарищи думают, что комментарий ушел в спам (или просто что-то пошло не так) и отправляют комментарий заново по нескольку раз.

Чтобы подобного избежать, мы доработаем наш скрипт.

Функция №2 — даем знать, что комментарий добавлен

Сразу отмечу, что, для того, чтобы работала нижеследующая часть кода, необходимо, чтобы в шаблоне комментариев стояла якорь-ссылка для каждого комментария, т.е. в PHP-коде это выглядит примерно следующим образом:

<a href="<?php echo htmlspecialchars( get_comment_link( $comment->comment_ID ) ) ?>"><?php printf(__('%1$s at %2$s'), get_comment_date(), get_comment_time()) ?></a>

А если смотреть исходный HTML-код страницы WordPress-сайта, то должен быть примерно такой код:

<a href="адрес_страницы/#comment-123" title="02 февраля, 2008 в 10:06 пп">текст ссылки</a>

И еще один важный момент. Чтобы после отправки комментария появлялось сообщение о том, что он добавлен, необходимо, чтобы список комментариев находился в теге с идентификатором #commentlist, например:

<ul id="commentlist">
	тут список комментов
</ul>

Сначала мы добавим новую куку, которая будет создаваться также при нажатии на кнопку «Добавить комментарий» и создадим переменную, в которую ее будем считывать:

var submitCheck = readCookie('wp_submit_check');

$j('#commentform').submit(function() {
	...
	createCookie('wp_submit_check', '1', 1);
});

Ее значение ставим равным «1» и срок ее хранения — 1 день.

Все, что мы будем делать дальше, будет работать только при условии, что значение куки «wp_submit_check» равно «1».

if (submitCheck == '1') { ... }

А кука эта когда у нас появляется? Правильно — при отправке формы. Т.е. все манипуляции с кукой wp_submit_check сделаны для того, чтобы выполнять нижеследующие действия только тогда, когда отправлен комментарий, и посетителю нужно об этом сообщить.

Если кратко на словах, то мы будем делать следующее — после добавления комментария посетитель перенаправляется на страницу с адресом вида http://site.ru/postname/#comment-123, где цифра — это идентификатор добавленного комментария. Далее проверяем, есть ли уже на странице ссылка с точно таким же якорем (#comment-123), если нет, то показываем посетителю сообщение о том, что его комментарий добавлен.

Дальнейшие комментарии напишу в самом скрипте:

if (submitCheck == '1') {

	// считываем URL из адресной строки браузера и заносим в переменную
	var href = window.location.href;

	// проверяем, находимся ли мы на странице вида http://site.ru/postname/#comment-123
	// (страница с таким адресом открывается всегда при добавлении комментария в WordPress)
	if (/.+#comment-(\d+)/ig.test(href)) {

		// получаем цифру из URL в адресной строке,
		// которая стоит после "#comment-",
		// и помещаем ее в переменную
		var cid = href.replace(/.+#comment-(\d+)/ig, '$1');

		// создаем регулярное выражение, которое будем использовать дальше,
		// и заносим его в переменную reg
		var reg = new RegExp('#comment-' + cid,'ig');

		// переменная, значение которой будем менять,
		// если ссылка с якорем найдется
		var commentIdFind = 0;

		// пробегаемся по каждой ссылке в комментариях
		$j('#commentlist a').each(function() {

			// тестируем URL ссылки (атрибут href) с помощью
			// регулярного выражения reg (см. выше)
			if (reg.test($j(this).attr('href'))) {

				// если нашли такую ссылку с якорем из адресной строки
				// (а это значит, что коммент выведен на страницу и ничего
				// дополнительно показывать посетителю не нужно),
				// то переменной commentIdFind ставим значение = 1
				// и удаляем куку wp_submit_check
				commentIdFind = 1;
				eraseCookie('wp_submit_check');
			}

		})

		// если commentIdFind = 0, получается, что ссылка не была найдена,
		// значит комментарий не выведен на страницу, и нужно показать сообщение
		if (commentIdFind == 0) {

			// выводим сообщение путем добавления тега <li></li> в конец списка с комментариями
			// и назначаем ему идентификатор вида comment-123,
			// где вместо цифры подставляется ID добавленного комментария,
			// для того, чтобы проскроллить страницу к этому сообщению
			// HTML-код сообщения измените под себя
			$j('#commentlist').append('<li id="comment-' + cid + '"><em><strong>' + author + '</strong>, спасибо за Ваш комментарий! Он будет опубликован, как только его проверит администратор сайта.</em></li>');

			// считываем юзер-агента для определения браузера Opera,
			// чтобы применить к нему особые стили
			var userAgent = navigator.userAgent.toLowerCase();
			if (/opera/.test(userAgent)) { var top = 'html'; } else { var top = 'html, body'; }

			// определяем расстояние от верхнего края браузера
			// до элемента с нашим сообщением, заносим в переменную
			var offset = $j('#comment-' + cid).offset().top;

			// плавно прокручиваем окно браузера до сообщения,
			// которое показываем посетителю
			$j(top).animate({scrollTop: offset}, 500);
		}

	// если мы ушли со странице вида http://site.ru/postname/#comment-123,
	// то удаляем куку wp_submit_check
	} else {
		eraseCookie('wp_submit_check');
	}

}

В результате выполнения этой части кода комментатор увидит вот такое сообщение (естественно, если выполнены все вышеописанные условия):

%username%, спасибо за Ваш комментарий! Он будет опубликован, как только его проверит администратор сайта.

Конечный код jQuery-скрипта

В итоге у нас получился вот такой скрипт (для тех, кто не читал мои подробности по коду — обратите внимание на условия работы скрипта: условие раз, условие два, условие три):

function createCookie(name,value,days) {
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/";
}
function readCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}
function eraseCookie(name) {
	createCookie(name,"",-1);
}

var $j = jQuery.noConflict();

$j(function() {

	var author = readCookie('wp_commenter_author');
	var email = readCookie('wp_commenter_email');
	var url = readCookie('wp_commenter_url');
	var submitCheck = readCookie('wp_submit_check');

	if (author) $j('input[name="author"]').val(author);
	if (email) $j('input[name="email"]').val(email);
	if (url) $j('input[name="url"]').val(url);

	$j('#commentform').submit(function() {
		if ($j('input[name="author"]').length) createCookie('wp_commenter_author', $j('input[name="author"]').val(), 365);
		if ($j('input[name="email"]').length) createCookie('wp_commenter_email', $j('input[name="email"]').val(), 365);
		if ($j('input[name="url"]').length) createCookie('wp_commenter_url', $j('input[name="url"]').val(), 365);
		createCookie('wp_submit_check', '1', 1);
	});

	if (submitCheck == '1') {

		var href = window.location.href;

		if (/.+#comment-(\d+)/ig.test(href)) {

			var cid = href.replace(/.+#comment-(\d+)/ig, '$1');
			var reg = new RegExp('#comment-' + cid,'ig');
			var commentIdFind = 0;

			$j('#commentlist a').each(function() {
				if (reg.test($j(this).attr('href'))) {
					commentIdFind = 1;
					eraseCookie('wp_submit_check');
				}
			})
			if (commentIdFind == 0) {
				$j('#commentlist').append('<li id="comment-' + cid + '"><em><strong>' + author + '</strong>, спасибо за Ваш комментарий! Он будет опубликован, как только его проверит администратор сайта.</em></li>');
				var userAgent = navigator.userAgent.toLowerCase();
				if (/opera/.test(userAgent)) { var top = 'html'; } else { var top = 'html, body'; }
				var offset = $j('#comment-' + cid).offset().top;
				$j(top).animate({scrollTop: offset}, 500);
			}
		} else {
			eraseCookie('wp_submit_check');
		}

	}

})

Ну а как подключить этот скрипт к сайту, я, надеюсь, вы уже знаете, об этом я не раз писал в своих предыдущих статьях по jQuery.

Комментарии (72)
  1. 1

    Здравствуйте! Действительно полезный код, но я никак не могу разобраться с его установкой. Подскажите, пожалуйста, может я что то сделал не так?
    Первым делом я вставил код

    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
     <script type="text/javascript" src="http://health-bit.ru/comment.js"></script>

    В файл header.php прямо перед тегом

    </head>

    Затем сохранил Ваш код в файл comments.js и закинул на фтп своего блога (просто в папку httpdocs).
    Но ничего не работало. Потому я решил выполнить условия. Второе я сделал, т.е. вставил

    <a href="<?php echo htmlspecialchars( get_comment_link( $comment->comment_ID ) ) ?>"><?php printf(__('%1$s at %2$s'), get_comment_date(), get_comment_time()) ?></a>

    в файл comments.php (почти в начало, после конструкции

    <?php "что то там" ?>

    Однако все равно не работает (проверял просто: разлогинившись оставляю коммент и опять перехожу в этот же пост — ни данные не введены, ни коммента не видно).
    Возможно, не выполнено первое условие, но я никак не пойму, где искать этот тег form и его идентификатор?
    Подскажите, пожалуйста, в чем может быть проблема?
    Спасибо.

  2. 6

    Хм, все сохраняется кроме е-мейла. Вместо введенного почтового адреса запоминается фраза «введите ваш E-mail». Комментарии стандартные от вордпресс. Никто не сталкивался с этим? В чем может быть причина?

  3. 7

    проверим как отображается. Жаль нет скриншота

  4. 8

    Получается что на этом блоге не так работает скрипт? У меня лично не работает 2-я часть. Условие не выполняется т.к. у меня создаются такие страницы: сайт/?p=49409&cpage=1#comment-1444 тоесть выходит нумерация страницы. Пытался её туда засунуть — не выходит.

  5. 9

    У меня на блоге хоть и не запоминаются данные пользователя, но при двойном клике на поле имени или сайта, там появляются варианты. Можно выбрать свое имя, если вы уже раньше оставляли комментарий. Поэтому лишние скрипты использовать необязательно.

  6. 10

    Здравствуйте! Очень интересное решение! Я уже два дня не могу найти ответ на интересующий меня вопрос. Может вы сможете мне помочь. МаксКеш выводит внизу в футере ссылку с отображением статистики. Как её можно убрать? Почитал инструкцию. не фига не понял. Помогите, если можете, пожалуйста.

  7. 18

    Подскажите, пожалуйста, может не работать функция №2 из-за того, что вместо
    http://site.ru/postname/#comment-123
    ссылка имеет вид
    http://site.ru/postname.html#comment-123
    Если да, то как это исправить?

  8. 20

    Дмитрий, восхищаюсь вашим умением находить великолепные решения! Можете ли вы помочь в моей ситуации? На записях, в которых уже имеются комменты — уведомление о том, что комментарий ожидает проверки успешно работает. Если же коммент первый — посетитель ничего не видит, и часто отправляет по два комментария. Не видит ли ваш меткий глаз, где искать проблему отсутствия премодерации у первого комментария?

  9. 22

    Добрый день.
    WP 4 — в файлах comments.php не смогла найти описанную вами конструкцию.
    Но реализация вашей идеи важна. Не могли бы вы мне помочь?

Ваш комментарий