<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:georss="http://www.georss.org/georss">
<channel>
<title>Статьи - ПафНутиЙ-Блог</title>
<link>https://pafnuty.name/</link>
<language>ru</language><item>
<title>Обновление BlockPro до версии 4.7</title>
<link>https://pafnuty.name/statyi/163-blockpro-47.html</link>
<pdalink>https://pafnuty.name/statyi/163-blockpro-47.html</pdalink>
<guid>https://pafnuty.name/statyi/163-blockpro-47.html</guid>
<pubDate>Fri, 08 Jan 2016 19:02:38 +0300</pubDate>
<category>index</category>

<enclosure url="http://pafnuty.name/uploads/posts/2016-01/1452268883_logo_site_w_version.png" type="image/png" />
<content:encoded><![CDATA[<img src="http://pafnuty.name/uploads/posts/2016-01/1452268883_logo_site_w_version.png" style="max-width:100%;" data-maxwidth="480" alt="Обновление BlockPro до версии 4.7"><br><br>В этом обновлении произведён рефакторинг модуля, реализована интеграция со сторонними сервисами по обработке картинок (tinypng и kraken) и добавлена возможность собственной, нестандартной фильтрации новостей.<br><br><h2>Требования к системе</h2><br><ul><li> Версия DLE: <b>10.2 и выше</b><br></li><li> Поддерживаемая кодировка: <b>UTF-8</b><br></li><li> Версия php: <b>5.4 и выше</b><br></li><li> Наличие IonCube Loader: <b>Да (v.5.0 и выше)</b> Модуль имеет привязку к домену. Его можно использовать на разных поддоменах, доменных зонах одного домена. На localhost активация лицензии не требуется.<br>Для проверки совместимости настроек сайта и модуля можно <a href="http://bp.pafnuty.name/bp_check.zip" target="_blank" rel="noopener external">скачать специальный скрипт</a> (распакуйте содержимое архива на сайт и запустите php-файл).<br></li></ul><br><a class="store-link" href="http://store.pafnuty.name/15-blockpro.html" title="Купить BlockPro" rel="external noopener">Купить модуль BlockPro</a><br>Перед покупкой обязательно <a href="http://bp.pafnuty.name/" target="_blank" rel="noopener external">ознакомьтесь с требованиями и лицензией на сайте</a>!<br><br><h2>Что нового в BlockPro 4.7</h2><br><ul><li> <b>Минимально необходимая версия php — 5.4.</b><br></li><li> <b>Минимально необходимая версия ionCube — 5.0.</b><br></li><li> Теперь модуль не зависит от настройки <b>short_open_tag</b>.<br></li><li> Настройки лицензии вынесены в админку модуля.<br></li><li> <b>Добавлена интеграция с сервисом tinyPNG.</b> (подробности ниже).<br></li><li> <b>Добавлена интеграция с сервисом Kraken.io</b> (подробности ниже).<br></li><li> Добавлен новый параметр <b>setFilter</b>. (подробности ниже).<br></li><li> Добавлен новый тег <b>{$totalCount}</b>, выводящий общее количество новостей в выборке. Если постраничная навигация включена - тег выводит общее кол-во новостей co всех страниц.<br></li><li> Обновлён шаблонизатор до актуальной версии.<br></li></ul><br><br><h3>Интеграция со сторонними сервисами по обработке картинок</h3><br>В этой версии модуля реализована интеграция с двумя отличными сервисами: и .<br>У каждого из них есть свои преимущества и недостатки, свои тарифные планы и лимиты на бесплатное использование.<br>Для использования сервисов следует предварительно получить API ключи. Ссылки на инструкции вы найдёте в админке модуля, на вкладке настроек.<br>Сам процесс получения картинок через сервисы очень прост — достаточно прописать нужный модификатор к контенту, из которого предполагается получение изображений. Допустим необходимо получить квадратную картинку из краткой новости с обрезкой лишнего, размером 150x150px. Для этого можно использовать три варианта кода:<br><br>1. Встроенный механизм ресайза:<br><pre><code>{$el.short_story|image:$noimage:'small':'1':'150':'':'crop'}</code></pre><br>2. Через tinyPNG:<br><pre><code>{$el.short_story|tinypng:$noimage:'small':'1':'150':'':'crop'}</code></pre><br>3. Через Kraken.io:<br><pre><code>{$el.short_story|kraken:$noimage:'small':'1':'150':'':'crop'}</code></pre><br><br>Как видно разница лишь в названии модификатора, остальной синтаксис передачи параметров не изменяется. Однако сервисы игнорируют указание качества картинки, а tinypng может использовать только следующие типы ресайза: portrait, landscape, auto, crop.<br><br>Так же следует учитывать, что первая генерация блока при использовании сервисов замедляет отображение страницы т.к. изображения отправляются на другие сервера, обрабатываются и возвращаются. Последующие генерации блоков будут быстрыми.<br><br>Код для вставки в шаблон по-прежнему можно получить на вкладке "Хелперы" в админке модуля.<br><br><h3>Собственные условия фильтрации, параметр setFilter</h3><br>Этот параметр ориентирован на опытных пользователей и находится в стадии бета-тестирования. Как правило собственная фильтрация необходима, когда в БД вносились изменения и нужно отобрать новости с учётом новых колонок.<br>Техническое описание параметра находится в разделе <a href="http://bp.pafnuty.name/documentation/#setFilter" target="_blank" rel="noopener external">документации к модулю</a>, поэтому предлагаю разобрать простой пример его использования.<br>Допустим в БД, в таблице новостей заведена новая колонка <b>season</b>, в которой хранится номер сезона некоего сериала. Необходимо выбрать новости (серии) четвёртого сезона из просматриваемой категории.<br>Строка подключения будет такой:<br><pre><code>&#123;include file="engine/modules/base/blockpro.php?catId=this&amp;sort=date&amp;fields=p.season&amp;setFilter=p.season|=|4"}</code></pre><br>Как видно в строку подключения мы прописали параметр <b>fields</b>, который добавляет в запрос выборку из нестандартной колонки <b>season</b>, а параметр <b>setFilter</b> добавит фильтрацию по нужному нам сезону.<br>Полный SQL-запрос будет таким:<br><pre><code>SELECT p.id, p.autor, p.date, p.short_story, p.full_story, p.xfields, p.title, p.category, p.alt_name, p.allow_comm, p.comm_num, p.fixed, p.allow_main, p.symbol, p.tags, e.news_read, e.allow_rate, e.rating, e.vote_num, e.votes, e.related_ids, e.view_edit, e.editdate, e.editor, e.reason, p.season FROM `dle_post` p LEFT JOIN `dle_post_extras` e ON (p.id=e.news_id) WHERE approve AND p.season = 4 ORDER BY fixed DESC, p.date DESC LIMIT 0, 10</code></pre><br><br>Таким образом теперь есть возможность выводить новости ещё более гибко, чем раньше!<br><br><h2>Исправление ошибок и улучшения</h2><br><ul><li> Исправлена ошибка логики построения запроса для отбора новостей в режиме вывода афишей.<br></li><li> Исправлено некорректное построение ЧПУ при третьем типе настроек в DLE.<br></li><li> Исправлена недостаточная фильтрация при отборе новостей по тегам.<br></li><li> Исправлено некорректное формирование ссылки на аватар пользователя в dle 10.6.<br></li><li> Исправлена некорректная выборка новостей при фильтрации по допполям с логикой OR.<br></li><li> Исправлена ошибка генерации предпросмотра блока в админке и создания виджета при использовании Memcache.<br></li><li> Исправлена mysql ошибка, при некоторых случаях выборки по автору новости.<br></li><li> Мелкие исправления, улучшения и доработки класса resize.php.<br></li><li> Улучшение модификатора <b>declination</b>, теперь отрицательные числа тоже корректно склоняются.<br></li><li> Исправлена ошибка с некорректной генерацией строки подключения при предпросмотре виджетов в админке<br></li><li> Рефакторинг, оптимизация и улучшение php кода модуля.<br></li></ul><br><br><br>Ещё раз хочу напомнить, что модуль работает только в кодировке UTF-8 и поддержка windows-1251 не планируется.<br><br>На этом пока всё. Обновиться можно , а купить модуль — <a href="http://store.pafnuty.name/15-blockpro.html" target="_blank" rel="noopener external">в магазинчике</a>.]]></content:encoded>
</item><item>
<title>Большое обновление BlockPro для DLE (v4.5)</title>
<link>https://pafnuty.name/statyi/158-blockpro-4-5.html</link>
<pdalink>https://pafnuty.name/statyi/158-blockpro-4-5.html</pdalink>
<guid>https://pafnuty.name/statyi/158-blockpro-4-5.html</guid>
<pubDate>Wed, 22 Apr 2015 00:01:43 +0300</pubDate>
<category>index</category>

<enclosure url="http://pafnuty.name/uploads/posts/2015-04/1429648647_00.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2015-04/thumbs/1429648682_06.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2015-04/thumbs/1429648640_01.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2015-04/thumbs/1429648659_02.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2015-04/thumbs/1429648646_03.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2015-04/thumbs/1429648673_04.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2015-04/thumbs/1429648623_05.png" type="image/png" />
<content:encoded><![CDATA[<div align="center"><img src="http://pafnuty.name/uploads/posts/2015-04/1429648647_00.png" style="max-width:100%;" data-maxwidth="480" alt="Большое обновление BlockPro для DLE (v4.5)"></div><br><br>В этом обновлении очень много приятных нововведений и улучшений, как функциональных, так и визуальных.<br>Новый установщик, улучшенный генератор строк подключения, режим афиши, RSS-ленты, вывод всех картинок новости, переменные для формирования кеша и многое другое.<br><br><h2>Требования к системе</h2><br><ul><li> Версия DLE: <b>10.2 и выше</b><br></li><li> Поддерживаемая кодировка: <b>UTF-8</b><br></li><li> Версия php: <b>5.3 и выше</b><br></li><li> Наличие IonCube Loader: <b>Да (v.4.4 и выше)</b> Модуль имеет привязку к домену. Его можно использовать на разных поддоменах, доменных зонах одного домена, а также на localhost без активации лицензии.<br>Для проверки совместимости настроек сайта и модуля можно <a href="http://bp.pafnuty.name/bp_check.zip" target="_blank" rel="noopener external">скачать специальный скрипт</a> (распакуйте содержимое архива на сайт и запустите php-файл).<br></li></ul><br><a class="store-link" href="http://store.pafnuty.name/15-blockpro.html" title="Купить BlockPro" rel="external noopener">Купить модуль BlockPro</a> (теперь всего 420р!)<br>Перед покупкой обязательно <a href="http://bp.pafnuty.name/" target="_blank" rel="noopener external">ознакомьтесь с требованиями и лицензией на сайте</a>!<br><br><h2>Для чего нужен модуль?</h2><br>Модуль предназначен для очень гибкого вывода новостей на сайте под управлением CMS DataLife Engine.<br>С ним Вы сможете воплотить жизнь практически любую свою задумку т.к. подобных аналогов по гибкости вывода контента на данный момент просто не существует.<br>Если вы сомневаетесь в необходимости использования модуля — ознакомьтесь с таблицей сравнения возможностей BlockPro и custom:<br><div align="center"><a href="http://pafnuty.name/uploads/posts/2015-04/1429648682_06.png" class="highslide" target="_blank" rel="noopener external"><img src="http://pafnuty.name/uploads/posts/2015-04/thumbs/1429648682_06.png" style="max-width:100%;" alt=""></a></div><br> <br><h2>Что нового в BlockPro 4.5</h2><br>Как показала практика, в большинстве случаев модуль устанавливается на один сайт. <b>Поэтому <i>для одного доменного имени</i> модуль будет стоить всего 420р, а не 840р</b> (домены в разных доменных зонах и поддомены не требуют дополнительных лицензий). Кроме того, все, кто уже купил модуль по старой цене — автоматически получат по 3 ключа. Если вам нужно более 5 ключей – не отчаивайтесь! Вопрос скидок решается индивидуально.<br><br><b>Так же, начиная с данной версии, модуль поставляется в частично закодированном виде и с привязкой к домену.</b> Закодированы только файлы, имеющие ключевой функционал. На этот шаг пришлось пойти т.к., к сожалению, в последнее время участились случаи нелегального распространения модуля.<br><br><h2>Немного скриншотов:</h2><br>Новый генератор строк подключения<br><div align="center"><a href="http://pafnuty.name/uploads/posts/2015-04/1429648640_01.png" class="highslide" target="_blank" rel="noopener external"><img src="http://pafnuty.name/uploads/posts/2015-04/thumbs/1429648640_01.png" style="max-width:100%;" alt=""></a></div><br><br>Готовая строка подключения<br><div align="center"><a href="http://pafnuty.name/uploads/posts/2015-04/1429648659_02.png" class="highslide" target="_blank" rel="noopener external"><img src="http://pafnuty.name/uploads/posts/2015-04/thumbs/1429648659_02.png" style="max-width:100%;" alt=""></a></div><br><br>Форма создания виджета<br><div align="center"><a href="http://pafnuty.name/uploads/posts/2015-04/1429648646_03.png" class="highslide" target="_blank" rel="noopener external"><img src="http://pafnuty.name/uploads/posts/2015-04/thumbs/1429648646_03.png" style="max-width:100%;" alt=""></a></div><br><div align="center"><a href="http://pafnuty.name/uploads/posts/2015-04/1429648673_04.png" class="highslide" target="_blank" rel="noopener external"><img src="http://pafnuty.name/uploads/posts/2015-04/thumbs/1429648673_04.png" style="max-width:100%;" alt=""></a></div><br><br>Список виджетов<br><div align="center"><a href="http://pafnuty.name/uploads/posts/2015-04/1429648623_05.png" class="highslide" target="_blank" rel="noopener external"><img src="http://pafnuty.name/uploads/posts/2015-04/thumbs/1429648623_05.png" style="max-width:100%;" alt=""></a></div><br><br><h2>Исправление ошибок:</h2><br><ul><li> Исправлена ошибка с <b>xfields NOT LIKE LIKE</b> в запросе на фильтрацию исключающих значений допполей.<br></li><li> Исправлена ошибка с выводом новостей, не принадлежащих указанному временному интервалу. Пример ошибки: новость, опубликованная вчера попадала в выборку по параметрам <b>&amp;day=1</b>. Теперь день "начинается" корректно. Можно вводить новости, опубликованные только в указанные интервалы дней. Вчерашняя новость не попадёт в сегодняшнюю выборку, а сегодняшняя не попадёт во вчерашнюю.<br></li><li> Исправлена ошибка с ненужным показом постраничной навигации, когда новостей меньше, чем на две страницы.<br></li><li> Исправлена ошибка генерации уменьшенной копии изображения, если не указано качество создаваемой копии.<br></li><li> Исправлена ошибка с некорректной генерацией строки подключения в некоторых случаях. Вместо символа <b>&amp;</b> вставлялся символ <b>¬</b> и удалялась часть текста строки подключения, что приводило к некорректному выводу контента через модуль.<br></li><li> Исправлена ошибка с выводом прикрепленных файлов в новости.<br></li><li> Исправлена потенциальная ошибка с выводом скрытого текста.<br></li><li> Исправлена ошибка генерации предпросмотра блока в админке при использовании memcache.<br></li><li> Исправлена потенциальная ошибка в DLE 10.4 при сортировке типа "Хит" и попадании в выборку новостей с отрицательным рейтингом.<br></li><li> Исправлена ошибка с отображением админки, если сайт установлен в подпапку.<br></li><li> Устранено возникновение ошибок MySQL при некоторых сочетаниях параметров вывода новостей.<br></li><li> Исправлен мелкий баг с отображением старых стилей и скриптов при обновлении модуля, если используется кеширование фронтенда сторонними скриптами (например pagespeed for nginx).<br></li></ul><br><br><h2>Улучшения:</h2><br><ul><li> Добавлена поддержка нового рейтинга 10.4.<br></li><li> Обновлён шаблонизатор Fenom до актуальной версии. Теперь шаблонизатор работает ещё быстрее, а возможностей вывода контента стало больше.<br></li><li> <b>Добавлен шаблон модуля с кучей примеров вывода данных.</b> И подробными комментариями о том, что там вообще происходит. Очень пригодится тем, кто работает с модулем впервые. <pre><code>&#123;THEME}/blockpro/fullexample.tpl</code></pre><br></li><li> Добавлен учёт настроек DLE по публикации новостей на не наступившую дату. Теперь модуль будет выводить новости, дата которых не наступила, если это разрешено в настройках DLE.<br></li><li> Теперь при AJAX-запросе, если страница уже закеширована, будет отдан 304 статус, что значительно ускоряет отображение результата и снижает нагрузку на хостинг.<br></li><li> <b>[beta]</b> Добавлена поддержка навигации по стрелкам браузера при ajax-навигации в блоке с модулем. А так же при обновлении страницы, если пользователь находился на определённой странице в навигации блока, будет загружена эта страница. Пока такая навигация введена экспериментально и корректно работает только для одного блока на странице (или одного из блоков, если их много).<br></li><li> <b>Добавлен режим афиши: вывод новостей только на не наступившую дату.</b> При этом параметры <b>&amp;day</b> и <b>&amp;dayCoun</b>t не вычитают, а прибавляют дни. Например для вывода афиши на послезавтра нужно прописать <b>?future=y&amp;day=3&amp;dayCount=1</b>. Режим афиши не зависит от настроек DLE по выводу новостей на не наступившую дату.<br></li><li> Добавлен шаблон-пример с группировкой новостей по дате <b>group.tpl</b>. На основе этого шаблона можно делать практически любую доступную группировку. Шаблон снабжен подробными комментариями.<br></li><li> Добавлены теги <b>{$el.rating&#111;nclickPlus}</b> и <b>{$el.rating&#111;nclickMinus}</b> для вывода атрибута &#111;nclick для повышения и понижения рейтинга в цифровом варианте настроек.<br></li><li> Добавлен новый тег <b>{$dleConfig}</b> для доступа к конфигу DLE внутри шаблона. Использовать можно так: <b>{if $dleConfig.rating_type == '2'}Тип рейтинга: 2{/if}</b>.<br></li><li> Добавлен новый тег <b>{$dleModule}</b> для доступа к переменной внутри шаблона модуля.<br></li><li> Добавлен новый тег <b>{$lang}</b> для доступа к языковым фразам внутри шаблона. Этот тег будет полезен, если один шаблон используется для нескольких языков сайта.<br></li><li> Добавлена новая переменная <b>cacheVars</b>, позволяющая указать значимые параметры для формирования кеша. В переменную можно передавать через запятую ключи, доступные через <b>$_REQUEST</b>, или значения переменной <b>$dle_module</b>. Эта функция полезна, если требуется выводить разное оформление в блоке с новостями для разных страниц сайта. Таким образом, если прописать <b>&amp;caceVars=newsid,category,forum</b>, будет создаваться отдельный кеш для каждой новости, каждой категории и модуля forum (при интеграции форума).<br></li><li> Добавлена сортировка новостей по количеству скачиваний прикрепленного файла или файлов <b>&amp;sort=download</b>.<br></li><li> Добавлена сортировка новостей по дате редактирования <b>&amp;sort=editdate</b>.<br></li><li> Добавлена сортировка новостей по символьному коду <b>&amp;sort=symbol</b>.<br></li><li> Добавлена фильтрация новостей по символьному коду. Использование: <b>&amp;symbols=a,A</b> для вывода новостей, содержащих указанные символьные коды. Или &amp;symbols=this для вывода новостей текущего символьного каталога. <b>&amp;notSymbols=Б,г,V,v</b> для игнорирования новостей, содержащих указанные символьные коды. Или <b>&amp;notSymbols=Б,г,V,v</b> для игнорирования новостей текущего символьного каталога. <br></li><li> Добавлена сортировка по значению дополнительного поля. Для этого необходимо указывать <b>&amp;sort=xf|fieldname</b>, где <b>fieldname</b> — название дополнительного поля. Такая сортировка будет полезна при выводе каталога товаров (по цене) или фильмов по годам производства. Пример строки подключения<br><pre><code>&#123;include file="engine/modules/base/blockpro.php?xfilter=price&amp;amp;sort=xf|price&amp;amp;order=new"}</code></pre><br>где <b>xfilter=price</b> прописано для отбрасывания пустых значений.<br></li><li> Добавлен новый параметр <b>&amp;xfSortType=string</b>. Его необходимо указывать, если требуется сортировка по значению дополнительного поля, при этом указанное допполе содержит текст, а не цифры.<br></li><li> Добавлен новый модификатор dump, для реализации вывода переменной через php-функцию <b>print_r</b>. <b>{$el|dump}</b>, который выведет распечатку массива новости. Модификатор очень полезен при отладке вывода контента.<br></li><li> Переработан функционал получения картинок в шаблоне. Теперь можно получать массив всех картинок. Для этого третьим параметром нужно передавать не номер получаемой картинки, а cтроку all. При этом будут действовать все настройки ресайза и типов картинок. Такая возможность очень полезна для вывода галереи из картинок новости (или из дополнительного поля).<br></li><li> Если при получении картинки попадётся смайлик или спойлер - будет взята следующая картинка (раньше возвращалась заглушка).<br></li><li> Добавлена возможность получать картинку без обработки. Такая возможность нужна, если исходная картинка уже соответствует требованиям вывода, и дополнительные действия над ней производить не требуется. Получение такой картинки похоже на получение оригинала, однако первым параметром необходимо указывать intext. Пример: <pre><code>{$el.full_story|image:$noimage:'intext':'2'}</code></pre> или <pre><code>{$el.full_story|image:$noimage:'intext':'all'}</code></pre> для получения массива всех картинок без обработки.<br></li><li> Добавлена возможность указывать собственную папку для сохранения обработанных картинок. Для этого последним параметром необходимо передать имя папки, начинающееся со слеша. В этом случаи путь будет построен от корня сайта. Пример:<br><pre><code>{$el.full_story|image:$noimage:'small':'all':'150':'85':'crop':true:true:'/uploads/myfolder/'}</code></pre><br>Все картинки из новости будут уменьшены до 150px и сложены в папку /uploads/myfolder/150/. Папка создаётся автоматически.<br></li><li> Заменён тип отправки формы генератора в админке, с post на get. Теперь при обновлении страницы через F5 форма генерации строки подключения сбрасываться не будет. <br></li><li> Переработана структура формы генератора строки подключения, теперь она более удобная и логичная. <br></li><li> При генерации строки подключения больше не будут выводиться значения по умолчанию. Строка стала более понятная и короткая.<br></li><li> Добавлена возможность выводить контент модуля на стороннем сайте (виджет). Для этого необходимо сгенерировать строку подключения в админке и на вкладке просмотра результатов нажать на кнопку "Создать виджет". Далее следовать указаниям. <br></li><li> Реализована асинхронная загрузка виджета, так что можно не опасаться за подвисание страницы из-за того, что сайт-донор виджета упал или работает очень медленно.<br></li><li> Добавлен шаблон-пример для корректного вывода контента на стороннем сайте. Шаблон снабжен подробными комментариями, так что проблем с перенастройкой вывода не будет.<br></li><li> Добавлена возможность вывода RSS-ленты через модуль. Теперь можно настроить RSS канал в точности с вашими желаниями. Весь функционал модуля полностью доступен и для RSS.<br></li><li> Добавлен типовой шаблон для вывода RSS-ленты. Шаблон снабжен подробными комментариями, так что проблем с перенастройкой вывода не будет.<br></li><li> Увеличена скорость выборки с фильтрацией по ID новостей и по категориям (если не используются мультикатегории).<br></li><li> Значительно увеличена скорость выборки случайных новостей на больших БД (проверено на БД с 20 000 + новостями, скорость выполнения запроса возросла c 0.3cек до 0.005cек). Для этого введён новый параметр сортировки <b>&amp;sort=randomLight</b>.<br></li><li> Добавлен новый параметр <b>&amp;order=asis</b> — сортировка "как есть". На данный момент она распространяется только на ID новостей. Такая сортировка может понадобиться, если требуется вывести новости в строго определённом порядке по их ID. Для вывода следует добавить в строку подключения <b>&amp;order=asis&amp;postId=1,5,6,15</b>. В этом случае новости выведутся в том порядке, в котором заданы в параметре postId.<br></li><li> Добавлен новый параметр <b>&amp;saveRelated=y</b> — возможность записывать похожие новости в БД (по аналогии со стандартным функционалом). По умолчанию параметр отключен. Рекомендуется включать для ускорения работы модуля, особенно на больших БД. Похожие новости при включенной настройке будут выведены в том порядке, в котором они были записаны в БД (по умолчанию в DLE похожие новости сортируются по id).<br></li><li> Обновлён установщик модуля. Теперь он тоже адаптивный и выглядит более современно и удобно.<br></li></ul><br><br>Ещё раз хочу напомнить, что модуль работает только в кодировке UTF-8 и поддержка windows-1251 не планируется.<br><br>На этом пока всё. Обновиться можно , а купить модуль — <a href="http://store.pafnuty.name/15-blockpro.html" target="_blank" rel="noopener external">в магазинчике</a>.]]></content:encoded>
</item><item>
<title>Как защитить свой php-скрипт и привязать его к домену на примере модуля для DLE</title>
<link>https://pafnuty.name/statyi/157-pcp-cs.html</link>
<pdalink>https://pafnuty.name/statyi/157-pcp-cs.html</pdalink>
<guid>https://pafnuty.name/statyi/157-pcp-cs.html</guid>
<pubDate>Fri, 13 Feb 2015 22:46:21 +0300</pubDate>
<category>index</category>

<enclosure url="http://pafnuty.name/uploads/posts/2015-02/1423855416_logo.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2015-02/thumbs/1423855498_pcp-cs.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2015-02/thumbs/1423855450_1.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2015-02/thumbs/1423855497_2.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2015-02/thumbs/1423855452_3.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2015-02/thumbs/1423855504_4.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2015-02/thumbs/1423855443_5.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2015-02/1423855522_7.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2015-02/1423855451_8.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2015-02/1423855460_9.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2015-02/1423855462_10.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2015-02/thumbs/1423855457_11.png" type="image/png" />
<content:encoded><![CDATA[<div align="center"><img src="http://pafnuty.name/uploads/posts/2015-02/1423855416_logo.png" style="max-width:100%;" data-maxwidth="480" alt="Как защитить свой php-скрипт и привязать его к домену на примере модуля для DLE"></div><br><br>Ни для кого не секрет, что вопрос защиты своего php-скрипта рано или поздно встаёт перед любым разработчиком и сегодня я покажу как просто и эффективно защитить свой скрипт на практическом примере - на простеньком модуле для DLE.<br><br>Как то давно я навскидку написал простой модуль вывода информации об аттачменте в любом месте сайта - <a href="https://github.com/pafnuty/showattach" rel="external noopener">ShowAttach</a>, но до ума модуль не довёл и публиковать его не стал. Вот этот модуль и возьмём за основу для защиты.<br>Так же нам понадобятся прямые руки и система <a href="https://github.com/Mofsy/pcp-cs" rel="external noopener">PCP-CS</a> от <a href="http://mofsy.ru" target="_blank" rel="noopener external">Олега Mofsy</a>.<br><br><h2>Что такое pcp-cs</h2><br><b>PCP-CS — PHP Code Protect Client-Server</b>. Другими словами клиент-серверное приложение для привязки скриптов к определенным ограничениям (домен, ip сервера и т.д.).<br><br><h2>Как работает?</h2><br>Очень упрощенная схема выглядит так:<br><div align="center"><a href="http://pafnuty.name/uploads/posts/2015-02/1423855498_pcp-cs.png" class="highslide" target="_blank" rel="noopener external"><img src="http://pafnuty.name/uploads/posts/2015-02/thumbs/1423855498_pcp-cs.png" style="max-width:100%;" alt=""></a></div><br><br><h2>Ключевые преимущества</h2><br><ul><li> <b>Лёгкая интеграция в нужный скрипт.</b> Достаточно вставить код клиента в скрипт и прописать необходимые параметры<br></li><li> <b>Встроенная админка для управления серверной частью.</b> Система имеет встроенную административную часть для управления лицензиями, просмотра логов и т.п.<br></li><li> <b>Продуманная реализация проверок.</b> Проверки лицензии осуществляются сначала из локального ключа, и если период проверки истёк - данные запрашиваются с сервера проверки, и если сервер проверки недоступен - защищаемый скрипт не перестанет функционировать в течении заданного периода времени.<br></li></ul><br><br><h2>Установка и настройка серверной части PCP-CS</h2><br>Т.к. на момент написания статьи встроенная админка системы не достаточно функциональна, я буду использовать <a href="https://github.com/pafnuty/pcp-cs-admin" rel="external noopener">стороннюю админку</a>, написанную специально для этой системы.<br>Настройку можно производить как на хостинге, так и на локальной машине. Для локалки рекомендую OpenServer.<br><br><ol type="1"><li> Скачиваем с гитхаба админку по кнопке <b>"Download ZIP"</b>. Она уже содержит последнюю версию серверной части pcp-cs, так что на данном этапе больше ничего не потребуется.<br></li><li> Распаковываем папку upload в корень.<br></li><li> Выполняем запрос из <b>sql.sql</b>. Если необходимо сразу завести пользователя, то выполняем запрос:<br><pre><code>INSERT INTO `pcp_users` (`user_id`, `email`, `password`, `name`, `user_group`) VALUES (1, 'admin@admin.ru', 'c3284d0f94606de1fd2af172aba15bf3', 'admin', 1);</code></pre> логин и пароль в этом случаи будут admin, admin<br></li><li> Настраиваем конфиг в трёх файлах: <br><b>admin/config/db_config.php</b> - Конфиг БД<br><b>admin/config/config.php</b> - Конфиг админки<br><b>api/config.php</b> - Конфиг pcp-cs<br>Такое разделение обусловлено независимостью админки от серверной части pcp-cs.<br></li><li> Всё! Адинка готова к работе. Можно приступать к защите подопытного модуля.<br><div align="center"><a href="http://pafnuty.name/uploads/posts/2015-02/1423855450_1.png" class="highslide" target="_blank" rel="noopener external"><img src="http://pafnuty.name/uploads/posts/2015-02/thumbs/1423855450_1.png" style="max-width:100%;" alt=""></a></div><br></li></ol><br><br><h2>Внедрение клиентской части</h2><br>Прежде всего необходимо настроить серверную часть. <br>Для этого создаём новый метод проверки данных. <br><div align="center"><a href="http://pafnuty.name/uploads/posts/2015-02/1423855497_2.png" class="highslide" target="_blank" rel="noopener external"><img src="http://pafnuty.name/uploads/posts/2015-02/thumbs/1423855497_2.png" style="max-width:100%;" alt=""></a></div><br><div align="center"><a href="http://pafnuty.name/uploads/posts/2015-02/1423855452_3.png" class="highslide" target="_blank" rel="noopener external"><img src="http://pafnuty.name/uploads/posts/2015-02/thumbs/1423855452_3.png" style="max-width:100%;" alt=""></a></div><br><br>после успешного создания метода идём в раздел "Лицензии" и добавляе новый лицензионный ключ.<br><div align="center"><a href="http://pafnuty.name/uploads/posts/2015-02/1423855504_4.png" class="highslide" target="_blank" rel="noopener external"><img src="http://pafnuty.name/uploads/posts/2015-02/thumbs/1423855504_4.png" style="max-width:100%;" alt=""></a></div><br><div align="center"><a href="http://pafnuty.name/uploads/posts/2015-02/1423855443_5.png" class="highslide" target="_blank" rel="noopener external"><img src="http://pafnuty.name/uploads/posts/2015-02/thumbs/1423855443_5.png" style="max-width:100%;" alt=""></a></div><br><br>В результате получаем лицезионный ключ, который в дальнейшем можно отдавать покупателю модуля.<br><br><b>Вот теперь можно внедрять непосредственно код клиента!</b><br><br>Сейчас наш подопытный образец модуля (кстати не рекомендую его использовать на живых проетах т.к. он не доработан как следует и будет вызывать повышение нагрузки на бд) выглядит так:<br><div class="dle-spoiler noncontenteditable"><div class="title_spoiler"><a href="#" data-id="sp8f20d0377f7878f1e6" class="spoiler-btn-svg"><svg width="18" height="18" fill="currentColor" viewBox="0 0 20 20"><path id="svg-sp8f20d0377f7878f1e6" d="M17.418 6.109c0.272-0.268 0.709-0.268 0.979 0s0.271 0.701 0 0.969l-7.908 7.83c-0.27 0.268-0.707 0.268-0.979 0l-7.908-7.83c-0.27-0.268-0.27-0.701 0-0.969s0.709-0.268 0.979 0l7.419 7.141 7.418-7.141z"></path></svg></a><a href="#" data-id="sp8f20d0377f7878f1e6" class="spoiler-btn contenteditable">Show Attach</a></div><div id="sp8f20d0377f7878f1e6" class="text_spoiler"><div class="contenteditable"><pre><code>&lt;?php<br>/*<br>=============================================================================<br>Show Attach - модуль вывода информации об аттачменте для для DLE<br>=============================================================================<br>Автор: ПафНутиЙ<br>URL: http://pafnuty.name/<br>twitter: https://twitter.com/pafnuty_name<br>google+: http://gplus.to/pafnuty<br>email: pafnuty10@gmail.com<br>=============================================================================<br> */<br><br>if (!defined('DATALIFEENGINE')) {<br> die("Go fuck yourself!");<br>}<br><br>$cfg = array(<br> 'newsId' =&gt; !empty($news_id) ? (int) $news_id : false,<br> 'template' =&gt; !empty($template) ? $template : 'showattach/default',<br> 'multiple' =&gt; !empty($multiple) ? true : false,<br>);<br>if ($cfg['newsId']) {<br><br> // Запрос в БД<br> $row = $db-&gt;super_query("SELECT id, news_id, name, onserver, date, dcount FROM " . PREFIX . "_files WHERE news_id = '" . $cfg['newsId'] . "'", $cfg['multiple']);<br> $tpl-&gt;load_template($cfg['template'] . '.tpl');<br> if (!$cfg['multiple']) {<br> $rows[] = $row;<br> } else {<br> $rows = $row;<br> }<br><br> foreach ($rows as $attachItem) {<br> if ($rows[0]['id']) {<br> $onserver = $attachItem['onserver'];<br> $md5 = md5_file(ROOT_DIR . '/uploads/files/' . $onserver);<br> $size = formatsize(@filesize(ROOT_DIR . '/uploads/files/' . $onserver));<br><br> $tpl-&gt;set('{attach_id}', $attachItem['id']);<br> $tpl-&gt;set('{attach_name}', $attachItem['name']);<br> $tpl-&gt;set('{attach_size}', $size);<br> $tpl-&gt;set('{attach_md5}', $md5);<br><br> $rowDate = $attachItem['date'];<br> if (date('Ymd', $rowDate) == date('Ymd', $_TIME)) {<br> $tpl-&gt;set('{attach_date}', $lang['time_heute'] . langdate(", H:i", $rowDate));<br> } elseif (date('Ymd', $rowDate) == date('Ymd', ($_TIME - 86400))) {<br> $tpl-&gt;set('{attach_date}', $lang['time_gestern'] . langdate(", H:i", $rowDate));<br> } else {<br> $tpl-&gt;set('{attach_date}', langdate($config['timestamp_active'], $rowDate));<br> }<br><br> $tpl-&gt;copy_template = preg_replace_callback("#{attach_date=(.+?)}#i", "formdate", $tpl-&gt;copy_template);<br><br> $tpl-&gt;set('{attach_download}', $attachItem['dcount']);<br><br> $tpl-&gt;set('[attach]', "");<br> $tpl-&gt;set('[/attach]', "");<br> $tpl-&gt;set_block("'[not-attach](.*?)[/not-attach]'si", "");<br><br> if ($user_group[$member_id['user_group']]['allow_files']) {<br> $tpl-&gt;set('[allow-attach]', "");<br> $tpl-&gt;set('[/allow-attach]', "");<br> $tpl-&gt;set_block("'[not-allow-attach](.*?)[/not-allow-attach]'si", "");<br> } else {<br> $tpl-&gt;set('[not-allow-attach]', "");<br> $tpl-&gt;set('[/not-allow-attach]', "");<br> $tpl-&gt;set_block("'[allow-attach](.*?)[/allow-attach]'si", "");<br> }<br><br> } else {<br> $tpl-&gt;set_block("'[attach](.*?)[/attach]'si", "");<br> $tpl-&gt;set('[not-attach]', "");<br> $tpl-&gt;set('[/not-attach]', "");<br> }<br><br> $tpl-&gt;compile('showAttach');<br> }<br><br> $showAttach = $tpl-&gt;result['showAttach'];<br><br> $tpl-&gt;clear();<br><br> // Выводим результат работы модуля<br> echo $showAttach;<br>}<br>?&gt;<br></code></pre></div></div></div><br><br>Вот такой внешний вид вывода данных этим модулем:<br><div align="center"><img src="http://pafnuty.name/uploads/posts/2015-02/1423855522_7.png" style="max-width:100%;" data-maxwidth="480" alt=""></div><br><br>Нам необходимо заблокировать работу скрипта, если он используется без лицензии.<br><br>Для начала добавим в конфиг модуля параметр <b>key</b>, в который будем передавать полученный лицензионный ключ.<br><pre><code>'key' =&gt; !empty($key) ? $key : false,</code></pre><br>и завернём рабочий код в условие с проверкой этого параметра, а так же добавим отдельное условие для вывода сообщения об отсутствии ключа.<br><pre><code>// Если есть ключ и задан ID новости — работаем<br>if ($cfg['newsId'] &amp;&amp; $key) {<br>......<br>} elseif (!$key) {<br> $showAttach = '&lt;span style="color: red;"&gt;Не указан лицензионный ключ.&lt;/span&gt;';<br>}<br>echo $showAttach;</code></pre><br>результат:<br><div align="center"><img src="http://pafnuty.name/uploads/posts/2015-02/1423855451_8.png" style="max-width:100%;" data-maxwidth="480" alt=""></div><br><br>Добавляем в строку подключения ключ и проверяем:<br><div align="center"><img src="http://pafnuty.name/uploads/posts/2015-02/1423855460_9.png" style="max-width:100%;" data-maxwidth="480" alt=""></div><br><br>Теперь нужно добавить проверку введённого ключа с помощью pcp-cs.<br><ol type="1"><li> Внедряем код клиента в наш модуль. Для экономии ресурсов рекомендую сжать код <a href="https://raw.githubusercontent.com/Mofsy/pcp-cs/master/client/client.class.php" rel="external noopener">клиентского класса</a> через любой удобный инструмент. Я использовал сервис . Вставляем полученный код перед основным кодом модуля. Если при проверке выдаёт ошибку:<br><pre><code>Fatal error: Namespace declaration statement has to be the very first statement in the script in</code></pre> <br>просто удалите этот код:<br><pre><code>namespace MofsyLicenseClient;</code></pre><br></li><li> Сразу после кода клиентского класса можно его задействовать и произвести проверку.<br>В комментарияк к коду ниже я расписал что и как происходит.<br><pre><code>// Теперь можно запустить проверку лицензии<br>$protect = new Protect();<br><br>// Секретный ключ из созданного метода проверки<br>$protect-&gt;secret_key = 'mqgAqWnSwZZM8YX7BEd9'; <br><br>// Куда будем класть локальный (зашифрованный) ключ?<br>$protect-&gt;local_key_path = ENGINE_DIR . '/data/';<br><br>// Имя и расширение локального ключа <br>$protect-&gt;local_key_name = 'showattach.lic';<br><br>// Адрес сервера проверки<br>$protect-&gt;server = 'http://pcp-cs.loc/api.php';<br><br>// Дата релиза модуля (пригодится если лицензия даётся на определённую версию модуля)<br>$protect-&gt;release_date = '2015-02-10';<br><br>// Ключ активации (лицензионный ключ), тот, который передаётся в параметрах строки подключения модуля<br>$protect-&gt;activation_key = $key;<br><br>// Сообщения о различных ошибках<br>$protect-&gt;status_messages = array(<br> 'status_1' =&gt; '&lt;span style="color:green;"&gt;Активна&lt;/span&gt;',<br> 'status_2' =&gt; '&lt;span style="color:darkblue;"&gt;Внимание&lt;/span&gt;: срок действия лицензии закончился.',<br> 'status_3' =&gt; '&lt;span style="color:orange;"&gt;Внимание&lt;/span&gt;: лицензия переиздана. Ожидает повторной активации.',<br> 'status_4' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: лицензия была приостановлена.',<br> 'localhost' =&gt; '&lt;span style="color:orange;"&gt;Активна на localhost&lt;/span&gt;: используется локальный компьютер, на реальном сервере произойдет активация, если вы правильно ввели лицензионный ключ активации в настройках.',<br> 'pending' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: лицензия ожидает рассмотрения.',<br> 'download_access_expired' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: ключ активации не подходит для установленной версии. Пожалуйста поставьте более старую версию продукта.',<br> 'missing_activation_key' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: ключ активации не указан.',<br> 'could_not_obtain_local_key' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: невозможно получить новый локальный ключ.',<br> 'maximum_delay_period_expired' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: льготный период локального ключа истек.',<br> 'local_key_tampering' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: локальный лицензионный ключ поврежден или не действителен.',<br> 'local_key_invalid_for_location' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: локальный ключ не подходит к данному окружению.',<br> 'missing_license_file' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: создайте следующий пустой файл и папки если их нету:&lt;br /&gt;',<br> 'license_file_not_writable' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: сделайте для записи следующие пути:&lt;br /&gt;',<br> 'invalid_local_key_storage' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: не возможно удалить старый локальный ключ.',<br> 'could_not_save_local_key' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: не возможно записать новый локальный ключ.',<br> 'activation_key_string_mismatch' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: локальный ключ не действителен для указанного ключа активации.'<br><br>);<br><br>// Запускаем валидацию лицензии<br>$protect-&gt;validate();<br><br>$license = false;<br><br>// Если истина, то лицензия в боевом состоянии и можно работать дальше.<br>if($protect-&gt;status) {<br> $license = true;<br>}</code></pre><br><br>Немного доработок в условиях:<br><pre><code>// Если есть ключ и задан ID новости и проверка лицензии прошла успешно — работаем<br>if ($cfg['newsId'] &amp;&amp; $key &amp;&amp; $license) {<br>....<br>} <br>if (!$key) {<br> // Если ключ не передан — надо бы сообщить об этом<br> $showAttach = '&lt;span style="color: red;"&gt;Не указан лицензионный ключ.&lt;/span&gt;';<br>} <br>if (!$license) {<br> // Если лицензия не проверилась - скжем об этом<br> $showAttach = (!$protect-&gt;errors) ? 'Ошибка лицензии.' : $protect-&gt;errors;<br>}<br><br>echo $showAttach;</code></pre><br><br>Если всё сделано правильно — файл с лицензией будет успешно создан в нужной папке, скрипт будет работать., а в админке PCP-CS появится запись о том, где активирован ключ.<br><div align="center"><img src="http://pafnuty.name/uploads/posts/2015-02/1423855462_10.png" style="max-width:100%;" data-maxwidth="480" alt=""></div><br><div align="center"><a href="http://pafnuty.name/uploads/posts/2015-02/1423855457_11.png" class="highslide" target="_blank" rel="noopener external"><img src="http://pafnuty.name/uploads/posts/2015-02/thumbs/1423855457_11.png" style="max-width:100%;" alt=""></a></div><br><br>Для проверки попробуйте изменить данные лицензионного ключа в строке подключения.<br></li></ol><br><br><b>Вот, собственно, и вся процедура реализации защиты модуля.</b> Остаётся только закодировать код модуля в IonCube и можно продавать или раздавать бесплатно.<br>Так же не забывайте, что лучше всего реализовать в модуле возможность кеширования и проверять лицензию при отсутствии кеша модуля, так экономятся ресурсы хостинга и ускоряется работа.<br>После всех действий код модуля должен выглядеть как то так:<br><div class="dle-spoiler noncontenteditable"><div class="title_spoiler"><a href="#" data-id="sp300c934d8f548ad5f0" class="spoiler-btn-svg"><svg width="18" height="18" fill="currentColor" viewBox="0 0 20 20"><path id="svg-sp300c934d8f548ad5f0" d="M17.418 6.109c0.272-0.268 0.709-0.268 0.979 0s0.271 0.701 0 0.969l-7.908 7.83c-0.27 0.268-0.707 0.268-0.979 0l-7.908-7.83c-0.27-0.268-0.27-0.701 0-0.969s0.709-0.268 0.979 0l7.419 7.141 7.418-7.141z"></path></svg></a><a href="#" data-id="sp300c934d8f548ad5f0" class="spoiler-btn contenteditable">Итоговый код</a></div><div id="sp300c934d8f548ad5f0" class="text_spoiler"><div class="contenteditable"><pre><code>&lt;?php<br>/*<br>=============================================================================<br>Show Attach - модуль вывода информации об аттачменте для для DLE<br>=============================================================================<br>Автор: ПафНутиЙ<br>URL: http://pafnuty.name/<br>twitter: https://twitter.com/pafnuty_name<br>google+: http://gplus.to/pafnuty<br>email: pafnuty10@gmail.com<br>=============================================================================<br> */<br><br>if (!defined('DATALIFEENGINE')) {<br> die("Go fuck yourself!");<br>}<br><br>$cfg = array(<br> 'newsId' =&gt; !empty($news_id) ? (int) $news_id : false,<br> 'template' =&gt; !empty($template) ? $template : 'showattach/default',<br> 'multiple' =&gt; !empty($multiple) ? true : false,<br> 'key' =&gt; !empty($key) ? $key : false, // Обязательный параметр ключ<br>);<br>// Клиентский класс --&gt;<br> <br> class Protect{ public $status=false; public $errors=false; public $activation_key=''; public $activation_key_expires; public $secret_key='fdfbLhlLgnJDKJklblngkk6krtkghm565678kl78klkUUHtvdfdoghphj'; public $server=''; public $remote_port=80; public $remote_timeout=20; public $local_ua='PHP code protect'; public $use_localhost=false; public $use_expires=true; public $local_key_storage='filesystem'; public $local_key_path='./'; public $local_key_name='license.lic'; public $local_key_transport_order='scf'; public $local_key_delay_period=7; public $local_key_last; public $release_date='2014-10-24'; public $user_name=''; public $status_messages=array('status_1'=&gt;'This activation key is active.','status_2'=&gt;'Error: This activation key has expired.','status_3'=&gt;'Activation key republished. Awaiting reactivation.','status_4'=&gt;'Error: This activation key has been suspended.','localhost'=&gt;'This activation key is active (localhost).','pending'=&gt;'Error: This activation key is pending review.','download_access_expired'=&gt;'Error: This version of the software was released after your download access expired. Please downgrade software or contact support for more information.','missing_activation_key'=&gt;'Error: The activation key variable is empty.','could_not_obtain_local_key'=&gt;'Error: I could not obtain a new local key.','maximum_delay_period_expired'=&gt;'Error: The maximum local key delay period has expired.','local_key_tampering'=&gt;'Error: The local key has been tampered with or is invalid.','local_key_invalid_for_location'=&gt;'Error: The local key is invalid for this location.','missing_license_file'=&gt;'Error: Please create the following file (and directories if they dont exist already): ','license_file_not_writable'=&gt;'Error: Please make the following path writable: ','invalid_local_key_storage'=&gt;'Error: I could not determine the local key storage on clear.','could_not_save_local_key'=&gt;'Error: I could not save the local key.','activation_key_string_mismatch'=&gt;'Error: The local key is invalid for this activation key.'); private $trigger_delay_period; public function __construct(){} public function validate(){if($this-&gt;use_localhost&amp;&amp;$this-&gt;getIpLocal()&amp;&amp;$this-&gt;isWindows()&amp;&amp;!file_exists("{$this-&gt;local_key_path}{$this-&gt;local_key_name}")){$this-&gt;status=true;return $this-&gt;errors=$this-&gt;status_messages['localhost'];}if(!$this-&gt;activation_key){return $this-&gt;errors=$this-&gt;status_messages['missing_activation_key'];}switch($this-&gt;local_key_storage){case 'filesystem':$local_key=$this-&gt;readLocalKey();break;default:return $this-&gt;errors=$this-&gt;status_messages['missing_activation_key'];}$this-&gt;trigger_delay_period=$this-&gt;status_messages['could_not_obtain_local_key'];if($this-&gt;errors==$this-&gt;trigger_delay_period&amp;&amp;$this-&gt;local_key_delay_period){$delay=$this-&gt;processDelayPeriod($this-&gt;local_key_last);if($delay['write']){if($this-&gt;local_key_storage=='filesystem'){$this-&gt;writeLocalKey($delay['local_key'],"{$this-&gt;local_key_path}{$this-&gt;local_key_name}");}}if($delay['errors']){return $this-&gt;errors=$delay['errors'];}$this-&gt;errors=false;return $this;}if($this-&gt;errors){return $this-&gt;errors;}return $this-&gt;validateLocalKey($local_key);} private function calcMaxDelay($local_key_expires,$delay){return ((integer)$local_key_expires+((integer)$delay*86400));} private function processDelayPeriod($local_key){$local_key_src=$this-&gt;decodeLocalKey($local_key);$parts=$this-&gt;splitLocalKey($local_key_src);$key_data=unserialize($parts[0]);$local_key_expires=(integer)$key_data['local_key_expires'];unset($parts,$key_data);$write_new_key=false;$parts=explode("nn",$local_key);$local_key=$parts[0];foreach($local_key_delay_period=explode(',',$this-&gt;local_key_delay_period) as $key=&gt;$delay){if(!$key){$local_key.="n";}if($this-&gt;calcMaxDelay($local_key_expires,$delay)&gt;time()){continue;}$local_key.="n{$delay}";$write_new_key=true;}if(time()&gt;$this-&gt;calcMaxDelay($local_key_expires,array_pop($local_key_delay_period))){return array('write'=&gt;false,'local_key'=&gt;'','errors'=&gt;$this-&gt;status_messages['maximum_delay_period_expired']);}return array('write'=&gt;$write_new_key,'local_key'=&gt;$local_key,'errors'=&gt;false);} private function inDelayPeriod($local_key,$local_key_expires){$delay=$this-&gt;splitLocalKey($local_key,"nn");if(!isset($delay[1])){return -1;}return (integer)($this-&gt;calcMaxDelay($local_key_expires,array_pop(explode("n",$delay[1])))-time());} private function decodeLocalKey($local_key){return base64_decode(str_replace("n",'',urldecode($local_key)));} private function splitLocalKey($local_key,$token='{protect}'){return explode($token,$local_key);} private function validateAccess($key,$valid_accesses){return in_array($key,(array)$valid_accesses);} private function wildcardIp($key){$octets=explode('.',$key);array_pop($octets);$ip_range[]=implode('.',$octets).'.*';array_pop($octets);$ip_range[]=implode('.',$octets).'.*';array_pop($octets);$ip_range[]=implode('.',$octets).'.*';return $ip_range;} private function wildcardServerHostname($key){$hostname=explode('.',$key);unset($hostname[0]);$hostname=(!isset($hostname[1]))?array($key):$hostname;return '*.'.implode('.',$hostname);} private function extractAccessSet($instances,$enforce){foreach($instances as $key=&gt;$instance){if($key!=$enforce){continue;}return $instance;}return array();} private function validateLocalKey($local_key){$local_key_src=$this-&gt;decodeLocalKey($local_key);$parts=$this-&gt;splitLocalKey($local_key_src);if(!isset($parts[1])){return $this-&gt;errors=$this-&gt;status_messages['local_key_tampering'];}if(md5((string)$this-&gt;secret_key.(string)$parts[0])!=$parts[1]){return $this-&gt;errors=$this-&gt;status_messages['local_key_tampering'];}unset($this-&gt;secret_key);$key_data=unserialize($parts[0]);$instance=$key_data['instance'];unset($key_data['instance']);$enforce=$key_data['enforce'];unset($key_data['enforce']);$this-&gt;user_name=$key_data['user_name'];if((string)$key_data['activation_key_expires']=='never'){$this-&gt;activation_key_expires=0;}else {$this-&gt;activation_key_expires=(integer)$key_data['activation_key_expires'];}if((string)$key_data['activation_key']!=(string)$this-&gt;activation_key){return $this-&gt;errors=$this-&gt;status_messages['activation_key_string_mismatch'];}if((integer)$key_data['status']!=1&amp;&amp;(integer)$key_data['status']!=2){return $this-&gt;errors=$this-&gt;status_messages['status_'.$key_data['status']];}if($this-&gt;use_expires==false&amp;&amp;(string)$key_data['activation_key_expires']!='never'&amp;&amp;(integer)$key_data['activation_key_expires']&lt;time()){return $this-&gt;errors=$this-&gt;status_messages['status_2'];}if($this-&gt;use_expires==false&amp;&amp;(string)$key_data['local_key_expires']!='never'&amp;&amp;(integer)$key_data['local_key_expires']&lt;time()){if($this-&gt;inDelayPeriod($local_key,$key_data['local_key_expires'])&lt;0){$this-&gt;clearLocalKey();return $this-&gt;validate();}}if($this-&gt;use_expires==true&amp;&amp;(string)$key_data['activation_key_expires']!='never'&amp;&amp;(integer)$key_data['activation_key_expires']&lt;strtotime($this-&gt;release_date)){return $this-&gt;errors=$this-&gt;status_messages['download_access_expired'];}if($this-&gt;use_expires==true&amp;&amp;(string)$key_data['local_key_expires']!='never'&amp;&amp;(integer)$key_data['local_key_expires']&lt;time()&amp;&amp;(integer)$key_data['activation_key_expires']&gt;(integer)$key_data['local_key_expires']+604800){if($this-&gt;inDelayPeriod($local_key,$key_data['local_key_expires'])&lt;0){$this-&gt;clearLocalKey();return $this-&gt;validate();}}$conflicts=array();$access_details=$this-&gt;accessDetails();foreach((array)$enforce as $key){$valid_accesses=$this-&gt;extractAccessSet($instance,$key);if(!$this-&gt;validateAccess($access_details[$key],$valid_accesses)){$conflicts[$key]=true;if(in_array($key,array('ip','server_ip'))){foreach($this-&gt;wildcardIp($access_details[$key]) as $ip){if($this-&gt;validateAccess($ip,$valid_accesses)){unset($conflicts[$key]);break;}}}elseif(in_array($key,array('domain'))){if(isset($key_data['domain_wildcard'])){if($key_data['domain_wildcard']==1&amp;&amp;preg_match("/".$valid_accesses[0]."z/i",$access_details[$key])){$access_details[$key]='*.'.$valid_accesses[0];}if($key_data['domain_wildcard']==2){$exp_domain=explode('.',$valid_accesses[0]);$exp_domain=$exp_domain[0];if(preg_match("/".$exp_domain."/i",$access_details[$key])){$access_details[$key]='*.'.$valid_accesses[0].'.*';}}if($key_data['domain_wildcard']==3){$exp_domain=explode('.',$valid_accesses[0]);$exp_domain=$exp_domain[0];if(preg_match("/A".$exp_domain."/i",$access_details[$key])){$access_details[$key]=$valid_accesses[0].'.*';}}}if($this-&gt;validateAccess($access_details[$key],$valid_accesses)){unset($conflicts[$key]);}}elseif(in_array($key,array('server_hostname'))){if($this-&gt;validateAccess($this-&gt;wildcardServerHostname($access_details[$key]),$valid_accesses)){unset($conflicts[$key]);}}}}if(!empty($conflicts)){return $this-&gt;errors=$this-&gt;status_messages['local_key_invalid_for_location'];}$this-&gt;errors=$this-&gt;status_messages['status_1'];return $this-&gt;status=true;} public function readLocalKey(){if(!is_dir($this-&gt;local_key_path)){mkdir($this-&gt;local_key_path,0755,true);}if(!file_exists($path="{$this-&gt;local_key_path}{$this-&gt;local_key_name}")){$f=@fopen($path,'w');if(!$f){return $this-&gt;errors=$this-&gt;status_messages['missing_license_file'].$path;}else {fwrite($f,'');fclose($f);}}if(!is_writable($path)){@chmod($path,0777);if(!is_writable($path)){@chmod("$path",0755);if(!is_writable($path)){return $this-&gt;errors=$this-&gt;status_messages['license_file_not_writable'].$path;}}}if(!$local_key=@file_get_contents($path)){$local_key=$this-&gt;getServerLocalKey();if($this-&gt;errors){return $this-&gt;errors;}$this-&gt;writeLocalKey(urldecode($local_key),$path);}return $this-&gt;local_key_last=$local_key;} public function clearLocalKey(){if($this-&gt;local_key_storage=='filesystem'){$this-&gt;writeLocalKey('',"{$this-&gt;local_key_path}{$this-&gt;local_key_name}");}else {$this-&gt;errors=$this-&gt;status_messages['invalid_local_key_storage'];}} public function writeLocalKey($local_key,$path){$fp=@fopen($path,'w');if(!$fp){return $this-&gt;errors=$this-&gt;status_messages['could_not_save_local_key'];}@fwrite($fp,$local_key);@fclose($fp);return true;} private function getServerLocalKey(){$query_string='activation_key='.urlencode($this-&gt;activation_key).'&amp;';$query_string.=http_build_query($this-&gt;accessDetails());if($this-&gt;errors){return false;}$priority=$this-&gt;local_key_transport_order;$result=false;while(strlen($priority)){$use=substr($priority,0,1);if($use=='s'){if($result=$this-&gt;useFsockopen($this-&gt;server,$query_string)){break;}}if($use=='c'){if($result=$this-&gt;useCurl($this-&gt;server,$query_string)){break;}}if($use=='f'){if($result=$this-&gt;useFopen($this-&gt;server,$query_string)){break;}}$priority=substr($priority,1);}if(!$result){$this-&gt;errors=$this-&gt;status_messages['could_not_obtain_local_key'];return false;}if(substr($result,0,7)=='Invalid'){$this-&gt;errors=str_replace('Invalid','Error',$result);return false;}if(substr($result,0,5)=='Error'){$this-&gt;errors=$result;return false;}return $result;} private function accessDetails(){$access_details=array();if(function_exists('phpinfo')){ob_start();phpinfo();$phpinfo=ob_get_contents();ob_end_clean();$list=strip_tags($phpinfo);$access_details['domain']=$this-&gt;scrapePhpInfo($list,'HTTP_HOST');$access_details['ip']=$this-&gt;scrapePhpInfo($list,'SERVER_ADDR');$access_details['directory']=$this-&gt;scrapePhpInfo($list,'SCRIPT_FILENAME');$access_details['server_hostname']=$this-&gt;scrapePhpInfo($list,'System');$access_details['server_ip']=@gethostbyname($access_details['server_hostname']);}$access_details['domain']=($access_details['domain'])?$access_details['domain']:$_SERVER['HTTP_HOST'];$access_details['ip']=($access_details['ip'])?$access_details['ip']:$this-&gt;serverAddr();$access_details['directory']=($access_details['directory'])?$access_details['directory']:$this-&gt;pathTranslated();$access_details['server_hostname']=($access_details['server_hostname'])?$access_details['server_hostname']:@gethostbyaddr($access_details['ip']);$access_details['server_hostname']=($access_details['server_hostname'])?$access_details['server_hostname']:'Unknown';$access_details['server_ip']=($access_details['server_ip'])?$access_details['server_ip']:@gethostbyaddr($access_details['ip']);$access_details['server_ip']=($access_details['server_ip'])?$access_details['server_ip']:'Unknown';foreach($access_details as $key=&gt;$value){$access_details[$key]=($access_details[$key])?$access_details[$key]:'Unknown';}return $access_details;} private function pathTranslated(){$option=array('PATH_TRANSLATED','ORIG_PATH_TRANSLATED','SCRIPT_FILENAME','DOCUMENT_ROOT','APPL_PHYSICAL_PATH');foreach($option as $key){if(!isset($_SERVER[$key])||strlen(trim($_SERVER[$key]))&lt;=0){continue;}if($this-&gt;isWindows()&amp;&amp;strpos($_SERVER[$key],'')){return @substr($_SERVER[$key],0,@strrpos($_SERVER[$key],''));}return @substr($_SERVER[$key],0,@strrpos($_SERVER[$key],'/'));}return false;} private function serverAddr(){$options=array('SERVER_ADDR','LOCAL_ADDR');foreach($options as $key){if(isset($_SERVER[$key])){return $_SERVER[$key];}}return false;} private function scrapePhpInfo($all,$target){$all=explode($target,$all);if(count($all)&lt;2){return false;}$all=explode("n",$all[1]);$all=trim($all[0]);if($target=='System'){$all=explode(" ",$all);$all=trim($all[(strtolower($all[0])=='windows'&amp;&amp;strtolower($all[1])=='nt')?2:1]);}if($target=='SCRIPT_FILENAME'){$slash=($this-&gt;isWindows()?'':'/');$all=explode($slash,$all);array_pop($all);$all=implode($slash,$all);}if(substr($all,1,1)==']'){return false;}return $all;} private function useFsockopen($url,$query_string){if(!function_exists('fsockopen')){return false;}$url=parse_url($url);$fp=@fsockopen($url['host'],$this-&gt;remote_port,$errno,$errstr,$this-&gt;remote_timeout);if(!$fp){return false;}$header="POST {$url['path']} HTTP/1.0rn";$header.="Host: {$url['host']}rn";$header.="Content-type: application/x-www-form-urlencodedrn";$header.="User-Agent: ".$this-&gt;local_ua."rn";$header.="Content-length: ".@strlen($query_string)."rn";$header.="Connection: closernrn";$header.=$query_string;$result=false;fputs($fp,$header);while(!feof($fp)){$result.=fgets($fp,1024);}fclose($fp);if(strpos($result,'200')===false){return false;}$result=explode("rnrn",$result,2);if(!$result[1]){return false;}return $result[1];} private function useCurl($url,$query_string){if(!function_exists('curl_init')){return false;}$curl=curl_init();$header[0]="Accept: text/xml,application/xml,application/xhtml+xml,";$header[0].="text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";$header[]="Cache-Control: max-age=0";$header[]="Connection: keep-alive";$header[]="Keep-Alive: 300";$header[]="Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";$header[]="Accept-Language: en-us,en;q=0.5";$header[]="Pragma: ";curl_setopt($curl,CURLOPT_URL,$url);curl_setopt($curl,CURLOPT_USERAGENT,$this-&gt;local_ua);curl_setopt($curl,CURLOPT_HTTPHEADER,$header);curl_setopt($curl,CURLOPT_ENCODING,'gzip,deflate');curl_setopt($curl,CURLOPT_AUTOREFERER,true);curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);curl_setopt($curl,CURLOPT_POST,1);curl_setopt($curl,CURLOPT_POSTFIELDS,$query_string);curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,0);curl_setopt($curl,CURLOPT_SSL_VERIFYHOST,0);curl_setopt($curl,CURLOPT_CONNECTTIMEOUT,$this-&gt;remote_timeout);curl_setopt($curl,CURLOPT_TIMEOUT,$this-&gt;remote_timeout);$result=curl_exec($curl);$info=curl_getinfo($curl);curl_close($curl);if((integer)$info['http_code']!=200){return false;}return $result;} private function useFopen($url,$query_string){if(!function_exists('file_get_contents')||!ini_get('allow_url_fopen')||!extension_loaded('openssl')){return false;}$stream=array('http'=&gt;array('method'=&gt;'POST','header'=&gt;"Content-type: application/x-www-form-urlencodedrnUser-Agent: ".$this-&gt;local_ua,'content'=&gt;$query_string));$context=null;$context=stream_context_create($stream);return @file_get_contents($url,false,$context);} private function isWindows(){return (strtolower(substr(php_uname(),0,7))=='windows');} private function getIpLocal(){$local_ip='';if(function_exists('phpinfo')){ob_start();phpinfo();$phpinfo=ob_get_contents();ob_end_clean();$list=strip_tags($phpinfo);$local_ip=$this-&gt;scrapePhpInfo($list,'SERVER_ADDR');}$local_ip=($local_ip)?$local_ip:$this-&gt;serverAddr();if($local_ip=='127.0.0.1')return true;return false;}}<br>// &lt;-- клиентский класс<br><br>// Теперь можно запустить проверку лицензии<br>$protect = new Protect();<br><br>// Секретный ключ из созданного метода проверки<br>$protect-&gt;secret_key = 'mqgAqWnSwZZM8YX7BEd9'; <br><br>// Куда будем класть локальный (зашифрованный) ключ?<br>$protect-&gt;local_key_path = ENGINE_DIR . '/data/';<br><br>// Имя и расширение локального ключа <br>$protect-&gt;local_key_name = 'showattach.lic';<br><br>// Адрес сервера проверки<br>$protect-&gt;server = 'http://pcp-cs.loc/api.php';<br><br>// Дата релиза модуля (пригодится если лицензия даётся на определённую версию модуля)<br>$protect-&gt;release_date = '2015-02-10';<br><br>// Ключ активации (лицензионный ключ), тот, который передаётся в параметрах строки подключения модуля<br>$protect-&gt;activation_key = $key;<br><br>// Сообщения о различных ошибках<br>$protect-&gt;status_messages = array(<br> 'status_1' =&gt; '&lt;span style="color:green;"&gt;Активна&lt;/span&gt;',<br> 'status_2' =&gt; '&lt;span style="color:darkblue;"&gt;Внимание&lt;/span&gt;: срок действия лицензии закончился.',<br> 'status_3' =&gt; '&lt;span style="color:orange;"&gt;Внимание&lt;/span&gt;: лицензия переиздана. Ожидает повторной активации.',<br> 'status_4' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: лицензия была приостановлена.',<br> 'localhost' =&gt; '&lt;span style="color:orange;"&gt;Активна на localhost&lt;/span&gt;: используется локальный компьютер, на реальном сервере произойдет активация, если вы правильно ввели лицензионный ключ активации в настройках.',<br> 'pending' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: лицензия ожидает рассмотрения.',<br> 'download_access_expired' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: ключ активации не подходит для установленной версии. Пожалуйста поставьте более старую версию продукта.',<br> 'missing_activation_key' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: ключ активации не указан.',<br> 'could_not_obtain_local_key' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: невозможно получить новый локальный ключ.',<br> 'maximum_delay_period_expired' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: льготный период локального ключа истек.',<br> 'local_key_tampering' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: локальный лицензионный ключ поврежден или не действителен.',<br> 'local_key_invalid_for_location' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: локальный ключ не подходит к данному окружению.',<br> 'missing_license_file' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: создайте следующий пустой файл и папки если их нету:&lt;br /&gt;',<br> 'license_file_not_writable' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: сделайте для записи следующие пути:&lt;br /&gt;',<br> 'invalid_local_key_storage' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: не возможно удалить старый локальный ключ.',<br> 'could_not_save_local_key' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: не возможно записать новый локальный ключ.',<br> 'activation_key_string_mismatch' =&gt; '&lt;span style="color:red;"&gt;Ошибка&lt;/span&gt;: локальный ключ не действителен для указанного ключа активации.'<br><br>);<br><br>// Запускаем валидацию лицензии<br>$protect-&gt;validate();<br><br>$license = false;<br><br>// Если истина, то лицензия в боевом состоянии и можно работать дальше.<br>if($protect-&gt;status) {<br> $license = true;<br>}<br><br>// Если есть ключ и задан ID новости и проверка лицензии прошла успешно — работаем<br>if ($cfg['newsId'] &amp;&amp; $key &amp;&amp; $license) {<br><br> // Запрос в БД<br> $row = $db-&gt;super_query("SELECT id, news_id, name, onserver, date, dcount FROM " . PREFIX . "_files WHERE news_id = '" . $cfg['newsId'] . "'", $cfg['multiple']);<br> $tpl-&gt;load_template($cfg['template'] . '.tpl');<br> if (!$cfg['multiple']) {<br> $rows[] = $row;<br> } else {<br> $rows = $row;<br> }<br><br> foreach ($rows as $attachItem) {<br> if ($rows[0]['id']) {<br> $onserver = $attachItem['onserver'];<br> $md5 = md5_file(ROOT_DIR . '/uploads/files/' . $onserver);<br> $size = formatsize(@filesize(ROOT_DIR . '/uploads/files/' . $onserver));<br><br> $tpl-&gt;set('{attach_id}', $attachItem['id']);<br> $tpl-&gt;set('{attach_name}', $attachItem['name']);<br> $tpl-&gt;set('{attach_size}', $size);<br> $tpl-&gt;set('{attach_md5}', $md5);<br><br> $rowDate = $attachItem['date'];<br> if (date('Ymd', $rowDate) == date('Ymd', $_TIME)) {<br> $tpl-&gt;set('{attach_date}', $lang['time_heute'] . langdate(", H:i", $rowDate));<br> } elseif (date('Ymd', $rowDate) == date('Ymd', ($_TIME - 86400))) {<br> $tpl-&gt;set('{attach_date}', $lang['time_gestern'] . langdate(", H:i", $rowDate));<br> } else {<br> $tpl-&gt;set('{attach_date}', langdate($config['timestamp_active'], $rowDate));<br> }<br><br> $tpl-&gt;copy_template = preg_replace_callback("#{attach_date=(.+?)}#i", "formdate", $tpl-&gt;copy_template);<br><br> $tpl-&gt;set('{attach_download}', $attachItem['dcount']);<br><br> $tpl-&gt;set('[attach]', "");<br> $tpl-&gt;set('[/attach]', "");<br> $tpl-&gt;set_block("'[not-attach](.*?)[/not-attach]'si", "");<br><br> if ($user_group[$member_id['user_group']]['allow_files']) {<br> $tpl-&gt;set('[allow-attach]', "");<br> $tpl-&gt;set('[/allow-attach]', "");<br> $tpl-&gt;set_block("'[not-allow-attach](.*?)[/not-allow-attach]'si", "");<br> } else {<br> $tpl-&gt;set('[not-allow-attach]', "");<br> $tpl-&gt;set('[/not-allow-attach]', "");<br> $tpl-&gt;set_block("'[allow-attach](.*?)[/allow-attach]'si", "");<br> }<br><br> } else {<br> $tpl-&gt;set_block("'[attach](.*?)[/attach]'si", "");<br> $tpl-&gt;set('[not-attach]', "");<br> $tpl-&gt;set('[/not-attach]', "");<br> }<br><br> $tpl-&gt;compile('showAttach');<br> }<br><br> $showAttach = $tpl-&gt;result['showAttach'];<br><br> $tpl-&gt;clear();<br><br> // Выводим результат работы модуля<br>} <br>if (!$key) {<br> // Если ключ не передан — надо бы сообщить об этом<br> $showAttach = '&lt;span style="color: red;"&gt;Не указан лицензионный ключ.&lt;/span&gt;';<br>} <br>if (!$license) {<br> // Если лицензия не проверилась - скжем об этом<br> $showAttach = (!$protect-&gt;errors) ? 'Ошибка лицензии.' : $protect-&gt;errors;<br>}<br><br>echo $showAttach;<br>?&gt;<br></code></pre></div></div></div><br><br><h2>В качестве дополнения</h2><br>Код pcp-cs очень хорошо прокомментирован и для реализации различных вариантов проверки достаточно почитать что там написано. К примеру для использования модуля на локалке без активации достаточно прописать параметр: <br><pre><code>$protect-&gt;use_localhost = true;</code></pre><br>Ну и конечно же если у вас есть идеи и пожелания - мы с Олегом будем всегда рады видеть эти пожелания <a href="https://github.com/Mofsy/pcp-cs/issues" rel="external noopener">на гитхабе</a>]]></content:encoded>
</item><item>
<title>Добавляем новые теги в постраничную навигацию на DLE 10.1 (простой хак)</title>
<link>https://pafnuty.name/statyi/148-newtags-in-pagenav.html</link>
<pdalink>https://pafnuty.name/statyi/148-newtags-in-pagenav.html</pdalink>
<guid>https://pafnuty.name/statyi/148-newtags-in-pagenav.html</guid>
<pubDate>Sun, 05 Jan 2014 13:52:34 +0400</pubDate>
<category>index</category>

<enclosure url="http://pafnuty.name/uploads/posts/2014-01/1388915837_pagenav.png" type="image/png" />
<content:encoded><![CDATA[<div align="center"><img src="http://pafnuty.name/uploads/posts/2014-01/1388915837_pagenav.png" style="max-width:100%;" data-maxwidth="480" alt="Добавляем новые теги в постраничную навигацию на DLE 10.1 (простой хак)"></div><br>В DLE 10.1 наконец вели шаблон для постраничной навигации в полной новости, но по прежнему нет возможности вывести номер текущей страницы и общее число страниц. Этот небольшой хак исправит ситуацию (хоть я и не люблю хаки, но очень часто без них никуда не денешься)!<br>Хак добавляет новые теги в постраничную навигацию по новостям и в полной новости для DLE 10.1<br><br><h2>Установка</h2><ol type="1"><li> Открываем файл <b>engine/modules/show.short.php</b> <br></li><li> Находим код:<br><pre><code>$enpages_count = @ceil( $count_all / $config['news_number'] );</code></pre><br>Ниже вставляем:<br><pre><code>$tpl-&gt;set('{page_count}', $enpages_count);</code></pre><br></li><li> Далее ищем (будет два вхождения]):<br><pre><code>$pages .= "&lt;span&gt;$j&lt;/span&gt; ";</code></pre><br>Ниже вставляем:<br><pre><code>$tpl-&gt;set('{current_page}', $j);</code></pre><br></li><li> Далее ищем:<br><pre><code>$pages .= "&lt;span&gt;{$enpages_count}&lt;/span&gt; ";</code></pre><br>Заменяем на:<br><pre><code>{<br> $pages .= "&lt;span&gt;{$enpages_count}&lt;/span&gt; ";<br> $tpl-&gt;set('{current_page}', $j);<br>}</code></pre><br></li><li> Открываем файл <b>engine/modules/show.full.php</b> <br></li><li> Находим код:<br><pre><code>$tpl2-&gt;load_template( 'splitnewsnavigation.tpl' );</code></pre><br>Ниже вставляем:<br><pre><code>$tpl2-&gt;set('{page_count}', $anzahl_seiten);</code></pre><br></li><li> Далее ищем (будет два вхождения]):<br><pre><code>$listpages .= "&lt;span&gt;$j&lt;/span&gt; ";</code></pre><br>Ниже вставляем:<br><pre><code>$tpl2-&gt;set('{current_page}', $j);</code></pre><br></li><li> Далее ищем:<br><pre><code>$listpages .= "&lt;span&gt;{$anzahl_seiten}&lt;/span&gt; ";</code></pre><br>Заменяем на:<br><pre><code>{<br> $listpages .= "&lt;span&gt;{$anzahl_seiten}&lt;/span&gt; ";<br> $tpl2-&gt;set('{current_page}', $anzahl_seiten);<br>}</code></pre><br></li><li> Открываем шаблоны <b>navigation.tpl</b> и <b>splitnewsnavigation.tpl</b><br></li><li> В нужном месте вставляем теги:<br><b>{page_count}</b> - общее число страниц,<br><b>{current_page}</b> - номер текущей страницы.<br></li><li> ВСЁ!<br></li></ol><br>Надеюсь подобный функционал со временем будет включен в дистрибутив.]]></content:encoded>
</item><item>
<title>Как сделать из Sublime Text удобный переводчик или обзор полезного плагина YandexTranslate</title>
<link>https://pafnuty.name/statyi/146-yandex-translate.html</link>
<pdalink>https://pafnuty.name/statyi/146-yandex-translate.html</pdalink>
<guid>https://pafnuty.name/statyi/146-yandex-translate.html</guid>
<pubDate>Wed, 11 Dec 2013 23:00:46 +0400</pubDate>
<category>index</category>

<enclosure url="http://pafnuty.name/uploads/posts/2013-12/1386787783_00.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2013-12/thumbs/1386787799_01.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2013-12/1386787793_02.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2013-12/1386787865_03.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2013-12/thumbs/1386787800_settings.png" type="image/png" />
<content:encoded><![CDATA[<div align="center"><img src="http://pafnuty.name/uploads/posts/2013-12/1386787783_00.png" style="max-width:100%;" data-maxwidth="480" alt="Как сделать из Sublime Text удобный переводчик или обзор полезного плагина YandexTranslate"></div><br>Сегодня я хочу рассказать про плагин для Sublime Text 2 и 3, позволяющий легко переводить любой выделенный текст на любой, из доступных Яндексу, язык. Плагин делал для себя - поэтому он конечно же удобнее аналогов. Я, например, уже забыл когда пользовался инструментами перевода вне редактора.<br><br><h2>Предыстория</h2><br>Всё началось с того, что мне нужно было перевести много небольших кусочков текста в определённом количестве файлов, естественно текст элементарно выделялся с помощью поиска через регулярку, но вот обратная вставка переведенного текста была совсем не удобна.<br><br>Процесс перевода был примерно таким:<br><ol type="1"><li> Скопировать нужный текст, предварительно найдя нужные куски среди переменных.<br></li><li> Вставить в форму Google translate или Яндекс.переводчик (ну или в любой другой переводчик).<br></li><li> Скопировать первую переведённую строку.<br></li><li> Заменить первую оригинальную строку в редакторе<br></li><li> Скопировать вторую переведённую строку<br></li><li> Заменить вторую переведённую строку<br></li><li> Скопировать N переведённую строку.<br></li><li> Заменить N переведённую строку<br></li><li> Сохранить переведённый файл.<br></li></ol><br>Согласитесь не очень радужная перспектива, если нужно перевести ~150-300 строк текста, разбросанных по файлу.<br>Т.к. я в качестве редактора давно использую Sublime Text - первым делом решил поискать плагин для перевода выделенного текста. <br><br><div align="center"><a href="http://pafnuty.name/uploads/posts/2013-12/1386787799_01.png" class="highslide" target="_blank" rel="noopener external"><img src="http://pafnuty.name/uploads/posts/2013-12/thumbs/1386787799_01.png" style="max-width:100%;" alt=""></a></div><br><br>Для Sublime Text 3 я нашёл только один, но он переводил на китайский, для второй версии редактора было ещё два плагина, но один не завёлся на третьей версии, а второй не подошел под мои требования (просто, удобно, быстро). В итоге было решено написать собственный плагин <s>с преферансом и дамами</s>, который просто переводил бы выделенный текст и заменял выделение на результат перевода.<br><br>Как известно у сервисов перевода есть собственные API, но лишь для Яндекса нашлась . Вот эту библиотеку я и взял за основу будущего плагина.<br>Описывать подробно процесс написания кода на не известном для меня языке я лучше не буду, т.к. он больше напоминал тыкание слепого котёнка в разные углы. <br>Пришлось много разбираться с англоязычной документацией API sublime text, гуглить, разбираться с API Яндекс.перевода (тогда доки были в другом, менее удобном виде, чем сейчас), гуглить, разбираться с элементарными детскими ошибками (python я вообще первый раз в жизни увидел) типа лишнего пробела после табуляции, гуглить, гуглить.<br><br>Однако спустя полдня и полночи ковыряний у меня получилось перевести выделенный текст! Дальше процесс пошёл проще и на данный момент плагин полностью отвечает моим требованиям и я им с радостью пользуюсь. Так потратив пару дней на написание удобного инструмента я сэкономил себе много времени и нервов.<br><br><h2>Возможности и особенности плагина YandexTranslate</h2><br><ul><li> <b>Простота работы с плагином</b>. Конечно же именно из-за простоты, скорости и удобства всё и затевалось.<br></li><li> <b>Работа через уникальный API-ключ.</b> У ключей есть ограничение на количество переводов, поэтому выгоднее иметь собственный ключ.<br></li><li> <b>Перевод выделенного текста и замена выделения на результат перевода.</b> Другими словами не нужно совершать лишних движений. Процесс перевода теперь состоит из трёх пунктов: <br>1) Выделить текст. <br>2) Нажать хоткей.<br>2) Сохранить файл.<br></li><li> <b>Поддержка мультивыделения.</b> Каждый выделенный кусочек текста будет заменён на результат перевода соответствующего кусочка текста.<br></li><li> <b>Поддержка горячих клавиш.</b> По умолчанию используются следующие сочетания:<br><b>Ctrl+Alt+T</b> - перевод выделенного теста на язык по умолчанию (по умолчанию русский).<br><b>Ctrl+Alt+Shift+T</b> - перевод выделенного теста на любой, доступный Яндекс.переводчику язык. Будет показан диалог выбора языка.<br></li><li> <b>Вызов плагина через контекстное или главное меню.</b> Соответствующие пункты появляются, если выделен какой-либо текст.<br><br><div align="center"><img src="http://pafnuty.name/uploads/posts/2013-12/1386787793_02.png" style="max-width:100%;" data-maxwidth="480" alt=""></div><br><br></li><li> <b>Настройка языка для перевода по умолчанию.</b> По умолчанию плагин переводит любой текст на русский язык, но если вы попытаетесь перевести текст, язык которого совпадает с языком перевода по умолчанию - будет предложено перевести этот текст на любой из доступных Яндексу языков. <br><br><div align="center"><img src="http://pafnuty.name/uploads/posts/2013-12/1386787865_03.png" style="max-width:100%;" data-maxwidth="480" alt=""></div><br><br></li><li> <b>Настройка типа переводимого текста.</b> По умолчанию плагин будет переводить любой выделенный текст как обычный текст, но можно задать тип HTML и тогда html-теги переводиться не будут.<br></li></ul><br><br><h2>Настройка плагина</h2><br>Так как API Яндекс не работает без соответствующего API-ключа — ключ придётся , это совершенно бесплатно и очень просто, так что больше минуты времени не займёт.<br><br>Теперь открываем Sublime Text 2 или 3 и через Package Control ищем плагин <b>YandexTranslate</b> () и устанавливаем.<br>Открываем настройки плагина и вставляем полученный API-key, примерно так:<br><br><div align="center"><a href="http://pafnuty.name/uploads/posts/2013-12/1386787800_settings.png" class="highslide" target="_blank" rel="noopener external"><img src="http://pafnuty.name/uploads/posts/2013-12/thumbs/1386787800_settings.png" style="max-width:100%;" alt=""></a></div><br><br><pre><code>{<br> "key": "trnsl.1.1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx "<br>}</code></pre><br>Сохраняем настройки, выделяем любой текст и жмём <b>Ctrl+Alt+T</b>.<br>Радуемся удобству перевода текста <span class="emoji noncontenteditable"><img style="vertical-align:middle;border:none;" alt="smile" src="https://pafnuty.name/public/emoticons/smile.gif"></span> <br><br>Если вы хотите сменить язык перевода по умолчанию (output_language), то возможно потребуется перезапуск редактора.<br><br>Приятного использования! Надеюсь вам, как и мне, будет полезен плагин.<br><br>P.S. Если вдруг среди читателей есть программисты, работающие с python - буду рад услышать замечания по .]]></content:encoded>
</item><item>
<title>Хак - несколько форм добавления новостей в DLE, простая и правильная реализация</title>
<link>https://pafnuty.name/statyi/145-more-addnews.html</link>
<pdalink>https://pafnuty.name/statyi/145-more-addnews.html</pdalink>
<guid>https://pafnuty.name/statyi/145-more-addnews.html</guid>
<pubDate>Wed, 13 Nov 2013 00:20:19 +0400</pubDate>
<category>index</category>

<enclosure url="http://pafnuty.name/uploads/posts/2013-11/1384287427_more_addnews.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2013-11/thumbs/1384287490_addnews1.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2013-11/thumbs/1384287440_addnews2.png" type="image/png" />
<content:encoded><![CDATA[<div align="center"><img src="http://pafnuty.name/uploads/posts/2013-11/1384287427_more_addnews.jpg" style="max-width:100%;" data-maxwidth="480" alt="Хак - несколько форм добавления новостей в DLE, простая и правильная реализация"></div><br>В сети есть разные варианты реализации нескольких форм добавления новостей на DLE-сайт. <br>Кто-то копирует addnews.php, что в корне не верно т.к. создаёт кучу бесполезных дублей кода и создаёт лишние проблемы при обновлении сайта.<br>Кто-то назначает название шаблона через get, что в принципе правильно, но при этом об элементарной фильтрации данных не задумывается, что тоже не правильно.<br>Я предлагаю правильное, на мой взгляд, решение, которое в т.ч. решает проблемы с разными заголовками окна браузера и спидбара для каждого шаблона добавления новости, ну и приятная мелочь - правильно работающая ссылка "Добавить ещё" при успешном добавлении новости.<br><br>Т.к. создание отдельного шаблона добавления новости - действие не очень частое, то выносить какие либо настройка в конфиги и прочее не вижу смысла.<br>Допустим перед нами стоит конкретная задача: реализовать два дополнительных шаблона для добавления новостей, один - добавлять истории (имя шаблона addhistory.tpl), второй - добавлять события (addevent.tpl).<br><br><h2>Установка хака</h2><br><ol type="1"><li> Открываем файл <b>/engine/modules/addnews.php</b><br>Находим:<br><pre><code>$allow_addnews = true;</code></pre><br>Ниже вставляем:<br><pre><code>$addnewsTemplate = 'addnews'; // Шаблон по умолчанию<br>if($_GET['tpl'] == 'history') $addnewsTemplate = 'addhistory'; // Шаблон для добавления историй<br>if($_GET['tpl'] == 'event') $addnewsTemplate = 'addevent'; // Шаблон для добавления событий</code></pre><br>Т.е. из примера мы видим, что для каждого шаблона необходимо создавать отдельное условие, сделано это для того, чтобы избежать сообщений об отсутствующем шаблоне когда не фильтруется $_GET.<br><br>Далее находим:<br><pre><code>$tpl-&gt;load_template( 'addnews.tpl' );</code></pre><br>Заменяем на:<br><pre><code>$tpl-&gt;load_template( $addnewsTemplate.'.tpl' );</code></pre><br><br></li><li> Теперь сделаем нормальный ЧПУ для новых форм добавления новостей. Открываем <pre><code>/.htaccsess</code></pre><br><div align="center"><a href="http://pafnuty.name/uploads/posts/2013-11/1384287490_addnews1.png" class="highslide" target="_blank" rel="noopener external"><img src="http://pafnuty.name/uploads/posts/2013-11/thumbs/1384287490_addnews1.png" style="max-width:100%;" alt=""></a></div><br>Находим:<br><pre><code>RewriteRule ^addnews.html$ index.php?do=addnews [L]</code></pre><br>Ниже вставляем:<br><pre><code>RewriteRule ^addhistory.html$ index.php?do=addnews&amp;tpl=history [L]<br>RewriteRule ^addevent.html$ index.php?do=addnews&amp;tpl=event [L]</code></pre><br><br></li><li> Ну и конечно же нам нужны собственные заголовки для спидбара и метатега title.<br><div align="center"><a href="http://pafnuty.name/uploads/posts/2013-11/1384287440_addnews2.png" class="highslide" target="_blank" rel="noopener external"><img src="http://pafnuty.name/uploads/posts/2013-11/thumbs/1384287440_addnews2.png" style="max-width:100%;" alt=""></a></div><br>Открываем файл <b>engine/engine.php</b><br>Находим:<br><pre><code>elseif ($do == 'addnews') $nam_e = $lang['title_addnews'];</code></pre><br>Заменяем на:<br><pre><code>elseif ($do == 'addnews') {<br> switch ($_GET['tpl']) {<br> case 'history':<br> $nam_e = 'Добавление истории';<br> break;<br><br> case 'event':<br> $nam_e = 'Добавление события';<br> break;<br> <br> default:<br> $nam_e = $lang['title_addnews'];<br> break;<br> }<br>}</code></pre><br></li><li> Готово!<br>Думаю дальнейшее добавление новых шаблонов не составит труда.<br></li></ol><br><h2>Необязательные доработки</h2><br>После добавления новости через нестандартную форму добавления, если нужно добавить ещё - то по умолчанию при клике на ссылку "Добавить ещё" нас перекинет на стандартную форму добавления. Это и исправим.<br>Открываем файл <b>engine/modules/addnews.php</b><br>Ищем:<br><pre><code>if( $config['allow_alt_url'] == "yes" ) msgbox( $lang['add_ok'], "{$msg} &lt;a href="{$config['http_home_url']}" . "addnews.html"&gt;$lang[add_noch]&lt;/a&gt; $lang[add_or] &lt;a href="{$config['http_home_url']}"&gt;$lang[all_prev]&lt;/a&gt;" );</code></pre><br>Заменяем на:<br><pre><code>if( $config['allow_alt_url'] == "yes" ) { <br> $_adnt = explode('tpl=', $_SERVER['QUERY_STRING']);<br> $addNewsTplName = ($_adnt[1]) ? $_adnt[1] : 'addnews' ;<br> msgbox( $lang['add_ok'], "{$msg} &lt;a href='{$config['http_home_url']}{$addNewsTplName}" . ".html'&gt;$lang[add_noch]&lt;/a&gt; $lang[add_or] &lt;a href='{$config['http_home_url']}'&gt;$lang[all_prev]&lt;/a&gt;" );<br>}</code></pre><br>Теперь ссылка будет отрабатывать правильно!<br><br>Надеюсь статья была вам интересна и жду ваших мнений!]]></content:encoded>
</item><item>
<title>Пишем свой модуль для DLE - подробная инструкция</title>
<link>https://pafnuty.name/statyi/144-make-a-module.html</link>
<pdalink>https://pafnuty.name/statyi/144-make-a-module.html</pdalink>
<guid>https://pafnuty.name/statyi/144-make-a-module.html</guid>
<pubDate>Sun, 29 Sep 2013 17:56:53 +0400</pubDate>
<category>index</category>

<enclosure url="http://pafnuty.name/uploads/posts/2013-09/1380462780_make_a_module.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2013-09/1380462730_idea.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2013-09/1380462757_stop_dle_api.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2013-09/1380462744_create_cache.png" type="image/png" />
<content:encoded><![CDATA[<div align="center"><img src="http://pafnuty.name/uploads/posts/2013-09/1380462780_make_a_module.png" style="max-width:100%;" data-maxwidth="480" alt="Пишем свой модуль для DLE - подробная инструкция"></div><br>В этой статье я постараюсь очень подробно разобрать процесс создания простого модуля для DLE с кешированием и собственными шаблонами. Сначала разберём модуль без шаблона, а после дополним его собственным шаблоном. Итогом статьи будет работоспособный модуль без админки, вызываемый в любом месте сайта через строку подключения.<br><br><h2>Вступление</h2><br>На написание данного поста меня натолкнул . Точнее заставил задуматься, почему люди усложняют себе жизнь постоянными правками движка там, где это по большому счёту не нужно. Причина оказалась очень проста - мало кто умеет и знает как правильно написать свой модуль для DLE, хотя это довольно таки просто на самом деле.<br>Сразу стоит оговориться, что я человек, имеющий слабое знание даже основ php (что не мешает мне делать качественные модули для dle), поэтому статья для опытного программиста скорее всего не будет представлять интерес, а вот для новичка - будет крайне полезна.<br><br><h2>С чего начать?</h2><br><div align="center"><img src="http://pafnuty.name/uploads/posts/2013-09/1380462730_idea.png" style="max-width:100%;" data-maxwidth="480" alt=""></div><br>Конечно же начинать лучше с идеи или исходя из конкретной потребности!<br>В нашем случаи будем рассматривать необходимость вывода в <s>профиле пользователя</s> в любом месте сайта количество новостей определённого пользователя в определённых категориях.<br>Таким образом мы уже определили пару переменных, которые будут в нашем модуле - это <b>имя пользователя</b> и <b>id категории</b>. <br>Естественно результат работы модуля лучше кешировать т.к. лишние запросы в БД нам совершенно не нужны. Так же нам не нужен шаблон модуля, но для примера я приведу код модуля и с шаблоном, т.к. правильное подключение шаблона тоже очень важно и при более или менее сложном модуле экономит много ресурсов за счёт уменьшения кода самого модуля. <br>Круг задач определён, можно приступать к написанию кода. Вспоминаем что у DLE , и вполне логичным кажется использование готового API для этой задачи, <b>но я крайне не рекомендую вообще его использовать</b> даже (особенно!) в сложных модулях.<br><br><h2>Почему не стоит использовать DLE_API</h2><br>Всё просто - это крайне кривая штука, которая не развивается аж с версии 8.2 (на момент написания текущая версия движка - 10.0 и по сравнению с предыдущей версией пофиксили лишь баг с невозможностью регистрации пользователя через api, никаких доработок не производилось).<br><div align="center"><img src="http://pafnuty.name/uploads/posts/2013-09/1380462757_stop_dle_api.png" style="max-width:100%;" data-maxwidth="480" alt=""></div><br><b>Использование dle_api значительно увеличивает расход оперативной памяти</b>, что совсем, совсем не хорошо.<br>Методы, описанные в dle_api расходятся с оригинальным функционалом.<br><b>Общий совет</b>: если вам нужна какой то метод или функция из dle_api - просто скопируйте её в свой модуль.<br>Возможно моих скромных наработок хватит на набор методов и функций, которые можно будет использовать в дальнейшем, но это тема для отдельной статьи.<br><br><h2>Пишем код</h2><br>Прежде, чем писать любой модуль (кроме файлов, отвечающих за ajax), нужно в обязательном порядке, в самом начале прописать одну строку:<br><pre><code>if (!defined('DATALIFEENGINE')) die("Error!");</code></pre> <br>Это не позволит пользователю обратиться к файлу модуля напрямую, а значит он не сможет передать собственные параметры в модуль и взломать сайт через этот модуль. Думаю важность этого момента трудно переоценить, но тем не менее я очень часто натыкаюсь на отсутствие подобной конструкции в коде модуля.<br><br><h3>Конфигурация и кеширование</h3> <br>Я решил остановиться на этом моменте подробно, т.к. это один из самых частых вопросов, которые задаются, если человек решил написать свой модуль.<br><br><b>Конфигурацию модуля лучше всего записывать в массив</b> - это даст возможность беспроблемного создания кеша для каждого вызова модуля с разным набором конфигурации, в нашем случаи для каждого пользователя. <br>Объясню почему. Допустим мы написали модуль, он кешируется, и строка создания кеша выглядит следующим образом:<br><pre><code>create_cache($var1.$var2.$var3.$var4, $myModule, $config['skin']);</code></pre><br>где:<br>- <b>$var1.$var2.$var3.$var4</b> - переменные модуля.<br>- <b>$myModule</b> - текст, который должен записаться в кеш.<br><br>Тут всё замечательно ровно до тех пор, пока не понадобится добавить новую переменную. Как правило автор просто забывает прописать эту новую переменную в строку формирования и получения кеша и потом гадает над причинами неработоспособности или неправильной работы модуля (что негативно сказывается на его психологическом равновесии). Таким образом конфиг модуля лучше писать так:<br><pre><code>$myConfig = array(<br> 'var1' =&gt; $var1,<br> 'var2' =&gt; $var2,<br> 'var3' =&gt; $var3,<br> 'var4' =&gt; $var4<br>);<br>$cacheName = md5(implode('_', $myConfig));</code></pre><br>а строка создания кеша будет такая:<br><pre><code>create_cache($cacheName, $myModule, $config['skin']);</code></pre><br>Таким образом нам вообще не придётся лазить в этот код никогда, всё будет происходить автоматом.<br><br><b>Используйте префиксы и суффиксы кеша</b> - это позволит гарантировать автоматическую очистку кеша, а так же позволит (при необходимости) создавать отдельные кеши для разных групп пользователей.<br>Для наглядности поясню взгляните на картинку:<br><div align="center"><img src="http://pafnuty.name/uploads/posts/2013-09/1380462744_create_cache.png" style="max-width:100%;" data-maxwidth="480" alt=""></div><br><b>В DLE есть много дефолтных префиксов</b>, но нас интересуют лишь те, которые автоматически чистятся при определённых условиях, поэтому приведу список таких префиксов:<br><ol type="1"><li><b>news, rss, comm</b> - при добавлении новости или комментария.<br></li><li><b>news, related, tagscloud, archives, calendar, topnews, rss</b> - при добавлении новости.<br></li><li><b>comm</b> - при редактировании комментария.<br></li><li><b>news, rss</b> - при редактировании новости, при выcтавлении рейтинга<br></li><li><b>news, full, comm, rss</b> - при массовом удалении комментариев<br></li><li><b>news, full, comm, tagscloud, archives, calendar, rss</b> - при удалении новости<br></li></ol><br>Значит в нашем случаи нужен префикс <b>archives</b> т.к. кеш модуля надо сбрасывать только при добавлении или удалении новости (он мне просто понравился, можно использовать и calendar и rss). Код конфига и создания кеша тут приводить не буду дабы не захламлять статью, весь код модуля можно посмотреть ниже.<br><br><b>Текст кеша</b> - это результат работы модуля, который будет записан в кеш, тут всё просто.<br><b>ID кеша или его имя</b> - сюда лучше всего передавать переменную $cacheName, о которой писалось выше и переменную $config['skin'] - это для того, чтобы иметь разные кеши для разных шаблонов сайта.<br><b>Суффикс кеша</b> - может принимать два значения true или false, если передано значение true, то для каждой группы пользователей будет создаваться свой кеш-файл, это бывает нужно, если разным группам пользователей нужно показывать разный контент.<br><br><h2>Универсальная заготовка для модуля с кешем, без шаблона</h2><br>Учитывая всё выше написанное мы можем создать простую заготовку для модуля, который будет использовать кеш, но не будет в своей работе использовать шаблон. <b>Всего 15 строк кода!</b> <br><pre><code>&lt;?php<br>if (!defined('DATALIFEENGINE')) die("Go fuck yourself!");<br>$myConfig = array(<br> 'cachePrefix' =&gt; !empty($cachePrefix) ? $cachePrefix : 'archives',<br> 'cacheSuffix' =&gt; !empty($cacheSuffix) ? $cacheSuffix : false<br>);<br>$cacheName = md5(implode('_', $myConfig));<br>$myModule = false;<br>$myModule = dle_cache($myConfig['cachePrefix'], $cacheName . $config['skin'], $myConfig['cacheSuffix']);<br>if (!$myModule) {<br> $myModule = 'Hello World!'; // Результат работы модуля.<br> create_cache($myConfig['cachePrefix'], $myModule, $cacheName . $config['skin'], $myConfig['cacheSuffix']);<br>}<br>echo $myModule;<br>?&gt;</code></pre><br>Всё довольно просто, правда?<br><br><h3>Запрос в БД и проверки.</h3><br>Ранее мы определили, что нам потребуется две переменные - имя юзера и id категории, а для выборки нужных значений нам необходимо будет составить запрос в БД. <br>Заведём две переменные: <b>$userName</b> и <b>$caId</b> - эти переменные будут передаваться в модуль через строку подключения.<br>Однако для нормальной работы модуля нужно проверять данные (ведь от кривых рук спасения практически не существует).<br><br><blockquote><b>Небольшое отступление</b>: <br>Я советую использовать однообразный тип переменных модуля и переменных конфига модуля, т.е. <br><pre><code>'userName' =&gt; $userName,</code></pre><br>или<br><pre><code>'user_name' =&gt; $user_name,</code></pre><br>выглядит гораздо более читабельным, чем <br><pre><code>'user_name' =&gt; $userName,</code></pre> <br>Однако использование нижней черты я не практикую в dle, т.к. разок из-за этого сработал фильтр в dle и модуль не отработал, хотя возможно это лишь единичный случай.</blockquote><br><br>Самая простая проверка - условие if else:<br><pre><code>$userName = !empty($userName) ? $db-&gt;safesql(strip_tags(stripslashes($userName))) : false;</code></pre><br>т.е. мы просто проверяем передана ли переменная через строку подключения, если передана (т.е. не пустая) - прогоняем её значение через <b>$db-&gt;safesql</b> - это обезопасит нас от "неправильных" логинов пользователей, т.к. значение переменной будет вставлено в запрос к БД.<br>Переменную $catId фильтровать не нужно, т.к. она кроме как цифрой никак не задаётся и глупо писать что-то другое в строке подключения. Однако должно быть какое-то дефолтное значение, поэтому проверка нужна и её мы можем прописать непосредственно в конфиге модуля:<br><pre><code>$myConfig = array(<br> ...<br> 'catId' =&gt; !empty($catId) ? $catId : '1', // По умолчанию id=1<br> ...<br>);</code></pre><br><b>С переменными разобрались, теперь запрос в БД.</b><br>В нашем случаи необходимо всего одно значение из БД и использовать полноценный запрос, а потом его разбирать - не имеет смысла, поэтому мы будем использовать метод <b>super_query</b>, он по умолчанию возвращает одномерный массив.<br>Наш итоговый запрос будет таким:<br><pre><code>$row = $db-&gt;super_query("SELECT COUNT(*) as count FROM " . PREFIX . "_post WHERE category = '".$myConfig['catId']."' AND autor = '".$myConfig['userName']."'");</code></pre><br>Где:<br><b>$myConfig['catId']</b> - id категории.<br><b>$myConfig['userName']</b> - имя пользователя. <br>ниже пропишем отладочный код:<br><pre><code>echo "&lt;pre&gt;"; print_r($row); echo "&lt;/pre&gt;";</code></pre><br>Вызов модуля осуществляем так:<br><pre><code>&#123;include file="engine/modules/mymodule.php?&amp;userName=user&amp;catId=5"}</code></pre><br>Если переменные указаны правильно - результатом отладки будет массив с данными, состоящий из одного элемента <b>count</b><br><pre><code>Array (<br> [count] =&gt; 155<br>)</code></pre><br>где: <br><b>155</b> - количество новостей у указанного пользователя в указанной категории<br><br>Теперь можно вывести результат по нормальному:<br><pre><code>$count = ($row['count'] &gt; 0) ? $row['count'] : 'новостей нет';<br>$myModule = $count;</code></pre><br><br><blockquote>Тут следует отметить, что если в кеш будет записан один нолик - dle его не будет "считать кешем" и создаст новый, поэтому нужно писать туда что-то, отличное от нуля.</blockquote><br><br>Итоговый код нашего модуля будет таким:<br><pre><code>&lt;?php<br>if (!defined('DATALIFEENGINE')) die("Go fuck yourself!");<br>$userName = !empty($userName) ? $db-&gt;safesql(strip_tags(stripslashes($userName))) : false;<br>$myConfig = array(<br> 'userName' =&gt; $userName,<br> 'catId' =&gt; !empty($catId) ? $catId : '1',<br> 'cachePrefix' =&gt; !empty($cachePrefix) ? $cachePrefix : 'archives',<br> 'cacheSuffix' =&gt; !empty($cacheSuffix) ? true : false<br>);<br>$cacheName = md5(implode('_', $myConfig));<br>$myModule = false;<br>$myModule = dle_cache($myConfig['cachePrefix'], $cacheName . $config['skin'], $myConfig['cacheSuffix']);<br>if (!$myModule) {<br> $row = $db-&gt;super_query("SELECT COUNT(*) as count FROM " . PREFIX . "_post WHERE category = '" . $myConfig['catId'] . "' AND autor = '" . $myConfig['userName'] . "'");<br> $count = ($row['count'] &gt; 0) ? $row['count'] : 'новостей нет';<br> $myModule = $count;<br> create_cache($myConfig['cachePrefix'], $myModule, $cacheName . $config['skin'], $myConfig['cacheSuffix']);<br>}<br>echo $myModule;<br>?&gt;</code></pre><br><b>Всего 20 строк кода - и получаем готовое решение конкретной проблемы.</b><br>Код модуля вполне рабочий и его можно использовать в реальном проекте, однако приведён этот код для ознакомления с принципами правильного написания модулей для DLE.<br><br>На этом можно было бы закончить, но выше я обещал показать код модуля с использованием собственного шаблона.<br><br><h2>Универсальная заготовка для модуля с кешем и собственным шаблоном</h2><br>В целом тот же модуль, что мы написали без шаблона вполне легко перетащить и на "шаблонный" модуль, однако в нашем случаи это не имеет смысла, т.к. модуль выводит всего одну цифру. Собственный шаблон в модуле лучше использовать когда необходимо вывести несколько значений с разным оформлением в разных местах сайта.<br>Не буду расписывать что к чему, т.к. статья уже и так довольно таки большая, а лучше оставлю два листинга кода, один без комментариев - для использования в качестве заготовки:<br><pre><code>&lt;?php<br>if (!defined('DATALIFEENGINE'))<br> die("Go fuck yourself!");<br>$userName = !empty($userName) ? $db-&gt;safesql(strip_tags(stripslashes($userName))) : false;<br>$myConfig = array(<br> 'userName' =&gt; $userName,<br> 'catId' =&gt; !empty($catId) ? $catId : '1',<br> 'template' =&gt; !empty($template) ? $template : 'default',<br> 'cachePrefix' =&gt; !empty($cachePrefix) ? $cachePrefix : 'news',<br> 'cacheSuffix' =&gt; !empty($cacheSuffix) ? true : false<br>);<br>$cacheName = md5(implode('_', $myConfig));<br>$myModule = false;<br>$myModule = dle_cache($myConfig['cachePrefix'], $cacheName . $config['skin'], $myConfig['cacheSuffix']);<br>if (!$myModule) {<br> if (file_exists(TEMPLATE_DIR . '/' . $myConfig['template'] . '.tpl')) {<br> if (!isset($tpl)) {<br> $tpl = new dle_template();<br> $tpl-&gt;dir = TEMPLATE_DIR;<br> } else {<br> $tpl-&gt;result['myModule'] = '';<br> }<br> $tpl-&gt;load_template($myConfig['template'] . '.tpl');<br> $row = $db-&gt;super_query("SELECT COUNT(*) as count FROM " . PREFIX . "_post WHERE category = '" . $myConfig['catId'] . "' AND autor = '" . $myConfig['userName'] . "'");<br> $count = ($row['count'] &gt; 0) ? $row['count'] : 'новостей нет';<br> $tpl-&gt;set('{tag_name}', $count);<br> $tpl-&gt;compile('myModule');<br> $myModule = $tpl-&gt;result['myModule'];<br> create_cache($myConfig['cachePrefix'], $myModule, $cacheName . $config['skin'], $myConfig['cacheSuffix']);<br> $tpl-&gt;clear();<br> } else {<br> $myModule = '&lt;b style="color:red"&gt;Отсутствует файл шаблона: ' . $config['skin'] . '/' . $myConfig['template'] . '.tpl&lt;/b&gt;';<br> }<br>}<br>echo $myModule;<br>?&gt;</code></pre><br>А второй с подробными комментариями, для понимания что-к-чему:<br><div class="dle-spoiler noncontenteditable"><div class="title_spoiler"><a href="#" data-id="spb575d65dc15c020e11" class="spoiler-btn-svg"><svg width="18" height="18" fill="currentColor" viewBox="0 0 20 20"><path id="svg-spb575d65dc15c020e11" d="M17.418 6.109c0.272-0.268 0.709-0.268 0.979 0s0.271 0.701 0 0.969l-7.908 7.83c-0.27 0.268-0.707 0.268-0.979 0l-7.908-7.83c-0.27-0.268-0.27-0.701 0-0.969s0.709-0.268 0.979 0l7.419 7.141 7.418-7.141z"></path></svg></a><a href="#" data-id="spb575d65dc15c020e11" class="spoiler-btn contenteditable">Показать / Скрыть текст</a></div><div id="spb575d65dc15c020e11" class="text_spoiler"><div class="contenteditable"><pre><code>&lt;?php<br><br>/**<br> * НазваниеМодуля - Пример реализации простого модуля для DLE с кешированием и подключением собственных шаблонов вывода.<br> * =======================================================<br> * Автор: Юзернейм <br> * URL: http://site.ru/<br> * ICQ: 111222 <br> * email: username@site.ru<br> * =======================================================<br> * Файл: название.этого.файла.php<br> * -------------------------------------------------------<br> * Версия: X.X.X (01.01.2013)<br> * =======================================================<br> */<br><br>// Главная строка.<br>// Без этой строчки любой пользователь может получить прямой доступ к файлу модуля и передать туда свои переменные, что чревато элементарным взломом сайта.<br><br>if (!defined('DATALIFEENGINE')) die("Go fuck yourself!");<br><br>// Пример определения переменной имени пользователя<br>$userName = !empty($userName) ? $db-&gt;safesql(strip_tags(stripslashes($userName))) : false;<br><br>// Конфиг модуля<br>// Лучше делать массивом - тогда будет проще в нём ориентироваться в дальнейшем и не нужно будет добавлять новые переменные в формирование кеша.<br><br>$myConfig = array(<br> // Имя пользователя<br> 'userName' =&gt; $userName,<br><br> // ID категории новости<br> 'catId' =&gt; !empty($catId) ? $catId : '1',<br><br> // Шаблон, через который будет выведено содержимое модуля<br> 'template' =&gt; !empty($template) ? $template : 'default',<br><br> // Префикс кеша - довольно важная настройка, если нужно автоматически очищать кеш при определённых событиях (как правило достаточно префикса news)<br> /**<br> * Префиксы, наличие одного из которых гарантирует автоматическую очистку кеша при определённых условиях:<br> * news, rss, comm - при добавлении новости или комментария.<br> * news, related, tagscloud, archives, calendar, topnews, rss - при добавлении новости.<br> * comm - при редактировании комментария.<br> * news, rss - при редактировании новости, при выcтавлении рейтинга<br> * news, full, comm, rss - при массовом удалении комментариев<br> * news, full, comm, tagscloud, archives, calendar, rss - при удалении новости<br> */<br> 'cachePrefix' =&gt; !empty($cachePrefix) ? $cachePrefix : 'news',<br><br> // Суффикс кеша - если задать его, то бдет создаваться отдельные кеши для каждой группы пользователей.<br> 'cacheSuffix' =&gt; !empty($cacheSuffix) ? true : false,<br> <br>);<br><br>// Имя кеш-файла.<br>// Формируется из md5 конфига - это самый оптимальный вариант, т.к. уменьшает количество ненужных операций.<br>$cacheName = md5(implode('_', $myConfig));<br><br>// Присваиваем переменной, выводящей работу модуля в шаблон, значение false перед началом работы модуля.<br>$myModule = false;<br><br>// Пытаемся подцепить значение из кеша (если кеширование на сайте включено и если модуль создал кеш - будет выведено содержимое кеша)<br>$myModule = dle_cache($myConfig['cachePrefix'], $cacheName.$config['skin'], $myConfig['cacheSuffix']);<br><br>if (!$myModule) {<br> // Если в кеше ничего нет - запускаем работу модуля <br> if(file_exists(TEMPLATE_DIR.'/'.$myConfig['template'].'.tpl')) {<br> // Если файл шаблона модуля существует - работаем дальше<br><br> // Проверяем определена ли переменная $tpl и класс dle_template<br> if(!isset($tpl)) {<br> $tpl = new dle_template();<br> $tpl-&gt;dir = TEMPLATE_DIR;<br> } else {<br> $tpl-&gt;result['myModule'] = '';<br> }<br><br> // Загружаем шаблон<br> $tpl-&gt;load_template($myConfig['template'].'.tpl');<br><br> // Выполняем нужный нам запрос в БД.<br> $row = $db-&gt;super_query("SELECT COUNT(*) as count FROM " . PREFIX . "_post WHERE category = '".$myConfig['catId']."' AND autor = '".$myConfig['userName']."'");<br><br> // Определяем теги шаблона<br> $count = ($row['count'] &gt; 0) ? $row['count'] : 'новостей нет';<br><br> $tpl-&gt;set('{tag_name}', $count);<br><br> // Компилим шаблон<br> $tpl-&gt;compile('myModule');<br> // Записываем <br> $myModule = $tpl-&gt;result['myModule'];<br><br> // Записываем результат работы в кеш.<br> create_cache($myConfig['cachePrefix'], $myModule, $cacheName.$config['skin'], $myConfig['cacheSuffix']);<br><br> $tpl-&gt;clear();<br> <br> <br> } else {<br> // Если файла с шаблоном модуля нет - скажем об этом юзеру в удобном виде, а не на фоне пустой белой страницы<br> $myModule = '&lt;b style="color:red"&gt;Отсутствует файл шаблона: '.$config['skin'].'/'.$myConfig['template'].'.tpl&lt;/b&gt;';<br> }<br>} <br><br>// Выводим результат работы модуля<br>echo $myModule;<br>?&gt;<br></code></pre></div></div></div><br><br>Вот теперь пожалуй можно закончить статью!<br>Если будет время, идея и желание - я постараюсь написать про создание более сложного модуля. <br>А пока жду ваших вопросов и мнений.]]></content:encoded>
</item><item>
<title>23 сервиса и сайта для лёгкого начала качественной вёрстки</title>
<link>https://pafnuty.name/statyi/css/136-23-servisa-i-sayta-dlya-legkogo-nachala-kachestvennoy-verstki.html</link>
<pdalink>https://pafnuty.name/statyi/css/136-23-servisa-i-sayta-dlya-legkogo-nachala-kachestvennoy-verstki.html</pdalink>
<guid>https://pafnuty.name/statyi/css/136-23-servisa-i-sayta-dlya-legkogo-nachala-kachestvennoy-verstki.html</guid>
<pubDate>Tue, 11 Dec 2012 01:22:51 +0400</pubDate>
<category>index</category>

<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355174143_23serv.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170821_01.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170785_02.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170794_03.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170822_05.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170813_06.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170797_07.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170842_08.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170842_090.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170773_09.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170840_10.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170837_11.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170787_12.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170803_13.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170827_14.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170810_15.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170865_16.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170846_17.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170821_20.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170757_00.png" type="image/png" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170822_18.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355171717_24.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170803_21.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170776_22.jpg" type="image/jpeg" />
<enclosure url="http://pafnuty.name/uploads/posts/2012-12/1355170795_23.jpg" type="image/jpeg" />
<content:encoded><![CDATA[<img src="http://pafnuty.name/uploads/posts/2012-12/1355174143_23serv.png" style="max-width:100%;" data-maxwidth="480" alt="23 сервиса и сайта для лёгкого начала качественной вёрстки"><br><br>Сегодня я представляю вам краткий обзор некоторых сайтов и сервисов, которые будут полезны для прочтения и использования в работе.<br>Часть из представленных сайтов уже не представляют практического интереса, однако и там можно почерпнуть для себя что-то важное или просто по улыбаться, глядя на старые (по меркам интернета) решения.<br><br><ol type="1"><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170821_01.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>Позволяет создавать разнообразные заготовки от 1 до 5ти колонок. <br>Интерфейс с удобными ползунками и минималистичным дизайном.<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170785_02.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>На странице есть 40 вариантов готовых заготовок без каких либо опций. <br>Не очень интересно, да и сервис староват уже.<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170794_03.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>Это практически полновесный визуальный редактор.<br>Позволяет создавать практически полноценную XHTML страницу.<br>Очень удобно, если вы только только начинаете заниматься вёрсткой.<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170822_05.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br><br>Почти 700 разных бесплатных CSS-шаблонов.<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170813_06.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>Три адаптивных html5&amp;CSS3 шаблона довольно привлекательного вида.<br>Отлично подойдут для модификации под свои нужды.<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170797_07.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>И ещё 17 бесплатных (в т.ч. для коммерческого использования) адаптивных html5&amp;CSS3 шаблонов. <br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170842_08.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>25 готовых вариантов расположения блоков и колонок в заготовке с возможностью скачать каждую на странице-примере.<br>Настройки параметров отсутствуют.<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170842_090.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>Думаю этот сайт не нуждается в представлении.<br>В одном наборе и css и PSD и печатный шаблон, action для photoshop (пользуюсь регулярно для быстрого построения сетки) и много чего ещё.<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170773_09.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>Знаменитая сетка 960px, но в резиновом исполнении.<br>Возможность выбрать из нескольких готовых вариантов заготовок с библиотеками jQuery или MooTools.<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170840_10.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>8 разных вариантов заготовок для вёрстки. 4 - фиксированной ширины и 4 - резиновых.<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170837_11.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>20 разных бесплатных CSS-шаблонов.<br>Доступно только скачивание и небольшие картинки-превьюшки <br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170787_12.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br>50+ разных css-шаблонов. <br><br>Ничего особенного - но доступны предпросмотр детальное описание шаблонов и скачивание.<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170803_13.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>Схожий по функционалу с csstemplater.com генератор css-шаблонов. <br>Отличие в том, что есть возможность сразу добавить место блок под горизонтальное меню и выбор цвета заливки блоков.<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170827_14.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>Почти 3500 бесплатных и платных шаблонов. Бесплатные так себе, основной упор сайт делает на продажу premium-шаблонов по приемлемым ценам.<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170810_15.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>Отличная адаптивная сетка, рассчитанная на среднюю ширину мониторов 1280px.<br>Так же можно скачать и PSD c сеткой.<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170865_16.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>Старенький но не плохой набор готовых css-шаблонов.<br>На ыбор доступны 6 вариантов расположения блоков, а так же представлена основная типографика.<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170846_17.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>Несколько однотипных шаблонов в разных цветовых решениях. <br>Возможно кому то будет полезно.<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170821_20.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>Сервис, на котором вы можете узнать о поддержке того или иного html5-тега или css3-свойства различными браузерами.<br>Одозначно должен быть в закладках у любого верстальщика и веб-программиста.<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170757_00.png" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>На мой взгляд лучший из всех представленных генераторов.<br>Во первых - на русском языке.<br>Во вторых - простая в понимании структура кода и ничего лишнего.<br>В третьих - мне просто нравятся полезные сервисы и плагины Dimox&#96;a, они качественные и легко поддаются модификации.<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170822_18.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>Лучший на мой взгляд готовый набор для быстрого старта правильной вёрстки на html5.<br>Всё очень подробно документировано, начиная от отдельного faq и заканчивая подробными комментариями в кода.<br>Единственный минус (вероятно для некоторых) - английский язык, но минус легко устраняется с google translate.<br>В сочетании с csstemplater.com получается отлично.<br><br>Ну а если уж совсем хочется избавить себя от лишней работы по вёрстке:<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355171717_24.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>На выбор три варианта готовых наборов на основе html5boilerplate. <br>Второй я использовал при созданиии , сайт был запущен за день, включая регистрацию домена и его делигирование.<br><br>Ну и в довесок:<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170803_21.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>Сервис, которым я постоянно пользуюсь, полезнее инструмента для генерации градиентов просто не существует!<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170776_22.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>Обойти вниманием бутсрап не возможно впринципе.<br>Рассказывать не имеет смысла - всё наглядно продемонстрировано на примерах.<br><br></li><li> <br><img src="http://pafnuty.name/uploads/posts/2012-12/1355170795_23.jpg" style="max-width:100%;" data-maxwidth="480" alt=""><br><br>Так же отличный фреймворк для адаптивной вёрстки. <br>В сети уже достаточно много различных вреймворков, рассматривать их все не вижу смысла, достаточно этого и bootstrap.<br></li></ol><br><br>На этом обзор можно считать завершенным, надеюсь он был действительно полезен и вы найдёте что-то новое для себя.]]></content:encoded>
</item><item>
<title>Вместо воскресных сниппетов №5</title>
<link>https://pafnuty.name/statyi/jquery/130-sunday-snippets-5.html</link>
<pdalink>https://pafnuty.name/statyi/jquery/130-sunday-snippets-5.html</pdalink>
<guid>https://pafnuty.name/statyi/jquery/130-sunday-snippets-5.html</guid>
<pubDate>Sun, 19 Aug 2012 23:43:29 +0400</pubDate>
<category>index</category>

<enclosure url="http://pafnuty.name/uploads/posts/2012-08/1345404768_three_bars.png" type="image/png" />
<content:encoded><![CDATA[<div align="center"><img src="http://pafnuty.name/uploads/posts/2012-08/1345404768_three_bars.png" style="max-width:100%;" data-maxwidth="480" alt="Вместо воскресных сниппетов №5"></div><br>Сегодня описания и самих сниппетов не будет. <br>Это связано с плотной работой над двумя важными вещами: обновление дизайна dle-faq.ru и ремонтом машины.<br>Однако без вкусностей не обойдётся.<br>Первая вкусность: Обновлённый , который перекочевал на <br>Вторая вкусность: Плагин для jQuery - , обещанный на прошлой неделе и со среды так не дождавшийся публикации. (плагин так же живёт на )<br><br>Если есть вопросы относительно модуля или скрипта - задавайте - разъясню. По модулю желательно писать в <a href="http://pafnuty.name/modules/125-showstat-for-dle.html" target="_blank" rel="noopener external">соответствующей теме</a>]]></content:encoded>
</item><item>
<title>Воскресные сниппеты №4</title>
<link>https://pafnuty.name/statyi/css/129-sunday-snippets-4.html</link>
<pdalink>https://pafnuty.name/statyi/css/129-sunday-snippets-4.html</pdalink>
<guid>https://pafnuty.name/statyi/css/129-sunday-snippets-4.html</guid>
<pubDate>Sun, 12 Aug 2012 22:05:28 +0400</pubDate>
<category>index</category>

<enclosure url="http://pafnuty.name/uploads/posts/2012-08/1344794262_ss4.png" type="image/png" />
<content:encoded><![CDATA[<div align="center"><img src="http://pafnuty.name/uploads/posts/2012-08/1344794262_ss4.png" style="max-width:100%;" data-maxwidth="480" alt="Воскресные сниппеты №4"></div><br>Сегодня подборка CSS-снипеетов. <br>Ничего особенного, но многое полезно и одновременно забывается, так что сделал эту подборку больше для себя.<br><h2>Подборка полезных css сиппетов</h2><br><ol type="1"><li> Установка font-size для body (поможет легко использовать em вместо px):<br><pre><code>body {<br> font-size: 62.5%; /* font-size 1em = 10px */<br> }<br> p {<br> font-size: 1.2em; /* 1.2em = 12px */<br> }</code></pre><br><br><br></li><li> Вертикальное выравнивание картинки в блоке (полезно когда картинки разного размера):<br><pre><code>div {<br> width: 100px;<br> height: 100px;<br> line-height: 100px;<br> overflow: hidden;<br> }<br> div img {<br> max-width: 100px;<br> vertical-align: middle;<br> display: block;<br> }<br></code></pre><br><br></li><li> Изменение цвета выделения:<br><pre><code>::-moz-selection { background: #b3d4fc; text-shadow: none; }<br>::selection { background: #b3d4fc; text-shadow: none; }</code></pre><br><br></li><li> Предотвращение схлопывания родительского элемента если внутри есть плавающие блоки (clearfix):<br><pre><code>.clearfix:before, <br>.clearfix:after { content: ""; display: table; }<br>.clearfix:after { clear: both; }<br>.clearfix { *zoom: 1; }</code></pre><br><br></li><li> Можно использовать и вот такую конструкцию:<br><pre><code>.clr {clear: both; height: 0; overflow: hidden;} <br>а перед закрывающим тегом родительского блока вставить: <br>&lt;div class="clr"&gt;&lt;/div&gt;</code></pre><br><br></li><li> Запрет выделения текста:<br><pre><code>div {<br> -webkit-user-select: none;<br> -moz-user-select: none;<br> -ms-user-select: none;<br> user-select: none;<br> }</code></pre><br><br><br></li><li> "Подсветка" целевого элемента на странице:<br>например имеем код:<br><pre><code>&lt;h3 id="123"&gt;текст&lt;/h3&gt;</code></pre><br>при переходе по ссылке site.ru/index.html#123 внешний вид нашего заголовка поменяется.<br>На основе этого псевдокласса можно сделать даже : <br><br><pre><code>h3 {<br> font: normal 1.3em/1.8em Arial, Helvetica, sans-serif;<br> color: #ccc;<br>}<br><br>h3:target {<br> font-size: 1.6em;<br> text-decoration: underline;<br> color: #68c;<br>}<br></code></pre><br><br></li><li> "Подсветка внешних ссылок"<br><pre><code>a {<br> color: #c06;<br> }<br> a[target*="blank"] {<br> color: #f50;<br> }<br> a[target*="blank"]:after {<br> content: url(icon.png); /*маленькая иконка для большей наглядности*/<br> }</code></pre><br><br></li><li> Размытый текст:<br><pre><code>h1 { /*ie9 и старше, как всегда отсасывает... юзаем костыли*/<br> color:transparent;<br> text-shadow: 0 0 10px #ccc; <br> }&amp;#8203;</code></pre><br><br></li><li> Треугольник на css:<br><pre><code>.someclass {<br> width: 0; <br> height: 0; <br> border-left:solid 5px transparent;<br> border-right:solid 5px transparent; <br> border-bottom:solid 5px black;<br> }</code></pre><br><br>- Тоже самое применимо к спискам:<br><pre><code>ul {<br> margin: 0.75em 0;<br> padding: 0 1em;<br> list-style: none;<br>}<br>li:before { <br> content: "";<br> border-color: transparent #111;<br> border-style: solid;<br> border-width: 0.35em 0 0.35em 0.45em;<br> display: block;<br> height: 0;<br> width: 0;<br> left: -1em;<br> top: 0.9em;<br> position: relative;<br>}</code></pre><br><br><br></li><li> Кроссбраузерный inline-block для блочных элементов:<br><pre><code>li {<br> width: 200px;<br> min-height: 250px;<br> display: -moz-inline-stack;<br> display: inline-block;<br> vertical-align: top;<br> margin: 5px;<br> zoom: 1;<br> *display: inline;<br> }</code></pre><br><br></li><li> Замена текста картинкой (новый вариант без text-indent):<br><pre><code>.ir { <br> border: 0; <br> font: 0/0 a; <br> text-shadow: none; <br> color: transparent; <br> background-color: transparent; <br> }</code></pre><br><br></li><li> Видимый контур у всех элементов (<b>полезно для отладки</b>):<br><pre><code>* { outline: 2px dotted red }<br>* * { outline: 2px dotted green }<br>* * * { outline: 2px dotted orange }<br>* * * * { outline: 2px dotted blue }<br>* * * * * { outline: 1px solid red }<br>* * * * * * { outline: 1px solid green }<br>* * * * * * * { outline: 1px solid orange }<br>* * * * * * * * { outline: 1px solid blue }</code></pre><br><br><br></li><li> Стили для печати страницы (от htbp.com):<br><br><pre><code>@media print {<br> * { background: transparent !important; color: black !important; box-shadow:none !important; text-shadow: none !important; } /* Black prints faster: h5bp.com/s */<br> a, a:visited { text-decoration: underline; }<br> a[href]:after { content: " (" attr(href) ")"; }<br> abbr[title]:after { content: " (" attr(title) ")"; }<br> .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } /* Don't show links for images, or javascript/internal links */<br> pre, blockquote { border: 1px solid #999; page-break-inside: avoid; }<br> thead { display: table-header-group; } /* h5bp.com/t */<br> tr, img { page-break-inside: avoid; }<br> img { max-width: 100% !important; }<br> @page { margin: 0.5cm; }<br> p, h2, h3 { orphans: 3; widows: 3; }<br> h2, h3 { page-break-after: avoid; }<br>}<br></code></pre><br><br></li><li> Стили для таблиц (от bootstrap):<br><pre><code>/*TABLES*/<br><br>table.bordered {<br> max-width: 100%;<br> background-color: transparent;<br> border-collapse: collapse;<br> border-spacing: 0;<br> width: 100%;<br> margin-bottom: 18px;<br>}<br><br>table.bordered {<br> border: 1px solid #dddddd;<br> border-collapse: separate;<br> *border-collapse: collapsed;<br> border-left: 0;<br> -webkit-border-radius: 4px;<br> -moz-border-radius: 4px;<br> border-radius: 4px;<br>}<br><br>table.bordered caption + thead tr:first-child th,<br>table.bordered caption + tbody tr:first-child th,<br>table.bordered caption + tbody tr:first-child td,<br>table.bordered colgroup + thead tr:first-child th,<br>table.bordered colgroup + tbody tr:first-child th,<br>table.bordered colgroup + tbody tr:first-child td,<br>table.bordered thead:first-child tr:first-child th,<br>table.bordered tbody:first-child tr:first-child th,<br>table.bordered tbody:first-child tr:first-child td { border-top: 0; }<br><br>table.bordered caption + thead tr:first-child th,<br>table.bordered caption + thead tr:first-child td,<br>table.bordered colgroup + thead tr:first-child th,<br>table.bordered colgroup + thead tr:first-child td,<br>table.bordered thead:first-child tr:first-child th,<br>table.bordered thead:first-child tr:first-child td { border-top: 0; }<br><br>table.bordered tbody + tbody { border-top: 2px solid #dddddd; }<br><br>table.bordered tbody tr:hover td,<br>table.bordered tbody tr:hover th { background-color: #f5f5f5; }<br><br>table.bordered tbody tr:nth-child(odd) td,<br>table.bordered tbody tr:nth-child(odd) th { background-color: #f9f9f9; }<br><br>table.bordered th { font-weight: bold; }<br><br>table.bordered th,<br>table.bordered td {<br> padding: 8px;<br> line-height: 18px;<br> text-align: left;<br> vertical-align: top;<br> border-top: 1px solid #dddddd;<br>}<br><br>table.bordered th,<br>table.bordered td { border-left: 1px solid #dddddd; }<br><br>table.bordered thead th { vertical-align: bottom; }<br><br>table.bordered thead:first-child tr:first-child th:first-child,<br>table.bordered tbody:first-child tr:first-child td:first-child {<br> -webkit-border-top-left-radius: 4px;<br> border-top-left-radius: 4px;<br> -moz-border-radius-topleft: 4px;<br>}<br><br>table.bordered thead:first-child tr:first-child th:last-child,<br>table.bordered tbody:first-child tr:first-child td:last-child {<br> -webkit-border-top-right-radius: 4px;<br> border-top-right-radius: 4px;<br> -moz-border-radius-topright: 4px;<br>}<br><br>table.bordered thead:last-child tr:last-child th:first-child,<br>table.bordered tbody:last-child tr:last-child td:first-child {<br> -webkit-border-radius: 0 0 0 4px;<br> -moz-border-radius: 0 0 0 4px;<br> border-radius: 0 0 0 4px;<br> -webkit-border-bottom-left-radius: 4px;<br> border-bottom-left-radius: 4px;<br> -moz-border-radius-bottomleft: 4px;<br>}<br><br>table.bordered thead:last-child tr:last-child th:last-child,<br>table.bordered tbody:last-child tr:last-child td:last-child {<br> -webkit-border-bottom-right-radius: 4px;<br> border-bottom-right-radius: 4px;<br> -moz-border-radius-bottomright: 4px;<br>}<br><br>table.bordered th,<br>table.bordered td { padding: 4px 5px; }<br><br><br>table.bordered.character {<br> border: none !important;<br> padding: 0 !important;<br> background: #fff !important;<br> }</code></pre><br></li></ol><br><br>На сегодня всё.<br>P.S. На неделе будет плагин, о котором говорил ранее, пока расписывать всё по полкам вермени не хватает.]]></content:encoded>
</item></channel></rss>