2. Описание системы «1С – Битрикс

advertisement
ПРАВИТЕЛЬСТВО РОССИЙСКОЙ ФЕДЕРАЦИИ
Федеральное государственное автономное образовательное учреждение
высшего профессионального образования
Национальный исследовательский университет
«Высшая школа экономики»
Московский институт электроники и математики
Факультет Прикладной математики и кибернетики
Кафедра Кибернетики
Дипломная работа
по специальности 230201.65 «Информационные системы и технологии»
Разработка информационного web-сайта на основе
системы «1С-Битрикс: Управление сайтом»
Студент группы МС101
Дрюцкий Д.А.
Руководитель
профессор, к.т.н.
Лавренов С.М.
Зав. кафедрой
профессор, д.т.н.
Афанасьев В.Н.
Москва 2013
Реферат
Дипломная работа по специальности 230201.65 «Информационные системы и
технологии» студента группы МС-101 Дрюцкого Дмитрия Андреевича на тему:
Разработка
информационного
web-сайта
на
основе
системы
«1С-
Битрикс: Управление сайтом»
139 с., 9 рис., 4 табл., 13 источников, 5 прил.
WEB-ПРИЛОЖЕНИЕ, СИСТЕМА, 1C-БИТРИКС, ИНФОРМАЦИОННЫЙ БЛОК,
СТРУКТУРА, КОМПОНЕНТ, AJAX, API, РЕЗЕРВНОЕ КОПИРОВАНИЕ.
Объектом исследования является технология разработки web-сайтов на основе
системы «1С – Битрикс: Управление сайтом».
Цель работы – разработка информационного web-сайта на основе системы «1С
– Битрикс: Управление сайтом».
В процессе работы была спроектирована и реализована структура хранения
данных web-сайта, спроектирована и реализована структура страниц web-сайта,
написан код программных компонентов. Для обеспечения сохранности данных было
настроено автоматическое резервное копирование таблиц базы данных и файлов
файловой структуры web-сайта. Для обеспечения возможности командной
разработки была настроена система управления версиями Subversion. Для проверки
производительности web-сайта было проведено нагрузочное тестирование с
последующей оценкой результатов тестирования и корректировкой сценариев
работы web-сайта согласно результатам тестирования.
В результате работы был получен web-сайт, удовлетворяющий требованиям
заказчика и доступный всем пользователям сети Internet. Web-сайт был
оптимизирован
под
высокие
нагрузки
тестирования.
2
согласно
результатам
нагрузочного
Содержание
Обозначения и сокращения
6
Определения
7
Введение
9
1. Описание основных используемых технологий и языков программирования
10
1.1. Общие сведения
10
1.2. Язык программирования PHP
10
1.3. Web-сервер Apache
11
1.4. СУБД MySQL
12
1.5. Язык программирования JavaScript
12
1.6. Технология AJAX
13
1.7. Таблица стилей CSS
14
1.8. Общая схема работы web-приложения
14
2. Описание системы «1С – Битрикс: Управление сайтом»
16
2.1. Общие сведения
16
2.2. Преимущества и недостатки системы
16
2.3. Целесообразность использования системы
18
2.4. Сравнение с другими системами
19
3. Описание API системы «1С – Битрикс: Управление сайтом»
21
3.1. Общие сведения
21
3.2. API модуля «Главный модуль»
21
3.3. API модуля «Информационные блоки»
23
4. Формирование требований к сценариям работы web-приложения
26
4.1. Общие сведения
26
4.2. Список требований
26
5. Диаграмма информационных блоков
29
5.1. Общие сведения
29
5.2. ER – диаграмма данных web-приложения
29
5.3. Определение информационных блоков
31
6. Описание программных компонентов
34
6.1. Создание общей структуры страниц web-приложения
34
6.2. Реализация структуры страниц web-приложения, физическая структура
36
6.3. Список программных компонентов
39
6.3.1. Компонент «Список всех игр»
39
6.3.2. Компонент «Список новых и выходящих игр»
40
3
6.3.3. Компонент «Список лучших игр»
42
6.3.4. Компонент «Список 100 лучших игр»
43
6.3.5. Компонент «Фильтр по играм»
44
6.3.6. Компонент «Поиск по играм»
45
6.3.7. Компонент «Автоподсказки в поиске»
46
6.3.8. Компонент «Список обзоров к игре»
47
6.3.9. Компонент «Детальная страница игры»
48
6.3.10. Компонент «Форма поиска»
48
6.3.11. Компонент «Страница разработчика игры»
49
6.3.12. Компонент «Список лучших игр за год»
50
6.4. Диаграмма связей между страницами web-приложения и компонентами
51
6.5. Особенности реализации сценариев работы web-приложения.
51
6.5.1. Общие сведения
51
6.5.2. Сохранение состояния страниц при использовании AJAX – запросов
52
6.5.3. Обработка событий панели управления.
55
7. Резервное копирование данных web-приложения. Система управления версиями
59
7.1. Общие сведения
59
7.2. Реализация резервного копирования
59
7.3. Система управления версиями
62
7.3.1. Описание системы Subversion
62
7.3.2. Настройка Subversion для работы с web-приложением
64
8. Нагрузочное тестирование
69
8.1. Общие сведения
69
8.2. Техника проведения нагрузочного тестирования
69
8.3. Оценка результатов тестирования
71
Заключение
76
Список использованных источников
77
Приложение A. Принцип работы системы «1С – Битрикс: Управление сайтом»
78
А.1. Общие сведения
78
А.2. Модульная структура системы
78
А.2.1. Главный модуль
79
А.2.2. Модуль «Управление структурой»
79
А.2.3. Модуль «Информационные блоки»
80
А.3. Компоненты
81
А.3.1. Общие сведения
81
4
А.3.2. Файловая структура компонента
81
А.3.3. Схема обмена данными между файлами компонента
85
А.3.4. Публичный раздел системы
87
А.3.4.1. Порядок загрузки страницы web-приложения
87
А.3.4.2. Подключение модулей системы
87
А.3.4.3. Подключение шаблонов web-приложения
88
А.3.4.4. Подключение компонентов web-приложения
88
Приложение Б. Требования заказчика к работе web-приложения и дизайн-концепция
90
Приложение В. Исходные коды программных компонентов
95
В.1. Компонент games.calendar
95
В.2. Компонент games.raiting
99
В.3. Компонент games.filter
102
В.4. Компонент games.search
113
В.5. Компонент games.detail
117
В.6. Компонент search_autocomplete
121
В.7. Компонент reviews.list
126
Приложение Г. Исходные коды файла script.js (основной JavaScript – сценарий web-приложения)
129
Приложение Д. Исходные коды файла init.php
133
5
Обозначения и сокращения
СУБД – система управления базой данных
AJAX – Asynchronous Javascript and XML (асинхронный JavaScript и XML)
API – Application Programming Interface (программный интерфейс приложения)
CMS – Content management system (система управления контентом)
CSS – Cascading Style Sheets (каскадные таблицы стилей)
HTML – HyperText Markup Language (язык разметки гипертекста)
JS – JavaScript
XML – eXtensible Markup Language (расширенный язык разметки)
6
Определения
В
настоящей
дипломной
работе
применяют
следующие
термины
с
соответствующими определениями.
Web-приложение – клиент-серверное приложение, в котором сервером
выступает web-сервер, клиентом программа или устройство, способное получить
доступ к web-серверу. Логика web-приложения распределена между сервером и
клиентом, хранение данных осуществляется преимущественно на сервере, обмен
информацией происходит по сети.
Web - сервер — это сервер, принимающий HTTP-запросы от клиентов, обычно веббраузеров, и выдающий им HTTP-ответы, обычно вместе с HTML-страницей,
изображением, файлом, медиа-потоком или другими данными.
CMS (Content management system) – компьютерная система или программа,
используемая для обеспечения и организации совместного процесса создания,
редактирования и управления текстовыми и мультимедиа документами.
API (Application Programming Interface) – набор готовых классов, функций,
структур и констант, предоставляемых приложением (библиотекой, сервисом) для
использования
во
внешних
программных
продуктах.
API
определяет
функциональность, которую предоставляет программа (модуль, библиотека), при
этом API позволяет абстрагироваться от того, как именно эта функциональность
реализована.
Экземпляр программы – копия продукта «1C-Битрикс: Управление сайтом»,
включающая в себя исходный текст продукта и только одну копию структуры и
таблиц базы данных, входящих в состав продукта.
Сайт – совокупность данных одного экземпляра программы с уникальным
идентификатором, с помощью которого группируются объекты web-приложения
для их совместного отображения и использования, обычно в одном внешнем виде,
языке интерфейса, доменном имени или каталоге. В нашем случае web-приложение
и сайт являются синонимами, т.к. у нас имеется один экземпляр программы с
уникальным идентификатором.
7
Административный раздел (Панель управления) – раздел сайта, содержащий
интерфейс
для
управления
модулями
системы,
структурой,
содержанием,
посетителями и другими составляющими сайта.
Пользователь – посетитель сайта, относящийся к определенной группе
(группам) и осуществляющий доступ к ресурсам сайта в соответствии с настройкой
прав данной группы.
Группа пользователей – совокупность пользователей сайта, обладающих
определенными правами на доступ и управление ресурсами сайта.
8
Введение
Целью данной дипломной работы является проектирование и разработка webприложения согласно требованиям заказчика. Цель функционирования webприложения – предоставление большого объема различной информации об играх
для персональных компьютеров и других устройств.
При разработке web-приложения должны быть использованы все современные
технологии и инструментальные средства. Сценарии работы web-приложения
должны быть максимально быстрыми и обеспечивать работу web-приложения с
большими объемами данных. Должен быть сведен к минимуму объем данных,
которыми обменивается web-сервер и web-клиент, web-сервер и СУБД.
Должна быть спроектирована и реализована структура данных webприложения и структура страниц и программных компонентов web-приложения.
В процессе разработки web-приложения должна быть задействована система
управления версиями для поддержания возможности командной разработки. Для
обеспечения безопасности данных web-приложения должен быть автоматизирован
процесс резервного копирования данных.
Для оценки возможностей web-приложения по обслуживанию потока
клиентов должно быть проведено нагрузочное тестирование web-приложения и
проведена оценка результатов тестирования.
Данное направление дипломной работы выбрано, потому что умение
разрабатывать web-приложения на базе популярных систем управления сайтами
является одним из самых востребованных на современном рынке информационных
технологий. В то же время создание качественного web-приложения требует
глубокого знания инструментальных средств разработки web-приложений, таких
как
языки
программирования
PHP,
JavaScript,
технология
AJAX.
Эти
инструментальные средства были изучены и применены в процессе выполнения
дипломной работы.
9
1. Описание основных используемых технологий и языков
программирования
1.1. Общие сведения
В процессе разработки web-приложения используется несколько программных
средств и языков программирования. Каждое программное средство, каждый язык
программирования используются по мере надобности для круга задач, при решении
которых они действительно необходимы.
Для разработки web-приложения были использованы следующие программные
средства:
 для написания программного кода на стороне web-сервера: язык PHP;
 для реализации доступа к страницам web-приложения посредством протокола
HTTP: web-сервер Apache;
 для хранения и обработки данных: СУБД MySQL;
 для реализации асинхронных запросов к web-серверу: технология AJAX;
 для написания программного кода на стороне клиента: язык JavaScript и
библиотека jQuery;
 для стилизации внешнего вида web-страниц: таблица стилей CSS.
1.2. Язык программирования PHP
PHP (PHP: Hypertext Preprocessor – «PHP: препроцессор гипертекста») –
язык для написания сценариев, исполняемых на компьютере web-приложения
посредством интерпретации исходного кода. Основное предназначение языка PHP –
это выполнение на сервере сценариев, создающих динамические web-страницы.
PHP поддерживает широкие объектно-ориентированные возможности, полная
поддержка которых была введена в пятой версии языка. Также в пятой версии языка
реализован механизм обработки исключений.
В язык PHP встроено большое количество функций для работы со
стандартными конструкциями языка. В первую очередь в языке реализовано
10
большое количество функций работы со строками, массивами, объектами. Также в
PHP встроено большое количество функций для работы с другими программами.
В процессе разработки web-приложения язык PHP в первую очередь
использовался мной для получения и обработки данных, хранящихся в базе данных,
работы с файловой системой компьютера web-приложения, реализации процесса
кэширования данных и вывода динамических данных в код разметки web-страницы
для последующей передачи сформированной web-страницы пользователю.
1.3. Web-сервер Apache
Apache – свободный web-сервер, наиболее часто используемый в Unixподобных операционных системах. Основными достоинствами Apache считаются
надёжность и гибкость конфигурации. Apache предоставляет удобные средства
конфигурации как на уровне всего web-приложения (httpd.conf), так и на уровне
директории (.htaccess). Указанная выше особенность позволяет удобно управлять
действиями web-сервера при обработке запроса к тем или иным страницам и
разделам web-приложения.
Web-сервер Apache позволяет включить дополнительный модуль обработки
URL –
mod_rewrite. Данный модуль является средством преобразования URL,
который указан в запросе к web-приложению, из одной формы в другую согласно
заранее написанным правилам (RewriteRule). Для написания правил обработки и
преобразования URL используются регулярные выражения (Regular expressions).
Таким образом, можно создавать псевдонимы имен web-страниц, делая внешний
вид URL, которые использует web-клиент при запросах к web-серверу, более
короткими, читабельными и запоминающимися.
В процессе разработки web-приложения и в данный момент, когда webприложение функционирует и доступно пользователям, Web-сервер Apache
используется мной для обеспечения возможности доступа web-клиентов к ресурсам
web-приложения посредством протокола
HTTP, координации работы web-
приложения, ограничения доступа к некоторым разделам и страницам webприложения, обработки и преобразования URL, указанных в запросах web-клиентов.
11
1.4. СУБД MySQL
MySQL представляет собой систему управления реляционными базами данных
с поддержкой языка SQL. СУБД MySQL предоставляет все основные средства для
хранения, обработки и изменения данных. В СУБД MySQL поддерживается
возможность выбора типа таблиц. Основными типами являются MyISAM и InnoDB.
Таблицы с типом InnoDB поддерживают транзакции на уровне отдельных записей.
MySQL имеет API для языка PHP (и многих других языков), а также обеспечивает
поддержку для ODBC посредством ODBC-драйвера MyODBC.
В процессе разработки web-приложения и в данный момент, когда webприложение функционирует и доступно пользователям, СУБД MySQL используется
мной для хранения данных, необходимых для стабильного функционирования webприложения.
1.5. Язык программирования JavaScript
JavaScript
–
объектно-ориентированный
язык
программирования
для
написания сценариев. Чаще всего JavaScript используется для написания сценариев
работы
с
web-страницами,
отображаемыми
web-браузером.
Web-браузер
интерпретирует код сценария языка JavaScript, и на основе описанных в сценарии
действий производит манипуляции с разметкой web-страницы. Таким образом,
посредством языка JavaScript реализуется возможность программирования на
стороне клиента. Язык JavaScript предоставляет возможность доступа к элементам
разметки web-страницы посредством объектов.
При создании сценариев на
языке JavaScript приходится сталкиваться с трудностями, связанными с тем, что
различные web-браузеры могут по-разному интерпретировать эти сценарии. Самые
серьезные трудности возникают, если какой-либо из браузеров не поддерживает тот
или иной объект, метод или свойство. Наиболее практичным и современным
способом решения данной проблемы является использование свободной библиотеки
jQuery [7]. Данная библиотека реализована на языке JavaScript и расширяет
возможности данного языка, нивелируя различия между браузерами.
12
В процессе разработки web-приложения язык JavaScript и библиотека jQuery
были использованы мной для создания сценариев работы с разметкой web-страниц и
в процессе использования технологии AJAX.
1.6. Технология AJAX
AJAX (Asynchronous Javascript and XML - «асинхронный JavaScript и XML») –
технология, посредством которой возможно осуществить асинхронный запрос к
компьютеру web-приложения. Другими словами, это запрос, который не требует
перезагрузки страницы web-клиентом [8].
Технология AJAX предоставляет возможность по какому-либо действию
пользователя (например, нажатие на ссылку) не осуществлять запрос на полное
получение web-страницы, а получать только нужную в данный момент часть webстраницы. Таким образом, снижается объем данных, передаваемых между
компьютером web-приложения и компьютером пользователя, следовательно,
снижается время, которое требуется на запрос и получение ответа. Кроме того, за
счет того, что страница не перезагружается, у пользователя создается впечатление
непрерывной работы с web-приложением.
Технология AJAX включает в себя следующие инструменты:
 язык JavaScript. Именно посредством данного языка возможно осуществить
запрос к компьютеру web-приложения в асинхронном режиме;
 XML (eXtensible Markup Language – расширяемый язык разметки) - текстовый
формат, предназначенный для хранения структурированных данных для
обмена информацией между программами;
В процессе разработки web-приложения технология AJAX использовалась
мной для «фоновой» загрузки с компьютера web-приложения web-клиентом
частей страниц там, где не выгодно с точки зрения объема загружаемых данных и
эстетичности работы web-приложения осуществлять полную загрузку webстраницы в ответ на действия пользователя.
13
1.7. Таблица стилей CSS
CSS (Cascading Style Sheets - каскадные таблицы стилей) - технология
описания внешнего вида документа, написанного языком разметки. Основной целью
разработки CSS являлось разделение содержимого (написанного на HTML или
другом языке разметки) и представления документа (написанного на CSS). Это
разделение может увеличить доступность документа, предоставить большую
гибкость и возможность управления его представлением, а также уменьшить
сложность и повторяемость в структурном содержимом [6].
В процессе разработки web-приложения каскадные таблицы стилей CSS
использовались мной для управления внешним видом страниц web-приложения и
передачи таблиц стилей web-клиентам пользователей для правильного отображения
и поддержания дизайна web-приложения.
1.8. Общая схема работы web-приложения
Web - клиент
HTTP-запрос к web-серверу
Web-сервер Apache
CSS
JS
Ответ web-сервера
Интерпретатор
PHP
Ф.С.
СУБД MySQL
компьютер
пользователя
компьютер
web - приложения
(Ф.С. – файловая система, JS – JavaScript)
Рисунок 1 – Общая схема работы web-приложения
На рисунке 1 изображена общая схема запроса web-клиентом и получения
страницы с компьютера web-приложения.
14
Для того чтобы получить страницу web-приложения, web-клиент отправляет
HTTP – запрос к компьютеру web-приложения, который обрабатывается webсервером Apache. Web-клиент может отправить как обычный запрос, так и
асинхронный запрос к web-серверу с помощью технологии AJAX. Web-сервер
Apache обрабатывает запрос, ищет требуемый web-клиентом ресурс в файловой
системе компьютера web-приложения и, в зависимости от типа ресурса и
конфигурации самого web-сервера, сразу формирует ответ web-клиенту, либо
отправляет ресурс на обработку интерпретатором PHP. Интерпретатор PHP
выполняет исходный код, содержащийся в переданном ему web-сервером ресурсе,
при необходимости с помощью API обращается к СУБД MySQL, получая или
изменяя данные, и возвращает сформированную страницу обратно web-серверу
Apache для дальнейшей отправки web-клиенту.
В принятом web-клиентом коде может содержаться код на языке JavaScript и
ссылки на каскадные таблицы стилей CSS. В этом случае каскадные таблицы стилей
применяются web-клиентом к разметке полученной страницы, а код JavaScript
исполняется, внося требуемые изменения в страницу на стороне клиента, либо
формируя новый запрос к компьютеру web-приложения с помощью технологии
AJAX.
15
2. Описание системы «1С – Битрикс: Управление сайтом»
2.1. Общие сведения
Система «1C – Битрикс: Управление сайтом» представляет собой программное
ядро для всестороннего управления web-проектами любой сложности.
Система «1С – Битрикс: Управление сайтом» включает в себя API для
построения web-приложения и CMS для управления созданным web-приложением и
его данными.
Система является платной, стоимость системы варьируется в зависимости от
редакций, которые в свою очередь отличаются друг от друга набором доступных
модулей.
В состав «1С-Битрикс: Управление сайтом» входит 28 модулей. Продукт
позволяет управлять информационным наполнением сайта, структурой, форумами,
рекламой, рассылкой, распределять права между группами пользователей,
анализировать статистику посещений, оценивать эффективность рекламных
кампаний и многое другое.
2.2. Преимущества и недостатки системы
Замечание. Далее вместо словосочетания система «1С – Битрикс: Управление
сайтом» будет для краткости использоваться слово система.
На данный момент система «1С – Битрикс: Управление сайтом» является
одной из ведущих CMS, используемых для разработки web-проектов. Количество
проектов, реализованных на базе системы «1С – Битрикс: Управление сайтом»
составляет более чем 40 000.
Можно выделить следующие основные преимущества системы:
 Стабильность работы системы. Web-приложения, правильно разработанные
на базе системы (под правильностью следует понимать следование
рекомендациям по разработке системы, использование API системы), могут
достаточно долго работать без специального обслуживания. В системе
16
предусмотрена технология обновления (SiteUpdate), позволяющая постоянно
следить за обновлениями системы.
 Безопасность системы. В состав системы входит модуль «Проактивная
защита». Благодаря данному модулю система обеспечивает высокий уровень
безопасности web-приложения.
 Удобный и понятный интерфейс.
 Универсальная среда разработки, наличие API системы. API системы
обладает большим количеством классов и функций, позволяющих получить
доступ ко всей функциональности модулей и построить web-приложение на
базе работы модулей.
 Подробная документация. Документация по работе системы и по принципам
построения web-приложения на базе системы доступна бесплатно и содержит
в себе практически полное описание всех модулей системы, классов и методов
API системы.
 Удобное представление данных web-приложения, хранящихся в базе данных.
Данные web-приложения могут быть представлены в виде информационных
блоков. Информационные блоки предоставляют возможность удобно (без
навыков владения СУБД) добавлять данные в систему, управлять данными и
просматривать данные. Наиболее удачным решением является возможность
разбить записи базы данных на блоки и папки и добавлять к записям
неограниченное количество свойств. В системе предусмотрены классы и
методы API для доступа и управления элементами информационных блоков.
К недостаткам системы следует отнести невозможность выбора нужных
модулей при покупке системы, т.е. существует несколько редакций, в каждой из
которой находятся строго определенные модули, перемещать модули между
редакциями и формировать список модулей «под себя» невозможно. Также следует
указать, что при разработке web-приложения можно выделить ряд неточностей или
недостатков работы API системы, в частности невозможность сортировки элементов
или фильтрации элементов разных информационных блоков по свойствам с
одинаковым кодом.
17
2.3. Целесообразность использования системы
Разработанное мной web-приложение в первую очередь обладает достаточно
большим объемом данных сложной структуры. Поэтому для того, чтобы удобно и
быстро администрировать данные, необходима гибкая система представления
данных с четкой структурой и интуитивно понятным интерфейсом. Без
использования системы пришлось бы создавать большое количество таблиц в базе
данных, настраивать связи между ними, писать специальные сценарии для
управления данными, возможно, создавать API. В системе же, как уже было
написано выше, предусмотрена достаточно удобная система управления данными.
Таким образом, удобное управление данными web-приложения определяет первую
причину использования мной данной системы.
В системе предусмотрено API, состоящее из большого количества классов и
функций. Использование протестированных и отлаженных функций обеспечивает
безопасность и правильность работы фрагментов сценариев web-приложения. Ряд
задач, достаточно часто требующих написания большого количества кода, с
помощью API системы уже решен, и классы и функции, отвечающие за решение
данных задач, готовы к использованию в сценариях web-приложения. Так как
сценарии разработанного мной web-приложения должны работать быстро,
стабильно и поддерживать безопасность web-приложения, наличие API системы
определяет вторую причину использования мной данной системы.
В общем, система обладает большой популярностью, занимает высокое место
среди аналогичных систем. Производители системы обеспечивают клиентам
техническую поддержку, отвечают на вопросы и готовы в крайних случаях оказать
помощь в решении проблем. Популярность системы, большое количество проектов,
разработанных на базе системы и техническая поддержка, обеспечиваемая
разработчиками системы, определяет третью причину использования мной данной
системы.
18
2.4. Сравнение с другими системами
У системы «1С – Битрикс: Управление сайтом» существует большое
количество конкурентов, в том числе предоставляющих возможность использования
на бесплатной основе. Основным конкурентом «1С – Битрикс: Управление сайтом»
является система UMI.CMS. В данной системе также предусмотрена возможность
управления данными web-приложения (CMS) и API для разработки webприложения. UMI.CMS также является модульной системой и поддерживает
возможность выбора редакций.
В UMI.CMS присутствует ряд особенностей, которые отсутствуют в «1С –
Битрикс: Управление сайтом», например, наличие корзины, в которую помещаются
все удаленные страницы, новости и т.д. с возможностью последующего
восстановления. Тем не менее, в UMI.CMS есть ряд существенных недостатков по
сравнению с «1С – Битрикс: Управление сайтом», основным из которых является
отсутствие
модуля,
обладающего
функциональностью,
схожей
с
функциональностью модуля «Информационные блоки». В UMI.CMS основным
является понятие страницы как универсального носителя информации. Добавлять к
страницам произвольные свойства оказывается достаточно затруднительной и
нетривиальной задачей, при этом гибкость, которая достигается путем задания
различных типов свойств в информационных блоках «1С - Битрикс», в UMI.CMS
отсутствует. Также UMI.CMS проигрывает системе «1С – Битрикс: Управление
сайтом» в реализации API системы. Как уже упоминалось выше, построенная на
понятии страницы, UMI.CMS предоставляет достаточно небольшое количество
классов и методов для построения web-приложения по сравнению с API системы
«1С - Битрикс», не все классы и функции подробно описаны в документации, из-за
чего при разработке приходится экспериментировать. В UMI.CMS отсутствует
удобная реализация инкапсуляции программного кода, которая реализована в «1С –
Битрикс: Управление сайтом» посредством компонентов. Вся функциональность
реализована посредством модулей и макросов, как методов основного класса
модуля, определяемого в основном сценарии модуля. Также преимуществом
системы «1С – Битрикс: Управление сайтом» является удобная система шаблонов,
19
позволяющая системе определять шаблон страниц web-приложения в зависимости
от различных внешних условий, не прибегая к написанию дополнительного
программного кода и разнообразие настроек web-приложения. В то же время в
UMI.CMS система шаблонов реализована не настолько детально и гибко, а
возможностей настроек web-приложения меньше, чем в системе «1С – Битрикс:
Управление сайтом».
Таким образом, система «1С – Битрикс: Управление сайтом» представляется
наиболее подходящей для разработки web-приложения. Подробное описание
принципов работы системы «1С – Битрикс: Управление сайтом» дано в приложении
A.
20
3. Описание API системы «1С – Битрикс: Управление сайтом»
3.1. Общие сведения
Программный интерфейс (API) системы состоит из набора классов и функций,
обеспечивающих
возможность
в
сценариях
web-приложения
использовать
функциональность модулей системы. Таким образом, у каждого модуля имеется
свой набор классов и функций для работы с системой. При разработке webприложения использовались классы и функции следующих основных модулей:
 API модуля «Главный модуль»;
 API модуля «Информационные блоки»;
