PHP | Кирилл Павлюков
Фев 01

Логотип «ZF CONF 2010″

Конференция под названием «ZF Conf 2010″ пройдет в конце марта в Санкт-Петербурге, сообщают. На днях был запущен сайт конференции – http://www.zfconf.ru/, и началась предварительная регистрация участников. Организаторы просят всех желающих пройти регистрацию как можно скорее, т.к. для решение ряда организационных вопросов им необходимо знать хотя бы приблизительно оценить количество человек, которых хотели бы посетить конференцию или каким-то иным образом принять участие в ней.На днях закончился прием тезисов по планируемым докладам. В состав докладчиков вошли ведущие участники русскоязычного сообщества ZF, опытнейшие специалисты и лично один из создателей фрэймворка! Каждый предстоящий доклад доступен для обсуждения на форуме ZF-сообщества. Ваше мнение очень важно для авторов!Принимайте активное участие в обсуждении докладов. Это поможет авторам понять, какие темы наиболее интересны для разработчиков.Планируемые доклады

  • Жизненный цикл предложений (proposals) в проекте Zend Framework
    Александр Веремьев (Zend Technologies, Zend Framework Core команда)
  • Zend_Search_Lucene в деталях
    Александр Веремьев (Zend Technologies, Zend Framework Core команда)
  • Zend Framework и производительность
    Александр Махомет (создатель сообщества ZendFramework.ru)
  • Zend Framework и MVC, «толстая» модель
    Александр Стешенко (Norada Corporation, PHP-разработчик)
  • ФотоСтрана.ru: Прототипирование с использованием ZF (история боевого применения Zend Framework в highload-проекте)
    Леонид Жаворонков (ФотоСтрана.ru, тимлид)
  • Использование очередей сообщений в повседневных проектах
    Денис Баклыков (Обновление, веб-разработчик)
  • Zend Framework и Doctrine
    Степан Танасийчук (руководитель веб-студии stfalcon.com)
  • Zend Framework и мультиязычность
    Степан Танасийчук (руководитель веб-студии stfalcon.com)
  • История проекта e-Штаб
    Анатолий Ларин (e-Легион, веб-программист)
  • Что нового несет нам Zend Framework 2.0?
    Надежда Блинова (Wizartech, веб-программист), Георгий Туревич (Wizartech, ведущий веб-программист)
  • Интеграция Zend Framework c Javascript-фрэймворками jQuery и Dojo Toolkit
    Георгий Туревич (Wizartech, ведущий веб-программист)

Смело обсуждайте доклады и регистрируйтесь!За последними новостями можно следить здесь:
RSS-канал: feeds.feedburner.com/zfconf
Twitter: @zfconf
Группа Вконтакте: vkontakte.ru/club14951507К участию также приглашаются информационные и финансовые спонсоры.

Share

Автор: Кирилл Павлюков \\ Метки: , , , ,

Сен 09

Недавно на нашем основном сайте упала посещаемость. При этом поток с поисковых систем был на прежнем уровне, а количество посетителей с новостных информеров пошло вниз. Сверив статистику, мы были удивлены, что одному из партнеров мы отдавали в день около 300-500 посетителей, что, в принципе, очень мало.
Учитывая то, что недавно этот партнер (якобы случайно) нарушил достигнутые между нами договоренности, нам присланная статистика показалась заниженной. Я начал думать, как можно посчитать количество переходов, если информер – это яваскрипт, который отрабатывается на стороне клиента.

И в этом на помощь пришел jQuery!
Несколько строк кода – и переходы по внешним ссылкам считаются нашим сервером!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// подключаем jQuery
<script src="http://code.jquery.com/jquery-latest.js"></script>
// подключаем класс для работы с base64
<script src="http://www.zavtra.com.ua/jscripts/base64.js"></script>
 
<script>
// при загрузке документа...
$(document).ready(function(){
// получить массив все ссылки, которые содержат текст "xxx.yyy" в аттрибуте href
var x= $("a[href*='xxx.yyy']");
// для каждой найденной ссылки...
jQuery.each(x, function() {
  // url - это старый адрес закодированный по Base64
  url = Base64.encode($(this).attr("href"));
  // назначаем новый аттрибут href: адрес PHP-скрипта на нашем сервере,
  // и в качестве строки запроса - полученный в прошлой строке урл
  $(this).attr("href", "http://www.zavtra.com.ua/go2un.php?" + url);
});

});
</script>

Вуаля! Теперь все переходы идут через скрипт на нашем сервере. А в этом скрипте мы записываем информацию о переходе в лог-файл и перенаправляем пользователя по адресу, который получаем из раскодированной строки запроса.

Share

Автор: Кирилл Павлюков \\ Метки: , , , ,

Авг 11

