Автоопределение города пользователя при оформлении заказа (на основе GeoIP)
Я тут в соседнем разделе пообещал примерчик определения города с помощью GeoIP, но подумал что лучше для него создать отдельную тему в "Полезных решениях".
Итак, задача определить город пользователя и подставить его в форму ввода адреса, так чтобы пользователю осталось только вписать улицу-дом-квартиру.
Для начала надо выбрать сервис, который поможет нам определить город. Сервис должен содержать достаточно подробную информацию о России, и обладать возможностью взаимодействовать с клиентскими приложениями через XML-запросы. В качестве такого сервиса я выбрал
Для некоторых возможно будет недостатком то, что этот сервис определяет только города России, не охватывая даже ближнего зарубежья, но я полагаю в сети для него есть немало аналогов.
Сервис выбран. Теперь нам потребуется php-класс, осуществляющий взаимодействие с ним.
Для этого копируем нижеприведенный код, и сохраняем его в папку /modules/ под именем geoip.class.php
Теперь осталось внести изменения в ТДС "Интернет-магазин корзина" и в xsl-шаблон.
В ТДС "Интернет-магазин корзина" находим следующий фрагмент:
После этого меняем код xsl-шаблона "МагазинАдресДоставки" на следующий:
И пользуемся
Потестировать технологию какое-то время можно будет оформляя заказы здесь -
У меня однозначно определяется Санкт-Петербург
P.S. Прошу не обращать внимания на некоторый бардак на сайте - это просто тестовая площадка для моих экспериментов.
Итак, задача определить город пользователя и подставить его в форму ввода адреса, так чтобы пользователю осталось только вписать улицу-дом-квартиру.
Для начала надо выбрать сервис, который поможет нам определить город. Сервис должен содержать достаточно подробную информацию о России, и обладать возможностью взаимодействовать с клиентскими приложениями через XML-запросы. В качестве такого сервиса я выбрал
Для некоторых возможно будет недостатком то, что этот сервис определяет только города России, не охватывая даже ближнего зарубежья, но я полагаю в сети для него есть немало аналогов.
Сервис выбран. Теперь нам потребуется php-класс, осуществляющий взаимодействие с ним.
Для этого копируем нижеприведенный код, и сохраняем его в папку /modules/ под именем geoip.class.php
// файл /modules/geoip.class.php
<?php
/*
This program is free software. You can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License.
Home:
Author: Egor N. Zuskin
Adapted for HostCMS by James V. Kotoff
Simple for php:
$ipList = new IPGeo("xxx.xxx.xxx.xxx,xxx.xxx.xxx.xxx");
print $ipList->ip("xxx.xxx.xxx.xxx"); // city: xxxx
or
$ipList = new IPGeo(array("xxx.xxx.xxx.xxx", "xxx.xxx.xxx.xxx"));
print $ipList->ip("xxx.xxx.xxx.xxx","region"); // region: xxxx
or
$ipList = new IPGeo("xxx.xxx.xxx.xxx");
print $ipList->ip("xxx.xxx.xxx.xxx", "district"); // district: xxxx
*/
DEFINE("IPGEO_SERVER", "194.85.91.253"); // сервер ip geo
DEFINE("IPGEO_SERVER_PORT", 8090); // порт
DEFINE("IPGEO_DEFAULT_PARAM", "city"); // поле возвращаемое поумолчанию
DEFINE("IPGEO_DEBUG", false); // признак отладки (не обращается к серверу)
/**
* @author ice
* Класс для получения ip адресов с сервиса ipgeobase.ru
*/
class IPGeo
{
var $xml = ""; // текст возвращаемого xml
var $ip_arr = array(); // массив ip адресов
var $fields_arr = array("all"); // список запрашиваемых полей
var $cache = array(); // кешь ответа
/**
* Создание класса и запрос к серверу
* @param $AIpList список ip адресов, строкой либо строкой через запятую либо массивом
* @return bool
*/
function IPGeo($AIpList)
{
if (IPGEO_DEBUG) {
return true;
}
if (is_array($AIpList)) {
$ip_arr = $AIpList;
} else {
if (strpos($AIpList, ",") === false) {
$ip_arr = array(trim($AIpList));
} else {
$ip_arr = explode(",", trim($AIpList));
}
}
$ip_arr = array_unique($ip_arr);
$ip_arr = $this->check_ip_list_valid($ip_arr);
if (count($ip_arr) == 0)
return false;
$ips = "<ip>" . implode("</ip><ip>", $ip_arr) . "</ip>";
$fields = "<" . implode("/><", $this->fields_arr) . "/>";
$post_string = "<ipquery><fields>" . $fields . "</fields><ip-list>" . $ips .
"</ip-list></ipquery>";
if (!$socket = fsockopen(IPGEO_SERVER, IPGEO_SERVER_PORT))
return false;
$query = "POST /geo/geo.html HTTP/1.1\r\n";
$query .= "Content-Length: " . strlen($post_string) . "\r\n";
$query .= "\r\n";
$query .= $post_string;
$query .= "\r\n\r\n";
$response = "";
fwrite($socket, $query);
while (!feof($socket)) {
$response .= fgets($socket, 2048);
}
fclose($socket);
$this->xml = trim(substr($response, strpos($response, "\r\n\r\n")));
return true;
}
/**
* Возвращает запрошенное поле для ip адреса
* @param $AIp IP адрес
* @param $AFieldName Поле
* @return string
*/
function ip($AIp, $AFieldName = IPGEO_DEFAULT_PARAM)
{
if (IPGEO_DEBUG) {
return false;
}
if (isset($this->cache[$AIp][$AFieldName])) {
return $this->cache[$AIp][$AFieldName];
} else {
if ($this->xml) {
$doc = new DOMDocument;
$doc->loadXML($this->xml);
$xmlpath = new domxpath($doc);
$ip_ansver = $doc->getElementsByTagName("ip-answer")->item(0);
$items = $xmlpath->query("ip", $ip_ansver);
foreach ($items as $it) {
$ip = $it->getAttribute('value');
if ($ip == $AIp) {
$message = @$xmlpath->query("message", $it)->item(0)->nodeValue;
$field_value = ($message <> "") ? false : iconv("UTF-8", "CP1251", $xmlpath->
query($AFieldName, $it)->item(0)->nodeValue);
$this->cache[$AIp][$AFieldName] = $field_value;
return $field_value;
}
}
}
}
return false;
}
/**
* Возвращает список правильных ip адресов проверенных по маске xxx.xxx.xxx.xxx < 256
* @param $AIpList масив ip адресов
* @return array
*/
function check_ip_list_valid($AIpList)
{
$return = array();
foreach ($AIpList as $ip) {
if (ereg("([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3}).([0-9]{1,3})", $ip, $par)) {
if ($par[1] < 256 && $par[2] < 256 && $par[3] < 256 && $par[4] < 256) {
$return[] = $ip;
}
}
}
return $return;
}
}
?>
<?php
/*
This program is free software. You can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License.
Home:
Author: Egor N. Zuskin
Adapted for HostCMS by James V. Kotoff
Simple for php:
$ipList = new IPGeo("xxx.xxx.xxx.xxx,xxx.xxx.xxx.xxx");
print $ipList->ip("xxx.xxx.xxx.xxx"); // city: xxxx
or
$ipList = new IPGeo(array("xxx.xxx.xxx.xxx", "xxx.xxx.xxx.xxx"));
print $ipList->ip("xxx.xxx.xxx.xxx","region"); // region: xxxx
or
$ipList = new IPGeo("xxx.xxx.xxx.xxx");
print $ipList->ip("xxx.xxx.xxx.xxx", "district"); // district: xxxx
*/
DEFINE("IPGEO_SERVER", "194.85.91.253"); // сервер ip geo
DEFINE("IPGEO_SERVER_PORT", 8090); // порт
DEFINE("IPGEO_DEFAULT_PARAM", "city"); // поле возвращаемое поумолчанию
DEFINE("IPGEO_DEBUG", false); // признак отладки (не обращается к серверу)
/**
* @author ice
* Класс для получения ip адресов с сервиса ipgeobase.ru
*/
class IPGeo
{
var $xml = ""; // текст возвращаемого xml
var $ip_arr = array(); // массив ip адресов
var $fields_arr = array("all"); // список запрашиваемых полей
var $cache = array(); // кешь ответа
/**
* Создание класса и запрос к серверу
* @param $AIpList список ip адресов, строкой либо строкой через запятую либо массивом
* @return bool
*/
function IPGeo($AIpList)
{
if (IPGEO_DEBUG) {
return true;
}
if (is_array($AIpList)) {
$ip_arr = $AIpList;
} else {
if (strpos($AIpList, ",") === false) {
$ip_arr = array(trim($AIpList));
} else {
$ip_arr = explode(",", trim($AIpList));
}
}
$ip_arr = array_unique($ip_arr);
$ip_arr = $this->check_ip_list_valid($ip_arr);
if (count($ip_arr) == 0)
return false;
$ips = "<ip>" . implode("</ip><ip>", $ip_arr) . "</ip>";
$fields = "<" . implode("/><", $this->fields_arr) . "/>";
$post_string = "<ipquery><fields>" . $fields . "</fields><ip-list>" . $ips .
"</ip-list></ipquery>";
if (!$socket = fsockopen(IPGEO_SERVER, IPGEO_SERVER_PORT))
return false;
$query = "POST /geo/geo.html HTTP/1.1\r\n";
$query .= "Content-Length: " . strlen($post_string) . "\r\n";
$query .= "\r\n";
$query .= $post_string;
$query .= "\r\n\r\n";
$response = "";
fwrite($socket, $query);
while (!feof($socket)) {
$response .= fgets($socket, 2048);
}
fclose($socket);
$this->xml = trim(substr($response, strpos($response, "\r\n\r\n")));
return true;
}
/**
* Возвращает запрошенное поле для ip адреса
* @param $AIp IP адрес
* @param $AFieldName Поле
* @return string
*/
function ip($AIp, $AFieldName = IPGEO_DEFAULT_PARAM)
{
if (IPGEO_DEBUG) {
return false;
}
if (isset($this->cache[$AIp][$AFieldName])) {
return $this->cache[$AIp][$AFieldName];
} else {
if ($this->xml) {
$doc = new DOMDocument;
$doc->loadXML($this->xml);
$xmlpath = new domxpath($doc);
$ip_ansver = $doc->getElementsByTagName("ip-answer")->item(0);
$items = $xmlpath->query("ip", $ip_ansver);
foreach ($items as $it) {
$ip = $it->getAttribute('value');
if ($ip == $AIp) {
$message = @$xmlpath->query("message", $it)->item(0)->nodeValue;
$field_value = ($message <> "") ? false : iconv("UTF-8", "CP1251", $xmlpath->
query($AFieldName, $it)->item(0)->nodeValue);
$this->cache[$AIp][$AFieldName] = $field_value;
return $field_value;
}
}
}
}
return false;
}
/**
* Возвращает список правильных ip адресов проверенных по маске xxx.xxx.xxx.xxx < 256
* @param $AIpList масив ip адресов
* @return array
*/
function check_ip_list_valid($AIpList)
{
$return = array();
foreach ($AIpList as $ip) {
if (ereg("([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3}).([0-9]{1,3})", $ip, $par)) {
if ($par[1] < 256 && $par[2] < 256 && $par[3] < 256 && $par[4] < 256) {
$return[] = $ip;
}
}
}
return $return;
}
}
?>
Теперь осталось внести изменения в ТДС "Интернет-магазин корзина" и в xsl-шаблон.
В ТДС "Интернет-магазин корзина" находим следующий фрагмент:
...
}
/* Запоминаем купон */
$_SESSION['shop_coupon_text'] = to_str($_POST['shop_coupon_text']);
/* Отображаем форму ввода адреса */
// Не выбираем show_location, show_city и show_city_area, т.к. подгружаются через AJAX
$shop->ShowAddress(to_str($GLOBALS['LA']['xsl_delivery_address']), $shop_id, array('show_location' => false, 'show_city' => false, 'show_city_area' => false), $external_propertys);
}
...
и меняем его на следующий фрагмент:}
/* Запоминаем купон */
$_SESSION['shop_coupon_text'] = to_str($_POST['shop_coupon_text']);
/* Отображаем форму ввода адреса */
// Не выбираем show_location, show_city и show_city_area, т.к. подгружаются через AJAX
$shop->ShowAddress(to_str($GLOBALS['LA']['xsl_delivery_address']), $shop_id, array('show_location' => false, 'show_city' => false, 'show_city_area' => false), $external_propertys);
}
...
...
}
/* Запоминаем купон */
$_SESSION['shop_coupon_text'] = to_str($_POST['shop_coupon_text']);
// Определяем город пользователя
@include_once (CMS_FOLDER . '/modules/geoip.class.php');
if (defined('IPGEO_SERVER')) {
$user_ip = ($_SERVER["REMOTE_ADDR"]) ? $_SERVER["REMOTE_ADDR"] : getenv("HTTP_X_FORWARDED_FOR");
$IPGeo = new IPGeo($user_ip);
$city = trim(to_str($IPGeo->ip($user_ip)));
if ($city) {
$cities = $shop->GetAllCity();
while ($city_row = mysql_fetch_assoc($cities)) {
if (strtolower($city_row['shop_city_name']) == strtolower($city)) {
$city_id = $city_row['shop_city_id'];
$location_id = $city_row['shop_location_id'];
break;
}
}
if (isset($city_id)) {
$location_row = $shop->GetLocation($location_id);
$country_id = $location_row['shop_country_id'];
}
}
}
/* Отображаем форму ввода адреса */
if (isset($city_id)) {
$external_propertys['city_id'] = $city_id;
$external_propertys['location_id'] = $location_id;
$external_propertys['country_id'] = $country_id;
$shop->ShowAddress(to_str($GLOBALS['LA']['xsl_delivery_address']), $shop_id,
array('show_location' => true, 'show_city' => true, 'show_city_area' => true), $external_propertys);
} else {
// Не выбираем show_location, show_city и show_city_area, т.к. подгружаются через AJAX
$shop->ShowAddress(to_str($GLOBALS['LA']['xsl_delivery_address']), $shop_id,
array('show_location' => false, 'show_city' => false, 'show_city_area' => false),
$external_propertys);
}
}
...
}
/* Запоминаем купон */
$_SESSION['shop_coupon_text'] = to_str($_POST['shop_coupon_text']);
// Определяем город пользователя
@include_once (CMS_FOLDER . '/modules/geoip.class.php');
if (defined('IPGEO_SERVER')) {
$user_ip = ($_SERVER["REMOTE_ADDR"]) ? $_SERVER["REMOTE_ADDR"] : getenv("HTTP_X_FORWARDED_FOR");
$IPGeo = new IPGeo($user_ip);
$city = trim(to_str($IPGeo->ip($user_ip)));
if ($city) {
$cities = $shop->GetAllCity();
while ($city_row = mysql_fetch_assoc($cities)) {
if (strtolower($city_row['shop_city_name']) == strtolower($city)) {
$city_id = $city_row['shop_city_id'];
$location_id = $city_row['shop_location_id'];
break;
}
}
if (isset($city_id)) {
$location_row = $shop->GetLocation($location_id);
$country_id = $location_row['shop_country_id'];
}
}
}
/* Отображаем форму ввода адреса */
if (isset($city_id)) {
$external_propertys['city_id'] = $city_id;
$external_propertys['location_id'] = $location_id;
$external_propertys['country_id'] = $country_id;
$shop->ShowAddress(to_str($GLOBALS['LA']['xsl_delivery_address']), $shop_id,
array('show_location' => true, 'show_city' => true, 'show_city_area' => true), $external_propertys);
} else {
// Не выбираем show_location, show_city и show_city_area, т.к. подгружаются через AJAX
$shop->ShowAddress(to_str($GLOBALS['LA']['xsl_delivery_address']), $shop_id,
array('show_location' => false, 'show_city' => false, 'show_city_area' => false),
$external_propertys);
}
}
...
После этого меняем код xsl-шаблона "МагазинАдресДоставки" на следующий:
<?xml version="1.0" encoding="windows-1251"?>
<!DOCTYPE xsl:stylesheet>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output xmlns="http://www.w3.org/TR/xhtml1/strict" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" encoding="Windows-1251" indent="yes" method="html" omit-xml-declaration="no" version="1.0" media-type="text/xml"/>
<xsl:template match="/locations">
<!-- Строка шага заказа -->
<ul class="shop_navigation gray">
<li class="shop_navigation_current">
<span>Адрес доставки</span>→</li>
<li>
<span>Способ доставки</span>→</li>
<li>
<span>Форма оплаты</span>→</li>
<li>
<span>Данные доставки</span>
</li>
</ul>
<SCRIPT type="text/javascript" language="JavaScript">
<xsl:comment>
<xsl:text disable-output-escaping="yes">
<![CDATA[
location_select_id = "location";
city_select_id = "sel_city";
cityarea_select_id = "sel_city_area";
]]>
</xsl:text>
</xsl:comment>
</SCRIPT>
<xsl:variable name="country_id">
<xsl:choose>
<xsl:when test="/locations/external_propertys/country_id/node()">
<xsl:value-of select="/locations/external_propertys/country_id"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="/locations/country[@select = 1]/@id"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<form name="address" id="address" method="POST">
<h1>Адрес доставки</h1>
<p>
<a href="{shop/path}cart/">Корзина</a>
</p>
<table>
<tr>
<td>Страна:</td>
<td>
<select id="country" style="width: 390px;" name="country" onchange="doSetLocation(this.options[this.selectedIndex].value, '{/locations/shop/path}cart/')">
<option value="0">..</option>
<xsl:apply-templates select="country"/>
</select>
<span class="red_star" style="position: relative; top: 4px;">*</span>
</td>
</tr>
<tr>
<td>Область:</td>
<td>
<select name="location" style="width: 390px;" id="location" onchange="doSetCity(this.options[this.selectedIndex].value, '{/locations/shop/path}cart/')">
<option value="0">..</option>
<xsl:apply-templates select="location[@parent = $country_id]"/>
</select>
<span class="red_star" style="position: relative; top: 4px;">*</span>
</td>
</tr>
<tr>
<td>Город:</td>
<td>
<select name="sel_city" style="width: 390px;" id="sel_city" onchange="doSetCityArea(this.options[this.selectedIndex].value, '{/locations/shop/path}cart/')">
<option value="0">..</option>
<xsl:variable name="location_id">
<xsl:choose>
<xsl:when test="/locations/external_propertys/location_id/node()">
<xsl:value-of select="/locations/external_propertys/location_id"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="location[@parent = $country_id]/@id"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:apply-templates select="city[@parent = $location_id]"/>
</select>
</td>
</tr>
<tr>
<td>Район города:</td>
<td>
<select name="sel_city_area" style="width: 390px;" id="sel_city_area">
<option value="0">..</option>
</select>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">Индекс:</td>
<td>
<input type="text" size="5" class="large_input" style="width: 90px;" name="index" value="{external_propertys/site_users_postcode}"/>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">Улица, дом, квартира:<br/>
(город, район, если не выбраны)</td>
<td>
<input type="text" size="30" class="large_input" style="width: 390px;" name="full_address" value="{external_propertys/site_users_address}"/>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">Фамилия, Имя, Отчество:</td>
<td>
<input type="text" size="30" class="large_input" style="width: 124px; margin-right: 5px;" name="site_users_surname" value="{external_propertys/site_users_surname}"/>
<input type="text" size="30" class="large_input" style="width: 124px; margin-right: 5px;" name="site_users_name" value="{external_propertys/site_users_name}"/>
<input type="text" size="30" class="large_input" style="width: 124px; margin-right: 5px;" name="site_users_patronymic" value="{external_propertys/site_users_patronymic}"/>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">Компания:</td>
<td>
<input type="text" size="30" class="large_input" style="width: 390px;" name="site_users_company" value="{external_propertys/site_users_company}"/>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">Телефон:</td>
<td>
<input type="text" size="30" class="large_input" style="width: 390px;" name="site_users_phone" value="{external_propertys/site_users_phone}"/>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">Факс:</td>
<td>
<input type="text" size="30" class="large_input" style="width: 390px;" name="site_users_fax" value="{external_propertys/site_users_fax}"/>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">E-mail:</td>
<td>
<input type="text" size="30" class="large_input" style="width: 390px;" name="site_users_email" value="{external_propertys/site_users_email}"/>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">Комментарий к заказу:</td>
<td>
<textarea rows="2" class="large_input" style="width: 390px;" name="description"></textarea>
</td>
</tr>
<tr>
<td>
<div class="gray_button">
<div>
<input name="step_2" value="Далее →" type="submit"></input>
</div>
</div>
</td>
</tr>
</table>
</form>
<!-- Автоматически заполняем все дочерние элементы страны, если город пользователя не определился по GeoIP-->
<xsl:if test="not(/locations/external_propertys/country_id/node())" >
<SCRIPT type="text/javascript" language="JavaScript">var oldHandler=window['onload'];
window['onload']=function(){if(typeof(oldHandler)=='function'){oldHandler();}newHandler();};
function newHandler(){
doSetLocation(document.getElementById('country').options[document.getElementById('country').selectedIndex].value, '<xsl:value-of select="/locations/shop/path"/>cart/');
}</SCRIPT>
</xsl:if>
</xsl:template>
<!-- Шаблон заполняет options для стран -->
<xsl:template match="country">
<xsl:choose>
<!-- Если страна задана по умолчанию -->
<xsl:when test="(not(/locations/external_propertys/country_id/node()) and @select=1) or (/locations/external_propertys/country_id/node() and @id = /locations/external_propertys/country_id)">
<option value="{@id}" selected="selected" style="font-weight: bold;">
<xsl:value-of disable-output-escaping="yes" select="name"/>
</option>
</xsl:when>
<xsl:otherwise>
<option value="{@id}">
<xsl:value-of disable-output-escaping="yes" select="name"/>
</option>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Шаблон заполняет options для местоположений (областей) -->
<xsl:template match="location">
<xsl:choose>
<!-- Если страна задана по умолчанию -->
<xsl:when test="/locations/external_propertys/location_id/node() and @id = /locations/external_propertys/location_id">
<option value="{@id}" selected="selected" style="font-weight: bold;">
<xsl:value-of disable-output-escaping="yes" select="name"/>
</option>
</xsl:when>
<xsl:otherwise>
<option value="{@id}">
<xsl:value-of disable-output-escaping="yes" select="name"/>
</option>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Шаблон заполняет options для городов -->
<xsl:template match="city">
<xsl:choose>
<!-- Если страна задана по умолчанию -->
<xsl:when test="/locations/external_propertys/city_id/node() and @id = /locations/external_propertys/city_id">
<option value="{@id}" selected="selected" style="font-weight: bold;">
<xsl:value-of disable-output-escaping="yes" select="name"/>
</option>
</xsl:when>
<xsl:otherwise>
<option value="{@id}">
<xsl:value-of disable-output-escaping="yes" select="name"/>
</option>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Шаблон заполняет options для районов -->
<xsl:template match="cityarea">
<option value="{@id}">
<xsl:value-of disable-output-escaping="yes" select="name"/>
</option>
</xsl:template>
</xsl:stylesheet>
<!DOCTYPE xsl:stylesheet>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output xmlns="http://www.w3.org/TR/xhtml1/strict" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" encoding="Windows-1251" indent="yes" method="html" omit-xml-declaration="no" version="1.0" media-type="text/xml"/>
<xsl:template match="/locations">
<!-- Строка шага заказа -->
<ul class="shop_navigation gray">
<li class="shop_navigation_current">
<span>Адрес доставки</span>→</li>
<li>
<span>Способ доставки</span>→</li>
<li>
<span>Форма оплаты</span>→</li>
<li>
<span>Данные доставки</span>
</li>
</ul>
<SCRIPT type="text/javascript" language="JavaScript">
<xsl:comment>
<xsl:text disable-output-escaping="yes">
<![CDATA[
location_select_id = "location";
city_select_id = "sel_city";
cityarea_select_id = "sel_city_area";
]]>
</xsl:text>
</xsl:comment>
</SCRIPT>
<xsl:variable name="country_id">
<xsl:choose>
<xsl:when test="/locations/external_propertys/country_id/node()">
<xsl:value-of select="/locations/external_propertys/country_id"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="/locations/country[@select = 1]/@id"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<form name="address" id="address" method="POST">
<h1>Адрес доставки</h1>
<p>
<a href="{shop/path}cart/">Корзина</a>
</p>
<table>
<tr>
<td>Страна:</td>
<td>
<select id="country" style="width: 390px;" name="country" onchange="doSetLocation(this.options[this.selectedIndex].value, '{/locations/shop/path}cart/')">
<option value="0">..</option>
<xsl:apply-templates select="country"/>
</select>
<span class="red_star" style="position: relative; top: 4px;">*</span>
</td>
</tr>
<tr>
<td>Область:</td>
<td>
<select name="location" style="width: 390px;" id="location" onchange="doSetCity(this.options[this.selectedIndex].value, '{/locations/shop/path}cart/')">
<option value="0">..</option>
<xsl:apply-templates select="location[@parent = $country_id]"/>
</select>
<span class="red_star" style="position: relative; top: 4px;">*</span>
</td>
</tr>
<tr>
<td>Город:</td>
<td>
<select name="sel_city" style="width: 390px;" id="sel_city" onchange="doSetCityArea(this.options[this.selectedIndex].value, '{/locations/shop/path}cart/')">
<option value="0">..</option>
<xsl:variable name="location_id">
<xsl:choose>
<xsl:when test="/locations/external_propertys/location_id/node()">
<xsl:value-of select="/locations/external_propertys/location_id"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="location[@parent = $country_id]/@id"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:apply-templates select="city[@parent = $location_id]"/>
</select>
</td>
</tr>
<tr>
<td>Район города:</td>
<td>
<select name="sel_city_area" style="width: 390px;" id="sel_city_area">
<option value="0">..</option>
</select>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">Индекс:</td>
<td>
<input type="text" size="5" class="large_input" style="width: 90px;" name="index" value="{external_propertys/site_users_postcode}"/>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">Улица, дом, квартира:<br/>
(город, район, если не выбраны)</td>
<td>
<input type="text" size="30" class="large_input" style="width: 390px;" name="full_address" value="{external_propertys/site_users_address}"/>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">Фамилия, Имя, Отчество:</td>
<td>
<input type="text" size="30" class="large_input" style="width: 124px; margin-right: 5px;" name="site_users_surname" value="{external_propertys/site_users_surname}"/>
<input type="text" size="30" class="large_input" style="width: 124px; margin-right: 5px;" name="site_users_name" value="{external_propertys/site_users_name}"/>
<input type="text" size="30" class="large_input" style="width: 124px; margin-right: 5px;" name="site_users_patronymic" value="{external_propertys/site_users_patronymic}"/>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">Компания:</td>
<td>
<input type="text" size="30" class="large_input" style="width: 390px;" name="site_users_company" value="{external_propertys/site_users_company}"/>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">Телефон:</td>
<td>
<input type="text" size="30" class="large_input" style="width: 390px;" name="site_users_phone" value="{external_propertys/site_users_phone}"/>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">Факс:</td>
<td>
<input type="text" size="30" class="large_input" style="width: 390px;" name="site_users_fax" value="{external_propertys/site_users_fax}"/>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">E-mail:</td>
<td>
<input type="text" size="30" class="large_input" style="width: 390px;" name="site_users_email" value="{external_propertys/site_users_email}"/>
</td>
</tr>
<tr>
<td style="vertical-align: middle;">Комментарий к заказу:</td>
<td>
<textarea rows="2" class="large_input" style="width: 390px;" name="description"></textarea>
</td>
</tr>
<tr>
<td>
<div class="gray_button">
<div>
<input name="step_2" value="Далее →" type="submit"></input>
</div>
</div>
</td>
</tr>
</table>
</form>
<!-- Автоматически заполняем все дочерние элементы страны, если город пользователя не определился по GeoIP-->
<xsl:if test="not(/locations/external_propertys/country_id/node())" >
<SCRIPT type="text/javascript" language="JavaScript">var oldHandler=window['onload'];
window['onload']=function(){if(typeof(oldHandler)=='function'){oldHandler();}newHandler();};
function newHandler(){
doSetLocation(document.getElementById('country').options[document.getElementById('country').selectedIndex].value, '<xsl:value-of select="/locations/shop/path"/>cart/');
}</SCRIPT>
</xsl:if>
</xsl:template>
<!-- Шаблон заполняет options для стран -->
<xsl:template match="country">
<xsl:choose>
<!-- Если страна задана по умолчанию -->
<xsl:when test="(not(/locations/external_propertys/country_id/node()) and @select=1) or (/locations/external_propertys/country_id/node() and @id = /locations/external_propertys/country_id)">
<option value="{@id}" selected="selected" style="font-weight: bold;">
<xsl:value-of disable-output-escaping="yes" select="name"/>
</option>
</xsl:when>
<xsl:otherwise>
<option value="{@id}">
<xsl:value-of disable-output-escaping="yes" select="name"/>
</option>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Шаблон заполняет options для местоположений (областей) -->
<xsl:template match="location">
<xsl:choose>
<!-- Если страна задана по умолчанию -->
<xsl:when test="/locations/external_propertys/location_id/node() and @id = /locations/external_propertys/location_id">
<option value="{@id}" selected="selected" style="font-weight: bold;">
<xsl:value-of disable-output-escaping="yes" select="name"/>
</option>
</xsl:when>
<xsl:otherwise>
<option value="{@id}">
<xsl:value-of disable-output-escaping="yes" select="name"/>
</option>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Шаблон заполняет options для городов -->
<xsl:template match="city">
<xsl:choose>
<!-- Если страна задана по умолчанию -->
<xsl:when test="/locations/external_propertys/city_id/node() and @id = /locations/external_propertys/city_id">
<option value="{@id}" selected="selected" style="font-weight: bold;">
<xsl:value-of disable-output-escaping="yes" select="name"/>
</option>
</xsl:when>
<xsl:otherwise>
<option value="{@id}">
<xsl:value-of disable-output-escaping="yes" select="name"/>
</option>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Шаблон заполняет options для районов -->
<xsl:template match="cityarea">
<option value="{@id}">
<xsl:value-of disable-output-escaping="yes" select="name"/>
</option>
</xsl:template>
</xsl:stylesheet>
И пользуемся

