Всем доброго дня. Не попаду к сожалению сегодня на drupal day в Москве, но обязательно планирую попасть на camp в феврале И, к такому приятному дню решил сделать и свой вклад. Ниже - изменения в темизации drupal 7 по сравнению с drupal 6. Материал взят с drupal.org, и фактически это мой вольный перевод с небольшими комментариями.
Изначально я его опубликовал в своем блоге, но там черновики раскиданные на 3 части, для удобства на drupal.ru я все это склеил в один большой пост. Если будут правки, замечания - милости просим.
Primary / secondary links теперь называются Main и Secondary меню
В принципе, мелочи, не стоящие особого внимания. Запомнить просто
Отображение терминов таксономии
Ранее надо было обращаться к переменной $taxonomy, для рендера ссылок таксономии текущей ноды. Теперь это не нужно. Фактически, массив таксономии "переехал" в node, и доступен через $node->content['links']['terms']['#value'] .
Отрендеренные ссылки не изменились, разве что если в drupal 6 мы выводили их так:
<?php if ($taxonomy): ?>
<div class="terms">
<?php print $terms ?>
</div>
<?php endif;?>
то в drupal 7 выводим так:
<?php if ($terms): ?>
<div class="terms">
<?php print $terms ?>
</div>
<?php endif;?>
Изменение Doctype
В связи с поддержкой RDF(а об этом кстати отдельно - писать и писать), изменился стандартный drupal 6 doctype
<?php
<!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="<?php print $language->language ?>" lang="<?php print $language->language ?>"
dir="<?php print $language->dir ?>">
<head>
?>
на следующий:
<?php
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $language->language ?>" dir="<?php print $language->dir ?>"
<?php print $rdf_namespaces ?>>
<head profile="<?php print $grddl_profile ?>">
?>
Класс clear-block переименован в clearfix
Собственно, тут и писать нечего.
box.tpl.php удален
А никто и не пользовался почти Теперь данный "аморфный" блок не нужен, т.к. выводимый ранее через него контент имеет собственные функции, такие как theme_search_results_listing() и theme_comment_form_box()
$help стал регионом
Как помните, в drupal 6 были следующие стандартные регионы: left, right, content, header, footer. К ним добавился еще и help. Т.е. если ранее вывод подсказок был сверху контента, то теперь мы получили значительно большую свободу в его отображении. Ура!
выглядит в .info это так: regions[help] = Справка
И да, кстати - я не совсем понял является ли теперь определение стандартных регионов обязательным(но кажется они все-таки подхватываются автоматически, если у вас нет ни одного определенного региона в .info, в общем как и в drupal6). Разработчики ужесточили правила описания в .info файле. Лучше указывать Аналогичные рекомендации и относительно "стандартных", определяющихся ранее автоматически файлов style.css, script.js.
$mission стал регионом "highlight"
Тоже, в принципе, комментировать особо нечего, аналогично с $help.
$footer_message удален
Пользуйтесь регионом footer.
$content стал регионом content
Замечу что пока не не совсем ясно - в drupal 6 вывод контента и регион были "совмещены". Либо произошло разделение, либо $content стал полноценным регионом и автоматически выводит в себе основное содержание(эдакое "сращивание наоборот"). Поработаем, поймем
Новые css id для блоков
Первой строчкой идут привычные нам css id для drupal 6, второй - как оно будет выглядеть в drupal 7. В принципе - это только косметические изменения, для повышения удобства работы с кодом. Но тем кто темизует, так или иначе придется привыкать и запоминать изменения.
Последние записи в блогах
было: block-blog-0
стало: block-blog-recent
Навигация в подшивках
block-book-0
block-book-navigation
Комментарии
block-comment-0
block-comment-recent
Активные топики форума
block-forum-0
block-forum-active
Новые топики форума
block-forum-1
block-forum-new
Переключение языка
block-locale-0
block-locale-language-switcher
Синдикация
block-node-0
block-node-syndicate
Последние голосования
block-poll-0
block-poll-recent
Информация об авторе
block-profile-0
block-profile-author-information
Поиск (это уже вкуснее для повседневной темизации
block-search-0
block-search-form
Популярные записи
block-statistics-0
block-statistics-popular
Powered by Drupal (не нуждается в переводе
block-system-0
block-system-powered-by
Блок авторизации (это тоже оч полезно и удобочитаемо. наконец-то!
block-user-0
block-user-login
Навигация
block-user-1
block-system-navigation (ура!)
Новые пользователи на сайте
block-user-2
block-user-new
Кто онлайн
block-user-3
block-user-online
HTML классы генерируются через переменную
Любая тема теперь может вывести $classes из шаблона темы при рендере. Т.е. то что ранее делалось в том же zen'е через function zen_preprocess_page(&$vars, $hook), теперь достигается простым использованием в page.tpl.php такой конструкции:
<?php
<div class="<?php print $classes ?>">
...
</div>
?>
HTML атрибуты генерируются через переменную
В любой теме вы теперь сможете использовать $attributes, $title_attributes и $content_attributes. RDF-модуль и другие модули могут добавлять важную информацию через эти переменные.
Пример:
<?php
<div id="..." class="..."<?php print $attributes; ?>>
<h2<?php print $title_attributes; ?>>...</h2>
<div class="content"<?php print $content_attributes; ?>>...</div>
</div>
?>
Подозреваю, что основным мотивом была работа с RDF, так разрекламированная в drupal 7. На практике надо будет смотреть как с этим работать. На текущий момент я только бегло просматривал работу с темами в drupal 7 и мне сложно комментировать данное изменение с практической точки зрения.
Переменные process functions могут использоваться для любых хуков темы.
К примеру теперь можно будет назначить правило, дабы во всех меню ссылки, начинающиеся с http:/https: открывались в новом окне. Таким вот образом:
<?php
function mytheme_preprocess_menu_link(&$variables) {
if (
substr($variables['element']['#href'], 0, 5) == 'http:' ||
substr($variables['element']['#href'], 0, 6) == 'https:'
) {
$variables['element']['#localized_options']['attributes']['target'] = '_blank';
}
}
?>
Все функции тем теперь имеют унифицированный аргумент, $variables
В drupal 6 обходились через описание аргументов в hook_theme(). В drupal 7 упростили процедуру.
Как было в drupal 6:
<?php
function drupal_common_theme() {
return array(
...
'table' => array(
'arguments' => array('header' => NULL, 'rows' => NULL, 'attributes' => array(), 'caption' => NULL),
),
...
);
}
function
theme_table($header, $rows, $attributes = array(), $caption = NULL) {
...
}
?>
Как стало в drupal 7
<?php
function drupal_common_theme() {
return array(
...
'table' => array(
'variables' => array('header' => NULL, 'rows' => NULL, 'attributes' => array(), 'caption' => NULL,
'colgroups' => array(), 'sticky' => TRUE),
),
...
);
}
function
theme_table($variables) {
$header = $variables['header'];
$rows = $variables['rows'];
$attributes = $variables['attributes'];
$caption = $variables['caption'];
$colgroups = $variables['colgroups'];
$sticky = $variables['sticky'];
...
}
?>
Не знаю как вам, а мне нововведения нравятся, ввиду большей наглядности и простоты для запоминания. Вообще drupal в очень многих мелочах сделал большой шаг вперед с точки зрения удобства работы с кодом, время на сборку тем теперь обещает быть куда меньшим.
Имена функций должны начинаться имени темы
Функции в template.php теперь должны использовать соответствующее имя темы. Теперь не надо использовать phptemplate_function.
Все CSS и JavaScript файлы должны быть описаны в .info файле
В drupal6 style.css и script.js подключались автоматически. Теперь этого не будет. Требуется указывать все файлы стилей и скриптов. Опять же - это шаг к большей строгости кода.
Переименован $block->content в $content внутри block.tpl.php
В принципе, без комментариев. Хотя с этим связана довольно долгая история, с которой можно ознакомиться на drupal.org
Раздельный рендеринг шаблона
Из заголовка не понятно "о чем это". Поясню. В drupal 7 появляется замечательная функция render(). И вторая замечательная функция hide(). Что они делают - будет ясно из примера. Ниже приведен код из node.tpl.php
<?php
<div class="content">
<?php
// Спрячем комментарии и ссылки. Потом мы сможем вывести их отдельно.
hide($content['comments']);
hide($content['links']);
print render($content);
?>
</div>
<?php print render($content['links']); ?>
<?php print render($content['comments']); ?>.
?>
render() возвращает все объекты, содержащиеся в $content. Таким образом print render($content) работает как print $content в drupal 6. Когда же мы хотим вывести отдельные элементы $content, мы делаем это с помощью такого кода(для ссылок): print render($content['links']). Если нам надо спрятать отдельные элементы, мы делаем это с помощью функции hide() перед вызовом print render($content).
В ядро добавлен jQuery UI (1.7)
Файлы JQuery UI вы можете найти в misc/ui. И можете добавлять css и js из данной библиотеки через drupal_add_js() и drupal_add_css().
Присоединение JavaScript и CSS для drupal_render
Теперь мы можем отдельным элементам указывать, какие js и css файлы с ними связаны.
Как было в Drupal 6:
<?php
function example_admin_settings() {
// Добавляем example.admin.css
drupal_add_css(drupal_get_path('module', 'example') .'/example.admin.css');
// Добавляем inline-JavaScript
drupal_add_js('alert("Вы познакомились с тестовой формой.");', 'inline');
// Добавляем js настройки.
drupal_add_js(array('mymodule' => 'example'), 'setting');
$form['example'] = array(
'#type' => 'fieldset',
'#title' => t('Example');
);
return $form;
}
?>
Как будет в Drupal 7:
<?php
function example_admin_settings() {
$form['#attached_css'] = array(
// Добавляем example.admin/css.
drupal_get_path('module', 'example') . '/example.admin.css'
),
$form['#attached_js'] = array(
// Добавляем inline JavaScript.
'alert("Вы познакомились с тестовой формой.");' => 'inline',
// Add a JavaScript setting. Note that when the key is a number, the 'data' property will be used.
array(
'data' => array('mymodule' => array(...)),
'type' => 'setting'
),
);
$form['example'] = array(
'#type' => 'fieldset',
'#title' => t('Example');
);
return $form;
}
?>
$closure станет $page_bottom, новый регион $page_top и скрытые регионы
Drupal 6 располагал специальной переменной $closure, которую выводили обычно в конце HTML body и которая могла быть темизована через theme_footer() . В Drupal 7 пошли путем приведения вывода к одному общему стандарту для различных областей страницы. Что это значит? Что теперь у нас есть регион page_bottom, в котором и выводится переменная $closure, вам не надо теперь прописывать ее специально (ура! бывало, о ней забывали в процессе создания темы, что вызывало минуты размышления "а что тут не так). Также появился регион page_top. Рассмотрим различия в коде:
Drupal 6:
<?php
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
...
<body class="<?php print $body_classes; ?>">
...
<?php print $closure; ?>
</body>
</html>
?>
Drupal 7:
<?php
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN"
"http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">
...
<body class="<?php print $classes; ?>">
<?php print $page_top; ?>
...
<?php print $page_bottom; ?>
</body>
</html>
?>
Если вы создаете свои произвольные регионы, важно помнить что нужно включить page_top, page_bottom в ваш .info файл (и вообще сделать включение обязательных регионов привычкой при создании тем).
regions[help] = Help
regions[page_top] = Page top
regions[page_bottom] = Page bottom
regions[indicators] = Indicators
regions_hidden[] = indicators
page_top и page_botoom являются скрытыми регионами (hidden), таким образом они не будут отображаться в админке в разделе администрирования блоков. Может оказаться полезным создание большего количества спрятанных регионов для ваших тем (к примеру для вывода ваших самописных модулей), что вы можете теперь делать в .info файле, массивом regions_hidden[] :
regions[help] = Help
regions[page_top] = Page top
regions[page_bottom] = Page bottom
regions[indicators] = Indicators
regions_hidden[] = indicators
$left и $right переменные теперь называются $sidebar_first и $sidebar_second; CSS ID соответственно изменились
В drupal 6 сайдбары (колонки, если хотите) назывались $left и $right. В drupal 7 они называются $sidebar_first и $sidebar_second.
Drupal 6
<?php if (!empty($left)): ?>
<div id="sidebar-left" class="column sidebar">
<?php print $left; ?>
</div> <!-- /sidebar-left -->
<?php endif; ?>
...
<?php if (!empty($right)): ?>
<div id="sidebar-right" class="column sidebar">
<?php print $right; ?>
</div> <!-- /sidebar-right -->
<?php endif; ?>
Drupal 7
<?php if ($sidebar_first): ?>
<div id="sidebar-first" class="column sidebar"><div class="section region">
<?php print $sidebar_first; ?>
</div></div> <!-- /.section, /#sidebar-first -->
<?php endif; ?>
<?php if ($sidebar_second): ?>
<div id="sidebar-second" class="column sidebar"><div class="section region">
<?php print $sidebar_second; ?>
</div></div> <!-- /.section, /#sidebar-second -->
<?php endif; ?>
CSS идентификаторы тоже изменились:
.sidebar-left --> .sidebar-first
.sidebar-right --> .sidebar-second
$picture стала $user_picture, и CSS класс 'picture' изменился на 'user-picture'
$picture выводила изображение (аватарку) из профиля пользователя. Теперь, видимо, решили сделать имя более конкретным и отвечающим нуждам - $user_picture.
Добавились новые классы для скрытия содержимого
Этот пункт - особое "Вау!" от drupal 7 для верстальщика. Добавились 2 класса: .element-hidden и .element-invisible. У каждого свое назначение:
.element-hidden - назначение этого класса скрыть элемент от всех пользователей. Используется для элементов, которые не должны быть показаны сразу после открытия страницы. К примеру это выплывающие/проявляющиеся/еще-как-то-анимированные блоки, меню и т.п. К ним удобно применять jquery show() и hide() функции.
.element-invisible - предназначен для сокрытия элементов визуально. В документации было очень непонятно описано что же это обозначает, и мне пришлось покопаться в коде и обсуждениях на drupal.org. Основная идея в том, чтобы для полновесных браузеров этот элемент не был виден, но обозначался к примеру для парсеров, которые отметают элементы с display:none, или спецефичных устройств с звуковым выводом(их еще называют срин-ридеры, устройства которые озвучивают содержание сайта, часто используются людьми с нарушениями зрения). Чтобы было совсем понятнее, посмотрим как это реализовано в коде (system.css) в drupal 7:
display: none;
}
.element-invisible {
height: 0;
overflow: hidden;
position: absolute;
}
JavaScript переменная Drupal.jsEnabled удалена
По той причине что больше нет смысла ее использовать. Либо jquery работает, либо нет Процет браузеров, который поддерживает джаваскрипт, но не поддерживает jquery исчезающе мал.
PHPTemplate обзавелся wildcard
Нет, правда, я не знаю как это адекватно перевести В общем wildcard - групповой символ, когда вы хотите обозначить множественное значение. В поиске зачастую "some*" звездочка это wildcard. В Drupal 6 мы таким образом именовали файлы шаблонов: page-user.tpl.php или page-user-1.tpl.php. И если мы хотели изменить все страницы пользователей мы меняли page-user.tpl.php, невольно изменяя и страницу входа на сайт (login page). В Drupal 7 же будет доступно такое решение в названиях файлов шаблонов: page-user-%.tpl.php. Символ % тут заменяет целочисленные аргументы - т.е. он будет действовать для page-user-1.tpl.php, page-user-2.tpl.php и т.п., но не будет работать для page-user-edit.tpl.php. Что несколько облегчит наши тяжкие раздумья насчет архитектуры сайта
Изменена system_elements()
Причина - появившиеся при тестировании баги, связанные с drupal_render().
Дополнительные изменения видимости элементов
В связи с тем что на вооружение drupal7 взят описанный выше класс .element-invisible, теперь при отключенном css будет видно, как проходит инсталляция, на каком этапе сейчас находится установка сайта (ранее при отключенном css все это было проблематично увидеть, аналогично со скрин-ридерами дело обстоит), а также текущее положение пользователя в хлебных крошках.
Изменены атрибуты alt и title у RSS-иконки
В drupal 6 атрибут alt был статично прописан как "Syndicate content", а title параметр брался из переменной $title. В drupal 7 и alt и title содержат один текст, который представляет из себя "Subscribe to " + $title.
Изменился вывод $search_box
Если в drupal 6 форма поиска выводилась в любом месте темы через $search_box, то в drupal 7 эта возможность убрана. Теперь поиск это часть системы блоков. Т.е. форма поиска - элемент блока поиска, и темизоваться будет как соответствующий блок. Что гарантирует дополнительную работу при обновлении d6->d7 людям, которые использовали $search_box.
Изменение в построении меню, ссылок и табов
Изменения связаны с появлением drupal_render(), соответственно функция menu_tree_output() выводит теперь массив элементов для рендера. Функции theme_menu_item_link() и theme_menu_item() были удалены, теперь их заменяют элементы theme_menu_link().
theme_links() обзавелся новым параметром $heading для большей доступности
Вывод будет теперь более гибким. Пока сложно понять, насколько это будет полезным. Выглядеть будет так:
<?php print theme('links', $secondary_menu, array('id' => 'secondary-menu', 'class' => array('links', 'clearfix')), array('text' => t('Secondary menu'), 'level' => 'h2',
'class' => array('element-invisible'))); ?>
Улучшены theme_get_setting() и THEME_settings()
В Drupal 6 можно было добавлять собственные формы (настройки) в admin/build/themes/settings/имятемы. Нужно было создавать файл theme-settings.php в директории темы и использовать следующее решение:
<?php
/**
* Implementation of THEMEHOOK_settings() function.
*
* param $saved_settings
* array An array of saved settings for this theme.
* return
* array A form array.
*/
function phptemplate_settings($saved_settings) { }
?>
В drupal 7 все стало несколько более гибким, и теперь в theme-settings.php вы можете использовать функцию THEMENAME_form_system_theme_settings_alter(&$form, $form_state). К примеру, у вас есть тема "foo" и вы хотите добавить текстовое поле с значением по умолчанию "hi all".
<?php
function foo_form_system_theme_settings_alter(&$form, $form_state) {
$form['caberet_example'] = array(
'#type' => 'textfield',
'#title' => t('Widget'),
'#default_value' => theme_get_setting('foo_example'),
'#description' => t("Place this text in the widget spot on your site."),
);
}
?>
Для того чтобы установить значение по умолчанию, вы просто добавляете в ваш .info файл строчку:
и в любом php файле темы вызвать
<?php
$foo_example = theme_get_setting('foo_example');
?>
Маркер для обязательных полей
Теперь для того чтобы отметить какое-либо поле как "обязательное", вы можете использовать новую функцию theme_form_required_marker() . В drupal 6 надо было использовать theme_form_element(), т.е. переопределять эту довольно емкую функцию - теперь эти лишние телодвижения упростили, сделав для такой довольно популярной задачи theme_form_required_marker() .
Новая функция theme_link()
Сделана для упрощения переопределения ссылок. Для тех, кто пользовался hook_preprocess_link() вполне знакома конструкция (для drupal 6):
<?php
function mytheme_preprocess_link(&$variables) {
// In order to style links differently depending on what they are linking to,
// add classes that contain information about the link path.
if (strpos($variables['path'], ':') !== FALSE) {
// For external links, add a class indicating an external link and a class
// indicating the scheme (e.g., for 'mailto:...' links, add a 'link-mailto'
// class).
$variables['options']['attributes']['class'][] = 'link-external';
$variables['options']['attributes']['class'][] = 'link-' . parse_url($variables['path'], PHP_URL_SCHEME);
}
else {
// For internal paths, add a class indicating an internal link.
$variables['options']['attributes']['class'][] = 'link-internal';
if (empty($variables['path']) || $variables['path'] == '<front>') {
// Add a class indicating a link to the front page.
$variables['options']['attributes']['class'][] = 'link-front';
}
else {
// For internal links not to the front page, add a class for each part
// of the path. For example, for a link to 'admin/structure/block', add
// the classes 'link-path-admin', 'link-path-admin-structure', and
// 'link-path-admin-structure-block'.
$class = 'link-path';
foreach (explode('/', $variables['path']) as $path_part) {
$class .= '-' . $path_part;
$variables['options']['attributes']['class'][] = $class;
}
}
}
}
?>
Как это может выглядеть сейчас в drupal 7? Так:
<?php
function mytheme_link($variables) {
// Place a span within and outside the anchor tag in order to implement some
// special styling.
return '<span class="link-wrapper"><a href="' . check_plain(url($path, $options)) . '"' . drupal_attributes($options['attributes']) .
'><span class="link-content-wrapper">' .
($options['html'] ? $text : check_plain($text)) . '</span></a></span>';
}
?>
Для "родных тем" ядра будет облегчен доступ к основным ссылкам
Для тех кто работает только с клавиатурой или использует устройства голосового воспроизведения контента(скрин-ридеры).
Комментарии
спасибо, узнал много нового
p.s. Вот бы все докладчики где-нить выложили свои доклады для ДД
box.tpl.php я пользовался стилем box
спасибо за разбор изменений...
Будет полезно при переделке тем от 6-ки к 7-ке.
$content стал полноценным регионом, который нужно обязательно указывать в .info файле. Собственно контент выводится в блоке Main page content (который не обязательно помещать в $content).
Поправте на page--user--%.tpl.php в 7м друпале все дефисы в названиях *.tpl.php файлов двойные.
Спасибо, полезная информация.
Такой вопрос. Делал пробную тему для 7-ки и в моей теме, в фале template.php переопределял функцию theme_link()(в моем случае archi__link() название темы archi) просто добавил span внутрь ссылок. Никаких изменений... Попробовал переопределить theme_breadcrumbs() для проверки, тоже самое. Кто может подскажет в чем трабл? Кеш очищал... Ниче не меняется.
У меня замечание:
Main menu и Secondary menu.
Почитаю еще. Как раз занимаюсь переделкой темы. Удобно, ваш вольный перевод и оригинал на друпал орг.
Оу, я так давно его писал, еще когда 7ка бетой была. Может там уже что изменилось, сам когда что-то надо, смотрю на d.org.
Все четко. Это мы привередничаем
Слово меню переведенное на русский, не собьет с толку тех кто занимается переделыванием шаблонов друпала.
А тем кого собьет, незачем заниматься этим
Тему почти переделал. Но вот с регионами траблы еще не решил.
Хороший обзор, коротко и по делу!
Неверно. В Drupal 7 это делается через drupal_add_library(). Пример:
<?php
drupal_add_library('system', 'ui.dialog');
?>
Далее, в Drupal 7 такой код работатать не будет:
Аттрибутов #attached_js и #attached_css не существует. Правильным является такой код:
<?php
$form['#attached']['css'] = array(
// Add example.admin/css.
drupal_get_path('module', 'example') . '/example.admin.css'
),
$form['#attached']['js'] = array(
// Add some inline JavaScript.
'alert("You are visiting the example form.");' => 'inline',
// Add a JavaScript setting. Note that when the key is a number, the 'data' property will be used.
array(
'data' => array('mymodule' => array(...)),
'type' => 'setting'
),
);
?>
Документация по апгрейду тем с д6 на д7 была неверна слегка, я этот момент там уже исправил.
Так в D7 тоже работать не будет:
В семёрки все регионы помещены в $page, к тому же они должны быть отрендерены перед выводом:
<?php if ($page['sidebar_first']): ?>
<div id="sidebar-first" class="column sidebar"><div class="section region">
<?php print render($page['sidebar_first']); ?>
</div></div> <!-- /.section, /#sidebar-first -->
<?php endif; ?>
Да, верные замечания. Писалось когда 7ка еще была в бете, с тех пор исходную страничку http://drupal.org/update/themes/6/7 многократно подправили, а вносить все эти правки в перевод нет никакой возможности по времени. Так что за любые заметки - спасибо.
Какие регионы всеже обязательные? Какую роль выполняют page_top и page_bottom и в каком месте page они должны быть прописаны?
Что такое regions[indicators] = Indicators ?
Выведите на главную. Хоть все и приверженцы Д8, но статья очень полезная
Весьма своевременное пожелание!
Статья неожиданно всплыла вверх в трекере
Вернем на главную, если ТС пообещает снова ее не тереть.
не надо на главную) не 2009 год) сорри, заголовок менял, поднялась