SEO – это Search Engine Optimization, или, говоря по-русски, оптимизация под поисковые машины. Именно этим делом я занимаюсь в последние дни и недели. В этой заметке я не расскажу ничего нового или неизвестного ранее опытному оптимизатору. Но начинающему СЕОшнику, надеюсь, эта заметка окажется полезной.
Рецепт первый – блоги
Для продвижения основного сайта я создал блог на Blogspot. Почему именно там? Почему не на своем хостинге?
Во-первых, потому что хотелось, чтобы блог и сайт висели на разных айпишниках. Где-то читал, что ссылки с сайтов, которые висят на одном айпиадресе с основным, учитываются меньше, чем с сайтов, разнесенных по разным уголкам сети.
Во-вторых, заметки с блогохостингов очень быстро попадают в поисковый индекс. Иногда – в течение нескольких минут. В то же время, если бы блог магазина был сам по себе, то пришлось бы потратить немало сил и на его продвижение, чтобы он стал регулярно посещаться ботами поисковиков.
В третьих, также роль играет то, что на блогохостинге тебя могут найти другие пользователи этой службы, стать подписчиком…
В четвертых, на блогохостингах (ЖЖ, Ли.Ру) можно создавать сообщества. Путем перелинковки блога с сообществом, сообщества с другим сообществом или основным сайтом, я надеюсь прибавить ТИЦ и ПР. Кроме того, если писать хорошо (и главное – не только рекламу товаров, а и то, что интересно пользователям) вполне реально найти постоянных читателей, и, как следствие, – посетителей основного сайта.
В общем, чем больше блогов разных и клевых – тем лучше!
Но писать надо везде только эксклюзивный контент! Если будете тупо кросс-постить одно и тоже сообщение в несколько блогов – результат если и будет, то скорее всего негативный.

Рецепт номер 2. Ссылок больше, но меньшими дозами.
Есть у меня сайт, который замечательно индексируется Гуглом. На сайте порядка 150 тысяч страниц, и если поставить сквозную ссылку – то на ссылочная масса продвигаемого сайта возрастет очень резко.
А поисковики такого не любят. Ибо как-то неестесственно смотрится, что до определенного дня на сайт было, условно, 10 ссылок, а за сутки добавилось 99990 новых… Поэтому я решил кормить Яшу и Гугля «с ложечки». Для этого пришлось написать небольшой скриптик. Вряд ли он вас устроит в том виде, в котором я его публикую здесь, но идея должна быть понятна.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php
// в качестве идентификатора страницы, на которой будет размещена ссылка,
// используем md5-хеш строки запроса
$page = md5($_SERVER['REQUEST_URI']);
// количество ссылок, которые будут добавляться в течение дня
$maxLinksPerDay = 100;
// количество ссылок, которое будет добавляться на 1 страницу
$linksPerPage = 1;
// "клей" нужен если ссылок на странице больше 1
$glue = ', ';
// временных массив для хранения ссылок до их вывода
$linksToDisplay = array();

