Веб-разработка на 1Script. Глава 2

Публикация № 821424

Разработка - Практика программирования

1script onescript web веб-разработка слава роботам

Продолжение учебника по веб-разработке с помощью фреймворка Oscript.Web. Структура приложения, основные объекты, URL-маршрутизация, универсальная консоль серверов 1С.

Краткое содержание предыдущих серий

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

Попутно, я пишу данный цикл статей, которые совокупно можно считать книгой-учебником по разрабатываемому Фреймворку. И, поскольку, фреймворк находится в разработке – этот цикл статей еще и своего рода блог или репортаж с мест событий. Рабочее название проекта Oscript.Web или OSP.NET

В прошлой главе мы настроили окружение разработчика, установили необходимые инструменты, а также создали каркас будущего приложения. Приложением у нас является универсальная консоль администрирования серверов 1С, которая не требует постоянной перерегистрации COM-объектов для подключения к серверам 1С разных версий. Эта консоль получила название «Odminus». Ее исходный код открыт, и по мере накопления опыта в использовании фреймворка OSP.NET, сообщество сможет ее развивать самостоятельно. Итак, продолжим.

Проектирование

После написания первой статьи я сел и стал думать, как именно должно выглядеть будущее приложение. Оказалось, что каркас интерфейса, который был создан в прошлой статье – не совсем отвечает тому, какую структуру должна иметь консоль администрирования серверов 1С.

Я большой фанат книги А. Купера «Психбольница в руках пациентов». Легендарная вещь, раскрывающая глаза на проектирование систем, которыми напрямую должен пользоваться homo sapiens. Если вы еще не читали – прямо срочно бежим на торренты в магазин и приобретаем. Очень советую. Если очень упростить, то Купер говорит, что приложение надо проектировать с интерфейса. С того, как именно с приложением будет взаимодействовать человек. И именно удобство для человека должно определить дальнейшую технологическую архитектуру системы. Те части, с которыми человек не взаимодействует – можно проектировать любым удобным способом.

Так вот, оказалось, что веб-приложение тоже удобнее всего проектировать с того, что именно видит человек. Более того, наиболее важным и сложным для меня оказалось продумывание навигации по приложению. Какие именно гиперссылки в какую часть системы будут вести. Время гиперссылок web 1.0 прошло. Километровые параметры запроса в url – это уже не клево, и используется только в особых случаях. Мы все привыкли видеть адресацию вида /users/add, clients/pupkin/edit и /books/purchase/1c-radchenko и подобные. Адреса вида /users.php?action=delete&id=00001 это уже прошлый век.

Как мы помним из прошлой главы, в MVC применяется маршрутизация в разрезе Контроллеров и Действий. Причем Контроллер и Действие получаются из URL, запрошенного пользователем. Правила, по которому URL превращается в Контроллер и Действие называется Маршрутом.

Так вот, попытка разложить модель данных консоли администрирования 1С на Контроллеры и Действия, да еще и так, чтобы это было наглядно для homo sapiens (по заветам Купера), привела меня к мысли о том, что типовая схема маршрутизации /controller=Ноme/action=Index/id? не всегда хороша.

Схема взаимодействия

Итак, что из главного должен видеть 1с-одмин, который пользуется консолью? Если отбросить модные дашборды, которые всегда можно прикрутить позже – одмин должен видеть следующее:

  • Какие центральные сервера у него есть
  • Внутри сервера видеть кластеры
  • Внутри кластеров – базы, сеансы и все остальное

Далее, поскольку все администрирование с помощью штатного приложения rac так или иначе требует указания идентификатора кластера, то получается следующая схема маршрутов:

  • Выбор машины с агентом кластера
    • Контроллер agents и его CRUD операции (create/read/update/delete)
    • Типовая схема маршрута контроллер/действие/идентификатор подходит хорошо
  • Выбор кластера внутри агента
    • Пусть будет адрес вида /agents/clusters для списка кластеров
    • Пусть будет адрес /cluster/overview/id. Причем id кластера это GUID, поскольку именно его возвращает утилита rac и именно с этим GUID мы работаем, управляя кластером через нее.
    • Типовая схема тоже подходит худо-бедно
  • Операции с объектами кластера
    • Все CRUD вещи над каждой из сущностей удобнее завернуть в отдельный контроллер для каждой из них. Чтобы были контроллеры /databases /sessions /servers и т.п.
    • Все они потребуют помимо id сущности еще и UUID кластера. Поэтому родился вот такой вариант адреса: /database/create?cluster=UUID или /database/ZUP/sessions?cluster=UUD

Также получилось, что главная страница (которая по умолчанию маршрутизируется в home/index), во-первых, не очень-то и нужна, а во-вторых – должна иметь другой интерфейс, без боковой панели. Боковую панель удобно выводить уже для манипуляций кластером и его объектами.

Главной же страницей, исходя из вышесказанного, должна быть страница контроллера agents.

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

Устройство веб-приложения

Для продолжения нам потребуется свежая версия движка Oscript.Web, которую можно скачать по адресу https://github.com/EvilBeaver/OneScript.Web/releases/tag/v0.2-alpha. Распакуйте архив в удобное место. Желательно, чтобы путь к этому месту был коротким. D:\osweb отлично подойдет.

