Модификация сниппета WebSignup

С момента публикации заметки об интеграции freeCap PHP CAPTCHA в MODX за мной остался должок, а именно описание изменений в файле websignup.inc.php сниппета WebSignup. Возвращаю. Все, что можно, переведено на русский язык.

Изменения:

  1. Собственно новая капча.
  2. Генерация выпадающего списка стран взамен громоздкого «чистого» HTML
  3. Добавление опционального пункта Пол

Предупреждаю, получилось длинно.

Пойдем сверху вниз. Воспользуемся функцией MODx API regClientStartupScript для добавления пользовательских скриптов в секцию head веб-страницы.

После строки

if (!$tpl) $tpl = getWebSignuptpl($useCaptcha);

добавляем скрипт, который управляет сменой картинок капчи:


if ($useCaptcha) $modx->regClientStartupScript('
<script type="text/javascript">
// freeCap v1.4.1. Copyright 2005 Howard Yeend <www.puremango.co.uk>
function new_freecap()
{
	// loads new freeCap image
	if(document.getElementById)
	{
		// extract image name from image source (i.e. cut off ?randomness)
		thesrc = document.getElementById("freecap").src;
		thesrc = thesrc.substring(0,thesrc.lastIndexOf(".")+4);
		// add ?(random) to prevent browser/isp caching
		document.getElementById("freecap").src = thesrc+"?"+Math.round(Math.random()*100000);
	} else {
		alert("Ошибка автообновления freeCap картинки\nНажмите кнопку регистрации для обновления");
	}
}
</script>
');

Находим строки


	// verify form code
	if($useCaptcha && $_SESSION['veriword']!=$formcode) {
		$output = webLoginAlert("Incorrect form code. Please enter the correct code displayed by the image.").$tpl;
		return;
	}

Комментируем целиком и после них добавляем


	// freeCap v1.4.1. Copyright 2005 Howard Yeend <www.puremango.co.uk>
	if ($useCaptcha) {
		if (!empty($_SESSION['freecap_word_hash']) && !empty($_POST['formcode'])) {
			// all freeCap words are lowercase.
			// font #4 looks uppercase, but trust me, it's not...
			if ($_SESSION['hash_func'](strtolower($_POST['formcode']))==$_SESSION['freecap_word_hash']) {	
				// reset freeCap session vars
				// cannot stress enough how important it is to do this
				// defeats re-use of known image with spoofed session id
				$_SESSION['freecap_attempts'] = 0;
				$_SESSION['freecap_word_hash'] = false;
			}	
			else {
				$output = webLoginAlert("Ошибка при вводе антиспам-кода! Повторите попытку.").$tpl;
				return;
			}
		}
		else {
			$output = webLoginAlert("Вы забыли ввести антиспам-код! Повторите попытку.").$tpl;
			return;
		}
	}
	// end of freeCap

И у нас остался HTML формы регистрации. С ним, в том числе и с пунктом Пол разберемся немного позже.

Дальше очень важный в плане многоязычности момент. Дело в том, что автор сниппета почему-то не использовал PHP для создания выпадающего списка стран select, а зафигачил его прямо в html, создав порядка 240 сторчек и более 13 Кбайт балласта. Это при том, что аналогичный список в менеджере в формах регистрации сделан с помощью цикла foreach и соответствующих языковых файлов.

Исправим упущение. Дальнейший кусочек кода сделан с учетом многоязычности.
Выведем в цикле список стран и воспользуемся языковым файлом стран. Язык можем задать дополнительным параметром сниппета &language=`russian-UTF8`, либо будет использован язык админки. Само собой, мы можем задать любой язык, который присутствует в каталоге /manager/includes/lang/country. Некоторые, как русский, должны быть в кодировке UTF-8.

Находим строки


if (!$isPostBack){
    // display signup screen

Перед ними добавляем:


// added by jen - path to $language_country file
$clPath = $modx->config['base_path']."manager/includes/lang/country/";
if (isset($language)) 
// language variables for modx placeholders
	include_once $clPath.$language."_country.inc.php";
else
	include_once $clPath.$modx->config['manager_language']."_country.inc.php";

// added by jen - build multilingual dropdown list of countries
for ($i=1; $i<=count($_country_lang); $i++) {
	$countries .= "<option value=\"" . $i. "\">" . $_country_lang[$i] . "</option>\n";
}

Следом идут строки


    $tpl = $tpls[0];
    $tpl = str_replace("[+action+]",$modx->makeURL($modx->documentIdentifier),$tpl);

после которых вставляем

$tpl = str_replace("[+countries+]",$countries,$tpl);

Возникает закономерный вопрос, почему не воспользоваться более удобным циклом foreach, который сам бы отсортировал элементы языкового массива по алфавиту, ведь в случае for алфавитный порядок для языка, отличного от английского, нарушается.
Дело в том. что в конце файла есть javascript'ик, который идентифицирует по номеру выбранный элемент списка и в случае перезагрузки страницы возвращает его. Таким образом, все поля кроме пароля сохраняют уже введенные значения.

Для английского числовые индексы стран возрастают в соответствии с алфавитным порядком. В родном же индексы играют в чехарду, если страны вписаны по алфавиту.

Например, первой в русскоязычном списке стран стоит Австралия (в ангийском — Афганистан):


$_country_lang["13"] = 'Австралия'; // Australia
...
$_country_lang["1"] = 'Афганистан'; // Afghanistan

Допустим, вы выбрали Австралию. После перезагрузки, в случае цикла for, она же и останется. С циклом foreach выскочит Афганистан. А мы не заметили! И оказались совсем не там, где хотели.
Я пока что не стал тратить время на исправление ситуации (дефицит навыков порождает избыточные временные затраты), а пошел кратчайшим путем. Все равно это лучше, чем инглиш названия стран в русской форме регистрации.

Дальше. Опциональный пункт Пол. Чтобы его реализовать, чуть ниже находим строчку

$country = $modx->db->escape($modx->stripTags($_POST['country']));

и перед ней добавляем

$gender = $modx->db->escape($modx->stripTags($_POST['gender']));

Далее находим строку

$tpl = str_replace("[+country+]",$country,$tpl);

и перед ней добавляем


    $tpl = str_replace("[+gender+]",$gender,$tpl);
    $tpl = str_replace("[+countries+]",$countries,$tpl);

И не забываем, чтобы пол нашего юзера попал в базу данных. Для этого находим SQL-запрос


// save user attributes
$sql = "INSERT INTO ".$modx->getFullTableName("web_user_attributes")." (internalKey, fullname, email, zip, state, country) 
        VALUES($key, '$fullname', '$email', '$zip', '$state', '$country');";

и добавляем пол


$sql = "INSERT INTO ".$modx->getFullTableName("web_user_attributes")." (internalKey, fullname, email, gender, country, state, zip) 
        VALUES($key, '$fullname', '$email', '$gender', '$country', '$state', '$zip');";

Далее — шаблон по умолчанию формы регистрации.

Находим подходящее место для вставки строки Пол. У меня он расположился под строкой Email. Добавляем:


<tr>
  <td>Пол</td>
  <td>
    <select size="1" name="gender">
      <option selected="selected" value=""> </option>
      <option value="1">Мужской</option>
      <option value="2">Женский</option>
    </select>
  </td>
</tr>

Приуныли? Уже почти дошли.

Находим список стран:


<tr>
  <td>Country:</td>
    <td><select size="1" name="country" style="width:300px">
      <option value="" selected>&nbsp;</option>
      <option value="1">Afghanistan</option>
      ...
      <!-- МАМА ДОРОГАЯ!!! -->
      ...
      <option value="239">Zimbabwe</option>
    </select>
  </td>
</tr>

Удаляем к … маме. Можете, конечно, закомментировать, дело вкуса. :lol:

Взамен вставляем


<tr>
  <td>Страна</td>
  <td>
    <select size="1" name="country">
      <option selected="selected" value=""> </option>
      [+countries+]
    </select>
  </td>
</tr>

Далее находим строку таблицы с самой виновницей переполоха, капчей. Комментируем (удаляем):


<tr>
  <td valign="top">Form code:*</td>
  <td>
    <input type="text" name="formcode" class="inputBox" style="width:150px" size="20">
    <a href="[+action+]"><img align="top" src="manager/includes/veriword.php?rand=<?php echo rand(); ?>" width="148" height="60" alt="If you have trouble reading the code, click on the code itself to generate a new random code." style="border: 1px solid #003399"></a>
  </td>
</tr>

Заменяем на:


<tr>
  <td style="vertical-align: top;">Защита от спама</td>
  <td>
    <a href="[+action+]" onClick="this.blur();new_freecap();return false;">
    <img id="freecap" src="manager/includes/freecap/freecap.php" />
    </a>
  </td>
</tr>
<tr>
  <td>Слово на картинке: *</td>
  <td><input type="text" name="formcode" /></td>
</tr>

Ну и наконец изменяем скрипт с учетом добавления пункта Пол. Находим скрипт


<script language="javascript" type="text/javascript"> 
  var id = "[+country+]";
  var f = document.websignupfrm;
  var i = parseInt(id);
  if (!isNaN(i)) f.country.options[i].selected = true;
</script>

И заменяем на


<script type="text/javascript">
  // jen - add code for "gender" item
  var gen = "[+gender+]";
  var ctr = "[+country+]";
  var g = parseInt(gen);
  var c = parseInt(ctr);
  var f = document.websignupfrm;
  if (!isNaN(g)) f.gender.options[g].selected = true;
  if (!isNaN(c)) f.country.options[c].selected = true;
</script>

Всё! Вот как это выглядит на сайте:

Для полной реализации многоязычности используется решение от aceman3000.
В ходе написания этого материала были замечены и исправлены мелкие баги в готовом файле websignup.inc.php.
Архив для скачивания обновлен и находится на прежнем месте.

Loading comments …   ←   Модификация сниппета WebSignup

Fields marked with asterix are required.


Max 40 alpha-numeric characters, including spaces.


Valid email. Max 255 characters. Not displayed publicly.

*