// Для начала проверим, имеются ли ссылки, которые уже
// добавлены на запрашиваемую страницу.
$mresult = @cms_query("SELECT `".$_CONFIG['sql_pref']."Links`.`Link`
FROM `"
.$_CONFIG['sql_pref']."LinksPages`
LEFT JOIN `"
.$_CONFIG['sql_pref']."Links` ON `".$_CONFIG['sql_pref']."Links`.`LID` = `".$_CONFIG['sql_pref']."LinksPages`.`LID`
WHERE `"
.$_CONFIG['sql_pref']."LinksPages`.Page = '{$page}'");
if (mysql_num_rows($mresult) > 0) {
    // если есть - сохраним их во временный массив
    while ($mline = mysql_fetch_object($mresult)) {
        $linksToDisplay[] = $mline->Link;
    }
} else {
    // если ссылок нет - проверим, сколько ссылок уже добавлено за сегодня
    $mresult = @cms_query("SELECT COUNT(id) AS Count FROM `".$_CONFIG['sql_pref']."LinksPages`
    WHERE Created = CURDATE() GROUP BY Created"
);
    $mline = mysql_fetch_object($mresult);
    if ($mline->Count < $maxLinksPerDay) {
        // Если меньше, чем разрешено в настройках, добавим новые
        $mresult = @cms_query("SELECT LID, Link FROM `".$_CONFIG['sql_pref']."Links`
        ORDER BY `Times_used`, RAND()
        LIMIT 0, $linksPerPage"
);
        if (mysql_num_rows($mresult) > 0) {
            while ($mline = mysql_fetch_object($mresult)) {
                $mmresult = @cms_query("INSERT INTO `".$_CONFIG['sql_pref']."LinksPages` (`Page`, `LID`, `Created`)
                    VALUES ('{$page}', '{$mline->LID}', CURDATE())"
);
                $mmresult = @cms_query("UPDATE `".$_CONFIG['sql_pref']."Links`
                    SET `Times_used` = `Times_used` + 1
                    WHERE LID = {$mline->LID}"
);
                $linksToDisplay[] = $mline->Link;
            }
        }
    }
}

// Выводим то, что насобирали :-)
if (count($linksToDisplay) > 0) echo join($glue, $linksToDisplay);

?>

Вот дамп структуры базы:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CREATE TABLE `Links` (
`LID` int(11) NOT NULL auto_increment,
`Link` varchar(255) NOT NULL,
`Times_used` int(11) NOT NULL,
PRIMARY KEY (`LID`),
KEY `Times_used` (`Times_used`)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251;

CREATE TABLE `LinksPages` (
`id` int(11) NOT NULL auto_increment,
`Page` varchar(64) NOT NULL,
`LID` int(11) NOT NULL,
`Created` date NOT NULL,
PRIMARY KEY (`id`),
KEY `Page` (`Page`),
KEY `Created` (`Created`)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251;

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

Share

Автор: Кирилл Павлюков \\ Метки: , , ,

Окт 12

Недавно Владимир в комментариях к заметке о SPAW задал вопрос относительно вставки уменьшенных копий изображения.

В свое время передо мной тоже стояла такая задача. Здесь я расскажу о том, как я решил ее в своем проекте.

Для начала теория. Вставив ибображение на странице редактора SPAW, вы можете задать ему практически любой доступный HTML-атрибут, а также изменить размер изображения до необходимого вам минимума. Затем, на стороне сервера с помощью регулярных выражений можно извлечь из текста все изображения и обработать их, и привести к нужному виду.

В последнем проекте (новостной сайт) была необходимость создания превьюшек, а также максимально легко добавлять к изображениям подпись и источник, откуда оно было взято. Подпись редакторы указывали в атрибуте alt, источник – в title, а необходимые размеры брались из style. Кроме того, использовался и атрибут align.

Ниже приведен код метода из контроллера действий News (проект написан на Zend Framework), который отвечал за необходимый мне функционал. Код снабжен большим количеством комментариев, поэтому отдельно я его описывать не стану. Если возникнут вопросы – задавайте их в комментариях.

Читать запись полностью »

Share

Автор: Кирилл Павлюков \\ Метки: , , ,

Окт 12

В предыдущей заметке я вкратце рассказал о том, что представляет из себя CMS 2z. В этой – приведу пример того, как можно доработать модуль голосования, чтобы результаты опросов выводились в более приятном для глаза виде. По-умолчанию результаты опросов выводятся в текстовом виде: вариант ответа, количество ответов и процент проголосовавших за этот вариант. А душа просила чего-то более красивого… В этой заметке речь пойдет о том, как вывести результаты голосования в виде красивых цветных шкалок. Я видел, что на форуме разработчиков 2z этот вопрос озвучивали, но решение так и не было найдено. Чтож, попытаюсь восполнить пробел своим примером. Так как в коде шаблона PHP использовать не получится, придется дорабатывать непосредственно код модуля voting. Итак, нам нужно доработать 2 файла плагина + шаблоны.
2z/extras/voting/config.php:
Найдите строку 27 с таким текстом:

1
array_push($cfg, array('name' => 'skin', 'title' => 'Используемый скин', 'descr' => 'Вы можете использовать только один скин единовременно', 'type' => 'select', 'values' => $skList, 'value' => extra_get_param('voting','skin')));

После нее вставьте еще одну строку:

1
array_push($cfg, array('name' => 'max_px', 'title' => 'Максимальная ширина шкалы', 'descr' => 'Укажите максимальный размер шкалы в пикселах', 'type' => 'input', 'value' => extra_get_param('voting','max_px')));

2z/extras/voting/voting.php:
Найдите строку 82:

1
$cnt = 0;

И вставьте после нее 2 строки:

1
2
$max_px = extra_get_param('voting','max_px');
$max_cnt = 0;

Теперь найдите строку (87):

1
foreach ($lrows as $lrow) { $cnt += $lrow['cnt']; }

И ЗАМЕНИТЕ ее на:

1
foreach ($lrows as $lrow) { $cnt += $lrow['cnt']; $max_cnt = max($lrow['cnt'], $max_cnt); }

Найдите строку:

1
'perc' => intval($lrow['cnt']*100/$cnt),

и после нее добавьте строку:

1
'px' => intval($lrow['cnt']*$max_px/$max_cnt),

Теперь у вас в шаблне появилась еще 1 переменная: {px}
Используйте ее в свое удовольствие!
Ну, например, вот так:

1
<div style="height: 10px; background-color: #003399; width: {px}"></div>
Share

Автор: Кирилл Павлюков \\ Метки: , , ,

Окт 12

2z – это шустро-быстрая модульная CMS. Призвана служить во благо инернет-СМИ и прочих подобных сайтов. Мое мнение о ней неоднозначное. С одной стороны, весь необходимый функционал есть, все работает, баги регулярно исправляются, новые модули пишутся. Это, безусловно, плюсы.
С другой стороны – мне не нравится качество кода. Много лишнего, кое что требует серьезной подчистки, ну и процедурное программирование почти в чистом виде. Вероятно, в этом нет ничего плохого – ведь множество популярных проектов процедурно… Но мне просто такой подход кажется не достаточно масштабируемым, хоть и более производительным. И еще админка… С ней пришлось поковыряться, чтобы понять, как она работает. У меня это заняло минут 15, отсилы. Но редактора сайта, которому я сегодня показывал новую админку интерфейс привел в ужас! Пришлось объяснять как для идиота: нажимаем кнопочку такую-то, чтобы получилось то-то. И это при том, что редактор отнюдь не новичек, сайты редактирует уже больше 4 лет. Это что касается минусов.

Но вернусь снова к плюсам.
Во-первых, несмотря на процедурное программирование, код довольно-таки интуитивно понятен. Мне потребовалось не больше 2 часов, чтобы разобраться с принципами системы.
Во-вторых, простой шаблонизатор. Стандартный шаблон отвратителен, но поменять его на что-то более-менее нормальное не требуется много усилий. Создание шаблона для проекта, который мы запустили на 2z, заняло меньше примерно полдня. (Имеется ввиду на простой дизайн, порезку, верстку и адаптацию к шаблону).
В третьих, много бесплатных модулей и шаблонов. Для нормального новостного сайта более чем достаточно. Кстати, говорят, что 2z любят СЕОшники, за то, что на основе этой системы можно сделать десятки сайтов которые функционально будут на весьма высоком уровне. Среди модулей есть генератор карты сайта и файла Sitemap. Ну и другие вкусности типа календаря, архива, опросов…

Короче, маст кноу )
Даже если вы не будете использовать 2z, то просто посмотреть на него вы обязаны!

ЗЫ. На правах, так сказать, рекламы презентую линк свежезапущенного проекта: Выборы 2008 – достали!. Он конечно еще безумно сырой, но так как тематика довольно недолгоиграющая, было принято решение вывешивать «те, що маємо», а дорабатывать уже по ходу.

Share

Автор: Кирилл Павлюков \\ Метки: , , ,

Сен 04

Это перевод второй части статьи с DevZone об использовании Zend_Acl MVC Zend Framework.

В первой части мы говорили о том, как настроить экземпляр Zend_Acl, и как присоединить его к окружению MVC (с использованием плагина фронт-контроллера). Но как насчет установки другого действия для обработки запрета доступа, или как сделать, чтобы статью мог редактировать только ее владелец? Это и кое-то другое вы найдете в приведенных ниже примерах.

Как я упоминал в первой части, эта статья основана на Предложениях Zend Framework (ссылка), которая в настоящее время находится в лаборатории (Лаборатория – часть кода и концепции Zend Framework, которая внесена в проект, но дожидается утверждения для внесения в основной релиз, – прим. переводчика).

1. Использование модулей

Давайте поговорим об использовании модулей. Мы использовали в качестве примера для данной статьи сайт аналогичный devzone. Как насчет того, чтобы создать административный модуль, для утверждения (одобрения) статей и некоторых других задач (например, измениения быстрых ссылок или разделов)? Для этого нам потребуется изменить модель ресурсов нашего ACL:

Модуль по-умолчанию:

  • Контроллер user.
  • Контроллер article.

Административный модуль:

  • Контроллер article.
  • Контроллер quick-link.
  • Контроллер category.

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

ВАЖНО
Помните, этот код и создание объекта Zend_Acl должны быть выполнены до вызова метода диспетчеризации фронт-контроллера (в файле bootstrap).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/** Создание Ролей */
require_once 'Zend/Acl/Role.php';
$myAcl->addRole(new Zend_Acl_Role('guest'))
      ->addRole(new Zend_Acl_Role('writer'), 'guest')
      ->addRole(new Zend_Acl_Role('admin'), 'writer');

/** Создание Ресурсов */
require_once 'Zend/Acl/Resource.php';
/** Модуль по-умолчанию */
$myAcl->add(new Zend_Acl_Resource('user'))
      ->add(new Zend_Acl_Resource('article'));

/** Административный модуль */
$myAcl->add(new Zend_Acl_Resource('admin'))
      ->add(new Zend_Acl_Resource('admin:article', 'admin'))
      ->add(new Zend_Acl_Resource('admin:quick-link', 'admin'))
      ->add(new Zend_Acl_Resource('admin:category', 'admin'));

/** Создание привелегий */
$myAcl->allow('guest', 'user')
      ->deny('guest', 'article')
      ->allow('guest', 'article', 'view')
      ->allow(array('writer', 'admin'), 'article', array('add', 'edit'))
      ->allow('admin', 'admin');

/** Установка фронт-контроллера */
require_once 'Zend/Controller/Front.php';
$front = Zend_Controller_Front::getInstance();
$front->setControllerDirectory(array('default' => 'path/to/default/controllers',
                                     'admin' => 'path/to/admin/controllers'));

/** Регистрация объекта плагина */
require_once 'Zend/Controller/Plugin/Acl.php';
$front->registerPlugin(new Zend_Controller_Plugin_Acl($myAcl, 'guest'));

/** Запуск диспетчеризации */
$front->dispatch();

Заметьте, мы создали ресурс для каждого контроллера модуля по-умолчанию, а для административного модуля – ресурс для самого модуля и по одному для каждого контроллера внутри модуля (в формате ‘модуль:контроллер’), которые являются дочерними по отношению к ресурсу модуля администрирования. Кроме того мы указали, что только пользователи с административной ролью имеют доступ к административному модулю.

2. Использование ролей

Войдя в приложение, пользователь должен получить одну из ролей (в нашем примере роль может быть ‘guest, ‘writer’ или ‘admin’). Так как же мы можем определить роль в наших приложениях? Для начала необходимо сохранить роль в переменной, которая хранится в сессии. Поэтому, после авторизации пользователя, нужно сохранить его роль в хранилище сессии, а при последующих запросах, извлечь ее из сессии, чтобы передать плагину фронт-контроллера в bootstrap-файле.

Контроллер User

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
user controller class UserController extends Zend_Controller_Action
{
    protected $_application;

    public function init()
    {
        require_once 'Zend/Session/Namespace.php';
        $this->_application = new Zend_Session_Namespace('myApplication');
    }

    public function loginAction()
    {
        ... Код проверки авторизации
        if ($valid) {
            /** Сохранение роли в сессии */
            $this->_application->currentRole = $user->role;
            $this->_application->loggedUser = $user->username;
        }
    }

    public function logoutAction()
    {
        $this->_application->currentRole = 'guest';
        $this->_application->loggedUser = null;
    }
}

bootstrap-файл

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/** Загрузка данных приложения из сессии */
require_once 'Zend/Session/Namespace.php';
$application = new Zend_Session_Namespace('myApplication');

if (!isset($application->currentRole)) {
    $application->currentRole = 'guest';
}

/** Установка фронт-контроллера */
require_once 'Zend/Controller/Front.php';
$front = Zend_Controller_Front::getInstance();
$front->setControllerDirectory('path/to/controllers');

/** Регистрация объекта плагина */
require_once 'Zend/Controller/Plugin/Acl.php';
$front->registerPlugin(new Zend_Controller_Plugin_Acl($myAcl, $application->currentRole));

/** Запуск диспетчеризации */
$front->dispatch();

3. Установка действия, отображаемого в случае запрета доступа

Возможно, некоторым из вас (надеюсь, никому =D) просто не понравится идея хранения действия, отображаемого в случае запрета доступа, в контроллере ошибок (error controller), или просто захочется иметь какой-то обходной путь. В этом случае вам поможет метод setErrorPage плагина фронт-контроллера.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/** Установка фронт-контроллера */
require_once 'Zend/Controller/Front.php';
$front = Zend_Controller_Front::getInstance();
$front->setControllerDirectory('path/to/controllers');
 
/** Установка действия, отображаемого в случае запрета доступа  */
require_once 'Zend/Controller/Plugin/Acl.php';
$aclPlugin = new Zend_Controller_Plugin_Acl($myAcl, 'guest');
$aclPlugin->setErrorPage('goaway', 'my-error-controller', 'my-module');

/** Регистрация объекта плагина */
$front->registerPlugin($aclPlugin);
 
/** Запуск диспетчеризации */
$front->dispatch();

К методу setErrorPage можно обратиться, указав только имя действия (в этом случае будут использованы имя контроллера и модуля по-умолчанию: ‘error’ и ‘default’ соответственно), но также можно передать методу действие и контроллер или все три параметра.

4. Использование помощника действий

В конце мы рассмотрим одну из наиболее важных частей этого предложения: как на примере devzone только авторам и администраторам разрешить редактировать статьи (Но, стоп: здесь есть упущенный момент. Если я – автор, и у меня есть доступ к article/edit/:id, в таком случае я смогу редактировать не только свои статьи, но и чужие. Это проблема, не так ли? Итак, что же нам теперь делать? Управлять с помощью помощника действий (Action Helper), который позволит обратиться к нашему ACL внутри любого контроллера, а не только во время запуска.
Итак, перво-наперво нам нужно зарегистрировать наш плагин фронт-контроллера и Controller Action Helper Broker.
Bootstrap-файл

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/** Загрузка данных приложения из сессии */
require_once 'Zend/Session/Namespace.php';
$application = new Zend_Session_Namespace('myApplication');

if (!isset($application->loggedUser)) {
    $application->loggedUser = null;
}

/** Установка фронт-контроллера */
require_once 'Zend/Controller/Front.php';
$front = Zend_Controller_Front::getInstance();
$front->setControllerDirectory('path/to/controllers');

/** Регистрация объекта плагина */
require_once 'Zend/Controller/Plugin/Acl.php';
$front->registerPlugin(new Zend_Controller_Plugin_Acl($myAcl, $application->currentRole));

/** Регистрация объекта Action Helper */
require_once 'Zend/Controller/Action/Helper/Acl.php';
require_once 'Zend/Controller/Action/HelperBroker.php';
Zend_Controller_Action_HelperBroker::addHelper(new Zend_Controller_Action_Helper_Acl());

/** Запуск диспетчеризации */
$front->dispatch();

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
article controller class ArticleController extends Zend_Controller_Action
{
    protected $_acl;
    protected $_application;
 
    public function init()
    {
        /** Получаем наш помощник действия (Action Helper) */
        $this->_acl = $this->_helper->getHelper('acl');

        require_once 'Zend/Session/Namespace.php';
        $this->_application = new Zend_Session_Namespace('myApplication');
    }

    ...
 
    public function editAction()
    {
        /** Получаем статью по id */
        $article = new Article($this->_request->id);

        /** Проверяем, является ли пользователь владельцем или администратором */
        if (($article->author != $this->_application->loggedUser) && ($this->_application->currentRole != 'admin')) {
            $this->_acl->denyAccess();
        }
 
        ...
    }
}

Заключение

И последняя рекомендация: придерживайтесь принципа Оставлять Все Простым (Keep It Simple). Если вам не нужны динамически подгружаемые ACL, написанные руками правила – это не грех, это только лишь лучший способ реализовать необходимый функционал.

Автор статьи: Альдемар Берналь

Автор перевода: Кирилл Павлюков

Дополнительная информация:
Zend_Acl & MVC IntegrationА здесь вы найдете исходный код примеров к данной статье:
Исходный код
Оригинал статьи на DevZone:
http://devzone.zend.com/article/3510-Zend_Acl-and-MVC-Integration-Part-II-Advanced-Use

Share

Автор: Кирилл Павлюков \\ Метки: , , , ,

Авг 29

На сайте разработчиков Zend Framework еще в конце июня появилась статья об интеграции Zend_Acl и парадигмы MVC. Несмотря на полезность и актуальность информации, до сих пор я не встречал перевода этой статьи на русский язык. Ниже приведена моя попытка исправить это. Кроме того, я уже заканчиваю перевод продолжения этого материала, поэтому к понедельнику выложу и его.

Так что же не правильного в компоненте Zend_Acl и текущей реализации MVC в Zend Framework? Нет ничего неправильного, просто для разработчиков не вполне очевидно, как достичь оптимальной интеграции этих двух важнейших частей фреймворка.

Стоит отметить, что данная статья основана на текущих Предложениях к Zend Framework (ссылка), но в данный момент эти рекомендации находятся на утверждении. Окей! Как это работает? Для реализации этого в Предложениях существует два ключевых компонента:

  1. Плагин фронт-контроллера (Front Controller Plugin). Этот компонент проверяет, имеет ли текущий пользователь доступ к запрашиваемой странице.
  2. Помощник действий (Action Helper). Этот компонент позволяет делать проверку прав доступа пользователя внутри контроллера действий.

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

Ресурсы:

  • Контроллер user.
  • Контроллер article.

Роли:

Share

Автор: Кирилл Павлюков \\ Метки: , , , , ,

Авг 27

Собственно, сабж. :-)

Думается, что через недельку-другую уже будет полноценный релиз. По крайней мере, я на это надеюсь. Нет, даже не так! Я на это УПОВАЮ!!! )))