Откроем Visual Studio Code и в нем откроем папку с последней версией исходников сего учебника. Свежие исходники нужно получить из git (https://github.com/EvilBeaver/osp-articles) операцией git pull. Наш пример кода находится в папке chapter-02.

Начало работы системы

Как и в привычном нам 1С-приложении, выполнение программы начинается с метода ПриНачалеРаботыСистемы, расположенного в модуле приложения.

Модуль приложения - это файл main.os, расположенный в корне каталога исходников src. Мы бегло рассмотрели его в прошлой главе.

Процедура ПриНачалеРаботыСистемы отвечает за настройку веб-приложения. В ней выполняются все необходимые инициализации, относящиеся к приложению в целом. В отличие от 1С, в которой ПриНачалеРаботыСистемы вызывается на клиенте и относится к клиентской сессии, в веб-движке 1script эта процедура вызывается один раз, при старте сервера приложений. Все переменные, объявленные в модуле приложения являются глобальными не для сессии, а для приложения в целом. Все пользовательские сеансы делят между собой состояние, хранимое в глобальных переменных. Поэтому не стоит хранить там данные, которые могут изменяться клиентскими сессиями, во избежание непредсказуемых результатов многопоточного доступа к одним и тем же данным.

Сейчас в этой процедуре находятся 2 вызова:

Процедура ПриНачалеРаботыСистемы()

       ИспользоватьСтатическиеФайлы();
       ИспользоватьМаршруты();

КонецПроцедуры

В модуле приложения доступны так называемые pipeline-методы. Эти методы отвечают за то, как веб-запрос будет проходить по внутренностям веб-приложения. Каждый такой вызов метода отвечает за добавление в конвейер обработки определенной логики. В приведенном примере в конвейер добавляется обработчик статических файлов. Если веб-запрос запрашивает файл, который реально существует на диске, то клиенту будет сразу возвращен этот файл. Если же путь веб-запроса не указывает на реальный файл, то будет переход к следующему методу ИспользоватьМаршруты. И тогда в игру вступит упомянутая ранее маршрутизация и деление на Контроллеры, Действия и прочее.

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

Пример: есть обращение от клиента по адресу «/catalog/books/radchenko.zip» Все, что сервер имеет на входе – это данная строка. Что нужно отдать клиенту? Возможно это прямой путь к файлу на сервере, а возможно это обращение к некоей бизнес логике, которая достанет из базы данных нужный архив и вышлет его клиенту. Как приложение должно реагировать на этот запрос? Методы конвейера как раз и позволяют определить порядок обработки запроса. В нашем случае – сначала будет выполнен поиск файла на диске, а потом (если его не оказалось) попытка передачи запроса в подсистему маршрутизации контроллеров.

Если убрать метод ИспользоватьСтатическиеФайлы(), то файлы с диска вообще не будут обслуживаться. А если убрать метод ИспользоватьМаршруты(), то вообще не будет работать подсистема MVC – только обращение к реальным файлам.

Помимо упомянутых методов в текущей версии существует еще метод ИспользоватьСессии(). Он отвечает за хранение/восстановление пользовательских сеансов на основании заголовков Cookies.

Если хранение состояния сессии в приложении не требуется, то эта функциональность просто не добавляется в конвейер.

Вернемся к Консоли Серверов

Если посмотреть еще раз на принятую схему взаимодействия нашего приложения, то становится ясно, что места для странички Home не находится. Более того, на главной странице есть боковая панель, которую особо нечем наполнить. Наиболее логично оставить в качестве главной страницы список агентов (URL /agents) и убрать боковую панель. А вот когда мы переходим к работе внутри кластера, вот тогда боковая панель появится.

Раскладки

Как мы уже говорили, в разметке сайта используется так называемая «страница раскладки». Каркас главной разметки, который определяет относительное расположение блоков содержания. Напомню, что раскладки хранятся в папке views/shared и начинаются с подчеркивания.

Мы хотим, чтобы страница со списком агентов кластера открывалась без боковой панели (поскольку в ней нечего толком разместить). Создадим рядом с основной раскладкой файл _noSidebarLayout.cshtml следующего содержания:

<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    <link rel="stylesheet" href="/redirect.php?url=aHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvc2VtYW50aWMtdWkvMi4yLjEzL3NlbWFudGljLm1pbi5jc3M="/>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.13/semantic.min.js"></script>
</head>
<body>
    <!-- top menu -->
    <div class="ui inverted huge borderless fluid menu">
      <a class="header item">Odminus</a>
      <div class="right menu">
        <a class="item">Настройки</a>
        <a class="item">Помощь</a>
      </div>
    </div>
    <div class="ui container">
        @RenderBody()
    </div>
</body>

Очень простая разметка, по сути заголовок страницы и место под содержимое, которое будет выведено в месте вызова RenderBody()

Далее, зайдем в файл /views/agents/Item.cshtml и в самом начале файла добавим переопределение раскладки:

@{
    Layout = "_noSidebarLayout"
}

Ту же операцию повторим для файла /views/agents/Index.cshtml

Код внутри скобок – это выражение Razor. Мы остановимся на нем несколько позже. Смысл данного кода таков, что мы не ходим использовать раскладку по-умолчанию, а хотим для данной страницы применять другую. Кстати, если интересно, а как система узнает – какая именно раскладка является «раскладкой по умолчанию» - загляните в файл _ViewStart.cshtml. Можно, кстати, вообще отменить использование раскладок и полностью сверстать страницу с нуля. Тогда нужно свойству Layout присвоить значение null. Пока не стоит сильно вдаваться в детали верстки страниц Razor, я думаю, ему нужно будет посвятить отдельную главу. Кроме того, это далеко не единственный способ верстки фронтенда в современном мире. Razor прост, понятен, он ближе к чистому HTML чем магические JS-фреймворки React/Angular и иже с ними.

Фронтенду для 1С-ников посвящены отдельные научно-исследовательские работы в нашей секретно-подпольной лаборатории «Очумелые ручки». Пока же я полагаю, что Razor выучивается достаточно просто для того, чтобы на него не сильно отвлекаться при изучении нашей темы.

Итак, мы переопределили свойство Layout и назначили новую раскладку.  Теперь, при переходе на адрес /agents мы будем видеть, что расположение контента внутри страницы поменялось и соответствует новой раскладке.

Боковая панель на странице агентов теперь отсутствует, т.к. применена другая раскладка страницы.

Переопределение маршрутов

Как уже говорилось выше, мы пришли к выводу, что страничка home нам пока не нужна. И главной страницей является страница /agents. Однако, схема маршрутизации, которая нами применяется, использует в качестве контроллера по умолчанию контроллер home. Нам нужно объяснить системе, что мы хотим применять нестандартную схему маршрутизации. Большинство веб-приложений имеют несколько разных схем для разных участков приложения. По мере развития приложения нам, скорее всего, придется добавлять новые и новые варианты маршрутизации URL.

Прочтите, пожалуйста, внимательно следующий раздел.

Маршрутизация в подробностях

Система маршрутизации работает благодаря коллекции маршрутов. Все маршруты в совокупности образуют схему маршрутизации приложения – то есть на какие запросы приложение будет откликаться и отдавать результат.

Если вы посмотрите на устройство HTTP-сервисов в 1С, то вы увидите, что там тоже есть система маршрутизации и задание сегментов URL. Здесь будет нечто похожее, только, на мой взгляд, несколько более лаконичное.

Вбивать вручную каждый адрес, который распознает приложение – занятие неблагодарное. Поэтому, обычно задают некий шаблон адреса, который потом сопоставляется с запрошенной страницей. Вот простой пример: http://someserver.com:2541/Admin/Login

Этот URL можно разложить на сегменты: части адреса, которые следуют после имени сервера и порта и разделены символом «/». В данном случае у нас в адресе 2 сегмента: Admin и Login. Схема маршрутизации, применяемая по умолчанию, сооотнесет первый сегмент с именем Контроллера, а второй – с методом Действия в данном Контроллере.

Шаблон URL, соответствующий данному адресу, будет следующим: {controller}/{action}

При обработке входящего HTTP-запроса система маршрутизации сопоставит запрошенный адрес Admin/Login с шаблоном и присвоит переменным сегментов controller и action значения Admin и Login соответственно. Как вы, наверное, догадались, переменные сегментов объявляются с помощью фигурных скобок.

Обычно приложение обходит список имеющихся шаблонов маршрутизации и сопоставляет с каждым из них запрошенный URL. Если не вдаваться в детали, то сопоставление выполняется по совпадению количества сегментов. Любой адрес, состоящий из двух сегментов будет совпадать с нашим шаблоном {controller}/{action}

Адрес /Admin не совпадет – сегментов слишком мало. Адрес /Admin/Login/NewUser тоже не совпадет, сегментов слишком много.

Если же количество сегментов совпадает, то значения сегментных переменных извлекаются из адреса и присваиваются.

Регистрация маршрутов в приложении

Выше была описана функция ИспользоватьМаршруты и ее роль в конвейере обработки HTTP-запроса.

В эту функцию можно передать строкой имя метода-обработчика, который будет выполнять настройку маршрутов.

Почему так сложно? Приложение может вообще не использовать маршруты в конвейере обработки и отдавать, скажем, только файлы с диска. Обработчиков в конвейере может быть достаточно много и текущий вариант, показался наиболее простым способом параметризации шага конвейера. В метод «шага» передается имя обработчика, который может тем или иным образом настроить шаг конвейера.

Продолжим: создадим в модуле приложения (main.os) следующую процедуру:

Процедура ОпределениеМаршрутов(КоллекцияМаршрутов)

КонецПроцедуры

Затем, в процедуре ПриНачалеРаботыСистемы передайте имя «ОпределениеМаршрутов» в качестве строкового параметра метода ИспользоватьМаршруты:

Процедура ПриНачалеРаботыСистемы()
	ИспользоватьСтатическиеФайлы();
	ИспользоватьМаршруты("ОпределениеМаршрутов");
КонецПроцедуры

Параметром обработчика ОпределениеМаршрутов является специальная коллекция «КоллекцияМаршрутов», которая позволяет описать возможные шаблоны URL для нашего приложения.

Например, приведенный маршрут {controller}/{action} регистрируется вот так:

КоллекцияМаршрутов.Добавить("Основной","{controller}/{action}");

 

Значения по умолчанию и необязательные сегменты

Обычно, если не удалось сопоставить URL ни одному из шаблонов в коллекции маршрутов, то клиенту отдается ответ 404 «Страница не найдена».

Поэтому, если зайти просто на адрес нашего сайта, то сегментов после имени сервера не будет. А значит, маршрутизация не найдет маршрута для "нуля" сегментов, и запрос выдаст 404. Разумеется, это неудобно, поэтому, шаблоны маршрутов поддерживают значения по умолчанию. Значение по умолчанию применяется, когда URL не содержит сегмента, но в шаблоне задано значение, которое можно подставить в качестве значения этого сегмента.

На протяжении данной книги я уже упоминал шаблоны, применяющие значения по умолчанию. В частности, если не передать в метод ИспользоватьМаршруты никаких параметров, то будет использован шаблон URL по умолчанию, который выглядит вот так:

{controller=Home}/{action=Index}/{id?}

Значения по умолчанию присвоены внутри объявления сегментных переменных. Если сегмент в URL будет отсутствовать, то система подставит вместо него значение по умолчанию и проведет маршрутизацию. Именно поэтому адреса «/», «/Home» и «/Home/Index» идентичны.

Обратите также внимание на необязательный сегмент id. Необязательный сегмент помечается знаком вопроса. У него нет значения по умолчанию (которое позволяет опустить 3 сегмент), он просто может отсутствовать в запрошенном URL.

Статичные сегменты

Шаблон адреса не обязательно должен содержать только переменные. Допускается указание и статичных строк.

  • /Public/Documents/{controller}/{action=Index} – часть сегментов статична
  • /Library/Page{number}/{user} – второй сегмент включает в себя переменную. Он совпадет с адресом Page1, Page2 и PageАБЫРВАЛГ. Значение number не будет включать в себя префикс Page

Значения по умолчанию вне шаблона

Не всегда возможно указать контроллер и действие внутри шаблона URL. Обратите внимание на последний пример. В нем нет «подсказки» для системы маршрутизации. Она не узнает в какой контроллер отправить запрос на обработку. Для таких случаев применяется 3-й параметр метода «Добавить» в коллекции маршрутов. В качестве 3 параметра можно передать соответствие со значениями по умолчанию для каждой переменной в шаблоне маршрута.

Умолчания = Новый Соответствие;
Умолчания.Вставить("controller","LibArchive");
Умолчания.Вставить("action","Extract");
Умолчания.Вставить("number","1");

КоллекцияМаршрутов.Добавить("Основной",
     "Library/Page{number}/{user}",
      Умолчания);

Особенности обработки маршрутов

Маршруты обрабатываются в порядке из добавления в коллекцию маршрутов. Поэтому, наиболее «общие» шаблоны нужно располагать в конце списка, иначе они «съедят» любой URL и маршрутизация не дойдет до более специфичных маршрутов.

Переменные controller и action единственные «предопределенные» переменные, о которых знает система и использует для маршрутизации. Остальные переменные никак не обрабатываются и просто доступны для вашего кода в коллекции ЗначенияМаршрута при обработке запроса. Вы можете сами решать – что с ними делать.

Помимо «знака вопроса», которым обозначается необязательный сегмент, есть еще один спецсимвол – «звездочка». Переменная, начинающаяся со звездочки «захватывает» вообще все, что встречается в URL в качестве своего значения. Вот шаблон «со звездочкой»:

«{controller=home}/{action=index}/{id?}/{*anything}»

Этот шаблон будет совпадать вообще с любым URL, из любого числа сегментов:

/

controller = home; action = index; id = Неопределено; anything = Неопределено

/agents/delete

controller = agents; action = delete; id = Неопределено; anything = Неопределено

/agents/delete/0001/Blabla/orBloBlo

controller = agents; action = delete; id = 0001; anything = Blabla/orBloBlo

В качестве значения «переменной со звездочкой» захвачен весь «хвост» адреса, что бы в нем ни находилось.

Ограничения на маршруты

Помните пример шаблона /Library/Page{number}/{user}, который совпадал с адресами Page1, Page2 и PageАБЫРВАЛГ?

Чтобы такого не происходило, можно задавать некоторые ограничения на совпадающие значения. В данном случае, мы можем задать, чтобы в качестве number нам передавалось только число:

/Library/Page{number:int}/{user}

Теперь, данный шаблон будет совпадать только с адресами, у которых часть {number} будет являться целым числом.

Вот несколько часто применяемых ограничений:

alpha

Регистронезависимая строка символов

Разрешено ABC и abc, но не разрешено ab123

bool

true и false

 

datetime

Строка, которую можно распарсить в дату-время.

Поддерживается много различных форматов, см. документацию .NET

range(min,max)

Диапазон  значений между min и max (включительно)

 

regex(expr)

Регулярное выражение

URL совпадает, если сегмент совпадает с выражением expr

Остальные варианты ограничений можно найти в документации ASP.NET MVC

Устройство контроллеров

Итак, мы поняли, как работают маршруты. Укажем в методе «ОпределениеМаршрутов», что страницей по-умолчанию у нас является agents.

Процедура ОпределениеМаршрутов(КоллекцияМаршрутов)
	КоллекцияМаршрутов.Добавить("Основной","{controller=agents}/{action=Index}/{id?}");
КонецПроцедуры

Теперь, давайте обратимся к коду контроллера agents и посмотрим подробнее, как он работает:

Методы «Действия»

Как мы помним из предыдущей главы, каждый экспортный метод контроллера является методом «Действия», который вызывается системой маршрутизации. Для контроллера agents это методы Index, Add, Edit и Delete.

Как правило, метод действия – это функция, которая возвращает один из специальных объектов группы РезультатДействия. Результат действия, это объект, который отвечает за формирование ответа, отправляемого клиенту в виде результата HTTP-запроса.

Существует несколько «результатов действия»:

Тип

Вспомогательный метод контроллера

Описание

РезультатДействияСодержимое

Содержимое()

Возвращает строковое тело HTTP ответа: текст, json, xml и т.п.

РезультатДействияФайл

Файл()

Инициирует скачивание файла клиентом

РезультатДействияПеренаправление

Перенаправление()

Инициирует редирект со стороны клиента

РезультатДействияКодСостояния

КодСостояния()

Отправляет на клиент код состояния HTTP. Например, 404

РезультатДействияСтраница

Представление()

Вызывает генерацию «представления (view)» и отправку результата на клиент

В первой колонке таблицы указан тип, который реализует тот или иной результат действия. Все эти типы могут быть созданы через оператор Новый и возвращены из метода-действия.

Однако, в Контроллере существуют вспомогательные методы, упрощающие создание указанных объектов. Эти методы перечислены во второй колонке таблицы.

Разбор на примере

Рассмотрим построчно код метода Add контроллера agents. Напомню, что он вызывается по URL /agents/add с использованием шаблона {controller}/{action}/{id?}

В коде будут даны комментарии по ключевым моментам обработки данного HTTP-запроса.

Функция Add() Экспорт

    // Проверяем, что Add вызван с помощью отправки формы методом HTTP POST
    Если ЗапросHttp.Метод = "POST" Тогда
        
        // Получаем контекст формы из HTTP-запроса
        НовыйID  = ЗапросHttp.ДанныеФормы["Идентификатор"];
        // Получаем сохраненный на сервере список известных центральных серверов
        ТЗ = ЦентральныеСерверы.ПолучитьСписок();
        // Создадим будущую запись таблицы центральных серверов.
        Элемент = ТЗ.Добавить();
        
        // Проверяем, что в форму добавления не ввели повторный идентификатор, 
        // который уже есть в базе.
        Элементы = ТЗ.НайтиСтроки(Новый Структура("Идентификатор", НовыйID));
        Если Элементы.Количество() > 0 Тогда
            // Регистрируем ошибку в специальном объекте СостояниеМодели
            СостояниеМодели.ДобавитьОшибку("Идентификатор","Такой ID уже есть в списке агентов");
            // Метод СохранитьДанные (см. ниже в коде)
            // наполняет Элемент значениями из ДанныхФормы
            СохранитьДанные(Элемент);
            // Отправляем эти данные обратно на клиента.
            // Поскольку это ошибка, то клиент должен увидеть ту же самую форму
            // Которую он заполнил, но с сообщениями из СостояниеМодели.
            // Для создания объекта РезультатДействияСтраница пользуемся вспомогательным методом контроллера Представление()
            Возврат Представление("Item", Элемент);
        КонецЕсли;
        
        // Если ошибки не было
        // наполняем Элемент из ДанныхФормы (метод СохранитьДанные см. ниже в коде)
        СохранитьДанные(Элемент);
        // Записываем на диск измененную таблицу центральных серверов.
        ЦентральныеСерверы.Записать(ТЗ);
        // Формируем объект РезультатДействияПеренаправление с помощью вспомогательного метода контроллера Перенаправление()
        Возврат Перенаправление("/agents/index");
    КонецЕсли;
    
    // А если это не метод POST, значит нам ничего не отправлено, а пользователь только-только запросил страницу /agents/add и собирается ввести данные нового агента.
    // Возвращаем клиенту форму с полями для заполнения.
    Возврат Представление("Item");

КонецФункции

Процедура СохранитьДанные(Знач Элемент)
    
    ДанныеФормы = ЗапросHttp.ДанныеФормы;
    Элемент.Идентификатор = ДанныеФормы["Идентификатор"];
    Элемент.СетевоеИмя = ДанныеФормы["СетевоеИмя"];
    Элемент.Порт = ДанныеФормы["Порт"];
    Элемент.Описание = ДанныеФормы["Описание"];
    Элемент.Режим = "RAS";

КонецПроцедуры

Заключение

На данный момент нет в сети ресурса с полным описанием всех методов и классов, доступных в веб-движке для 1Script. Работа над таким ресурсом ведется и, надеюсь, в ближайшее время он появится хотя бы в виде синтакс-помощника.

В этой главе мы узнали, как стартует веб-приложение, как настраиваются маршруты URL, по какому принципу пишется код контроллера и его методов действия, что такое «результат действия».

В следующей главе мы займемся непосредственно созданием операций администрирования.

  • Подключим библиотеку irac от Артема Кузнецова для программной работы с утилитами администрирования RAC/RAS.
  • Добавим контроллер управления списком кластеров в агенте
  • Расширим схему маршрутизации URL
  • Создадим боковую панель с элементами кластера: информационными базами, администраторами, серверами и т.п.

Оставайтесь на связи, и да пребудет с вами Сила! :)