Потестировать технологию какое-то время можно будет оформляя заказы здесь -
У меня однозначно определяется Санкт-Петербург

P.S. Прошу не обращать внимания на некоторый бардак на сайте - это просто тестовая площадка для моих экспериментов.
Заказов не беру. Консультирую редко.
Добавлю еще несколько слов в качестве просьбы к разработчикам системы.
Вот мне лично в приведенном выше коде не нравится кусок, который преобразует название города, возвращаемое сервисом, во внутренний id города в контексте HostCMS:
Проблему можно было-бы обойти, сделав в этом месте прямой запрос к БД, но я считаю что не стоит пользоваться этим способом, без совершенно крайней на то необходимости.
Поэтому просьба - нельзя ли в классе shop добавить набор методов выбора географических объектов по их названиям, а не только по id как есть сейчас? То есть что-нибудь типа GetCityByName(), GetLocationByName() ну и т.д.
Вот мне лично в приведенном выше коде не нравится кусок, который преобразует название города, возвращаемое сервисом, во внутренний id города в контексте HostCMS:
$cities = $shop->GetAllCity();
while ($city_row = mysql_fetch_assoc($cities)) {
if (strtolower($city_row['shop_city_name']) == strtolower($city)) {
и так далее
- как-то не рационально перебирать все города пока не найдется нужный, да и на хайлоад-проектах может сказаться негативно.while ($city_row = mysql_fetch_assoc($cities)) {
if (strtolower($city_row['shop_city_name']) == strtolower($city)) {
и так далее
Проблему можно было-бы обойти, сделав в этом месте прямой запрос к БД, но я считаю что не стоит пользоваться этим способом, без совершенно крайней на то необходимости.
Поэтому просьба - нельзя ли в классе shop добавить набор методов выбора географических объектов по их названиям, а не только по id как есть сейчас? То есть что-нибудь типа GetCityByName(), GetLocationByName() ну и т.д.
Заказов не беру. Консультирую редко.
Kotoff,
решил попробовать поставить, но на у меня Питер не определяется
У меня все тоже многоточие)
Но если в целом эта штука заработает - то вам огромное спасибо за код!
решил попробовать поставить, но на у меня Питер не определяется
У меня все тоже многоточие)Но если в целом эта штука заработает - то вам огромное спасибо за код!
ironwayru, а вы сами откуда, и какой у вас ip? Описанное выше может не работать в двух случаях - когда не определился город, и если написание названия города в сервисе не совпадает с таковым в hostcms (например, опечатка).
Первая причина определяется очень просто - достаточно зайти сюда и посмотреть, определится ли город по вашему ip в самом сервисе. Если нет - то тут уже от меня ничего не зависит, нужно писать в сам сервис, что я вот в таком городе, у меня вот такой ip, а ваш сервис меня не находит.
А если сервис ваш город определяет, а магазин - нет, тогда пишите что за город у вас такой, и ip ваш тоже напишите, буду смотреть в чем дело
Первая причина определяется очень просто - достаточно зайти сюда и посмотреть, определится ли город по вашему ip в самом сервисе. Если нет - то тут уже от меня ничего не зависит, нужно писать в сам сервис, что я вот в таком городе, у меня вот такой ip, а ваш сервис меня не находит.
А если сервис ваш город определяет, а магазин - нет, тогда пишите что за город у вас такой, и ip ваш тоже напишите, буду смотреть в чем дело

Заказов не беру. Консультирую редко.
Ну и парочку картинок, в качестве пруфа:
Определение города в сервисе -
И определение города в корзине -
Определение города в сервисе -
И определение города в корзине -
Заказов не беру. Консультирую редко.
Потестил сервис:
Питер, оператор Корбина
сервис ipgeobase.ru
ваш: ip *****
Ваш город: Москва
Ваш регион: Москва
Ваш округ: Центральный
Питер, оператор Корбина
сервис ipgeobase.ru
ваш: ip *****
Ваш город: Москва
Ваш регион: Москва
Ваш округ: Центральный
ку
Kotoff писал(а):
Ну я там выше написал, что Питер у меня не определяется
Я из Питера
Но проблема у меня, точнее у конторы - айпи определяется как чешский
))
Но я себе такую тему все равно поставлю, т.к. база у них обновляется регулярно, и я думаю, что, как правило, ай-пи будет определяться правильно
ironwayru, а вы сами откуда, и какой у вас ip?
Ну я там выше написал, что Питер у меня не определяется
Я из Питера
Но проблема у меня, точнее у конторы - айпи определяется как чешский
))Но я себе такую тему все равно поставлю, т.к. база у них обновляется регулярно, и я думаю, что, как правило, ай-пи будет определяться правильно