Что нового в RC3? Да не понятно. По идее, ничего нового тут быть не должно. Но я обнаружил в документации новый элемент Zend_Captcha и соответственно Zend_Form_Element_Captcha. Не уверен, но мне кажется что даже в RC2 этого не было. Хотя, в SVN пишут, что эти фишки были скопированы в ветку релиза 1.6 еще 6 августа. Соответственно, тогда они должны были присутствовать во втором релиз-кандидате. В любом случае, наличие этих элементов – вещь мега-полезная и рульная!

Для тех, кто в танке. Zend Framework – это библиотека PHP-классов, созданная с целью облегчить и ускорить процесс разработки сложных сайтов. Основывается на философии MVC. Больше можно прочитать на официальном сайте. 

ЗЫ. Так задолбали спамеры со своими комментами. Кто знает хороший антиспам для WordPress? Akismet не предлагать )

Share

Автор: Кирилл Павлюков \\ Метки: , , ,

Июл 30

Вы, вероятно уже поняли, что мой любимый визуальный редактор (Rich Text Editor, по-буржуйски) – это SPAW Editor. Я уже неоднократно писал заметки о SPAW. В частности, о том, как подружить SPAW и WordPress, и как сделать плагин Spaw для Smarty. Сегодня пришло время подружить SPAW с не менее замечательной опенсорсной разработкой – Zend Framework.