Специальные предложения

Комментарии
Избранное Подписка Сортировка: Древо развёрнутое
Свернуть все
2. lustin 23.04.18 10:09 Сейчас в теме
Если кому-то интересно построить собственное корпоративное приложение на oscript.web, а не на php - можете обращаться в качестве "ранних адаптаторов". Фактически мы такое делаем и код "почти на 1С".
3. Steelvan 23.04.18 15:12 Сейчас в теме
Как будете решать вопрос отладки ?
Что будет в качестве среды проектирования ?
Будет ли конструктор форм ?
4. Evil Beaver 6705 23.04.18 16:15 Сейчас в теме
(3) Отладка будет, штатная, средствами Oscript Debugger. Среда разработки - VSCode
Конструктор форм пока не предполагается, т.к. речь не только о формах, но и вообще о UI приложения. Когда появятся более серьезные наработки по этой части - будем публиковать.
33. skyadmin 84 16.05.20 20:22 Сейчас в теме
(4) Было бы здорово что-то похожее на EDT увидеть)
5. Steelvan 23.04.18 22:14 Сейчас в теме
Я правильно понял, что весь js код для обозревателя разработчик должен писать самостоятельно ?

Oscript предлагает только серверную часть ?
6. lustin 24.04.18 18:26 Сейчас в теме
(5) Нет - не правильно, просто пока не публикуется в виде статьи. пока только gif'ки



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

