Введение
После публикации статьи, с описанием каркасной конфигурации, для создания web-приложений OneScript и почитав комментарии, я подумал, что неплохо бы сделать пример на ее основе, посложнее, чем вывод пары страничек html, а также такой, чтобы его можно было использовать в практических целях. В разное время на Инфорстарте появлялись публикации, с описанием создания различных CMS с использованием http-сервисов 1С:Предприятие, и я решил в качестве демонстрации создать еще один “велосипед”.
Поскольку опыта в написании CMS я не имею, при помощи google было найдено пару простейших CMS с открытым кодом, которые можно было бы взять за основу – это PICO CMS и Grav CMS. Поскольку я не имею опыта написания программ на php, а также не занимаюсь web-разработкой, пришлось остановить свой выбор на PICO CMS, как на более простой.
О том, что из этого получилось – читайте ниже, а посмотреть на это чудо-творение можно нажав на кнопку "Показать демо" или нажав сюда.
Анализ алгоритма работы PICO CMS
После просмотра исходного кода PICO CMS, стало понятным, что в общих чертах она работает по примерно следующей схеме:
- Клиент из браузера отправляет запрос с идентификатором контента, который необходимо отобразить. Контент представляет собой обычные текстовые файлы в формате markdown, которые расположены на web-сервере.
- CMS ищет и загружает контент в память, в соответствии с настройками, которые хранятся в конфигурационных файлах. Конфигурационные файлы – это обычные текстовые файлы в формате yaml.
- CMS имеет так называемые темы. Фактически – это набор шаблонов html-страниц, а также необходимое оформление в виде css, javascript и других файлов. В содержании контента содержится информация о том, какой шаблон необходимо использовать для отображения страницы. Соответственно, на основе этой информации загружается необходимый шаблон.
- Производится преобразование контента из markdown в html.
- На основе загруженного шаблона, обработанного контента и других параметров, шаблонизатор формирует html страницу, которая и возвращается клиенту.
В качестве шаблонизатора, в системах, написанных на php как правило используется twig. Если совсем утрировано – это такой прокачанный вариант СтрЗаменить.
Основные принципы работы Yep
Поскольку Yep CMS создана на 1С, она использует концепцию СКД, где html-страница, возвращаемая пользователю рассматривается как некий отчет. Схема работы, в этом случае, имеет примерно следующий вид:
В качестве схемы данных выступает контент, в качестве параметров компоновки – параметры запроса. Макетом компоновки данных является "Тема" - это макет выводимой страницы, а также ее оформление. На основе этих компонентов, компоновщик данных формирует соответствие, элементами которого являются макет страницы, контент, значения параметров для заполнения макета etc. Процессором вывода данных является сам web-сервис. Он вызывает компоновщик данных, затем на основе полученного результата формирует html страницу и возвращает ее клиенту в теле ответа.
Детали реализации
Файловая структура
Файловая структура приложения представлена на рисунке ниже:
Как можно увидеть, в дополнение к стандартным файлам и папкам web-приложения, в корне приложения находятся файлы – Конфигурация.yaml и НастройкиТемы.yaml. Первый – содержит глобальные настройки CMS (такие как имя документа по умолчанию, имя текущей темы etc.), второй – настройки текущей темы (цвета etc). В корне приложения расположены файлы скриптов OneScript – это index.os и article.os, которые формируют страницы/отчеты на основе нашего макета компоновки данных и отправляют их клиенту. Также имеются две папки Контент и Темы. В папке “Контент”, находятся файлы, содержимое которых мы будем отображать, а также файл, описывающий меню нашего сайта.
В папке “Темы” содержатся варианты оформления наших страниц-отчетов, а также макеты/шаблоны страниц.
Идентификатор контента
Служит для идентификации файла, содержание которого мы хотим отобразить. В нашем случае, будем передавать его в качестве параметра, аналогично тому, как это сделано в WordPress. В качестве идентификатора используем относительный виртуальный путь к файлу контента, причем используется имя файла контента без расширения. Таким образом, запрос на отображение определенного файла будет выглядеть примерно следующим образом:
http://ИмяСайта/ИмяПроцессораВывода.os?p=ОтносительныйПуть/ИмяФайлаКонтента
Глобальные настройки CMS
Находятся в файле Конфигурация.yaml, который имеет формат yaml и расположен в корне приложения. Содержимое файла имеет примерно следующий вид:
---
# Файл конфигурации Yep CMS
# Название Вашего сайта
ИмяСайта: Yep!
# Краткое описание сайта
Описание: Простая flat-file CMS на 1С
# Имя файла, содержащего информацию о меню
ИмяФайлаМеню: Меню.yaml
# Каталог, где расположен контент (файлы md etc.)
КаталогКонтента: Контент/
# Расширение для файлов контента
РасширениеФайлаКонтента: ".md"
# Каталог, где расположены темы
КаталогТем: Темы/
# Имя папки активной темы
ТекущаяТема: "Демо"
# Расширение для файлов макетов (шаблонов)
РасширениеФайлаМакета: ".html"
# Указывает, надо ли преобразовывать контент из Markdown в Html автоматически
ПреобразоватьКонтент: да
# Определяет имя файла контента по умолчанию. Используется, если контент с указанным именем не найден
СтраницаПоУмолчанию: index
# Список расширений Markdown, который используется для преобразования.
# Если параметр не задан, используется yaml+advanced
РасширенияMarkdown: yaml+advanced
Меню сайта
В отличие от Pico и Grav, где меню сайта строится динамически на основе файловой структуры, в настоящей CMS меню сайта описывается файлом Меню.yaml, который расположен в корне папки контента и имеет примерно следующую структуру:
---
# Файл конфигурации меню сайта
# Меню представляет собой массив элементов (пунктов меню)
# Для организации подменю, в пункт меню добавляется свойство Подменю.
# Пример пункта меню со ссылкой
#- Заголовок: "Со ссылкой"
# url: "http://oscript.io"
# Пример пункта меню с подменю
#- Заголовок: С подменю
# Подменю:
# - Заголовок: Подменю1
# Каталог: Описание/
# Контент: overview
# Страница: index
- Заголовок: ГЛАВНАЯ
Страница: index
Каталог: /
Контент: index
- Заголовок: ОПИСАНИЕ
Каталог: Описание/
Контент: overview
Страница: index
- Заголовок: ДОКУМЕНТАЦИЯ
Каталог: Документация/
Контент: index
Страница: article
- Заголовок: ЗАГРУЗИТЬ
Каталог: Download/
Контент: links
Страница: index
Формат контента
Файлы контента представляют собой простые текстовые файлы в формате Markdown, которые имеют заголовок, содержащий метаданные в формате yaml.
Примерное содержание файла контента представлено ниже:
---
Заголовок: Yep CMS
Описание: Простая flat-file CMS на языке 1С
ПреобразоватьКонтент: да
ЭтоМакет: да
ЗаполнитьПараметры: да
---
![Это ссылка]({{КаталогКонтента}}/images/default_banner.png)
## ![alt text]({{КаталогТем}}{{ТекущаяТема}}/images/checkmark-o24.png) Простая "файловая" CMS
Yep CMS - это простая система управления контентом, где в качестве контента используются файлы в формате Markdown.
Основой, для создания настоящей системы, является [Pico CMS](http://picocms.org). Однако, некоторые идеи заимствованы также из [Grav CMS](https://getgrav.org/).
## ![alt text]({{КаталогТем}}{{ТекущаяТема}}/images/checkmark-o24.png) Создана на платформе OneScript
Yep CMS - фактически является http-сервисом, написанным на языке 1С:Предприятие, который выполняется платформой [OneScript](http://oscript.io).
## ![alt text]({{КаталогТем}}{{ТекущаяТема}}/images/checkmark-o24.png) Разработка и отладка в конфигураторе
Поскольку платформа OneScript обеспечивает совместимость с языком 1С:Предприятие, разработка, отладка и тестирование может производиться непосредственно в конфигураторе.
Как можно увидеть, файл контента имееет заголовок в формате yaml, в котором содержится ряд параметров. Обязательным параметром страницы является “Заголовок”. Также имеется ряд параметров, отвечающих за преобразование контента из формата markdown в формат html.
Компоновка данных
Процесс компоновки данных запускается вызовом функции ПолучитьРезультатКомпоновкиДанных, которая расположена в общем модуле ПроцессорКомпоновкиДанных. Исходный код функции представлен ниже:
// Формирует результат компоновки данных, который используется для вывода страницы
//
Функция ПолучитьРезультатКомпоновкиДанных(ПараметрыКомпоновки) Экспорт
РезультатКомпоновки = Новый Соответствие;
РезультатКомпоновки.Вставить("Ответ", Новый HTTPСервисОтвет(200));
КонфигурацияCMS = ПолучитьКонфигурацию();
Если КонфигурацияCMS.Получить("РасширенияMarkdown") = Неопределено Тогда
КонфигурацияCMS.Вставить("РасширенияMarkdown", "yaml+advanced");
КонецЕсли;
ПараметрыКомпоновки.Вставить("Конфигурация", КонфигурацияCMS);
НастройкиТемы = ПолучитьНастройкиТемы();
Если Не НастройкиТемы = Неопределено Тогда
СкопироватьПараметры(НастройкиТемы, КонфигурацияCMS, "НастройкиТемы.");
КонецЕсли;
РезультатКомпоновки.Вставить("Конфигурация", КонфигурацияCMS);
РезультатКомпоновки.Вставить("Меню", ПолучитьМеню(ПараметрыКомпоновки));
РезультатКомпоновки.Вставить("Макет", ПолучитьМакет(ПараметрыКомпоновки));
Страница = ПолучитьСтраницу(ПараметрыКомпоновки);
Если Страница = Неопределено Тогда
РезультатКомпоновки.Вставить("Ответ", Новый HTTPСервисОтвет(404));
Возврат РезультатКомпоновки;
КонецЕсли;
РезультатКомпоновки.Вставить("Страница", Страница);
ПараметрыВывода = ПолучитьПараметрыВывода(ПараметрыКомпоновки, РезультатКомпоновки);
РезультатКомпоновки.Вставить("ПараметрыВывода", ПараметрыВывода);
ОбработатьКонтент(РезультатКомпоновки);
ПараметрыВывода.Вставить("Контент", Страница.Получить("Контент"));
Возврат РезультатКомпоновки;
КонецФункции
Обработка контента
Обработка контента (его преобразование из markdown в html) осуществляется в функции ОбработатьКонтент. Исходный код функции представлен ниже:
// Осуществляет преобразования загруженного контента, в соответствии с настройками
//
Процедура ОбработатьКонтент(РезультатКомпоновки, Страница = Неопределено) Экспорт
Если Страница = Неопределено Тогда
Страница = РезультатКомпоновки["Страница"];
КонецЕсли;
КонфигурацияCMS = РезультатКомпоновки["Конфигурация"];
ПараметрыВывода = РезультатКомпоновки["ПараметрыВывода"];
Если НРег(Страница.Получить("ЭтоМакет")) = "да" И Нрег(Страница.Получить("ЗаполнитьПараметры")) = "да" Тогда
Страница.Вставить("Контент", Макеты.ЗаполнитьПараметры(Страница.Получить("Контент"), ПараметрыВывода));
КонецЕсли;
Если Не НРег(Страница.Получить("ПреобразоватьКонтент")) = "нет" И Не НРег(КонфигурацияCMS.Получить("ПреобразоватьКонтент")) = "нет" Тогда
Страница.Вставить("Контент", MarkdownПроцессор.ПолучитьHtmlИзMarkdown(Страница.Получить("Контент"), КонфигурацияCMS["РасширенияMarkdown"]));
КонецЕсли;
КонецПроцедуры
Как можно увидеть, на процесс обработки контента влияют некотороые глобальные параметры конфигурации, а также параметры, специфичные для файла контента.
Если в свойствах файла контента, установлены свойства ЭтоМакет = да и ЗаполнитьПараметры = да, то в этом случае, перед преобразованием будет осуществлена подстановка параметров из результатов компоновки в тело контента.
Если Свойство ПреобразоватьКонтент, которое может быть определено в свойствах файла контента и в глобальных настройках не запрещают преобразование, контент будет преобразован в html. Если же преобразование запрещено – контент будет помещен в результаты компоновки в исходном виде, что позволяет манипулировать им во время вывода.
Создание темы (оформления)
Поскольку я не являюсь каким-либо специалистом в html, css etc, я просто позаимствовал тему по умолчанию Pico CMS. Файловая структура темы после распаковки имеет следующий вид:
Перенесем все папки и файлы, которые относятся к оформлению (css, js etc.) в нашу тему и создадим на основе файла index.twig макет страницы, а также процессор вывода.
Макет страницы
Исходный файл index.twig имеет следующий вид:
<!DOCTYPE html>
<html class="no-js">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
<title>{% if meta.title %}{{ meta.title }} | {% endif %}{{ site_title }}</title>
{% if meta.description %}
<meta name="description" content="{{ meta.description|striptags }}" />
{% endif %}{% if meta.robots %}
<meta name="robots" content="{{ meta.robots }}" />
{% endif %}
{% if current_page %}
<link rel="canonical" href="{{ current_page.url }}" />
{% endif %}
<link rel="stylesheet" href="{{ theme_url }}/css/style.css" type="text/css" />
<link rel="stylesheet" href="{{ theme_url }}/css/droidsans.css" type="text/css" />
<link rel="stylesheet" href="{{ theme_url }}/css/fontello.css" type="text/css" />
</head>
<body{% if config.theme_config.widescreen %} class="widescreen"{% endif %}>
<div id="header">
<div class="container">
<a id="nav-toggle" title="Toggle Menu" role="button" aria-controls="nav" aria-expanded="false" tabindex="1">
<span class="icon-menu" aria-hidden="true"></span>
<span class="sr-only">Toggle Menu</span>
</a>
<h1>
<a href="{{ "index"|link }}">{{ site_title }}</a>
</h1>
<div id="nav" role="region" tabindex="-1">
<ul>
{% for page in pages if page.title and not page.hidden %}
{% set pageDepth = page.id|split('/')|length %}
{% if (pageDepth == 2) and (page.id ends with "/index") or (pageDepth == 1) %}
<li{% if page.id == current_page.id %} class="active"{% endif %}>
<a href="{{ page.url }}">{{ page.title }}</a>
</li>
{% endif %}
{% endfor %}
</ul>
</div>
</div>
</div>
<div id="main">
<div class="container">
{{ content }}
</div>
</div>
<div id="footer">
<div class="container">
<div class="social">
{% for social in pages._meta.meta.social %}
<a href="{{ social.url }}" title="{{ social.title }}" role="button">
<span class="icon-{{ social.icon }}" aria-hidden="true"></span>
<span class="sr-only">{{ social.title }}</span>
</a>
{% endfor %}
</div>
<p>
<a href="/redirect.php?url=aHR0cDovL3BpY29jbXMub3JnLw==">Pico</a> was made by <a href="/redirect.php?url=aHR0cDovL2dpbGJlcnQucGVsbGVncm9tLm1l">Gilbert Pellegrom</a>
and is maintained by <a href="/redirect.php?url=aHR0cHM6Ly9naXRodWIuY29tL3BpY29jbXMvUGljby9ncmFwaHMvY29udHJpYnV0b3Jz">The Pico Community</a>.
Released under the <a href="/redirect.php?url=aHR0cHM6Ly9naXRodWIuY29tL3BpY29jbXMvUGljby9ibG9iL21hc3Rlci9MSUNFTlNFLm1k">MIT license</a>.
</p>
</div>
</div>
<script src="{{ theme_url }}/js/modernizr-3.3.1-custom.min.js" type="text/javascript"></script>
<script src="{{ theme_url }}/js/utils.js" type="text/javascript"></script>
<script src="{{ theme_url }}/js/pico.js" type="text/javascript"></script>
</body>
</html>
Как можно увидеть, файл представляет собой страницу html со вставками кода шаблонизатора twig. Создадим на его основе макет с областями, аналогично тому, как это сделано в 1С:Предприятие или Crystal Reports. В качестве разделителей областей будем использовать комментарии html.
После редактирования, файл макета будет иметь следующий вид:
<!--Начало-->
<!DOCTYPE html>
<html class="no-js">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
<title>{{Заголовок}}</title>
<meta name="description" content="{{Описание}}" />
<!---
{% if current_page %}
<link rel="canonical" href="{ current_page.url }}" />
{% endif %}
-->
<link rel="stylesheet" href="{{КаталогТем}}{{ТекущаяТема}}/css/style.css" type="text/css" />
<link rel="stylesheet" href="{{КаталогТем}}{{ТекущаяТема}}/css/droidsans.css" type="text/css" />
<link rel="stylesheet" href="{{КаталогТем}}{{ТекущаяТема}}/css/fontello.css" type="text/css" />
<link rel="stylesheet" href="{{КаталогТем}}{{ТекущаяТема}}/highlight/github.css">
<script src="{{КаталогТем}}{{ТекущаяТема}}/highlight/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
</head>
<!---<body{% if config.theme_config.widescreen %} class="widescreen"{% endif %}>-->
<body>
<div id="header">
<div class="container">
<a id="nav-toggle" title="Toggle Menu" role="button" aria-controls="nav" aria-expanded="false" tabindex="1">
<span class="icon-menu" aria-hidden="true"></span>
<span class="sr-only">Toggle Menu</span>
</a>
<h1>
<a href="">{{ИмяСайта}}</a>
</h1>
<div id="nav" role="region" tabindex="-1">
<ul>
<!--ПунктМеню-->
<li{{ОформлениеПунктаМеню}}>
<a href="{{url}}">{{Заголовок}}</a>
</li>
<!--ПунктМеню-->
<!--Контент-->
</ul>
</div>
</div>
</div>
<div id="main">
<div class="container">
{{Контент}}
</div>
</div>
<div id="footer">
<div class="container">
<!--
<div class="social">
{% for social in pages._meta.meta.social %}
<a href="{ social.url }}" title="{ social.title }}" role="button">
<span class="icon-{ social.icon }}" aria-hidden="true"></span>
<span class="sr-only">{ social.title }}</span>
</a>
{% endfor %}
</div>
-->
<p>
Создано на основе <a href="/redirect.php?url=aHR0cDovL3BpY29jbXMub3JnLw==">Pico</a> в среде <a href="/redirect.php?url=aHR0cDovLzFjLnJ1Lw==">1С:Предприятие</a>
, выполняется на <a href="/redirect.php?url=aHR0cDovL29zY3JpcHQuaW8=">OneScript</a>.
</p>
</div>
</div>
<script src="{{КаталогТем}}{{ТекущаяТема}}/js/modernizr-3.3.1-custom.min.js" type="text/javascript"></script>
<script src="{{КаталогТем}}{{ТекущаяТема}}/js/utils.js" type="text/javascript"></script>
<script src="{{КаталогТем}}{{ТекущаяТема}}/js/pico.js" type="text/javascript"></script>
</body>
</html>
<!--Контент-->
Сохраним его с именем index.html в папку темы.
Вывод страницы
На основе анализа кода шаблонизатора, создадим http-сервис с именем index.os, который будет формировать страницу на основе результатов компоновки. Исходный код сервиса представлен ниже:
Функция ОбработкаВызоваHTTPСервиса(Запрос) Экспорт
ПараметрыКомпоновки = Новый Соответствие;
ПараметрыКомпоновки.Вставить("Запрос", Запрос);
РезультатКомпоновки = ПроцессорКомпоновкиДанных.ПолучитьРезультатКомпоновкиДанных(ПараметрыКомпоновки);
Ответ = РезультатКомпоновки["Ответ"];
Если Не Ответ.КодСостояния = 200 Тогда
Возврат Ответ;
КонецЕсли;
Макет = РезультатКомпоновки["Макет"];
ПараметрыВывода = РезультатКомпоновки["ПараметрыВывода"];
Текст = "";
// Формируем область до меню
Область = Макеты.ПолучитьОбласть(Макет, "<!--Начало-->", "<!--ПунктМеню-->");
Текст = Макеты.ЗаполнитьПараметры(Область.Текст, ПараметрыВывода);
// Формируем меню
Меню = РезультатКомпоновки.Получить("Меню");
Страница = РезультатКомпоновки["Страница"];
Если Не Меню = Неопределено Тогда
Для каждого ПунктМеню Из Меню Цикл
Область = Макеты.ПолучитьОбласть(Макет, "<!--ПунктМеню-->", "<!--ПунктМеню-->");
Если Не ПунктМеню.Получить("Каталог") = Неопределено И СтрНачинаетсяС(Страница["Идентификатор"], ПунктМеню.Получить("Каталог")) Тогда
ПунктМеню.Вставить("ОформлениеПунктаМеню", " class=""active"" ");
КонецЕсли;
Текст = Текст + Макеты.ЗаполнитьПараметры(Область.Текст, ПунктМеню);
КонецЦикла;
КонецЕсли;
//// Вставляем контент
Область = Макеты.ПолучитьОбласть(Макет, "<!--Контент-->", "<!--Контент-->");
Текст = Текст + Макеты.ЗаполнитьПараметры(Область.Текст, ПараметрыВывода);
Текст = Макеты.УдалитьНезаполненныеПараметры(Текст);
Ответ.УстановитьТелоИзСтроки(Текст);
Возврат Ответ;
КонецФункции
Добавляем статьи
К сожалению, тема по умолчанию не содержит каких-либо функций, позволяющих отображать контент с динамической навигацией, поэтому добавим его самостоятельно.
Для этого, на основе предыдущего, создадим макет примерно следующего вида:
<!--Начало-->
<!DOCTYPE html>
<html class="no-js">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
<title>{{Заголовок}}</title>
<meta name="description" content="{{Описание}}" />
<!---
{% if current_page %}
<link rel="canonical" href="{ current_page.url }}" />
{% endif %}
-->
<link rel="stylesheet" href="{{КаталогТем}}{{ТекущаяТема}}/css/style.css" type="text/css" />
<link rel="stylesheet" href="{{КаталогТем}}{{ТекущаяТема}}/css/droidsans.css" type="text/css" />
<link rel="stylesheet" href="{{КаталогТем}}{{ТекущаяТема}}/css/fontello.css" type="text/css" />
<link rel="stylesheet" href="{{КаталогТем}}{{ТекущаяТема}}/highlight/github.css">
<script src="{{КаталогТем}}{{ТекущаяТема}}/highlight/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
</head>
<!---<body{% if config.theme_config.widescreen %} class="widescreen"{% endif %}>-->
<body>
<div id="header">
<div class="container">
<a id="nav-toggle" title="Toggle Menu" role="button" aria-controls="nav" aria-expanded="false" tabindex="1">
<span class="icon-menu" aria-hidden="true"></span>
<span class="sr-only">Toggle Menu</span>
</a>
<h1>
<a href="">{{ИмяСайта}}</a>
</h1>
<div id="nav" role="region" tabindex="-1">
<ul>
<!--ПунктМеню-->
<li{{ОформлениеПунктаМеню}}>
<a href="{{url}}">{{Заголовок}}</a>
</li>
<!--ПунктМеню-->
<!--ЗавершениеЗаголовка-->
</ul>
</div>
</div>
</div>
<div style="width=100%; padding-left: 2em; padding-top: 1em;">
<!--ЗавершениеЗаголовка-->
<!--НавигацияПапки-->
<a href="{{url}}">{{Заголовок}}</a>
<!--НавигацияПапки-->
<!--ПередНавигацияСтраница-->
</div>
<div id="main">
<aside style="float: left;padding-left: 2rem; min-width: 32em;">
<!--ПередНавигацияСтраница-->
<!--НавигацияСтраница-->
<a href="{{url}}">{{Заголовок}}</a><br>
<!--НавигацияСтраница-->
<!--Контент-->
</aside>
<div class="container">
{{Контент}}
</div>
</div>
<div id="footer">
<div class="container">
<!--
<div class="social">
{% for social in pages._meta.meta.social %}
<a href="{ social.url }}" title="{ social.title }}" role="button">
<span class="icon-{ social.icon }}" aria-hidden="true"></span>
<span class="sr-only">{ social.title }}</span>
</a>
{% endfor %}
</div>
-->
<p>
Создано на основе <a href="/redirect.php?url=aHR0cDovL3BpY29jbXMub3JnLw==">Pico</a> в среде <a href="/redirect.php?url=aHR0cDovLzFjLnJ1Lw==">1С:Предприятие</a>
, выполняется на <a href="/redirect.php?url=aHR0cDovL29zY3JpcHQuaW8=">OneScript</a>.
</p>
</div>
</div>
<script src="{{КаталогТем}}{{ТекущаяТема}}/js/modernizr-3.3.1-custom.min.js" type="text/javascript"></script>
<script src="{{КаталогТем}}{{ТекущаяТема}}/js/utils.js" type="text/javascript"></script>
<script src="{{КаталогТем}}{{ТекущаяТема}}/js/pico.js" type="text/javascript"></script>
</body>
</html>
<!--Контент-->
а также соответствующий ему http-сервис с именем article.os
Функция ОбработкаВызоваHTTPСервиса(Запрос) Экспорт
ПараметрыКомпоновки = Новый Соответствие;
ПараметрыКомпоновки.Вставить("Запрос", Запрос);
РезультатКомпоновки = ПроцессорКомпоновкиДанных.ПолучитьРезультатКомпоновкиДанных(ПараметрыКомпоновки);
Ответ = РезультатКомпоновки["Ответ"];
Если Не Ответ.КодСостояния = 200 Тогда
Возврат Ответ;
КонецЕсли;
Макет = РезультатКомпоновки["Макет"];
ПараметрыВывода = РезультатКомпоновки["ПараметрыВывода"];
Текст = "";
// Формируем область до меню
Область = Макеты.ПолучитьОбласть(Макет, "<!--Начало-->", "<!--ПунктМеню-->");
Текст = Макеты.ЗаполнитьПараметры(Область.Текст, ПараметрыВывода);
// Формируем меню
Меню = РезультатКомпоновки.Получить("Меню");
Страница = РезультатКомпоновки["Страница"];
Если Не Меню = Неопределено Тогда
Для каждого ПунктМеню Из Меню Цикл
Область = Макеты.ПолучитьОбласть(Макет, "<!--ПунктМеню-->", "<!--ПунктМеню-->");
Если Не ПунктМеню.Получить("Каталог") = Неопределено И СтрНачинаетсяС(Страница["Идентификатор"], ПунктМеню.Получить("Каталог")) Тогда
ПунктМеню.Вставить("ОформлениеПунктаМеню", " class=""active"" ");
КонецЕсли;
Текст = Текст + Макеты.ЗаполнитьПараметры(Область.Текст, ПунктМеню);
КонецЦикла;
КонецЕсли;
// Вставляем область после меню
Область = Макеты.ПолучитьОбласть(Макет, "<!--ЗавершениеЗаголовка-->", "<!--ЗавершениеЗаголовка-->");
Текст = Текст + Область.Текст;
// Формируем навигацию папок
Область = Макеты.ПолучитьОбласть(Макет, "<!--НавигацияПапки-->", "<!--НавигацияПапки-->");
СтраницыПапки = ПолучитьСтраницыПапок(РезультатКомпоновки);
ТекстНавигацияПапки = "";
Количество = СтраницыПапки.Количество();
Индекс = 0;
Для Индекс = 0 По Количество - 1 Цикл
Если Не Индекс = Количество - 1 Тогда
СтраницыПапки[Индекс].Заголовок = СтраницыПапки[Индекс].Заголовок + " > ";
КонецЕсли;
ТекстНавигацияПапки = ТекстНавигацияПапки + Макеты.ЗаполнитьПараметры(Область.Текст, СтраницыПапки[Индекс]);
КонецЦикла;
Текст = Текст + ТекстНавигацияПапки;
// Формируем навигацию страниц и подпапок текущей папки
Область = Макеты.ПолучитьОбласть(Макет, "<!--ПередНавигацияСтраница-->", "<!--ПередНавигацияСтраница-->");
Текст = Текст + Область.Текст;
Область = Макеты.ПолучитьОбласть(Макет, "<!--НавигацияСтраница-->", "<!--НавигацияСтраница-->");
МассивСтраниц = ПолучитьСтраницыТекущейПапки(РезультатКомпоновки);
МассивСтраниц = ОтсортироватьСтраницыПоЗаголовку(МассивСтраниц);
Для каждого СтраницаНавигация Из МассивСтраниц Цикл
Если СтраницаНавигация.Идентификатор = Страница["Идентификатор"] Тогда
СтраницаНавигация.Заголовок = "<b>" + СтраницаНавигация.Заголовок + "</b>";
КонецЕсли;
Текст = Текст + Макеты.ЗаполнитьПараметры(Область.Текст, СтраницаНавигация);
КонецЦикла;
//// Вставляем контент
Область = Макеты.ПолучитьОбласть(Макет, "<!--Контент-->", "<!--Контент-->");
Текст = Текст + Макеты.ЗаполнитьПараметры(Область.Текст, ПараметрыВывода);
Текст = Макеты.УдалитьНезаполненныеПараметры(Текст);
Ответ.УстановитьТелоИзСтроки(Текст);
Возврат Ответ;
КонецФункции
Функция ПолучитьСтраницыПапок(РезультатКомпоновки)
КонфигурацияCMS = РезультатКомпоновки["Конфигурация"];
Страница = РезультатКомпоновки["Страница"];
Идентификатор = Страница["Идентификатор"];
// Получаем страницу default для каждой подпапки
НомерСимвола = 0;
НомерВхождения = 1;
НомерСимвола = СтрНайти(Идентификатор, "/",,НомерВхождения);
Процессор = YamlПроцессор.НовыйYamlПроцессор();
МассивСтраниц = Новый Массив;
Пока Не НомерСимвола = 0 Цикл
ИдентификаторПоУмолчанию = Лев(Идентификатор, НомерСимвола) + КонфигурацияCMS["СтраницаПоУмолчанию"];
ФизическийПутьКСтранице = ПроцессорКомпоновкиДанных.ПолучитьФизическийПутьИзВиртуального(
КонфигурацияCMS["КаталогКонтента"] + ИдентификаторПоУмолчанию) +
КонфигурацияCMS["РасширениеФайлаКонтента"];
СтраницаПапка = ПроцессорКомпоновкиДанных.ЗагрузитьСтраницу(ФизическийПутьКСтранице, Процессор);
СтруктураСтраница = Новый Структура;
СтруктураСтраница.Вставить("url", "article.os?p=" + ИдентификаторПоУмолчанию);
СтруктураСтраница.Вставить("Заголовок", СтраницаПапка["Заголовок"]);
СтруктураСтраница.Вставить("Идентификатор", ИдентификаторПоУмолчанию);
МассивСтраниц.Добавить(СтруктураСтраница);
НомерВхождения = НомерВхождения + 1;
НомерСимвола = СтрНайти(Идентификатор, "/",,,НомерВхождения);
КонецЦикла;
Индекс = МассивСтраниц.Количество() - 1;
Если Не МассивСтраниц[Индекс].Идентификатор = Идентификатор Тогда
СтруктураСтраница = Новый Структура;
СтруктураСтраница.Вставить("url", "article.os?p=" + Идентификатор);
СтруктураСтраница.Вставить("Заголовок", Страница["Заголовок"]);
СтруктураСтраница.Вставить("Идентификатор", Идентификатор);
МассивСтраниц.Добавить(СтруктураСтраница);
КонецЕсли;
Возврат МассивСтраниц;
КонецФункции
Функция ПолучитьСтраницыТекущейПапки(РезультатКомпоновки)
КонфигурацияCMS = РезультатКомпоновки["Конфигурация"];
Страница = РезультатКомпоновки["Страница"];
Идентификатор = Страница["Идентификатор"];
ВиртуальныйКаталог = Лев(Идентификатор, СтрНайти(Идентификатор, "/", НаправлениеПоиска.СКонца,,1));
Процессор = YamlПроцессор.НовыйYamlПроцессор();
МассивСтраниц = Новый Массив;
ФизическийПутьКСтранице = ПроцессорКомпоновкиДанных.ПолучитьФизическийПутьИзВиртуального(
КонфигурацияCMS["КаталогКонтента"] +
Страница["Идентификатор"] +
КонфигурацияCMS["РасширениеФайлаКонтента"]);
Файл = Новый Файл(ФизическийПутьКСтранице);
Файлы = НайтиФайлы(Файл.Путь, "*.*", Ложь);
Для каждого Файл Из Файлы Цикл
Если Файл.ЭтоКаталог() Тогда
ФайлСтраницы = НайтиФайлы(Файл.ПолноеИмя, КонфигурацияCMS["СтраницаПоУмолчанию"] + КонфигурацияCMS["РасширениеФайлаКонтента"], Ложь);
Если ФайлСтраницы.Количество() = 0 Тогда
Продолжить;
КонецЕсли;
ТекСтраница = ПроцессорКомпоновкиДанных.ЗагрузитьСтраницу(ФайлСтраницы[0].ПолноеИмя, Процессор);
СтруктураСтраница = Новый Структура;
СтруктураСтраница.Вставить("url", "article.os?p=" + ВиртуальныйКаталог + Файл.Имя + "/" + КонфигурацияCMS["СтраницаПоУмолчанию"]);
СтруктураСтраница.Вставить("Идентификатор", ВиртуальныйКаталог + Файл.Имя + "/" + КонфигурацияCMS["СтраницаПоУмолчанию"]);
СтруктураСтраница.Вставить("Заголовок", ТекСтраница["Заголовок"]);
МассивСтраниц.Добавить(СтруктураСтраница);
ИначеЕсли Не Файл.Расширение = КонфигурацияCMS["РасширениеФайлаКонтента"] Тогда
Продолжить;
Иначе
ТекСтраница = ПроцессорКомпоновкиДанных.ЗагрузитьСтраницу(Файл.ПолноеИмя, Процессор);
СтруктураСтраница = Новый Структура;
СтруктураСтраница.Вставить("url", "article.os?p=" + ВиртуальныйКаталог + Файл.ИмяБезРасширения);
СтруктураСтраница.Вставить("Идентификатор", ВиртуальныйКаталог + Файл.ИмяБезРасширения);
СтруктураСтраница.Вставить("Заголовок", ТекСтраница["Заголовок"]);
МассивСтраниц.Добавить(СтруктураСтраница);
КонецЕсли;
КонецЦикла;
Возврат МассивСтраниц;
КонецФункции
Функция ОтсортироватьСтраницыПоЗаголовку(МассивСтраниц)
// Страниц не 100500, сортируем пузырьком :)
// //infostart.ru/public/204320/
Для i = 0 По МассивСтраниц.ВГраница() Цикл
Для j = 0 ПО МассивСтраниц.Вграница() - i - 1 Цикл
Если МассивСтраниц[j].Заголовок > МассивСтраниц[j + 1].Заголовок Тогда
Замена = МассивСтраниц[j];
МассивСтраниц[j] = МассивСтраниц[j + 1];
МассивСтраниц[j + 1] = Замена;
КонецЕсли;
КонецЦикла;
КонецЦикла;
Возврат МассивСтраниц;
КонецФункции
Ну вот в общем-то и все. Наша тема готова. Формируем приложение способом, описанным в предыдущей статье и размещаем его на web-сервере.
Заключение
Вот таким вот нехитрым способом, мы получили web-приложение OneScript, которое Вы можете использовать для создания своих собственных сайтов, которые могут быть посвящены к примеру описанию созданной Вами конфигурации или чему-либо еще. Любая критика, предложения, замечания etc. приветствуются.
P.S.
Если Вы хорошо (в отличие от автора) владеете html, css, javascript, считаете web-приложжения OneScript или OneScript или все вместе - вредными, никчемными технологиями или вчерашним днем, и имеете желание и возможность адаптировать или создать тему etc. – Welcome!