Создаем скрипт для автоматической отправки опечаток

Если вы следите за новыми web сервисами, то наверняка слышали о системе Orphus Дмитрия Котерова. Её основная цель – уменьшить количество орфографических ошибок на web сайтах.
Идея очень простая и элегантная. Если посетитель видит ошибку, то он выделяет её с помощью мышки и нажимает «Ctrl+Enter». После этого, владелец сайта получает письмо с выделенным текстом. Главное преимущество в том, что посетителю нужно сделать минимум действий. Никаких перезагрузок страниц и заполнения форм, нужна только поддержка JavaScript в браузере.
В этой статье я расскажу о том, как самостоятельно сделать подобную систему для собственного сайта.
Преимущества такого решения.
1) Вы не зависите от стороннего сервиса.
2) Сообщения об опечатках можно будет просматривать через web интерфейс. Все-таки это удобнее чем копаться в почте
.
3) Можно легко реализовать защиту от спама.
Итак, приступаем.
Наша система будет состоять из трёх компонентов:
1) html страница со JavaScript функцией, которая будет отправлять сообщение (AJAX-запрос);
2) PHP скрипт, добавляющий сообщение в базу данных;
3) PHP скрипт для просмотра сообщений.
Такую простую структуру я выбрал специально, т.к. по большому счету эта система должна быть интегрирована в движок сайта, а их очень много. Привязываться к отдельному решению мне не хотелось.
Рассмотрим главную страницу.
-
<?xml version="1.0" encoding="UTF-8" ?>
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-
<html xmlns="http://www.w3.org/1999/xhtml">
-
<head>
-
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-
<title>Ctrl Space Sender</title>
-
-
<link rel="stylesheet" href="css/blueprint/screen.css" type="text/css" media="screen, projection" />
-
<link rel="stylesheet" href="css/blueprint/print.css" type="text/css" media="print" />
-
<!–[if IE]><link rel="stylesheet" href="css/blueprint/ie.css" type="text/css" media="screen, projection" /><![endif]–>
-
-
<script type="text/javascript" src="js/jquery/jquery-1.2.3.js"></script>
-
<script type="text/javascript">
-
$(document).ready(
-
function() {
-
//назначаем обработчик нажатия на Ctrl + Enter
-
$(document).keypress(
-
function(e) {
-
//если посетитель нажал Ctrl + Enter…
-
if ((e.ctrlKey == true) && (e.keyCode == 13)) {
-
//…ищем выделенный текст…
-
var selectedText = window.getSelection();
-
//…и отправляем запрос
-
$.ajax({
-
type: "POST",
-
url: "errorscollector.php",
-
data: {text:[selectedText], pageurl:[window.location.href]},
-
success: function(msg) {
-
alert(msg);
-
}
-
});
-
}
-
}
-
);
-
}
-
);
-
</script>
-
-
</head>
-
<body>
-
<h1>Привет</h1>
-
-
<p>Эта тестовая страница выполняет обработку нажатия на клавиши….</p>
-
</body>
-
</html>
Наибольший интерес здесь представляет скрипт обработки нажатий на клавиши (строки 13-37). Чтобы немного сократить его код я использовал библиотеку jQuery (строка 12).
Разберем его подробнее.
С помощью функции ready указываем, что наш код должен выполняться только после полной загрузки страницы.
Функция keypress назначает обработчик, который будет вызываться при нажатии на любую клавишу. В нашем случае этим обработчиком является анонимная функция (строки 18-33).
Примечание. Вы можете назначить обработчик любому блоку на странице. Для этого с помощью функции $(...) укажите нужный блок.
В первом параметре наша функция получает объект с данными о возникшем событии (в данном случае это нажатие на клавишу).
Код нажатой клавиши содержится в свойстве keyCode этого объекта. Кроме того, если свойство ctrlKey указывает на то, была ли нажата клавиша «Ctrl» (ctrlKey == true). Аналогичное назначение имеет свойство altKey (устанавливается в true, если нажата клавиша «Alt»).
Т.е. мы проверяем, была ли нажата комбинация клавиш «Ctrl+Enter» и если да, то с помощью window.getSelection() получаем выделенный текст и отправляем AJAX запрос.
В параметре url указываем название PHP скрипта, который добавляет данные об опечатке в базу, а в параметре data – выделенный текст и адрес страницы.
После получения ответа от сервера будет вызвана функция, указанная в параметре success (строки 28-30). Она просто покажет сообщение сервера посетителю.
Таблица, в которой будут храниться сообщения, называется errorsdata и имеет 5 полей:
1) id – первичный ключ;
2) addtime – время добавления сообщения;
3) errmes – текст сообщения;
4) userip – IP адрес посетителя;
5) pageUrl – адрес страницы, на которой была найдена ошибка.
Теперь рассмотрим PHP скрипт, сохраняющий сообщения в базе данных.
-
<?php
-
try {
-
//получаем IP посетителя
-
$userIp = $_SERVER['REMOTE_ADDR'];
-
-
$con = new PDO('mysql:host=localhost;dbname=databaseName', 'userName', 'password');
-
$stm = $con->prepare('SELECT id FROM errorsdata WHERE userip=:ip AND addtime > DATE_SUB(NOW(), INTERVAL 1 HOUR)');
-
$stm->execute(array('ip'=>$userIp));
-
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
-
if (count($res) < 3) {
-
$qIns = $con->prepare('INSERT INTO errorsdata (errmes, userip, pageUrl) VALUES (:mes, :ip, :url)');
-
$qIns->execute(array('mes'=>$_POST['text'], 'ip'=>$userIp, 'url'=>$_POST['pageurl']));
-
if ($qIns->rowCount() == 1) {
-
echo "Ваше сообщение получено";
-
}
-
}
-
else {
-
echo "Вы отправляете слишком много сообщений. Пожалейте администратора!";
-
}
-
}
-
catch (PDOException $e) {
-
echo 'Не могу подключиться к БД';
-
}
-
?>
Принцип работы следующий.
Сначала мы проверяем сколько запросов в течении последнего часа отправил данный посетитель. Для этого мы получаем из базы все записи, с данным IP и добавленные не ранее часа назад (строки 7-9).
Если таких записей меньше 3-х – добавляем сообщение в базу (строки 10-16).
В противном случае – игнорируем запрос.
Примечание. В данном примере для работы с базой я использовал PDO, если вы встраиваете эту систему в какой-то фреймворк, то удобнее будет использовать его библиотеку для работы с БД.
Как видите, скрипт возвратит обычную текстовую строку, которая и будет показана посетителю (строка 29 первого листинга).
Теперь напишем скрипт для просмотра сообщений.
Предупреждаю сразу. Прежде чем использовать его в реальном приложении, необходимо добавить авторизацию.
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru" lang="ru">
-
-
<head>
-
<title>Сообщения об ошибках</title>
-
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
-
<meta http-equiv="Content-Style-Type" content="text/css" />
-
-
<link rel="stylesheet" href="css/blueprint/screen.css" type="text/css" media="screen, projection" />
-
<link rel="stylesheet" href="css/blueprint/print.css" type="text/css" media="print" />
-
<!–[if IE]><link rel="stylesheet" href="css/blueprint/ie.css" type="text/css" media="screen, projection" /><![endif]–>
-
</head>
-
-
<body>
-
-
<?php
-
try {
-
$con = new PDO('mysql:host=localhost;dbname=databaseName', 'userName', 'password');
-
$stm = $con->prepare('SELECT id, addtime, errmes, pageUrl FROM errorsdata');
-
$stm->execute();
-
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
-
if (count($res) == 0) {
-
echo "Сообщения отсутствуют";
-
}
-
else {
-
echo "<table summary=\"Эта таблица содержит сообщения об ошибках\">";
-
echo "<caption>Сообщения об ошибках</caption>";
-
echo "<tr>";
-
echo "<th>#</th>";
-
echo "<th>Текст с ошибкой</th>";
-
echo "<th>Дата</th>";
-
echo "<th>URL страницы</th>";
-
echo "</tr>";
-
$i = 0;
-
foreach ($res as $row) {
-
if ($i % 2 == 0) {
-
echo "<tr>";
-
}
-
else {
-
echo "<tr class=\"even\">";
-
}
-
echo "<td>".$row['id']."</td>";
-
echo "<td>".$row['errmes']."</td>";
-
echo "<td>".$row['addtime']."</td>";
-
echo "<td><a href=\"".$row['pageUrl']."\">".$row['pageUrl']."</a></td>";
-
echo "</tr>";
-
$i++;
-
}
-
echo "</table>";
-
}
-
}
-
catch (PDOException $e) {
-
echo '<h1>Не могу подключиться к БД</h1>';
-
}
-
-
?>
-
</body>
-
</html>
По большому счету тут и комментировать нечего. Основную часть занимает обычная html разметка.
Мы выполняем всего один запрос (строки 20-22), с помощью которого получаем данные из базы.
После этого в цикле добавляем эти данные в html таблицу.
Кстати, чтобы не писать стили самому я использовал CSS фреймворк Blueprint. Раньше я с ним практически не работал, но, похоже, вещь довольно удобная.
Скачать
Вы можете скачать архив со скриптом. Для того чтобы запустить пример вам нужно:
1) включить поддержку PDO (в php.ini);
2) создать базу данных;
3) создать таблицу для хранения сообщений (запрос, создающий таблицу находится в файле dump.sql);
4) указать параметры подключения к базе в файлах viewer.php и errorscollector.php.
Как видите, с помощью нескольких десятков строк кода мы создали довольно удобную систему отправки сообщений. Если у вас возникли вопросы или замечания – пишите, постараюсь ответить
.
UPD (27.12.2008): Внимание! Скрипт, приведенный в статье, работает только в FireFox и Opera. Кроссбраузерный вариант приведен в комментариях (тестировался в FireFox, Opera, IE и Chrome).
Интересно почитать:
Простая техника для настройки MFA сайтов и для улучшения QS на лендинг страницах.
Понравилась статья? Подписывайтесь на продолжение
!
Опубликовано в HTML, JavaScript, PHP, Web разработка
Комментарии (48)
Вы можете отслеживать обсуждение записи с помощью RSS 2.0 ![]()
Вы также можете оставить комментарий, или трекбек с Вашего сайта.










Если б это все еще оформить в виде плагина для вордпреса например, было б просто суппер.
Сначала нужно поискать готовый плагин
А вообще идея хорошая и реализовать в принципе не сложно.
Ну и как, нашли плагин, у меня не удалось… Думаю самому написать?
Если честно, не искал
Не уверен, что стоит заниматься плагином. Скрипт достаточно простой. Кроме того, у отдельного решения есть свои преимущества. Например, можно собирать данные с нескольких сайтов, которые работают на других движках.
Хотя, думаю, такой плагин сможет составить реальную конкуренцию решению Дмитрия Котерова.
Поставил в планы на разработку, думаю в конце недели сделаю.
аналогично поступил при разработке сайт http://www.jollyday.ru правда навешал всё это на jQuery
У меня тоже в примере jQuery используется
Правда есть одно замечание. Если бы вы не написали, в жизни бы не догадался, что можно отправить сообщение. В левый нижний угол редко кто смотрит.
И если можно вопрос по реализации. У вас запрос отправляется странице /orpho/orpho.html в которой, как я понял вы подключаете orpho.php. Вы использовали отдельный скрипт или встраивали в движок?
Грубо говоря, там небольшая система роутинга, запросы направляет в нужное место, а уже там выкидывает либо в базу либо на e-mail.
Владимир, последние 2 коммента, на которые вы отвечали - это спам (Санек, Серега). Сегодня точно такие же пытались опубликовать на моем блоге. И еще на нескольких блогах видел их копии.
Спасибо!
.
Вообще-то в отношении второго комментария были подозрения из-за фразы "и за кошелек тоже", но решил перестраховаться и подождать ответит он на вопрос или нет. А на первый никогда бы не подумал, что это спам.
Только что поискал в Google фразу "за душу берут, и за кошелек тоже" (точное соответствие) - 12 результатов.
Похоже теперь все подозрительные комментарии придется так проверять
Явная тенденция - пишут так чтобы текст был вроде бы по теме поста и стараются играть на самолюбии автора.
В общем удалил оба комментария и свои ответы.
Спасибо еще раз
Да, действительно играют на самолюбии. Рекомендую вам более предвзято относиться к комментариям, ссылки с автора которых ведут на коммерческие сайты. Есть ряд сервисов, которые за это платят, поэтому часто публикуют вроде бы и коммент по теме, но на самом деле он лишь ради ссылки, за которую потом автору заплатят копейку. Я для себя предусмотрел ряд мер, которые исключают появление подобных комментариев на моем блоге. Если вас это интересует, то сможете найти у меня статьи на эту тему по тегу "спам".
Тема, конечно, интересная. Правда я все ваши статьи о спаме раньше прочитал
.
Теоретически если спам добавляется вручную, то он пройдет через любую автоматическую проверку. И, как мне кажется, единственный выход в этой ситуации - сделать добавление спама не выгодным для спамера. Т.е. максимально отсеять "тупой" спам, а читать пост и писать осмысленные комментарии за те копейки, которые платят рекламодатели, надеюсь, будут очень не многие.
Использовать радикальные методы вроде Maxsite Comment Allow мне лично очень не хочется. Может если ситуация станет хуже…
А вот WP-SpamFree я не пробовал, идея убрать captcha выглядит очень заманчиво.
P.S. Кстати, у вас в "Облаке тегов" почему-то нет тега "Спам" (раза три смотрел
). Хотя сам тег есть (http://dimox.name/tag/spam/) и в конце постов он отображается.
Спасибо за код, очень полезная штуковина, буду следить за новинками…
Отличная статья, и полезная. Действительно, иногда проскакивают ошибки - ничего не поделаеш!
Спасибо за код, попробую использывать на своём сайте.
Мне подошёл, спасибо)
давно искал эти коды, чуть переделаю и как раз будет!
а как это будет выглядеть без jquery?
Значительно объемнее
Сделать нужно всего две операции:
1) назначить обработчик Ctrl+Space;
2) из этого обработчика отправить ajax запрос (этот кусок без jquery или другой библиотеки займет несколько десятков строк).
Вобщем, никакого желания переписывать
дело в объеме и трафике. у меня сама по себе страница на 100 кб, и если ради опечаток тащить еще и библиотеку на те же 100 кб, то как бы полезность самой идеи уже не так очевидна. конечно, библиотека скачается всего один раз, а дальше прокешуется, но тем не менее…
Загружать 100кБ совсем не обязательно.
Ведь библиотеку можно сжать и заархивировать. (Подробно я писал в этой статье.)
В результате получается около 16кБ.
спасибо! у меня почти все получилось, а архив вышел на 14 кб…
остается только проблема с кодировкой. на сервере все (и php и таблицы базы) в win1251, а скрипт передает в utf8. пока поставил перекодировщик в скрипте, что кидает в базу… где и как задать кодировку для js скриптов я так и не нашел. может подскажите?
Посмотрите строку 7 в скрипте. Кроме того, нужно сам скрипт сохранить в win1251 (можно сделать, например, с помощью pspad).
Кодировка js скриптов определяется кодировкой файла в котором он сохранен. Кроме того, нужно проверять настройки web сервера.
Точнее сказать сложно. Слишком мало данных (какие скрипты? в какой кодировке? и что должно получиться?)
файлы в win1251 пересохранил первым делом.
на везде сервере стоит win1251
кодировка страницы объявлена win1251. добавил charset="windows-1251" в теги вызова яваскриптов - не помогло.
у меня стоит свои php скритп на запись в базу. счас в отладке он возвращает строку "добавлено успешно" и строку sql запроса.
получается так: выделяю, нажимаю.
выскакивает окошко с надписью о добавлении в виде "????????" и нормальными русскими и латинскими буквами в строке sql. в саму sql базу добавляется абракадабра.
Подождите, но ведь ajax запрос можно отправить только в utf-8.
Похоже, единственный вариант - изменять кодировку на стороне сервера. Например, с помощью mb_convert_encoding.
с кодировкой ладно…
у меня работает скрипт только в фаирфоксе,
в опере блокирует отправку из скрипта,
в хроме молчит и ничего не происходит,
в експлорере тоже ничего не происходит.
причем так же себя ведет и ваш пример..
Я допустил ошибку в тексте. Вместо Ctrl+Space нужно читать Ctrl+Enter. В скрипте стоит код клавиши Enter (e.keyCode == 13 первый листинг строка 20).
Проверил в опере - все работает. Если заметите еще ошибки, обязательно пишите!
владимир, я не столько далек от программирования, чтобы не разобраться с кодами клавиш. хотя похоже в опере это меня и подвело - поставил отправку на алт+интер, а в опере по этому сочетанию проверка на мощеников, вот оно и открывалось, вместо отправки опечатки.
исправил на кнтр+интер, в опере заработало. експлорер все еще молчит, если в нем не заработает, то придется все таки делать отдельной стандартной формой…
http://syzran-small.net
проверьте, как у вас будет вести себя скрипт
Я прошу прощения, действительно лень было тестировать во всех браузерах и понадеялся на jQuery
Да и пример задумывался как демонстрационный…
В общем, проблем две.
1) Разные браузеры по-разному обрабатывают событие keypress.
2) Получение выделенного текста в IE отличается от остальных браузеров.
Вдаваться в подробности не буду, просто приведу рабочий код:
$(document).ready( function() { //назначаем обработчик нажатия на Ctrl + Enter var isCtrl = false; $(document).keyup(function (e) { if(e.which == 17) isCtrl=false; }).keydown(function (e) { if(e.which == 17) isCtrl=true; if(e.which == 13 && isCtrl == true) { //...ищем выделенный текст... if (window.getSelection) { var selectedText = window.getSelection(); } else if (document.getSelection) { var selectedText = document.getSelection(); } else if (document.selection) { var selectedText = document.selection.createRange().text; } //...и отправляем запрос $.ajax({ type: "POST", url: "errorscollector.php", data: {text:[selectedText], pageurl:[window.location.href]}, success: function(msg) { alert(msg); } }); } }); });По поводу последней версии кода, есть одна странность (обнаружена в Firefox 3). Если перед вызовом $.ajax вставить, например, confirm() с вопросом "Отправить сообщение?", или простой alert, то почему-то открывается окно firefox'овского менеджера загрузок (хотя оно должно открываться по Ctrl+J). Если вместо Ctrl использовать Alt (код - 18), всё проходит на ура. Кроме того, первоначальный вариант с $(document).keypress() также срабатывает корректно. В чем может быть проблема? В Firefox? в jQuery?
Похоже, это недокументированные возможности FF
Разница между первым и вторым вариантом в обработчике нажатия на кнопку (keydown и keypress).
Не получается у меня определить в чем проблема. Коды у Enter'а и буквы J разные, почему происходит вызов окна - не понятно. Проверял firebug'ом - код работает именно так, как и должен.
К тому же в этом варианте кода вообще не должно играть роль какое сочетание клавиш используется…
Вобщем, если разберетесь, напишите, пожалуйста. Но, думаю, проблема именно в FF и решить ее получится только переходом на FF4
В яндекс блогах увидел ссылку на скриптик КОППА - тоже орфо система, но сам я не тестил её.
Спасибо за ссылку!
А также выделеный текст можно получить таким образом:
var content = window.content ? window.content : window;
var selText = false;
if (content.getSelection) {
selText = content.getSelection().toString();
} else if (document.selection && document.selection.createRange) {
selText = document.selection.createRange().text;
}
Если я правильно понял, вы перебираете такие варианты:
1) window.content.getSelection()
2) window.getSelection()
3) document.selection.createRange()
Разница с моим в том, что у меня есть ещё вызов
document.getSelection()
но нет
window.getSelection()
Не знаете на каких браузерах это может сказаться?
А можно на примере показать, как обойтись без подключения jQuery
у меня на Joomla где то конфликт, если работает этот скрипт, то не работает другой, который разворачивает превьюшки картинок в реальный размер. Даже нашел где именно проблема, в файле самого скрипта (jquery-1.2.3.js) строку с переменной window.$ заремливаю, тогда картиночный скрипт начинает работать, отремливаю, орфографический скрипт работает, картиночный нет.
Есть мысли как побороть?
Этот скрипт использует jQuery для назначения обработчиков событий
$(document).ready$(document).keypress
и отправки AJAX запроса
$.ajaxУверен, вы легко найдете готовые примеры таких скпиптов без jQuery.
Но прежде всего стоит попробовать
$.noConflict()
Эта функция восстанавливает значение переменной $.
FireBug выдает такую ошибку:
setting a property that has only a getter
где собака зарыта?
Это означает, что вы пытаетесь изменить значение приватного свойства объекта, которое имеет только метод чтения (getter).
Напишите подробнее когда возникла ошибка. Иначе сложно сказать что-то конкретное.
решилось заменой строки
data: {text:[selectedText], pageurl:[window.location.href]},
на
data: {text:"+selectedText, pageurl:"+window.location.href},
т.е. принудительно преобразовываем полученную переменную с выделением в строковую.
Странно, у меня все нормально работает. К тому же я ориентировался на код, приведенный в документации jQuery.
При такой записи (text:[selectedText]) jQuery сама выполнит преобразование в &text=…&…
А какая у вас версия FF и jQuery?
[...] без помощи блога SimpleCoding.org была написана вот такая [...]
jQuery 1.2.6
Знаю, что старовата, но на ней завязано ядро Drupal 6.x.
Поэтому сменить пока нельзя.
FF 3.0.7.
Но код не работал ни в одном из браузеров.
А можете FireBug'ом посмотреть как формируется строка запроса в вашем варианте?
Рад, что читали
Да, тега действительно не было, стояло ограничение на количество. Теперь поправил. Спасибо за подсказку
P.S. Сори за то, что я развел тут оффтоп.
Тема спама для блоггера офтопом быть не может
отлично! теперь работает везде! спасибо =)))
Ждем