P.S. Фоновые задания тут https://github.com/EvilBeaver/OneScript.Web/pull/13
12. Evil Beaver 6705 25.04.18 18:35 Сейчас в теме
(5) В целом да, это серверная часть. Клиентский фронтенд и кодинг интерфейса клиента находятся на стадии научно-исследовательской работы. Доказано - кодить на 1С на клиенте - можно. Есть несколько концепций, есть наработки.
В статье дана оговорка, что сейчас это в рамках секретно-подпольной лаборатории, но рано или поздно - выйдет в свет.
7. Steelvan 25.04.18 10:20 Сейчас в теме
Синтаксис сильно отличается от 1С. Это почти как новый язык учить.

А как будут выглядеть конструкции, типа "Если Тогда" или "Цикл КонецЦикла" ?
В виде "if() {}" и "for(;;)" ?
9. kuntashov 366 25.04.18 13:53 Сейчас в теме
(7) Вы про что? Синтаксис 1Script полностью совместим с 1С, вы куда-то не туда посмотрели.
10. Steelvan 25.04.18 14:22 Сейчас в теме
(9) Вы про 1Script который на сервере, или в обозревателе ?

Мы про обозреватель переписываемся :)
11. kuntashov 366 25.04.18 18:21 Сейчас в теме
(10) Нет пока 1Скрипта в обозревателе, даже не понятно, где вы его увидеть смогли.

