Yep — простая flat-file CMS на OneScript





В статье рассмотрено создание простейшей flat-file CMS, на основе каркасной конфигурации для web-приложений OneScript, в среде 1С:Предприятие.

Введение

После публикации статьи, с описанием каркасной конфигурации, для создания web-приложений OneScript и почитав комментарии, я подумал, что неплохо бы сделать пример на ее основе, посложнее, чем вывод пары страничек html, а также такой, чтобы его можно было использовать в практических целях. В разное время на Инфорстарте появлялись публикации, с описанием создания различных CMS с использованием http-сервисов 1С:Предприятие, и я решил в качестве демонстрации создать еще один “велосипед”.

Поскольку опыта в написании CMS я не имею, при помощи google было найдено пару простейших CMS с открытым кодом, которые можно было бы взять за основу – это PICO CMS и Grav CMS. Поскольку я не имею опыта написания программ на php, а также не занимаюсь web-разработкой, пришлось остановить свой выбор на PICO CMS, как на более простой.

О том, что из этого получилось – читайте ниже, а посмотреть на это чудо-творение можно нажав на кнопку "Показать демо" или нажав сюда.

 

Анализ алгоритма работы PICO CMS

После просмотра исходного кода PICO CMS, стало понятным, что в общих чертах она работает по примерно следующей схеме:

  1. Клиент из браузера отправляет запрос с идентификатором контента, который необходимо отобразить. Контент представляет собой обычные текстовые файлы в формате markdown, которые расположены на web-сервере.
  2. CMS ищет и загружает контент в память, в соответствии с настройками, которые хранятся в конфигурационных файлах. Конфигурационные файлы – это обычные текстовые файлы в формате yaml.
  3. CMS имеет так называемые темы. Фактически – это набор шаблонов html-страниц, а также необходимое оформление в виде css, javascript и других файлов. В содержании контента содержится информация о том, какой шаблон необходимо использовать для отображения страницы. Соответственно, на основе этой информации загружается необходимый шаблон.
  4. Производится преобразование контента из markdown в html.
  5. На основе загруженного шаблона, обработанного контента и других параметров, шаблонизатор формирует 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! 