3.2. API модуля «Главный модуль»
В состав API модуля «Главный модуль» входит набор основных классов и
функций для разработки web-приложения. Использование классов и функций
данного модуля не требует дополнительных действий по включению модуля, все
классы и функции доступны в сценариях web-приложения после одного из этапов
загрузки страницы web-приложения.
Можно выделить следующий список классов и их методов, использованных
при разработке web-приложения.
Класс CMain.
CMain – главный класс страницы web-приложения. После одного из этапов по
загрузке страницы в сценарии становится доступным инициализированный
системой объект данного класса с именем $APPLICATION.
void ShowTitle(string property_code=”title”, bool strip_tags = true)
Метод выводит заголовок страницы.
void SetTitle(string title)
Метод устанавливает заголовок страницы.
void ShowCSS(bool external = true, bool XhtmlStyle = true)
Метод выводит таблицу стилей CSS страницы.
void ShowMeta(string property_id = “”, string meta_name = false, bool
XhtmlStyle = true)
21
Метод отображает свойство с id = property_id в виде мета-тега страницы (<meta>).
void
ShowProperty(string
property_id
=
“”,
mixed
default_value
=
false)
Метод отображает свойство страницы с id=property_id, учитывая свойства раздела.
string GetCurPageParam(string add_params = “”, array remove_params =
array(), bool get_index_page = false)
Метод возвращает адрес текущей страницы web-приложения, добавляя к списку
параметров новые и (или) удаляя старые параметры.
mixed IncludeComponent(string componentName, string componentTemplate,
array
arParams=array(),
object
parentComponent=null,
array
arFunctionParams=array())
Метод подключает компонент на странице. Возвращает код компонента.
Функции.
int AddToTimeStamp(array add, int timestamp = false)
Метод добавляет к дате в Unix-формате заданный интервал времени. Возвращает
новую дату также в Unix-формате.
Класс CFile.
CFile – Класс для работы с файлами web-приложения.
array GetFileArray(int file_id)
Метод возвращает массив, содержащий описание файла (путь к файлу, имя файла,
размер) с идентификатором file_id.
Класс CDBResult.
CDBResult – класс результата выполнения запроса к базе данных.
array GetNext(bool text_html=true, bool text_original = true)
Метод возвращает массив значений полей, приведенный в HTML-безопасный вид.
Если достигнут конец результата выборки, метод вернет false.
void
NavStart(int
page_size=10,
bool
show_all=true,
int
page_number=false)
Метод разбивает результат выборки на страницы.
void
NavPrint(string
title,
bool
show_always=false,
text_css_class="text", string template_path=false)
Метод выводит ссылки для постраничной навигации.
22
string
Класс CModule.
CModule – класс для работы с модулями.
bool IncludeModule(string module_id)
Метод проверяет установлен ли модуль module_id и если установлен, подключает
его. Возвращает "true", если модуль установлен, иначе - "false".
Класс CBitrixComponent.
CBitrixComponent – класс для работы с компонентами web-приложения.
bool StartResultCache(int cacheTime, string additionalCacheID, string
cachePath)
Метод поддержки внутреннего кэширования компонента. Если кэш действителен,
метод отправляет на экран его содержимое. Если кэш недействителен, метод
возвращает true, кэширование завершается и кэш сохраняется.
void AbortResultCache()
Метод отменяет кэширование в компоненте.
bool ClearResultCache(string additionalCacheID, string cachePath)
Метод очищает кэш компонента. В случае успешного выполнения очистки
возвращает true.
void IncludeComponentTemplate(string templatePage)
Метод инициирует и подключает шаблон компонента.
3.3. API модуля «Информационные блоки»
Программный интерфейс данного модуля предназначен для работы с
информационными блоками web-приложения. В первую очередь с помощью
классов и функций данного API можно получить необходимые данные из
информационных блоков, обработать или изменить их.
Можно выделить следующий список классов и их методов, использованных
при разработке web-приложения.
Класс СIBlock.
CIBlock – класс для работы с информационными блоками.
СDBResult GetList(array arOrder = array("SORT"=>"ASC), array arFilter)
23
Метод
возвращает
список
информационных
блоков
по
фильтру
arFilter,
отсортированный в порядке arOrder.
CDBResult GetByID(int ID)
Метод возвращает информационный блок по его ID.
Класс CIBlockElement.
CIBlockElement – основной класс модуля «Информационные блоки». Данный класс
предоставляет набор методов для работы с элементами информационных блоков.
CIBlockResult
GetList(array
arOrder
=
array("SORT"=>"ASC"),
array
arFilter = array(), mixed arGroupBy = false, mixed arNavStartParams =
false, array arSelectFields = array())
Возвращает список элементов по фильтру arFilter, отсортированных в порядке
arOrder. Элементы могут быть группированы по правилам, указанным в массиве
arGroupBy, ограничены по количеству по правилам, указанным в массиве
arNavStartParams. Могут быть выбраны только поля и свойства, указанные в массиве
arSelectFields.
bool SetPropertyValueCode(int ELEMENT_ID, string PROPERTY_CODE, string
PROPERTY_VALUE)
Метод устанавливает свойству с кодом, равным PROPERTY_CODE, элемента с ID,
равным ELEMENT_ID, значение PROPERTY_VALUE.
Класс CIBlockSection.
CIBlockSection – класс для работы с разделами информационных блоков.
CIBlockResult
GetList(array
arOrder
=
array("SORT"=>"ASC"),
array
arFilter = array(), bool bIncCnt = false)
Метод возвращает список разделов информационных блоков, отсортированный в
порядке arOrder и отфильтрованный по правилам, заданным в arFilter.
CIBlockResult GetNavChain(int IBLOCK_ID, int SECTION_ID)
Метод возвращает путь по дереву от корня до раздела SECTION_ID.
Класс CIBlockResult.
CIBlockResult - вспомогательный класс для работы с объектами результатов
выборок, наследуется от класса CDBResult и содержит все его параметры и методы.
void SetUrlTemplates(string DetailUrl = "", string SectionUrl = "",
string ListUrl = "")
24
Метод устанавливает шаблоны путей для элементов, разделов и списка элементов
вместо тех, которые указаны в настройках информационного блока.
_CIBElement GetNextElement()
Метод возвращает из выборки объект _CIBElement, и передвигает курсор на
следующую
запись.
Если достигнута последняя запись (или в результате нет записей), функция вернет
false.
Класс _CIBElement.
_CIBElement - вспомогательный класс для работы с объектами, которые возвращает
CIBlockResult::GetNextElement.
array GetFields()
Метод возвращает массив значений полей элемента.
array GetProperties(arOrder = array(), arFilter = array())
Метод возвращает значения свойств текущего элемента информационного блока.
25
4. Формирование требований к сценариям работы webприложения
4.1. Общие сведения
В системе «1С – Битрикс: Управление сайтом» функциональность webприложения определяется функциональностью программных компонентов, которые
разрабатываются в зависимости от требований заказчика к работе web-приложения.
Как правило, заказчик предоставляет подробное словесное (не содержащее
технических деталей) описание работы страниц web-приложения, которое должно
быть обработано проектировщиком, и на основе которого должны быть выделены
конкретные требования к работе web-приложения, определены основные сценарии и
последовательность их вызова. При этом заказчик имеет возможность внести свои
замечания в разрабатываемую программную модель на любом этапе и сделать
вывод о том, что модель полностью его удовлетворяет, и имеет смысл переходить к
следующему этапу непосредственной реализации спроектированной модели.
Специфика
разработки
web-приложения
такова,
что
при
построении
программной модели немаловажную роль играет так называемая дизайн-концепция,
т.е.
представление
внешнего
вида
страниц
web-приложения,
которое
разрабатывается специалистом в области дизайна и прикладывается к требованиям
заказчика. Вид сформированных специалистом в области дизайна страниц webприложения позволяет разработчику определить связи между страницами webприложения и дополнить список параметров и шаблонов компонентов.
В нашем случае в начале работы над web-приложением заказчиком были
предоставлены как описание работы будущего web-приложения, так и дизайн
концепция, на основании которых можно поэтапно разработать программную
модель работы web-приложения. Требования заказчика и дизайн-концепция
находятся в приложении Б.
4.2. Список требований
В процессе анализа присланных заказчиком документов можно выделить
следующий список требований к web-приложению:
26
 web-приложение должно выводить список всех игр для каждой из платформ;
 при отображении списка игры должны быть разбиты на страницы, должна
быть возможность указать количество игр, выводимых на странице;
 web-приложение должно обеспечивать возможность фильтрации игр по
жанрам, дате выхода и оценке и выводить удовлетворяющие настройкам
фильтра игры;
 фильтр должен быть настраиваемым;
 web-приложение должно обеспечивать поиск игр по первой букве алфавита;
 web-приложение должно выводить список новых игр для каждой из платформ;
 web-приложение должно выводить список выходящих игр для каждой из
платформ;
 должна быть возможность просматривать список игр за конкретный месяц;
 web-приложение должно выводить список игр в порядке убывания рейтинга
для каждой из платформ;
 web-приложение должно выводить список 100 лучших игр для каждой из
платформ;
 web-приложение должно обеспечивать возможность поиска по играм;
 web-приложение должно отображать список найденных игр для каждой из
платформ;
 при вводе данных в поле поиска web-приложение должно выводить
пользователю автоподсказки, т.е. игры, соответствующие по названию части
введенной пользователем фразы;
 оценка игры должна считаться на основе оценок, указанных в обзорах к игре;
 web-приложение должно выводить детальную страницу игры;
 web-приложение должно выводить список обзоров к игре;
 обзоры должны делиться на русские и английские в зависимости от изданийисточников обзоров и сортироваться по оценке издания к игре и важности
издания;
 web-приложение должно выводить список скриншотов к игре;
27
 web-приложение должно обеспечивать возможность создания и заполнения
статических страниц;
 web-приложение должно выводить список лучших игр за конкретный год по
каждой из платформ;
 web-приложение должно выводить информацию о разработчике игры;
 web-приложение должно выводить список игр за год для каждой из платформ.
Рисунок 2 – список требований к сценариям работы web-приложения
На рисунке 2 приведены все требования к работе web-приложения и указана
связь между отдельными требованиями. Следует заметить, что не все требования в
дальнейшем будут представлять собой программные компоненты. Часть требований
подразумевает конфигурацию системы, часть требований будет реализована в
логике работы компонента, реализующего другие требования.
28
5. Диаграмма информационных блоков
5.1. Общие сведения
При формировании информационных блоков независимо от того, как
конкретно реализуется в системе хранение данных, следует сначала разработать
модель данных, воспользовавшись диаграммой «Сущность - связь».
5.2. ER – диаграмма данных web-приложения
Проанализировав требования, можно выделить четыре основных сущности:
 сущность «Игра»
 сущность «Платформа»
 сущность «Обзор»
 сущность «Разработчик»
Сущность «Игра».
В состав сущности «Игра» можно включить следующие основные атрибуты:
Игра
Код
Название
Описание
Уменьшенная обложка
Обложка
Английское название
Метаоценка
Официальный сайт
Дата выхода в России
Жанр
Системные требования
Ключевым атрибутом сущности является атрибут «Код».
Сущность «Обзор».
В состав сущности «Обзор» входят следующие основные атрибуты:
Обзор
Заголовок
Краткий текст
Полный текст
Издатель
Оценка
Ссылка на первоисточник
29
Сразу выделить ключевой атрибут сущности сложно, так как вполне возможно
появление в системе абсолютно идентичных по значениям атрибутов экземпляров
данной
сущности.
Возможно,
в
процессе
реализации,
придется
искусственный уникальный идентификатор.
Сущность «Разработчик».
В состав сущности «Разработчик» входят следующие основные атрибуты:
Разработчик
Наименование
Страна разработчика
Год основания
Официальный сайт
Логотип
Ключевым атрибутом сущности является атрибут «Наименование».
Сущность «Платформа».
В состав сущности «Платформа» входят следующие основные атрибуты:
Платформа
Название
Иконка
Большая иконка
Ключевым атрибутом сущности является атрибут «Название».
Таким образом, можно построить диаграмму:
30
ввести
Игра
Платформа
Код
Название
Уменьшенная обложка
Обложка
Английское название
Метаоценка
Официальный сайт
Дата выхода в России
Жанр
Системные требования
Относится
Название
Иконка
Большая иконка
Включает
Разработана
Имеет
Имеет
Разработал
Относится
Относится
Разработчик
Наименование
Страна
Год основания
Официальный сайт
Логотип
Обзор
Заголовок
Краткий текст
Полный текст
Издатель
Оценка
Ссылка на источник
Рисунок 3 – Диаграмма «Сущность - связь»
5.3. Определение информационных блоков
После того, как сформирована ER-диаграмма, можно приступить к
непосредственному определению информационных блоков, их полей и свойств.
Наиболее простым решением данной задачи было бы создание для каждой из
сущностей «Игра», «Разработчик», «Обзор», «Платформа» информационного блока
и указания его полей и свойств.
Тем не менее, если проанализировать возможности, которые предоставляет
система «1С – Битрикс: Управление сайтом» в модуле «Информационные блоки»,
можно сделать вывод, что наиболее удобно было бы сущности «Игра» и
«Платформа» объединить в одном информационном блоке, сопоставив сущности
«Игра» элемент информационного блока, сущности «Платформа» - раздел
информационного блока. Взаимосвязь разделов и элементов информационного
блока обеспечивает возможность реализации связи, предусмотренной между
31
сущностями «Игра» и «Платформа». Причем, обеспечивается как обязательность
связи, так и отношение «многие ко многим».
В итоге получаем первый информационный блок – «Игры». Часть атрибутов
сущности «Игра» может быть представлена в виде полей информационного блока,
часть - с помощью свойств. То же самое можно сказать и об атрибутах сущности
«Платформа», только здесь будут использованы поля и свойства раздела
информационного блока. В системе, как уже сказано выше, предусмотрен
внутренний механизм связи между разделами и элементами информационных
блоков, поэтому введения дополнительных идентификаторов при реализации связей
между сущностями не предполагается. Среди свойств элементов информационного
блока необходимо предусмотреть свойство, с помощью которого будет реализована
связь
с
информационном
блоком,
реализующем
сущность
«Разработчик».
Значением данного свойства будет наименование разработчика, при этом связь
обязательна.
Для реализации сущности «Обзор» следует предусмотреть еще один
информационный блок «Обзоры». Экземпляры сущности «Обзоры» реализуются в
информационном блоке посредством элементов, атрибуты сущности - посредством
полей и свойств элемента. Среди свойств следует предусмотреть поле, которое
будет являться внешним ключом и обеспечивать связь информационным блоком
«Игры». Это поле будет содержать идентификатор элемента информационного
блока «Игры», причем у каждого элемента это поле должно быть заполнено
(обязательность связи).
Для
реализации
сущности
«Разработчик»
следует
предусмотреть
информационный блок «Разработчики». Экземпляры сущности реализуются в
информационном блоке посредством элементов, атрибуты сущности – посредством
полей и свойств элементов. Среди свойств информационного блока следует
предусмотреть поле, которое будет содержать в себе набор идентификаторов
разделов информационного блока «Игры». Таким образом, реализуется связь с
сущностью «Платформа».
32
После определения информационных блоков можно построить диаграмму
связи между информационными блоками.
Рисунок 4 – Диаграмма информационных блоков
33
6. Описание программных компонентов
6.1. Создание общей структуры страниц web-приложения
Совокупность файлов и папок в файловой системе web-приложения, каждая из
которых содержит некоторый сценарий или другое ценное содержимое, составляет
структуру страниц web-приложения.
Для того чтобы составить правильную структуру web-приложения, следует
определить, какие из требований в сценариях каких из web-страниц будут
реализованы.
Далее рассмотрим подробно сформированные ранее требования и попробуем
определить страницы web-приложения, ответственные за реализацию того или
иного требования.
Web-приложение должно выводить список всех игр для каждой из платформ.
Логично предположить, что для каждой из платформ следует предусмотреть
страницу «Все игры», на которой будут выведен список всех игр, возможно
фильтры и возможность выбора списка игр по алфавиту.
Web-приложение должно выводить список новых игр для каждой из
платформ. Должна быть возможность просматривать список игр за конкретный
месяц. Эти два требования могут быть реализованы на страницах «Все новые
игры» и «Новые игры», причем на первой странице будут выведен весь
постраничный список новых игр, а на второй будут выведены новые игры по
конкретному месяцу с возможностью переключения месяцев.
Web-приложение должно выводить список выходящих игр для каждой из
платформ. Должна быть возможность просматривать список игр за конкретный
месяц. Данные требования реализуются аналогично предыдущим требованиям,
страницы будут называться «График выхода» и «Весь график выхода».
Web-приложение должно выводить список игр в порядке убывания рейтинга
для каждой из платформ. Для реализации данного требования следует
предусмотреть страницу «Лучшие игры», на которой будет выведен список лучших
игр с возможностью фильтрации. Согласно пожеланиям заказчика следует
34
предусмотреть также переход с данной страницы на страницу «Все лучшие игры»,
где будет отображен постраничный список всех игр, начиная с игры с самым
высоким рейтингом, также возможностью фильтрации.
Web-приложение должно выводить список 100 лучших игр для каждой из
платформ. Для реализации данного требования следует предусмотреть страницу
«100 лучших игр».
Web-приложение
должно
выводить
детальную
страницу
игры.
Для
реализации данного требования следует предусмотреть страницу «Детальная
страница игры». На этой же странице будет реализовано требование «webприложение должно выводить список обзоров к игре», требование «обзоры должны
делиться на русские и английские в зависимости от изданий - источников обзоров и
сортироваться по оценке издания к игре и важности издания», и требование «webприложение должно выводить список скриншотов к игре», т.к. обзоры является
неотъемлемой частью игры, как и список скриншотов.
Web-приложение должно отображать список найденных игр для каждой из
платформ. Для реализации данного требования стоит предусмотреть страницу
«Результаты поиска по играм».
Web-приложение должно выводить список игр за год для каждой из
платформ. Для реализации данного требования стоит предусмотреть страницу
«Лучшие игры за год».
Web-приложение должно выводить информацию о разработчике игры. Для
реализации данного требования стоит предусмотреть страницу «Страница
разработчика игры».
Таким образом, руководствуясь определенным выше списком страниц, можно
составить схему связей между страницами:
35
Рисунок 5 – Общая структура страниц web-приложения
Замечание. На данном и следующих рисунках данного раздела стрелки
обозначают возможность переходов между страницами web-приложения.
Схема, отображенная на рисунке 5, является общей. В ней не задано тонкостей
реализации, т.е. конкретных названий страниц в файловой структуре webприложения. Тем не менее, эта структурная схема является основополагающей для
последующей реализации структуры web-приложения.
6.2. Реализация структуры страниц web-приложения, физическая структура
Под реализацией структуры страниц web-приложения подразумевается
непосредственное создание файлов и папок согласно схеме и размещение на
компьютере web-приложения.
Для того чтобы физическая структура была ясна и логически верно построена,
каждой странице общей схемы страниц web-приложения поставим в соответствие
папку файловой системы web-приложения, внутри которой будет расположен файл
index.php, который будет определять сценарий работы страницы. Таким образом,
36
согласно
стандартным
настройкам
web-сервера,
URL
вида
http://www.metagames.ru/index/new/ инициирует выполнение сценария index.php.
Таким образом, если использовать построенную выше общую схему страниц
web-приложения, то получим следующее:
Рисунок 6 – Физическая структура страниц web-приложения
Соответствие папок общей и физической структуры, изображенной на рисунке
6, представлено ниже:
Таблица 1 – соответствие элементов общей и физической структуры webприложения
Главная страница
index.php
Все игры
pc, xbox_360, index (платформа)
Новые игры
платформа / new
Все новые игры
платформа / new / all
График выхода
платформа / new / upcoming
Весь график выхода
платформа / upcoming / all
37
Лучшие игры
платформа / top
Все лучшие игры
платформа / top / all
100 лучших игр
платформа / top_100
Результаты поиска по играм
Search
Детальная страница игры
Detail
Страница разработчика игры
Developer
Лучшие игры за год
платформа / year_top
Если отобразить структуру в виде дерева папок и файлов, получим следующий вид:
корневая_директория
index.php
pc
new
index.php
all
index.php
upcoming
index.php
all
index.php
top
index.php
all
index.php
top_100
index.php
year_top
index.php
--- Другие_платформы --search
index.php
38
detail
index.php
developer
index.php
6.3. Список программных компонентов
После того, как структура страниц web-приложения создана, следует перейти
к следующему этапу – написание сценариев работы страниц. В системе «1С –
Битрикс:
Управление
сайтом»
для
реализации
сценариев
используются
программные компоненты.
Каждый компонент реализует то или иное требование или несколько
требований сразу. Далее будет сформирован список компонентов, для каждого из
которых будет указан номер требования (требований) из списка указанных выше,
описана логика работы, список входных параметров и шаблоны.
6.3.1. Компонент «Список всех игр»
Имя компонента
games.main
Реализация требований
1, 2
Логика работы:
Компонент выводит список всех элементов из указанного информационного блока.
Если указан код раздела информационного блока, выводится список элементов,
принадлежащий данному разделу. Список разбивается на страницы, внизу
выводится постраничная навигация. Если в настройках компонента задан
дополнительный
фильтр,
выводится
список
элементов,
удовлетворяющий
параметрам фильтра.
Параметры компонента:
Идентификатор
Множ.
Тип
Описание
IBLOCK_TYPE
Нет
Список
Тип информационного блока
IBLOCK_ID
Нет
Список
ID информационного блока
39
IBLOCK_SECTION_CODE
Нет
Список
Код раздела (платформы)
COUNT
Нет
Число
Количество игр на странице
FILTER
Нет
Строка
Переменная фильтра
DETAIL_URL
Нет
Строка
Ссылка на детальное описание
элемента
SORT_BY1
Нет
Список
Поле первой сортировки
SORT_ORDER1
Нет
Список
Направление первой сортировки
SORT_BY2
Нет
Список
Поле второй сортировки
SORT_ORDER2
Нет
Список
Направление второй сортировки
Шаблоны:
Идентификатор
Описание
.default
Основной шаблон
6.3.2. Компонент «Список новых и выходящих игр»
Имя компонента
games.calendar
Реализация требований
6, 7, 8
Логика работы:
Компонент
выводит
элементов
из
указанного
информационного
блока,
отсортированных по значениям свойства с кодом DATES_DATE_REL_RUS по
убыванию. Если указан код раздела инфоблока, выводится список элементов,
принадлежащий
данному
дополнительный
фильтр,
разделу.
Если
выводится
в
список
настройках
компонента
элементов,
задан
удовлетворяющий
параметрам фильтра. В настройках компонента определен переключатель, согласно
которому ограничивается вывод списка элементов по текущей дате, т.е.
предписание
не
выводить
элементы
со
значением
свойства
с
кодом
DATES_DATE_REL_RUS, большим или меньшим текущей даты. Также в
настройках компонента определен флаг, указывающий, выводить ли по умолчанию
40
при незаданном фильтре только элементы со значением свойства с указанным выше
кодом, соответствующим текущему месяцу.
Параметры компонента:
Идентификатор
Множ.
Тип
Описание
IBLOCK_TYPE
Нет
Список
Тип информационного блока
IBLOCK_ID
Нет
Список
ID информационного блока
IBLOCK_SECTION_CODE
Нет
Список
Код раздела (платформы)
COUNT
Нет
Число
Количество игр на странице
FILTER
Нет
Строка
Переменная фильтра
DETAIL_URL
Нет
Строка
Ссылка на детальное описание
элемента
DATE_BORDER
Нет
Список
Ограничение вывода по дате
CURRENT_MONTH_DEFAULT
Нет
Флаг
Выводить поумолчанию игры
текущего месяца
SORT_BY1
Нет
Список
Поле первой сортировки
SORT_ORDER1
Нет
Список
Направление первой
сортировки
SORT_BY2
Нет
Список
Поле второй сортировки
Шаблоны:
Идентификатор
Описание
main_page
Вспомогательный шаблон.
Используется на главной странице
web-приложения
Вспомогательный шаблон,
Short
предназначен для вывода списка всех
новых или выходящих игр
.default
Основной шаблон, предназначен для
вывода списка игр для текущего или
выбранного месяца
41
6.3.3. Компонент «Список лучших игр»
Имя компонента
games.raiting
Реализация требований
9
Логика работы:
Компонент выводит список элементов из указанного информационного блока,
отсортированных по значениям свойства с кодом MAIN_METAMARK по убыванию
(сортировку можно изменить). Если указан код раздела инфоблока, выводится
список элементов, принадлежащий данному разделу. Если в настройках компонента
задан дополнительный фильтр, выводится список элементов, удовлетворяющий
параметрам фильтра.
Параметры компонента:
Идентификатор
Множ.
Тип
Описание
IBLOCK_TYPE
Нет
Список
Тип информационного блока
IBLOCK_ID
Нет
Список
ID информационного блока
IBLOCK_SECTION_CODE
Нет
Список
Код раздела (платформы)
COUNT
Нет
Число
Количество игр на странице
FILTER
Нет
Строка
Переменная фильтра
DETAIL_URL
Нет
Строка
Ссылка на детальное описание
элемента
SORT_BY1
Нет
Список
Поле первой сортировки
SORT_ORDER1
Нет
Список
Направление первой
сортировки
SORT_BY2
Нет
Список
Поле второй сортировки
SORT_ORDER2
Нет
Список
Направление второй
сортировки
Шаблоны:
Идентификатор
Описание
Short
Вспомогательный шаблон,
42
предназначен для вывода всех
лучших игр
Вспомогательный шаблон,
main_page
предназначен для вывода списка
лучших игр на главной странице
Основной шаблон, предназначен для
.default
вывода ограниченного количества
лучших игр
6.3.4. Компонент «Список 100 лучших игр»
Имя компонента
games.top100
Реализация требований
10
Логика работы:
Компонент выводит список 100 элементов из указанного информационного блока,
отсортированных по значениям свойства с кодом MAIN_METAMARK по
убыванию. Если указан код раздела инфоблока, выводится список элементов,
принадлежащих данному разделу.
Параметры компонента:
Идентификатор
Множ.
Тип
Описание
IBLOCK_TYPE
Нет
Список
Тип информационного блока
IBLOCK_ID
Нет
Список
ID информационного блока
IBLOCK_SECTION_CODE
Нет
Список
Код раздела (платформы)
Шаблоны:
Идентификатор
Описание
.default
Основной шаблон, предназначен для
вывода 100 лучших игр
43
6.3.5. Компонент «Фильтр по играм»
Имя компонента
games.filter
Реализация требований
3, 4, 5, 8
Логика работы:
Компонент выводит форму, которая необходима для фильтрации списков
элементов, размещенных на одной странице с компонентом и настроенных на связь
с данным компонентом. В форме выводится по умолчанию три поля: жанр, дата
выхода, метаоценка. Эти поля можно убрать или показать в зависимости от настроек
компонента. Также можно указать, выводить или нет алфавит. Значениями списков
полей могут быть как точные значения, так и специальные текстовые значения,
которые указываются в настройках компонента по специальному шаблону. Также в
настройках можно указать список основных жанров, выводимых в поле «жанр».
Результатом работы компонента является сформированный массив, который будет
передан далее одному из компонентов, отображающих список элементов. Передача
параметров фильтра должна происходить без перезагрузки страницы, с помощью
технологии AJAX; перезагружаться должна только область компонента, выводящего
список игр.
Параметры компонента:
Идентификатор
Множ
Тип
Описание
Списо
Тип информационного блока
к
платформ
Списо
ID информационного блока
к
платформ
Строк
Имя ключа массива фильтра
.
IBLOCK_TYPE
IBLOCK_ID
FILTER_VARIABLE_NAME
Нет
Нет
Нет
а
PERIOD_LIST
Да
Списо
Список интервалов времени
к
(формат: <название_варианта>
[количество_дней])
44
MARKS_LIST
Да
Списо
Список оценок
к
(формат: <название_варианта>
[минимальная_соответсвущая_оцен
ка]
[цвет_надписи])
DETAIL_DATE_MAX
DETAIL_DATE_MIN
MAIN_GENRE
Нет
Нет
Да
Строк
Максимальная дата в блоке
а
«Уточнить дату»
Строк
Минимальная дата в блоке
а
«Уточнить дату»
Списо
Список основных жанров
к
USE_ALPHA
Нет
Флаг
Использовать алфавит
OUT_PLATFORM
Нет
Флаг
Выводить платформы
SHOW_METAMARK_FILT
Нет
Флаг
Выводить фильтр по метаоценке
Нет
Флаг
Выводить фильтр по дате выхода
ER
SHOW_DATE_FILTER
Шаблоны:
Идентификатор
Описание
month_switcher
Вспомогательный шаблон,
предназначен для вывода
переключателей по месяцам
Вспомогательный шаблон,
main_page
предназначен для вывода на главной
странице
Основной шаблон
.default
6.3.6. Компонент «Поиск по играм»
Имя компонента
games.search
Реализация требований
11, 12
45
Логика работы:
Компонент выводит форму для поиска элементов указанного информационного
блока и, также, список найденных элементов, если поиск осуществлен.
Параметры компонента:
Идентификатор
Множ.
Тип
Описание
IBLOCK_TYPE
Нет
Список
Тип информационного блока
IBLOCK_ID
Нет
Список
ID информационного блока
IBLOCK_SECTION_CODE
Нет
Список
Код раздела (платформы)
COUNT
Нет
Число
Количество игр на странице
DETAIL_URL
Нет
Строка
Ссылка на детальное описание
элемента
SORT_BY1
Нет
Список
Поле первой сортировки
SORT_ORDER1
Нет
Список
Направление первой сортировки
SORT_BY2
Нет
Список
Поле второй сортировки
SORT_ORDER2
Нет
Список
Направление второй сортировки
Шаблоны:
Идентификатор
Описание
.default
Основной шаблон, выводит список
найденных игр
6.3.7. Компонент «Автоподсказки в поиске»
Имя компонента
search.autocomplete
Реализация требований
13
Логика работы:
Компонент выводит список элементов информационных блоков, реагируя на ввод
данных в поле формы компонента search.form. Игры выбираются по части
46
введенной фразы, поиск по списку элементов осуществляется по названию (полю
NAME) и по значениям свойства с кодом MAIN_ENG_NAME.
Шаблоны:
Идентификатор
Описание
search_page
Вспомогательный шаблон, выводится
на странице поиска
Основной шаблон, выводит список
.default
найденных игр
6.3.8. Компонент «Список обзоров к игре»
Имя компонента
reviews.list
Реализация требований
16, 17
Логика работы:
Компонент выводит список обзоров к игре из определенного информационного
блока. Обзоры делятся на русские и английские согласно изданиям (значениям
свойства с ID=35), список английских обзоров можно задать в настройках
компонента. Обзоры сортируются по полю MARK и по индексу сортировки
значения свойства с ID=35 по возрастанию.
Параметры компонента:
Идентификатор
Множ.
Тип
Описание
IBLOCK_TYPE
Нет
Список
Тип информационного блока
IBLOCK_ID
Нет
Список
ID информационного блока
GAME_ID
Нет
Число
ID игры
TRUNCATE_LEN
Нет
Число
Длина поля DETAIL_TEXT обзора
ENG_REVIEWS
Да
Список
Список английских изданий
обзоров
Шаблоны:
Идентификатор
Описание
47
Основной шаблон
.default
6.3.9. Компонент «Детальная страница игры»
Имя компонента
games.detail
Реализация требований
15, 18
Логика работы:
Компонент выводит детальную страницу игры, т.е. значения всех свойств и полей
элемента информационного блока в соответствии с дизайн-концепцией страницы.
Здесь же выводится галерея скриншотов к игре.
Параметры компонента:
Идентификатор
Множ.
Тип
Описание
IBLOCK_TYPE
Нет
Список
Тип информационного блока
IBLOCK_ID
Нет
Список
ID информационного блока
IBLOCK_SECTION_CODE
Нет
Список
Код раздела (платформы)
ELEMENT_CODE
Нет
Строка
Код элемента игры
Шаблоны:
Идентификатор
Описание
.default
Основной шаблон
6.3.10. Компонент «Форма поиска»
Имя компонента
search.form
Реализация требований
5
Логика работы:
Компонент выводит форму поиска по элементам информационных блоков. Также
выводится один выбранный случайным образом элемент из указанного в настройках
информационного блока.
Параметры компонента:
48
Идентификатор
Множ.
Тип
Описание
IBLOCK_TYPE
Нет
Список
Тип информационного блока
IBLOCK_ID
Нет
Список
ID информационного блока
SEARCH_PAGE
Нет
Строка
Страница с результатми поиска
Шаблоны:
Идентификатор
Описание
search_page
Вспомогательный шаблон, выводится
на странице результатов поиска
Основной шаблон
.default
6.3.11. Компонент «Страница разработчика игры»
Имя компонента
developer.detail
Реализация требований
21
Логика работы:
Компонент выводит детальную страницу разработчика игры, т.е. значения всех
свойств и полей выбранного элемента информационного блока в соответствии с
дизайн-концепцией страницы.
Параметры компонента:
Идентификатор
Множ.
Тип
Описание
IBLOCK_TYPE
Нет
Список
Тип информационного блока
IBLOCK_ID
Нет
Список
ID информационного блока
DEVELOPER_NAME
Нет
Список
Наименование разработчика
Шаблоны:
Идентификатор
Описание
.default
Основной шаблон
49
6.3.12. Компонент «Список лучших игр за год»
Имя компонента
games.year_top
Реализация требований
22
Логика работы:
Компонент выводит список элементов, отсортированных по значениям свойства
элемента информационного блока с кодом MAIN_METAMARK по убыванию,
отфильтрованных по значениям свойства DATES_DATE_REL_RUS, в которых год
соответствует предыдущему или выбранному году. В компоненте предусмотрена
возможность переключения между годами. Если в настройках компонента задан код
раздела инфоблока, выводится список элементов из конкретного раздела, в
противном случае выводится список из пяти элементов для каждого из разделов
информационного блока.
Параметры компонента:
Идентификатор
Множ.
Тип
Описание
IBLOCK_TYPE
Нет
Список
Тип информационного блока
IBLOCK_ID
Нет
Список
ID информационного блока
IBLOCK_SECTION_CODE
Нет
Список
Код раздела (платформы)
DETAIL_URL
Нет
Строка
Шаблон пути к детальной
странице игры
Шаблоны:
Идентификатор
Описание
search_page
Вспомогательный шаблон, выводится
на странице результатов поиска
Основной шаблон
.default
50
6.4. Диаграмма связей между страницами web-приложения и компонентами
Рисунок 7 – диаграмма связей между компонентами и страницами web-приложения
На
рисунке
7
изображена
диаграмма
связей
между
программными
компонентами и страницами web-приложения. Каждый компонент в момент
загрузки страницы становится частью страницы, т.е. страница выступает
своеобразным контейнером для компонента. В качестве подписей к связям
выступают идентификаторы шаблонов компонентов.
Исходный код сценариев component.php,
result_modifier.php, script.js
компонентов находится в приложении В.
6.5. Особенности реализации сценариев работы web-приложения.
6.5.1. Общие сведения
В общем, реализация логики работы компонента состоит в написании
программного кода с использованием требуемых классов и функций API системы,
описанных в разделе 3. Главной особенностью при написании программного кода
51
являются обеспечение сохранения состояния страниц при выполнении AJAX запросов к страницам web-приложения и использование обработчиков событий
панели управления.
6.5.2. Сохранение состояния страниц при использовании AJAX – запросов
Возможности web-браузера и языка JavaScript.
Основным
недостатком
технологии
AJAX
является
невозможность
стандартными средствами браузера обеспечить сохранение состояния страниц webприложения при использовании AJAX - запросов. Так как технология AJAX
реализуется посредством языка JavaScript, путь к странице, отображаемый в
адресной строке браузером пользователю, не меняется. То есть, даже не смотря на
то,
что
пользователь,
находясь
www.metagames.ru/index/top/index.php,
на
странице
посредством
с
адресом,
элементов
например,
web-страницы
инициировал несколько запросов к компьютеру web-приложения, в адресной строке
по-прежнему будет указанный выше путь, и при перезагрузке страницы все
действия пользователя будут сброшены. Не все пользователи готовы повторить
выполненные ранее процедуры и могут покинуть web-сайт.
Чтобы разработать решение данной проблемы, необходимо проанализировать
возможности JavaScript. В первую очередь интересует возможность работы с
адресной строкой браузера. Также следует проанализировать поведение браузера
при изменении адресной строки.
После анализа возможностей браузера сделан вывод, что изменить адресную
строку, не вызывая перезагрузки страницы, отображенной в браузере, возможно
только с помощью дописывания к пути, указанному в адресной строке,
произвольного набора символов после знака “#”, т.е. изменение пути в адресной
строке браузера с www.metagames.ru/index/top/index.php
на,
например,
перезагрузке
www.metagames.ru/index/top/index.php#filter:up
страницы.
Этой
особенностью
поведения
не
приведет
браузера
к
можно
воспользоваться, чтобы фиксировать в адресной строке имена и значения
52
переменных, характеризующих состояние страниц web-приложения при работе
пользователя с ними.
Анализ возможностей JavaScript позволяет определить объекты и свойства
объектов, позволяющие изменить адресную строку браузера. Ключевым объектом в
данном случае является объект window. Объект window сочетает в себе 2
направления работы с кодом страницы: window является глобальным объектом
JavaScript и объектом окна браузера. Объект window среди полей имеет объект
location, который и позволяет получить доступ к пути, указанному в адресной
строке браузера, и изменить этот путь.
window.location += “#some_text”;
Таким образом, благодаря указанным выше возможностям браузера и языка
JavaScript, можно изменить путь, указанный в строке браузера, отметив в этом пути
текущее
состояние
страницы
web-приложения.
Остается
предусмотреть
возможность восстановления состояния страницы из пути в адресной строке перед
загрузкой страницы. Эта возможность опять же реализуется использованием
объекта location, описанного в предыдущем абзаце. С помощью этого объекта
можно получить доступ к пути в адресной строке браузера, разбить на части и
восстановить имена и значения переменных, характеризующих состояние страницы.
Непосредственная реализация
Данный механизм необходим, прежде всего, в компоненте games.filter, т.к.
действия пользователя по фильтрации списка различных игр инициируют большое
количество AJAX-запросов к серверу. Но следует предусмотреть универсальный
механизм
восстановления
использования
на
состояния
различных
страниц,
страницах
чтобы
иметь
web-приложения
возможность
независимо
от
компонентов. Напишем на языке JavaScript функцию, которая будет формировать
строку, характеризующую состояние страницы web-приложения:
function ajax_state_string(url, exclude_array){}
Данная функция принимает в качестве первого параметра путь к странице webприложения, которая была бы загружена по действию пользователя, если бы не
использовалась технология AJAX. Предполагается, что в данном пути указаны все
53
переменные и их значения, которые будут определять состояние страницы webприложения после выполнения запроса. exclude_array – дополнительный параметр,
содержащий массив параметров, которые необходимо исключить из списка
переменных, указанных в параметре url, может быть пустым. Функция возвращает
массив из двух элементов, первый из которых с индексом 0 содержит
сформированную строку запроса к компьютеру web-приложения с помощью
технологии AJAX; второй, с индексом 1, содержит строку состояния, которая будет
помещена в адресную строку браузера.
Array([0]
=>
“строка_запроса_AJAX”,
[1]
=>
“строка_состояния_страницы”)
Таким образом, эта функция может быть вызвана до момента выполнения AJAXзапроса, строка запроса может быть использована в момент запроса, строка
состояния – после выполнения запроса для помещения в адресную строку браузера.
Ниже приведен типичный пример:
//вызов функции
var paths =
ajax_state_string(“http://www.metagames.ru/index.php?filter=up&datei=1
0”, new Array());
/*
далее идет ajax-запрос с использованием технологии AJAX к странице с
адресом paths[0]
*/
//сохраняем строку состояния в адресной строке браузера
window.location += “#”+paths[1];
Следующим шагом является написание функции для восстановления из
адресной строки браузера состояния загруженной страницы:
function restore_hash_part(path, result_block, ar_next,
ajax_status_id, filterupped){}
Функция принимает следующие параметры:
 path – путь, указанный в адресной строке браузера;
 result_block – ID элемента страницы, в который должен быть загружен
результат;
54
 ar_next – массив, содержит в качестве первого элемента (индекс - 0) имя
переменной, в качестве второго элемента (индекс 1) значение переменной,
указывающей на то, что имеет смысл восстанавливать состояние (это не
простой, а ajax-запрос);
 ajax_status_id – ID элемента страницы для визуальной анимации процесса
восстановления состояния;
 filterupped – логическая переменная. Если true, процесс восстановления
выполнен не будет;
Эта функция может быть вызвана сразу после загрузки страницы. Типичный пример
использования:
//момент загрузки страницы
window.onLoad = function()
{
restore_hash_part(window.location, “result_block_id”, new
Array(“filter”, “up”), “ajax_status_id”, false));
}
Две данные функции в совокупности образуют интерфейс сохранения
состояния страниц разработанного web-приложения и используются на различных
страницах web-приложения для сохранения истории AJAX-запросов. Использование
данных функций решает проблему невозможности использования кнопок «назад» и
«вперед» в панели web-браузера, сохранения адресов страниц с данными,
полученными при помощи AJAX-запросов, в закладках и при перезагрузке
страницы. Полный исходный код функций представлен в приложении Г.
6.5.3. Обработка событий панели управления.
В предыдущей главе при создании структуры web-приложения не было учтено
требование «оценка игры должна считаться на основе оценок, указанных в обзорах
к игре». Данное требование могло бы быть реализовано в логике работы
компонентов, выводящих информацию по играм. Но каждый раз подсчитывать
оценку, постоянно обращаясь к элементам информационного блока «Обзоры»,
слишком ресурсоёмко с точки зрения использования ресурсов компьютера и
55
скорости работы страниц web-приложения. Например, один из списков игр
содержит 40 записей, каждая из которых соотносится в среднем с пятью обзорами.
Итого – нужно просмотреть в среднем 200 обзоров, что может заметно увеличить
скорость загрузки страницы. Наиболее приемлемым средством реализации
требования была бы возможность автоматического расчёта и установки значения
оценки игры при создании, изменении или удалении обзора, т.е., выражаясь в
терминах системы, установки значения свойства с кодом MAIN_METAMARK
элемента информационного блока «Игры» при изменении, удалении или добавлении
элемента информационного блока «Обзоры», в котором в значение свойства с кодом
GAME_ID совпадает с идентификатором элемента информационного блока «Игры».
Но вносить изменения в сценарии работы панели управления системы не
рекомендуется. Таким образом, актуальным становится вопрос, как можно
перехватить событие добавления элемента информационного блока.
Для решения такого типа проблем в системе предусмотрена технология
событий и обработчиков событий панели управления. При реализации указанного в
предыдущем абзаце требования необходимо использовать следующие типы
событий:
 Событие добавления элемента информационного блока. Нас интересует
событие, наступающее при успешном добавлении элемента. В системе данное
событие имеет код «OnAfterIblockElementAdd»;
 Событие изменения элемента информационного блока. Код –
«OnAfterIblockElementUpdate»;
 Событие удаления элемента информационного блока. Код –
«OnBeforeIblockElementDelete»;
Имея возможность перехвата и обработки указанных выше событий, можно
выстроить алгоритм реализации требования.
 Получить данные элемента информационного блока, участвующего в
событии;
 Определить информационный блок, которому принадлежит элемент;
56
 Если информационный блок не соответствует информационному блоку
«Обзоры», прекратить обработку события;
 Определить изменяемый элемент информационного блока «Игры» (свойство с
кодом GAME_ID);
 Провести расчёт и присвоить полученный результат свойству с кодом
MAIN_METAMARK элемента информационного блока «Игры».
Чтобы зарегистрировать обработчик, необходимо воспользоваться встроенной
функцией API системы:
void AddEventHandler(string from_module_id, string event_id, mixed
callback, int sort = 100, mixed full_path = false)
from_module_id – идентификатор модуля, который будет инициировать событие;
event_id – идентификатор события;
callback – название функции обработчика;
sort – очередность (порядок), в котором выполняется данный обработчик;
full_path – полный путь к файлу для подключения при возникновении события перед
вызовом callback;
В нашем случае, регистрация обработчиков событий будет выглядеть
следующим образом:
AddEventHandler("iblock", "OnAfterIblockElementAdd",
"add_review_handler");
AddEventHandler("iblock", "OnAfterIblockElementUpdate",
"add_review_handler");
AddEventHandler("iblock", "OnBeforeIblockElementDelete",
"delete_review_handler");
Для реализации алгоритма, указанного выше, написано две функции:
void delete_review_handler(int ID)
Функция – обработчик события удаления элемента информационного блока с
идентификатором ID.
void add_review_handler(array &arFields)
57
Функция – обработчик события добавления элемента информационного блока.
$arFields – передаваемый по ссылке массив с данными добавленного элемента. Эта
же функция обрабатывает и событие изменения элемента.
Таким образом, требование по автоматическому подсчету оценки игры по
оценкам обзоров может быть реализовано без изменения сценариев панели
управления и без нагрузки на сервер web-приложения в момент загрузки страниц.
Полный исходный код реализации обработки событий размещен в приложении Д.
58
7. Резервное копирование данных web-приложения. Система
управления версиями
7.1. Общие сведения
Современные web-приложения содержат большое количество данных, на
сбор, обработку и размещение которых уходит большое количество времени и
денежных ресурсов. Поэтому безвозвратная утеря данных (прежде всего,
хранящихся в базе данных) web-приложений может привести к фатальным
последствиям вплоть до полной потери актуальности web-приложения и его так
называемой «смерти», т.е. потери интереса пользователя к web-приложению.
Помимо данных, хранящихся в БД, опасности некорректного изменения
подвержены и файлы, хранящиеся в файловой системе компьютера web-приложения
и обеспечивающие его работу. В первую очередь, существует опасность взлома и
изменения файлов, содержащих сценарии на языках PHP, JavaScript, которые
обеспечивают работу системы и web-приложения. Помимо несанкционированного
доступа существует опасность изменения файлов сценариев и посредством
санкционированного
доступа,
например,
изменения
текстов
сценариев
разработчиками, занимающимися поддержкой и обслуживанием web-приложения,
которые могут быть некорректными и дестабилизировать работу web-приложения.
Поэтому необходимо обезопасить данные и файлы web-приложения, обеспечив их
резервное копирование.
7.2. Реализация резервного копирования
Для того чтобы защитить данные, хранящиеся в базе, следует воспользоваться
утилитами, предоставляемыми СУБД. В нашем случае это утилита mysqldump [5],
предоставляемая СУБД MySQL. Эта утилита должна быть запущена из командной
строки операционной системы, которая отвечает за работу web-приложения. В
нашем случае это операционная система GNU/Linux, т.е. для запуска утилиты
можно воспользоваться командной оболочкой Bourne Shell [3]. В процессе
реализации резервного копирования был написан сценарий на языке Bourne Shell,
59
который обеспечивает резервное копирование данных и сохраняет файл копии в
файловой структуре web-приложения.
DUMP_FILE_NAME=`date +%Y%m%d%H%M%S`
mysqldump –u[логин_пользователя] –p[пароль] -f
[имя_базы_данных] > /tmp/$DUMP_FILE_NAME.sql
if [ $? -eq 0 ]
then
gzip -c /tmp/$DUMP_FILE_NAME.sql
>
/home/bitrix/www/bitrix/backup/$DUMP_FILE_NAME.auto.sql.gz
chmod 644
/home/bitrix/www/bitrix/backup/$DUMP_FILE_NAME.auto.sql.gz
chown bitrix:bitrix
/home/bitrix/www/bitrix/backup/$DUMP_FILE_NAME.auto.sql.gz
fi
rm -f /tmp/$DUMP_FILE_NAME.sql
В данном сценарии происходит вызов утилиты mysqldump с указанием имени
БД, логина и пароля пользователя для доступа к БД. Далее происходит сохранение
файла в локальной директории компьютера с последующим его сжатием с помощью
утилиты gzip и назначения прав доступа и имени пользователя ОС для доступа к
этим файлам на скачивание посредством панели управления системы «1С –
Битрикс: Управление сайтом». Локальная копия файла в конце удаляется. Теперь,
присвоив данному сценарию имя mysql_dump.sh, можно осуществить резервное
копирование данных посредством вызова данного сценария:
[root@v8733 sh_scripts]# ./mysql_dump.sh
Для
организации
резервного
копирования
файловой
структуры
web-
приложения был написан сценарий file_dump.sh.
DUMP_FILE_NAME=`date +%Y%m%d%H%M%S`
tar --exclude=bitrix/backup -czf
/home/bitrix/www/bitrix/backup/$DUMP_FILE_NAME.auto.file.t
ar.gz /home/bitrix/www
60
if [ $? -eq 0 ]
then
chown bitrix:bitrix
/home/bitrix/www/bitrix/backup/$DUMP_FILE_NAME.auto.file.t
ar.gz
fi
В данном сценарии происходит архивация данных с помощью утилиты tar с
последующим сохранением архива в файловой структуре web-приложения. Из
архива логично исключается сам раздел, который будет хранить резервные копии
файлов и архивов. Аналогично процесс резервного копирования можно выполнить,
запустив на выполнение данный сценарий:
[root@v8733 sh_scripts]# ./file_dump.sh
Данные
сценарии
решают
задачу
резервного
копирования
данных,
хранящихся в базе данных и в файловой структуре web-приложения, но для полной
автоматизации
копировались
данного
процесса необходимо
автоматически,
т.е.
в
сделать так, чтобы данные
определенное
время
с
определенной
периодичностью. Для того чтобы реализовать данное требование, необходимо
воспользоваться демоном – планировщиком задач cron. Для того чтобы поставить
данному планировщику задание, необходимо создать в файловой структуре
компьютера web-приложения файл задач. В нашем случае данный файл будет иметь
имя «cron.tasks». Для того, чтобы поставить задачу осуществления резервного
копирования данных и файлов web-приложения на определенное время, необходимо
внести в файл cron.tasks следующий текст:
01 3 * * * /home/bitrix/sh_scripts/mysql_dump.sh
1>/dev/null 2>/home/bitrix/logs/mysql_dump.log
10 3 * * 2 /home/bitrix/sh_scripts/file_dump.sh
1>/dev/null 2>/home/bitrix/logs/file_dump.log
В данном тексте указываются строки, каждая из которых содержит задачу с
указанием времени, имени сценария, который должен быть запущен в указанное
время. Таким образом, сценарий mysql_dump.sh, отвечающий за запуск резервного
61
копирования данных БД, будет запущен в 3:01:00 каждый день, сценарий
копирования файловой системы будет запущен в 3:10:00 каждый второй день
недели, т.е. каждый вторник. Такой принцип распределения времени выбран
потому, что данные БД меняются достаточно часто, в то же время объем,
занимаемый файлом резервной копии, достаточно долго не будет превышать размер
более 10 Мб. Поэтому эти данные можно резервировать каждый день. А файловая
система изменяется достаточно редко, в то же время файл резервной копии (т.е.
архив) занимает на диске компьютера web-приложения более чем 30 Мб. Поэтому
файлы следует копировать раз в неделю. Также важно «развести» процесс
копирования данных файловой системы и БД, чтобы исключить излишнюю
нагрузку на компьютер web-приложения. Опытным путем выяснено, что
копирование данных БД проходит менее чем за 2 минуты, процесс, отвечающий за
копирование данных файловой системы, наоборот, выполняется более чем за 2
минуты, поэтому процесс копирования файловой системы решено запускать через
10 минут после запуска процесса резервного копирования БД.
Теперь, чтобы окончательно зафиксировать задачи планировщика, следует
запустить утилиту crontab:
[root@v8733 sh_scripts]# crontab cron.tasks
После её выполнения которой задачи будут зафиксированы.
После получения нескольких файлов резервных копий была произведена
попытка развертывания архива файловой системы и дампа базы данных на
локальном компьютере. Процесс восстановления прошел успешно и локальная
копия web-приложения до сих пор используется для поддержки web-приложения.
7.3. Система управления версиями
7.3.1. Описание системы Subversion
Система управления версиями используется в процессе разработки и
обслуживания web-приложений. Основной задачей системы управления версиями
является обеспечение совместного редактирования и использования информации.
Использование данной системы обеспечивает возможность командной разработки
62
web-приложения и централизованного хранения данных в едином хранилище.
Хранилище может быть расположено на удаленном сервере и к нему должна быть
обеспечена возможность удаленного доступа нескольких клиентов. Система
управления версиями решает одну из важнейших проблем совместной разработки и
изменения информации – проблему разделения файлов.
Для обеспечения возможности командной разработки мной была использована
централизованная система управления версиями Subversion. На данный момент
Subversion, которую еще называют SVN по имени входящей в дистрибутив системы
клиентской программы, является ведущей системой управления версиями, данную
систему используют ведущие компании по разработке web-приложений. В системе
Subversion реализована модель «Копирование – Изменение – Слияние» [4],
позволяющая разработчикам независимо друг от друга разрабатывать сценарии webприложения, объединяя результаты своих трудов в едином централизованном
хранилище, чаще всего расположенном на компьютере, на котором находится webприложение.
Хранилище содержит информацию в форме дерева файлов — типичном
представлении файлов и каталогов. Любое количество клиентов подключается к
хранилищу и читает или записывает эти файлы. Записывая данные, клиент делает
информацию
доступной
для
остальных.
Читая
данные,
клиент
получает
информацию от других. Хранилище в системе Subversion принято называть
репозиторием. Данные действия отражены на рисунке 8.
Рисунок 8 – схема работы системы управления версиями
С точки зрения пользователя хранилище Subversion представляет собой
«двумерную» файловую систему. Объекты в хранилище (файлы и директории)
63
идентифицируются двумя «координатами»: именем и номером ревизии. Другими
словами, хранилище представляет собой массив мгновенных снимков (ревизий)
дерева файлов и директорий, индексируемый номером ревизии. Каждый такой
снимок - обычная (одномерная) файловая система. Каждый клиент (в нашем случае
– разработчик web-приложения) может сделать такой снимок (checkout) и записать
его на диск своего локального компьютера. Такой снимок называется рабочей
копией. Рабочая копия — это созданная клиентской программой Subversion
локальная копия части данных из хранилища, содержащая помимо собственно
данных некоторую служебную информацию (скрытые директории с именем .svn).
Далее клиент может осуществлять изменения файлов рабочей копии на своем
локальном компьютере. После изменения файлов клиентом он может зафиксировать
(commit) данные изменения в самом репозитории. Еще одним важным действием,
которое может выполнить клиент, является слияние (update) последних изменений
файлов репозитория с рабочей копией. Данное действие позволяет обеспечить
наличие у клиента самой последней версии файлов репозитория. Можно также
обновить рабочую копию до определенной версии, указав её номер. Перед
непосредственным использованием системы управления версий в web-приложении
следует упомянуть еще одно действие – откат (revert) изменений. Данное действие
позволяет отменить изменения, сделанные в рабочей копии клиента и вернуть файл
в состояние, которым он обладает в репозитории.
7.3.2. Настройка Subversion для работы с web-приложением
В состав дистрибутива GNU/Linux, установленного на компьютере webприложения, Subversion входит и установлен. Для настройки системы Subversion,
таким образом, необходимо выполнить следующую последовательность действий:
1. Создание и настройка репозитория;
2. Версионирование файлов и папок web-приложения;
3. Создание рабочей копии на локальном компьютере;
64
Создание и настройка репозитория.
Для того чтобы создать и настроить репозиторий, необходимо в командной
строке
операционной
системы
выполнить
команду
svnadmin
create
<имя_репозитория>. В нашем случае репозиторий будет носить имя metagames.
Соответственно, команда будет иметь вид:
[root@v8733 ~]# svnadmin create /home/svn/metagames
Все хранилища расположены в директории /home/svn.
Далее следует настроить доступ к репозиторию, указав логин и пароль
доступа к репозиторию, название репозитория и права доступа разных групп
пользователей к репозиторию. Для этого нужно править файл svnserve.conf,
находящийся в файле /home/svn/metagames:
anon-access=none
auth-access=write
password-db=passwd
realm=metagames
В данном файле мы запретили доступ к репозиторию всем пользователям,
кроме авторизованных, указали имя репозитория (metagames) и указали имя файла,
содержащего логин и пароль доступа к репозиторию (passwd).
После внесения изменений необходимо перезапустить процесс системы
Subversion, выполнив последовательно команды killall и svnserve.
[bitrix@v8733 ~]# killall svnserve;
[bitrix@v8733 ~]# svnserve –d –r /home/svn
Важно отметить, что процесс web-сервера Apache (httpd) на компьютере webприложения запущен от имени пользователя с именем bitrix. Соответственно,
владельцем файлов web-приложения является указанный выше пользователь.
Поэтому запуск процесса системы Subversion svnserve следует выполнить от имени
пользователя с именем bitrix, чтобы обеспечить сохранность настроек доступа и
владельца изменяемых файлов.
Стандартная структура файлов верхнего уровня репозитория содержит
следующую структуру:
65
branches
trunk
tags
Для того чтобы зафиксировать такую файловую структуру в репозитории,
необходимо создать какую – нибудь папку в файловой структуре компьютера
(например, init) web-приложения, выполнить команду checkout
svn checkout svn://metagames.ru/metagames/ init
, создать в данной папке указанные выше подпапки, выполнить для каждой из
созданных подпапок команду add
svn add init/branches
….
и выполнить команду commit
svn commit init –m “Start doings”.
После этого в репозитории будет создана и версионирована требуемая структура.
Версионирование файлов и папок web-приложения.
Для того, чтобы версионировать файлы и папки web-приложения, нужно
заранее определиться, какие именно файлы и папки стоит заносить в репозиторий,
какие нет. После этого для выбранных папок следует выполнить команду checkout,
например:
svn
checkout
svn://metagames.ru/metagames/trunk
/home/bitrix/www/index/
Далее следует выполнить команду добавления требуемых файлов и подпапок
выбранной папки и выполнить команду add.
svn add home/bitrix/www/index/all/
svn add home/bitrix/www/index/index.php
В конце следует зафиксировать изменения в репозитории, выполнив команду
commit
svn
commit
home/bitrix/www/index
платформ“.
66
–m
“Сценарии
всех
Таким образом, выполнив данные процедуры для всех папок и файлов webприложения, можно сформировать структуру репозитория.
Создание рабочей копии на локальном компьютере.
Теперь, когда репозиторий на компьютере web-приложения создан, можно
выполнить снимок репозитория и создать рабочую копию репозитория на
удаленном компьютере. Для этого можно воспользоваться программой – клиентом
системы Subversion Tortoise SVN. Данная программа позволяет работать с
репозиторием, вносить изменения в файлы и фиксировать их. В данной программе в
графическом режиме доступны команды update, add, commit и т.д. В процессе
разработки web-приложения на локальном копьютере была развернута копия webприложения, затем для корневой папки с помощью команды Tortoise SVN checkout
было выполнено версионирование файлов локального компьютера. Дальнейшие
изменения файлов web-приложения проходили и проходят по следующему
сценарию:
1. Изменение файла, например, /home/bitrix/index.php на локальном компьютере;
2. Выполнение фиксации изменений данного файла в репозитории с помощью
команды Tortoise SVN commit;
3. Выполнение команды svn update для файла самого web-приложения в
командной строке операционной системы, управляющей web-приложением на
удаленном компьютере;
Данная технология управления версиями была использована при разработке
web-приложения и используется в данный момент для поддержания работы webприложения и обеспечения возможности работы над web-приложением нескольких
разработчиков. Также стоит отметить важное для разработки свойство сохранения
истории разработки в виде версий, которое позволяет следить за процессом
разработки и делать откаты изменений в сценариях web-приложения, если
разработчиками допущены какие – либо ошибки. Плюсом является то, что для
доступа к файловой структуре web-приложения более не требуется доступов по
протоколам FTP и HTTP, весь процесс разработки проходит на локальном
компьютере, что позволяет максимально отладить сценарии перед обновлением
67
репозитория и файлов web-приложения. Минусами использования системы
управления версиями при разработке web-приложения является то, что от
разработчиков в данном случае требуется наличие знаний по работе с данной
системой. Также следует отметить то, что приходится разворачивать локальную
копию web-приложения на компьютере разработчика, но наличие таких пакетов
разработки, как «Джентльменский набор Web-разработчика», заметно упрощает
данный процесс.
68
8. Нагрузочное тестирование
8.1. Общие сведения
Нагрузочное тестирование применяется для
того, чтобы определить,
насколько производительно разработанное web-приложение и насколько оно готово
к обслуживанию большого количества клиентов. Проведение нагрузочного
тестирования, которое еще называют «стресс-тест», позволяет не только определить
среднее значение пользователей, которые могут быть обслужены web-приложением
без существенных задержек, но и определить «узкие места» web-приложения, т.е.
ошибки в работе сценариев web-приложения, большие расходы ресурсов при
запросах к СУБД, неверные конфигурации web-сервера и другие ошибки, которые
могут вызвать замедление работы web-приложения.
8.2. Техника проведения нагрузочного тестирования
Для того чтобы провести нагрузочное тестирование, необходимо выполнить
следующие действия:
1. Имитировать нагрузку на компьютер web-приложения;
2. Во время имитации нагрузки снимать показатели, важные для анализа
результатов теста;
3. При необходимости повторить указанные выше пункты с большими
значениями нагрузки;
Имитация нагрузки
Требования к способам имитации нагрузки:
 должна быть возможность указать время имитации;
 должна быть возможность указать количество одновременных запросов;
 должна быть возможность указать файл со списком адресов страниц webприложения, на сценарии которых должна осуществляться нагрузка, должна
быть возможность примерной имитации поведения пользователя сети Internet
(случайные переходы и т.д.);
69
Наиболее подходящим средством является программа Siege [12], которая
реализует указанные выше требования. Данная программа должна быть установлена
на сторонний компьютер с операционной системой GNU/Linux, в нашем случае –
GNU/Linux Ubuntu 10.4.
После установки программу можно запустить из
командной строки ОС.
Получение показателей нагрузки.
Для того чтобы получать показатели производительности, в системе «1С –
Битрикс: Управление сайтом» предусмотрен модуль «Производительность».
Данный модуль предоставляет возможность для просмотра результатов нагрузки на
web-приложение, таких как время генерации страницы, количество и время запросов
к СУБД в сценариях компонентов, расположенных на странице web-приложения и
средние показатели нагрузки на компьютер web-приложения при обращении
пользователя к той или иной странице. Для того чтобы осуществить анализ нагрузки
на компьютер web-приложения, в модуле «Производительность» необходимо
запустить временной сценарий, который принято в системе называть «панелью
производительности».
Рисунок 9 – Данные, предоставляемые модулем “Производительность”
Таким образом, техника нагрузочного тестирования, выбранная мной, заключается в
сколь возможно одновременном запуске программы Siege на стороннем компьютере
и панели производительности на компьютере web-приложения и последующей
оценки данных, предоставляемых панелью производительности после завершения
имитации нагрузки на компьютер web-приложения.
70
Проведение нагрузочного тестирования.
Для того, чтобы начать имитацию нагрузки на компьютер web-приложения,
необходимо запустить в командной строке ОС стороннего компьютера процесс
программы Siege:
[root@v8733 ~]# siege -c 50 -d 1 -t300S -f /home/dima/access_log.log
-c 50 – количество одновременных запросов к компьютеру web-приложения, t300S –
время имитации, -d 1 – интервал между запросами (от 0 до 1 секунды),
/home/dima/access_log.log – файл с адресами, на которые необходимо осуществлять
запросы. Наиболее простым способом получения файла access_log.log является
написание специальной функции, которая будет вызываться каждый раз при
загрузке какой – либо страницы web-приложения и фиксировать в определенном
файле уникальные адреса страниц. Далее следует открывать страницы webприложения, чтобы файл автоматически пополнялся адресами. Примерное
количество адресов, достаточных для выполнения имитации, будем считать 150
адресов. Это число больше, чем физическое число страниц web-приложения, но в
списке адресов имеются адреса запросов к страницам с разными параметрами GET и
POST.
Для реализации сбора адресов была написана функция:
void write_access_log(string filepath)
, где filepath – имя файла, в который будут записаны адреса посещаемых
пользователями страниц. Исходный код данной функции представлен в приложении
Д.
После
запуска
процесса
Siege
следует
сразу
же
запустить
панель
производительности. В течение некоторого времени будет осуществляться анализ и
фиксация данных нагрузки и в конце будет выведен результат в виде таблицы, по
которому можно будет судить о способности web-приложения выдерживать
нагрузки.
8.3. Оценка результатов тестирования
Для проведения первого теста было выбрано число одновременных
пользователей, равное 100 (с=100) и время имитации, равное 300 секунд (t=300S).
71
[root@v8733
~]#
siege
-c
100
-d
1
-t300S
–i
-f
/home/dima/access_log.log
При проведении данного теста были получены следующие результаты:
Таблица 2 – результаты теста 1; с=100, t=300S
Адрес страницы
Нагрузка,
Среднее время формирования страницы,
%
сек
/pc/index.php
49.43
6.0035
/index/new/all/index.php
18.92
10.6723
/pc/new/index.php
9.87
3.7513
/detail/index.php
5.99
1.7449
/index/top/index.php
1.39
3.0484
/xbox_360/index.php
1.20
2.0960
/about/index.php
0.80
1.9985
/advertisement/index.php
0.74
1.8442
/contacts/index.php
0.63
1.5661
/pc/upcoming/index.php
0.57
1.6750
/pc/top/index.php
5.62
0.4352
/search/index.php
2.92
0.7424
/index/new/index.php
1.94
2.6039
Данные результаты позволяют судить о том, что в сценариях компонентов
имеются серьезные недочёты, из-за которых используется большое количество
ресурсов компьютера и замедляется процесс формирования страницы и отправки её
пользователю. Наиболее серьёзному анализу следует подвергнуть компоненты
страниц /index/new/all/index.php. Как следует из диаграммы, размещенной на
рисунке 7 (раздел 6), на данной странице находится компонент games.calendar. В
процессе реализации логики работы компонента для получения свойств элементов
информационного блока, указанного в настройках компонента, используется метод
_CIBElement::GetProperties(), который выбирает все свойства элемента, следствием
72
чего стало большое число запросов к web-приложению и формирование большого
файла кэша (свыше 1 Мб). Данный метод был использован для универсальности
работы компонента: в одном из шаблонов компонента можно было бы получить
значение любого из свойств без дополнительных действий. Но платой за
универсальность является замедление работы компонента. После совещания с
заказчиком было принято решение изменить сценарий работы компонента, устранив
указанные выше недостатки и тем самым понизив универсальность компонента, но
значительно повысив производительность работы компонента.
После внесения изменений для тех же показателей был проведен второй тест,
который дал результаты, указанные в таблице 3.
Таблица 3 – результаты теста 2. с=100, t=300S
Адрес страницы
Нагрузка,
Среднее время формирования страницы,
%
сек
/pc/index.php
29.72
0.9559
/index/new/all/index.php
22.06
0.6110
/pc/new/index.php
4.13
0.7122
/detail/index.php
5.37
0.4817
/index/top/index.php
2.11
0.4624
/xbox_360/index.php
1.51
0.6463
/about/index.php
0.48
0.2360
/advertisement/index.php
0.72
0.5875
/contacts/index.php
0.48
0.4291
/pc/upcoming/index.php
1.03
0.9263
/pc/top/index.php
3.31
1.0613
/search/index.php
9.08
1.9390
/index/new/index.php
3.43
0.4970
Как видно из результатов теста 2, представленных в таблице 2, после
изменения логики работы проблемного компонента нагрузка на компьютер web73
приложения и время генерации страниц заметно уменьшились. Теперь только время
генерации страниц /search/index.php и /pc/top/index.php превышает одну секунду.
Это можно объяснить тем, что у компонентов games.search и games.raiting не
включено
кэширование.
Для
устранения
проблемы
необходимо
включить
кэширование данных компонентов.
Теперь, после устранения всех проблем, выявленных при предыдущих тестах,
можно увеличить число одновременных пользователей при имитации нагрузки до
300 и увеличить время имитации до 600 секунд (10 минут), чтобы оценить
поведение web-приложения при больших нагрузках.
[root@v8733
~]#
siege
-c
300
-d
1
–t600S
–i
-f
/home/dima/access_log.log
После окончания имитации были получены следующие результаты:
Таблица 4 – результаты теста 3. с=300, t=600S
Адрес страницы
/pc/index.php
Нагрузка,
Среднее время формирования страницы,
%
сек
33.79
0.4454
Таблица 4 – результаты теста 3. с=300, t=600S.
/index/new/all/index.php
23.66
0.2403
/pc/new/index.php
5.00
0.3264
/detail/index.php
4.28
0.1797
/index/top/index.php
2.49
0.2138
/xbox_360/index.php
1.74
0.3262
/about/index.php
0.26
0.1330
/advertisement/index.php
0.61
0.1560
/contacts/index.php
0.45
0.1843
/pc/upcoming/index.php
0.48
0.2465
/pc/top/index.php
2.82
0.3863
/search/index.php
5.28
0.3390
/index/new/index.php
3.78
0.2219
74
Как видно из результатов теста со значениями параметров c=300 и t=600S,
время генерации страниц и нагрузка на компьютер web-приложения заметно
уменьшились,
чему
поспос
обствовало
включение
кэширования
во
всех
компонентах и исправление ошибок разработки. Число в 300 одновременных
посетителей является достаточно большим и в то же время не вызывает долгой
генерации страниц (долгим считается время генерации страницы пользователям с
временным промежутком более 1-2 секунд), что позволяет сделать вывод о том, что
web-приложение обладает еще большим запасом ресурсов для обслуживания
посетителей.
75
Заключение
В дипломной работе осуществлена разработка информационного web-сайта на
основе системы «1С – Битрикс: Управление сайтом».
В процессе выполнения дипломной работы была спроектирована и
реализована структура хранения данных web-сайта, спроектирована и реализована
структура страниц web-сайта, написан код программных компонентов. Для
обеспечения сохранности данных было настроено автоматическое резервное
копирование таблиц базы данных и файлов файловой структуры web-сайта. Для
обеспечения
возможности
командной
разработки
была
настроена
система
управления версиями Subversion. Для проверки производительности web-сайта было
проведено
нагрузочное тестирование с последующей
оценкой результатов
тестирования и корректировкой сценариев работы web-сайта согласно результатам
тестирования.
В результате работы был получен web-сайт, удовлетворяющий требованиям
заказчика и доступный всем пользователям сети Internet. Web-сайт был
оптимизирован
под
высокие
нагрузки
тестирования.
76
согласно
результатам
нагрузочного
Список использованных источников
1. Кузнецов М.В., Симдянов И.В. Самоучитель PHP 5. – СПб.: БХВ –
Петербург, 2004. – 560 с.
2. Кузнецов
М.В.,
Симдянов
И.В.
Объектно-ориентированное
программирование на PHP. – СПб.: БХВ – Петербург, 2007. – 608 с.
3. Робачевский А.М., Немнюгин С.А., Стесик О.Л. Операционная система
UNIX. – 2-е изд., перераб. и доп. – СПб.: БХВ-Петербург, 2007. – 656 с.
4. Ben Collins-Sussman, Brian W. Firzpatrick, C. Michael Pilato. Version Control
with Subversion.: O’Reilly Media, Inc. 1005 Gravenstein Highway North,
Sebastopol, 2004. – 277 p.
5. Васвани В. Полный справочник по MySQL. – М.: Издательский дом
«Вильямс», 2006. – 528 с.
6. Бадд Э., Молл К., Коллизон С. Мастерская CSS: профессиональное
применение Web – стандартов. – М. : ООО «И.Д. Вильямс», 2007. – 272 с.
7. Бибо Б., Кац И. jQuery. Подробное руководство по продвинутому JavaScript.
– СПб.: Символ-Плюс, 2009. – 384 с.
8. Дари К., Бринзаре Б., Черчез-Тоза Ф., Бусика М. AJAX и PHP: разработка
динамических веб-приложений. – СПб.: Символ-Плюс, 2007. – 336 с.
9. Конверс Т., Парк Д., Морган К.. PHP 5 и MySQL. Библия пользователя. – М.
: Издательский дом «Вильямс», 2009. – 1216 с.
10. «1С – Битрикс: Управление сайтом». Документация для разработчиков. URL:
http://dev.1c-bitrix.ru/api_help/ (дата обращения: с 30.03.2010 по 01.06.2010).
11. Википедия — свободная энциклопедия. URL: http://ru.wikipedia.org/wiki/
(дата обращения: с 30.03.2010 по 01.06.2010)
12. Joe Dog Software – Siege Home. URL: http://joedog.org/index/siege-home (дата
обращения: 10.05.2010)
13. World Wide Web Consortium (W3C). URL: http://www.w3.org/ (дата
обращения: 05.04.2010).
77
Приложение A. Принцип работы системы «1С – Битрикс:
Управление сайтом»
А.1. Общие сведения
Физически система представляет собой совокупность файлов и папок, которые
располагаются в корневой директории компьютера, на котором находится webприложение. Файлы в основном представляют собой сценарии на языке PHP,
определяющие функциональность модулей системы, административного раздела.
Для поддержания структурной ясности файлы объединяются в разделы.
Например, сценарии, определяющие функциональность модуля «Главный модуль»
объединены в папку main, которая, в свою очередь, является одной из подпапок
папки modules.
«1С – Битрикс: Управление сайтом» является модульной структурой. В
системе определены модули, количество которых зависит от той или иной редакции
системы.
Возможность управления модулями предусмотрена в административном
разделе системы. В этом же разделе имеется возможность получить доступ к
файловой структуре web-приложения. Для того, чтобы получить доступ к
административному разделу, пользователь должен принадлежать к группе (группам)
пользователей, в настройках которой указан уровень доступа, позволяющий доступ
к административному разделу. Перед доступом в административный раздел
пользователь должен пройти процедуру авторизации.
Для доступа к работе самого web-приложения, разрабатываемого на основе
системы, предусмотрен публичный раздел. Данный раздел доступен всем
пользователям сети Internet, не требует авторизации. В данном разделе размещаются
страницы web-приложения и другие материалы (например, файлы изображений),
которые могут быть доступны пользователям сети Internet.
А.2. Модульная структура системы
Как уже было отмечено выше, система «1С – Битрикс: Управление сайтом»
является модульной системой, т.е. вся функциональность системы логически
78
состоит из функциональности независимых модулей, взаимодействующих между
собой. Количество модулей зависит от редакции системы, но имеется набор
модулей, которые присутствуют в каждой редакции и выполняют набор
необходимых функций. Это следующие модули:
 модуль «Главный модуль»;
 модуль «Управление структурой»;
 модуль «Информационные блоки»;
А.2.1. Главный модуль
Модуль «Главный модуль» выполняет основные функции работы системы и
построенного на ней web-приложения. В функции данного модуля входят:
 функция обработки URL запросов web-клиентов к компьютеру webприложения;
 функция инициализации web-приложения;
 функция выполнения подключения к базе данных;
 функция создания основных объектов классов API системы;
 функция выполнения загрузки страниц web-приложения;
 функция управления настройками web-приложения;
 функция выполнения обновления системы и web-приложения.
Основными функциями являются функция выполнения загрузки страниц webприложения функция создания основных объектов классов API и функция
подключения к базе данных. Описание действий, выполняемых системой при
загрузке страницы, будет описана в разделе 3.4.1. Подключение к базе данных
осуществляется неявно при загрузке страницы в зависимости от содержимого файла
dbconn.php, который выполняется во время загрузки страницы и содержит адрес, по
которому доступна база данных, логин и пароль пользователя базы данных и имя
базы данных.
А.2.2. Модуль «Управление структурой»
Модуль «Управление структурой» выполняет следующие функции:
79
 функция управления файловой структурой web-приложения (создание,
удаление, изменение файлов и папок);
 функция управления свойствами файлов и папок, в т.ч. определенных
разработчиком;
 функция поддержания целостности структуры файловой системы webприложения;
 функция управления доступом к файлам и папкам файловой системы на