На скриншоте Алексея? Посмотрите внимательнее на скриншот: там последней строчкой "СкомпилироватьСНастройками()", это означает, что методы, которые вы видите выше просто генерируют HTML, но происходит это на сервере.

Т.е. это просто библиотека серверная, которая генерирует HTML-код веб-компонент или просто html-код.

Теоретически, на клиенте 1Скрипт можно сделать после выхода blazor'а.
13. Evil Beaver 6705 25.04.18 18:37 Сейчас в теме
(11) или сделать blazor, или сделать babel но для 1С - вариантов набирается на десяток, пробуем на вкус и оцениваем.
16. Evil Beaver 6705 25.04.18 18:55 Сейчас в теме
(11) Да, происходит генерация на сервере, но не статики. Генерируется в том числе и "реактивный" UI, откликающийся на изменение свойств связанных компонентов. И все это на языке 1С. Но да, это целый получается фреймворк, который надо осваивать. В этом пока главная проблема - упростить до привычных конструкций, не превратив это в жесткий "Управляемый интерфейс".
17. kuntashov 366 25.04.18 22:23 Сейчас в теме
(16) Я ничего не писал про статику, я написал "HTML-код веб-компонент", но подразумевал "код веб-компонент", т.к. какая разница, что на самом деле генерировать, "HTML" по инерции добавил )
18. Evil Beaver 6705 25.04.18 23:02 Сейчас в теме
(17) я просто счел нужным уточнить немного :)
19. lustin 01.05.18 16:41 Сейчас в теме
(17) Ты полностью прав. Пока я реализую обвязку - то есть классы и модули и функции поведения. Также выделил - темы и стили. Движок в рельности строит ASG на языке clojure, после чего с помощью lein сервера копилирует в минифифированный js (React.js). и как ты верно заметил я с очень большим интересом подсматриваю сюда https://github.com/aspnet/Blazor/tree/dev/samples/MonoSanity
kuntashov; +1 Ответить
15. Evil Beaver 6705 25.04.18 18:53 Сейчас в теме
(7) В идеале - Если Тогда, но сейчас этого нет.
8. Steelvan 25.04.18 13:04 Сейчас в теме
И пара вопросов в догонку:
*) С какими типами будет работать разработчик: "Массив", "Число", "ТаблицаЗначений" или "Array", "Number" и "НетТаблицыВjs" ?
*) Нужно будет везде использовать "=" и система сама поймет, или "==" и ""===" ломая все привычки написания кода 1Сника ?
14. Evil Beaver 6705 25.04.18 18:51 Сейчас в теме
(8) Вы про код внутри view или про серверную логику?