Принципы интеграции

В общем-то, можно инстанционировать экземпляр SPAW просто в нужном контроллере действий, и передать его экземпляр скрипту вида, который выведет в нужном месте HTML-код. Но! Во-первых, это очень просто, а мы легких путей не ищем. А – главное – во-вторых, тогда потеряется возможность фильтрации и валидации введенных пользователями данных с помощью механизмов Zend_Form. Можно, конечно, самостоятельно произвести над данными необходимые действия. Но не кошерно это, что ли.

Итак, в этой статье я опишу, как сделать редактор SPAW полноценным элементом Zend_Form.

Для начала, разберем, что собой представляет HTML-вывод SPAW. Элементы ввода (для каждой страницы редактора) – это обычные textarea, которые обрамлены килобайтами трудночитаемого HTML и JS. Это довольно примитивная характеристика, но для понимания сути ее достаточно. Следовательно, нам необходимо всего лишь «завернуть» Zend_Form_Element_Textarea в соответствующую оболочку кода. Примерно так мы и поступим – мы напишем свой Zend_Form_Decorator.

Что необходимо

Так как SPAW может оперировать двумя типами объектов (объект редактора, собственно, и объект страницы), нам понадобится 2 соответствующих декоратора: My_Decorator_Spaw и My_Decorator_SpawPage.