ironwayru, а кто у вас провайдер? На слева под определившимся городом есть ссылочка - "Неправильно? Сообщите нам!" - так и сообщите им!
compaq, ага, бывает такая проблема с Корбиной, у меня у самого выделенный домашний ip от Корбины на одних сайтах определяется как Питер, на других как Москва. Хотя что интересно, ipgeobase у меня дома Питер определяет правильно. У вас видимо ip из другого пула, или может быть подключаетесь как-то иначе, через другой VPN.
Но в любом случае есть есть смысл сообщить об этом в сервис и вам
compaq, ага, бывает такая проблема с Корбиной, у меня у самого выделенный домашний ip от Корбины на одних сайтах определяется как Питер, на других как Москва. Хотя что интересно, ipgeobase у меня дома Питер определяет правильно. У вас видимо ip из другого пула, или может быть подключаетесь как-то иначе, через другой VPN.
Но в любом случае есть есть смысл сообщить об этом в сервис и вам

Заказов не беру. Консультирую редко.
Kotoff писал(а):
Сообщил
Провайдер Инфо-Лан. Кстати, провайдер на разных сервисах определяется правильно)
Проблема сейчас в том, что после замены шаблона "АдресДоставки" у меня вобще пустая страница отображается) Не пойму пока, в чем дело... Причем даже правый блок и подвал не загружаются... И дело даже не в шаблоне - может в ТДС что не так...
так и сообщите им!
Сообщил

Провайдер Инфо-Лан. Кстати, провайдер на разных сервисах определяется правильно)
Проблема сейчас в том, что после замены шаблона "АдресДоставки" у меня вобще пустая страница отображается) Не пойму пока, в чем дело... Причем даже правый блок и подвал не загружаются... И дело даже не в шаблоне - может в ТДС что не так...
В ТДС правки внесли невнимательно, либо стерли нужную фигурную скобку, либо наборот, поставили.
Заказов не беру. Консультирую редко.
Авторизация