Вся серверная логика - на 1С. Все привычные типы, таблицы значений и прочее - есть. Даже регламентные задания есть.
Что касается кода View, то движок представлений в принципе - вещь заменяемая. Один вытащил, другой вставил. Сейчас предлагается родной для ASP движок Razor. Это HTML плюс короткие вставки на C#. Самое главное, что C# учить не надо. Достаточно выучить простейшие конструкции @if и @foreach, поскольку все, что делает View - это отображает на экране переданную из кода 1С "Модель". Моделью может выступать любой объект - Массив, Структура, ТаблицаЗначений. И в коде Razor к ним можно обращаться привычным образом, через точку получая свойства и вызывая методы 1С.

@foreach(var КлючИЗначение in @Model.СтруктураЧегоНибудь){
    <td>Ключ        = @КлючИЗначение.Ключ</td>
    <td>Значение = @КлючИЗначение.Значение</td>
}


Однако, Razor - достаточно низкоуровневая штука, т.е. лежит очень близко к чистому HTML. Гораздо важнее уметь верстать HTML, а синтаксис Razor где-то на десятом месте. И это тоже 1С-нику непривычно.

На рынке существуют фронтенд-фреймворки React, Vue, Angular и прочие. Как-правило, современный UI пишется на них. Но это еще более непривычные 1С-нику штуки.

Итого имеем:

* Бизнес-логика - вся на 1С и привычна 1С-нику. Массив, Число, ТаблицаЗначений
* Разметка страниц, клиентская логика - на данный момент нет. Зато есть разнообразие готовых не-1С-инструментов, можно взять любой и выучить.
* Встроенный шаблонизатор Razor прост сам по себе, но требует умения верстать хотя бы чуть-чуть (2-3 часа в гугле, semantic, material, bootstrap).
* Рано или поздно, появятся готовые решения, позволяющие строить UI в привычной 1С-нику манере
* Есть вероятность, что в результате получится "Управляемый интерфейс", разметка которого прибита гвоздями и поменять ее сложно.
* Компромисс между свободой верстки и простотой создания UI всегда будет существовать, независимо от технологии
kote; kuntashov; +2 Ответить
22. pavelyar 02.06.18 18:00 Сейчас в теме
23. Evil Beaver 6705 03.06.18 20:23 Сейчас в теме
26. vandalsvq 1147 18.10.18 18:37 Сейчас в теме
(0) прям заразили на конференции этой темой. Хочется попробовать и присоединиться к сообществу. В какой роли жизнь покажет, но надеюсь (привет тебе, Алексей) не в качестве хитрого башкира. Идея оказалась на удивление близка.
Evil Beaver; +1 Ответить
29. Evil Beaver 6705 26.10.18 13:37 Сейчас в теме
(26) Ща придет хитрый башкир и начнет спрашивать, при чем тут он ))
27. dv.kurmanov 21.10.18 18:45 Сейчас в теме
28. Evil Beaver 6705 22.10.18 18:51 Сейчас в теме
ИспользоватьАвторизацию() в main.os

Смотрите каталог examples в репо проекта
30. Vo-Va 163 13.11.18 12:22 Сейчас в теме
Продолжение будет или на этом все?
31. Evil Beaver 6705 14.11.18 12:12 Сейчас в теме
(30) Будет. Просто надо придумать, что дальше писать.
32. Vo-Va 163 14.11.18 12:43 Сейчас в теме
(31) как вариант про отладку и подробнее про Razor. Это то, что останавливает меня в начинании написать что-то свое. Ну это с точки зрения прожжённого одинэсника, который кроме 1с ничего не знает)
Оставьте свое сообщение

См. также

3 онлайн-курса по 1С-программированию: обмен данными, расчетные задачи и бухгалтерские задачи с 12 мая по 8 июля 2020 г. Промо

Практика программирования v8 Бесплатно (free)

Пакет из 3-х курсов по 1С-программированию. Основная цель - сформировать у слушателей практические навыки, связанные с реализацией задач обмена для прикладных решений, работающих на платформе “1С:Предприятие”, а также с разработкой прикладных решений, предназначенных для автоматизации расчета заработной платы и задач бухгалтерского учета.

22.04.2020    3194    23    infostart    2    

Как разработать Web приложение и остаться 1С-ником

WEB Бесплатно (free)

Создание современных веб-приложений обходится для бизнеса дорого и требует постоянной актуализации различных фреймворков, что не всегда оправданно. Как применить инженерный подход и предоставить бизнес-пользователям доступ к данным 1С в удобном и защищенном веб-приложении на конференции Infostart Event 2019 Inception рассказал руководитель управления ИТ компании WiseAdvice Олег Филиппов.

18.05.2020    8481    0    comol    85    

JSON в запросах 1C#

Практика программирования Бесплатно (free)

Практические примеры работы с JSON непосредственно в языке запросов. Перенос курсов валют между УТ и БП. Требуется SQL Server 2016 и выше.

24.04.2020    2585    0    zhichkin    6    

Визионное программирование

Практика программирования Бесплатно (free)

Новый способ программирования и его практическая демонстрация.

22.04.2020    3373    0    mkalimulin    60    

Использование программных перечислений, ч.1: строковые константы Промо

Практика программирования v8 1cv8.cf Бесплатно (free)

Часто ли у вас возникает необходимость в коде выполнять сравнение на строку?

10.12.2016    34173    0    unichkin    45    

Как зайти на http://lkul.nalog.ru c VipNet

WEB Россия Бесплатно (free)

Для тех, кто не может пройти последний пункт "Проверка защищённого соединения с сервером Личного кабинета юридического лица" на сайте http://lkul.nalog.ru/check_cryptopro.php с применением VipNet.

19.04.2020    1489    0    Voblhned    0    

nsq - еще один менеджер очередей