уровне web-приложения.
А.2.3. Модуль «Информационные блоки»
Информационные блоки - модуль, позволяющий каталогизировать и
управлять различными типами (блоками) однородной информации.
Модуль «Информационные блоки» - это своеобразная надстройка над СУБД,
хранящей данные web-приложения. Наличие данного модуля позволяет удобно
разделить информацию по логическим разделам и управлять ей, не вдаваясь в
подробности хранения данных непосредственно в базе данных той или иной СУБД.
Можно выделить несколько основных функций модуля:
 функция представления данных, хранящихся в базе данных (разделение на
блоки, разделы);
 функция управления данными (удаление, редактирование, создание записей в
базе данных);
 функция поддержания целостности данных;
 функция обеспечения доступа к данным посредством API модуля.
В состав модуля «Информационные блоки» входят следующие определения:
Типы инфоблоков используются для группировки информационных блоков.
Информационные блоки одного типа чаще всего характеризуются принадлежностью
к определенной тематике и одинаковой (или схожей) структурой.
Информационные блоки – блоки однородной информации.
80
Разделы – логические единицы, используемые для группировки элементов
внутри информационного блока. С помощью разделов создается иерархическая
структура хранения информации.
Элементы
информационных
блоков
непосредственно
-
информация,
размещаемая в информационных блоках.
Совокупность данных сущностей и связей между ними составляет структуру
информационных блоков web-приложения.
А.3. Компоненты
А.3.1. Общие сведения
Компоненты системы являются блоками, с помощью которых строится
публичная часть web-приложения. В состав компонентов входят сценарии,
написанные и отлаженные один раз с целью многократного использования на
различных страницах web-приложения.
Компоненты можно сравнить с классами в языках программирования,
например в языке PHP. Классы также размещены в одном месте, имеют методы,
определяющие функциональность объектов класса и могут быть использованы
многократно в разных местах вызывающей программы.
Компоненты
функциональности
являются
средством
web-приложения.
Т.е.,
инкапсуляции
разработчику,
(сокрытия)
который
будет
использовать компонент, не обязательно знать, как конкретно реализована
функциональность, им предоставляемая. Разработчику достаточно знать имя
компонента, набор параметров и правила, по которым он должен быть подключен на
странице. Компонент может содержать в себе вызовы других компонентов. Эти
свойства компонентов показывают, насколько они похожи на классы в языках
программирования.
А.3.2. Файловая структура компонента
Физически компоненты «1С – Битрикс: Управление сайтом» представляют
набор файлов и папок, которые определяют функциональность компонента и
81
результаты его работы. Компонент должен быть размещен в папке web-приложения
bitrix/components.
Файловая структура компонента выглядит следующим образом:
<пространство_имен_компонентов>
<имя_компонента>
lang
templates
<имя_шаблона_компонента>
lang
images
template.php
style.css
script.js
.parameters.php
result_modifier.php
component.php
.description.php
.parameters.php
<пространство_имен_компонентов> - папка, в которой можно разместить
компоненты одного типа, имеющие какие – либо общие признаки или выполняющие
схожие действия, но с разными модулями или частями web-приложения. Например,
набор компонентов по работе с файловой структурой web-приложения логично
разместить в папке file_structure.
lang – папка, в которой размещаются подпапки, определяющие языковые
фразы, использованные в компоненте. Эта папка необходима для автоматического
выбора фраз на нужном языке, если web-приложение выводит информацию на
нескольких языках. В данной папке содержатся подпапки, именами которых
являются идентификаторы языков, настраиваемых в административном разделе
системы.
82
lang / ru (или идетификатор другого языка) - внутри данной папки содержатся
файлы с расширением .php и именем, соответствующим именам файлов,
находящихся на одном уровне с папкой lang. Содержимое файла является
сценарием на языке PHP, в котором определяется массив $MESS, ключами которого
являются идентификаторы языковых фраз, значениями – сами языковые фразы.
Например
<?php $MESS[“EMPTY_LIST”] = “Ничего не найдено”; ?>
После того, как этот массив определен, языковые фразы будут доступны в
одноименном файле с помощью функции API системы GetMessage, например:
<?php echo GetMessage(“EMPTY_LIST”); ?>
component.php – основной файл компонента, который содержит в себе
сценарий, определяющий функциональность компонента. В данном файле
производятся запросы к базе данных на получение данных, обработка этих данных,
проводятся вычисления, осуществляется кеширование данных, производится выбор
и подключение шаблона компонента. Без данного файла файловая структура с точки
зрения системы не является компонентом.
parameters.php – файл компонента, в котором производится настройка
параметров компонента. В сценарии, находящемся в данном файле, также возможны
запросы к базе данных, вычисления и т.д., аналогично файлу component.php. В
конечном итоге, сценарий файла .parameters.php сводится к определению массива
$arComponentParameters, который имеет следующую структуру:
$arComponentParameters = array(
“GROUPS” => array(
“идентификатор_группы_N” => array(
“NAME” => “имя_группы”
),
),
“PARAMETERS” => array(
“идентификатор_параметра_N” => array(
“NAME” => “имя_параметра”,
83
“TYPE” => “тип_параметра”,
“PARENT” => “идентификатор_группы”,
)
));
description.php – файл, в котором содержится сценарий, определяющий второе
имя компонента (первым является имя папки компонента), описание компонента и
схему расположения компонента в разделе «Компоненты 2.0» визуального
редактора административного раздела системы. Работа сценария в конечном итоге
сводится к определению специального массивa $arComponentDescription:
$arComponentDescription = array(
“NAME” => “имя_компонента”,
“DESCRIPTION” => “описание_компонента”,
“PATH” => array(
“ID” => “идетификатор_ветки_визуального_редактора”,
“NAME” => “имя_ветки_редактора”
)
);
templates – папка, содержащая в себе шаблоны вывода компонента, т.е.
сценарии, обеспечивающие формирование кода web-страниц, который должен быть
передан web-клиенту для последующего отображения пользователю.
templates / .default (другой шаблон) – папка конкретного шаблона. Шаблонов
компонента может быть несколько.
templates / .default / template.php – файл, содержащий сценарий, определяющий
дополнительные параметры компонента для конкретного шаблона. Сценарий файла
сводится в конечном итоге к определению массива $arTemplateParameters, во
многом схожего с описанным выше массивом $arComponentParameters.
$arTemplateParameters = array(
“идентификатор_параметра_N” => array(
“NAME” => “имя_параметра”,
“TYPE” => “тип_параметра”,
84
)
);
templates / .default / style.css – файл, в котором расположены стили CSS,
которые будут переданы web-клиенту вместе с выводом шаблона для последующего
отображения пользователю.
templates / .default / script.js – файл, в котором расположены сценарии
JavaScript, которые будут переданы web-клиенту вместе с выводом шаблона.
templates / .default / template.php – файл, содержащий в себе сценарий,
реализующий вывод данных в виде HTML – разметки или в другом виде, понятном
web-клиенту или другому устройству, обращающемуся к web-приложению.
templates / .default / lang – аналогична папке lang, описанной выше.
templates / .default / images – папка, в которой содержатся дополнительные
изображения, используемые в шаблоне.
templates / .default / result_modifirer.php – дополнительный сценарий, который
выполняется перед выполнением сценария шаблона компонента
А.3.3. Схема обмена данными между файлами компонента
Любой компонент должен быть вызван в сценарии страницы web-приложения.
Точная процедура вызова будет описана в разделе 3.4.4., но будем считать, что webстраница передает данные компоненту в момент вызова последнего.
85
Страница, содержащая сценарий
вызова компонента
Компонент
$arParams,
id шаблона
component.php
Шаблон 2
Шаблон 1
result_modifier.php
Шаблон N
$arParams,
$arResult
result_modifier.php
result_modifier.php
template.php
template.php
template.php
Рисунок А.1 – Схема обмена данными между файлами компонента
На рисунке А.1 представлена схема обмена данными между сценариями
компонента при вызове компонента на странице web-приложения.
При вызове компонента в сценарии web-страницы указываются значения
параметров компонента, которые затем посредством массива $arParams передаются
в сценарий файла component.php.
В
сценарии
файла
component.php
посредством
метода
IncludeComponentTemplate класса CBitrixComponent происходит вызов одного из
шаблонов компонента. id шаблона также определяется в сценарии страницы webприложения и неявно для разработчика передается указанный выше метод.
Соответственно, подключается сценарий файла template.php одного из шаблонов, в
который передается возможно измененный в сценарии component.php массив
$arParams и, также, сформированный в сценарии component.php массив $arResult.
Оба этих массива доступны также и в файле result_modifier.php, который
подключается перед подключением файла template.php.
Работа компонента заканчивается в момент завершения работы сценария
файла component.php, т.е. возможно выполнить действия уже после подключения
шаблона. Однако, если массив $arResult будет изменен в сценарии шаблона, в
сценарий файла компонента component.php измененные данные переданы не будут.
86
А.3.4. Публичный раздел системы
А.3.4.1. Порядок загрузки страницы web-приложения
При загрузке страницы web-приложения системой проделывается ряд
процедур, которые позволяют инициализировать web-приложение и обеспечить
правильность работы шаблона, компонентов и модулей, подключенных и
вызываемых в сценарии web-страницы.
Ниже приведены основные этапы, которая страница проходит при загрузке
системой. Часть этапов опущена, т.к. не имеет смысловой загрузки для разработки
проектного web-приложения.
1. Подключение /bitrix/php_interface/dbconn.php
2. Соединение с базой данных. Создается объект $DB класса CDatabase
3. Подключение /bitrix/php_interface/after_connect.php
4. Определение текущего сайта. Создается объект $APPLICATION класса CMain
и определяются константы SITE_ID, SITE_DIR, SITE_SERVER_NAME,
SITE_CHARSET, FORMAT_DATE, FORMAT_DATETIME, LANGUAGE_ID.
Также подключаются все классы и функции главного модуля.
5. Подключение /bitrix/php_interface/init.php
6. Открытие сессии. Создаются все переменные массива $_SESSION.
7. Определение пользователя, авторизация. Создается объект CUser $USER.
8. Определение текущего шаблона сайта
9. Начало буферизации вывода
10.Подключение /bitrix/templates/ID шаблона сайта/header.php
11.Вывод тела страницы в буфер
12.Подключение /bitrix/templates/ID шаблона сайта/footer.php
13.Завершение буферизации страницы
14.Завершение соединения с базой данных. Переменная $DB более недоступна.
А.3.4.2. Подключение модулей системы
Подключение модуля на web-странице фактически означает подключение всех
классов и функций API модуля, которые могут быть использованы в сценарии webстраницы и ее компонентов.
87
Для того, чтобы подключить модуль, необходимо вызвать специальный метод:
CModule::IncludeModule(“имя_модуля”)
, который производит подключение всех классов, функций и инициализирует
константы API модуля.
А.3.4.3. Подключение шаблонов web-приложения
Настройка шаблонов web-приложения производится в настройках webприложения. Для того, чтобы система при загрузке страницы могла определить
нужный шаблон и подключить его на страницу, страница должна иметь следующий
вид:
<?php
require_once($_SERVER[“DOCUMENT_ROOT”].”/bitrix/header.php”)
?>
-------------------Любой сценарий, текст, и т.д.-------------------<?php
require_once($_SERVER[“DOCUMENT_ROOT”].”/bitrix/footer.php”)
?>
А.3.4.4. Подключение компонентов web-приложения
Для того, чтобы вызвать компонент, необходимо также воспользоваться API
модуля «Главный модуль», вызвав специальный метод класса CMain, который
представлен на странице объектом $APPLICATION:
$APPLICATION -> InculdeComponent(
“пространство_имен_компонентов:имя_компонента”,
“id_шаблона_компонента”,
Array(
”Ключ_параметра_1” => ”значение_параметра_1”,
”Ключ_параметра_N” => ”значение_параметра_N”
) //массив значений параметров компонента
88
);
Первый параметр данного метода определяет имя подключаемого компонента,
перед которым через символ ”:” указывается имя пространства имен компонентов.
Второй параметр определяет идентификатор шаблона, который следует
подключить в компоненте.
Третий параметр компонента – это массив значений параметров компонента,
ключами которого являются идентификаторы параметров, значениями – значения
параметров.
89
Приложение Б. Требования заказчика к работе web-приложения
и дизайн-концепция
Задание на разработку сайта metagames.ru
Информационная архитектура
Структура
1. Главная
2. Список игр с алфавитным
указателем
3. Детальная страница игры
4. Страница с рейтингом
5. Страница с результатами поиска
 Рейтинг игр за последние 12 месяцев
 График выхода
 Новые игры, появившиеся в базе
Компонент «список игр»
Компонент «детальная страница игры»
Компонент «список игр»
Компонент «фильтры»
Компонент «результаты поиска»
Функционал
Компонент «Детальная страница игры»
Для каждой игры должна выводится следующая информация:
Базовая информация:
1. Название игры
2. Название игры на английском (если русское название отлично)
3. Официальное изображение игры (обложка игры)
4. Метаоценка
5. Оценка пользователей
6. Разработчик
7. Жанр
8. Дата выхода в России
9. Возраст
10.Краткая информация об игре
11.Кнопка «купить на ozon.ru»
12.Скриншоты (При нажатии на скриншот, он показывается в оригинальном
размере)
Также, в виде дополнительной вкладке над зоной со скриншотами у пользователя
есть возможность переключиться на:
13.Видеотрейлер
Дополнительная информация:
14.Системные требования (при нажатии показывается divс минимальными и
рекомендуемыми системными требованиями)
90
15.Возможность мультиплеера
16.Другие платформы на которых представлена игра
17.Дополнения
Кроме того, для каждой игры выводится две оценки:
 метаоценка(и ссылка «как получена»)
