С чего начать создание темы Cotonti
Популярные запросы: тема Omnis, плагин Pagelist, Cotonti 0.9.24.2, ЧПУ, Font Face
- 503 просмотра
- 20 января, 2023
- Обновлено: 16 октября, 2023
- admin
- Время чтения: 11 минут
После того, как вы развернули на удаленном или локальном сервере дистрибутив Cotonti, необходимо создать новую тему оформления. В эпоху Seditio ее называли скин (skin).
Как правило, тема для Котонти создается "с нуля". Для этого необходимо использовать коробочную тему Nemesis и произвести над ней некоторые действия, чтобы уникализировать ее и добавить немного необходимого функционала. Если в дальнейшем планируется разработка новых тем, рекомендую сохранить созданный каркас, например, на Github для того, чтобы дальнейшие процедуры клонирования занимали меньше времени и усилий.
Копирование и переименование
Откроем папку /themes. Здесь находятся две темы: Nemesis и Sumisun. Последнюю сразу удаляем -- это модельная тема давно не обновлялась и уже потеряла свою актуальность. Работать мы будем с темой Nemesis.
Прежде всего, придумаем название новой темы и соответствующим образом переименуем папку. Например, nemesis -> kudos. Аналогичным образом поступим с четырьмя "именными" файлами в папке:
- nemesis.php -> kudos.php
- nemesis.en.lang.php -> kudos.en.lang.php
- nemesis.rc.php -> kudos.rc.php
- nemesis.ru.lang.php -> kudos.ru.lang.php
Откроем файл kudos.php и отредактируем "шапку":
/* ==================== [BEGIN_COT_THEME] Name=Kudos Version=1.00b Schemes=default:Default [END_COT_THEME] ==================== */
Аналогичным образом поступим с файлами локализаций:
/** * User English Language File for Kudos theme * * @package Cotonti * @copyright (c) Cotonti Team * @license https://github.com/Cotonti/Cotonti/blob/master/License.txt */
и
/** * User Russian Language File for Kudos theme * * @package Cotonti * @copyright (c) Cotonti Team * @license https://github.com/Cotonti/Cotonti/blob/master/License.txt */
Также исправим header файла-загрузчика:
/** * JavaScript and CSS loader for Kudos theme * * @package Cotonti * @copyright (c) Cotonti Team * @license https://github.com/Cotonti/Cotonti/blob/master/License.txt */
Редактирование основных файлов
Очистим основные файлы для дальнейшей работы с ними. В первую очередь нас интересуют файлы TPL-шаблонов.
Правка файлов-шаблонов
Откроем header.tpl и удалим верстку ниже открывающего тега body. Тег HEADER_META_KEYWORDS нам тоже уже не понадобится. В итоге получим:
<!-- BEGIN: HEADER --> <!DOCTYPE html> <html lang="{PHP.cfg.defaultlang}"> <head> <title>{HEADER_TITLE}</title> <!-- IF {HEADER_META_DESCRIPTION} --> <meta name="description" content="{HEADER_META_DESCRIPTION}" /> <!-- ENDIF --> <!-- IF {HEADER_META_KEYWORDS} --> <meta name="keywords" content="{HEADER_META_KEYWORDS}" /> <!-- ENDIF --> <meta http-equiv="content-type" content="{HEADER_META_CONTENTTYPE}; charset=UTF-8" /> <meta name="generator" content="Cotonti https://www.cotonti.com" /> <link rel="canonical" href="{HEADER_CANONICAL_URL}" /> {HEADER_BASEHREF} {HEADER_HEAD} <link rel="shortcut icon" href="favicon.ico" /> <link rel="apple-touch-icon" href="apple-touch-icon.png" /> </head> <body> <header class="py-3 bg-secondary-subtle"> <div class="container"> <div class="row"> <div class="col"> <p class="text-center mb-0"> This is header <a href="{PHP.cfg.mainurl}">Back Home</a> </p> </div> </div> </div> </header> <!-- END: HEADER -->
Так же поступим с шаблоном footer.tpl:
<!-- BEGIN: FOOTER --> <footer class="py-3 bg-secondary-subtle"> <div class="container"> <div class="row"> <div class="col"> <p class="text-center mb-0"> This is footer </p> </div> </div> </div> </footer> {FOOTER_RC} </body> </html> <!-- END: FOOTER -->
Шаблон index.tpl очищаем полностью:
<!-- BEGIN: MAIN --> <main id="home" class="my-4"> <div class="container"> <div class="row"> <div class="col"> <p class="text-center mb-0"> This is index </p> </div> </div> </div> </main> <!-- END: MAIN -->
Пройдемся по остальным шаблонам. Самым трудоемким будет login.tpl. Будем считать, что в качестве CSS-фреймворка используется Bootstrap:
<!-- BEGIN: MAIN --> <main id="login" class="my-4"> <div class="container"> <div class="row"> <div class="mx-auto col col-lg-7 col-xl-6 col-xxl-5"> {FILE "{PHP.cfg.themes_dir}/{PHP.theme}/warnings.tpl"} <div class="title mb-3"> <ul class="breadcrumb"> <li class="breadcrumb-item"> <a href="{PHP.cfg.mainurl}" title="{PHP.L.Home}">{PHP.L.Home}</a> </li> <li class="breadcrumb-item">{PHP.L.Login}</li> </ul> <h1 class="display-6 lh-1">{USERS_AUTH_TITLE}</h1> </div> <!-- IF {PHP.usr.id} --> <div class="alert alert-info"> <p class="mb-0"> {PHP.L.users_loggedinas} <strong>{PHP.usr.name}</strong>. {PHP.L.users_logoutfirst} </p> </div> <div class="btn-group w-100"> <!-- IF {PHP.usr.maingrp} == 5 OR {PHP.usr.maingrp} == 6 --> <a class="btn btn-danger btn flex-fill" href="{PHP|cot_url('admin')}">{PHP.L.Adminpanel}</a> <!-- ENDIF --> <a class="btn btn-success btn flex-fill" href="{PHP|cot_url('users', 'm=profile')}">{PHP.L.Profile}</a> <a class="btn btn-success btn flex-fill" href="{PHP.sys.xk|cot_url('login','out=1&x=$this', '', 0, 1)}">{PHP.L.Logout}</a> </div> <!-- ELSE --> <form name="login" action="{USERS_AUTH_SEND}" method="post"> <div class="input-group mb-3"> <span class="input-group-text small w-25">{PHP.L.Name}:</span> {USERS_AUTH_USER} </div> <div class="input-group mb-3"> <span class="input-group-text small w-25">{PHP.L.Password}:</span> {USERS_AUTH_PASSWORD} <div class="input-group-text"> {USERS_AUTH_REMEMBER} </div> </div> <div class="btn-group w-100"> <button type="submit" name="rlogin" value="0" class="btn btn-success btn flex-fill">{PHP.L.Login}</button> <!-- IF !{PHP.cfg.users.disablereg} --> <a href="{PHP|cot_url('users','m=register')}" class="btn btn-primary btn flex-fill">{PHP.L.Registration}</a> <!-- ENDIF --> <a href="{PHP|cot_url('users','m=passrecover')}" class="btn btn-primary btn flex-fill">{PHP.L.users_lostpass}</a> </div> </form> <!-- ENDIF --> <!-- BEGIN: USERS_AUTH_MAINTENANCE --> <div class="alert alert-warning mb-0" role="alert"> <h4> {PHP.L.users_maintenance1} <!-- IF {PHP.cfg.maintenancereason} --> ({PHP.cfg.maintenancereason}) <!-- ENDIF --> </h4> <p class="mb-0"> {PHP.L.users_maintenance2} </p> </div> <!-- END: USERS_AUTH_MAINTENANCE --> </div> </div> </div> </main> <!-- END: MAIN -->
Следующий в очереди error.tpl:
<!-- BEGIN: MAIN --> <!DOCTYPE html> <html lang="{PHP.cfg.defaultlang}"> <head> <title>{MESSAGE_TITLE}</title> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta name="generator" content="Cotonti http://www.cotonti.com" /> {MESSAGE_BASEHREF} {MESSAGE_STYLESHEET} {MESSAGE_REDIRECT} </head> <body> <main id="error"> <div class="container"> <div class="row"> <div class="col"> <h1>{MESSAGE_TITLE}</h1> <div> {MESSAGE_BODY} </div> </div> </div> </div> </main> </body> </html> <!-- END: MAIN -->
За ним message.tpl:
<!-- BEGIN: MAIN --> <!-- IF !{AJAX_MODE} --> <main id="message" class="my-4"> <div class="container"> <div class="row"> <div class="col"> <div class="title p-0"> <h1 class="lh-1 text-center mb-3">{MESSAGE_TITLE}</h1> </div> <!-- ENDIF --> <div class="alert alert-warning mb-0" role="alert"> <p class="text-center mb-0"> {MESSAGE_BODY} </p> <div class="d-flex justify-content-center"> <!-- BEGIN: MESSAGE_CONFIRM --> <a id="confirmYes" href="{MESSAGE_CONFIRM_YES}" class="confirmButton btn btn-success btn-sm fw-bold mx-2 text-uppercase w-25">{PHP.L.Yes}</a> <a id="confirmNo" href="{MESSAGE_CONFIRM_NO}" class="confirmButton btn btn-danger btn-sm bold fw-bold text-uppercase mx-2 w-25">{PHP.L.No}</a> <!-- END: MESSAGE_CONFIRM --> </div> </div> <!-- IF !{AJAX_MODE} --> </div> </div> </div> </main> <!-- ENDIF --> <!-- END: MAIN -->
За ним plugin.tpl:
<!-- BEGIN: MAIN --> <div class="container"> <div class="row"> <div class="col"> <h1>{PLUGIN_TITLE}</h1> <div> {PLUGIN_BODY} </div> </div> </div> </div> <!-- END: MAIN -->
Далее poput.tpl:
<!-- BEGIN: MAIN --> <!DOCTYPE html> <html lang="{PHP.cfg.defaultlang}"> <head> {POPUP_METAS} {POPUP_JAVASCRIPT} <base href="{PHP.cfg.mainurl}/" /> <script type="text/javascript"> //<![CDATA[ function add(text) { insertText(document, "{POPUP_C2}", text); } //]]> </script> <link href="themes/{PHP.theme}/css/{PHP.scheme}.css" type="text/css" rel="stylesheet" /> </head> <body> {POPUP_BODY} </body> </html> <!-- END: MAIN -->
И в завершение warnings.tpl:
<!-- BEGIN: ERROR --> <div class="alert alert-danger" role="alert"> <h4>{PHP.L.Error}</h4> <p class="m-0"> <!-- BEGIN: ERROR_ROW --> <span>{ERROR_ROW_MSG}</span> <!-- END: ERROR_ROW --> </p> </div> <!-- END: ERROR --> <!-- BEGIN: WARNING --> <div class="alert alert-warning" role="alert"> <h4>{PHP.L.Warning}</h4> <p class="m-0"> <!-- BEGIN: WARNING_ROW --> <span>{WARNING_ROW_MSG}</span> <!-- END: WARNING_ROW --> </p> </div> <!-- END: WARNING --> <!-- BEGIN: DONE --> <div class="alert alert-success" role="alert"> <h4>{PHP.L.Done}</h4> <p class="m-0"> <!-- BEGIN: DONE_ROW --> <span>{DONE_ROW_MSG}</span> <!-- END: DONE_ROW --> </p> </div> <!-- END: DONE -->
Папки img и inc очищаем полностью, за исключением блокирующих просмотр файлов index.html.
Правка файлов стилей
Здесь будет немного радикальных перестановок. Настраивать стили будем под использование LESS -- динамического языка стилей, с помощью которого можно более гибко и просто работать со стилями нашего будущего веб-сайта:
- переименуем папку css в папку less,
- удалим файл reset.css (его функционал уже реализован в библиотеке Bootstrap),
- файл default.css очистим и переименуем в default.less,
- файл extras.css очистим и переименуем в responsive.css,
- файл modalbox.css переименуем в modalbox.less,
- создадим пустую папку css, в которой будут размещаться скомпилированные и минифицированные файлы стилей.
Настроим компилятор LESS и отразим наши исправления в файле-загрузчике:
<?php /** * JavaScript and CSS loader for Kudos Theme * * @package Kudos * @copyright (c) Cotonti Team * @license https://github.com/Cotonti/Cotonti/blob/master/License.txt */ defined('COT_CODE') or die('Wrong URL.'); $R['theme-version'] = 1; Resources::linkFileFooter($cfg['themes_dir'].'/'.$usr['theme'].'/css/modalbox.css'); Resources::linkFileFooter($cfg['themes_dir'].'/'.$usr['theme'].'/css/default.css?v='.$R['theme-version']); Resources::linkFileFooter($cfg['themes_dir'].'/'.$usr['theme'].'/css/responsive.css?v='.$R['theme-version']); Resources::linkFileFooter($cfg['themes_dir'].'/'.$usr['theme'].'/js/js.js');
Поясним такой выбор: стили в default.less прописываются м грузятся в первую очередь (mobile first), а в responsive.less мы будем списывать стили, использующие медиа-запросы (media queries). Переменная theme-version в загрузчике используется для кэширования стилей.
Чтобы использовать все возможности LESS и сделать работу со стилями еще более комфортной и продуктивной, создадим в папке less файл с переменными vars.less и подключим его в default.less и responsive.less директивой @import:
@import "vars.less";
Добавляем шаблоны модулей и плагинов
Прежде всего нам понадобятся четыре основных шаблона модуля Page, необходимые для просмотра, добавления и правки страницы, а также для просмотра раздела.
Шаблон page.tpl:
<!-- BEGIN: MAIN --> <main id="page_{PAGE_ID}" class="my-4"> <div class="container"> <div class="row"> <div class="col"> <div class="title mb-3"> <h1 class="lh-1">{PAGE_SHORTTITLE}</h1> {PAGE_CRUMBS} </div> </div> </div> <div class="row"> <div class="col"> <div class="textbox"> {PAGE_TEXT} </div> {PAGE_COMMENTS_DISPLAY} </div> </div> <!-- IF {PHP.usr.isadmin} --> {FILE "{PHP.cfg.themes_dir}/{PHP.theme}/inc/admin-page.tpl"} <!-- ENDIF --> </div> </main> <!-- END: MAIN -->
Шаблон page.add.tpl:
<!-- BEGIN: MAIN --> <main id="page_add" class="my-4"> <div class="container"> <div class="row"> <div class="col"> {FILE "themes/{PHP.theme}/warnings.tpl"} <h1 class="lh-1 mb-3">{PAGEADD_PAGETITLE}</h1> <form action="{PAGEADD_FORM_SEND}" enctype="multipart/form-data" method="post" name="pageform" class="mb-3"> <div class="table-responsive"> <table class="table table-striped m-0"> <tr> <td class="w-25">{PHP.L.Category}:</td> <td class="w-75">{PAGEADD_FORM_CAT}</td> </tr> <tr> <td>{PHP.L.Title}:</td> <td>{PAGEADD_FORM_TITLE}</td> </tr> <tr> <td>{PHP.L.Description}:</td> <td>{PAGEADD_FORM_DESC}</td> </tr> <tr> <td>{PHP.L.Author}:</td> <td>{PAGEADD_FORM_AUTHOR}</td> </tr> <tr> <td>{PHP.L.Begin}:</td> <td>{PAGEADD_FORM_BEGIN}</td> </tr> <tr> <td>{PHP.L.Expire}:</td> <td>{PAGEADD_FORM_EXPIRE}</td> </tr> <tr> <td>{PHP.L.Alias}:</td> <td>{PAGEADD_FORM_ALIAS}</td> </tr> <tr> <td>{PHP.L.page_metatitle}:</td> <td>{PAGEADD_FORM_METATITLE}</td> </tr> <tr> <td>{PHP.L.page_metadesc}:</td> <td>{PAGEADD_FORM_METADESC}</td> </tr> <!-- BEGIN: TAGS --> <tr> <td>{PAGEADD_TOP_TAGS}:</td> <td>{PAGEADD_FORM_TAGS} ({PAGEADD_TOP_TAGS_HINT})</td> </tr> <!-- END: TAGS --> <tr> <td>{PHP.L.Owner}:</td> <td>{PAGEADD_FORM_OWNER}</td> </tr> <tr> <td>{PHP.L.Parser}:</td> <td>{PAGEADD_FORM_PARSER}</td> </tr> <tr> <td colspan="2"> {PAGEADD_FORM_TEXT} </td> </tr> <tr> <td colspan="2"> <!-- IF {PHP.usr_can_publish} --> <button type="submit" name="rpagestate" value="0" class="btn btn-success btn-sm">{PHP.L.Publish}</button> <!-- ENDIF --> <button type="submit" name="rpagestate" value="2" class="submit btn btn-warning btn-sm">{PHP.L.Saveasdraft}</button> <button type="submit" name="rpagestate" value="1" class="btn btn-danger btn-sm">{PHP.L.Submitforapproval}</button> </td> </tr> </table> </div> </form> </div> </div> </div> </main>
Шаблон page.edit.tpl:
<!-- BEGIN: MAIN --> <main id="page_edit" class="my-4"> <div class="container"> <div class="row"> <div class="col"> {FILE "themes/{PHP.theme}/warnings.tpl"} <h1 class="lh-1 mb-3">{PAGEEDIT_PAGETITLE} #{PAGEEDIT_FORM_ID} ({PAGEEDIT_FORM_LOCALSTATUS})</h1> <form action="{PAGEEDIT_FORM_SEND}" enctype="multipart/form-data" method="post" name="pageform"> <div class="table-responsive"> <table class="table table-striped m-0"> <tr> <td class="w-25">{PHP.L.Category}:</td> <td class="w-75">{PAGEEDIT_FORM_CAT}</td> </tr> <tr> <td>{PHP.L.Title}:</td> <td>{PAGEEDIT_FORM_TITLE}</td> </tr> <tr> <td>{PHP.L.Description}:</td> <td>{PAGEEDIT_FORM_DESC}</td> </tr> <tr> <td>{PHP.L.Author}:</td> <td>{PAGEEDIT_FORM_AUTHOR}</td> </tr> <tr> <td>{PHP.L.Date}:</td> <td> {PAGEEDIT_FORM_DATE} <div class="form-group m0"> <div class="checkbox mb0"> <label class="checkbox"> <span class="title pt-1"> <input type="checkbox" value="1" name="rpagedatenow" class="me-2">{PHP.L.page_date_now} </span> </label> </div> </div> </td> </tr> <tr> <td>{PHP.L.Begin}:</td> <td>{PAGEEDIT_FORM_BEGIN}</td> </tr> <tr> <td>{PHP.L.Expire}:</td> <td>{PAGEEDIT_FORM_EXPIRE}</td> </tr> <tr> <td>{PHP.L.Alias}:</td> <td>{PAGEEDIT_FORM_ALIAS}</td> </tr> <tr> <td>{PHP.L.page_metatitle}:</td> <td>{PAGEEDIT_FORM_METATITLE}</td> </tr> <tr> <td>{PHP.L.page_metadesc}:</td> <td>{PAGEEDIT_FORM_METADESC}</td> </tr> <!-- BEGIN: TAGS --> <tr> <td>{PAGEEDIT_TOP_TAGS}:</td> <td>{PAGEEDIT_FORM_TAGS} ({PAGEEDIT_TOP_TAGS_HINT})</td> </tr> <!-- END: TAGS --> <!-- BEGIN: ADMIN --> <tr> <td>{PHP.L.Hits}:</td> <td>{PAGEEDIT_FORM_PAGECOUNT}</td> </tr> <tr> <td>{PHP.L.Owner}:</td> <td>{PAGEEDIT_FORM_OWNERID}</td> </tr> <!-- END: ADMIN --> <tr> <td>{PHP.L.Parser}:</td> <td>{PAGEEDIT_FORM_PARSER}</td> </tr> <tr> <td colspan="2"> {PAGEEDIT_FORM_TEXT} </td> </tr> <tr> <td>{PHP.L.page_deletepage}:</td> <td>{PAGEEDIT_FORM_DELETE}</td> </tr> <tr> <td colspan="2"> <!-- IF {PHP.usr_can_publish} --> <button type="submit" name="rpagestate" value="0" class="btn btn-success btn-sm">{PHP.L.Publish}</button> <!-- ENDIF --> <button type="submit" name="rpagestate" value="2" class="submit btn btn-warning btn-sm">{PHP.L.Saveasdraft}</button> <button type="submit" name="rpagestate" value="1" class="btn btn-danger btn-sm">{PHP.L.Submitforapproval}</button> </td> </tr> </table> </div> </form> </div> </div> </div> </main> <!-- END: MAIN -->
Шаблон page.list.tpl:
<!-- BEGIN: MAIN --> <main id="list_{PHP.cat.id}" class="my-4"> <div class="container"> <div class="row"> <div class="col"> <div class="title mb-3"> <h1 class="lh-1">{LIST_CATTITLE}</h1> {LIST_CRUMBS} </div> </div> </div> <div class="row"> <div class="col"> <!-- IF {LIST_ROW_NUM} --> <!-- BEGIN: LIST_ROW --> <article class="card py-2 px-3"> <a href="{LIST_ROW_URL}">{LIST_ROW_SHORTTITLE}</a> <ul class="list-inline mb-0"> <li class="list-inline-item"> {PHP.L.Views}: {LIST_ROW_COUNT} </li> <li class="list-inline-item"> {PHP.L.Date}: {LIST_ROW_DATE} </li> </ul> <!-- IF {LIST_ROW_DESC} --> <p class="mb-0"> {LIST_ROW_DESC} </p> <!-- ENDIF --> </article> <!-- END: LIST_ROW --> <!-- IF {LIST_TOP_PAGINATION} --> <nav id="pagination-container"> <ul class="pagination"> {LIST_TOP_PAGEPREV}{LIST_TOP_PAGINATION}{LIST_TOP_PAGENEXT} </ul> </nav> <!-- ENDIF --> <!-- ENDIF --> </div> </div> <!-- IF {PHP.usr.isadmin} --> {FILE "{PHP.cfg.themes_dir}/{PHP.theme}/inc/admin-list.tpl"} <!-- ENDIF --> </div> </main> <!-- END: MAIN -->
Чтобы задействовать функционал "хлебных крошек", загрузим и установим плагин Crumbs. В админке (раздел Конфигурация / Темы) включим опцию "Ссылка на главную страницу в навигационной цепочке".
В папке inc разместим файлы подгрузки блоков администрирования страниц и разделов:
admin-page.tpl:
<div id="adminblock" class="row"> <div class="col"> <ul class="list-inline mt-3 mb-0"> <li class="list-inline-item"> <a href="{PHP|cot_url('admin')}">{PHP.L.Adminpanel}</a> </li> <li class="list-inline-item">{PAGE_ADMIN_UNVALIDATE}</li> <li class="list-inline-item"> <a href="{PAGE_CAT|cot_url('page','m=add&c=$this')}">{PHP.L.page_addtitle}</a> </li> <li class="list-inline-item"> {PAGE_ADMIN_EDIT} </li> <li class="list-inline-item"> {PAGE_ADMIN_CLONE} </li> <li class="list-inline-item"> {PAGE_ADMIN_DELETE} </li> <!-- IF {PHP|cot_auth('plug', 'attach2', 'W')} --> <li class="list-inline-item"> {PAGE_ID|att_widget('page', $this, 'attach2.link')} </li> <!-- ENDIF --> <li class="list-inline-item"> {PHP.out.loginout} </li> </ul> </div> </div>
admin-list.tpl:
<div id="adminblock" class="row"> <div class="col"> <ul class="list-inline mt-3 mb-0"> <li class="list-inline-item"> <a href="{PHP|cot_url('admin')}">{PHP.L.Adminpanel}</a> </li> <li class="list-inline-item"> <a href="admin.php?m=structure&n=page&id={PHP.cat.id}&x={PHP.sys.xk}">Настройки раздела</a> </li> <li class="list-inline-item"> {LIST_SUBMITNEWPAGE} </li> <!-- IF {PHP|cot_auth('plug', 'attach2', 'W')} --> <li class="list-inline-item"> {PHP.cat.id|att_widget('list',$this,'attach2.link')} </li> <!-- ENDIF --> <li class="list-inline-item">{PHP.out.loginout}</li> </ul> </div> </div>
Для того, чтобы единообразить внешний вид кнопок, используем файл js.js, попутно проверив его загрузку:
$().ready(function() { if ($('div#adminblock').length) { $('div#adminblock ul > li > a').each(function() { $(this).addClass('btn btn-primary btn-sm'); }) } });
Теперь займемся шаблонами модуля users.
users.details.tpl:
<!-- BEGIN: MAIN --> <main id="users_details" class="my-4"> <div class="container"> <div class="row"> <div class="col"> {FILE "{PHP.cfg.themes_dir}/{PHP.cfg.defaulttheme}/warnings.tpl"} <div class="title mb-3"> <h1 class="lh-1"> {PHP.L.User} {USERS_DETAILS_NICKNAME} <!-- BEGIN: USERS_DETAILS_ADMIN --> [ {USERS_DETAILS_ADMIN_EDIT} ] <!-- END: USERS_DETAILS_ADMIN --> </h1> <ul class="breadcrumb"> <li class="breadcrumb-item"> <a href="{PHP.cfg.mainurl}" title="{PHP.L.Home}">{PHP.L.Home}</a> </li> <li class="breadcrumb-item"> <a href="{PHP|cot_url('users')}">{PHP.L.Users}</a> </li> <li class="breadcrumb-item"> {USERS_DETAILS_NICKNAME} </li> </ul> </div> <div class="table-responsive"> <table class="table table-striped table-hover mb-0"> <!-- IF {PHP.cot_modules.pm} --> <tr> <td>{PHP.L.users_sendpm}:</td> <td>{USERS_DETAILS_PM}</td> </tr> <!-- ENDIF --> <tr> <td class="w-25">{PHP.L.Maingroup}:</td> <td class="w-75">{USERS_DETAILS_MAINGRP}</td> </tr> <tr> <td>{PHP.L.Groupsmembership}:</td> <td>{USERS_DETAILS_GROUPS}</td> </tr> <tr> <td>{PHP.L.Country}:</td> <td>{USERS_DETAILS_COUNTRYFLAG} {USERS_DETAILS_COUNTRY}</td> </tr> <tr> <td>{PHP.L.Timezone}:</td> <td>{USERS_DETAILS_TIMEZONE}</td> </tr> <tr> <td>{PHP.L.Birthdate}:</td> <td>{USERS_DETAILS_BIRTHDATE}</td> </tr> <tr> <td>{PHP.L.Age}:</td> <td>{USERS_DETAILS_AGE}</td> </tr> <tr> <td>{PHP.L.Gender}:</td> <td>{USERS_DETAILS_GENDER}</td> </tr> <tr> <td>{PHP.L.Signature}:</td> <td>{USERS_DETAILS_TEXT}</td> </tr> <tr> <td>{PHP.L.Registered}:</td> <td>{USERS_DETAILS_REGDATE}</td> </tr> <!-- IF {PHP.usr.isadmin} --> <tr> <td> Last seen: </td> <td> {USERS_DETAILS_LASTLOG_STAMP|cot_date('H:i j F Y', $this)} </td> </tr> <!-- ENDIF --> <!-- IF {USERS_DETAILS_AVATAR} --> <tr> <td>{PHP.L.Avatar}:</td> <td>{USERS_DETAILS_AVATAR}</td> </tr> <!-- ENDIF --> <!-- IF {USERS_DETAILS_PHOTO} --> <tr> <td>{PHP.L.Photo}:</td> <td>{USERS_DETAILS_PHOTO}</td> </tr> <!-- ENDIF --> </table> </div> </div> </div> </div> </main> <!-- END: MAIN -->
users.edit.tpl:
<!-- BEGIN: MAIN --> <main id="users_edit" class="my-4"> <div class="container"> <div class="row"> <div class="col"> {FILE "{PHP.cfg.themes_dir}/{PHP.cfg.defaulttheme}/warnings.tpl"} <div class="title mb-3"> <h1 class="lh-1">{PHP.L.Edit} {PHP.urr.user_name}</h1> <ul class="breadcrumb"> <li class="breadcrumb-item"> <a href="{PHP.cfg.mainurl}" title="{PHP.L.Home}">{PHP.L.Home}</a> </li> <li class="breadcrumb-item"> <a href="{PHP|cot_url('users')}">{PHP.L.Users}</a> </li> <li class="breadcrumb-item"> {PHP.urr.user_name} </li> </ul> </div> <form action="{USERS_EDIT_SEND}" method="post" name="useredit" enctype="multipart/form-data"> <input type="hidden" name="id" value="{USERS_EDIT_ID}" /> <div class="table-responsive"> <table class="table table-striped table-hover mb-0"> <tr> <td class="w-25">{PHP.L.users_id}:</td> <td class="w-75">#{USERS_EDIT_ID}</td> </tr> <tr> <td>{PHP.L.Username}:</td> <td>{USERS_EDIT_NAME}</td> </tr> <tr> <td>{PHP.L.Groupsmembership}:</td> <td> {PHP.L.Maingroup}: {USERS_EDIT_GROUPS} </td> </tr> <tr> <td>{PHP.L.Country}:</td> <td>{USERS_EDIT_COUNTRY}</td> </tr> <tr> <td>{PHP.L.Timezone}:</td> <td>{USERS_EDIT_TIMEZONE}</td> </tr> <tr> <td>{PHP.L.Theme}:</td> <td>{USERS_EDIT_THEME}</td> </tr> <tr> <td>{PHP.L.Language}:</td> <td>{USERS_EDIT_LANG}</td> </tr> <!-- IF {USERS_EDIT_AVATAR} --> <tr> <td>{PHP.L.Avatar}:</td> <td>{USERS_EDIT_AVATAR}</td> </tr> <!-- ENDIF --> <!-- IF {USERS_EDIT_SIGNATURE} --> <tr> <td>{PHP.L.Signature}:</td> <td>{USERS_EDIT_SIGNATURE}</td> </tr> <!-- ENDIF --> <!-- IF {USERS_EDIT_PHOTO} --> <tr> <td>{PHP.L.Photo}:</td> <td>{USERS_EDIT_PHOTO}</td> </tr> <!-- ENDIF --> <tr> <td>{PHP.L.users_newpass}:</td> <td> {USERS_EDIT_NEWPASS} <p class="small">{PHP.L.users_newpasshint1}</p> </td> </tr> <tr> <td>{PHP.L.Email}:</td> <td>{USERS_EDIT_EMAIL}</td> </tr> <tr> <td>{PHP.L.users_hideemail}:</td> <td>{USERS_EDIT_HIDEEMAIL}</td> </tr> <!-- IF {PHP.cot_modules.pm} --> <tr> <td>{PHP.L.users_pmnotify}:</td> <td>{USERS_EDIT_PMNOTIFY}<br />{PHP.themelang.usersedit.PMnotifyhint}</td> </tr> <!-- ENDIF --> <tr> <td>{PHP.L.Birthdate}:</td> <td>{USERS_EDIT_BIRTHDATE}</td> </tr> <tr> <td>{PHP.L.Gender}:</td> <td>{USERS_EDIT_GENDER}</td> </tr> <tr> <td>{PHP.L.Signature}:</td> <td>{USERS_EDIT_TEXT}</td> </tr> <tr> <td>{PHP.L.Registered}:</td> <td>{USERS_EDIT_REGDATE}</td> </tr> <tr> <td>{PHP.L.Lastlogged}:</td> <td>{USERS_EDIT_LASTLOG}</td> </tr> <tr> <td>{PHP.L.users_lastip}:</td> <td>{USERS_EDIT_LASTIP}</td> </tr> <tr> <td>{PHP.L.users_logcounter}:</td> <td>{USERS_EDIT_LOGCOUNT}</td> </tr> <tr> <td>{PHP.L.users_deleteuser}:</td> <td>{USERS_EDIT_DELETE}</td> </tr> <tr> <td colspan="2"> <button type="submit" class="btn btn-primary">{PHP.L.Update}</button> </td> </tr> </table> </div> </form> </div> </div> </div> </main> <!-- END: MAIN -->
users.passrecover.tpl:
<!-- BEGIN: MAIN --> <main id="users_passrecover" class="my-4"> <div class="container"> <div class="row"> <div class="mx-auto col col-lg-7 col-xl-6 col-xxl-5"> {FILE "{PHP.cfg.themes_dir}/{PHP.theme}/warnings.tpl"} <div class="title mb-3"> <h1 class="lh-1">{PHP.L.pasrec_title}</h1> <ul class="breadcrumb"> <li class="breadcrumb-item"> <a href="{PHP.cfg.mainurl}">{PHP.cfg.maintitle}</a> </li> <li class="breadcrumb-item">{PHP.L.pasrec_title}</li> </ul> </div> <!-- IF {PHP.msg} == 'request' --> <div class="alert alert-info mb-0"> <p class="mb-0"> {PHP.L.pasrec_mailsent} </p> </div> <!-- ENDIF --> <!-- IF {PHP.msg} == 'auth' --> <div class="alert alert-info mb-0"> <p class="mb-0"> {PHP.L.pasrec_mailsent2} </p> </div> <!-- ENDIF --> <!-- IF !{PHP.msg} --> <form role="form" name="reqauth" action="{PASSRECOVER_URL_FORM}" method="post" class=""> <ol> <li>{PHP.L.pasrec_explain1}</li> <li>{PHP.L.pasrec_explain2}</li> <li>{PHP.L.pasrec_explain3}</li> </ol> <div class="input-group mb-3"> <input type="text" class="form-control" name="email" /> <button type="submit" class="btn btn-primary">{PHP.L.pasrec_request}</button> </div> <div class="alert alert-info mb-0"> {PHP.L.pasrec_explain4} </div> </form> <!-- ENDIF --> </div> </div> </div> </main> <!-- END: MAIN -->
users.profile.tpl:
<!-- BEGIN: MAIN --> <main id="users_profile" class="mb-4"> <div class="container"> <div class="row"> <div class="col"> {FILE "{PHP.cfg.themes_dir}/{PHP.theme}/warnings.tpl"} <div class="title mb-3"> <h1 class="lh-1">{PHP.L.Profile}</h1> <ul class="breadcrumb"> <li class="breadcrumb-item"> <a href="{PHP.cfg.mainurl}" title="{PHP.L.Home}">{PHP.L.Home}</a> </li> <li class="breadcrumb-item"> {USERS_PROFILE_TITLE} </li> </ul> </div> <form action="{USERS_PROFILE_FORM_SEND}" method="post" enctype="multipart/form-data" name="profile"> <input type="hidden" name="userid" value="{USERS_PROFILE_ID}" /> <div class="table-responsive"> <table class="table table-striped table-hover mb-0"> <tr> <td class="w-25">{PHP.L.Username}:</td> <td class="w-75">{USERS_PROFILE_NAME}</td> </tr> <tr> <td>{PHP.L.Groupsmembership}:</td> <td>{USERS_PROFILE_GROUPS}</td> </tr> <tr> <td>{PHP.L.Registered}:</td> <td>{USERS_PROFILE_REGDATE}</td> </tr> <!-- BEGIN: USERS_PROFILE_EMAILCHANGE --> <tr> <td>{PHP.L.Email}:</td> <td> {PHP.L.Email}:<br />{USERS_PROFILE_EMAIL} <!-- BEGIN: USERS_PROFILE_EMAILPROTECTION --> <script type="text/javascript"> $(document).ready(function(){ $("#emailnotes").hide(); $("#emailtd").click(function(){$("#emailnotes").slideDown();}); }); </script> <div> {PHP.themelang.usersprofile.Emailpassword}:<br />{USERS_PROFILE_EMAILPASS} </div> <div>{PHP.themelang.usersprofile.Emailnotes}</div> <!-- END: USERS_PROFILE_EMAILPROTECTION --> </td> </tr> <!-- END: USERS_PROFILE_EMAILCHANGE --> <tr> <td>{PHP.L.users_hideemail}:</td> <td>{USERS_PROFILE_HIDEEMAIL}</td> </tr> <!-- IF {PHP.cot_modules.pm} --> <tr> <td>{PHP.L.users_pmnotify}:</td> <td> {USERS_PROFILE_PMNOTIFY} <p class="small mb-0">{PHP.L.users_pmnotifyhint}</p> </td> </tr> <!-- ENDIF --> <tr> <td>{PHP.L.Theme}:</td> <td>{USERS_PROFILE_THEME}</td> </tr> <tr> <td>{PHP.L.Language}:</td> <td>{USERS_PROFILE_LANG}</td> </tr> <tr> <td>{PHP.L.Country}:</td> <td>{USERS_PROFILE_COUNTRY}</td> </tr> <tr> <td>{PHP.L.Timezone}:</td> <td>{USERS_PROFILE_TIMEZONE}</td> </tr> <tr> <td>{PHP.L.Birthdate}:</td> <td>{USERS_PROFILE_BIRTHDATE} </td> </tr> <tr> <td>{PHP.L.Gender}:</td> <td>{USERS_PROFILE_GENDER}</td> </tr> <!-- IF {USERS_PROFILE_AVATAR} --> <tr> <td>{PHP.L.Avatar}:</td> <td>{USERS_PROFILE_AVATAR}</td> </tr> <!-- ENDIF --> <!-- IF {USERS_PROFILE_PHOTO} --> <tr> <td>{PHP.L.Photo}:</td> <td>{USERS_PROFILE_PHOTO}</td> </tr> <!-- ENDIF --> <tr> <td>{PHP.L.Signature}:</td> <td>{USERS_PROFILE_TEXT}</td> </tr> <tr> <td> {PHP.L.users_newpass}: <p class="small">{PHP.L.users_newpasshint1}</p> </td> <td> {USERS_PROFILE_OLDPASS} <p class="small mb-2">{PHP.L.users_oldpasshint}</p> {USERS_PROFILE_NEWPASS1} {USERS_PROFILE_NEWPASS2} <p class="small m-0">{PHP.L.users_newpasshint2}</p> </td> </tr> <tr> <td colspan="2"> <button type="submit" class="btn btn-primary">{PHP.L.Update}</button> </td> </tr> </table> </div> </form> </div> </div> </div> </main> <!-- END: MAIN -->
users.register.tpl:
<!-- BEGIN: MAIN --> <main id="users_register" class="my-4"> <div class="container"> <div class="row"> <div class="mx-auto col col-lg-7 col-xl-6 col-xxl-5"> {FILE "{PHP.cfg.themes_dir}/{PHP.theme}/warnings.tpl"} <div class="title mb-3"> <h1 class="lh-1">{USERS_REGISTER_TITLE}</h1> <ul class="breadcrumb"> <li class="breadcrumb-item"> <a href="{PHP.cfg.mainurl}">{PHP.cfg.maintitle}</a> </li> <li class="breadcrumb-item"> {PHP.L.Register} </li> </ul> </div> <form name="login" action="{USERS_REGISTER_SEND}" method="post" enctype="multipart/form-data"> <div class="mb-3"> <label class="control-label" for="">{PHP.L.Username}</label> {USERS_REGISTER_USER} </div> <div class="mb-3"> <label class="control-label" for="">{PHP.L.users_validemail} {PHP.L.users_validemailhint}</label> {USERS_REGISTER_EMAIL} </div> <div class="mb-3"> <label class="control-label" for="">{PHP.L.Password}</label> {USERS_REGISTER_PASSWORD} </div> <div class="mb-3"> <label class="control-label" for="">{PHP.L.users_confirmpass}</label> {USERS_REGISTER_PASSWORDREPEAT} </div> <div class="mb-3"> <label class="control-label" for="">{USERS_REGISTER_VERIFYIMG}</label> {USERS_REGISTER_VERIFYINPUT} </div> <div class="btn-group w-100"> <button type="submit" class="btn btn-success btn flex-fill">{PHP.L.Registration}</button> <a href="{PHP|cot_url('login')}" class="btn btn-primary btn flex-fill">{PHP.L.Login}</a> <a href="{PHP|cot_url('users','m=passrecover')}" class="btn btn-primary btn flex-fill">{PHP.L.users_lostpass}</a> </div> </form> </div> </div> </div> </main> <!-- END: MAIN -->
users.tpl:
<!-- BEGIN: MAIN --> <main id="users" class="my-4"> <div class="container"> <div class="row"> <div class="col"> {FILE "{PHP.cfg.themes_dir}/{PHP.cfg.defaulttheme}/warnings.tpl"} <div class="title mb-3"> <h1 class="lh-1">{PHP.L.Users}</h1> <ul class="breadcrumb"> <li class="breadcrumb-item"> <a href="{PHP.cfg.mainurl}" title="{PHP.L.Home}">{PHP.L.Home}</a> </li> <li class="breadcrumb-item"> {PHP.L.Users} </li> </ul> </div> <div class="table-responsive"> <table class="table table-striped table-hover"> <thead> <tr class="text-center"> <th class="w-5">{USERS_TOP_PM}</th> <th class="w-25">{USERS_TOP_NAME}</th> <th class="w-25">{USERS_TOP_GRPTITLE}</th> <th class="w-20">{USERS_TOP_GRPLEVEL}</th> <th class="w-25">{USERS_TOP_REGDATE}</th> </tr> </thead> <tbody> <!-- BEGIN: USERS_ROW --> <tr class="text-center"> <td>{USERS_ROW_PM}</td> <td>{USERS_ROW_NAME} {USERS_ROW_TAG}</td> <td>{USERS_ROW_MAINGRP}</td> <td>{USERS_ROW_MAINGRPSTARS}</td> <td>{USERS_ROW_REGDATE}</td> </tr> <!-- END: USERS_ROW --> </tbody> </table> <p class="text-center mb-0"> {PHP.L.users_usersperpage}: {USERS_TOP_MAXPERPAGE} | {PHP.L.users_usersinthissection}: {USERS_TOP_TOTALUSERS} </p> <!-- IF {USERS_TOP_PAGNAV} --> <nav id="pagination-container"> <ul class="pagination"> {USERS_TOP_PAGEPREV}{USERS_TOP_PAGNAV}{USERS_TOP_PAGENEXT} </ul> </nav> <!-- ENDIF --> </div> </div> </div> </div> </main> <!-- END: MAIN -->
Для того, чтобы вся эта история заработала, подключим Bootstrap. Например, при помощи нашего плагина Bootstrap.
Практически все стало красиво. Исключение -- некоторые локации модуля users:
- users.details, users.edit, users.profile -- список групп пользователя выводится в контейнере ul;
- login -- теги USERS_AUTH_USER, USERS_AUTH_PASSWORD и USERS_AUTH_REMEMBER не имеют классов Bootstrap;
- register -- теги USERS_REGISTER_USER, USERS_REGISTER_EMAIL, USERS_REGISTER_PASSWORD, USERS_REGISTER_PASSWORDREPEAT, USERS_REGISTER_VERIFYINPUT не имеют классов Bootstrap.
Можно исправить эти проблемы двумя способами:
- используя jQuery;
- переназначив соответствующие ресурсы миниплагином.
Поскольку jQuery мы уже использовали выше, выберем второй способ и создадим плагин Kudos с тремя частями и кастомным файлом ресурсов:
kudos.resources.php:
<?php /* * Redefine some Users module resources */ $R['users_code_grplist_begin'] = '<ul class="list-unstyled mb-0">'; $R['form_guest_remember'] = '<input type="checkbox" name="rremember" class="form-check-input" title="'.$L['users_rememberme'].'" />'; $R['form_guest_remember_forced'] = '<input type="checkbox" name="rremember" class="form-check-input" title="'.$L['users_rememberme'].'" checked="checked" disabled="disabled" />';
kudos.login.php:
<?php /* ==================== [BEGIN_COT_EXT] Hooks=users.auth.tags [END_COT_EXT] ==================== */ /** * Customization for Kudos theme * * @package Kudos * @copyright (c) Cotonti Team * @license https://github.com/Cotonti/Cotonti/blob/master/License.txt */ defined('COT_CODE') or die('Wrong URL'); require_once cot_incfile('kudos', 'plug', 'resources'); $t->assign(array( 'USERS_AUTH_USER' => cot_inputbox('text', 'rusername', $rusername, array('size' => '12', 'maxlength' => '100', 'class' => 'form-control')), 'USERS_AUTH_PASSWORD' => cot_inputbox('password', 'rpassword', '', array('size' => '12', 'maxlength' => '32', 'class' => 'form-control')), 'USERS_AUTH_REMEMBER' => Cot::$cfg['forcerememberme'] ? Cot::$R['form_guest_remember_forced'] : Cot::$R['form_guest_remember'] ));
kudos.register.php:
<?php /* ==================== [BEGIN_COT_EXT] Hooks=users.register.tags [END_COT_EXT] ==================== */ /** * Customization for Kudos theme * * @package Kudos * @copyright (c) Cotonti Team * @license https://github.com/Cotonti/Cotonti/blob/master/License.txt */ defined('COT_CODE') or die('Wrong URL'); $t->assign(array( 'USERS_REGISTER_USER' => cot_inputbox('text', 'rusername', $ruser['user_name'], array('size' => 24, 'maxlength' => 100, 'class' => 'form-control')), 'USERS_REGISTER_EMAIL' => cot_inputbox('text', 'ruseremail', $ruser['user_email'], array('size' => 24, 'maxlength' => 64, 'class' => 'form-control')), 'USERS_REGISTER_PASSWORD' => cot_inputbox('password', 'rpassword1', '', array('size' => 12, 'maxlength' => 32, 'class' => 'form-control')), 'USERS_REGISTER_PASSWORDREPEAT' => cot_inputbox('password', 'rpassword2', '', array('size' => 12, 'maxlength' => 32, 'class' => 'form-control')), 'USERS_REGISTER_VERIFYINPUT' => cot_inputbox('text', 'rverify', '', array('size' => 10, 'maxlength' => 20, 'class' => 'form-control')), ));
kudos.users.php:
<?php /* ==================== [BEGIN_COT_EXT] Hooks=users.edit.first,users.details.first,users.profile.first [END_COT_EXT] ==================== */ /** * Customization for Kudos theme * * @package Kudos * @copyright (c) Cotonti Team * @license https://github.com/Cotonti/Cotonti/blob/master/License.txt */ defined('COT_CODE') or die('Wrong URL'); require_once cot_incfile('kudos', 'plug', 'resources');
Ничего сверхъестественного плагин не делает, всего лишь переназначает необходимые нам ресурсные строки, добавляя им необходимые CSS-классы.
Заключение
Мы создали стартовую (модельную) тему для движка Cotonti. Фактически, это тема-пустышка, адаптированная под Bootstrap, в которой проработаны все служебные шаблоны. Добавьте к ней тему админки Yukon, и у Вас будет готовая сборка для создания нового проекта "с нуля".
Для кастомизации данной стартовой темы нам понадобилось написать небольшой плагин Kudos и установить готовые плагины для загрузки Bootstrap и генерации "хлебных крошек". Дальнейшая разработка сайта, возможно, потребует большего их количества, однако этого не стоит опасаться: Cotonti именно так и создана. Тема универсальной "заготовки" будет развиваться и дальше.
Спасибо за статью. Берём на вооружение :)
Новый комментарий