WEB Бесплатно (free)

В статье будет описан процесс запуска nsq. Данный mq достаточно прост в использовании по сравнению с другими. Самое главное для меня в этом менеджере, это возможность отправить данные так: http://127.0.0.1:4151/pub?topic=test-messages - вполне достаточно когда 1С выступает поставщиком данных.

05.03.2020    1802    0    pallid    9    

Использование машинного обучения для решения инцидентов. Практическое применение

Практика программирования Бесплатно (free)

Продолжаю (и заканчиваю) тему с автоматическим решением инцидентов. Перейдем от теории к практике.

25.02.2020    3169    0    Repich    9    

Вспомогательные инструкции в коде 1С Промо

Практика программирования v8 1cv8.cf Бесплатно (free)

Помогаем редактору кода 1С помогать нам писать и анализировать код.

15.10.2018    26148    0    tormozit    100    

Использование машинного обучения для решения инцидентов

Практика программирования Бесплатно (free)

История о том, как я с помощью машинного обучения сокращал затраты на техподдержку.

18.02.2020    4782    0    Repich    17    

Часовой на страже логов

Практика программирования Инструментарий разработчика Бесплатно (free)

При поддержке решений, которые установлены у большого количества пользователей на различных системах, очень важно вовремя получать подробную информацию о возникших проблемах. О том, как собирать логи и анализировать полученные данные в трекере ошибок Sentry на конференции Infostart Event 2019 Inception рассказал Андрей Крапивин.

13.01.2020    4852    0    Scorpion4eg    6    

Проброс IP-адреса клиента в http-сервис 1С. Реализация для IIS

WEB Администрирование веб-серверов IIS Бесплатно (free)

Настраиваем веб-сервер IIS для передачи в 1С IP-адреса клиента, вызвавшего http-сервис. Разбираемся с этим же вопросом при использовании фронтэнд вебсервера на примере nginx.

01.12.2019    4879    0    -vito-    6    

Оформление и рефакторинг сложных логических выражений Промо

Практика программирования v8 Россия Бесплатно (free)

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

20.09.2012    73670    0    tormozit    129    

Swagger для 1С.

OneScript WEB Бесплатно (free)

Решение для формирования Swagger спецификаций, описывающих HTTP сервисы конфигураций 1С.

21.10.2019    10114    0    botokash    35    

Приватный блокчейн и 1С популярно

Практика программирования Блокчейн Бесплатно (free)

Две предыдущие публикации на эту тему были сфокусированы преимущественно на технической стороне вопроса. Кроме того, их содержание оказалось понятным не каждому специалисту. В этой статье я постараюсь обяснить для всех и, что говорится, «на пальцах»: что такое приватный блокчейн, когда и зачем его следует применять и на что обратить внимание при использовании этой технологии в 1С.

02.09.2019    4914    0    mkalimulin    140    

Кодогенерация и метагенерация в 1С

Практика программирования Инструментарий разработчика Бесплатно (free)

В своем докладе на конференции INFOSTART EVENT 2018 EDUCATION Дмитрий Белозеров рассказал о разработке инструмента, позволяющего программно работать с метаданными 1С и писать скрипты для выполнения тех же действий, которые выполняет разработчик в конфигураторе –  с какими сложностями и нюансами пришлось столкнуться, и что получилось в итоге.

26.08.2019    7752    0    kirovsbis    28    

Запись значения в поле ввода/формы со срабатыванием события ПриИзменении Промо

Практика программирования v8 1cv8.cf Россия Бесплатно (free)

Иногда возникает необходимость после записи значения в какое либо поле ввода/формы вызвать для него обработчик события ПриИзменении, а о вызове самого события приходится только мечтать. В этой статье приводится программный способ вызова этого события.

11.07.2007    44763    0    tormozit    38    

Интеграция сценарного тестирования в процесс разработки

Практика программирования Инструментарий разработчика Бесплатно (free)

Разработчик системы «Тестер» Дмитрий Решитко в своем докладе на конференции INFOSTART EVENT 2018 EDUCATION показывает, что процесс тестирования можно очень плотно интегрировать в процесс разработки, что внедрение тестирования – это возможность развития программиста как такового, позволяющая ему упорядочивать ход мыслей и оставаться «в фокусе». Навыки построения процесса кодирования на стыке с тестированием сокращают время на концентрацию, освобождают от страха перед изменениями и улучшают память разработчика.

08.07.2019    8093    0    grumagargler    7    

Управляй качеством кода 1С с помощью SonarQube

Практика программирования Россия Бесплатно (free)

Управляй техническом долгом проектов 1С с помощью SonarQube. В статье рассматривается пример применения SonarQube при разработке.

07.07.2019    32916    0    olegtymko    229    

Обновляем картинки товаров по штрихкоду с помощью мобильного приложения "Штрихконь" (Пошаговое руководство)

Внешние источники данных WEB Бесплатно (free)

Владельцы интернет магазинов знают, что привлекательные картинки – главный способ привлечения покупателей, но найти в интернете красивую картинку, которая полностью соответствовала бы реальному товару очень сложно. Проще сделать фотографию на мобильный телефон и сразу загрузить её в базу 1С, а с помощью автоматического обмена обновить картинки на сайте.

10.04.2019    5438    0    inord777    2    

Как сделать из &НаКлиентеНаСервереБезКонтекста почти &НаКлиентеНаСервере Промо

Практика программирования v8 1cv8.cf Россия Бесплатно (free)

Как сделать метод формы, доступный на клиенте и на сервере одновременно, и сохранить при этом удобство разработки

10.09.2017    40518    0    tormozit    72    

О времени и 1С

Практика программирования Разработка Бесплатно (free)

Основы и особенности работы со временем в 1С. Как избавиться от боли при работе в разных часовых поясах. Что такое момент времени. И другое.

01.04.2019    28857    0    YPermitin    60    

Решение проблемы связи с сайтом, использующим протокол TLS 1.2

WEB Бесплатно (free)

Последнее время все чаще ко мне обращаются клиенты, у которых возникли проблемы при обмене с сайтом. Текст ошибки, как правило, имеет вид: - Ошибка инициализации SSL-соединения - Соединение с сервером было неожиданно прервано

19.03.2019    8565    0    zsrg    0    

Развитие 1С программиста Промо

Практика программирования Личная эффективность Бесплатно (free)

Делюсь своим опытом и видением развития 1С программиста.

17.10.2018    18214    0    pashamak    62    

Пример создания bridge (http api - tcp) для ККТ "Касса №1" ("К1-Ф")

Практика программирования ККМ Кассовые операции Кассовые операции Разработка Россия Бесплатно (free)

Пример создания bridge (http api - tcp) для ККТ "Касса №1" ("К1-Ф"). Данная статья будет полезна интеграторам, программистам, тем кто работает (интегрирует, разрабатывает) различное ТО либо железки. Версия и релиз технологической платформы не имеет значения.

17.03.2019    5571    0    dmarenin    1    

Быстрее чем INSERT! BULK-операции и примеры использования

Производительность и оптимизация (HighLoad) Практика программирования Внешние источники данных Перенос данных из 1C8 в 1C8 Разработка Бесплатно (free)

Microsoft SQL Server поддерживает так называемые BULK-операции, используемые для быстрого изменения больших объемов данных в базе. В статье пойдет речь о практических примерах их использования. Все примеры сделаны в контексте платформы 1С (а как иначе).

09.03.2019    19956    0    YPermitin    40    

Как писать понятные коммиты

Практика программирования Разработка Россия Бесплатно (free)

Как писать сообщения коммитов так, чтобы потом не было мучительно больно.

06.03.2019    11555    0    Scorpion4eg    35    

Выгрузка документа по условию Промо

Практика программирования Разработка v8 Бесплатно (free)

Что делать, если документы нужно выгружать не все подряд, а по какому-то фильтру: статусу, дате, набору условий... А что если он соответствовал этим условиям, а потом перестал? А если потом опять начал? Такие ситуации заставили попотеть не одного программиста.

25.04.2019    14161    0    m-rv    2    

Подготовка ребёнка к ЕГЭ по информатике. Часть шестнадцатая

Практика программирования Разработка Бесплатно (free)

Поиск выигрышной стратегии, завершающая статья.

22.02.2019    4863    0    vasilev2015    0    

Как прикрутить ГУИД к регистру сведений Промо

Практика программирования Перенос данных из 1C8 в 1C8 Разработка v8 Бесплатно (free)

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

16.04.2019    17768    0    m-rv    17    

Подготовка ребёнка к ЕГЭ по информатике. Часть тринадцатая

Практика программирования Разработка Бесплатно (free)

Исправление ошибок в программе, часть вторая.

20.02.2019    5087    0    vasilev2015    3    

Как сделать запрос на изменение данных Промо

Практика программирования v8 v8::Запросы 1cv8.cf Бесплатно (free)

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

01.06.2018    27423    0    m-rv    21    

Метод формирования движений в типовых регистрах нетиповыми регистраторами Промо

Практика программирования v8 1cv8.cf Бесплатно (free)

Вариант решения задач с проведением по типовым регистрам нетиповыми регистраторами. Зачем - чтобы при сравнении конфигурации не обращать внимание на свойства регистров и исключить вероятность допущения горькой оплошности при обновлении информационных баз, заменив типы регистраторов основной конфигурации типами конфигурации поставщика. Для программных продуктов, имеющих в своем составе метаданных документ "Корректировка регистров"("Корректировка записей регистров").

05.12.2017    25720    0    itriot11    34    

Подготовка ребёнка к ЕГЭ по информатике. Часть восьмая

Практика программирования Разработка Бесплатно (free)

Шифрование и дешифрование информации. Закон Фано

05.02.2019    4797    0    vasilev2015    1    

Расширяем свой багаж

Практика программирования Разработка Бесплатно (free)

Алгоритм решения возможной нетиповой задачи на собеседовании.

29.01.2019    5516    0    scientes    15    

Использование классов .Net в 1С для новичков Промо

Практика программирования Разработка внешних компонент Универсальные функции v7.7 v8 Бесплатно (free)

Руководство для новичков. Написав статью http://infostart.ru/public/238584/, я понял, что многие не понимают того, что написано. Поэтому в этой статье постараюсь более подробно остановиться на азах и без кода на вражеском языке (C#)

27.01.2016    71049    0    Serginio    108    

Подготовка ребёнка* к ЕГЭ по информатике. Часть пятая

Практика программирования Разработка Бесплатно (free)

Маршрутизация. Протокол IPv4 для ЕГЭ.

27.01.2019    5016    0    vasilev2015    5    

Подготовка ребёнка* к ЕГЭ по информатике. Часть четвертая

Практика программирования Разработка Бесплатно (free)

Решение систем логических уравнений повышенного уровня сложности.

25.01.2019    5218    0    vasilev2015    0    

Автоматические и управляемые блокировки применительно к типовым конфигурациям 1С Промо

Математика и алгоритмы Практика программирования v8 v8::blocking 1cv8.cf Бесплатно (free)

Основные принципы работы с режимами автоматических и управляемых блокировок в 1С Предприятие 8. Теория и применение в типовых конфигурациях: БП, УТ, ЕРП

10.11.2018    30599    0    ids79    40    

Подготовка ребенка* к ЕГЭ по информатике. Часть третья

Практика программирования Разработка Бесплатно (free)

Алгоритмы рекурсии, логические задачи. Подготовка к ЕГЭ.

22.01.2019    6630    0    vasilev2015    0    

Разработка и сценарное тестирование с Vanessa-ADD. Установка инструментов. Запись действий пользователя и выполнение сценариев

Практика программирования Vanessa Automation Бесплатно (free)

Вторая часть цикла публикаций, посвященных Vanessa-ADD и автоматизации тестирования.

21.01.2019    27973    0    Vladimir Litvinenko    96    

Подготовка ребенка* к ЕГЭ по информатике. Часть вторая

Практика программирования Бесплатно (free)

Примеры на Паскале. Если сам родитель* - поддержи ! Если сам водила - посигналь !

19.01.2019    5232    0    vasilev2015    0    

Подготовка к ЕГЭ сына - школьника (по информатике)

Практика программирования Бесплатно (free)

Примеры на Паскале. Если сам отец - поддержи ! Если сам водила - посигналь !

17.01.2019    5869    0    vasilev2015    50