31 Comments

  1. blackhole321

    (1) 😉

    Reply
  2. spacecraft

    (1)

    при использовании русского это должно быть написано

    …»Клиент из обозревателя отправляет запрос с идентификатором содержимого»…

    т.е. «идентификатор» нисколько не смущает?

    Или борцам за чистоту русского языка это позволительно?

    Reply
  3. herfis

    (3) А богомерзкое романо-германское «клиент», прости господи? Вместо православного «потребитель»?

    ИМХО, так надо: «потребитель из обозревателя отправляет запрос с отличительным признаком содержимого».

    Хотя я бы сразу на церковнославянский переходил. Хотя бы прикольно будет.

    Reply
  4. spacecraft

    (4) вспомнился отрывок из «Иван Васильевич меняет профессию»:

    — Ты как челобитную царю подаешь?

    Reply
  5. Steelvan

    (4) «потребитель из обозревателя отправляет запрос с опознавателем содержимого»

    как вариант, хотя вместо «опознавателем» можно еще подумать над короткими вариантами

    Reply
  6. Steelvan

    (4) Вместо «Потребитель» можно еще «Получатель».

    Reply
  7. blackhole321

    Коллеги, я тут ещё тему адаптировал

    http://1script.cf/yep/launch

    Боюсь не попасть бы с моей неграмотностью в неудобное положение опять. Что посоветуете, как переводить?

    Reply
  8. Steelvan

    Предлагай варианты, а мы поправим 🙂

    Reply
  9. Steelvan

    Если я правильно понял, автор серьезно интересуется технологиями и способами работы с 1С из сетевых технологий ?

    В чем причина такой заинтересованности ?

    Reply
  10. blackhole321

    (10)Да просто интерес. Темы на стыке технологий как правило не скучные 🙂

    Reply
  11. Alien_job

    Что-то ссылки не открываются

    Reply
  12. blackhole321

    (12)проблемы у провайдера. Говорят, через полчасика будет норм

    Reply
  13. blackhole321

    (12)починили, ссылки работают 🙂

    Reply
  14. bulpi
    Вот таким вот нехитрым способом, мы получили web-приложение OneScript, которое Вы можете использовать для создания своих собственных сайтов

    Да, только вот для этого надо уметь писать файлы в формате markdown.

    Типа вот так взял и простенько написал:

    ![Это ссылка]({{КаталогКонтента}}/images/default_banner.png)

    ## ![alt text]({{КаталогТем}}{{ТекущаяТема}}/images/checkmark-o24.png) Простая «файловая» CMS

    Или я чего-то не понял.

    Reply
  15. blackhole321

    (15)ну как бы да:), но это проще, чем html (для этого markdown и создавали). К сожалению пока нет библиотек, чтоб представил в мозгу и оно материализовалось в коде 🙂

    Reply
  16. quick

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

    Reply
  17. blackhole321

    (17)

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

    Поясните плз, на основании чего сделан такой вывод?

    На сколько мне известно, в типовых конфигурациях более 10 общих модулей и ничего, работает 🙂

    Reply
  18. quick

    (18) Потому что python …

    Потому что не хочется уже писать циклы для обычного среза массива а хочется Массив[10:15], ведь ерунда, но почему её не додумались добавить.

    Потому что хочется нормальных классов, декораторов и доступа к метаданным объектов (не путать с метаданными 1С). 1С этого не сделала, но что остановило OneScript? Если в OneScript появится ORM совместимый с 1С и хорошая интеграция с либами .NET, вот тогда это будет интересно, т.к позволит переписать оптимальней существующий говнокод 1С и не придумывать на каждый чих новое извращение. Если это все можно будет удобно закатать в Docker и деплоить в один пинок и в GIT без всяких плясок с бубном, тогда можно будет подумать 10 раз перед тем как начать на нем писать, АПокаЧтоЭтоВсеУдобочитаемыйКодИмениНугралиеваЧтоХренРазбереш­ь.

    P.S. Есть dokuwiki, она уже сейчас может решить 110% потребностей за счет расширений

    Reply
  19. blackhole321

    (19)

    Потому что python …

    А почему не php или c# или ВпишитеСвойЛюбимыйЯзыкПрограммирования :)?

    хочется Массив[10:15], ведь ерунда, но почему её не додумались добавить.

    Потому что хочется нормальных классов, декораторов и доступа к метаданным объектов

    Можно долго рассуждать на темы типа:

    Почему в 1С Тогда … КонецЕсли вместо { … }, ведь я привык к такому синтаксису?

    Почему Вам хочется объявлять массив так: Массив[10:15], а мне — int name[10][15]?

    Почему в PowerShell вместо операторов == и != используются -eq и -ne?

    Почему в PowerShell (о ужас!) нет классов (в привычном Вам виде), а в С их в принципе нет?



    etc.

    Язык программирования 1С такой, какой есть, и по моему мнению, надо относиться к этому как к данности. Соответственно Вы либо используете 1С (и соответственно ЯП 1С) либо нет 🙂

    На мой взгляд, как раз-таки наверное самым главным преимуществом OneScript является практически 100% совместимость с 1С, в плане написания кода. Соответственно, любой человек, владеющий программированием на языке 1С, практически без усилий, сможет писать на OneScript. В противном случае, затраты на изучение особенностей синтаксиса и «своих» библиотек и конструкций сопоставимы с затратами на изучение нового языка программирования.

    хорошая интеграция с либами .NET,

    Она и сейчас в общем-то неплохая, поскольку OneScript написан на .NET

    Если в OneScript появится ORM совместимый с 1С

    А зачем совместимый то? В 1С же все убого 🙂

    Если серьезно, посмотрите https://infostart.ru/public/672461/

    Прекрасная библиотека и вполне в стиле 1С.

    Если это все можно будет удобно закатать в Docker

    Посмотрите примеры на github, там есть и Docker 🙂

    P.S.

    Есть dokuwiki, она уже сейчас может решить 110% потребностей за счет расширений

    Ключевые слова: за счет расширений

    Не подскажете расширение, позволяющее сделать то же, что скажем в примере отсюда (https://infostart.ru/public/727311/)?

    Reply
  20. quick

    (20) так и есть, согласен с вами по всем пунктам.

    Могу только пожелать успехов и обрастать функционалом, может когда нибудь тоже подключусь с вами в разработку.

    Еще один взгляд на веб сервисы http://spyne.io , возможно найдете для себя что то полезное

    Reply
  21. webester

    Что такое MarkdownПроцессор? Где его реализация?

    Reply
  22. blackhole321

    (22) Markdown и Yaml процессоры — это библиотеки для чтения контента, соответственно в формате markdown и yaml. Описаны в статье https://infostart.ru/public/780389/ .

    Версии для OneScript уже включены в конфигурацию. Версии для 1С надо скачать, зарегистрировать regasm /codebase ПутьИмяДлл, затем создать COM+ приложение, если Ваша система x64

    Reply
  23. webester

    (23)Понятно, ком объект. Печаль.

    Reply
  24. blackhole321

    (24)Если не секрет — в чем печаль? Они же только для разработки нужны.

    Reply
  25. webester

    (25)Ну гораздо интереснее нативная реализация, которую можно потрогать самому в случае необходимости. Если есть дополнительный механизм, то всегда велика вероятность, что, что-то пойдет не так как ожидается. Ну и кроссплатформенность конечно. На рабочей машине, можно попробовать целиком уйти в какой нить центосубунту.

    Reply
  26. blackhole321

    (26)потрогать Вы можете, исходники доступны, правда на c#.

    Мне кажется, что Вы смотрите несколько не под тем углом. Вы же хотите решить какую-то проблему с минимальными усилиями. Язык 1с как мне кажется, не предназначен для написания больших системных библиотек. Оно конечно можно, но думаю, Вам не доставит радости лицезреть километры кода, которые выполняют одну функцию.

    Относительно кроссплатформенности — ключевые слова: можно было бы попробовать 🙂 Вопрос -зачем? Ваш код в продуктивные сможет выполняться на Линукс и windows.

    Reply
  27. webester

    (27)В чем соль. Было бы неплохо иметь возможность добавить к примеру свой тег не ковыряя c# и ничего не компилируя. Я здесь собственно поэтому, и статьи читаю по этой теме, что было бы круто написать все на одном языке.

    Мне кажется, что Вы смотрите несколько не под тем углом

    Используйте комобъект и не вые…живайтесь. Я правильно понял? ))

    (27)

    Оно конечно можно, но думаю, Вам не доставит радости лицезреть километры кода, которые выполняют одну функцию.

    Очень смешно говорить такое 1снику который помнит функцию сбора проводок в Зик77. Грешно смеяться над больными людьми.

    Я вас понял, если у нас этого нет, то вы должны согласиться, что по факту это вам как бы не особо то и нужно 🙂

    Reply
  28. blackhole321

    (28)

    Используйте комобъект и не вые…живайтесь. Я правильно понял? ))

    Я вас понял, если у нас этого нет, то вы должны согласиться, что по факту это вам как бы не особо то и нужно 🙂

    Нет, Вы неправильно поняли 🙂

    Вопрос не в том, что есть, а чего нет и пошли в сад 🙂

    Постараюсь пояснить свою мысль.

    Есть хорошая библиотека (https://github.com/lunet-io/markdig), которая реализует работу с markdown и на базе которой написана библиотека для OneScript. Фактически написана прослойка, которая вызывает методы этой библиотеки. Также, написаны COM-компонента для того, чтобы можно было разрабатывать и отлаживать приложения с использованием этого компонента из конфигуратора.

    Собственно как Вы можете видеть — библиотека не о трех строках, люди ее развивают и поддерживают. Вопрос в том — зачем ее писать на 1С? Ведь конечная цель, скажем к примеру — создать cms.

    Да конечно, эту компоненту можно реализовать в виде NativeAPI, однако это на порядок большие трудозатраты. Опять возникает вопрос — зачем? Если 90% разработчиков, в том числе и Вы, используют Windows. В дополнение представьте, что таких библиотек может быть 5, 10. Ради чего тратить время на то, чтобы реализовывать их через NativeApi?

    Ну вот собственно то, чем я руководствовался.

    Reply
  29. VasilVtoroy

    Жалко, нет скриншотов получившегося сайта

    Reply
  30. blackhole321

    (30) Зачем скриншоты? Там вверху ссылка на демо-сайт. http://1script.cf/yepcos или еще демо-тема — http://1script.cf/yep/launch

    Reply
  31. kote

    (0)

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *