Мой Telegram-канал
DGrid: обработка onDoubleClick на строке
Разбор
Наткнулся на StackOverflow на вопрос, который давно мучил и меня самого: как в dgrid обрабатывать двойной клик по строке? Встроенного обработчика события onDblClick
в классах библиотеки не было, поэтому я решил посмотреть, можно ли что-то сделать с помощью подручных средств.
Посмотрев код библиотеки, я обнаружил, что у класса Grid
есть методы row()
и cell()
, возвращающие информацию о строке и ячейке соответственно при передаче в качестве параметра события, которое произошло в таблице. Главная сложность была в определении того, на что ставить обработчик. Понятно, что для тысяч строк нельзя вешать onClick()
на каждую строку. В этом случае на контейнер вешают один единственный обработчик, который смотрит на то, от кого пришло событие, и уже тогда что-то делает. Всё оказалось не так уж сложно.
define([
"dojo/_base/declare",
"dojo/_base/lang",
"dgrid/Grid"
], function(declare, lang, Grid){
return declare(Grid, {
onDblClick: function(){
/* Функция-пустышка, которая должна быть заменена на настоящую в классах,
унаследованных от этого. Тут должна быть какая-то работа с переданным
объектом, состоящим из трех полей:
- id - id записи, если есть
- data - полный объект из связанного с таблицей Store
- element - DOM-элемент, на котором случился двойной клик */
},
_onDblClick: function(event){
/* Вызываем onDblClick, передав "чистый" объект с параметрами. Обычно тут
разработчики Dojo Toolkit рекомендуют дополнительно вызывать on.emit с
необходимыми параметрами. На случай, если кто-то ещё должен слушать
события, происходящие в таблице, и как-то на них реагировать */
this.onDblClick(this.row(event));
}, // _onDblClick
postCreate: function(){
// Установка обработчиков на построенную DOM-модель таблицы
this.inherited(arguments);
this.on(".dgrid-content .dgrid-row:dblclick", lang.hitch(this, "_onDblClick"));
} // postCreate
}); // declare
}); //define
Можно пойти дальше и создать класс-расширение, раз уж в Dojo реализовано множественное наследование. Если что, это рекомендуемый способ расширения функционала классов и каноничный подход к написанию плагинов.
Комментарии в исходном коде
Устал искать, в каком месте удалил лишнюю скобку. Устал определять по отступу, где закончился цикл или if
. Поэтому сейчас пишу код примерно так:
define([
/*
* Главное меню приложения.
* Обработчики событий должны устанавливаться в классе Application.
*/
"dojo/_base/array", // array.forEach в одном месте
"dojo/_base/declare",
"dijit/DropDownMenu",
"dijit/MenuBar",
"dijit/MenuBarItem",
"dijit/MenuItem",
"dijit/PopupMenuBarItem"
], function(
array,
declare,
DropDownMenu
MenuBar,
MenuBarItem,
MenuItem,
PopupMenuBarItem
){
// Создает пункт главного меню с DropDown'ом
function createMenuBarItem(label){
return new PopupMenuBarItem({
label: label,
popup: new DropDownMenu({})
});
} // createMenuBarItem
return declare(MenuBar, {
buildRendering: function(){
this.inherited(arguments);
// Какой-то код...
this.fileMenu = createMenuBarItem("Файл");
this.editMenu = createMenuBarItem("Правка");
this.dictsMenu = createMenuBarItem("Справочники");
array.forEach([
this.fileMenu,
this.editMenu,
this.dictsMenu
], this.addChild, this); // forEach
} // buildRendering
}); // declare
}); // define
Если вкратце, то я просто пишу после закрывающих скобок, что именно они закрывают - конец цикла, тела функции, массива и т. д. Подсмотрел такое в исходном коде одного сайта, там верстальщик в комментариях указывал границы логических блоков - хедер, футер, тело страницы, панель навигации и т. д.
Debian Buster, SecureBoot и Nvidia RTX 2070
Условия задачи
Дано:
- Ошибка Xorg в логах:
May 7 16:21:42 darktower kernel: [ 5.572986] Lockdown: ioperm is restricted; see https://wiki.debian.org/SecureBoot
- Debian 10 Booster с подключенными репозиториями
non-free
иcontrib
. - Права
root
- Видеокарта NVidia RTX 2070
- Компьютер с включенной в настройках материнской платы опцией
SecureBoot (EFI)
Проблема в том, что в ряде случаев видеокарта может не запускаться, потому что драйверы не подписаны. Для подписания драйверов следует выполнить действия, описанные в Wiki Debian / SecureBoot.
В моём случае пришлось выполнить следующую последовательность действий:
Установка драйвера Nvidia, исходных кодов ядра и mokutil
apt-get update
apt-get install nvidia-driver nvidia-xconfig linux-headers-amd64 mokutil
Пакет | Предназначение |
---|---|
nvidia-driver | Проприетарный драйвер Nvidia с поддержкой нужных моделей видеокарт. |
nvidia-xconfig | Утилита конфигурирования параметров Xorg для поддержки им нужных драйверов. |
linux-headers-amd64 | Исходные коды установленного ядра. |
mokutil | Утилита для работы с ключами EFI |
Генерация ключа EFI, его установка и подписывание драйверов.
Теперь действуем ровно так, как написано в руководстве Debian'а:
# Генерация ключей и импорт в EFI
cd /root
openssl req -new -x509 -newkey rsa:2048 -keyout MOK.priv -outform DER -out MOK.der -nodes -days 36500 -subj "/CN=NVidia RTX 2070 key/"
mokutil --import MOK.der
# Подписывание драйверов Nvidia
cd /lib/modules/4.19.0-8-amd64/updates/dkms/
/usr/lib/linux-kbuild-4.19/scripts/sign-file sha256 /root/MOK.priv /root/MOK.der nvidia-current-drm.ko
/usr/lib/linux-kbuild-4.19/scripts/sign-file sha256 /root/MOK.priv /root/MOK.der nvidia-current.ko
/usr/lib/linux-kbuild-4.19/scripts/sign-file sha256 /root/MOK.priv /root/MOK.der nvidia-current-modeset.ko
/usr/lib/linux-kbuild-4.19/scripts/sign-file sha256 /root/MOK.priv /root/MOK.der nvidia-current-uvm.ko
# Перенастройка Xorg под nvidia
cd /etc/X11
rm xorg.conf
nvidia-xconfig
Следует обратить внимание на путь, в котором расположены исходные модули ядра nvidia и скрипт для подписывания драйверов - они могут быть другими.
Перезагрузка
При перезагруке EFI выдаст сообщение, что появился запрос на установку собственного ключа цифровой подписи. Принимаем этот ключ и устанавливаем. После этого компьютер должен перезагрузиться и запустить установленный Linux, используя подписанные данным ключом драйверы. После этого запустится Xorg и всё будет нормально работать.
Автоматизация подписывания ключей.
Можно автоматизировать процесс по максимуму, если ручной выбор исходников ядра переключить на работу с результатами команды uname -r
и поместить всё в файл скрипта:
#!/bin/sh
SIGN=/usr/lib/linux-kbuild-4.19/scripts/sign-file
MOK=/root/MOK.priv
DER=/root/MOK.der
cd /lib/modules/$(uname -r)/updates/dkms/
$SIGN sha256 $MOK $DER nvidia-current-drm.ko
$SIGN sha256 $MOK $DER nvidia-current.ko
$SIGN sha256 $MOK $DER nvidia-current-modeset.ko
$SIGN sha256 $MOK $DER nvidia-current-uvm.ko
Его нужно запускать всякий раз при обновлении драйвера Nvidia или ядра.
Настройка Exim4 / Dovecot в Astra Linux SE 1.5
Настройка почты в Astra Linux 1.5 ALD
Исходные данные и задачи
Необходимо настроить сервер электронной почты, работающий в рамках домена Astra Linux 1.5 SE. Исходные данные:
Параметр | Значение |
---|---|
Имя домена | local.net |
FQDN сервера | server.local.net |
IP-адрес сервера | 192.168.0.1 |
Маска сети: | 255.255.255.0 |
Настройка сети
Отключаем графический менеджер управления сетевыми подключениями wicd
.
chkconfig wicd off
Прописываем настройки сети в файле /etc/network/interfaces
auto lo
iface lo inet loopback
auto eth0
allow-hotplug eth0
iface eth0 inet static
address 192.168.0.1
netmask 255.255.255.0
gateway 192.168.0.1
network 192.168.0.0
broadcast 192.168.0.255
dns-nameservers 192.168.0.1
dns-search local.lan
После этого нужно выполнить перезапуск демона сети:
service networking stop && service networking start
В документации к демону networking написано, что команда restart
является устаревшей и работает не так, как ожидается.
Необходимо в файле /etc/hosts
прописать соответствие FQDN сервера и его IP-адреса, а также задать hostname
127.0.0.1 localhost
192.168.0.1 server.local.net server
192.168.0.101 arm01.local.net arm01
192.168.0.102 arm02.local.net arm02
192.168.0.103 arm03.local.net arm03
В файле /etc/hostname
меняем краткое имя компьютера server
на полное server.local.net
, а также изменяем текущее значение системной переменной:
echo server.local.net > /etc/hostname
hostname server.local.net
Инициализация домена
Действуем согласно официальной инструкции от РусБиТех:
ald-init init
Появится уведомление о том, что команда init
уничтожит всю базу данных LDAP и Kerberos, будут остановлены и перезапущены службы, и упадет с неба большая звезда, горящая подобно светильнику, и падет на третью часть рек и на источники вод... Отвечаем утвердительно
Попросят ввести пароли для доступа к базе данных Kerberos и пароль администратора Astra Linux Directory. Лучше записать куда-нибудь, потому что понадобится ещё не раз.
Спустя какое-то время на экране появится сообщение:
Astra Linux Directory сконфигурирована.
Сервер ALD активен.
Клиент ALD включен.
Astra Linux Directory сервер успешно инициализирован.
Установка и настройка почтовых сервисов.
Необходимо установить три пакета:
Пакет | Описание |
---|---|
exim4-daemon-heavy | Передает сообщения и умеет работать с мандатными метками. |
dovecot-imapd | Умеет отдавать почту клиентам. |
dovecot-gssapi | Умеет принимать авторизацию на сервере через Kerberos (ALD). |
Отдельно отмечу, что сразу же после установки нужно будет запустить конфигурирование exim4
.
aptitude install exim4-daemon-heavy dovecot-imapd dovecot-gssapi -y
dpkg-reconfigure exim4-config
Процесс настройки exim4
не очень сложный, но нужно правильно ответить на несколько вопросов.
Вопрос | Ответ |
---|---|
Общий тип почтовой конфигурации | интернет-сайт; приём и отправка почты напрямую, используя SMTP |
Почтовое имя системы: | local.net |
IP-адреса, с которых следует ожидать входящие соединения SMTP: | 192.168.0.1 |
Другие места назначения, для которых должна приниматься почта: | local.net |
Домены, для которых доступна релейная передача почты: | Оставляем пустым |
Машины, для которых доступна релейная передача почты: | Оставляем пустым |
Сокращать количество DNS-запросов до минимума (дозвон по требованию)? | Нет |
Метод доставки локальной почты: | Maildir формат в домашнем каталоге |
Разделить конфигурацию на маленькие файлы? | Да |
Теперь нужно удалить из каталога /var/mail
файл с именем пользователя, созданного при установке системы (у меня обычно administrator
).
rm /var/mail/administrator
Создадим сервисы ALD для работы с почтой:
ald-admin service-add imap/server.local.lan
ald-admin sgroup-svc-add imap/server.local.lan --sgroup=mac
ald-admin sgroup-svc-add imap/server.local.lan --sgroup=mail
ald-admin service-add smtp/server.local.lan
ald-admin sgroup-svc-add smtp/server.local.lan --sgroup=mac
ald-admin sgroup-svc-add smtp/server.local.lan --sgroup=mail
ald-client update-svc-keytab imap/server.local.lan --ktfile="/var/lib/dovecot/dovecot.keytab"
ald-client update-svc-keytab smtp/server.local.lan --ktfile="/var/lib/dovecot/dovecot.keytab"
Указывая имя сервиса, важно помнить, что после слеша указывается то же самое имя сервера, что и в файле /etc/hostname
. Если даже в настройках Bind вы потом укажете, что mail.local.net
и smtp.local.net
являются всего лишь псевдонимами для server.local.net
, почта работать не будет, потому что Kerberos очень строго проверяет этот параметр.
После создания файла ключей нужно предоставить доступ к нему для dovecot
:
setfacl -m u:dovecot:x /var/lib/dovecot
setfacl -m u:dovecot:r /var/lib/dovecot/dovecot.keytab
Dovecot
Выполняем настройку dovecot
. Если убрать все комментарии из файлов, то получится примерно следующее (в тех файлах, где меняли):
!include_try /usr/share/dovecot/protocols.d/*.protocol
protocols = imap
listen = 192.168.0.1
dict {}
!include conf.d/*.conf
!include_try local.conf
disable_plaintext_auth = yes
auth_mechanisms = gssapi
auth_gssapi_hostname = server.local.lan
auth_krb5_keytab = /var/lib/dovecot/dovecot.keytab
!include auth-system.conf.ext
Я не могу в настройку SSL. Отключаем:
ssl = no
ssl_cert = </etc/dovecot/dovecot.pem
ssl_key = </dovecot/private/dovecot.pem
Маленькая настройка для корректной работы с почтой - добавляем в файле /etc/dovecot/conf.d/10-master.conf
в секцию service auth
:
# ...
service auth {
unix_listener auth-client {
mode = 0600
user = Debian-exim
}
# ...
}
Немного упрощаем себе и пользователям жизнь, автоматически создавая на сервере каталоги под входящие, отправленные и удаленные письма при первой авторизации (параметр auto = subscribe
).
namespace inbox {
mailbox Drafts {
special_use = \Drafts
auto = subscribe
}
mailbox Junk {
special_use = \Junk
}
mailbox Trash {
special_use = \Trash
auto = subscribe
}
mailbox Sent {
special_use = \Sent
auto = subscribe
}
}
Расположение каталогов внутри /var/mail/%username%
можно изменить, сделав более удобным, если в /etc/dovecot/conf.d/10-mail.conf
немного изменить параметр mail_location
:
mail_location = maildir:/var/mail/%u:LAYOUT=fs
Exim4
Теперь нужно сделать пару дополнительных настроек Exim4.
dovecot_gssapi:
driver = dovecot
public_name = GSSAPI
server_socket = /var/run/dovecot/auth-client
server_set_id = $auth1 # В конце цифра один, а не маленькая эль
Файл /etc/exim4/conf.d/acl/30_exim4-config_check_rcpt
очень большой, поэтому не привожу его полностью. В самом начале нужно добавить в секцию acl_check_rcpt
4 строки, чтобы выглядело примерно так:
acl_check_rcpt:
deny
message = "Auth required"
hosts = *:+relay_from_hosts
!authenticated = *
accept:
hosts = :
control = dkim_disable_verify
# и так далее
Проверка работоспособности
Заходим от имени пользователя ALD. Запускаем Thunderbird и создаем новую учетную запись. В параметрах IMAP- и SMTP-серверов пишем одно и то же - server.local.net
. При нажатии кнопки "Проверка параметров" параметры авторизации должны измениться на Kerberos/GSSAPI. Сохраняем, перезапускаем почтовый клиент. После второго запуска должно загрузиться дерево папок с сервера. Пишем письмо сами себе, если пришло - все хорошо. Если нет - очень внимательно проверяем содержимое всех конфигурационных файлов. Я сам потерял три дня из-за того, что в одном месте пропустил всего одну строку.
Настройка GRUB2 в Debian
Зачем настраивать GRUB 2
Решил я настроить свой GRUB 2, потому что не все его параметры меня устраивают. Ну, например, уменьшить таймаут, увеличить разрешение, в конце концов, поменять фоновую картинку. К чему в итоге пришёл, написано ниже.
Настройка разрешения
Первое, что нужно сделать - зайти в консоль самого GRUB'а при запуске. Для этого нужно нажать клавишу c
, и, если пароль на загрузчик не установлен, сразу же осуществляется переход к командной строке. Тут вводим одну команду из двух (результат на моём Debian 9 одинаковый):
videoinfo
vbeinfo
Обе команды дают один и тот же результат - список доступных GRUB'у режимов видеоадаптера. Однако, всё не так просто. Дело в том, что при запуске загрузчика загружается видеодрайвер, НО ЭТО НЕ ТОТ ВИДЕОДРАЙВЕР, который даёт полный доступ ко всем режимам видеоадаптера. Таким образом, у меня, например, максимально доступное разрешение не соответствует параметрам монитора, т. е. тут не всё так просто.
В общем, параметры монитора я выяснил, теперь надо было подкрутить /etc/default/grub
. Настраивать нужно именно этот файл, поскольку при вызове скрипта update-grub
настройки будут взяты оттуда. Ниже привожу только те настройки, которые менял.
GRUB_DEFAULT=0
GRUB_TIMEOUT=3
GRUB_GFXMODE=1280x1024x32 # Разрешение загрузочного меню GRUB
GRUB_GFXPAYLOAD_LINUX=1920x1080x32 # Передается в параметрах ядра
GRUB_BACKGROUND=/etc/alternatives/desktop-theme/grub/Hexagons-16x9.png
GRUB_DISABLE_OS_PROBER="true"
Краткое описание параметров:
GRUB_DEFAULT | Пункт меню по умолчанию |
GRUB_TIMEOUT | Время в секундах до загрузки пункта по умолчанию |
GRUB_GFXMODE | Графический режим загрузчика. Можно указать так же значение auto . |
GRUB_GFXPAYLOAD_LINUX | Разрешение графического режима, которое загрузчик передаст ядру. Если указать правильное значение, можно запускать ОС сразу с нужным разрешением экрана. |
GRUB_BACKGROUND | Путь к фону загрузчика. Допускается использовать файлы в форматах jpg, png и tga. При необходимости изображение будет отмасштабировано под GRUB_GFXMODE. Если картинки не будет в указанном месте, загрузчик будет запущен в текстовом режиме, без какой-либо графики. |
GRUB_DISABLE_OS_PROBER | Запретить запуск утилиты osprober , собирающей информацию о других установленных ОС. Поскольку у меня Linux единственная ОС на этом компьютере, могу себе позволить сэкономить немного времени. |
После настройки всех параметров обязательно нужно вызвать команду update-grub
. Для тех систем, где используется GRUB2, данная команда является символической ссылкой на update-grub2
.
Django, JSON и формы
Очень интересные результаты выдает Google при поиске по словам django json forms
. Большая часть ссылок ведет на Stack Overflow, но там всё одно и то же. Как правило, всё сводится к тому, чтобы: 1) сменить стек технологий; 2) разобрать request.POST
как словарь (это не работает); 3) передать данные каким-то другим способом.
А вот рабочее решение, проверенное в Django 1.4.22 (из-за некоторых особенностей мне сейчас приходится пользоваться именно таким старьем).
# -*- coding: utf-8 -*-
from __future__ import absolute_imports
from django.utils import simplejson as json
from django import http
from app.items import forms
from app.items import models
from api.items import serializers
from rest import response
def api_post_view(request):
if request.method == 'POST' and request.is_ajax():
# Ну да, всего одна строка. А вы что думали?
raw_data = json.loads(request.POST)
new_item_form = forms.CreateItemForm(raw_data)
if new_item_form.is_valid():
new_item = new_item_form.save()
serializer = serializers.ItemReadSerializer()
return reponse.JsonResponse(serializer.serialize(new_item))
else:
return response.JsonResponse(new_tem_form.errors)
else:
return http.HttpResponseBadRequest()
Насчет JsonResponse
тоже надо пару слов сказать. В старых версиях такого класса нет, но он отлично портируется из новых. Вот код:
from django.core.serializers.json import DjangoJSONEncoder
from django.utils import simplejson as json
from django.http.response import HttpResponse
class JsonResponse(HttpResponse):
"""
An HTTP response class that consumes data to be serialized to JSON.
:param data: Data to be dumped into json. By default only ``dict`` objects
are allowed to be passed due to a security flaw before EcmaScript 5. See
the ``safe`` parameter for more information.
:param encoder: Should be a json encoder class. Defaults to
``django.core.serializers.json.DjangoJSONEncoder``.
:param safe: Controls if only ``dict`` objects may be serialized. Defaults
to ``True``.
:param json_dumps_params: A dictionary of kwargs passed to json.dumps().
"""
def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,
json_dumps_params=None, **kwargs):
if safe and not isinstance(data, dict):
raise TypeError(
'In order to allow non-dict objects to be serialized set the '
'safe parameter to False.'
)
if json_dumps_params is None:
json_dumps_params = {}
kwargs.setdefault('content_type', 'application/json')
data = json.dumps(data, cls=encoder, **json_dumps_params)
super().__init__(content=data, **kwargs)