При этом, My_Decorator_SpawPage – это вовсе не декоратор в философии Zend_Form, а лишь механизм, с помощью которого мы будем информировать My_Decorator_Spaw о наличие дополнительных страниц. Конечно это не правильно, но другого выхода я не придумал :-( . В свою очередь, такая реализация накладывает одно ограничение на использование: все дополнительные страницы редактора должны быть объявлены ДО объявления основной страницы.

Приступим

Для начала приведу код декоратора My_Decorator_SpawPage:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

require_once $_SERVER['DOCUMENT_ROOT'] . '/spaw2/spaw.inc.php';

class My_Decorator_SpawPage extends Zend_Form_Decorator_Abstract
{
    public function render($content)
    {
        $element = $this->getElement();
        $element->setDisableLoadDefaultDecorators(true);
        $page = new SpawEditorPage($element->getName(),
                                   $element->getLabel(),
                                   $element->getValue(),
                                   $element->getAttrib('direction'));
        My_Decorator_Spaw::addPage($page);
        return NULL;
    }
}

Как видите, он создает объект страницы и передает его статическому методу My_Decorator_Spaw::addPage(). Этот метод сохраняет объект в хранилище My_Decorator_Spaw::$pages. Позже, при рендеринге основной части SPAW, все объекты страниц SpawEditorPage будут изъяты из этого хранилища. Конструктору SpawEditorPage передаюся 4 параметра, которые устанавливаются с помощью соответствующих методов объекта Zend_Form_Element_Textarea. Для понимания этого механизма либо обратитесь к мануалу, либо посмотрите раздел «Пример использования» в этой статье.

Ниже приведен код второго декоратора – My_Decorator_Spaw. Комментарии приведены в самом коде.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<?php

require_once $_SERVER['DOCUMENT_ROOT'] . '/spaw2/spaw.inc.php';

class My_Decorator_Spaw extends Zend_Form_Decorator_Abstract
{
    // объявим статическую переменную $pages - наше хранилище
    static $pages = array();

    // а это - статический метод для добавление объектов
    // страниц редактора в хранилище
    static function addPage($page) {
        array_push(self::$pages,$page);
    }
   
    // метод render() вызывается автоматически в момент генерации
    // кода элемента формы. Здесь и происходит перевоплощение обычной
    // textarea в полноценный визуальный редактор
    public function render($content)
    {
        // получим объект элемента формы для удобства доступа
        // к его свойствам
        $element = $this->getElement();
        // на всякий случай отключим дефолтные декораторы
        // для этого элемента
        $element->setDisableLoadDefaultDecorators(true);
        // создаем объект SpawEditor. В качестве параметров
        // передаются свойства элемента.
        $spaw = new SpawEditor($element->getName(),
                               $element->getValue(),
                               $element->getAttrib('language'),
                               $element->getAttrib('toolbarset'),
                               $element->getAttrib('theme'),
                               $element->getAttrib('width'),
                               $element->getAttrib('height'),
                               $element->getAttrib('stylesheet'),
                               $element->getLabel());
        // А это - небольшая хитрость. Дело в том, что конструктору
        // SpawEditor нельзя передать один параметр - направление письма.
        // Поэтому мы создадим страницу редактора с тем же именем.
        $page = new SpawEditorPage($element->getName(),
                                   $element->getLabel(),
                                   $element->getValue(),
                                   $element->getAttrib('direction'));
        $spaw->addPage($page);
       
        // Дальше следует проверка наличия дополнительных свойств,
        // и их установка для текущего объекта редактора
        if ($element->getAttrib('hideModeStrip')) {
            $spaw->hideModeStrip();
        }
        if ($element->getAttrib('hideStatusBar')) {
            $spaw->hideStatusBar();
        }
        if ($element->getAttrib('setFloatingMode')) {
            $spaw->setFloatingMode();
        }
        if (count($element->getAttrib('setStaticConfigValue')) > 0) {
            foreach ($element->getAttrib('setStaticConfigValue') as $name => $value) {
                SpawConfig::setStaticConfigValue($name,$value);
            }
        }
        if (count($element->getAttrib('setConfigValue')) > 0) {
            foreach ($element->getAttrib('setConfigValue') as $name => $value) {
                $spaw->setConfigValue($name,$value);
            }
        }        
       
        if (is_array($element->getAttrib('toolbar'))) {
            $toolbars = $element->getAttrib('toolbar');
            $element->getAttrib('toolbar');
            foreach ($toolbars as $toolbar) {
                    $spaw->addToolbar($toolbar);
                }
        }
       
        // Настало время извлечь из хранилища объекты дополнительных
        // страниц, и добавить объекту редактора
        foreach (self::$pages as $page) {
            $spaw->addPage($page);
        }
       
        // А дальше следует вывод. Комментарии, надеюсь, излишни.
        $separator = $this->getSeparator();
        $placement = $this->getPlacement();
        $output = $spaw->getHtml();

        switch ($placement) {
            case (self::PREPEND):
                return $output . $separator . $content;
            case (self::APPEND):
            default:
                return $content . $separator . $output;
        }
    }
}

Пример использования

Понять принцип использования вам поможет следующий код:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// Создадим собственно объект формы.
$form = new Zend_Form();
// Теперь нужно создать элемент для дополнительной страницы редактора.
// В этом элементе будет содержаться перевод статьи на иврит.
// Создадим новый экземпляр Zend_Form_Element_Textarea,
// в качестве параметра конструктора передадим имя элемента
// формы - 'article_hebrew'
$page = new Zend_Form_Element_Textarea('article_hebrew');
// Установим для этой страницы Label
$page->setLabel('Hebrew translation');
// Следующие 2 строки применяют к элементу созданный нами
// декоратор My_Decorator_SpawPage
$page->addPrefixPath('My_Decorator', 'My/Decorator/', 'decorator');
$page->setDecorators(array('SpawPage'));
// При необходимости - указываем текущее значение элемента
// $page->setValue('Hebrew text');
// А теперь укажем направление текста - справа на лево.
$page->setOptions(array('direction' => 'rtl'));

// Теперь создадим основную страницу редактора с именем 'article'
$element = new Zend_Form_Element_Textarea('article');
// Установим Label
$element->setLabel('English article');
// Следующие 2 строки применяют к элементу декоратор My_Decorator_Spaw
$element->addPrefixPath('My_Decorator', 'My/Decorator/', 'decorator');
$element->setDecorators(array('Spaw'));
// При необходимости - указываем текущее значение элемента
// $element->setValue('111');
// Метод setOptions() устанавливает массив параметров для элемента.
// Эти параметры будут использованы для настройки объекта редактора.
// Больше информации об этих параметрах можно найти в документации по SPAW.
$element->setOptions(array('direction' => 'ltr',
                           'language' => 'ru',
                           'width' => '600px',
                           'height' => '400px',
                           'toolbar' => array('edit','font'),
                           'theme' => 'spaw2',
                           'stylesheet' => 'cust.css',
                           'hideModeStrip' => TRUE,
                           'setFloatingMode' => FALSE,
                           'hideStatusBar' => TRUE,
                           'setStaticConfigValue' => array('default_height' => '200px',
                                                           'default_width' => '200px'),
                           'setConfigValue' => array('default_height' => '500px',
                                                     'default_width' => '500px')));
// Вначале добавим в форму все дополнительные страницы редактора
$form->addElement($page);
// И лишь затем - основную страницу. Это связано с тем, что
// рендеринг элементов происходит в порядке их добавления.
$form->addElement($element);
// Теперь можно вывести форму или передать ее в скрипт вида либо на обработку.
echo $form->render();

Послесловие

Вероятно, я выбрал не очень «изящный» способ интеграции. Но он наиболее легко реализуем. Если у вас будут другие соображения, как подружить SPAW и Zend Framework, пишите каменты – обсудим!

Share

Автор: Кирилл Павлюков \\ Метки: , , , ,