Wiki » История » Версия 2
  Dmitry Chernyak, 17.02.2021 17:02 
  
| 1 | 1 | Dmitry Chernyak | h1. Django-gentelella | 
|---|---|---|---|
| 2 | 1 | Dmitry Chernyak | |
| 3 | 1 | Dmitry Chernyak | *Django Gentelella* - это интерфейс для применения темы Gentelella https://colorlib.com/polygon/gentelella/ на проекты, написанные на основе фреймворка Django. Данный интерфейс состоит из собственных форм, базовых шаблонов страниц, к которым применена тема Gentelella, а также содержит набор кастомных элементов страниц (таблицы, модальные формы, элементы рендера и прочее). | 
| 4 | 1 | Dmitry Chernyak | |
| 5 | 1 | Dmitry Chernyak | h2. Установка | 
| 6 | 1 | Dmitry Chernyak | |
| 7 | 1 | Dmitry Chernyak | Для установки пакета необходимо склонировать репозиторий в директорию проекта | 
| 8 | 1 | Dmitry Chernyak | |
| 9 | 1 | Dmitry Chernyak | <pre> | 
| 10 | 2 | Dmitry Chernyak | git clone git://dev.skycover.ru:/django_gentelella.git | 
| 11 | 1 | Dmitry Chernyak | </pre> | 
| 12 | 1 | Dmitry Chernyak | |
| 13 | 1 | Dmitry Chernyak | добавить django_gentelella в список установленных приложений в @settings.py@ | 
| 14 | 1 | Dmitry Chernyak | |
| 15 | 1 | Dmitry Chernyak | <pre> | 
| 16 | 1 | Dmitry Chernyak | <...> | 
| 17 | 1 | Dmitry Chernyak | INSTALLED_APPS = [ | 
| 18 | 1 | Dmitry Chernyak | 'django.contrib.admin', | 
| 19 | 1 | Dmitry Chernyak | <...>, | 
| 20 | 1 | Dmitry Chernyak | 'django_gentelella', | 
| 21 | 1 | Dmitry Chernyak | ] | 
| 22 | 1 | Dmitry Chernyak | <...> | 
| 23 | 1 | Dmitry Chernyak | </pre> | 
| 24 | 1 | Dmitry Chernyak | |
| 25 | 1 | Dmitry Chernyak | h2. Использование | 
| 26 | 1 | Dmitry Chernyak | |
| 27 | 1 | Dmitry Chernyak | Для использования темы Gentelella в шаблонах следует встраивать их в базовый: | 
| 28 | 1 | Dmitry Chernyak | |
| 29 | 1 | Dmitry Chernyak | <pre> | 
| 30 | 1 | Dmitry Chernyak | {% extends 'django_gentelella/templates/base.html' %} | 
| 31 | 1 | Dmitry Chernyak | </pre> | 
| 32 | 1 | Dmitry Chernyak | |
| 33 | 1 | Dmitry Chernyak | Для использования компонентов необходимо импортировать django_gentelella в шаблон: | 
| 34 | 1 | Dmitry Chernyak | |
| 35 | 1 | Dmitry Chernyak | <pre> | 
| 36 | 1 | Dmitry Chernyak | {% load django_gentelella %} | 
| 37 | 1 | Dmitry Chernyak | </pre> | 
| 38 | 1 | Dmitry Chernyak | |
| 39 | 1 | Dmitry Chernyak | h2. Компоненты | 
| 40 | 1 | Dmitry Chernyak | |
| 41 | 1 | Dmitry Chernyak | h3. Модели | 
| 42 | 1 | Dmitry Chernyak | |
| 43 | 1 | Dmitry Chernyak | *IntervalField* — поле для хранения интервалов дат в форме количества секунд. Наследуется от BigInt, в качестве аргументов может принимать verbose_name, минимальное и максимальное значение, формат. | 
| 44 | 1 | Dmitry Chernyak | |
| 45 | 1 | Dmitry Chernyak | h3. Формы | 
| 46 | 1 | Dmitry Chernyak | |
| 47 | 1 | Dmitry Chernyak | *ModelForm* - заменяет используемые поля форм Django на соответствующие поля Django Gentelella | 
| 48 | 1 | Dmitry Chernyak | *Form* - оформляет используемые поля как form-control | 
| 49 | 1 | Dmitry Chernyak | *ROFormMixin* - миксин для установки статуса Read Only для полей. В качестве аргументов получает: | 
| 50 | 1 | Dmitry Chernyak | ## *read_only* - список полей для установки на них статуса | 
| 51 | 1 | Dmitry Chernyak | ## *read_only_exclude* - список полей, для которых статус устанавливать не надо (в случае, если read_only == '__all__') | 
| 52 | 1 | Dmitry Chernyak | |
| 53 | 1 | Dmitry Chernyak | *IntervalFormField* - поле для указания интервала времени в определённое количество часов, минут и секунд. | 
| 54 | 1 | Dmitry Chernyak | *IntervalWidget* - виджет для поля указания интервала. | 
| 55 | 1 | Dmitry Chernyak | !intfield.png! | 
| 56 | 1 | Dmitry Chernyak | |
| 57 | 1 | Dmitry Chernyak | *Select2* - виджет для оформления полей выбора элемента из множества. | 
| 58 | 1 | Dmitry Chernyak | !chfield.png! | 
| 59 | 1 | Dmitry Chernyak | |
| 60 | 1 | Dmitry Chernyak | *Select2Multiple* - виджет для оформления полей множественного выбора. | 
| 61 | 1 | Dmitry Chernyak | !multiselect.png! | 
| 62 | 1 | Dmitry Chernyak | |
| 63 | 1 | Dmitry Chernyak | *CheckBox* - виджет для оформления поля выбора как группы кнопок. | 
| 64 | 1 | Dmitry Chernyak | !check.png! | 
| 65 | 1 | Dmitry Chernyak | |
| 66 | 1 | Dmitry Chernyak | Также предоставлены собственные замены для стандартных виджетов: | 
| 67 | 1 | Dmitry Chernyak | |
| 68 | 1 | Dmitry Chernyak | |_. Виджет Django |_. Виджет DG | | 
| 69 | 1 | Dmitry Chernyak | | NumberInput | IntegerInput | | 
| 70 | 1 | Dmitry Chernyak | | TextInput | TextInput | | 
| 71 | 1 | Dmitry Chernyak | | Textarea | TextArea | | 
| 72 | 1 | Dmitry Chernyak | | Select | Select2 | | 
| 73 | 1 | Dmitry Chernyak | | SelectMultiple | Select2Multiple | | 
| 74 | 1 | Dmitry Chernyak | | DateTimeInput | DateTime | | 
| 75 | 1 | Dmitry Chernyak | | DateInput | Date | | 
| 76 | 1 | Dmitry Chernyak | | TimeInput | TimeInput | | 
| 77 | 1 | Dmitry Chernyak | | CheckboxInput | SwitheryInput | | 
| 78 | 1 | Dmitry Chernyak | |
| 79 | 1 | Dmitry Chernyak | *Пример использования компонентов Django Gentelella в формах* | 
| 80 | 1 | Dmitry Chernyak | |
| 81 | 1 | Dmitry Chernyak | <pre> | 
| 82 | 1 | Dmitry Chernyak | from django import forms | 
| 83 | 1 | Dmitry Chernyak | from django_gentelella.forms import Select2, Select2Multiple, Form, DateTimeRangeField, DateTimeRangeWidget, IntervalFormField, SwitheryInput, CheckBox, Form, Date | 
| 84 | 1 | Dmitry Chernyak | |
| 85 | 1 | Dmitry Chernyak | class TestForm(Form): | 
| 86 | 1 | Dmitry Chernyak | choices = ( | 
| 87 | 1 | Dmitry Chernyak |         ('spam', 'Spam'), | 
| 88 | 1 | Dmitry Chernyak |         ('eggs', 'Eggs'), | 
| 89 | 1 | Dmitry Chernyak |         ('spanish_inquisition', 'Spanish Inquisition'), | 
| 90 | 1 | Dmitry Chernyak | ) | 
| 91 | 1 | Dmitry Chernyak | interval = IntervalFormField(label='Interval field', required=False) | 
| 92 | 1 | Dmitry Chernyak | choice = forms.ChoiceField(label='Choice field', choices=choices, widget=Select2, required=False) | 
| 93 | 1 | Dmitry Chernyak | multiselect = forms.MultipleChoiceField(label='Multiple choices', choices=choices, widget=Select2Multiple, required=False) | 
| 94 | 1 | Dmitry Chernyak | swithery = forms.BooleanField(label='Swithery input', widget=SwitheryInput, required=False) | 
| 95 | 1 | Dmitry Chernyak | radiobox = forms.ChoiceField(label='Checkboxes', choices=choices, widget=CheckBox, required=False) | 
| 96 | 1 | Dmitry Chernyak | date = forms.DateField(label='Date', widget=Date) | 
| 97 | 1 | Dmitry Chernyak | </pre> | 
| 98 | 1 | Dmitry Chernyak | |
| 99 | 1 | Dmitry Chernyak | h3. Виды | 
| 100 | 1 | Dmitry Chernyak | |
| 101 | 1 | Dmitry Chernyak | *BaseView* - базовый вид, от которого необходимо наследовать прочие виды проекта. | 
| 102 | 1 | Dmitry Chernyak | |
| 103 | 1 | Dmitry Chernyak | Переменные класса: | 
| 104 | 1 | Dmitry Chernyak | # title - заголовок страницы. По-умолчанию: 'Base View'; | 
| 105 | 1 | Dmitry Chernyak | # title_icon - иконка страницы в формате названия из пакета Font Awesome. По умолчанию: 'bus'; | 
| 106 | 1 | Dmitry Chernyak | # position - идентификатор пункта меню, считающегося выбранным, когда страница открыта. По умолчанию: None; | 
| 107 | 1 | Dmitry Chernyak | # breadcrumbs - значение, отвечающее за отображение "хлебных крошек" вверху страницы. По умолчанию: False; | 
| 108 | 1 | Dmitry Chernyak | # menu - меню в левой колонке базового шаблона (см. "Редактирование меню"). По умолчанию: OrderedDict(); | 
| 109 | 1 | Dmitry Chernyak | # template_name - имя шаблона, использующегося в базовом виде. По умолчанию: 'django_gentelella/base.html'. | 
| 110 | 1 | Dmitry Chernyak | |
| 111 | 1 | Dmitry Chernyak | *GentelellaCreateView* - собственный вариант вида CreateView из django.views.generic. | 
| 112 | 1 | Dmitry Chernyak | |
| 113 | 1 | Dmitry Chernyak | Переменные класса: | 
| 114 | 1 | Dmitry Chernyak | # menu - меню в левой колонке базового шаблона (см. "Редактирование меню"). По умолчанию: NotImplemented; | 
| 115 | 1 | Dmitry Chernyak | # title - заголовок страницы. По умолчанию: NotImplemented; | 
| 116 | 1 | Dmitry Chernyak | # template_name - имя шаблона, использующегося в виде. По умолчанию: NotImplemented. | 
| 117 | 1 | Dmitry Chernyak | # model - модель, объект которой создаётся в данном виде. По умолчанию: NotImplementedError | 
| 118 | 1 | Dmitry Chernyak | # form_class - использующаяся на странице форма. По умолчанию: NotImplemented. | 
| 119 | 1 | Dmitry Chernyak | # success_url - ссылка, на которую будет совершён переход при успешном создании объекта. По умолчанию: NotImplemented. | 
| 120 | 1 | Dmitry Chernyak | |
| 121 | 1 | Dmitry Chernyak | Используется точно так же, как и CreateView из django.views.generic. | 
| 122 | 1 | Dmitry Chernyak | |
| 123 | 1 | Dmitry Chernyak | h3. Тэги шаблонов | 
| 124 | 1 | Dmitry Chernyak | |
| 125 | 1 | Dmitry Chernyak | *custom_table* - отрисовывает таблицу по заданным данным. В качестве аргументов принимает список данных и перечень полей (каждое поле записано как отдельный аргумент формата field__name="Name"). Первая строка внутри тэга - то, что следует отображать при отсутствии данных, далее - данные для тех полей с особым форматированием: | 
| 126 | 1 | Dmitry Chernyak | |
| 127 | 1 | Dmitry Chernyak | <pre> | 
| 128 | 1 | Dmitry Chernyak | {% field 'name' %} | 
| 129 | 1 | Dmitry Chernyak | <b>{{ obj.name }}</b> | 
| 130 | 1 | Dmitry Chernyak | </pre> | 
| 131 | 1 | Dmitry Chernyak | |
| 132 | 1 | Dmitry Chernyak | Также поля можно подавать через объект OrderedDict, где ключи - это идентификаторы полей, а значения - заголовки: | 
| 133 | 1 | Dmitry Chernyak | |
| 134 | 1 | Dmitry Chernyak | <pre> | 
| 135 | 1 | Dmitry Chernyak | fields = OrderedDict(( | 
| 136 | 1 | Dmitry Chernyak |     ('first', 'First Thing'), | 
| 137 | 1 | Dmitry Chernyak |     ('second', 'Second Thing'), | 
| 138 | 1 | Dmitry Chernyak |     ('third', 'Third Thing'), | 
| 139 | 1 | Dmitry Chernyak |     ('pseudo', 'Pseudothing') | 
| 140 | 1 | Dmitry Chernyak | )) | 
| 141 | 1 | Dmitry Chernyak | </pre> | 
| 142 | 1 | Dmitry Chernyak | |
| 143 | 1 | Dmitry Chernyak | Закрывающий тэг: @{% end_custom_table %}@ | 
| 144 | 1 | Dmitry Chernyak | |
| 145 | 1 | Dmitry Chernyak | Данные для таблицы подаются в виде списка словарей, где ключи - это названия полей таблицы. | 
| 146 | 1 | Dmitry Chernyak | |
| 147 | 1 | Dmitry Chernyak | Пример: | 
| 148 | 1 | Dmitry Chernyak | |
| 149 | 1 | Dmitry Chernyak | <pre> | 
| 150 | 1 | Dmitry Chernyak | table_contents = [{ | 
| 151 | 1 | Dmitry Chernyak | 'first': 'Spam', | 
| 152 | 1 | Dmitry Chernyak | 'second': 'Eggs', | 
| 153 | 1 | Dmitry Chernyak | 'third': 'Spanish Inquisition', | 
| 154 | 1 | Dmitry Chernyak | }, | 
| 155 | 1 | Dmitry Chernyak | { | 
| 156 | 1 | Dmitry Chernyak | 'first': 'Something Completely Different', | 
| 157 | 1 | Dmitry Chernyak | 'second': 'Ni!', | 
| 158 | 1 | Dmitry Chernyak | 'third': 'What is your favourite colour?' | 
| 159 | 1 | Dmitry Chernyak | } | 
| 160 | 1 | Dmitry Chernyak | ] | 
| 161 | 1 | Dmitry Chernyak | </pre> | 
| 162 | 1 | Dmitry Chernyak | |
| 163 | 1 | Dmitry Chernyak | <pre> | 
| 164 | 1 | Dmitry Chernyak | {% custom_table table_contents field__first='First Thing' field__second='Second Thing' field__third='Third Thing' field__pseudo='Pseudothing' %} | 
| 165 | 1 | Dmitry Chernyak | Nothing's going on here, citizen. | 
| 166 | 1 | Dmitry Chernyak |   {% field 'pseudo' %} | 
| 167 | 1 | Dmitry Chernyak | Beware the simulacrum! | 
| 168 | 1 | Dmitry Chernyak | {% end_custom_table %} | 
| 169 | 1 | Dmitry Chernyak | </pre> | 
| 170 | 1 | Dmitry Chernyak | |
| 171 | 1 | Dmitry Chernyak | <pre> | 
| 172 | 1 | Dmitry Chernyak | {% custom_table table_contents fields %} | 
| 173 | 1 | Dmitry Chernyak | Nothing's going on here, citizen. | 
| 174 | 1 | Dmitry Chernyak |   {% field 'pseudo' %} | 
| 175 | 1 | Dmitry Chernyak | Beware the simulacrum! | 
| 176 | 1 | Dmitry Chernyak | {% end_custom_table %} | 
| 177 | 1 | Dmitry Chernyak | </pre> | 
| 178 | 1 | Dmitry Chernyak | |
| 179 | 1 | Dmitry Chernyak | !table.png! | 
| 180 | 1 | Dmitry Chernyak | |
| 181 | 1 | Dmitry Chernyak | *xpanel* - div-контейнер для элементов в виде панели. В качестве обязательного аргумента принимает заголовок. Пример: | 
| 182 | 1 | Dmitry Chernyak | |
| 183 | 1 | Dmitry Chernyak | <pre> | 
| 184 | 1 | Dmitry Chernyak | {% xpanel 'Заголовок' %} | 
| 185 | 1 | Dmitry Chernyak | содержимое | 
| 186 | 1 | Dmitry Chernyak | {% end_xpanel %} | 
| 187 | 1 | Dmitry Chernyak | </pre> | 
| 188 | 1 | Dmitry Chernyak | |
| 189 | 1 | Dmitry Chernyak | !panel.png! | 
| 190 | 1 | Dmitry Chernyak | |
| 191 | 1 | Dmitry Chernyak | *modal* - контейнер для элементов в виде вызываемого модального окна. Принимает следующие аргументы: | 
| 192 | 1 | Dmitry Chernyak | ## Заголовок | 
| 193 | 1 | Dmitry Chernyak | ## size - размер окна | 
| 194 | 1 | Dmitry Chernyak | ## id_ - идентификатор окна | 
| 195 | 1 | Dmitry Chernyak | |
| 196 | 1 | Dmitry Chernyak | Пример использования: | 
| 197 | 1 | Dmitry Chernyak | |
| 198 | 1 | Dmitry Chernyak | <pre> | 
| 199 | 1 | Dmitry Chernyak | <button class='btn btn-primary' data-toggle="modal" data-target="#modal">Check messages</button> | 
| 200 | 1 | Dmitry Chernyak | |
| 201 | 1 | Dmitry Chernyak | {% modal 'Заголовок' size='xl' id_='modal' %} | 
| 202 | 1 | Dmitry Chernyak | Содержимое | 
| 203 | 1 | Dmitry Chernyak | {% end_modal %} | 
| 204 | 1 | Dmitry Chernyak | </pre> | 
| 205 | 1 | Dmitry Chernyak | |
| 206 | 1 | Dmitry Chernyak | !modal.png! | 
| 207 | 1 | Dmitry Chernyak | |
| 208 | 1 | Dmitry Chernyak | h3. Шаблоны | 
| 209 | 1 | Dmitry Chernyak | |
| 210 | 1 | Dmitry Chernyak | *base* - базовый шаблон, в который встраиваются все страницы сайта в теме. | 
| 211 | 1 | Dmitry Chernyak | *footer* - футер страницы. | 
| 212 | 1 | Dmitry Chernyak | *login* - стандартная страница логина. | 
| 213 | 1 | Dmitry Chernyak | |
| 214 | 1 | Dmitry Chernyak | h3. Редактирование бокового меню | 
| 215 | 1 | Dmitry Chernyak | |
| 216 | 1 | Dmitry Chernyak | Для того чтобы сделать кастомное боковое меню, следует создать собственный вид, наследующийся от BaseView из django_gentelella, в котором указать переменную menu с желаемым меню, сформированным в формате OrderedDict. Каждый элемент словаря должен иметь в качестве ключа уникальный идентификатор, а в качестве значения - кортеж или список со следующими данными: | 
| 217 | 1 | Dmitry Chernyak | # Название пункта | 
| 218 | 1 | Dmitry Chernyak | # Ссылка, на которую ведёт пункт | 
| 219 | 1 | Dmitry Chernyak | # Описание пункта | 
| 220 | 1 | Dmitry Chernyak | # Права, которыми должен обладать пользователь для отображения или сокрытия пункта. Например ['right_to_view|1'], где слева от | указано название права, справа - показывать или, наоборот, скрывать пункт при наличии такого права у пользователя. | 
| 221 | 1 | Dmitry Chernyak | # Дочерние пункты | 
| 222 | 1 | Dmitry Chernyak | |
| 223 | 1 | Dmitry Chernyak | Пример использования: | 
| 224 | 1 | Dmitry Chernyak | |
| 225 | 1 | Dmitry Chernyak | <pre> | 
| 226 | 1 | Dmitry Chernyak | MENU = OrderedDict(( | 
| 227 | 1 | Dmitry Chernyak | # название, ссылка, описание, 'права|показывать[01]', иконка | 
| 228 | 1 | Dmitry Chernyak |     ('main_page', ( | 
| 229 | 1 | Dmitry Chernyak |         'Страница', reverse_lazy(''), | 
| 230 | 1 | Dmitry Chernyak | 'Это страница, на ней написано', [], | 
| 231 | 1 | Dmitry Chernyak | 'credit-card', OrderedDict(( | 
| 232 | 1 | Dmitry Chernyak |             ('ultra_main_page', ( | 
| 233 | 1 | Dmitry Chernyak |                 'Та же страница', reverse_lazy(''), | 
| 234 | 1 | Dmitry Chernyak | 'Дочерний пункт меню', [], | 
| 235 | 1 | Dmitry Chernyak | 'credit-card', OrderedDict(( | 
| 236 | 1 | Dmitry Chernyak | |
| 237 | 1 | Dmitry Chernyak | )) | 
| 238 | 1 | Dmitry Chernyak | )), | 
| 239 | 1 | Dmitry Chernyak | )) | 
| 240 | 1 | Dmitry Chernyak | )), | 
| 241 | 1 | Dmitry Chernyak |     ('submain_page', ( | 
| 242 | 1 | Dmitry Chernyak |         'Та же страница', reverse_lazy('test'), | 
| 243 | 1 | Dmitry Chernyak | 'Это та же самая страница', [], | 
| 244 | 1 | Dmitry Chernyak | 'credit-card', OrderedDict(()) | 
| 245 | 1 | Dmitry Chernyak | )), | 
| 246 | 1 | Dmitry Chernyak | )) | 
| 247 | 1 | Dmitry Chernyak | |
| 248 | 1 | Dmitry Chernyak | |
| 249 | 1 | Dmitry Chernyak | class MyBaseView(BaseView): | 
| 250 | 1 | Dmitry Chernyak | menu = MENU | 
| 251 | 1 | Dmitry Chernyak | template_name = 'django_gentelella/base.html' | 
| 252 | 1 | Dmitry Chernyak | title_icon = 'balance-scale' | 
| 253 | 1 | Dmitry Chernyak | title = 'DG Test' | 
| 254 | 1 | Dmitry Chernyak | permission_required = () | 
| 255 | 1 | Dmitry Chernyak | |
| 256 | 1 | Dmitry Chernyak | def get(self, request, *args, **kwargs): | 
| 257 | 1 | Dmitry Chernyak | return super().get(request, *args, **kwargs) | 
| 258 | 1 | Dmitry Chernyak | </pre> | 
| 259 | 1 | Dmitry Chernyak | |
| 260 | 1 | Dmitry Chernyak | Создаваемые впоследствие виды наследоваться от MyBaseView. | 
| 261 | 1 | Dmitry Chernyak | |
| 262 | 1 | Dmitry Chernyak | !menu.png! | 
| 263 | 1 | Dmitry Chernyak | |
| 264 | 1 | Dmitry Chernyak | h3. Тэги | 
| 265 | 1 | Dmitry Chernyak | |
| 266 | 1 | Dmitry Chernyak | Тэги - это шаблоны компонентов, которые можно импортировать в собственный шаблон для стандартизированного рендера отдельных его частей. | 
| 267 | 1 | Dmitry Chernyak | |
| 268 | 1 | Dmitry Chernyak | **form_template** - шаблон для рендера форм. | 
| 269 | 1 | Dmitry Chernyak | Пример использования: | 
| 270 | 1 | Dmitry Chernyak | |
| 271 | 1 | Dmitry Chernyak | <pre> | 
| 272 | 1 | Dmitry Chernyak | {% include 'django_gentelella/tags/form_template.html' with form=form %} | 
| 273 | 1 | Dmitry Chernyak | </pre> | 
| 274 | 1 | Dmitry Chernyak | |
| 275 | 1 | Dmitry Chernyak | !form_t.png! | 
| 276 | 1 | Dmitry Chernyak | |
| 277 | 1 | Dmitry Chernyak | h3. Скрипты | 
| 278 | 1 | Dmitry Chernyak | |
| 279 | 1 | Dmitry Chernyak | *native_message* - скрипт на JS для создания браузерных уведомлений. Принимает следующие аргументы: | 
| 280 | 1 | Dmitry Chernyak | # title - заголовок; | 
| 281 | 1 | Dmitry Chernyak | # message - сообщение; | 
| 282 | 1 | Dmitry Chernyak | # options - опции уведомления. | 
| 283 | 1 | Dmitry Chernyak | |
| 284 | 1 | Dmitry Chernyak | h2. Дополнительные материалы | 
| 285 | 1 | Dmitry Chernyak | |
| 286 | 1 | Dmitry Chernyak | "Gentelella Alela!":https://colorlib.com/polygon/gentelella/index.html - демо-страница темы Gentelella. | 
 
  
  