Внизу располагается зона «обзоры», в которой идёт список рецензий. Для каждой
рецензии выводится:
1. Итоговая оценка
2. Название издания
3. Первые несколько строк рецензии
4. Ссылка «Прочесть полную рецензию на сайте издания»
Компонент «рейтинги»
Компонент выводится на главной странице и представляет из себя рейтинг 10 игр с
наивысшей метаоценкой за последние 12 месяцев. 1ая игра отображается крупно.
Остальные – списком. Снизу располагается ссылка «посмотреть весь рейтинг»,
ведущая на страницу «лучшие игры».
Над рейтингом располагается вкладка «Самые ожидамые», выводящая в таком же
формате рейтинг самых ожидаемых игр.
91
Компонент «форма поиска»
Компонент представляет из себя стандартное текстовое поле для ввода и кнопку или
иконку «искать». Как только пользователь начинает набирать текст, ему
автоматически должны отображаться подсказки (в виде игр, названия которых
начинаются с этих же букв).
Компонент «результаты поиска»
В результаты поиска попадают игры, у которых свойства «название» или «название
по-английски» содержат в себе введенный пользователем запрос (е и ё должны
считаться одинаковыми буквами).
Компонент выводит список игр,
удовлетворяющих результатам поиска. Для каждой игры выводится:
1. Мини-обложка
2. Название (является ссылкой на детальное описание игры)
3. Платформа
4. Метаоценка
5. Дата выхода
6. Однострочное описание игры
92
Компонент «алфавитный указатель»
Выводит список букв для английского и русского алфавита. Каждая буква является
ссылкой на раздел, в котором находятся все игры на неё начинающиеся
Компонент «список игр»
Компонент представляет из себя список игр. По умолчанию выводится 15 игр с
датой выхода в России не ранее 12 последних месяцев.
Для каждой игры выводится:
1. Название (является ссылкой на детальную страницу карточки)
2. Обложка
3. Дата выхода в России
4. Жанр
5. Метаоценка
Компонент может принимать следующие параметры для фильтрации выводимого
списка игр:
1. Букву русского или английского алфавита
2. Начальную дату выхода в России
3. Конечную дату выхода в России
4. Жанр
5. Минимальную метаоценку
6. Минимальную оценку пользователей
7. Минимальный возраст
8. Максимальный возраст
Компонент «Фильтры»
Компонент фильтры выводится на странице рейтинга игр.
Кроме того, над рейтингом расположены параметры, по которым пользователь
имеет возможность изменять параметры рейтинга:
1. Дата выхода (в виде ползунка на шкале времени)
2. Жанр (выбор из списка)
3. Возраст (выбор из списка)
Внизу списка располагается ссылка «все игры», позволяющая просматривать
рейтинг в виде более лаконичного списка (без изображений).
Требования
Требования к верстке и отображению
Верстка всех страниц должна удовлетворять форматуxhtml 1.0 strict и проходить
соответствующую валидацию на сайте validator.w3.org.
Кроме того, все страницы сайтадолжны корректно отображаться в следующих
браузерах:
93
1.
2.
3.
4.
5.
Internet explorer 7.0+
Mozilla firefox 3.0+
Opera 9.0+
Google Chrome
Safari (!! Уточнить версию)
Сайт должен отображаться к кодировке UTF-8
Время выполнения каждой из страниц сайта не должно превышать одной секунды
Требования к интеграции и коду
Интеграция с платформой «1С-Битрикс» должна быть произведена абсолютно
корректно, таким образом, чтобы последующие обновления системы («1СБитрикс») никак не влияли на работы сайта. В частности:
1. Не должно быть изменено ни одного файла из тех, которые могут быть
изменены при обновлении
2. Номера всех инфоблоков должны быть использованы только в виде констант.
Константы должны быть вынесены в конфигурационный файл (init.php)
3. Все используемые на сайте компоненты (как полностью созданные новые, так
и существующие стандартные) должны соответствовать формату компонентов
2.0 с четко разделенной логикой и представлением
Код должен быть максимально подробно закомментирован. В частности, перед
каждой функцией должно быть прописано, что она делает, а также описан каждый
из ее входных параметров.
В названиях функций и переменных должен быть использован только английский
язык (транслит недопустим). Кроме того, названия функций и переменных должны
отражать суть, для которой они предназначены.
94
Приложение В. Исходные коды программных компонентов
В.1. Компонент games.calendar
Файл component.php
<?php
//компонент
if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();
try
{
//массив с именами месяцев
global $arMonthNames;
//обрабатываем массив $arParams
prepare_params($arParams);
//проверяем параметры
if(strlen($arParams["IBLOCK_TYPE"]) <= 0){
$arParams["IBLOCK_TYPE"] = IB_TYPE_GAMES;
}
$arParams["COUNT"] = intval($arParams["COUNT"]);
if($arParams["COUNT"] <= 0){
$arParams["COUNT"] = 10;
}
if(strlen($arParams["SORT_BY1"]) <= 0){
$arParams["SORT_BY1"] = "PROPERTY_MAIN_METAMARK";
}
if($arParams["SORT_ORDER1"] != "DESC"){
$arParams["SORT_ORDER1"] = "ASC";
}
if(strlen($arParams["SORT_BY2"]) <= 0){
$arParams["SORT_BY2"] = "NAME";
}
if($arParams["SORT_ORDER2"] != "DESC"){
$arParams["SORT_ORDER2"] = "ASC";
}
$arParams["MIN_METAMARK_YELLOW"] = intval($arParams["MIN_METAMARK_YELLOW"]);
$arParams["MIN_METAMARK_GREEN"] = intval($arParams["MIN_METAMARK_GREEN"]);
if($arParams["MIN_METAMARK_YELLOW"] <= 0){
$arParams["MIN_METAMARK_YELLOW"] = 50;
}
if($arParams["MIN_METAMARK_GREEN"] <= 0){
$arParams["MIN_METAMARK_GREEN"] = 75;
}
//параметры сортировки
$arSortOrder = array(
$arParams["SORT_BY1"] => $arParams["SORT_ORDER1"],
$arParams["SORT_BY2"] => $arParams["SORT_ORDER2"],
"ID" => "DESC"
);
//параметры фильтрации
$arFilter = array(
"IBLOCK_TYPE" => $arParams["IBLOCK_TYPE"],
"ACTIVE" => "Y",
"SECTION_ACTIVE" => "Y"
);
//если в настройках указана фильтрация по секции (платформе), задаем параметры фильтра
if(!empty($arParams["IBLOCK_SECTION_CODE"])){
$arFilter["SECTION_CODE"] = $arParams["IBLOCK_SECTION_CODE"];
}
$current_year = intval(date("Y"));
$current_month = intval(date("m"));
$current_day = intval(date("d"));
$top_day = get_max_day($current_month, $current_year);
//если указан фильтр, включаем его в запрос
if(isset($arParams["FILTER"]) && is_array($arParams["FILTER"]) &&
count($arParams["FILTER"]) > 0){
$arFilter = array_merge($arFilter, $arParams["FILTER"]);
if(isset($arFilter[">=PROPERTY_DATES_DATE_REL_RUS"])){
95
$selected_month = intval(date("m",
strtotime($arFilter[">=PROPERTY_DATES_DATE_REL_RUS"])));
$selected_year = intval(date("Y",
strtotime($arFilter[">=PROPERTY_DATES_DATE_REL_RUS"])));
if($arParams["DATE_BORDER"] == "bottom"){
if($selected_year < $current_year){
$arFilter[">=PROPERTY_DATES_DATE_REL_RUS"] = date("Y-m-d");
}
elseif($selected_year == $current_year){
if($selected_month <= $current_month){
$arFilter[">=PROPERTY_DATES_DATE_REL_RUS"] = date("Y-m-d");
}
}
}
}
else{
$selected_month = date("m");
if($arParams["DATE_BORDER"] == "bottom" && $selected_month == $current_month){
$arFilter[">=PROPERTY_DATES_DATE_REL_RUS"] = date("Y-m-d");
}
}
if(isset($arFilter["<=PROPERTY_DATES_DATE_REL_RUS"])){
$selected_month = intval(date("m",
strtotime($arFilter["<=PROPERTY_DATES_DATE_REL_RUS"])));
$selected_year = intval(date("Y",
strtotime($arFilter["<=PROPERTY_DATES_DATE_REL_RUS"])));
if($arParams["DATE_BORDER"] == "top"){
if($selected_year > $current_year){
$arFilter["<=PROPERTY_DATES_DATE_REL_RUS"] = date("Y-m-d");
}
elseif($selected_year == $current_year){
if($selected_month >= $current_month){
$arFilter["<=PROPERTY_DATES_DATE_REL_RUS"] = date("Y-m-d");
}
}
}
}
else{
$selected_month = date("m");
if($arParams["DATE_BORDER"] == "top" && $selected_month == $current_month){
$arFilter["<=PROPERTY_DATES_DATE_REL_RUS"] = date("Y-m-d");
}
}
}
else{
//не выводим с датой меньше или больше текущей
if($arParams["DATE_BORDER"] == "top"){
$arFilter["<=PROPERTY_DATES_DATE_REL_RUS"] = date("Y-m-d");
}
elseif($arParams["DATE_BORDER"] == "bottom"){
$arFilter[">=PROPERTY_DATES_DATE_REL_RUS"] = date("Y-m-d");
}
//иначе указываем фильтрацию по текущему месяцу
if($arParams["CURRENT_MONTH_DEFAULT"] == "Y"){
if($arParams["DATE_BORDER"] != "bottom"){
$arFilter[">=PROPERTY_DATES_DATE_REL_RUS"] = date("Y-m-d",
strtotime("01.".$current_month.".".$current_year));
}
else{
$arFilter[">=PROPERTY_DATES_DATE_REL_RUS"] = date("Y-m-d");
}
if($arParams["DATE_BORDER"] != "top"){
$arFilter["<=PROPERTY_DATES_DATE_REL_RUS"] = date("Y-m-d",
strtotime($top_day.".".$current_month.".".$current_year));
}
else{
$arFilter["<=PROPERTY_DATES_DATE_REL_RUS"] = date("Y-m-d");
}
}
}
//кеширование зависит от настроек фильтра, отобразим это в ID кеша
96
//вносим в кеш данные постраничной навигации
$cache_id_arr = array(CDBResult::NavStringForCache($arParams["COUNT"]));
//и параметры фильтра
foreach($arFilter as $filt){
$cache_id_arr[] = trim(str_replace(array(";", ":", " ","-"), "", $filt));
}
$cache_id = implode("", $cache_id_arr);
//с данного момента включаем кеширование
if($this->StartResultCache(false, $cache_id)){
//формируем костяк массива, принимаемого в шаблоне
$arResult = array(
"ITEMS" => array(),
//массив самих игр
"NO_ITEMS" => false, //флаг наличия элементов в результирующем сете
"PAGENAV" => "",
//строка постраничной навигации
"IS_ERROR" => false
//флаг наличия какой-либо ошибки
);
//проверяем, подключен ли модуль
if(!CModule::IncludeModule("iblock")){
$arResult["IS_ERROR"] = true;
throw new Exception(GetMessage("C_MODULE_ERROR"));
}
$arSelect = array(
"NAME", "ID", "DETAIL_PAGE_URL", "PREVIEW_PICTURE",
"PROPERTY_DATES_DATE_REL_RUS", "PROPERTY_MAIN_METAMARK",
"PROPERTY_GENRE_GENRE", "IBLOCK_SECTION_ID", "PROPERTY_DATES_DATE_REL"
);
$db_element = CIBlockElement::GetList($arSortOrder, $arFilter, false,
array("nPageSize" => $arParams["COUNT"]), $arSelect);
//ошибка запроса к базе данных
if(!$db_element){
$arResult["IS_ERROR"] = true;
throw new Exception(DB_ERROR_TEXT);
}
//если элементов в запросе не найдено
if($db_element->SelectedRowsCount() <= 0){
$arResult["IS_ERROR"] = true;
$arResult["NO_ITEMS"] = true;
throw new Exception(GetMessage("C_NO_ITEMS"));
}
//разбиваем сет запроса на страницы
$db_element->NavStart($arParams["COUNT"]);
//получаем постраничную навигацию
$arResult["PAGENAV"] = $db_element->GetNavPrint("", false, "",
"/bitrix/templates/.default/pagenav_template.php");
//если в настройках компонента задан шаблон пути к элементу на детальн. страницу,
обрабатываем шаблон пути
if(!empty($arParams["DETAIL_URL"])){
$db_element->SetUrlTemplates($arParams["DETAIL_URL"]);
}
//массив ID секций (платформ)
$section_ids = array();
//связь между секцией и элементами (для быстрого вывода в шаблоне)
$section_id_elem = array();
//получаем элементы по одному
while($fetch_element = $db_element->GetNext()){
//получаем поля элемента (основные)
$fields["NAME"] = htmlspecialchars($fetch_element["NAME"]);
$fields["DETAIL_PAGE_URL"] = $fetch_element["DETAIL_PAGE_URL"];
$fields["PREVIEW_PICTURE"] = intval($fetch_element["PREVIEW_PICTURE"]);
$fields["IBLOCK_SECTION_ID"] = intval($fetch_element["IBLOCK_SECTION_ID"]);
$fields["ID"] = intval($fetch_element["ID"]);
//если задана превью-обложка, получаем массив ее изображения
if($fields["PREVIEW_PICTURE"] > 0){
$fields["PREVIEW_PICTURE"] = CFile::GetFileArray($fields["PREVIEW_PICTURE"]);
}
else{
$fields["PREVIEW_PICTURE"] = false;
}
//получаем свойства элемента
$props["DATES_DATE_REL_RUS"]["VALUE"] =
trim(htmlspecialchars($fetch_element["PROPERTY_DATES_DATE_REL_RUS_VALUE"]));
97
$props["DATES_DATE_REL"]["VALUE"] =
trim(htmlspecialchars($fetch_element["PROPERTY_DATES_DATE_REL_VALUE"]));
//если не указана дата выхода в России, используем дату выхода в общем
$props["MAIN_METAMARK"]["VALUE"] =
floatval($fetch_element["PROPERTY_MAIN_METAMARK_VALUE"]);
$props["GENRE_GENRE"]["VALUE"] = $fetch_element["PROPERTY_GENRE_GENRE_VALUE"];
if(empty($props["DATES_DATE_REL_RUS"]["VALUE"])){
$props["DATES_DATE_REL_RUS"]["VALUE"] = $props["DATES_DATE_REL"]["VALUE"];
}
//формируем вывод даты в соотвествии с требованиями
if(!empty($props["DATES_DATE_REL_RUS"]["VALUE"])){
$day = intval(date("d", strtotime($props["DATES_DATE_REL_RUS"]["VALUE"])));
$month_name = mb_substr($arMonthNames[intval(date("m",
strtotime($props["DATES_DATE_REL_RUS"]["VALUE"])))]["month_a"], 0, 3, LANG_CHARSET);
$month_name_full = $arMonthNames[intval(date("m",
strtotime($props["DATES_DATE_REL_RUS"]["VALUE"])))]["month_a"];
$year = date("Y", strtotime($props["DATES_DATE_REL_RUS"]["VALUE"]));
$props["DATES_DATE_REL_RUS"]["VALUE"] = $day." ".$month_name." ".$year;
$props["DATES_DATE_REL_RUS"]["VALUE_FULL"] = $day." ".$month_name_full."
".$year;
}
/*** В зависимости от значения метаоценки определяем стиль, который будет применен
к метаоценке при выводе ***/
if($props["MAIN_METAMARK"]["VALUE"] >= $arParams["MIN_METAMARK_GREEN"]){
$props["STYLE_RAITING"] = "green";
}
elseif($props["MAIN_METAMARK"]["VALUE"] >= $arParams["MIN_METAMARK_YELLOW"] &&
$props["MAIN_METAMARK"]["VALUE"] < $arParams["MIN_METAMARK_GREEN"]){
$props["STYLE_RAITING"] = "yellow";
}
elseif($props["MAIN_METAMARK"]["VALUE"] > 0){
$props["STYLE_RAITING"] = "red";
}
else{
$props["STYLE_RAITING"] = "no_mark";
}
/*** Конец определения стиля для метаоценки ***/
//забисываем в выходящий массив данные игры
$arResult["ITEMS"][$fields["ID"]] = array(
"FIELDS" => $fields,
"PROPS" => $props
);
//записываем ID всех секций элементов, находящихся в сете
if(!in_array($fields["IBLOCK_SECTION_ID"], $section_ids)){
$section_ids[] = $fields["IBLOCK_SECTION_ID"];
}
//и формируем связь между секцией и элементом
$section_id_elem[$fields["IBLOCK_SECTION_ID"]][] = $fields["ID"];
}
//готовим запрос к секциям инфоблока для получения нужных секций
$arSectionFilter = array(
"IBLOCK_TYPE" => $arParams["IBLOCK_TYPE"],
"IBLOCK_ID" => $arParams["IBLOCK_ID"],
"ID" => $section_ids,
"ACTIVE" => "Y"
);
//проводим запрос к списку секций (платформ)
$db_section = CIBlockSection::GetList(array(), $arSectionFilter, false, array("NAME",
"CODE", "PICTURE", "UF_*"));
//и получаем необходимые данные секции, занося их в данные по элементу (для вывода)
while($ar_section = $db_section->GetNext()){
foreach($section_id_elem[$ar_section["ID"]] as $elem_to_id){
//имя секции (платформы)
$arResult["ITEMS"][$elem_to_id]["FIELDS"]["SECTION_NAME"] =
$ar_section["NAME"];
//код секции (платформы)
$arResult["ITEMS"][$elem_to_id]["FIELDS"]["SECTION_CODE"] =
$ar_section["CODE"];
//цвет текста для вывода платформы
98
$arResult["ITEMS"][$elem_to_id]["FIELDS"]["SECTION_TEXT_COLOR"] =
$ar_section["UF_TEXT_COLOR"];
//если задана картинка для секции (маленькая), получаем массив ее изображения
if(intval($ar_section["PICTURE"]) > 0){
$arResult["ITEMS"][$elem_to_id]["FIELDS"]["SECTION_PICTURE"] =
CFile::GetFileArray(intval($ar_section["PICTURE"]));
}
}
}
//подключаем шаблон
$this->IncludeComponentTemplate();
}
}
catch(Exception $e)
{
//произошла ошибка, сбрасываем кеш
//чистим кеш
if(!$arResult["NO_ITEMS"]){
$this->ClearResultCache($cache_id);
}
$this->AbortResultCache();
//пишем ошибку в компонент arResult
$arResult["ERROR"] = $e->getMessage();
//подключаем шаблон
$this->IncludeComponentTemplate();
}?>
В.2. Компонент games.raiting
Файл component.php
<?php
//компонент
if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();
try
{
global $arMonthNames;
//обрабатываем массив $arParams
prepare_params($arParams);
//проверяем параметры
if(strlen($arParams["IBLOCK_TYPE"]) <= 0){
$arParams["IBLOCK_TYPE"] = IB_TYPE_GAMES;
}
$arParams["COUNT"] = intval($arParams["COUNT"]);
if($arParams["COUNT"] <= 0){
$arParams["COUNT"] = 10;
}
if(strlen($arParams["SORT_BY1"]) <= 0){
$arParams["SORT_BY1"] = "PROPERTY_MAIN_METAMARK";
}
if($arParams["SORT_ORDER1"] != "DESC"){
$arParams["SORT_ORDER1"] = "ASC";
}
if(strlen($arParams["SORT_BY2"]) <= 0){
$arParams["SORT_BY2"] = "NAME";
}
if($arParams["SORT_ORDER2"] != "DESC"){
$arParams["SORT_ORDER2"] = "ASC";
}
$arParams["MIN_METAMARK_YELLOW"] = intval($arParams["MIN_METAMARK_YELLOW"]);
$arParams["MIN_METAMARK_GREEN"] = intval($arParams["MIN_METAMARK_GREEN"]);
if($arParams["MIN_METAMARK_YELLOW"] <= 0){
$arParams["MIN_METAMARK_YELLOW"] = 50;
}
if($arParams["MIN_METAMARK_GREEN"] <= 0){
$arParams["MIN_METAMARK_GREEN"] = 75;
}
//параметры сортировки
$arSortOrder = array(
$arParams["SORT_BY1"] => $arParams["SORT_ORDER1"],
99
$arParams["SORT_BY2"] => $arParams["SORT_ORDER2"],
"ID" => "DESC"
);
//параметры фильтрации
$arFilter = array(
"IBLOCK_TYPE" => $arParams["IBLOCK_TYPE"],
"ACTIVE" => "Y",
"SECTION_ACTIVE" => "Y"
);
//если в настройках указана фильтрация по секции (платформе), задаем параметры фильтра
if(!empty($arParams["IBLOCK_SECTION_CODE"])){
$arFilter["SECTION_CODE"] = $arParams["IBLOCK_SECTION_CODE"];
}
//если указан фильтр, включаем его в запрос
if(isset($arParams["FILTER"]) && is_array($arParams["FILTER"]) &&
count($arParams["FILTER"]) > 0){
$arFilter = array_merge($arFilter, $arParams["FILTER"]);
}
//кеширование зависит от настроек фильтра, отобразим это в ID кеша
//вносим в кеш данные постраничной навигации
$cache_id_arr = array(CDBResult::NavStringForCache($arParams["COUNT"]));
//и параметры фильтра
foreach($arFilter as $filt){
$cache_id_arr[] = trim(str_replace(array(";", ":", " ","-"), "", $filt));
}
$cache_id = implode("", $cache_id_arr);
//с данного момента включаем кеширование
if($this->StartResultCache(false, $cache_id)){
//формируем костяк массива, принимаемого в шаблоне
$arResult = array(
"ITEMS" => array(),
//массив самих игр
"NO_ITEMS" => false, //флаг наличия элементов в результирующем сете
"PAGENAV" => "",
//строка постраничной навигации
"IS_ERROR" => false
//флаг наличия какой-либо ошибки
);
//проверяем, подключен ли модуль
if(!CModule::IncludeModule("iblock")){
$arResult["IS_ERROR"] = true;
throw new Exception(GetMessage("C_MODULE_ERROR"));
}
$arSelect = array(
"NAME", "ID", "DETAIL_PAGE_URL", "PREVIEW_PICTURE",
"PROPERTY_DATES_DATE_REL_RUS", "PROPERTY_MAIN_METAMARK",
"PROPERTY_GENRE_GENRE", "IBLOCK_SECTION_ID", "PROPERTY_DATES_DATE_REL"
);
$db_element = CIBlockElement::GetList($arSortOrder, $arFilter, false,
array("nPageSize" => $arParams["COUNT"]), $arSelect);
//ошибка запроса к базе данных
if(!$db_element){
$arResult["IS_ERROR"] = true;
throw new Exception(DB_ERROR_TEXT);
}
//если элементов в запросе не найдено
if($db_element->SelectedRowsCount() <= 0){
$arResult["IS_ERROR"] = true;
$arResult["NO_ITEMS"] = true;
throw new Exception(GetMessage("C_NO_ITEMS"));
}
//разбиваем сет запроса на страницы
$db_element->NavStart($arParams["COUNT"]);
//получаем постраничную навигацию
$arResult["PAGENAV"] = $db_element->GetNavPrint("", false, "",
"/bitrix/templates/.default/pagenav_template.php");
//если в настройках компонента задан шаблон пути к элементу на детальн. страницу,
обрабатываем шаблон пути
if(!empty($arParams["DETAIL_URL"])){
$db_element->SetUrlTemplates($arParams["DETAIL_URL"]);
}
//массив ID секций (платформ)
$section_ids = array();
//связь между секцией и элементами (для быстрого вывода в шаблоне)
100
$section_id_elem = array();
//получаем элементы по одному
while($fetch_element = $db_element->GetNext()){
//получаем поля элемента (основные)
$fields["NAME"] = htmlspecialchars($fetch_element["NAME"]);
$fields["DETAIL_PAGE_URL"] = $fetch_element["DETAIL_PAGE_URL"];
$fields["PREVIEW_PICTURE"] = intval($fetch_element["PREVIEW_PICTURE"]);
$fields["IBLOCK_SECTION_ID"] = intval($fetch_element["IBLOCK_SECTION_ID"]);
$fields["ID"] = intval($fetch_element["ID"]);
//если задана превью-обложка, получаем массив ее изображения
if($fields["PREVIEW_PICTURE"] > 0){
$fields["PREVIEW_PICTURE"] = CFile::GetFileArray($fields["PREVIEW_PICTURE"]);
}
else{
$fields["PREVIEW_PICTURE"] = false;
}
//получаем свойства элемента
$props["DATES_DATE_REL_RUS"]["VALUE"] =
trim(htmlspecialchars($fetch_element["PROPERTY_DATES_DATE_REL_RUS_VALUE"]));
$props["DATES_DATE_REL"]["VALUE"] =
trim(htmlspecialchars($fetch_element["PROPERTY_DATES_DATE_REL_VALUE"]));
//если не указана дата выхода в России, используем дату выхода в общем
$props["MAIN_METAMARK"]["VALUE"] =
floatval($fetch_element["PROPERTY_MAIN_METAMARK_VALUE"]);
$props["GENRE_GENRE"]["VALUE"] = $fetch_element["PROPERTY_GENRE_GENRE_VALUE"];
if(empty($props["DATES_DATE_REL_RUS"]["VALUE"])){
$props["DATES_DATE_REL_RUS"]["VALUE"] = $props["DATES_DATE_REL"]["VALUE"];
}
//формируем вывод даты в соотвествии с требованиями
if(!empty($props["DATES_DATE_REL_RUS"]["VALUE"])){
$day = intval(date("d", strtotime($props["DATES_DATE_REL_RUS"]["VALUE"])));
$month_name = mb_substr($arMonthNames[intval(date("m",
strtotime($props["DATES_DATE_REL_RUS"]["VALUE"])))]["month_a"], 0, 3, LANG_CHARSET);
$month_name_full = $arMonthNames[intval(date("m",
strtotime($props["DATES_DATE_REL_RUS"]["VALUE"])))]["month_a"];
$year = date("Y", strtotime($props["DATES_DATE_REL_RUS"]["VALUE"]));
$props["DATES_DATE_REL_RUS"]["VALUE"] = $day." ".$month_name." ".$year;
$props["DATES_DATE_REL_RUS"]["VALUE_FULL"] = $day." ".$month_name_full."
".$year;
}
/*** В зависимости от значения метаоценки определяем стиль, который будет применен
к метаоценке при выводе ***/
if($props["MAIN_METAMARK"]["VALUE"] >= $arParams["MIN_METAMARK_GREEN"]){
$props["STYLE_RAITING"] = "green";
}
elseif($props["MAIN_METAMARK"]["VALUE"] >= $arParams["MIN_METAMARK_YELLOW"] &&
$props["MAIN_METAMARK"]["VALUE"] < $arParams["MIN_METAMARK_GREEN"]){
$props["STYLE_RAITING"] = "yellow";
}
elseif($props["MAIN_METAMARK"]["VALUE"] > 0){
$props["STYLE_RAITING"] = "red";
}
else{
$props["STYLE_RAITING"] = "no_mark";
}
/*** Конец определения стиля для метаоценки ***/
//забисываем в выходящий массив данные игры
$arResult["ITEMS"][$fields["ID"]] = array(
"FIELDS" => $fields,
"PROPS" => $props
);
//записываем ID всех секций элементов, находящихся в сете
if(!in_array($fields["IBLOCK_SECTION_ID"], $section_ids)){
$section_ids[] = $fields["IBLOCK_SECTION_ID"];
}
//и формируем связь между секцией и элементом
$section_id_elem[$fields["IBLOCK_SECTION_ID"]][] = $fields["ID"];
}
//готовим запрос к секциям инфоблока для получения нужных секций
101
$arSectionFilter = array(
"IBLOCK_TYPE" => $arParams["IBLOCK_TYPE"],
"IBLOCK_ID" => $arParams["IBLOCK_ID"],
"ID" => $section_ids,
"ACTIVE" => "Y"
);
//проводим запрос к списку секций (платформ)
$db_section = CIBlockSection::GetList(array(), $arSectionFilter, false,
array("UF_*"));
//и получаем необходимые данные секции, занося их в данные по элементу (для вывода)
while($ar_section = $db_section->GetNext()){
foreach($section_id_elem[$ar_section["ID"]] as $elem_to_id){
//имя секции (платформы)
$arResult["ITEMS"][$elem_to_id]["FIELDS"]["SECTION_NAME"] =
$ar_section["NAME"];
//код секции (платформы)
$arResult["ITEMS"][$elem_to_id]["FIELDS"]["SECTION_CODE"] =
$ar_section["CODE"];
//цвет текста для вывода платформы
$arResult["ITEMS"][$elem_to_id]["FIELDS"]["SECTION_TEXT_COLOR"] =
$ar_section["UF_TEXT_COLOR"];
//если задана картинка для секции (маленькая), получаем массив ее изображения
if(intval($ar_section["PICTURE"]) > 0){
$arResult["ITEMS"][$elem_to_id]["FIELDS"]["SECTION_PICTURE"] =
CFile::GetFileArray(intval($ar_section["PICTURE"]));
}
}
}
//подключаем шаблон
$this->IncludeComponentTemplate();
}
}
catch(Exception $e)
{
//произошла ошибка, сбрасываем кеш
//чистим кеш
if(!$arResult["NO_ITEMS"]){
$this->ClearResultCache($cache_id);
}
$this->AbortResultCache();
//пишем ошибку в компонент arResult
$arResult["ERROR"] = $e->getMessage();
//подключаем шаблон
$this->IncludeComponentTemplate();
}
?>
В.3. Компонент games.filter
Файл component.php
<?php
//компонент
if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();
try
{
//обрабатываем массив $arParams
prepare_params($arParams);
//проверяем параметры
if(strlen($arParams["IBLOCK_TYPE"]) <= 0){
$arParams["IBLOCK_TYPE"] = IB_TYPE_GAMES;
}
if(empty($arParams["IBLOCK_ID"])){
$arParams["IBLOCK_ID"] = IB_GAMES_PC;
}
$arParams["COUNT"] = intval($arParams["COUNT"]);
if($arParams["COUNT"] <= 0){
$arParams["COUNT"] = 10;
}
if(strlen($arParams["SORT_BY1"]) <= 0){
102
$arParams["SORT_BY1"] = "ID";
}
if($arParams["SORT_ORDER1"] != "DESC"){
$arParams["SORT_ORDER1"] = "ASC";
}
if(strlen($arParams["SORT_BY2"]) <= 0){
$arParams["SORT_BY2"] = "NAME";
}
if($arParams["SORT_ORDER2"] != "DESC"){
$arParams["SORT_ORDER2"] = "ASC";
}
if(empty($arParams["DATE_REL_RUS_PROPERTY_CODE"])){
$arParams["DATE_REL_RUS_PROPERTY_CODE"] = "DATES_DATE_REL_RUS";
}
if(empty($arParams["MAIN_METAMARK_PROPERTY_CODE"])){
$arParams["MAIN_METAMARK_PROPERTY_CODE"] = "MAIN_METAMARK";
}
if(empty($arParams["FILTER_VARIABLE_NAME"])){
$arParams["FILTER_VARIABLE_NAME"] = "arrFilter";
}
$arParams["DETAIL_DATE_MIN"] = intval($arParams["DETAIL_DATE_MIN"]);
if($arParams["DETAIL_DATE_MIN"] <= 0){
$arParams["DETAIL_DATE_MIN"] = 1997;
}
$arParams["DETAIL_DATE_MAX"] = intval($arParams["DETAIL_DATE_MAX"]);
if($arParams["DETAIL_DATE_MAX"] <= 0){
$arParams["DETAIL_DATE_MAX"] = date("Y");
}
$period_list = $arParams["PERIOD_LIST"];
$arParams["PERIOD_LIST"] = array();
//проверка вариантов интервалов времени
$j = 1;
foreach($period_list as $key => $value){
if(!$matches = get_parts_reg($value, "/\[(\-|\+*)(\d+)(.)\]/i")){
continue;
}
$text = $matches["text"];
$matches = $matches["matches"];
//print_r($matches);
$matches[3] = mb_strtolower($matches[3], LANG_CHARSET);
if($matches[3] != "m" && $matches[3] != "d" && $matches[3] != "y"){
continue;
}
$arParams["PERIOD_LIST"][$j] = array(
"TITLE" => $text,
"TYPE" => $matches[3],
"VALUE" => ($matches[1] == "-" ? (-1)*intval($matches[2]) : intval($matches[2]))
);
$j++;
}
//проверка метаоценок
$mark_list = $arParams["MARKS_LIST"];
$arParams["MARKS_LIST"] = array();
$j = 1;
foreach($mark_list as $key => $value){
if(!$matches = get_parts_reg($value, "/\[\b(\d+)\]\[(.+)\]/i")){
continue;
}
$text = $matches["text"];
$matches = $matches["matches"];
$arParams["MARKS_LIST"][$j] = array(
"TITLE" => $text,
"VALUE" => intval($matches[1]),
"COLOR" => $matches[2]
);
$j++;
}
$arResult = array(
"GENRES" => array(),
"PLATFORMS" => array(),
"POST_VARIABLES" => array()
103
);
/******
Обработка отправленной формы фильтра и формирование выходного массива с параметрами
фильтрации
******/
if(isset($_REQUEST["filter"]) || isset($_REQUEST["with_no_script"])){
//временный массив для фильтра
$filter_var = array();
//предварительная обработка данных пришедшего запроса
$_REQUEST["marki"] = intval($_REQUEST["marki"]);
$_REQUEST["datei"] = intval($_REQUEST["datei"]);
$_REQUEST["platform"] = trim($_REQUEST["platform"]);
$_REQUEST["genre"] = trim(htmlspecialchars(urldecode($_REQUEST["genre"])));
$_REQUEST["markv"] = floatval($_REQUEST["markv"]);
$_REQUEST["daterange"] = intval($_REQUEST["daterange"]);
$_REQUEST["dfrom"] = trim($_REQUEST["dfrom"]);
$_REQUEST["dto"] = trim($_REQUEST["dto"]);
$_REQUEST["alpha"] = mb_substr(trim(urldecode($_REQUEST["alpha"])), 0, 1,
LANG_CHARSET);
if(!empty($_REQUEST["platform"])){
$filter_var["SECTION_CODE"] = $_REQUEST["platform"];
}
if(strlen($_REQUEST["alpha"]) > 0){
if($_REQUEST["alpha"] == "1"){
$numbs = array();
for($i=0; $i <= 9; $i++){
$numbs[] = $i."%";
}
//$filter_var["NAME"] = implode("|", $numbs);
$filter_var[">NAME"] = "-1%";
$filter_var["<=NAME"] = "9%";
}
else{
$filter_var["NAME"] = $_REQUEST["alpha"]."%";
}
//echo $filter_var["NAME"];
}
if(eregi("^[0-9]{4}$", $_REQUEST["dfrom"])){
$f_date_from = "year";
$_REQUEST["dfrom"] = intval($_REQUEST["dfrom"]);
if($_REQUEST["dfrom"] <= 0){
$_REQUEST["dfrom"] = $arParams["DETAIL_DATE_MIN"];
}
}
else{
if(strtotime($_REQUEST["dfrom"]) <= 0){
$_REQUEST["daterange"] = 0;
}
}
if(eregi("^[0-9]{4}$", $_REQUEST["dto"])){
$f_date_to = "year";
if($_REQUEST["dto"] <= 0){
$_REQUEST["dto"] = $arParams["DETAIL_DATE_MAX"];
}
}
else{
if(strtotime($_REQUEST["dto"]) <= 0){
$_REQUEST["daterange"] = 0;
}
}
//выьранный в списке временной интервал
$datei_key = $_REQUEST["datei"];
if($_REQUEST["daterange"] == 1){
$datei_key = 0;
$date_from = $_REQUEST["dfrom"];
$date_to = $_REQUEST["dto"];
if($f_date_from == "year" && $f_date_to == "year" && $date_to < $date_from){
$date_to = $date_from;
}
if($f_date_from == "year"){
104
$filter_var[">=PROPERTY_".$arParams["DATE_REL_RUS_PROPERTY_CODE"]] = date("Ym-d", strtotime("01.01.".$date_from));
}
else{
$filter_var[">=PROPERTY_".$arParams["DATE_REL_RUS_PROPERTY_CODE"]] = date("Ym-d", strtotime($date_from));
}
if($f_date_to == "year"){
$filter_var["<=PROPERTY_".$arParams["DATE_REL_RUS_PROPERTY_CODE"]] = date("Ym-d", strtotime("31.12.".$date_to));
}
else{
$filter_var["<=PROPERTY_".$arParams["DATE_REL_RUS_PROPERTY_CODE"]] = date("Ym-d", strtotime($date_to));
}
$arResult["POST_VARIABLES"]["datei"] = $date_from." - ".$date_to;
}
if($datei_key > 0){
//обрабатываем пришедший интервал, в зависимости от типа задавая параметры фильтру
$filter_var["<=PROPERTY_".$arParams["DATE_REL_RUS_PROPERTY_CODE"]] = date("Y-md");
$datei_type = $arParams["PERIOD_LIST"][$datei_key]["TYPE"];
$datei_value = intval($arParams["PERIOD_LIST"][$datei_key]["VALUE"]);
switch($datei_type)
{
//обрабатываем день
case "d":
//добавляем к текущей дате наше число со знаком минус
$interval_from = AddToTimestamp(array("DD" => -$datei_value));
break;
//месяц
case "m":
$interval_from = AddToTimestamp(array("MM" => -$datei_value));
break;
//год
case "y":
$interval_from = AddToTimestamp(array("YYYY" => -$datei_value));
break;
default:
break;
}
if($datei_value > 0){
$filter_var[">=PROPERTY_".$arParams["DATE_REL_RUS_PROPERTY_CODE"]] = date("Ym-d", $interval_from);
}
else{
$filter_var["<=PROPERTY_".$arParams["DATE_REL_RUS_PROPERTY_CODE"]] = date("Ym-d", $interval_from);
}
$arResult["POST_VARIABLES"]["datei"] =
$arParams["PERIOD_LIST"][$datei_key]["TITLE"];
}
//формируем в отдельном блоке фильтр по месяцам, если запрос пришел оттуда
$_REQUEST["month"] = intval($_REQUEST["month"]);
$_REQUEST["year"] = intval($_REQUEST["year"]);
if($_REQUEST["month"] > 0 && $_REQUEST["year"] > 0){
$top_day = get_max_day($_REQUEST["month"], $_REQUEST["year"]);
// echo $top_day;
$filter_var[">=PROPERTY_".$arParams["DATE_REL_RUS_PROPERTY_CODE"]] = date("Y-m-d",
strtotime("01.".$_REQUEST["month"].".".$_REQUEST["year"]));
$filter_var["<=PROPERTY_".$arParams["DATE_REL_RUS_PROPERTY_CODE"]] = date("Y-m-d",
strtotime($top_day.".".$_REQUEST["month"].".".$_REQUEST["year"]));
}
//выбранный интервал метаоценок
$marki_key = $_REQUEST["marki"];
$markv = $_REQUEST["markv"];
//обрабатываем пришедшую величину для метаоценки
//если указана точная метаоценка, формируем фильтр для нее
if($markv > 0){
$marki_key = -1;
$filter_var["PROPERTY_".$arParams["MAIN_METAMARK_PROPERTY_CODE"]] = $markv;
105
$arResult["POST_VARIABLES"]["marki"] = $markv;
}
//иначе если выбрано значение из списка интервалов, формируем фильтр по нему
if($marki_key > 0){
$filter_var[">=PROPERTY_".$arParams["MAIN_METAMARK_PROPERTY_CODE"]] =
$arParams["MARKS_LIST"][$marki_key]["VALUE"];
$arResult["POST_VARIABLES"]["marki"] =
$arParams["MARKS_LIST"][$marki_key]["TITLE"];
}
//формируем фильтр для жанра
$genre = $_REQUEST["genre"];
if(!empty($genre)){
$p_enum = CIBlockProperty::GetPropertyEnum($arParams["GENRE_PROPERTY_CODE"],
array(), array("XML_ID" => $genre));
$ar_enum = $p_enum->GetNext();
$filter_var["PROPERTY_".$arParams["GENRE_PROPERTY_CODE"]."_VALUE"] =
$ar_enum["VALUE"];
$arResult["POST_VARIABLES"]["GENRE"] = $ar_enum["VALUE"];
}
}
else{
//если мы на главной странице, то задаем опережающие значения фильра
if($arParams["MAIN_PAGE"] == "Y"){
$value_period_finded = false;
foreach($arParams["PERIOD_LIST"] as $key => $per){
if($per["TYPE"] == "m" && $per["VALUE"] == 6){
$value_period_finded = true;
break;
}
}
if($value_period_finded){
$_REQUEST["datei"] = $key;
$interval_from = AddToTimestamp(array("MM" => -6));
$filter_var[">=PROPERTY_".$arParams["DATE_REL_RUS_PROPERTY_CODE"]] = date("Ym-d", $interval_from);
$arResult["POST_VARIABLES"]["datei"] =
$arParams["PERIOD_LIST"][$key]["TITLE"];
}
}
}
$GLOBALS[$arParams["FILTER_VARIABLE_NAME"]] = $filter_var;
//проводим запрос к списку информационных блоков (смысл - список платформ)
$arIblockOrder = array(
$arParams["SORT_BY1"] => $arParams["SORT_ORDER1"],
$arParams["SORT_BY2"] => $arParams["SORT_ORDER2"],
"ID" => "DESC"
);
$arIblockFilter = array(
"IBLOCK_TYPE" => $arParams["IBLOCK_TYPE"],
"IBLOCK_ID" => $arParams["IBLOCK_ID"],
"ACTIVE" => "Y"
);
$db_iblock = CIBlockSection::GetList($arIblockOrder, $arIblockFilter);
if(!$db_iblock){
throw new Exception(DB_ERROR_TEXT);
}
//если платформ не найдено, нет смысла фильтровать, выходим
if($db_iblock->SelectedRowsCount() <= 0){
$this->IncludeComponentTemplate();
return;
}
while($fetch = $db_iblock->GetNext()){
$arResult["PLATFORMS"][$fetch["CODE"]] = array(
"NAME" => $fetch["NAME"],
"ID" => $fetch["ID"],
);
}
//запрос к списку жанров
$arEnumOrder = array(
"SORT" => "ASC",
"ID" => "ASC"
106
);
$arEnumFilter = array(
"CODE" => $arParams["GENRE_PROPERTY_CODE"],
"IBLOCK_ID" => $arParams["IBLOCK_ID"]
);
$db_enum = CIBlockPropertyEnum::GetList($arEnumOrder, $arEnumFilter);
while($fetch = $db_enum->GetNext()){
$genre_list[$fetch["XML_ID"]] = $fetch["VALUE"];
}
$arResult["ALL_GENRE"] = $genre_list;
//основные жанры
foreach($arParams["MAIN_GENRE"] as $val){
$arResult["MAIN_GENRE"][$val] = $genre_list[$val];
}
//подключаем шаблон
$this->IncludeComponentTemplate();
}
catch(Exception $ex)
{
$arResult["ERROR"] = $ex->getMessage();
}
?>
Файл script.js
/*скрипт*/
//устанавливает платформу в специальное поле типа input для последующего запроса к серверу и
выполняет запрос с помощью функции check_form_submit
function set_platform(path, form, iblock_id, result_block)
{
$("#iblock_id").val(iblock_id);
return check_form_submit(path, form, result_block);
}
function set_alpha(path, form, alpha, result_block)
{
$("#alpha").val(alpha);
return check_form_submit(path, form, result_block);
}
//собирает все инпуты и селекты из формы с id = ob и отправляет запрос на сервер
function check_form_submit(path, ob, result_block, additional_pars)
{
if($("#detail_date_enabled").val() == "0" || $("#detail_date_enabled").val() == ""){
$("#detail-date-from-catcher").val("");
$("#detail-date-to-catcher").val("");
}
if($("#mark_value").val() > 0){
document.getElementById("mark_interval").selectedIndex = 0;
}
//собираем все инпуты и селекты из фильтра
var query_string_arr = new Array("ajax_request_mode=Y");
var state_string_arr = new Array();
var query_string = "";
var state_string;
var query_string_ajax;
$("#"+ob.id+" input, #"+ob.id+" select").each(function(){
var name = $(this).attr("name");
var value = $(this).val();
if(name.length > 0 && value.length > 0 && name != "with_no_script"){
var val = parseInt(value);
if(!isNaN(val) && name != "dfrom" && name != "dto"){
if(val > 0 || name == "alpha"){
query_string_arr.push(name+"="+value);
state_string_arr.push(name+":"+value);
}
}
else{
query_string_arr.push(name+"="+value);
state_string_arr.push(name+":"+value);
}
}
107
});
query_string_arr.push(additional_pars);
query_string_ajax = query_string_arr.join("&");
query_string_arr.push("ajax_request_mode=Y");
query_string = query_string_arr.join("&");
state_string = state_string_arr.join("&");
set_server_request(path, query_string, "ajax-status", result_block, state_string);
return false;
}
$(function(){
//определяем набор ID элементов, задействованых в процессе имитации работы select'ов
var select_list_ids = new Array();
select_list_ids[0] = new Array("date_interval", "date-select-box", "date-select-catcher",
"date-select-opener", "detail_date_enabled");
select_list_ids[1] = new Array("mark_interval", "mark-select-box", "mark-select-catcher",
"mark-select-opener", "mark_value");
//проходим по этому списку, для каждого набора проделывая нужные действия
for(var j in select_list_ids)
{
var select_id = "#"+select_list_ids[j][0];
var select_id_v = select_list_ids[j][0];
var box_id = "#"+select_list_ids[j][1];
var catcher_id = "#"+select_list_ids[j][2];
var opener_id = "#"+select_list_ids[j][3];
var additional_id = "#" + select_list_ids[j][4];
//получаем из select записи
if(!document.getElementById(select_id_v)){
continue;
}
var list = document.getElementById(select_id_v).options;
for(var i=list.length - 1; i >=0; i--){
if($(list[i]).val().length <= 0){
continue;
}
//создаем последовательно объекты
var a = document.createElement("a");
//проверяем есть ли в названии слева код цвета текста
var html = list[i].text;
if(html.length <= 0){
continue;
}
if(html.indexOf("[") != -1 && html.indexOf("]") != -1){
from_index = html.indexOf("[");
to_index = html.indexOf("]");
var color = html.substring(from_index + 1, to_index);
if(color.length > 0){
var span = document.createElement("span");
$(span).css("color", color);
$(a).append(span);
}
html = html.substring(0, from_index);
list[i].text = list[i].text.substring(0, from_index);
$(span).html(html);
}
else{
$(a).html(html);
}
//тэгу a добавляем реакцию на событие - клик
$(a).hover(
function(e)
{
$(this).addClass("next_hover");
},
function(e)
{
$(this).removeClass("next_hover");
}
);
$(a).bind(
"click",
108
{list_option: list[i], b_id: box_id, add_id: additional_id, catcher:
catcher_id},
function(event){
$(event.data.add_id).val("");
var html = $(this).html();
$(event.data.catcher).attr("title", event.data.list_option.text);
$(event.data.catcher).val(event.data.list_option.text);
//выделяем нужный нам option
$(event.data.list_option).attr("selected", "selected");
//отправляем форму
document.getElementById("filter-box-form-sended").click();
$(event.data.b_id).hide();
});
$(a).addClass("next");
$(a).prependTo(box_id);
}
//связка по клику с кнопкой выбора
$(opener_id).bind(
"click",
{b_id : box_id, select_list_ids: select_list_ids},
function(event){
if($(event.data.b_id).css("display") == "none"){
$(event.data.b_id).show();
}
else{
$(event.data.b_id).hide();
}
for(var i in event.data.select_list_ids){
if(event.data.b_id != "#"+event.data.select_list_ids[i][1]){
$("#"+event.data.select_list_ids[i][1]).hide();
}
}
$("#genre-select-box").hide();
$("#detail-date-from-select-box").hide();
$("#detail-date-to-select-box").hide();
return false;
}
);
}
$("#detail-date-from-opener").bind(
"click",
function(event){
if($("#detail-date-from-select-box").css("display") == "none"){
$("#detail-date-from-select-box").show();
}
else{
$("#detail-date-from-select-box").hide();
}
$("#detail-date-to-select-box").hide();
return false;
}
);
$("#detail-date-to-opener").bind(
"click",
function(event){
if($("#detail-date-to-select-box").css("display") == "none"){
$("#detail-date-to-select-box").show();
}
else{
$("#detail-date-to-select-box").hide();
}
$("#detail-date-from-select-box").hide();
return false;
}
);
$("#detail-date-from-select-box a.next").bind("click", {list_option: false, b_id:
"#detail-date-from-select-box", catcher: "#detail-date-from-catcher"}, function(event){
$(event.data.catcher).attr("title", $(this).html());
$(event.data.catcher).datepicker('option',"defaultDate", "01.01."+$(this).html());
$(event.data.catcher).val($(this).html());
$(event.data.b_id).hide();
109
return false;
});
$("#detail-date-to-select-box a.next").bind("click", {list_option: false, b_id: "#detaildate-to-select-box", catcher: "#detail-date-to-catcher"}, function(event){
$(event.data.catcher).attr("title", $(this).html());
$(event.data.catcher).datepicker('option', "defaultDate", "31.12."+$(this).html());
$(event.data.catcher).val($(this).html());
$(event.data.b_id).hide();
return false;
});
//отдельная связка по клику для #genre-select-box
$("#genre-select-opener").bind(
"click",
{b_id : "#genre-select-box"},
function(event){
if($(event.data.b_id).css("display") == "none"){
$(event.data.b_id).show();
}
else{
$(event.data.b_id).hide();
}
$("#date-select-box").hide();
$("#mark-select-box").hide();
//$("#")
return false;
}
);
$("#genre-select-box a.next").bind("click", {list_option: false, b_id: "#genre-selectbox", catcher: "#genre-select-catcher"}, function(event){
$(event.data.catcher).attr("title", $(this).html());
$(event.data.catcher).val($(this).html());
var genre_list = document.getElementById("genre").options;
var check_flag = false;
for(var i = 0; i < genre_list.length; i++){
if(genre_list[i].text == $(this).html()){
$(genre_list[i]).attr("selected", "selected");
check_flag = true;
break;
}
}
if(!check_flag){
$(genre_list[0]).attr("selected", "selected");
}
//отправляем форму
document.getElementById("filter-box-form-sended").click();
$(event.data.b_id).hide();
return false;
});
$("#genre-select-box p.more a").bind("click", function(event){
if($("#genre-select-box .col").css("display") != "none"){
$("#genre-select-box .col").hide();
$("#genre-select-box .main").show();
$("#genre-select-box").addClass("genre_small");
$("#genre-select-box").removeClass("genre_wide");
$(this).html("Все жанры");
}
else{
$("#genre-select-box .col").show();
$("#genre-select-box .main").hide();
$("#genre-select-box").addClass("genre_wide");
$("#genre-select-box").removeClass("genre_small");
$(this).html("Основные");
}
return false;
});
$("div.filter-box .select-box .js-included").show();
$("div.filter-box .select-box .js-not-included").hide();
var def_date_from = $("#detail-date-from-catcher").val();
if(def_date_from && def_date_from.length > 0 && def_date_from.length <= 4){
def_date_from = "01.01."+def_date_from;
110
}
var def_date_to = $("#detail-date-to-catcher").val();
if(def_date_to && def_date_to.length > 0 && def_date_to.length <= 4){
def_date_to = "31.12."+def_date_to;
}
$("#detail_date_sender").bind("click", function(){
$("#detail_date_enabled").val("1");
});
$.datepicker.setDefaults(
$.extend($.datepicker.regional["ru"])
);
if(def_date_from && def_date_to){
$("#detail-date-from-catcher").datepicker({
dateFormat:"dd.mm.yy",
defaultDate: def_date_from
});
$("#detail-date-to-catcher").datepicker({
dateFormat:"dd.mm.yy",
defaultDate: def_date_to
});
}
$("#date_from_calendar").bind("click", function(){
$("#detail-date-from-catcher").datepicker("show");
return false;
});
$("#date_to_calendar").bind("click", function(){
$("#detail-date-to-catcher").datepicker("show");
return false;
});
$(".ui-datepicker").bind("click", function(event){
$("#date-select-box").show();
event.stopPropagation();
});
});
Файл result_modifier.php шаблона month_switcher
<?php
/*модификатор результата*/
global $arMonthNames;
//общее количество выводимых месяцев
if($arParams["DISPLAY_MONTHS_COUNT"] <= 0){
$arParams["DISPLAY_MONTHS_COUNT"] = 3;
}
//ограничение сверху или снизу
if(strlen($arParams["BORDER"]) <= 0){
$arParams["BORDER"] = "bottom";
}
//ограничения по годам для верха и для низа
$arParams["MIN_YEAR"] = intval($arParams["MIN_YEAR"]);
$arParams["MAX_YEAR"] = intval($arParams["MAX_YEAR"]);
if($arParams["MIN_YEAR"] <= 0){
$arParams["MIN_YEAR"] = 1990;
}
if($arParams["MAX_YEAR"] <= 0){
$arParams["MAX_YEAR"] = 2013;
}
$b_redirect = false;
$_REQUEST["month"] = intval($_REQUEST["month"]);
$_REQUEST["year"] = intval($_REQUEST["year"]);
if($_REQUEST["month"] > 12){
$_REQUEST["month"] = 12;
$b_redirect = true;
}
if($_REQUEST["month"] > 0 && $_REQUEST["year"] > 0 && !isset($_REQUEST["filter"])){
$b_redirect = true;
}
//если месяц задан в адресной строке, в качестве текущего используем его
if($_REQUEST["month"] > 0){
$current_month = $_REQUEST["month"];
111
}
else{
$current_month = intval(date("m"));
}
////аналогично для года
if($_REQUEST["year"] > 0){
$current_year = $_REQUEST["year"];
}
else{
$current_year = intval(date("Y"));
}
/*определяем середину интервала в зависимости от количества месяцев*/
if($arParams["DISPLAY_MONTHS_COUNT"] % 2 == 0){
$center = floor($arParams["DISPLAY_MONTHS_COUNT"] / 2);
}
else{
$center = ceil($arParams["DISPLAY_MONTHS_COUNT"] / 2);
}
//флаг того, что мы находимся в данный момент на текущей дате
$current_date_flag = false;
if($current_year == intval(date("Y")) && $current_month == intval(date("m"))){
$current_flag = true;
}
if($current_year < $arParams["MIN_YEAR"]){
$current_year = $arParams["MIN_YEAR"];
$current_month = 1;
$b_redirect = true;
}
$bottom_move_flag = false;
if($arParams["BORDER"] == "bottom" && ($current_year < intval(date("Y")) || ($current_month <
intval(date("m")) && $current_year <= intval(date("Y"))))){
$current_year = intval(date("Y"));
$current_month = intval(date("m"));
$b_redirect = true;
$bottom_move_flag = true;
}
if($current_year == $arParams["MIN_YEAR"] && $current_month == 1){
$bottom_move_flag = true;
}
$top_move_flag = false;
if($current_year > $arParams["MAX_YEAR"]){
$current_year = $arParams["MAX_YEAR"];
$current_month = 12;
$b_redirect = true;
$top_move_flag = true;
}
if($current_year == $arParams["MAX_YEAR"] && $current_month == 12){
$top_move_flag = true;
}
if($arParams["BORDER"] == "top" && ($current_year > intval(date("Y")) || ($current_month >
intval(date("m")) && $current_year >= intval(date("Y"))))){
$current_year = intval(date("Y"));
$current_month = intval(date("m"));
$b_redirect = true;
$top_move_flag = true;
}
$k = 1;
//массив с набором предыдущих месяцев
$arResult["PREV_MONTHS"] = array();
$prev_year = $current_year;
$prev_month = $current_month;
if(!$bottom_move_flag && ($arParams["BORDER"] != "bottom" || !$current_flag)){
while($k < $center){
if($prev_month - 1 <= 0){
$prev_year -= 1;
$prev_month = 12;
}
else{
$prev_month = $prev_month - 1;
}
112
$arResult["PREV_MONTHS"][] = array(
"month" => $prev_month,
"month_name" => $arMonthNames[$prev_month]["month"],
"month_year" => $prev_year,
);
$k++;
}
}
if($b_redirect){
LocalRedirect($APPLICATION>GetCurPageParam("filter=up&month=".$current_month."&year=".$current_year, array("filter",
"month", "year")));
exit(1);
}
$arResult["CURRENT_MONTH"] = $current_month;
$arResult["CURRENT_MONTH_NAME"] = $arMonthNames[$current_month]["month"];
$arResult["CURRENT_YEAR"] = $current_year;
$k = $center + 1;
$arResult["NEXT_MONTHS"] = array();
$next_month = $current_month;
$next_year = $current_year;
if(!$top_move_flag && ($arParams["BORDER"] != "top" || !$current_flag)){
while($k <= $arParams["DISPLAY_MONTHS_COUNT"]){
if($next_month + 1 > 12){
$next_year += 1;
$next_month = 1;
}
else{
$next_month = $next_month + 1;
}
$arResult["NEXT_MONTHS"][] = array(
"month" => $next_month,
"month_name" => $arMonthNames[$next_month]["month"],
"month_year" => $next_year,
);
$k++;
}
}
$arResult["PREV_MONTHS"] = array_reverse($arResult["PREV_MONTHS"]);
?>
В.4. Компонент games.search
Файл component.php
<?php
//компонент
if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();
function declension($numb, $forms = array())
{
$numb = strval($numb);
$numb_2 = substr($numb, strlen($numb) - 2);
$numb_1 = substr($numb, strlen($numb) - 1);
if($numb_2 == "11"){
return $forms[1];
}
if(intval($numb_2) > 9 && intval($numb_2) < 20){
return $forms[1];
}
if($numb_1 == "1"){
return $forms[0];
}
if($numb_1 == "2" || $numb_1 == "3" || $numb_1 == "4"){
return $forms[2];
}
return $forms[1];
}
try
{
113
//обрабатываем массив $arParams
prepare_params($arParams);
//проверяем параметры
if(strlen($arParams["IBLOCK_TYPE"]) <= 0){
$arParams["IBLOCK_TYPE"] = IB_TYPE_GAMES;
}
$arParams["COUNT"] = intval($arParams["COUNT"]);
if($arParams["COUNT"] <= 0){
$arParams["COUNT"] = 10;
}
if(strlen($arParams["SORT_BY1"]) <= 0){
$arParams["SORT_BY1"] = "PROPERTY_MAIN_METAMARK";
}
if($arParams["SORT_ORDER1"] != "DESC"){
$arParams["SORT_ORDER1"] = "ASC";
}
if(strlen($arParams["SORT_BY2"]) <= 0){
$arParams["SORT_BY2"] = "NAME";
}
if($arParams["SORT_ORDER2"] != "DESC"){
$arParams["SORT_ORDER2"] = "ASC";
}
$arParams["MIN_METAMARK_YELLOW"] = intval($arParams["MIN_METAMARK_YELLOW"]);
$arParams["MIN_METAMARK_GREEN"] = intval($arParams["MIN_METAMARK_GREEN"]);
if($arParams["MIN_METAMARK_YELLOW"] <= 0){
$arParams["MIN_METAMARK_YELLOW"] = 50;
}
if($arParams["MIN_METAMARK_GREEN"] <= 0){
$arParams["MIN_METAMARK_GREEN"] = 75;
}
//параметры сортировки
$arSortOrder = array(
$arParams["SORT_BY1"] => $arParams["SORT_ORDER1"],
$arParams["SORT_BY2"] => $arParams["SORT_ORDER2"],
"ID" => "DESC"
);
$search_text = trim(htmlspecialchars($_REQUEST["q"]));
if(empty($search_text)){
throw new Exception();
}
//параметры фильтрации
$arFilter = array(
"IBLOCK_TYPE" => $arParams["IBLOCK_TYPE"],
"ACTIVE" => "Y",
"SECTION_ACTIVE" => "Y"
);
if(strlen($search_text) > 0){
$arFilter["NAME"] = "%".$search_text."%";
}
$platform = trim(htmlspecialchars($_REQUEST["platform"]));
if(!empty($platform)){
$arFilter["SECTION_CODE"] = $platform;
}
//кеширование зависит от настроек фильтра, отобразим это в ID кеша
//вносим в кеш данные постраничной навигации
$cache_id_arr = array(CDBResult::NavStringForCache($arParams["COUNT"]));
//и параметры фильтра
$cache_id_arr[] = $search_text;
$cache_id_arr[] = $platform;
$cache_id = implode("", $cache_id_arr);
//с данного момента включаем кеширование
if($this->StartResultCache(false, $cache_id)){
//проверяем, подключен ли модуль
if(!CModule::IncludeModule("iblock")){
$arResult["IS_ERROR"] = true;
throw new Exception(GetMessage("C_MODULE_ERROR"));
}
//формируем костяк массива, принимаемого в шаблоне
$arResult = array(
"ITEMS" => array(),
//массив самих игр
"NO_ITEMS" => false, //флаг наличия элементов в результирующем сете
114
"PAGENAV" => "",
//строка постраничной навигации
"IS_ERROR" => false, //флаг наличия какой-либо ошибки
"SEARCH_TEXT" => $search_text,
"TOTAL_COUNT" => 0,
"PLATFORMS" => array()
);
//готовим запрос к секциям инфоблока для получения нужных секций
$arSectionFilter = array(
"IBLOCK_TYPE" => $arParams["IBLOCK_TYPE"],
"IBLOCK_ID" => $arParams["IBLOCK_ID"],
"ACTIVE" => "Y"
);
//проводим запрос к списку секций (платформ)
$db_section = CIBlockSection::GetList(array("SORT" => "ASC"), $arSectionFilter, false,
array("UF_*"));
//и получаем необходимые данные секции, занося их в данные по элементу (для вывода)
$ar_section = array();
$section_current_id = 0;
while($fetch = $db_section->GetNext()){
if($fetch["CODE"] == $arParams["IBLOCK_SECTION_CODE"]){
$section_current_id = $fetch["ID"];
}
$ar_section[$fetch["ID"]]["NAME"] = $fetch["NAME"];
//код секции (платформы)
$ar_section[$fetch["ID"]]["CODE"] = $fetch["CODE"];
//цвет текста для вывода платформы
$ar_section[$fetch["ID"]]["TEXT_COLOR"] = $fetch["UF_TEXT_COLOR"];
//изображение
$ar_section[$fetch["ID"]]["PICTURE"] = $fetch["PICTURE"];
//путь к папке
if(strlen($arParams["PATH_TO"]) > 0){
$ar_section[$fetch["ID"]]["PATH_TO"] = str_replace("#SECTION_CODE#",
$fetch["CODE"], $arParams["PATH_TO"]);
}
else{
$ar_section[$fetch["ID"]]["PATH_TO"] = $ar_section["SECTION_PAGE_URL"];
}
}
$arResult["PLATFORMS"] = $ar_section;
//запрос по полю "Название"
$db_element = CIBlockElement::GetList($arSortOrder, $arFilter, false/*,
$arNavParams*/);
//ошибка запроса к базе данных
if(!$db_element){
$arResult["IS_ERROR"] = true;
throw new Exception(DB_ERROR_TEXT);
}
$arResult["TOTAL_COUNT"] += $db_element->SelectedRowsCount();
unset($arFilter["NAME"]);
$arFilter["!NAME"] = "%".$search_text."%";
if(strlen($search_text) > 0){
$arFilter["PROPERTY_MAIN_ENG_NAME"] = "%".$search_text."%";
}
$db_element_eng = CIBlockElement::GetList($arSortOrder, $arFilter, false/*,
$arNavParams*/);
if(!$db_element_eng){
$arResult["IS_ERROR"] = true;
throw new Exception(DB_ERROR_TEXT);
}
$arResult["TOTAL_COUNT"] += $db_element_eng->SelectedRowsCount();
//echo $arResult["TOTAL_COUNT"];
//если элементов в запросе не найдено
if($arResult["TOTAL_COUNT"] <= 0){
$arResult["IS_ERROR"] = true;
$arResult["NO_ITEMS"] = true;
throw new Exception(GetMessage("C_NO_ITEMS"));
}
//если в настройках компонента задан шаблон пути к элементу на детальн. страницу,
обрабатываем шаблон пути
if(!empty($arParams["DETAIL_URL"])){
$db_element->SetUrlTemplates($arParams["DETAIL_URL"]);
115
$db_element_eng->SetUrlTemplates($arParams["DETAIL_URL"]);
}
$objects = array($db_element, $db_element_eng);
//массив ID секций (платформ)
$section_ids = array();
//связь между секцией и элементами (для быстрого вывода в шаблоне)
$section_id_elem = array();
//получаем элементы по одному
foreach($objects as $ob){
while($ob_element = $ob->GetNextElement()){
//получаем поля элемента (основные)
$fields = $ob_element->GetFields();
$fields["NAME"] = htmlspecialchars($fields["NAME"]);
//если задана превью-обложка, получаем массив ее изображения
if($fields["PREVIEW_PICTURE"] > 0){
$fields["PREVIEW_PICTURE"] =
CFile::GetFileArray($fields["PREVIEW_PICTURE"]);
}
else{
$fields["PREVIEW_PICTURE"] = false;
}
//получаем свойства элемента
$props = $ob_element->GetProperties();
//если не указана дата выхода в России, используем дату выхода в общем
if(empty($props["DATES_DATE_REL_RUS"]["VALUE"])){
$props["DATES_DATE_REL_RUS"]["VALUE"] = $props["DATES_DATE_REL"]["VALUE"];
}
/*** В зависимости от значения метаоценки определяем стиль, который будет
применен к метаоценке при выводе ***/
if($props["MAIN_METAMARK"]["VALUE"] >= $arParams["MIN_METAMARK_GREEN"]){
$props["STYLE_RAITING"] = "green";
}
elseif($props["MAIN_METAMARK"]["VALUE"] >= $arParams["MIN_METAMARK_YELLOW"] &&
$props["MAIN_METAMARK"]["VALUE"] < $arParams["MIN_METAMARK_GREEN"]){
$props["STYLE_RAITING"] = "yellow";
}
elseif($props["MAIN_METAMARK"]["VALUE"] > 0){
$props["STYLE_RAITING"] = "red";
}
else{
$props["STYLE_RAITING"] = "no_mark";
}
/*** Конец определения стиля для метаоценки ***/
//забисываем в выходящий массив данные игры
$arResult["ITEMS"][$fields["ID"]] = array(
"FIELDS" => $fields,
"PROPS" => $props
);
$arResult["ITEMS"][$fields["ID"]]["FIELDS"]["SECTION_NAME"] =
$ar_section[$fields["IBLOCK_SECTION_ID"]]["NAME"];
//код секции (платформы)
$arResult["ITEMS"][$fields["ID"]]["FIELDS"]["SECTION_CODE"] =
$ar_section[$fields["IBLOCK_SECTION_ID"]]["CODE"];
//цвет текста для вывода платформы
$arResult["ITEMS"][$fields["ID"]]["FIELDS"]["SECTION_TEXT_COLOR"] =
$ar_section[$fields["IBLOCK_SECTION_ID"]]["TEXT_COLOR"];
//если задана картинка для секции (маленькая), получаем массив ее изображения
if(intval($ar_section[$fields["IBLOCK_SECTION_ID"]]["PICTURE"]) > 0){
$arResult["ITEMS"][$fields["ID"]]["FIELDS"]["SECTION_PICTURE"] =
CFile::GetFileArray(intval($ar_section[$fields["IBLOCK_SECTION_ID"]]["PICTURE"]));
}
}
}
$cdbresult = new CDBResult;
$cdbresult->InitFromArray($arResult["ITEMS"]);
//разбиваем сет запроса на страницы
$cdbresult->NavStart($arParams["COUNT"]);
//получаем постраничную навигацию
116
$arResult["PAGENAV"] = $cdbresult->GetNavPrint("", false, "",
"/bitrix/templates/.default/pagenav_template.php");
$arResult["ITEMS"] = array();
while($res = $cdbresult->GetNext()){
$arResult["ITEMS"][] = $res;
}
//подключаем шаблон
$this->IncludeComponentTemplate();
}
}
catch(Exception $e)
{
//произошла ошибка, сбрасываем кеш
//чистим кеш
if(!$arResult["NO_ITEMS"]){
$this->ClearResultCache($cache_id);
}
$this->AbortResultCache();
//пишем ошибку в компонент arResult
$arResult["ERROR"] = $e->getMessage();
//подключаем шаблон
$this->IncludeComponentTemplate();
}
?>
В.5. Компонент games.detail
Файл component.php
<?php
//компонент
if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();
function get_file_parts($filename){
$ext = "";
//расширение
$fname = ""; //имя файла без расширения
$pos = strrpos($filename, ".");
//если точка найдена
if($pos !== false){
//вырезаем имя файла без расширения
$fname = substr($filename, 0, $pos);
//вырезаем расширение
$ext = ToLower(substr($filename, $pos + 1));
}
else{
$fname = $filename;
}
return array("file" => $filename, "extension" => $ext, "filename" => $fname);
}
function get_screens_by_game_code($gamecode, $path, $allow_extensions = array("jpg", "jpeg",
"gif", "png"))
{
$directory_path_full = $_SERVER["DOCUMENT_ROOT"].$path;
$gamecode = trim(htmlspecialchars($gamecode));
//если код игры не указан, выходим
if(empty($gamecode)){
return false;
}
//если директория пути не существует или является файлом, выходим
if(!file_exists($directory_path_full) || is_file($directory_path_full)){
return false;
}
//открываем директорию
//ошибка, не удалось открыть директорию
$fdir = @opendir($directory_path_full);
if(!$fdir){
return false;
}
//проходим по списку файлов
$screen_paths = array();
117
if($path[strlen($path) - 1] != "/"){
$path .= "/";
}
while($ffile = readdir($fdir)){
if($ffile != "." && $ffile != ".." && !is_dir($directory_path_full.$ffile)){
//проверяем, что расширение файла находится в списке разрешенных
$ffile_parts = get_file_parts($ffile);
if(!eregi("^[a-zA-Z0-9\-_\.]", $ffile_parts["filename"])){
continue;
}
if(in_array($ffile_parts["extension"], $allow_extensions)){
$screen_paths[$ffile_parts["filename"]] = $path.$ffile;
}
}
}
return $screen_paths;
}
try
{
//обрабатываем массив $arParams
prepare_params($arParams);
//проверяем параметры
if(strlen($arParams["IBLOCK_TYPE"]) <= 0){
$arParams["IBLOCK_TYPE"] = IB_TYPE_GAMES;
}
if(strlen($arParams["REVIEWS_IBLOCK_TYPE"]) <= 0){
$arParams["REVIEWS_IBLOCK_TYPE"] = IB_TYPE_REVIEWS;
}
/*
$arParams["REVIEWS_IBLOCK_ID"] = intval($arParams["REVIEWS_IBLOCK_ID"]);
if(intval($arParams["REVIEWS_IBLOCK_ID"]) <= 0){
$arParams["REVIEWS_IBLOCK_ID"] = 2;
}
*/
$arParams["IBLOCK_ID"] = intval($arParams["IBLOCK_ID"]);
if($arParams["IBLOCK_ID"] <= 0){
$arParams["IBLOCK_ID"] = IB_GAMES_PC;
}
if(strlen($arParams["IBLOCK_SECTION_CODE"]) <= 0){
$no_section = true;
throw new Exception(GetMessage("C_NO_IBLOCK_SECTION_CODE"));
}
if(strlen($arParams["ELEMENT_CODE"]) <= 0){
$no_elem = true;
throw new Exception(GetMessage("C_NO_IBLOCK_ELEMENT_CODE"));
}
$arParams["MIN_METAMARK_YELLOW"] = intval($arParams["MIN_METAMARK_YELLOW"]);
$arParams["MIN_METAMARK_GREEN"] = intval($arParams["MIN_METAMARK_GREEN"]);
if($arParams["MIN_METAMARK_YELLOW"] <= 0){
$arParams["MIN_METAMARK_YELLOW"] = 50;
}
if($arParams["MIN_METAMARK_GREEN"] <= 0){
$arParams["MIN_METAMARK_GREEN"] = 75;
}
//определяем парметры фильтрации для получения данных выбранной игры
$arFilter = array(
"IBLOCK_TYPE" => $arParams["IBLOCK_TYPE"],
"IBLOCK_ID" => $arParams["IBLOCK_ID"],
"SECTION_CODE" => $arParams["IBLOCK_SECTION_CODE"],
"CODE" => $arParams["ELEMENT_CODE"],
"SECTION_ACTIVE" => "Y",
"ACTIVE" => "Y",
);
//указываем выбирать только один элемент
$arNavParams = array(
"nTopCount" => 1
);
//формируем идентификатор кеша, который будет зависеть только параметров фильтрации
$cache_id = implode("_", $arFilter);
if($this->StartResultCache(false, $cache_id)){
//запрос на получение вариантов списка свойства "Издание"
118
$db_enum_list = CIBlockPropertyEnum::GetList(array("SORT" => "ASC"),
array("PROPERTY_ID" => 35));
$ar_enum = array();
while($fetch = $db_enum_list->GetNext()){
$ar_enum[$fetch["ID"]] = $fetch;
}
$arResult = array(
"NO_ITEMS" => false,
"ITEM" => array(),
"REVIEWS" => array(),
"NO_REVIEWS" => false
);
//проводим запрос к базе данных для получения игры
$db_element = CIBlockElement::GetList(array(), $arFilter, false, $arNavParams);
//если не найдено ни одного элемента
if(!$db_element || $db_element->SelectedRowsCount() <= 0){
$arResult["NO_ITEMS"] = true;
throw new Exception();
}
$ar_element_ob = $db_element->GetNextElement();
$fields = $ar_element_ob->GetFields();
//обрабатываем детальное изображение
$fields["NAME"] = htmlspecialchars($fields["NAME"]);
if($fields["DETAIL_PICTURE"] > 0){
$fields["DETAIL_PICTURE"] = CFile::GetFileArray($fields["DETAIL_PICTURE"]);
}
else{
$fields["DETAIL_PICTURE"] = false;
}
//определяем стиль для плашки метарейтинга в зависимости от значения метаоценки
$props = $ar_element_ob->GetProperties();
if($props["VIDEO_SCREEN"]["VALUE"] > 0){
$props["VIDEO_SCREEN"]["VALUE"] =
CFile::GetFileArray($props["VIDEO_SCREEN"]["VALUE"]);
}
else{
$$props["VIDEO_SCREEN"]["VALUE"] = false;
}
if(!empty($arParams["OZON_PATH_TEMPLATE"]) && !empty($props["OZON_ID"]["VALUE"])){
$arParams["OZON_PATH_TEMPLATE_SET"] = str_replace(array("#OZON_ID#"),
array($props["OZON_ID"]["VALUE"]), $arParams["OZON_PATH_TEMPLATE"]);
}
if($props["MAIN_METAMARK"]["VALUE"] >= $arParams["MIN_METAMARK_GREEN"]){
$props["STYLE_RAITING"] = "green";
}
elseif($props["MAIN_METAMARK"]["VALUE"] >= $arParams["MIN_METAMARK_YELLOW"] &&
$props["MAIN_METAMARK"]["VALUE"] < $arParams["MIN_METAMARK_GREEN"]){
$props["STYLE_RAITING"] = "yellow";
}
elseif($props["MAIN_METAMARK"]["VALUE"] > 0){
$props["STYLE_RAITING"] = "red";
}
else{
$props["STYLE_RAITING"] = "no_mark";
}
//получаем ID секции - платформы
$db_section = CIBlockSection::GetByID($fields["IBLOCK_SECTION_ID"]);
$ar_section = $db_section->GetNext();
//print_r($ar_section);
$fields["SECTION_NAME"] = $ar_section["NAME"];
if(intval($ar_section["DETAIL_PICTURE"]) > 0){
$ar_section["DETAIL_PICTURE"] =
CFile::GetFileArray(intval($ar_section["DETAIL_PICTURE"]));
}
else{
$ar_section["DETAIL_PICTURE"] = false;
}
$fields["SECTION_DETAIL_PICTURE"] = $ar_section["DETAIL_PICTURE"];
$arResult["ITEM"]["FIELDS"] = $fields;
//получаем скриншоты
$screens_small = get_screens_by_game_code(
119
$fields["CODE"],
str_replace(
array("#SECTION_CODE#", "#CODE#"),
array($arParams["IBLOCK_SECTION_CODE"], $fields["CODE"]),
$arParams["SCREEN_PATH_TEMPLATE_SMALL"]
)
);
if($screens_small){
foreach($screens_small as $code => $val){
$arResult["ITEM"]["FIELDS"]["SCREENS"][$code]["SMALL"] = $val;
}
}
//$arResult["ITEM"]["FIELDS"]["SCREENS"]
$screens_big = get_screens_by_game_code(
$fields["CODE"],
str_replace(
array("#SECTION_CODE#", "#CODE#"),
array($arParams["IBLOCK_SECTION_CODE"], $fields["CODE"]),
$arParams["SCREEN_PATH_TEMPLATE_BIG"]
)
);
if($screens_big){
foreach($screens_big as $code => $val){
$arResult["ITEM"]["FIELDS"]["SCREENS"][$code]["BIG"] = $val;
}
}
$arResult["ITEM"]["PROPS"] = $props;
//проводим вспомогательный запрос для получения обзоров и оценок
$arRevOrder = array(
"PROPERTY_MARK" => "DESC",
"PROPERTY_35" => "ASC",
"NAME" => "ASC"
);
$arRevFilter = array(
"IBLOCK_TYPE" => $arParams["REVIEWS_IBLOCK_TYPE"],
"PROPERTY_GAME_ID" => $arResult["ITEM"]["FIELDS"]["ID"],
"ACTIVE" => "Y"
);
$arRevSelect = array(
"ID", "NAME", "PROPERTY_MARK", "PROPERTY_35"
);
$db_reviews = CIBlockElement::GetList($arRevOrder, $arRevFilter, false, false,
$arRevSelect);
if($db_reviews && $db_reviews->SelectedRowsCount() > 0){
while($arReviews = $db_reviews->GetNext()){
$arResult["REVIEWS"][] = array(
"MARK" => $arReviews["PROPERTY_MARK_VALUE"],
"MAGAZINE" => $arReviews["PROPERTY_35_VALUE"],
"MAGAZINE_SORT" => $ar_enum[$arReviews["PROPERTY_35_ENUM_ID"]]["SORT"],
);
}
}
for($i=0; $i < count($arResult["REVIEWS"]); $i++)
for($j=$i+1; $j < count($arResult["REVIEWS"]); $j++)
{
if($arResult["REVIEWS"][$i]["MARK"] == $arResult["REVIEWS"][$j]["MARK"] &&
$arResult["REVIEWS"][$i]["MAGAZINE_SORT"] > $arResult["REVIEWS"][$j]["MAGAZINE_SORT"]){
$tmp = $arResult["REVIEWS"][$i];
$arResult["REVIEWS"][$i] = $arResult["REVIEWS"][$j];
$arResult["REVIEWS"][$j] = $tmp;
}
}
//готовим формирование навигационной цепочки для заголовка страницы
$chain_title_arr = array($arResult["ITEM"]["FIELDS"]["IBLOCK_NAME"]);
//получаем цепочку до элемента в дереве инфоблоков
$db_chain = CIBlockSection::GetNavChain($arParams["IBLOCK_ID"],
$arResult["ITEM"]["FIELDS"]["IBLOCK_SECTION_ID"]);
if($db_chain -> SelectedRowsCount() > 0){
while($fetch = $db_chain->GetNext()){
$chain_title_arr[] = $fetch["NAME"];
}
120
}
$chain_title_arr[] = $arResult["ITEM"]["FIELDS"]["NAME"];
$arResult["ITEM"]["FIELDS"]["FOR_TITLE"] = implode(" ", $chain_title_arr);
$this->IncludeComponentTemplate();
}
//устанавливаем заголовок страницы, если данное действие разрешено в параметрах
if($arParams["SET_TITLE"]){
$APPLICATION->SetTitle($arResult["ITEM"]["FIELDS"]["FOR_TITLE"]);
}
//в массив $GLOBALS записываем идентификатор игры для передачи в компонент "Обзоры к игре"
$GLOBALS["GAME_ID"] = intval($arResult["ITEM"]["FIELDS"]["ID"]);
}
catch(Exception $e)
{
//сбрасываем кеш и подключаем шаблон
if($no_section || $no_elem){
$arResult["NO_ITEMS"] = true;
}
$this->ClearResultCache($cache_id);
$this->AbortResultCache();
$this->IncludeComponentTemplate();
}
?>
В.6. Компонент search_autocomplete
Файл component.php
<?php
if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();
//обрабатываем массив $arParams
foreach($arParams as $key => $param){
if(!is_array($param)){
$arParams[$key] = trim($param);
}
else{
foreach($param as $p_key => $p_param){
$param[$p_key] = trim($p_param);
}
$arParams[$key] = $param;
}
}
//вначале считываем значение параметра стоки запроса и, если строка пуста, выходим
//проверяем, указан ли тип инфоблоков
if(strlen($arParams["IBLOCK_TYPE"]) <= 0){
$arParams["IBLOCK_TYPE"] = IB_TYPE_GAMES;
}
//проверяем, указаны ли инфоблоки и, если нет, присваиваем константу из init.php для PC
if(empty($arParams["IBLOCK_ID"])){
$arParams["IBLOCK_ID"] = IB_GAMES_PC;
}
//проверяем, указано ли время кеширования
$arParams["CACHE_TIME"] = intval($arParams["CACHE_TIME"]);
if(intval($arParams["CACHE_TIME"]) <= 0){
$arParams["CACHE_TIME"] = CACHE_TIME;
}
//проверяем, указан ли код свойства "Метаоценка"
if(strlen($arParams["PROPERTY_METAMARK"]) <= 0){
$arParams["PROPERTY_METAMARK"] = MAIN_METAMARK;
}
$arParams["COUNT"] = intval($arParams["COUNT"]);
if($arParams["COUNT"] <= 0){
$arParams["COUNT"] = 10;
}
if(empty($arParams["SEARCH_PAGE"])){
$arParams["SEARCH_PAGE"] = "/search/index.php?q=#REQUEST#";
}
//организуем блок с генерацией исключений в случае тех или иных ошибок
try
{
121
//подготавливаем параметры для запроса к элементам инфоблока
$arOrder = array(
"NAME" => "ASC",
);
//формируем фильтр
$arFilter = array(
"IBLOCK_TYPE" => $arParams["IBLOCK_TYPE"],
"IBLOCK_ID" => $arParams["IBLOCK_ID"],
"ACTIVE" => "Y",
"NAME" => "%".$_REQUEST["q"]."%"
);
$arSelect = array(
"NAME", "PROPERTY_".MAIN_METAMARK
);
$arNavParams = array(
//"nTopCount" => 10
);
//кеширование, раз в 10 минут, в зависимости от запроса
if($this->StartResultCache(60*10, $_REQUEST["q"])){
//проверяем, подключен ли модуль инф.блоков
if(!CModule::IncludeModule("iblock")){
throw new Exception(GetMessage("ERROR_MODULE_NOT_INCLUDED"));
}
//проводим запрос к базе данных
$db_element = CIBlockElement::GetList($arOrder, $arFilter, false, false, $arSelect);
//ошибка API или БД
if(!$db_element){
throw new Exception(DB_ERROR_TEXT);
}
if($db_element->SelectedRowsCount() <= 0){
//ничего не найдено, отменяем кеширование
$this->AbortResultCache();
return;
}
while($fetch = $db_element->GetNext()){
$mark_color = "";
$mark = floatval($fetch["PROPERTY_".MAIN_METAMARK."_VALUE"]);
if($mark >= 75.00){
$mark_color = $arParams["MARK_75"];
}
elseif($mark >= 50.00 && $mark < 75.00){
$mark_color = $arParams["MARK_50"];
}
elseif($mark > 0.00 && $mark < 50.00){
$mark_color = $arParams["MARK_49"];
}
$arResult["ITEMS"][] = array(
"name" => $fetch["NAME"],
"mark" => $mark,
"search_page" => str_replace("#REQUEST#", urlencode($fetch["NAME"]),
$arParams["SEARCH_PAGE"]),
"mark_color" => $mark_color
);
}
//на первое место ставим слова, с которых начинается поисковая фраза
for($i = 0; $i < count($arResult["ITEMS"]); $i++)
for($j = $i+1; $j < count($arResult["ITEMS"]); $j++)
{
if((mb_stripos($arResult["ITEMS"][$j]["name"], $_REQUEST["q"], 0, LANG_CHARSET) ==
0 && mb_stripos($arResult["ITEMS"][$i]["name"], $_REQUEST["q"], 0, LANG_CHARSET) != 0)){
$tmp = $arResult["ITEMS"][$i];
$arResult["ITEMS"][$i] = $arResult["ITEMS"][$j];
$arResult["ITEMS"][$j] = $tmp;
}
}
$k = 0;
$tmp = array();
foreach($arResult["ITEMS"] as $key => $item){
$word = "";
$name = $item["name"];
if($k >= $arParams["COUNT"]){
122
break;
}
//добавляем к имени выделение жирным поискового запроса
//получаем кусок слова
$start_pos = mb_stripos($name, $_REQUEST["q"], 0, LANG_CHARSET);
$word = mb_substr($name, $start_pos, mb_strlen($_REQUEST["q"], LANG_CHARSET),
LANG_CHARSET);
//echo $word;
$name = mb_eregi_replace($word, "<b>".$word."</b>", $name);
$item["name"] = $name;
$tmp[$key] = $item;
$k++;
}
$arResult["ITEMS"] = $tmp;
$this->IncludeComponentTemplate();
}
}
catch(Exception $e)
{
//очищаем кеш
$this->AbortResultCache();
}
?>
Файл search_autocomplete.js
var b_enter_pressed = false;
var b_keys_pressed = false;
//обработчик события нажатия клавиши (именно нажатие - down). Нужен для обработки клавиш внизвверх для навигации по посдказкам
function search_autocomplete_keydown(event)
{
if($(event.data.vars_box_id).css("display") == "none" || $(event.data.vars_box_id + "
a").size() <= 0){
return;
}
var selected_index = -1;
var next_index = 0;
var keyCode = event.keyCode;
if(keyCode == 38 || keyCode == 40){
b_keys_pressed = true;
var list = $(event.data.vars_box_id + " a").get();
for(var i=0; i < list.length; i++){
if($(list[i]).hasClass("hover")){
selected_index = i;
break;
}
}
if(keyCode == 40){
next_index = selected_index + 1;
if(next_index == list.length){
next_index = 0;
}
}
else if(keyCode == 38){
next_index = selected_index - 1;
if(next_index <= -1){
next_index = list.length - 1;
}
}
$(event.data.vars_box_id + " a").removeClass("hover");
$(list[next_index]).addClass("hover");
}
return;
}
//обработик нажатия на клавишу (кратковременное событие). Нуже для обработки клавиши Enter при
использовании подсказок
function search_autocomplete_keypress(event)
{
123
var keyCode = event.keyCode;
if(keyCode == 13){
if($(event.data.vars_box_id).css("display") != "none"){
//смотрим, есть ли выбранная ссылка
var b_has_link = false;
if($(event.data.vars_box_id + " a.hover").attr("title").length > 0){
set_to_search(this.id, event.data.vars_box_id, $(event.data.vars_box_id + "
a.hover").attr("title"));
b_enter_pressed = true;
return false;
}
}
}
}
//функция - обработчик события фокусирования поля ввода
function search_field_focus(ob, default_value)
{
if(ob == null){
return false;
}
if(default_value.toLowerCase() == ob.value.toLowerCase()){
ob.value = "";
}
$(ob).addClass("active");
/*
if(ob.value.length > 2){
search_autocomplete(ob.name, ob.value);
}*/
}
//функция - обработчик события отмены фокуса поля ввода
function search_field_blur(ob, default_value)
{
if(ob == null){
return false;
}
if(ob.value == "" || ob.value.toLowerCase() == default_value.toLowerCase()){
ob.value = default_value;
$(ob).removeClass("active");
}
//$("#search_variants").hide();
}
//установка текста в поле ввода из элемента - ссылки
function set_quick_search_text(href, input)
{
if(!href || !input){
return false;
}
input.value=$(href).attr("title");
input.className = "active";
return false;
}
//установка данных из автозаполнения в поиск
function set_to_search(field, block, value)
{
$("#"+field).attr("value", value).addClass("active");
$(block).hide();
return false;
}
//метка интервала для задержки перед вызовом запроса
var tm;
var XMLHttp;
//функция вызывается через некоторую паузу после обработки нажатия клавиши и производит
выборку и вывод подсказок
function search_autocomplete(field_name, current_value, count_of_items, iblock_type,
iblock_id, block_id, path_to_script)
{
//очищаем интервал
if(tm){
clearInterval(tm);
}
if(b_enter_pressed){
124
b_enter_pressed = false;
return;
}
//создаем AJAX-объект
XMLHttp = get_request_object();
if(!XMLHttp || XMLHttp.readyState != 0){
return;
}
//отправляем запрос серверу
XMLHttp = send_request(XMLHttp, path_to_script, "GET",
field_name+"="+current_value+"&count="+count_of_items+"&iblock_type="+iblock_type+"&iblock_id=
"+iblock_id);
if(!XMLHttp){
return;
}
//обрабатываем ответ от сервера
XMLHttp.onreadystatechange = function()
{
//если код состояния запроса = 4 (ответ получен)
if(XMLHttp.readyState == 4){
//если статус ответа отличен от 200 ОК, выводим предупреждение
if(XMLHttp.status != 200){
//alert("Server response is not 200 OK. Response: " + XMLHttp.status + " " +
XMLHttp.statusText);
}
else{
//иначе, если в ответе есть данные, заполняем ими блок автоподсказок
if(XMLHttp.responseText.length > 0){
$(block_id).html(XMLHttp.responseText);
$(block_id).show();
$(block_id+" a").hover(
function(){
if(!b_keys_pressed){
$(block_id +" a").removeClass("hover");
$(this).addClass("hover");
}
},
function(){
if(b_keys_pressed){
b_keys_pressed = false;
}
$(this).removeClass("hover");
}
);
}
else{
$(block_id).hide();
}
}
}
};
}
//функция - обработчик события ввода в поле поиска текстовой фразы
function search_autocomplete_handler(event)
{
//фокусируем элемент
this.focus();
field_name = this.name;
count_of_items = event.data.count_of_items;
iblock_type = event.data.iblock_type;
iblock_id = event.data.iblock_id;
block_id = event.data.vars_block_id;
path_to_script = event.data.path;
//если событие не keyup, выходим
if(event.type != "keyup"){
return;
}
//узнаем, какая клавиша нажата
var keyCode = event.keyCode;
//игонрируем кнопки, которые не несут полезной для автозаполнения информации
if((keyCode < 13 && keyCode != 8) ||
125
(keyCode >=14 && keyCode < 32) ||
(keyCode >= 33 && keyCode <= 46) ||
(keyCode >= 112 && keyCode <= 123)){
return;
}
//получаем текущее значение из поля
current_value = this.value;
//если длина значения поля меньше трех, выходим
if(current_value.length < 3){
$(event.data.vars_block_id).hide();
clearInterval(tm);
return;
}
//если интервал уже был установлен, очищаем его, таким образом не допуская запрос, если
текст набирается быстро
if(tm){
clearInterval(tm);
}
//устанавливаем интервал вызова функции, в которой происходит запрос и вывод данных
tm = setInterval("search_autocomplete(field_name, current_value, count_of_items,
iblock_type, iblock_id, block_id, path_to_script)", 200);
}
В.7. Компонент reviews.list
Файл component.php
<?php
//компонент
if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();
try
{
//обрабатываем массив $arParams
prepare_params($arParams);
//проверяем параметры
if(strlen($arParams["IBLOCK_TYPE"]) <= 0){
$arParams["IBLOCK_TYPE"] = IB_TYPE_REVIEWS;
}
if(empty($arParams["IBLOCK_ID"])){
$arParams["IBLOCK_ID"] = IB_REVIEWS_PC;
}
$arParams["GAME_ID"] = intval($arParams["GAME_ID"]);
if($arParams["GAME_ID"] <= 0){
return;
}
if(strlen($arParams["SORT_BY1"]) <= 0){
$arParams["SORT_BY1"] = "SORT";
}
if($arParams["SORT_ORDER1"] != "DESC"){
$arParams["SORT_ORDER1"] = "ASC";
}
if(strlen($arParams["SORT_BY2"]) <= 0){
$arParams["SORT_BY2"] = "ID";
}
if($arParams["SORT_ORDER2"] != "DESC"){
$arParams["SORT_ORDER2"] = "ASC";
}
$arParams["MIN_METAMARK_YELLOW"] = intval($arParams["MIN_METAMARK_YELLOW"]);
$arParams["MIN_METAMARK_GREEN"] = intval($arParams["MIN_METAMARK_GREEN"]);
if($arParams["MIN_METAMARK_YELLOW"] <= 0){
$arParams["MIN_METAMARK_YELLOW"] = 50;
}
if($arParams["MIN_METAMARK_GREEN"] <= 0){
$arParams["MIN_METAMARK_GREEN"] = 75;
}
$arParams["TRUNCATE_LEN"] = intval($arParams["TRUNCATE_LEN"]);
//параметры сортировки
$arSortOrder = array(
$arParams["SORT_BY1"] => $arParams["SORT_ORDER1"],
$arParams["SORT_BY2"] => $arParams["SORT_ORDER2"],
126
"ID" => "DESC"
);
//параметры фильтрации
$arFilter = array(
"IBLOCK_TYPE" => $arParams["IBLOCK_TYPE"],
"IBLOCK_ID" => $arParams["IBLOCK_ID"],
"ACTIVE" => "Y",
"PROPERTY_GAME_ID" => $arParams["GAME_ID"],
);
$arSelectedFields = array(
"NAME", "ID", "DETAIL_TEXT",
"PROPERTY_MARK", "PROPERTY_GAME_ID",
"PROPERTY_35", "PROPERTY_36"
);
//с данного момента включаем кеширование
$cache_id = $arParams["GAME_ID"];
if($this->StartResultCache(false, $cache_id)){
//проверяем, подключен ли модуль
if(!CModule::IncludeModule("iblock")){
throw new Exception(GetMessage("C_MODULE_ERROR"));
}
//запрос на получение вариантов списка свойства "Издание"
$db_enum_list = CIBlockPropertyEnum::GetList(array("SORT" => "ASC"),
array("PROPERTY_ID" => 35));
$ar_enum = array();
while($fetch = $db_enum_list->GetNext()){
$ar_enum[$fetch["ID"]] = $fetch;
}
//запрос на получение списка обзоров
$db_element = CIBlockElement::GetList($arSortOrder, $arFilter, false, false,
$arSelectedFields);
//ошибка запроса к базе данных, прекращаем кеширование
if(!$db_element){
throw new Exception(DB_ERROR_TEXT);
}
$arResult = array(
"ITEMS" => array(),
"NO_ITEMS" => false
);
if($db_element->SelectedRowsCount() <= 0){
$arResult["NO_ITEMS"] = true;
throw new Exception();
}
//если в настройках компонента задан шаблон пути к элементу на детальн. страницу,
обрабатываем шаблон пути
if(!empty($arParams["DETAIL_URL"])){
$db_element->SetUrlTemplates($arParams["DETAIL_URL"]);
}
//количество английских запросов
$arResult["ENG_COUNT"] = 0;
//количество русских запросов
$arResult["RUS_COUNT"] = 0;
while($fetch = $db_element->GetNext()){
$mark = intval($fetch["PROPERTY_MARK_VALUE"]);
$style_raiting = "";
if($mark >= $arParams["MIN_METAMARK_GREEN"]){
$style_raiting = "green";
}
elseif($mark >= $arParams["MIN_METAMARK_YELLOW"] && $mark <
$arParams["MIN_METAMARK_GREEN"]){
$style_raiting = "yellow";
}
elseif($mark > 0){
$style_raiting = "red";
}
else{
$style_raiting = "no_mark";
}
$arResult["ITEMS"][] = array(
"NAME" => $fetch["NAME"],
"DETAIL_PAGE_URL" => $fetch["DETAIL_PAGE_URL"],
127
"MARK" => $mark,
"PREVIEW" => strip_tags($fetch["DETAIL_TEXT"]),
"LINK" => $fetch["PROPERTY_36_VALUE"],
"MAGAZINE" => $fetch["PROPERTY_35_VALUE"],
"MAGAZINE_ID" => $fetch["PROPERTY_35_ENUM_ID"],
"MAGAZINE_SORT" => $ar_enum[$fetch["PROPERTY_35_ENUM_ID"]]["SORT"],
"STYLE_RAITING" => $style_raiting
);
if(in_array($fetch["PROPERTY_35_ENUM_ID"], $arParams["ENG_REVIEWS"])){
$arResult["ENG_COUNT"]++;
}
else{
$arResult["RUS_COUNT"]++;
}
}
for($k = 0; $k < count($arResult["ITEMS"]); $k++){
if($arParams["TRUNCATE_LEN"] != 0){
$arResult["ITEMS"][$k]["PREVIEW"] =
TruncateText($arResult["ITEMS"][$k]["PREVIEW"], $arParams["TRUNCATE_LEN"]);
}
}
for($i=0; $i < count($arResult["ITEMS"]); $i++)
for($j=$i+1; $j < count($arResult["ITEMS"]); $j++)
{
if($arResult["ITEMS"][$i]["MARK"] == $arResult["ITEMS"][$j]["MARK"] &&
$arResult["ITEMS"][$i]["MAGAZINE_SORT"] > $arResult["ITEMS"][$j]["MAGAZINE_SORT"]){
$tmp = $arResult["ITEMS"][$i];
$arResult["ITEMS"][$i] = $arResult["ITEMS"][$j];
$arResult["ITEMS"][$j] = $tmp;
}
}
$this->IncludeComponentTemplate();
}
}
catch(Exception $e)
{
//произошла ошибка, сбрасываем кеш
//чистим кеш
$this->ClearResultCache($cache_id);
$this->AbortResultCache();
//пишем ошибку в компонент arResult
$arResult["ERROR"] = $e->getMessage();
//подключаем шаблон
$this->IncludeComponentTemplate();
}
?>
128
Приложение Г. Исходные коды файла script.js (основной
JavaScript – сценарий web-приложения)
/**
* void restore_hash_part(string path, string result_block, string
* ajax_status_id, bool filterupped)
* Функция производит парсинг строки вида
* http://site.domain#param1:value1&..., преобразует этот вид в вид
* http://site.domain?param1=value1&...
* и проводит запрос к серверу по адресу path с помощью AJAX, выводя результат * в
блок result_block
* string path - путь к запрашиваемому файлу сервера
* string result_block - id блока, в который нужно вставить результат запроса
* string ajax_status_id - id всплывающей иконки статуса загрузки AJAX
* string filterupped - проверка, что один из параметров, инициирующий запрос,
* уже применен и запроса не требуется
*/
function restore_hash_part(path, result_block, ar_next, ajax_status_id, filterupped)
{
if(filterupped == 1){
return;
}
//готовим обработку пришедшего URL, если имеется hash-part с параметрами формы,
делаем ajax - запрос к серверу
var location = document.location.toString();
var hash_part_pos = location.indexOf("#");
//выбираем кусок с hash-part
if(hash_part_pos != -1){
var hash_part = location.substring(hash_part_pos + 1);
var query_string = "";
var query_string_arr = new Array("ajax_request_mode=Y");
//разбиваем hash_part на части имя=значение
var tmp = hash_part.split("&");
var flag_next = false;
for(var i in tmp){
var tmp_detail = tmp[i].split(":");
if(tmp_detail[0] == ar_next[0] && tmp_detail[1] == ar_next[1]){
flag_next = true;
}
query_string_arr.push(tmp_detail[0]+"="+tmp_detail[1]);
}
query_string = query_string_arr.join("&");
if(flag_next){
set_server_request(path, query_string, ajax_status_id, result_block,
hash_part);
}
return;
}
}
//функция определяет коордитнату абсолютного положения страницы по вертикали
function pageY(elem)
{
return elem.offsetParent ?
elem.offsetTop + pageY( elem.offsetParent ) :
elem.offsetTop;
}
/** функция выполняет ajax-запрос к странице с адресом path с параметрами
* query_string и выполняет загрузку данных в элемент с id = result_block,
* сохраняя состояние запроса в строке state_string
*/
129
function set_server_request(path, query_string, ajax_status_id, result_block,
state_string)
{
var XMLHttp = get_request_object();
if(XMLHttp.readyState == 0 || XMLHttp.readyState == 4){
XMLHttp = send_request(XMLHttp, path, "GET", query_string);
var status_block_pos = pageY(document.getElementById(result_block));
var block_top = status_block_pos;
var scroll_top = $(window).scrollTop();
if(block_top < scroll_top){
$("#"+ajax_status_id).css("top", scroll_top + 10);
}
$("#"+ajax_status_id).fadeIn();
}
XMLHttp.onreadystatechange = function(){
if(XMLHttp.readyState == 4){
if(XMLHttp.status == 200){
$("#"+result_block).html(XMLHttp.responseText);
if(state_string.length > 0){
document.location = "#"+state_string;
}
$("#"+ajax_status_id).hide();
}
}
}
}
/** array ajax_state_string(string url, array exclude_array)
* функция принимает в качестве параметра путь к странице с адресом url и
* разбивает ее на строку для ajax-запроса (индекс 0 возвращаемого массива) и * строку
сохранения состояния страницы (индекс 1 возвращаемого массива)
* в массиве exclude_array содержится список имен переменных, которые
* необходимо исключить при формировании возвращаемых значений
*/
function ajax_state_string(url, exclude_array)
{
if(url == undefined || url.length <= 0){
return false;
}
if(url.indexOf("?") == -1){
return url;
}
var query_string_arr = new Array("ajax_request_mode=Y");
var query_string = "";
var state_string_arr = new Array();
var state_string = "";
var q_string;
if(url.lastIndexOf("#") != -1){
q_string = url.substring(url.indexOf("?") + 1, url.lastIndexOf("#"));
}
else{
q_string = url.substring(url.indexOf("?") + 1);
}
var q_pairs = q_string.split("&");
var q_pair;
var not_in_list = false;
var filterupped = false;
for(var i in q_pairs){
not_in_list = false;
q_pair = q_pairs[i].split("=");
if(q_pair[0] == "filter"){
filterupped = true;
}
for(var j = 0; j < exclude_array.length; j++){
if(exclude_array[j] == q_pair[0]){
130
not_in_list = true;
break;
}
}
if(not_in_list){
continue;
}
query_string_arr.push(q_pair[0]+"="+decodeURIComponent(q_pair[1]));
if(q_pair[0] != "ajax_request_mode"){
state_string_arr.push(q_pair[0]+":"+q_pair[1]);
}
}
if(!filterupped){
query_string_arr.push("filter=up");
state_string_arr.push("filter:up");
}
query_string = query_string_arr.join("&");
state_string = state_string_arr.join("&");
return new Array(query_string, state_string);
}
/**
* Действия, выполняемые при загрузке страницы
*/
$(function(){
$("#signin-show-box").bind("click", function()
{
$(".auth-box").show();
$("#mark-select-box").hide();
$("#genre-select-box").hide();
$("#detail-date-from-select-box").hide();
$("#detail-date-to-select-box").hide();
$("#date-select-box").hide();
return false;
});
$(".auth-box").bind("click", function()
{
$(".auth-box").show();
return false;
});
$(document).bind("click", function(event)
{
$("#genre-select-box").hide();
var obj = event.target;
k = 0;
var date_select_box_flag = false;
var mark_select_box_flag = false;
while($(obj).attr("id") != undefined){
if($(obj).attr("id") == "date-select-box"){
$("#detail-date-from-select-box").hide();
$("#detail-date-to-select-box").hide();
date_select_box_flag = true;
break;
}
if($(obj).attr("id") == "mark-select-box"){
mark_select_box_flag = true;
}
obj = $(obj).parent();
k++;
//if(k > 100) break;
}
if(!mark_select_box_flag){
$("#mark-select-box").hide();
}
if(!date_select_box_flag){
131
$("#date-select-box").hide();
}
$(".auth-box").hide();
if(!event.target.id || event.target.id != "search_q"){
$("#search_variants").hide();
}
if(!event.target.id || event.target.id != "search_q_main"){
$("#search_variants_main").hide();
}
});});
132
Приложение Д. Исходные коды файла init.php
<?php
CPageOption::SetOptionString("main", "nav_page_in_session", "N");
/**
* bool write_access_log(string filepath) - функция фиксирует запрошенный адрес в
специальном файле
* @param string filepath - файл, в который нужно записать данные
* @return true в случае успешного завершения работы функции, false в противном случае
*/
function write_access_log($filepath)
{
if(SITE_SERVER_NAME == ""){
return false;
}
global $APPLICATION;
//получаем URL текущей запрошенной страницы, дополняя ее в начале именем сервера
$current_uri = "http://".SITE_SERVER_NAME.ToLower(
$APPLICATION->GetCurPageParam("", array(), true));
//$filepath = $_SERVER["DOCUMENT_ROOT"]."/".$filepath;
//флаг, указывающий, нужно ли записать URL в файл
$can_write = true;
//если файл существует
if(@file_exists($filepath)){
$fp_read = @fopen($filepath, "r");
if(!$fp_read){
fclose($fp_read);
return false;
}
//читаем его содержимое по строкам
while(!feof($fp_read)){
$exist_link = trim(fgets($fp_read));
//если URL уже есть в файле, отменяем запись
if(strcasecmp($exist_link, $current_uri) == 0){
$can_write = false;
break;
}
}
fclose($fp_read);
}
//пишем URL в файл
if($can_write){
if(@file_exists($filepath)){
//добавляем
$fp_append = @fopen($filepath, "a");
if(filesize($filepath) > 0){
$str = PHP_EOL.$current_uri;
}
else{
$str = $current_uri;
}
}
else{
//или создаем новый файл
$fp_append = @fopen($filepath, "w");
$str = $current_uri;
}
fwrite($fp_append, $str);
fclose($fp_append);
}
return true;
}
133
write_access_log($_SERVER["DOCUMENT_ROOT"]."/access_log.log");
/**
* Далее определяются константы типов и идентификаторов информационных блоков,
используемых в проекте
*/
/**
* тип информационных блоков "Игры"
*/
define("IB_TYPE_GAMES", "ib_games");
/**
* тип информационных блоков "Обзоры"
*/
define("IB_TYPE_REVIEWS", "ib_reviews");
/**
* Информационный блок "Игры" => "Игры"
*/
define("IB_GAMES_PC", 1);
/**
* Информационный блок "Обзоры" => "Обзоры"
*/
define("IB_REVIEWS_PC", 2);
/**
* Далее определяются символьные коды свойств, используемых в обработчике добавление
ИБ "Обзоры" и ИБ "Игры"
*/
/**
* Свойство "Оценка" ИБ "Обзоры"
*/
define("REVIEWS_PC_PROP_GAME", "GAME_ID");
/**
* Свойство "Метаоценка" ИБ "Игры"
*/
define("GAMES_PC_PROP_METAMARK", "MAIN_METAMARK");
/**
* Свойство "Оценка" ИБ "Обзоры"
*/
define("REVIEWS_PC_PROP_MARK", "MARK");
/**
* Указываем имя файла, в которое будет писать логи функция AddMessage2Log
*/
define("LOG_FILENAME", $_SERVER["DOCUMENT_ROOT"]."/bitrix/php_interface/init.log");
/**
* Текст при возникновении ошибки в работе базы данных
*/
define("DB_ERROR_TEXT", "Ошибка работы с данными");
/**
*/
define("CACHE_TIME", 3600);
/**
* Указываем пороговые минимальные значения для секторов метаоценок
*/
define("MIN_METAMARK_GREEN", 75);
define("MIN_METAMARK_YELLOW", 50);
/**
* Имя переменной, включающих ряд компонентов в режиме аякс-запроса
*/
define("AJAX_REQUEST_MODE_PARAM", "ajax_request_mode");
//список месяцев на русском языке
$arMonthNames = array(
1 => array("month" => "Январь", "month_a" => "января"),
2 => array("month" => "Февраль", "month_a" => "февраля"),
134
3 => array("month" => "Март", "month_a" => "марта"),
4 => array("month" => "Апрель", "month_a" => "апреля"),
5 => array("month" => "Май", "month_a" => "мая"),
6 => array("month" => "Июнь", "month_a" => "июня"),
7 => array("month" => "Июль", "month_a" => "июля"),
8 => array("month" => "Август", "month_a" => "августа"),
9 => array("month" => "Сентябрь", "month_a" => "сентября"),
10 => array("month" => "Октябрь", "month_a" => "октября"),
11 => array("month" => "Ноябрь", "month_a" => "ноября"),
12 => array("month" => "Декабрь", "month_a" => "декабря")
);
//подключение модуля "Информационные блоки"
CModule::IncludeModule("iblock");
//обработчики
AddEventHandler("iblock", "OnAfterIblockElementAdd", "add_review_handler");
AddEventHandler("iblock", "OnAfterIblockElementUpdate", "add_review_handler");
AddEventHandler("iblock", "OnBeforeIblockElementDelete", "delete_review_handler");
AddEventHandler("iblock", "onBeforeIblockElementAdd", "check_element_code");
AddEventHandler("iblock", "onBeforeIblockElementUpdate", "check_element_code");
/**
* bool delete_review_handler(int ID) - Обработчик события удаления элемента
информационного блока "Обзоры" => "PC"
* @author dda
* @param int ID Идентификатор удаляемого элемента информационного блока
* @return true в случае успеха, false в противном случае
*/
function delete_review_handler($ID)
{
AddMessage2Log("Вызван обработчик delete_review_handler события
OnAfterIblockElementDelete\n");
$db_current_element = CIBlockElement::GetByID($ID);
$ob_current_element = $db_current_element->GetNextElement();
//получаем поля элемента
$arFields = $ob_current_element->GetFields();
//и свойства
$arProperties = $ob_current_element->GetProperties();
//проверяем принадлежность добавляемого элемента к инфоблоку "Обзоры"
//и выходим, если это не так
if($arFields["IBLOCK_ID"] != IB_REVIEWS_PC){
AddMessage2Log("Обработчик завершил работу. Причина - элемент не того ИБ\n");
//return $arFields;
return true;
}
//запрос на получение свойств
if(!array_key_exists(REVIEWS_PC_PROP_GAME, $arProperties)){
return false;
}
$game_id = intval($arProperties[REVIEWS_PC_PROP_GAME]["VALUE"]);
//проверяем, есть ли элемент с таким ID=$game_id и, если нет, выходим
$db_game_check = CIBlockElement::GetByID($game_id);
if($db_game_check->SelectedRowsCount() <= 0){
AddMessage2Log("Обработчик завершил работу. Нет элемента с ID=".$game_id);
return false;
}
//получаем значения для других уже существующих обзоров с таким ID=$game_id
$review_filter = array(
"PROPERTY_".REVIEWS_PC_PROP_GAME => $game_id
);
$review_select = array(
"PROPERTY_".REVIEWS_PC_PROP_MARK,
);
135
$db_reviews_exists = CIBlockElement::GetList(array(), $review_filter, false,
false, $review_select);
//считаем сумму оценок и среднее арифметическое
$summ_mark_for_game = 0 - $arProperties[REVIEWS_PC_PROP_MARK]["VALUE"];
while($ar_reviews_exists =$db_reviews_exists->GetNext()){
$summ_mark_for_game +=
$ar_reviews_exists["PROPERTY_".REVIEWS_PC_PROP_MARK."_VALUE"];
}
//записываем это значение в свойство с кодом GAMES_PC_PROP_METAMARK для элемента
с ID=$game_id
$average = round($summ_mark_for_game/($db_reviews_exists->SelectedRowsCount() 1));
CIBlockElement::SetPropertyValueCode($game_id, GAMES_PC_PROP_METAMARK, $average);
#------- ЗАПРОС 4,5
AddMessage2Log("Обработчик завершил работу. Элементу с ID=".$game_id." в свойство
".GAMES_PC_PROP_METAMARK." записано значение ".$average."\n");
return true;
}
/** bool check_element_code(array &arFields)
* функция - обработчик добавления или изменения документа. Проверяет на валидность
символьный код элемента
* @param array arFields - массив с полями элемента. Передается по ссылке
* @return Возвращает true, если символьный код валиден и false, если нет
*/
function check_element_code(&$arFields)
{
global $APPLICATION;
if(!eregi("^[a-zA-Z0-9_]+$", $arFields["CODE"])){
$APPLICATION->throwException("Символьный код может содержать только латинские
буквы, цифры и знак \"_\"");
return false;
}
return true;
}
/**
* void add_review_handler(array &arFields) - Обработчик события добавления или
изменения элемента информационного блока "Обзоры" => "PC"
* @author dda
* @param array arFields Массив данных добавленного(измененного элемента). Данный
массив передается в функцию по ссылке и может быть изменен внутри неё
* @return Возвращаемых значений нет
*/
function add_review_handler(&$arFields)
{
AddMessage2Log("Вызван обработчик add_review_handler события
OnAfterIblockElementAdd(Update)\n");
//проверяем принадлежность добавляемого элемента к инфоблоку "Обзоры"
//и выходим, если это не так
if($arFields["IBLOCK_ID"] != IB_REVIEWS_PC){
return;
}
//проверяем, что элемент добавлен и не возникло ошибок
if($arFields["ID"] > 0 && empty($arFields["RESULT_MESSAGE"])){
//запрос на получение свойств
$db_prop = CIBlockElement::GetProperty($arFields["IBLOCK_ID"],
$arFields["ID"], array(), array("CODE" => REVIEWS_PC_PROP_GAME));
if($db_prop->SelectedRowsCount() <= 0){
AddMessage2Log("Обработчик завершил работу. нет свойства с кодом
".REVIEWS_PC_PROP_GAME."\n");
return;
}
$fetch = $db_prop->GetNext();
$game_id = intval($fetch["VALUE"]);
//проверяем, есть ли элемент с таким ID=$game_id и, если нет, выходим
136
$db_game_check = CIBlockElement::GetByID($game_id);
if($db_game_check->SelectedRowsCount() <= 0){
AddMessage2Log("Обработчик завершил работу. Нет элемента с
ID=".$game_id);
return;
}
//получаем значения для других уже существующих обзоров с таким ID=$game_id
$review_filter = array(
"PROPERTY_".REVIEWS_PC_PROP_GAME => $game_id
);
$review_select = array(
"PROPERTY_".REVIEWS_PC_PROP_MARK,
);
$db_reviews_exists = CIBlockElement::GetList(array(), $review_filter, false,
false, $review_select);
//считаем сумму оценок и среднее арифметическое
$summ_mark_for_game = 0;
while($ar_reviews_exists =$db_reviews_exists->GetNext()){
$summ_mark_for_game +=
$ar_reviews_exists["PROPERTY_".REVIEWS_PC_PROP_MARK."_VALUE"];
}
//записываем это значение в свойство с кодом GAMES_PC_PROP_METAMARK для
элемента с ID=$game_id
$average = round($summ_mark_for_game/$db_reviews_exists>SelectedRowsCount());
CIBlockElement::SetPropertyValueCode($game_id, GAMES_PC_PROP_METAMARK,
$average);
AddMessage2Log("Обработчик завершил работу. Элементу с ID=".$game_id." в
свойство ".GAMES_PC_PROP_METAMARK." записано значение ".$average."\n");
}
}
/**
* array get_iblock_type_list(array ar_sort) - функция получения списка информационных
блоков
* @author dda
* @param array ar_sort - массив вида ("поле_сортировки" => "направление_сортировки"),
указывающий, как сортировать список типов информационных блоков
* @return array ar_iblock_type - возвращает список типов информационных блоков
*/
function get_iblock_type_list($ar_sort)
{
$db_iblock_type = CIBlockType::GetList(array("NAME" => "ASC"));
$ar_iblock_type = array();
while($fetch = $db_iblock_type->GetNext()){
if($fetch_lang = CIBlockType::GetByIDLang($fetch["ID"], LANG)){
$ar_iblock_type[$fetch_lang["ID"]] = "[".$fetch_lang["ID"]."]
".$fetch_lang["NAME"];
}
}
return $ar_iblock_type;
}
/**
* array get_iblock_list(string type_id, array ar_sort, bool bActive = true) - функция
получения информационных блоков по типу type_id
* @author dda
* @param string type_id - тип информационного блока (идентификатор)
* @param array ar_sort - массив вида ("поле_сортировки" => "направление_сортировки"),
указывающий, как сортировать список информационных блоков
* @param bool bActive - флаг, указывющий, выбирать ли только активные инфоблоки.
Необязательный, по умолчанию true, т.е. выбирать только активные
* @return array ar_iblock - возвращает список информационных блоков
*/
function get_iblock_list($type_id, $ar_sort, $bActive = true, $field = "ID")
{
137
$arFilter = array(
"TYPE" => $type_id,
);
if($bActive){
$arFilter["ACTIVE"] = "Y";
}
$db_iblock = CIBlock::GetList($ar_sort, $arFilter);
$ar_iblock = array();
while($fetch = $db_iblock->GetNext()){
$ar_iblock[$fetch[$field]] = "[".$fetch[$field]."] ".$fetch["NAME"];
}
return $ar_iblock;
}
/**
* void prepare_params(&arParams)
* фунцкия обрабатывает массив $arParams, приходящий в файл component.php компонента
(удаление лишних пробелов, экранирование и т.д.)
* @author dda
* @param arParams - массив параметров, передается по ссылке
* @return функция ничего не возвращает
*/
function prepare_params(&$arParams)
{
foreach($arParams as $key => $param){
if(!is_array($param)){
$arParams[$key] = trim($param);
}
else{
foreach($param as $p_key => $p_param){
$param[$p_key] = trim($p_param);
}
$arParams[$key] = $param;
}
}
}
/**
* bool is_ajax_request()
* в функции проверяется, запущена ли страница в режиме ajax-запроса
* @return true в случае, если страница запущена в режиме ajax-запроса и false в
противном случае
*/
function is_ajax_request()
{
return (isset($_REQUEST[AJAX_REQUEST_MODE_PARAM]) &&
$_REQUEST[AJAX_REQUEST_MODE_PARAM] == "Y");
}
/**
* string get_ajax_param()
* функция возвращает имя переменной, отвечающей за запуск страницы в режиме ajaxзапроса
*/
function get_ajax_param()
{
return defined(AJAX_REQUEST_MODE_PARAM) ? AJAX_REQUEST_MODE_PARAM :
"ajax_request_mode";
}
/** array get_parts_reg(string value[,string pattern]) - функция обрабатывает строку
<название> [значение] и возвращает части в
* виде массива регулярного выражения или false в случае ошибки
* @param string value - строка, подлежащая разбору
* @param string pattern - регуляное выражение. Необязательное, по умолчанию "/\[\b(\d+)(.)\]/i"
*/
function get_parts_reg($value, $pattern = "/\[\b(\d+)(.)\]/i")
138
{
//ищем последнее вхождение открывающей значение скобки
$skob_pos = mb_strpos($value, "[", 0, LANG_CHARSET);
//если скобка не найдена, выходим
if($skob_pos === false){
return false;
}
//вырезаем 2 части - текст и значение
$text = trim(mb_substr($value, 0, $skob_pos, LANG_CHARSET));
$val = trim(mb_substr($value, $skob_pos, (mb_strlen($value, LANG_CHARSET) mb_strlen($text, LANG_CHARSET)), LANG_CHARSET));
//обрабатываем значение - проверяем наличие правильного шаблона
if(!preg_match($pattern, $val, $matches)){
return false;
}
return array("matches" => $matches, "text" => $text);
}
/** int get_max_day(int month, int year) - функция возвращает максимальный день в
зависимости от года и месяца
* @param int current_month - месяц
* @param int current_year - год
* @return возвращает число месяца
*/
function get_max_day($current_month, $current_year)
{
if($current_month == 2){
if($_REQUEST["year"] % 4 == 0){
$top_day = 29;
}
else{
$top_day = 28;
}
}
elseif($current_month < 8){
if($current_month % 2 == 0){
$top_day = 30;
}
else{
$top_day = 31;
}
}
elseif($current_month >= 8){
if($current_month % 2 != 0){
$top_day = 30;
}
else{
$top_day = 31;
}
}
return $top_day;
}
?>
139
Download