25 * Система пока не поставляется в сборке docker-compose вместе с настроенным http сервером и БД. На время пришлось отказаться от этой идеи, но в будущем она есть и будет реализована.
35 * Разверните проект в своей структуре. Переменная <code>$_SERVER['DOCUMENT_ROOT']</code> должна указывать на каталог public_html/ именно он будет доступен по http, а
36 * всё что находится выше данного каталога по http недоступно.
37 *
38 * Если ваша структура не имеет каталогов public_html/ или структура проекта будет мешаться с другой,
39 * вы должны настроить свой nginx/apache2 сервер соответсвующим образом под данную структуру.
40 *
41 *
42 * @attention
43 * Меняя структуру своего DOCUMENT_ROOT в настройках nginx/apache2 учитывайте, что вам может потребоваться перенастроить в конфигах Web сервера также <a target="_blank" href="https://www.php.net/manual/ru/ini.core.php#ini.open-basedir"><b>open_basedir</b></a> директиву, для того
44 * чтобы php обрабатывал файлы на уровень выше каталога public_html/ вашего проекта, иначе включаемые файлы и классы не смогут быть выполнены и вы не запустите даже установщик. Поскольку open_basedir может быть ограничена у вас именно настройками apache2/nginx. Но скорее всего вам придётся наоборот "опускать" структуру на уровень ниже, создав дополнительный public_html каталог. Например в типичной isp manager конфигурации каталогов /var/www/username/data/www/example.com/{доступ по http} вам может понадобится поправить web конфиги на такую структуру /var/www/username/data/www/example.com/public_html/{доступ по http} чтобы не мешать файлы проекта с другими доменами пользователя username
45 *
46 * Весь необходимый роутинг реализован в uri-router.php Для apache .htaccess файл есть в архиве, а если у вас nginx то достаточно прописать минимальные реврайт директивы
106 * изменения в scss прослушиваются и тут же применяются без перезагрузки, верстайте в своё удовольствие и на свой вкус - всё с системой вы делаете на свой страх и риск 😉
107 *
108 * @attention
109 * Учитывайте что при сборке очищаются следующие каталоги указанные в gulpfile.mjs
110 *
111 * @code
112 * /public_html/assets/system/css/
113 * /public_html/assets/system/js/
114 * /public_html/assets/system/img/
115 * @endcode
116 *
117 * В указанные каталоги командой сборки автоматически собираются файлы из каталогов
126 * На данном примере показаны логические каталоги <b>assets/system</b> для системного фронтенда, любые свои фронтенд сборки для сайтов-проектов вы можете
127 * собирать в другой каталог, и это будет логично <b>assets/my-frontend-dirname</b>. Для этого поправьте пути в <b>gulpfile.mjs</b>
128 *
129 * Вы можете собирать любые свои кастомизированные бустраповские фронтенд css/js сборки для ваших проектов работающих вне админ панели.
130 * Картинки при сборке сжимаются, файлы минифицируются, у минифицированных версий удаляются комментарии.
131 *
132 * @note
133 * Для динамического вызова модальных окон и действий из бекенда по триггерам, вы должны включить в свою сборку <b>app/js/main.js</b>
134 * Там нет лишнего функционала админ панели и модулей, всё это специально вынесено в отдельные js файлы.
135 *
136 * <b>app/js/main.js</b> импортирует в единую сборку только jQuery v3.6.0, js модули Bootstrap v4.6.1 и js файл взаимодействия с бекендом по триггерам - <b>app/js/import/wrong.js</b>
137 *
138 * Все остальные библиотеки, js, css вы подключаете по мере необходимости для вашего конкретного проекта и задач.
139 * Ваша локальная среда для сборки фронтенда - это каталог <b>app/</b> и команды gulp.
140 *
141 * В разделе <a target="_blank" href="/docs/layout.html"><b>шаблонизация, своя вёрстка, стили</b></a> подробно показаны способы подключения
142 * стилей и скриптов в шаблонах ваших страниц, таких способов несколько.
143 */
144
145
146
147
148
149
150
151 /**
152 *
153 * @page settings Настройки, переменные среды
154 *
155 * Чтобы увидеть все переменные среды приложения вызовите
156 * @code
157 * dd(Wrong\Start\Env::$e);
158 * @endcode
159 * Есть хранимые переменные среды, которые сохраняются при установке в файле .env а также есть хранимые в бд переменные.
160 * И есть динамические переменные среды, к которым относятся ip пользователя и его CSRF токен
161 *
162 * Хост, ip сервера и порт автоматически обновляются скриптом в .env в случае их изменения. К хранимым переменным также относятся переменные настроек системы,
163 * которые вы задаёте в админ панели, данные переменные подтягиваются из бд, после её подключения. Прочие переменные добавляются динамически.
164 *
165 * Установка дополнительных переменных среды происходит в файле include/session.php
166 *
167 * За переменные среды отвечает класс Start/Env.php
168 *
169 * В окне настроек всё интуитивно понятно, и вряд ли требует дополнительных пояснений. Обратите внимание, основной администратор системы, состоит в группе <b>Система</b>,
170 * но при этом данная группа не является подчинённой ему, она не подчинена никому.
171 * Это исключение, в остальных случаях, группы в которых вы состоите являются вам подчиненными.
172 * Поэтому при включении настройки "отображать только модели подчиненных групп", все группы принадлежащие группе Система будут скрыты у всех.
190 * <b>Иерархия прав доступа и действий над моделями</b> в системе реализована посредством групп и пользователей. Пользователь может состоять одновременно в различных группах.
191 * У каждой группы есть <a href="/docs/weight.html" target="_blank" rel="noopener noreferrer"><b>системный вес</b></a>, определяющий приоритет прав одной группы над другой. Если пользователь состоит в группах с различным весом, при расчете его приоритета
192 * над другим пользователем, либо имуществом другой группы(моделями), рассчитывается максимальный вес группы в которых он состоит.
193 *
194 * <b>Доступность модели(чтение)</b> пользователю оределяется наличием группы в которых состоит пользователь в группах доступа модели, либо же одна из групп в которых состоит пользователь является владельцем модели. Модели отключенных групп недоступны.
195 *
196 * <b>Возможность действий над моделью(запись)</b> определяется принадлежностью пользователя к группе владельцу модели или приоритетом
197 * максимального системного веса групп пользователя над группой владельцем модели. При этом функционал для данных действий должен быть тоже доступен.
198 *
199 * Есть 2 особых группы пользователей, неявная группа <b>Гости</b> - условно id 0, это неавторизованные пользователи. Данной группы нет в таблице групп, поскольку она бесправная(системный вес = 0) и для неё настраивать нечего, но она есть в системе.
200 * А также есть особая группа <b>Система</b> id 1, с максимальным весом, в которой состоит основной администратор системы и которой принадлежат все системные модели.
201 *
202 * <b>Моделями</b> в системе является всё что имеет группу владельца, это:
203 * - Группы пользователей
204 * - Пользователи
205 * - Шаблоны
206 * - Страницы
207 * - Выборки
208 * - Модальные окна
209 * - Действия
210 * - Cron задачи
211 *
212 * У моделей типа <b>Страницы, Выборки, Модальные окна, Действия</b> помимо группы владельца, есть группы доступа - это группы которым такая модель доступна по http через контроллер include/uri-router.php
213 * т.е. пользователи состоящие в указанных группах доступа модели могут её вызывать. Но они не могут произоводить действия надо этой моделью, включать/отключать её, изменять её группы, владельцев, переименовывать.
214 *
215 * Для модели типа <b>Шаблоны</b> группы доступа - это те группы, которым данный шаблон будет доступен при создании моделей, а также использование и встраивание таких шаблонов.
216 *
217 * Для модели типа <b>Пользователи</b> группы доступа - это те группы в которых состоит пользователь.
218 *
219 * Для группы владельца модели его модель всегда доступна, даже если она отключена.
220 *
221 * Модели принадлежащие особой группе <b>Система</b> защищены от удаления и переименования, основному администратору системы(id 1) доступна лишь часть действий над ними,
222 * несмотря на то, что администатор сам состоит в группе <b>Система</b>.
223 * Он может лишь включать/отключать системные модели, копировать, экспортировать, импортировать их, и менять у них группы доступа.
224 * Это исключение из общей логики работы с моделями владельцами которых вы являетесь.
225 * @attention
226 * Группы доступа у системных моделей следует менять с осторожностью, а также с осторожностью отключать их. Например, если вы отключите функционал отвечающий за авторизацию,
227 * то не сможете войти в систему, и вам придётся включать его через бд - есть поле act у каждой модели, отвечающее за это. Впрочем, перед каждым подобным действием, вам
228 * будет показано предупреждение.
229 *
230 * <b>Владельцы моделей</b> - это группа владелец которым принадлежит модель.
231 * Владелец может делать со своей группой всё что угодно - удалять, или менять любые её свойства - переназначать группу владельца(из числа подчиненных групп), изменять для модели группы доступа,
232 * переименовывать файлы обработчики(они автоматически будут перемещены в фс и созданы каталоги под них),
233 * переименовывать request http запросы, по которым доступна модель, включать и отключать её.
234 *
235 * <b>Подчиненные группы</b> - это группы в которых состоит пользователь и группы с меньшим системным весом, чем максимальный вес групп в которых состоит пользователь.
236 * С моделями, принадлежащими подчиненным группам, пользователь также может делать всё что угодно, если у него включен и доступен соответствующий функционал моделей для этого.
237 *
238 * Например действие удаление - api/action/system/global/rm.php(модель /api/action/rm) наша группа пользователя должна быть включена в группы доступа у модели rm,
239 * если мы хотим удалить какую либо модель подчиненной или своей группы.
240 *
241 * Для группы может быть назначен лимит моделей, более которого ей не может быть создано/назначено/копировано/импортировано принадлежащих моделей. По умолчанию лимит - 0, это безлимит.
242 *
243 * При добавлении новой группы, чтобы не перебирать затем каждую модель, её можно автоматически включить в группы доступа всех моделей у которых назначен доступ для "всех".
244 * А также новую группу можно автоматически включить в доступы всем моделям доступным самому владельцу новой группы.
245 *
246 * Если <b>отключить группу</b>, скрипт ведет себя так, будто этой группы у пользователя не существует. Если <b>отключить пользователя</b>, он во всех моделях получает ошибку(страницу, если эта страница) 403 - доступ запрещен.
247 *
248 * <b>Включенные модели</b> доступны включенным группам доступа пользователей и группе владельцу.
249 *
250 * <b>Отключенные модели</b> доступны только группе владельцу.
251 *
252 * <b>Выключение модели</b> делает её недоступной всем кроме группы владельца.
253 *
254 * <b>Модели выключенных групп</b> недоступны на чтение.
255 *
256 * <b>Выключение группы</b> пользователей исключает её из групп пользователя(временно, виртуально), т.е пользователь просто лишается всех прав выключенной группы.
257 *
258 * <b>Выключение пользователя</b> отключает ему весь функционал на всех моделях. Доступна становится лишь одна страница - 403.
259 *
260 * <b>Изменять свойства модели</b> могут группы владельцы и старшие по системному весу группы(имеющие доступ к функционалу изменения этих свойств).
261 */
262
263
264
265
266
267
268
269
270 /**
271 * @page weight Системный вес
272 *
273 * <b>Системный вес группы</b> определяет приоритеты(права действий) одних групп пользователей над другими в системе. Пользователь может состоять одновременно в разных группах
274 * с различным весом, при этом берется максимальный вес этих групп при расчете приоритетов над другим пользователем(его моделью).
275 *
276 * <b>Системный вес пользователя</b> - максимальный вес из активных(включенных) групп в которых он находится.
277 *
278 * <b>Подчиненными группами</b> пользователя являются все группы в которых он состоит, и группы с меньшим весом чем максимальный вес тех групп, в которых состоит пользователь.
279 * Исключение - группа <b>Система</b>, данная группа не подчинена никому, и группа <b>Гости</b>. Модели группы <b>Система</b> защищены таким образом, а группа <b>Гости</b> не может являться владельцем
280 * каких либо моделей, и может находиться лишь в группах доступа.
281 *
282 * Над моделями подчиненных групп можно производить любые доступные действия. Владельцами моделей можно назначать только подчиненные группы.
283 *
284 * Например: в группе <b>Demo</b> администраторы, включены и работают практически все модели-действия, включая удаление, изменение, создание моделей.
285 * Но при создании/изменении модели от группы <b>Demo</b> вы не сможете назначить ей владельца <b>Администраторы</b>.
286 * Вы не можете удалять модели принадлежащие <b>Администраторам</b>. Вы не можете изменять их любые свойства. Всё это вы можете делать только над моделями подчиненных
294 * @page access Проверка прав доступов пользователя
295 *
296 * В методе access() класса User.php собирается класс Access.php реализующий программную проверку прав доступа пользователя к моделям.
297 * Как уже сказано в разделе <a target="_blank" href="/docs/groups_users.html"><b>группы и пользователи</b></a> таких типов доступов всего 2:
298 *
299 * - <b>Доступность модели(чтение)</b>
300 * - <b>Возможность действий над моделью(запись)</b>
301 *
302 * Модели можно проверять на чтение и запись передавая в метод проверки аргумент $row - объект строки модели. На чтение(доступность) модели можно проверять
303 * по аргументам её request запроса или по её id
333 * Для того чтобы зафиксировать действия при разборе "полётов" воспользуйтесь <a target="_blank" href="/docs/logs.html"><b>записью логов</b></a>, там можно будет найти,
334 * кто когда куда входил, с каким ip и что делал.
335 *
336 * @note
337 * В таблице моделей, у пользователей, которые не входят в подчиненные группы скрыты звездочками конфиденциальные данные такие как ip, X-Auth-Token, email
353 * Предусмотрено добавление практически неограниченного числа групп, с любой иерархией и системным весом. У каждой группы, как и у любой модели есть группа владелец.
354 * Вы можете назначать любого владельца из числа <a target="_blank" href="/docs/weight.html"><b>подчиненных вам по системному весу групп</b></a>.
355 * И устанавливать группе любой системный вес меньше вашего собственного <a target="_blank" href="/docs/weight.html"><b>максимального системного веса</b></a>.
356 *
357 * Группам назначается лимит моделей(0 - безлимит) и каталог группы по умолчанию в файловой системе при создании файлов обработчиков моделей. При удалении группы или
358 * её очистке от моделей удаляются все файлы обработчики и модели принадлжежащие данной группе. Если файл обработчик указан так же в моделях других владельцев - такой файл удалён не будет.
359 *
360 * Подчинённые вам группы можно отключать. Если группа отключена и пользователь состоит в данной группе, система ведет себя так будто данной группы не существует, т.е. просто
361 * игнорирует наличие пользователя в группе. При этом все модели становятся недоступными на чтение.
362 *
363 * Пользователям групп можно массово добавлять другие группы и массово исключать пользователей из групп. Посредством групп вы можете
364 * <a target="_blank" href="/docs/related_functionality.html"><b>управлять связанным функционалом</b></a> целиком, а не только по отдельным компонентам(моделям).
365 *
366 * Для групп включается и отключается запись логов действий.
372 * При добавлении пользователя из админ панели вы можете назначать его группы доступа и владельца только из числа подчинённых вам групп. Вы видите конфиденциальные
373 * данные(ip, x-oauth-token, email) только подчинённых вам по системному весу пользователей. У каждого пользователя отключается x-oauth-token авторизация -
374 * от него не будут работать api запросы и соответсвенно <a target="_blank" href="/docs/cron.html"><b>cron задачи</b></a> выполняющиеся от его аккаунта.
375 *
376 * Вы можете входить под сессией подчиненных вам пользователей, кликнув на его id в таблице. В таблице пользователей выводятся не все данные, часть из них скрыта по умолчанию, для
377 * экономии места. Это изменяется в верхней панели, над таблицей. Есть кнопочка включающая асинхронное автообновление таблицы. Чтобы наблюдать за активностью пользователей и
378 * страниц на которых они находятся, воспользуйтесь этим и соответсвующими сортировками, поиском, фильтрами - все эти состояния сохраняются при перезагрузках в любых таблицах моделей.
379 *
380 * Отключение пользователя выключает для него любой функционал и страницы, он везде получает ошибку/страницу 403. Отключать можно только подчинённых пользователей, как и любые
381 * действия производимые с изменением свойств любой модели.
387 * При создании шаблона пользователь может устанавливать любые группы доступа. Группы доступа шаблона - это те группы, которым будет доступен данный шаблон
388 * на чтение(может быть встроен - использоваться). Группу владельца, как и в любой модели, можно устанавливать только из числа подчинённых групп.
389 *
390 * Можно добавлять неограниченное количество шаблонов, с различными вёрстками, различных типов и с различными доступами.
391 *
392 * <a target="_blank" href="/docs/layout.html"><b>Более подробно о шаблонах и их типах</b></a>
393 *
394 * Все доступные шаблоны находятся и создаются в каталоге <a href="/docs/dir_096b0de097b3e98f91191e9d894d4363.html"><b>templates/</b></a>
400 * При добавлении страниц вы можете указывать абсолютно любые группы доступа, в том числе с правами выше ваших. Даже если у вас будут пересекаться группы и у пользователей
401 * уже будут страницы с такими request uri, это никак не навредит им, поскольку эта <a target="_blank" href="/docs/routing.html"><b>логика разрешится uri роутером</b></a>.
402 *
403 * Группа владелец указывается только из числа подчиненных вам групп(на всех моделях!).
404 *
405 * У различных страниц может быть указан один и тот же файл обработчик, и разные шаблоны, или наоборот, здесь вариаций - ваша фантазия.
406 * Отключение страницы делает её недоступной всем(403), кроме владельца. На доступных страницах линки на отключённые страницы(как и триггеры модалок и действий),
407 * автоматически скрываются средствами внедряющегося css - им добавляется правило display:none!important; За это отвечает Html/Hideout.php метод hide
408 * вызывающийся в include/session.php по завершении работы скрипта.
409 *
410 * @note
411 * Можно создавать страницы, шаблоны и спокойно натягивать например новую вёрстку, прямо на продакшене, а затем только останется переключить модели.
412 *
413 * Любые страницы можно кешировать, указывая количество секунд актальности кеша. Ключем кеша страницы будет полный REQUEST_URI запроса(включая строку запроса).
420 * Работа api выборок по логике аналогична моделям страниц. Единственное отличие, запрос к выборке должен начинаться с /api/select/your-path/...
421 *
422 * В обработчиках можно указывать любую свою логику. В контексте системных моделей - это json ответы плагину Datatables, в вашем случае это наверняка будет иная логика.
423 *
424 * Любые выборки можно кешировать, указывая количество секунд актальности кеша. Ключем кеша выборки будет полный REQUEST_URI запроса(включая строку запроса).
430 * При добавлении модели модального окна логика групп доступа и владельца, та же что и при добавлении страниц/выборок.
431 * Также указывается шаблон модалки, из числа доступных данной группе шаблонов. Дополнительно можно указать флажок создать действие, вместе
432 * с модальным окном будет создана модель действия с теми же доступами. При этом, если добавляется окно с формой(а именно для этого обычно они и связываются логикой),
433 * то в форме модального окна будет автоматически установлен атрибут action соответствующий request uri созданного действия.
437 * В модальном окне с формой по умолчанию так же идёт универсальный ajax обработчик реализующий submit и отправку формы, и её закрытие с перезагрузкой Datatable.
438 * Всё ненужное вы можете легко убрать или добавить свою логику в данный обработчик. При отправке формы она и вся страница автоматически блокируются прелоадерами -
439 * не беспокойтесь за повторные отправки, здесь всё предусмотрено!
443 * Поскольку код модальных окон уничтожается из DOM при их закрытии, и добавляется вновь при вызове, мы можем вешать в нём каждый раз обработчики типа:
462 * Такой обработчик будет повешен на document, а не на элемент, каждый раз при вызове окна, и соответственно при клике на элементе .classname может выполниться
463 * несколько раз(сколько раз вызывалась форма). Для отключения такого поведения используйте метод jquery .off() Но лучше вешать обработчики, как сказал выше,
468 * Не обязательно создавать модели-компоненты модальных окон с вызовом их через api, для этого можно воспользоваться шаблонами и встроить их
469 * в любую страницу по принципу incode шаблонов, наделив данные шаблоны нужными правами. А если не требуются ограничения прав доступа,
470 * то можно встраивать и вызывать окна напрямую стандартным способом. Окна, которые изначально присутствовали в DOM не будут уничтожены при закрытии, и даже более
471 * того, окна которые вы вызвали из api, их тоже можно не уничтожать из DOM после закрытия. Для такого поведения им нужно добавить атрибут data-noremove.
477 * Логика добавления обработчиков действий аналогична модальным окнам относительно доступов. Дополнительно можно указать флажок создать модальное окно, вместе
478 * с действием будет создана модель модального окна, причем для него будет автоматически выбран шаблон с формой и в action атрибуте установлен
479 * соответствующий request uri созданного действия.
494 * Встроенные cron задачи позволяют реализовать практически любую логику изменения состояний системных моделей по расписанию - включение, отключение, замена,
495 * удаление, очистка, смена шаблонов с вёрсткой, страниц, uri.
496 *
497 * Синтаксис расписаний такой же как в Linux. В остальном это такая же модель как и остальные, имеющая свою группу владельца, вы не сможете поставить задачу
498 * от не подчиненного вам по системному весу пользователя.
499 *
500 * <a target="_blank" href="/docs/cron.html"><b>Более подробно о встроенном cron здесь</b></a>
501 *
502 *
503 */
504
505
506
507 /**
508 * @page related_functionality Управление связанным функционалом
509 *
510 * Предположим у вас есть некоторый функционал, который состоит не из одного компонента(модели), а включает в себя их некое множество. И им необходимо
511 * эффективно управлять целиком(пакетно).
512 *
513 * Например, у вас есть функционал "оформление заявок", который включает в себя определённые incode(встраиваемые) шаблоны, формы/действия, модальные окна, страницы.
514 * И вам нужно иметь возможность отключать и выключать целиком весь этот связанный функционал, а не по отдельности.
515 * Для этого создайте группу "оформление заявок" и свяжите группы доступа всех моделей лишь с данным функционалом(группой). И вы сможете управлять им с кнопки включения
516 * и отключения группы.
517 *
518 * Выключение группы делает все её модели недоступными на чтение и виртуально исключает из неё пользователей. Распределяя связанный функционал по группам вы можете
519 * управлять его доступностью пакетно.
520 *
521 * Кроме этого, в управлении группами вы можете массово добавлять пользователей определённых групп в другие группы и массово исключать их из указанных групп.
522 * Соответственно вы сможете легко одномоментно "вводить" функционал для опредёленных групп, или "забирать" функционал у определённых групп пользователей.
527 * Помимо этого, вы можете отключать и шаблоны, отключая например пакетно все страницы использующие данный шаблон.
528 *
529 */
530
531
532
533
534
535 /**
536 * @page userlandnaming Руководство по именованию
537 *
538 * Именование request для запросов модальных окон должно иметь следующий вид:
539 * @code
540 * /api/modal/my-modal
541 * @endcode
542 *
543 * Именование request для запросов действий может иметь любую вложенность, но должно начинаться с
544 * @code
545 * /api/action/.../my-action
546 * @endcode
547 *
548 * @note
549 * Хорошей практикой будет давать одни имена модальным окнам и обработчикам их форм.
550 *
551 *
552 * Именование request для запросов выборок может иметь любую вложенность, но должно начинаться с
553 * @code
554 * /api/select/.../my-select
555 * @endcode
556 *
557 * Именование request для страниц может иметь любую вложенность либо не иметь её вовсе.
558 * @code
559 * /...
560 * @endcode
561 *
562 *
563 * Для моделей с динамическими uri используйте всего одну запись в таблице моделей и один request и дописывайте необходимую php логику в include/uri-router.php
564 * В данном файле есть закомментированный пример реализации. Для контента используйте свою отдельную таблицу страниц. Назвать динамический запрос вы можете как угодно:
565 * @code
566 * /request-dinamic-model-name
567 * @endcode
568 *
569 * @warning
570 * Будет плохой практикой создавать отдельную модель <b>Страница</b> под каждую динамическую страницу вашего сайта!
571 *
572 * <a href="/docs/dinamic.html"><b>Более подробно о динамических страницах</b></a>
573 *
574 * Именование файлов обработчиков для действий, модальных окон, выборок и страниц
575 * @code
576 * /api/action/group-path/.../my-action.php
577 * /api/modal/group-path/.../my-action.php
578 * /api/select/group-path/.../my-select.php
579 * /api/page/group-path/.../my-page.php
580 * @endcode
581 * где group-path это один из доступных каталогов групп к которым принадлежит ваш аккаунт, далее может быть(или отсутствовать) любая вложенность
582 *
583 *
584 * @note
585 * Хорошей практикой будет объединять обработчики в дополнительные каталоги по смыслу архитектуры логики вашего проекта.
586 * Например, <a href="/docs/dir_1a6228b500bc1e8329862059492cfac8.html"><b>системные обработчики действий</b></a> и <a href="/docs/dir_3cbab2261c191b72730af3bc6a348131.html"><b>модальных окон</b></a>, ниже system каталога имеют дополнительную вложенность - эта разбивка по смыслу в чисто информативных целях
587 */
588
589
590
591
592
593
594
595
596
597
598 /**
599 * @page dinamic Динамические модели страниц
600 *
601 * Для моделей с динамическими uri используйте одну запись в таблице моделей и один request и дописывайте необходимую php логику в include/uri-router.php
602 *
603 * А для контента используйте свою отдельную таблицу страниц. Например создаем модель с request:
604 * @code
605 * /request-dinamic-model-name
606 * @endcode
607 *
608 *
609 * пример запроса к динамическим страницам
610 *
611 * - my-categories - таблица в бд с вашими динамическими категориями
612 * - my-pages - таблица в бд с вашим контентом динамических страниц
613 * - url - поля в бд ваших категорий и страниц для формирования запросов
614 *
615 * - /request-dinamic-model-name - ваша модель страницы (укажем только 1 уникальный request для неё и 1 обработчик)
616 *
617 * - $data_page - данные вашей страницы, которые будут доступны в контексте её файла
618 * - /any-category-url/any-page-url - запросы по которым будет доступна ваша динамическая модель
655 * Будет плохой практикой создавать отдельную модель <b>Страница</b> под каждую динамическую страницу вашего сайта! Используйте код выше.
656 */
657
658
659
660
661
662
663
664
665
666
667
668 /**
669 * @page routing Роутинг, контроллеры URI
670 *
671 * Исходя из логики описанной в разделе <a href="/docs/groups_users.html"><b>Группы и пользователи</b></a> доступ по http к модели возможен в совокупности следующих свойств:
672 *
673 * - Модель должна быть включена;
674 * - Одна из активных групп пользователя должна входить в группы доступа модели;
675 *
676 * или же:
677 *
678 * - Модель может быть выключена;
679 * - Одна из активных групп пользователя является владельцем данной модели;
680 *
681 *
682 * Роутинг запросов к request моделей учитывает <a href="/docs/weight.html"><b>системный вес пользователя</b></a> и сортирует модели с одинаковыми request на основе наибольшего минимального системного веса групп доступа моделей.
686 * Иными словами, контроллер может отдавать модели с одинаковым REQUEST_URI, но с разными файлами обработчиками и контентом в них, разными группами доступов и владельцев моделей.
687 * При этом пользователи могут находится в одних и тех же пересекаемых группах. Например пользователь 1 состоит в группе "администраторы" и "пользователи", а пользователь 2 состоит в группе "пользователи".
688 * Вес групп пользователя 1 больше. И есть 2 разных модели типа "страница" с одинаковым REQUEST_URI.
689 * Но для пользователя 1 доступны обе страницы, а для пользователя 2 только одна. В результате пользователям будут отданы разные страницы,
690 * т.к. пользователю 1 будет отдана страница наиболее подходящая по его весу групп.
691 *
692 *
693 * Разберем наглядный пример из самых простейших вариантов:
708 * Поэтому в группы доступа своих моделей любой пользователь может назначать любые даже старшие по весу группы.
709 * Даже если у них есть модели с такими же request uri, им это никак не навредит.
710 * Это как в Linux вы назначили своему файлу права rwx-rwx-rwx(0777) - делайте что хотите, ваши проблемы, не хотите не делайте, колхоз дело добровольное.
721 * Demo группе доступна теперь только такая главная. Аналогичного эффекта можно добиться, если отключить группу Demo.
722 * Скрипт будет вести себя так, будто этой группы нет. И пользователи группы Demo, если они не состоят в других активных группах,
723 * станут Гостями с нулевыми правами не авторизованных.
724 *
725 * Выше лишь базовый пример работы роутинг контроллера. Возможности комбинаций групп доступа, владельцев моделей и системного веса,
726 * обработчиков и шаблонов на основе этих компонентов ограничены лишь вашей фантазией!
727 *
728 * @warning
729 * Фантазия должна ограничиваться здравым смыслом;)
730 *
731 * Тем не менее ваши первичные действия в новом проекте всегда будут начинаться с главной. Вы же не собираетесь оставлять текущую главную страницу wrong-mvc.
732 * Не стоит её изменять. Просто отключите её, но сначала создайте свою <a href="/docs/your_first_model.html"><b>первую модель - главную страницу вашего проекта</b></a>
733 */
734
735
736
737
738
739
740/**
741 * @page your_first_model Ваша первая модель - главная
742 *
743 * Какими вероятнее всего будут ваши первичные действия при создании вашего нового проекта на базе wrong-mvc?
744 *
745 * Вам нужно создать свою главную страницу, со своим контентом и своим шаблоном, в котором будет подключен ваш дизайн, вёрстка и т.п. Верно?
746 *
747 * Для этого вы отключаете модель Страница id 1 - главная для не авторизованных. И создаёте свою любую модель главной с любым шаблоном и с запросом / с группой доступа <b>Гости</b>.
748 * Не забудьте включить свою главную, а то ваши пользователи увидят 403, ведь по умолчанию модели добавляются в выключенном состоянии. Вот и всё. Ваш сайт готов 😉
752 * Конечно, вероятнее всего, главная - это будет уже ваша вторая модель. Первой моделью вы <a href="/docs/layout.html"><b>создадите шаблон</b></a> для неё и подключите в него <a href="/docs/castomization.html"><b>свои стили/кастомизированную сборку</b></a>.
753 *
754 * Входить в систему вы можете с отдельной страницы <a target="_blank" href="/enter"><b>/enter</b></a> или прописать в своём шаблоне вызовы соответствующих триггеров вызова модальных окон авторизации.
755 *
756 * Но вот вы вошли в раж, и погнали клепать разделы своего проекта, один за другим. А их модели схожи между собой(всего-то чуток поменять).
757 * Тут вам пригодится <a href="/docs/import_export.html"><b>копирование моделей</b></a>
758 *
759 */
760
761
762
763
764
765
766
767 /**
768 * @page layout Шаблонизация, своя вёрстка, стили
769 *
770 * В системе предусмотрено 5 типов шаблонов, имеющие свою специфику и различные варианты добавления и встраивания.
771 *
772 * Шаблоны как и все остальные компоненты наследуют <a href="/docs/groups_users.html"><b>политики прав доступов</b></a>. Группы не имеющие прав доступа к шаблону не смогут его использовать. Если это page шаблон страницы, то все страницы с данным шаблоном будут недоступны(кроме владельца шаблона).
773 * Если это incode шаблон - он не будет встроен в страницу.
774 * Также, шаблон, к которому нет прав доступа, невозможно использовать при создании компонента(модели). Включая и выключая шаблоны - вы отключаете их доступность
775 * у всех кроме группы-владельца шаблона.
776 *
777 *
778 * Каждый тип разберем по отдельности и покажем, как использовать шаблоны различного типа.
779 *
780 * @section page Шаблоны типа: page
781 *
782 * Шаблоны данного типа - это основные(каркасные) шаблоны страниц, в которые автоматически встраивается контент(содержимое) страниц. Если можно так назвать - это wrapper или каркас шаблоны.
783 * То есть, вы <a href="/docs/models.html#pages"><b>создаете страницу</b></a>, с "голым" контентом-кодом страницы указывая для неё нужный шаблон.
784 * А код страницы уже автоматически встраивается в указанный для неё шаблон. Таким образом вы подтягиваете на страницу какой то общий каркас вёрстки,
785 * подключение стилей, скриптов и т.д.
786 *
787 * Есть несколько способов подключения стилей и скриптов, разберем их подробно.
788 * В моделях шаблоны создайте новый пустой шаблон для страниц своего сайта. По умолчанию ваш новый пустой шаблон уже содержит в head код подключения js и css системы:
795 * - main.min.css - это кастомизированный Bootstrap css админ панели со сборкой иконок от fontawesome и кастомными стилями для всплывающих сообщений
796 * - main.min.js - это системная сборка jQuery v3.6.0 + js модули Bootstrap v4.6.1 + js файл взаимодействия с бекендом по триггерам - app/js/import/wrong.js
797 * - $row->name - это название страницы модели, которое автоматически подтянется из модели страницы для которой будет указан данный шаблон.
798 * - <?php require $CONTENT_PAGE_FILE; ?> - это код, который автоматически подтянется из модели страницы для которой будет указан данный шаблон.
799 *
800 * вместо main.min.css вы укажите свою css сборку, а файл main.min.js вы должны оставить как есть.
801 *
802 * Скачайте готовую или <a href="/docs/castomization.html"><b>соберите свою кастомную Bootstrap css сборку</b></a> и подключите её вместо main.min.css
803 *
804 * Вы можете создавать любое количество шаблонов с разными дизайнами и менять их как перчатки.
805 * Например, у вас есть шаблон новогодний, включите логи для своей группы, поменяйте у страницы шаблон и верните на место,
806 * <a target="_blank" href="/docs/logs.html"><b>посмотрите в логах данные</b></a>, которые были отправлены на обработчик.
807 * И просто автоматизируйте эти действия через <a href="/docs/cron.html"><b>встроенные cron задачи</b></a>, включая новогодний шаблон 10 декабря и выключая 20 января например.
808 *
809 * Все доступные шаблоны находятся и создаются в каталоге <a href="/docs/dir_096b0de097b3e98f91191e9d894d4363.html"><b>templates/</b></a>
810 *
811 * <b>Способ подключения 1:</b>
812 *
813 * Вы можете подключать в свои проекты как минифицированные js/css файлы так нет, при этом не минифицированные версии будут автоматически минифицированы 1 раз, а если изменится
814 * исходник, файл снова будет минифицирован. Например:
815 *
816 * Данный код размещенный в head любого php шаблона будет просто выводить содержимое файлов в html странице обрамленным в тегах <style> и <script>
825 * А данный код, автоматически минифицирует и создаст .min версии и будет выводить их содержимое в тегах <style> и <script>, а в случае изменения исходников, минифицирует файлы заново.
835 * Этот способ не всегда удобен, если вы не собираете стили, а просто помещаете их в каталог assets, в этом случае, есть ещё 2 способа подключения стилей и скриптов
844 * данный код, инжектит уже теги подключения с аттрибутами src(link и script), но помимо этого добавляет в строку запроса время модификации файла, что автоматически
845 * всегда сбросит кеш. Указанные файлы также минифицируются автоматически.
846 *
847 * Этот способ обеспечивает сохранение логики всех относительных путей указанных в файлах, в отличии от способа 1, когда содержимое css, js встраивается
848 * непосредственно в html код страницы.
849 *
850 * <b>Способ подключения 3 (самый крутой):</b>
851 *
852 * Вы скачали некий шаблон верстки и положили его например в /assets/examples/tivo-1.0.0/ у каждого шаблона своя логика размещения css, js, картинок.
853 * Неправда ли муторно это всё изменять и прописывать?
854 *
855 * В файле шаблона просто укажите "магическую" константу USE_ASSETS_PATH:
861 * и оставьте дурную работу - дуракам, в тегах script, img всё само автоматически подставится в атрибуты src, а в тегах link в атрибуты href, причем так же с временем модификации файла.
862 * Как вот в этом шаблоне example1.php А в шаблоне example2.php успешно обрабатываются даже background-image: url(); Кстати заметьте, там наша "магическая" константа указана несколько иначе,
863 * потому как относительные пути шаблона несколько иные, но это никак не влияет на результат. В константе вы просто указываете путь к каталогу с ресурсами шаблона!
864 *
865 * Посмотрите какая прелесть, клепай шаблоны - не хочу:
871 * Иногда может понадобиться скрыть/показать только на определённой странице какой то элемент, но при этом у вас общий шаблон под все страницы. Воспользуйтесь атрибутами: <b>data-visible-page</b> или <b>data-hide-page</b>
872 *
873 * - data-visible-page="/example-request" элемент будет виден только на /example-request
874 * - data-hide-page="/example-request" элемент будет виден везде и скрыт только на /example-request
875 *
876 * пример использования <b>data-visible-page</b> в шаблоне tivo.php для ссылки PRICING, она показывается лишь на главной странице шаблона, а на остальных скрыта
877 *
878 * <b>Способ подключения 4 (асинхронный):</b>
879 *
880 * Любой css, js код плагина или библиотеки, вы можете подключить асинхронно, не сразу а по мере выполнения каких то действий на странице, для этого
885 * Каркасными шаблонами о которых сказано выше, не всегда удобно распределять более детальную логику блоков, особенно с учетом разграничения прав.
886 * Тут на помощь приходит тип встраивания и шаблоны - incode. Шаблон incode - это уже подключаемый шаблон непосредственно при помощи специального метода в абсолютно любом месте
887 * вашего приложения, будь то страницы, выборки, модальные окна или даже действия.
888 *
889 * Для php это просто встраиваемый кусок кода, но здесь дополнительно перед встраиванием осуществляются проверки прав доступа к шаблону -
890 * он не будет встроен для групп пользователей, у которых нет доступов на чтение модели данного шаблона. Т.е. код данного шаблона может содержать какие то блоки и любой код,
891 * доступные лишь определённым группам пользователей - определяются они группами доступа шаблона(а также определяемые включением и отключением самого шаблона)
892 *
893 * @code
894 * Wrong\Html\Template::require(777); // где 777 идентификатор любого шаблона
895 * @endcode
896 *
897 * Пусть вас не смущает область видимости ваших переменных - несмотря на то, что подключение файла осуществляется в методе класса,
898 * абосолютно все ваши переменные объявленные вне кода шаблона, будут доступны внутри него с теми же именами, точно так же, как если бы вы встроили файл шаблона
899 * обычной require конструкцией. Только здесь перед встраиванием автоматически делается проверка доступов к шаблону.
900 *
901 * И вы не только имеете доступ ко всем своим переменным объявленным ранее вне кода шаблона, вы можете их переназначать, объявлять новые,
902 * пользоваться объявленными экземплярами своих классов и т.д - все переменные автоматически доступны как будто из глобальной области видимости скрипта. Предположим код:
903 *
904 * @code
905 * $a = 1;
906 * Wrong\Html\Template::require(777); // здесь внутри кода шаблона $a изменилась на 2
907 * Wrong\Html\Template::require(999); // здесь $a изменилась уже на 3
908 * var_dump($a == 3); // true
909 * @endcode
910 *
911 *
912 * @section modal Шаблоны типа: modal
913 *
914 * Шаблоны данного типа можно встраивать 2 способами:
915 * - создавая модель модального окна, которая будет запрошена из api
916 * - или встраивая шаблон по принципу incode
917 *
918 * Если вы создаёте api модель - код выбранного шаблона будет скопирован в каталог /api/modal/... и уже не будет относиться к данному шаблону. Данной модели отдельно
919 * вы сможете задать права доступа и у неё будет свой отдельный код, который она просто возьмет из выбранного шаблона при создании. Кода окна в DOM при этом
920 * изначально не будет, он будет запрашиваться из api и уничтожаться при закрытии окна.
921 *
922 * А можно встраивать шаблоны модальных окон сразу же в текущий код, тем же способом что и incode:
923 * @code
924 * Wrong\Html\Template::require(777); // где 777 идентификатор шаблона окна
925 * @endcode
926 *
927 * В данном случае, если шаблон соответсвует правам доступа на чтение, он будет сразу встроен в код где он подключен.
928 *
929 * @section select Шаблоны типа: select
930 *
931 * При создании выборки код выбранного шаблона будет скопирован в каталог /api/select/... и уже не будет относиться к данному шаблону. Данной модели отдельно
932 * вы сможете задать права доступа и у неё будет свой отдельный код, который она просто возьмет из выбранного шаблона при создании модели(компонента).
933 *
934 * @section action Шаблоны типа: action
935 *
936 * При создании действия код выбранного шаблона будет скопирован в каталог /api/action/... и уже не будет относиться к данному шаблону. Данной модели отдельно
937 * вы сможете задать права доступа и у неё будет свой отдельный код, который она просто возьмет из выбранного шаблона при создании модели(компонента).
938 *
939 * @section template Кеширование шаблонов
940 *
941 * Встроенные по принципу incode шаблоны, а также каркасные шаблоны типа page можно кешировать, указывая количество секунд для кеширования.
942 * Для встроенных шаблонов будет отдаваться кешированный html участок шаблона. Для шаблонов типа page кеширован будет весь html вывод страницы, а ключем кеша будет REQUEST_URI запрос
943 * к странице с данным шаблоном.
944 *
945 */
946
947
948
949
950
951 /**
952 * @page import_export Экспорт, импорт, копирование моделей
953 *
954 * Модель любой подчиненной группы или модель владельцем которой вы являетесь можно экспортировать и импортировать в виде zip архива в систему на базе wrong-mvc
955 *
956 * При импорте будет автоматически создана модель и её php файл. В случае совпадения имен, запросов, файлов, им будет добавлен постфикс -copy
957 *
958 * Чтобы быстро создать модель из другой модели, с тем же кодом/контентом можно воспользоваться её копированием - это фактически одномоментный экспорт и импорт.
959 * @attention
960 * При импорте модели заливается файл .php, поэтому вы не должны назначать права доступа на действие импорта кому попало, во избежание попадания вредоносного кода на ваш сервер.
961 *
962 * Данный функционал предусмотрен для моделей типа <b>Шаблоны, Страницы, Выборки, Модальные окна, Действия, Задачи</b>
963 *
964 * Основной администратор состоящий в группе <b>Система</b> может осуществлять экспорт/импорт/копирование системных моделей, но при этом у новой модели группа владелец
965 * сменится на группу <b>Администраторы</b>. В остальных же случаях у импортируемой/копируемой модели остается прежняя группа владелец(она может быть только подчинена
966 * группе пользователя осуществляющего действие).
979 * Для групп предусмотрена настройка включения/отключения лога действий. Действия - это все запросы к апи начинающиеся
980 * @code
981 * /api/action/...
982 * @endcode
983 *
984 * Если логгирование для группы включено, в логах вы сможете видеть все действия пользователей этой группы -
985 * метод запроса, переданные на обработчик данные в json формате и ответ api. В случае если в ответе api была любая ошибка, строка будет подсвечена красным.
986 *
987 * Строки логов можно разворачивать как по одной, так и все, и включать автообновление(перезагрузку) таблицы.
991 * За запись логов отвечает класс Logs/Write.php
992 */
993
994 /**
995 * @page http_api HTTP API запросы, X-Auth-Token
996 *
997 * Это запросы к api системы извне по http.
998 * Если запрос выполняется от имени пользователя, в заголовках необходимо указать его X-Auth-Token, при этом работа через API должна быть включена на уровне системы,
999 * а также отдельно для данного пользователя. При отправке запроса к api <a target="_blank" href="/docs/csrf.html"><b>токен CSRF</b></a> указывать не нужно.
1000 *
1001 * Для запросов на выполнение действий включение/переключение функционала вам понадобится в основном всего два параметра в теле запроса - id и table.
1002 * Вы всегда можете выполнить действие вручную и <a target="_blank" href="/docs/logs.html"><b>посмотреть в логах его данные</b></a>.
1003 *
1004 * Например включаем/отключаем группу пользователей id 3 выполняя по сути вот это действие
1044 * Вы можете делать любые запросы к выборкам, действиям. Автоматизированные запросы от имени пользователя доступны также через
1045 * <a href="/docs/cron.html"><b>Встроенные CRON задачи</b></a>. Там вместо X-Auth-Token там достаточно указать id пользователя из подчинённой группы.
1046 *
1047 */
1048
1049
1050 /**
1051 * @page internal_api Cистемные curl API запросы, X-Auth-Token
1052 *
1053 * Запросы к внутрисистемному(на текущем хосте) API выполняются при помощи статического метода req() в Curl/API.php
1054 *
1055 * Если запрос выполняется от имени пользователя, в заголовках необходимо указать его X-Auth-Token, при этом работа через API должна быть включена на уровне системы,
1056 * а также отдельно для данного пользователя. При отправке запроса к api <a href="/docs/csrf.html"><b>токен CSRF</b></a> указывать не нужно.
1057 *
1058 * Вы можете указывать метод, данные, заголовки. Возвращается раскодированный json объект либо строка, если раскодировка не удалась.
1059 *
1060 * Вы можете делать любые запросы к выборкам, действиям. Автоматизированные запросы от имени пользователя доступны также через
1061 * <a href="/docs/cron.html"><b>Встроенные CRON задачи</b></a>. Вместо X-Auth-Token там достаточно указать id пользователя из подчинённой группы.
1062 *
1063 * Если запросу требуется тут же отвалиться, не дожидаясь ответа, то в таймауте можно задать 0.001
1095 * @page packages Подключение node и composer пакетов
1096 *
1097 * <b>Nodejs</b>
1098 *
1099 * Установка дополнительных nodejs пакетов выполнятеся в корневом каталоге проекта. Любые node модули вы можете включать в сборку
1100 * в каталоге /app как отдельными js/css минифицированными модулями, так и объединяя в общие(примеры main.scss, main.js) файлы. Это зависит от ваших целей. Например модули используемые
1101 * в админ панели, такие как Datatables, не нужный в сайтах-проектах js код админки, это всё вынесено отдельными сборками в system-admin.js
1102 * сборка из /app в /public_html производится командой
1103 * @code
1104 * gulp build
1105 * @endcode
1106 *
1107 * @attention
1108 * Учитывайте что при сборке очищаются следующие каталоги указанные в gulpfile.mjs
1109 *
1110 * @code
1111 * /public_html/assets/system/css/
1112 * /public_html/assets/system/js/
1113 * /public_html/assets/system/img/
1114 * @endcode
1115 *
1116 * @note
1117 * На данном примере показаны логические каталоги <b>assets/system</b> для системного фронтенда, любые свои фронтенд сборки для сайтов-проектов вы можете
1118 * собирать в другой каталог, и это будет логично <b>assets/my-frontend-dirname</b>. Для этого поправьте пути в <b>gulpfile.mjs</b>
1119 *
1120 * Аналогично можно включать в сборку css в /app/scss/*.scss любые пакеты из node_modules, собирая их целиком или по частям.
1121 * Это зависит от применения их в вашем проекте(глобально или лишь на отдельных страницах).
1122 *
1123 * Данный код размещенный в head любого php шаблона будет выводить содержимое файлов в html странице обрамленным в тегах <style> и <script>
1134 * На главной странице включается автозагрузчик из vendor
1135 * @code
1136 * require '../vendor/autoload.php';
1137 * @endcode
1138 *
1139 * Устанавливаете любые пакеты
1140 * @code
1141 * composer require package-name
1142 * @endcode
1143 *
1144 * и пользуетесь в своих проектах
1145 *
1146 */
1147
1148
1149
1150
1151
1152
1153
1154
1155 /**
1156 * @page triggers Триггеры действий и модальных окон
1157 *
1158 * - <b>Триггеры вызова действий</b> отправляют POST запросы к обработчику, в переменных запроса передаются всё переменные из атрибутов data-*
1159 * - <b>Триггеры вызова модальных окон</b> отправляют GET запросы к обработчику, в переменных запроса передаются всё переменные из атрибутов data-*
1160 *
1161 * Создайте <b>модель - действие</b> с request uri
1162 * @code
1163 * /api/action/my-action
1164 * @endcode
1165 *
1166 * Предположим наша модель выполняясь возвращает некий ответ в формате json
1167 *
1168 * Наведите мышкой на id модели в таблице и вызовите "конструктор триггера". Триггер действия - это некая кнопка или любой элемент,
1169 * с атрибутом <b>data-action</b>="my-action" Остальные атрибуты для триггера не обязательны.
1175 * При клике на такой триггер будет мгновенно отправлен POST запрос на обработчик модели /api/action/my-action
1176 * На время ajax запроса кнопка автоматически блокируется, а также будет блокирован весь экран - показывается полупрозрачный спиннер загрузки, это сделано во избежание повторных кликов.
1177 *
1178 * Для действия с подтверждением добавьте аттрибут <b>data-confirm</b>="true" - при клике на триггер, запрос не будет отправлен, а будет выдано стандартное
1179 * модальное окно с заголовком "Подтвердите действие" и кнопками "Отмена / Да". Запрос будет отправлен лишь при нажатии на "Да".
1180 *
1181 * Дополнительными атрибутами к триггеру, вы можете изменить заголовок и текст в модальном окне подтверждения:
1185 * В атрибутах можно указать любую вашу callback функцию, которая будет вызвана сразу после выполнения действия и получит в response параметр ответа json от обработчика
1186 * <b>data-callback</b>="callbackMyAction"
1187 *
1188 * А саму функцию вы пропишите в коде заранее, вот простейший код, который покажет сообщение-всплавашку с ошибкой или успехом действия:
1189 * @code
1190 * function callbackMyAction(response) {
1191 *
1192 * if (response.error) {
1193 * errorToast(response.error);
1194 * return;
1195 * }
1196 *
1197 * successToast(response.message);
1198 * }
1199 * @endcode
1200 *
1201 * Вы можете указать и другой формат ответа обработчика, отличный от json, например <b>data-response</b>="script" или <b>data-response</b>="html" или же <b>data-response</b>="xml"
1202 *
1203 * Вы можете указать дополнительно любые произвольные данные в data-* атрибутах триггера, например data-id="value1" data-subid="value2" и так далее.
1204 * И обработчик получит все эти переменные в POST в таком виде:
1205 * @code
1206 * $_POST['id'] = 'value1';
1207 * $_POST['subid'] = 'value2';
1208 * @endcode
1209 *
1210 * По умолчанию в обработчиках все передаваемые переменные рекурсивно обрабатываются для защиты от атак типа XSS
1211 *
1212 * @code
1213 * array_walk_recursive($_POST, function (&$item) {
1218 * Создаваемые модели действий уже содержат примеры успешных и ошибочных json ответов
1219 *
1220 * <hr>
1221 *
1222 * Создайте <b>модель - модальное окно</b> с request uri
1223 * @code
1224 * /api/modal/my-modal
1225 * @endcode
1226 *
1227 * Укажите для неё любой из доступных шаблонов окон. Наведите мышкой на id только что созданной модели в таблице и вызовите "конструктор триггера".
1228 *
1229 * Кнопка для триггера модального окна ничем не отличается от стандартного синтаксиса Bootstrap 4
1234 * Но в данном случае вовсе не обязательно размещать html код модального окна в коде страницы. Модальное окно будет запрошено по api и
1235 * автоматически добавлено в DOM документа. А после закрытия модалки её код будет автоматически уничтожен из DOM.
1236 *
1237 * На момент ajax запроса к api с модальным окном, кнопка триггер автоматически блокируется как и весь экран - показывается полупрозрачный спиннер загрузки.
1238 *
1239 * Аналогично можно добавить к атрибутам триггера имя вашей callback функции <b>data-callback</b>="callbackMyModal". Данная функция будет вызвана сразу же после
1240 * добавления кода модалки в DOM, но ещё до её отображения. Т.е. средствами javascript в callback функции вы можете изменить это окно до неузнаваемости или
1249 * Также вы можете передать любые переменные в data-* атрибутах, например data-id="value1" data-subid="value2", которые будут переданы в GET запросе вызова модального окна.
1250 *
1251 * <hr>
1252 *
1253 * Помимо вышеописанных методов, вы можете <a href="/docs/js_actions_modals.html"><b>вызывать действия и модальные окна при помощи специальных javascript функций</b></a>, аналогично передавая им любые параметры.
1254 * Об этом следующий раздел.
1255 */
1256
1257
1258 /**
1259 * @page js_actions_modals Javascript вызовы действий и окон
1260 *
1261 * Помимо вышеописанного способа вызова действий и окон при помощи триггеров, их можно вызывать и javascript методами.
1262 *
1263 * Разберем всё то же действие /api/action/my-action описанное в предыдущем разделе о триггерах. Чтобы вызвать его программно:
1264 * @code
1265 * _action({
1266 * action: 'my-action',
1267 * id : 'value1',
1268 * subid : 'value2'
1269 * }, response => {
1270 * console.log(response);
1271 * });
1272 * @endcode
1273 *
1274 * Элегантно и просто. Программный вызов не поддерживает возможности вызова подтверждающего окна.
1275 *
1276 * Вызовем модальное окно входа в систему:
1277 *
1278 * @code
1279 * _modal('#sign-in');
1280 * @endcode
1281 *
1282 * или
1283 *
1284 * @code
1285 * _modal('#error', null, 'error=Wrong MVC - это круто!&value_1=value1&value_2=value2');
1315 * Предусмотрен вызов системных всплывающих сообщений, для отображения информации об ошибках, предупреждениях, успешных действиях и прочие сообщения.
1316 *
1317 * Функции вызова принимают первым параметром строку сообщения, и вторым таймаут. Оба параметра не являются обязательными, есть тексты по умолчанию,
1318 * а таймаут по умолчанию равен 5 секундам.
1319 *
1320 * Вызов сообщений по порядку успех, ошибка, предупреждение, сообщение:
1333 * Вызов сообщений обычно комбинируется с <a href="/docs/triggers.html"><b>вызовом действий</b></a>, когда callback функция получает ответ от api. Также
1334 * они используются при <a href="/docs/ajaxforms.html"><b>отправке ajax форм</b></a>.
1335 *
1336 */
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349 /**
1350 * @page ajaxforms AJAX формы, блокировка submit кнопок
1351 *
1352 * Стандартные формы для сохранения данных из модальных окон выглядят вот так:
1357 * В случае получения ошибки 403, 404 будет вызвано всплывающее сообщение с соответствующим текстом. В случае ошибки в api также всплывающее сообщение об ошибке
1358 * с текстом.
1359 *
1360 * В случае успеха происходит скрытие окна и обновление таблицы. На время отправки ajax запроса форма блокируется функцией lockSubmit - это гарантирует
1361 * защиту от повторных кликов по кнопке отправки. Формы обрабатываются api действий.
1362 */
1363
1364
1365
1366
1367
1368
1369 /**
1370 * @page csrf CSRF защита POST/PUT/DELETE
1371 *
1372 * Любой, даже динамически созданной форме, всегда автоматически добавляется CSRF токен. Вы не видите его изначально в своих формах и не должны об этом заботиться.
1373 * Токен представляет из себя md5 хеш идентификатора сессии, задаётся в include/session.php - там автоматически добавляется в страницу javascript переменная с токеном,
1374 * <a href="/docs/interaction.html"><b>по взаимодействию со страницей</b></a> этот токен добавляется hidden полем любой форме.
1375 *
1376 * <b>Все POST/PUT/DELETE запросы</b> защищены токеном! При отправке POST данных, когда они созданы <a href="/docs/triggers.html"><b>триггерами действий</b></a> либо
1377 * <a href="/docs/js_actions_modals.html"><b>javascript вызовами методом _action()</b></a> токен CSRF будет добавлен автоматически.
1378 *
1379 * Все запросы данными методами, без верного токена, будут заблокированы системой.
1380 *
1381 * Исключение составляют запросы по api авторизованные X-Auth-Token - в данном случае они защищены авторизационным токеном, и токен CSRF не требуется.
1382 *
1383 * Учитывайте это, если создаёте свой кастомный ajax запрос, то вы должны вручную добавить CSRF токен:
1393 * Если вы вручную создаёте свой кастомный ajax запрос, то возможно, вы не до конца разобрались с методом <a href="/docs/js_actions_modals.html"><b>_action()</b></a>
1394 * и возможностями <a target="_blank" href="/docs/triggers.html"><b>триггеров</b></a>, они способны обеспечить практически любые POST ajax запросы и выборки из api.
1395 */
1396
1397
1398
1399
1400 /**
1401 * @page interaction Событие взаимодействия interaction
1402 *
1403 * Не чаще чем раз в 500мс при событиях javascript <b>load scroll click focus touchstart mouseenter</b> создаётся window событие взаимодействия с системой - <b>interaction</b>
1404 *
1405 * Вы можете его прослушивать для организации своей логики в приложении
1406 * @code
1407 * $(window).on("interaction", () => {
1408 * console.log("Вот это событие!");
1409 * });
1410 * @endcode
1411 *
1412 * По данному событию <b>interaction</b>, вы можете уже запускать автовоспроизведение аудио, видео, если пользователь уже совершал события отличные от <b>load</b>, т.е. скроллил, кликал и т.д <b>scroll click focus touchstart mouseenter</b>, поскольку <b>interaction</b> уже будет наследовать разрешенный промис взаимодействия(политики браузеров) - <a target="_blank" href="https://developer.chrome.com/blog/autoplay/">Uncaught (in promise) DOMException: play()</a>
1413 *
1414 *
1415 */
1416
1417
1418
1419
1420
1421 /**
1422 * @page textarea_counters Счётчики символов в textarea
1423 *
1424 * Любому полю типа textarea можно добавить автоматический счётчик введенных символов, для этого достаточно указать полю css класс .with-counter
1425 * Класс-родитель textarea должен позиционироваться как relative.
1453 * Но пока доступен лимит попыток данному IP, токен не генерируется и не отправляется с формой, поле остается пустым
1454 *
1455 * Окно с формой также содержит callback функцию verifyCallback, для заполнения этого поля и submit-а, которая так же не используется, до момента пока доступны попытки
1498 * На локальном сервере под ip 127.0.0.1 работу капчи вы не увидите, поскольку в методе check() класса Auth/Hcaptcha.php
1499 * прописано соответсвующее условие, делающее проверку с этим ip всегда валидной.
1500 *
1501 * Формы регистрации, авторизации, восстановления пароля, подтверждения почты, защищены по умолчанию, лимит - 5 попыток с одного IP в час.
1502 *
1503 */
1504
1505
1506
1507
1508
1509
1510 /**
1511 * @page loadlibs Подгрузка JS библиотек по мере применения
1512 *
1513 * Когда смотришь на классные, мощные проекты, но видишь код в несколько мегабайт(причем даже сжатый), то хочется плакать от боли или смеяться во сне.
1514 * Особенно, если ты помнишь ещё wml и рекомендации не делать странички свыше 2-4 кб. Такая экономия, это конечно перебор в наше время. Но при использовании
1515 * массы библиотек(плагинов), проект априори становится неповоротливым чудовищем. А ведь не только на разных страницах, но и в пределах одной можно подгружать код библиотек
1516 * тогда, и только тогда, когда он используется.
1517 *
1518 *
1519 * В качестве примера, возьмем <a href="/"><b>главную wrong-mvc</b></a>. Тут используется плагин Fancybox(137кб) и сборка редактора CodeMirror(356кб).
1520 * В сумме, не много, ни мало пол мегабайта. При этом далеко не факт, что зашедший на страницу пользователь кликнет на картинку, или вызовет редактор кода.
1521 *
1522 * А если это десятки библиотек?
1523 *
1524 * Что общего у всех плагинов? Это в основном некие css файлы, js файлы, и функция-метод инициализации. Так почему бы это не использовать?
1525 * Асинхронно загружать можно в том числе и свои коды, по мере необходимости.
1526 *
1527 * Функция loadLibs(cssPathArray, jsPathArray, funcName) принимает в параметрах
1528 *
1529 * - cssPathArray - массив путей к файлам
1530 * - jsPathArray - массив путей к файлам javascript
1531 * - funcName - имя метода, оно же имя функции, которое обязательно появится в свойствах window после выполнения js кода
1532 *
1533 * после загрузки всех файлов библиотеки функция возвращает разрешенный Promise.
1534 * css просто добавляются в head страницы, а js файлы загружаются поочередно, именно в том порядке в котором указаны в массиве!
1535 * Ведь у плагина может быть и несколько js файлов, и загрузиться они должны именно в определенном порядке.
1536 * Это обеспечивается встроенным стеком с callback функциями.
1537 *
1538 * @code
1539 * function loadLibs(cssPathArray, jsPathArray, funcName) {
1544 * if (jsPathArray && typeof window[funcName] !== 'function') {
1545 * loading();
1546 * let loads = [];
1547 * function func() {
1548 * f = loads.shift();
1549 * if (typeof f === 'function') {
1550 * f(func);
1551 * } else {
1552 * loading('hide');
1553 * resolve();
1554 * }
1555 * }
1556 * jsPathArray.forEach(path => {
1557 * loads.push((callback) => {
1558 * $.cachedScript(path).done(() => {
1559 * callback();
1560 * });
1561 * });
1562 * });
1563 * func();
1564 * } else {
1565 * resolve();
1566 * }
1567 * });
1568 * }
1569 * @endcode
1570 *
1571 * Свой кеширующий метод jquery ajax запроса мы могли бы и не добавлять, а обойтись стандартным ajax методом, но пусть будет, вдруг ещё пригодится:
1572 * @code
1573 * $.cachedScript = function (url, options) {
1574 * options = $.extend(options || {}, {
1575 * dataType: "script",
1576 * cache: true,
1577 * url: url
1578 * });
1579 * return $.ajax(options);
1580 * };
1581 * @endcode
1582 *
1583 * Но как же быть с кешем и почему кешированно, ведь не применится, спросите вы. Там всё будет кешировано тогда, когда это нужно, и обновлено тогда когда нужно. И вот почему.
1584 * Аргументы функции мы передаём, не в виде обычных массивов, а в виде обработанных бекендорм массивов, в которые добавлено в строку запроса время модификации файла.
1585 * Обработка осуществяется методом <a href="/docs/class_wrong_1_1_html_1_1_get.html#abc7daa0622551cfeca4942e5fb2a4292"><b>Wrong\Html\Get::pathArrayJSON</b></a>
1586 *
1587 * Вот так мы подгружаем плагин Fancybox в <a href="/docs/quest_8php_source.html"><b>шаблоне главной страницы для не авторизованных</b></a>.
1604 * плагин со всеми его файлами подгружаются лишь единожды по клику на любой элемент с атрибутом data-fancybox. И далее работает на странице как обычно. Первоначально при клике происходит проверка на наличие функции Fancybox в window объекте.
1605 * Если функции нет, подгружаем плагин при помощи loadLibs. После разрешения Promise мы выполняем необходимую инициализацию плагина и клик по тому самому объекту.
1606 *
1607 * Принцип подгрузки плагина CodeMirror аналогичен, но здесь есть особенность - ведь он инициализируется в модальном окне, которое <a href="/docs/triggers.html"><b>запрашивается триггером из api</b></a>. Тем
1608 * не менее, достаточно лишь 1 раз вызвать такую модалку - файлы плагина будут подгружены 1 раз, и в дальнейшем loadLibs Promise с этой библиотекой будет
1624 * @page stackjs Javascript-PHP стеки, отложенное выполнение
1625 *
1626 * Javascript стеки - это асинхронное взаимодейсвие php бекенда с js фронтендом. Вы устанавливаете в php бекенде js задачу(код), помещаете его в стек,
1627 * устанавливая таймаут выполнения кода, и опционально ключ в стеке во избежание дублирования задачи. Код выполняется спустя заданный таймаут, удаляясь из стека
1628 * и устанавливается следующий таймаут, если задачи ещё остались в стеке.
1629 *
1630 * Синтаксис добавления задач в стек определён статическим методом <a href="/docs/class_wrong_1_1_task_1_1stack_j_s.html#a87f6da2087b08888f4c87499bdae1b99"><b>StackJS::add($code, $timeout = 0, $key = '')</b></a>
1631 *
1632 * Выполнение и установка очередных таймаутов обеспечивается методом <a href="/docs/class_wrong_1_1_task_1_1stack_j_s.html#aafaba9fa1586d43a821eab809ee562aa"><b>StackJS::execute()</b></a>
1633 * и запросами к /api/action/stackjs
1634 *
1635 * При загрузке любой страницы выполняется метод <a href="/docs/class_wrong_1_1_task_1_1stack_j_s.html#aafaba9fa1586d43a821eab809ee562aa"><b>StackJS::execute()</b></a>, который добавляет в код страницы javascript, время выполнения которого в стеке истекло. При этом код удаляется
1636 * из стека. А также, если в стеке ещё остались задачи, время которых не истекло, то в код страницы добавляется функция с самым минимальным таймаутом из стека задач, которая
1637 * запросит /api/action/stackjs спустя этот таймаут. Этот запрос к api снова вернёт <a href="/docs/class_wrong_1_1_task_1_1stack_j_s.html#aafaba9fa1586d43a821eab809ee562aa"><b>StackJS::execute()</b></a> и т.д. пока стек не закончится.
1638 *
1639 * Есть также отдельно метод <a href="/docs/class_wrong_1_1_task_1_1stack_j_s.html#aa3515c48bb3cb8d97a1d2b4325fdb8f2"><b>StackJS::set()</b></a> который не выполняет js код,
1640 * а возвращает только функцию с таймаутом запроса к /api/action/stackjs
1646 * И спустя 30 секунд после её загрузки увидим всплывающее сообщение. А если мы спустя 10 секунд уйдем на другую страницу, то там оно покажется уже через 20 секунд.
1647 * А если мы перезагрузим эту страницу, до выполнения js кода из стека, то код и таймаут в стеке обновятся, и повторно,
1648 * т.е. дважды данный код выполнен не будет, благодаря уникальному ключу key-stack-test. Если нужно изменить это поведение -
1649 * просто не задавайте уникальный ключ элементу стека.
1650 *
1651 * Посмотрите, что автоматически добавила нам система в страницу, увидев в стеке отложенную задачу:
1655 * То есть, для реализации, мы не делаем постоянных ежесекундных ajax запросов к бекенду, нагружая тем самым сервер, до состояния кипящего чайника.
1656 * Кстати так делают именно чайники;) Мы делаем запросы тогда, и только тогда, когда они необходимы!
1657 *
1658 * Наглядно проедмонстрируем это в цикле, на той же странице добавим:
1669 * А затем обработчик /api/action/stackjs каждый последующий раз добавлял очередной код выполнения и очередной таймаут запроса к себе же, пока стек не закончился.
1670 *
1671 * Пунктуально поздравим наших пользователей с Новым Годом, секунда в секунду(если он конечно будет на странице,
1672 * а если у него сохранится сессия, то он увидит поздравление позже):
1673 * @code
1674 * Wrong\Task\Stackjs::add('successToast("С Новым Годом!");', strtotime('first day of January next year') - time(), 'new-year');
1675 * @endcode
1676 *
1677 * Усложняем задачи. Пример простейшей рекурсии, которая размещается и стартует при запросе к /api/action/my-any-action выполняясь каждые 5 секунд:
1684 * Теперь нам достаточно лишь однажды вызвать javascript действие /api/action/my-any-action и код будет выполняться на сайте бесконечно, пока мы не убьем свою сессию
1685 * @code
1686 * <script>
1687 * $.getScript('/api/action/my-any-action');
1688 * </script>
1689 * @endcode
1690 *
1691 * Но давайте уже будем работать с wrong-mvc как положено, ведь у нас есть специальные <a href="/docs/triggers.html"><b>триггеры действий</b></a>, которые активируют тоже самое поведение:
1697 * <a class="btn btn-primary" data-action="my-any-action" data-response="script" data-header="Запустить рекурсию?" data-body="Когда надоест, выйдите вон из своей сессии.">Запустить рекурсию!</a>
1698 * @endcode
1699 *
1700 * Вот ещё пример применения на странице:
1701 * @code
1702 * Wrong\Task\Stackjs::add('successToast("Я выполняюсь моментально без задержек! Но я вижу что ещё там в стеке кое что ниже есть через 3 секунды, поставлю ещё таймаут 3 секунды и вызову api когда придёт это время");', 0, 'key1');
1703 * Wrong\Task\Stackjs::add('_modal("#error", null, "error=Время пришло, api запрошено. Но я вижу там ещё кое что есть, через 4 сек, поставлю ещё таймаут на 4 секунды");', 3, 'key2');
1704 * Wrong\Task\Stackjs::add('$("#error").modal("hide");successToast("Окно скрыто. А что там, есть ещё что то в стеке?");', 7, 'key3');
1705 * Wrong\Task\Stackjs::add('dangerToast("Да, есть я в стеке! Но меня ждать ещё 23 секунды. Если дождёшься - я исполнюсь, а если перейдешь на другие страницы, то я всё равно исполнюсь, но ровно в свой срок!");', 30, 'key4');
1726 * В системе реализованы автоматически запускаемые cron задачи. Синтаксис расписаний задается так же как в любой unix системе(Минуты Часы Дни Месяцы Дни недели). Но в данном
1727 * случае это любые uri запросы на текущий домен, с любыми http методами(POST/PUT/GET/DELETE), с любыми устанавливаемыми заголовками(content-type), и данными тела запроса.
1728 * Крон задачи можно выполнять от имени других пользователей, если для него включена авторизация x-auth-token и его модель включена и является подчиненной вам по системному весу.
1732 * Вы можете включать и отключать определенный функционал проекта по заданному расписанию, или например менять шаблоны страниц с разными дизайнами. Для того, чтобы
1733 * посмотреть какие именно данные передать в теле запроса для cron задачи при исполнениии определённой модели, включите <a target="_blank" href="/docs/logs.html"><b>запись логов действий</b></a> для свой группы, выполните это действие вручную и посмотрите параметры тела нужного вам запроса в логах(id,action,table)
1748 * Кроме методов http вы можете поставить на cron любую cli команду, которая будет выполняться через exec
1749 *
1750 * @attention
1751 * Вы не должны давать права доступа на добавление cron задач кому попало. Это крайне небезопасно!
1752 *
1753 * @section cli_execute Выполение задач из CLI и по кнопке
1754 *
1755 * Вы можете запустить любую задачу из CLI перейдя в каталог public_html и вызвав
1756 *
1757 * @code
1758 * php -f cron.php 777
1759 * @endcode
1760 *
1761 * где 777 - id вашей задачи.
1762 *
1763 * Задачу можно запустить из админки по кнопке, даже если она отключена, она будет выполнена однократно, без учета натроенных для неё потоков.
1764 *
1765 * @section threads Многопоточность задач и контроль нагрузки
1766 *
1767 * Для каждой задачи вы можете устанавливать дополнительные настройки
1768 * - Минимум - минимальное количество запускаемых по расписанию задачей потоков её выполнения
1769 * - Максимум - масксимальное количество выполняемых потоков задачи при котором остальным запускам(потокам) этой задачи будет отказано
1770 * - Держать потоки - если установлено "да", то всегда будет поддерживаться минимальное установленное количество потоков, вне зависимости от периодичности запуска задачи. Каждый поток при запуске будет создавать необходимое количество дополнительных независимых форков.
1771 * - Предел нагрузки - устанавливается и рассчитывается в процентах от 1% до 1000% по формуле: текущий load average / кол-во логических ядер сервера * 100. Например 4/12*100 = 33% нагрузка. Где 4 это текущий la на 12 логических процессорах. 12 la из 12 ядер = 100% нагрузка сервера. 1000% - если сервер ещё работает, значит он крепыш. Если значение превышает установленное - в запуске очередного потока будет отказано. Вы устанавливаете лишь процент допустимой нагрузки при котором выполению данной задачи(любого её потока) будет отказано.
1772 *
1773 * Если вы не понимате зачем это вам, не настраивайте потоки, оставьте их по умолчанию и пользуйтесь cron задачами в их классическом варианте.
1774 * @note
1775 * При использовании большого количества потоков-программ в которых есть бд подключения, позботьтесь о настройках сервера бд <a target="_blank" href="https://mariadb.com/docs/server/ref/mdb/system-variables/max_connections/"><b>max_connections</b></a> и <a target="_blank" href="https://mariadb.com/docs/server/ref/mdb/system-variables/max_user_connections/"><b>max_user_connections</b></a>.
1779 * За техническую реализацию встроенного cron отвечает файл public_html/cron.php и класс Task/Cron.php
1780 *
1781 *
1782 *
1783 * Файл public_html/cron.php - запускает сам себя curl http запросом каждую минуту загружая метод load(), который в свою очередь посылает короткие независимые cli
1784 * запросы на выполнение задач к public_html/cron.php, в котором происходит выполение каждой задачи по отдельности.
1785 * Таким образом задачи полностью распарралеливаются и не зависят по времени выполнения друг от друга, ведь их может быть весьма большой стек и они могут
1786 * быть весьма ресурсоемкие.
1787 * Ежеминутно вызывается Wrong\Task\Cron::set_run_at() который проставляет всем задачам следующее(очередное) их время выполнения.
1788 *
1789 * Таким образом реализуется бесконечный ежеминутный цикл, с выполнением задач которые подходят под расписание.
1790 *
1791 * В классе Task/Cron.php реализован контроль за потоками и нагрузкой устанавливаемыми для каждой задачи.
1792 *
1793 * Когда автозапуск ещё не активирован - он активируется при помощи скрытой картинки,
1794 * которая инжектится в include/session.php при помощи <a target="_blank" href="/docs/stackjs.html"><b>js стека</b></a>(не всегда, а только если не активирован цикл автозапусков).
1795 * Это не совсем надежно, если на вашем сайте мало посетителей, т.к. иногда http запрос может не выполниться и цикл самозапуска public_html/cron.php прервется.
1796 * Бывает весьма редко, и если на сайте совсем никто не бывает, в основном цикл работает как часы.
1797 * Но чтобы повысить надежность, вы можете дополнительно поставить выполнение public_html/cron.php на свой системный cron раз минуту, вреда от этого не будет
1798 * (ведь дополнительные потоки блокируются), как скорее всего и пользы.
1818 * В проекте предусмотрено встроенное кеширование шаблонов, страниц и выборок. Об этом функционале рассказано в разделах выше.
1819 *
1820 * Вы можете также кешировать свои любые сущности-переменные(запросы к бд) при помощи класса Cache.php Он работает по аналогии Memcached и нейминг используемых методов идентичен.
1821 * Все объекты сохраняются в каталоге /temp/cache разбрасываются по уникальным каталогам для разгрузки фс, автоматически очищаются по истечении таймаутов.
1822 *
1823 * Разберем каждый метод в отдельности. Для того чтобы закешировать или вызвать из кеша любую сущность вы создаете экземпляр `new Cache()` вызывая конструктор:
1830 * Оба аргумента опциональны, но префикс желательно задать, он нужен для того чтобы не заморачиваться с уникальными ключами хранения. Например, вы работаете
1831 * с таблицей table_1 кешируя её записи, задайте префикс table_1 и в качестве уникальных ключей для записей вы сможете использовать например id, не беспокоясь что они пересекутся
1832 * с другими вашими объектами кеширования с такими же ключами. Т.е. это работает по той же аналогии, если бы вы в Memcached сохраняли данные на разных серверах.
1833 *
1834 * Для примера кешируем в файле public_html/cron.php объект строки из бд, чтобы не подключаться каждый раз в многопотоках к бд:
1835 * @code
1836 * $mem = new Wrong\Memory\Cache('cron');
1837 * if (!($row = $mem->get(777))) {
1838 * $row = Wrong\Models\Crontabs::find(777);
1839 * $mem->set(777, $row);
1840 * }
1841 * @endcode
1842 *
1843 * Метод `get()` получает сущность по определённому ключу, если она существует, метод `set()` сохраняет сущность в хранилище. В методе `set()`
1844 * вы можете задать таймаут актуальности кеша в секундах. По умолчанию используется таймаут установленный в константе `DEFAULT_TIMEOUT`
1845 *
1846 * Все доступные методы смотрите в описании класса <a target="_blank" href="/docs/class_wrong_1_1_memory_1_1_cache.html"><b>Cache</b></a>
1847 *
1848 * Очищать полностью системный кеш можно в окне настроек системы. Там же на кнопке выводится информация по общему занимаемому размеру каталогом /temp/cache
1849 *
1850 */
1851
1852
1853
1854
1855 /**
1856 * @page code_editor Встроенный редактор кода
1857 *
1858 * В системе предусмотрен встроенный редактор файлов обработчиков моделей с набором необходимых функций и поддержкой горячих клавиш. Безусловно это не полноценная IDE,
1859 * но редактор вполне сносный для мелких правок.
1860 *
1861 * Для редактирования кода модели пользователь должен обладать правами
1862 * групповых политик на изменение свойств модели - быть её владельцем или группа-владелец модели должна входить в подчиненные ему группы по системному весу. А также для него должен быть включен
1866 * Вы не должны предоставлять права доступа и включать данный функционал кому попало! Это свободная правка исполняемых .php файлов на вашем сервере.
1867 *
1868 * - Кнопка сохранения сохраняет файл и закрывает редактор в отличие от Ctrl + S.