YaST/Tutorials/Simple YaST Module/YaST Module Files
Содержание
Файлы модуля YaST
Ранее было показано как создать модуль YaST, установить и запустить его. Далее будет больше рассказано об исходных файлах проекта: для чего они нужны, что они содержат и какие функции выполняют.
Файлы проекта и слои YaST
На рисунке ниже показано соответствие файлов проекта и слоев YaST. Так новые модули YaST должны быть разделены на файлы в соответствии с последней версией. Например, файла sshd.scr нет в недавно созданном шаблоне.
Работа внутренних механизмов
Эта часть довольно сложная и не настолько важна - при желании ее можно пропустить.
Что происходит между вызовом команды /sbin/yast2 sshd и открытием диалогового окна?
- Скрипт /sbin/yast2 определяет внутренние переменные, такие как, например LC_CTYPE. Затем происходит проверка доступны ли библиотеки Qt или ncurses и выполняется бинарный файл /usr/lib/YaST2/bin/y2base.
- /usr/lib/YaST2/bin/y2base вызывает “sshd qt” или “sshd ncurses” как два параметра программы. y2base проверяет существует ли клиент sshd.ycp в каталоге /usr/share/YaST2/clients/ и запускает его.
- sshd.ycp импортирует различные бинарные модули Progress, Report или CommandLine, определяет параметры командной строки, а так же к sshd.ycp подключен файл wizards.ycp.
- К файлу wizarsds.ycp подключен файл dialogs.ycp с определением диалогов и файл complex.ycp c определением сложных функций диалогов и с определением последовательности диалогов.
- После закрытия пользовательского интерфейса управление возвращается клиенту sshd.ycp, который завершает операции и закрывается.
Обзор файлов с исходным кодом
Файлы исходного кода находятся в каталоге sshd/src/. Все эти файлы копируются каталог /usr/share/YaST2/ во время процедуры сборки и установки. Ниже рассмотрено назначение этих файлов на нескольких простых примерах. Файлы представлены здесь в упрощенном виде, большая часть информационного наполнения заменена многоточием. Важные части подчеркнуты. Файлы отсортированы согласно логики работы модуля.
Файл sshd.desktop
Параметры для приложений KDE, AutoYaST, центра управления YaST...
[Desktop Entry] Type=Application ... X-SuSE-YaST-Group=Misc ... Icon=yast-sshd Exec=/sbin/yast2 sshd ... Name=Sshd GenericName=sshd ...
Эти настройки используются другими приложениями, а не непосредственно конфигуратором YaST SSHD. Они определяют поведение и идентификацию модуля. Более подробное описание находится в YaST Desktop Files.
Файл sshd.ycp
Базовое клиентское приложение с поддержкой командной строки. Интерфейс командной строки будет описан в другой примере.
Клиенты хранятся в каталоге /usr/share/YaST2/clients/
/**
* Файл: clients/sshd.ycp
...
*/
{
textdomain "sshd";
/* The main () */
y2milestone ("----------------------------------------");
y2milestone ("Sshd module started");
import "Progress";
import "Report";
import "Summary";
import "CommandLine";
include "sshd/wizards.ycp";
map cmdline_description = $[
...
"guihandler" : SshdSequence,
...
];
/* это предлоежение или нет? */
boolean propose = false;
...
/* Функция main пользовательского интерфейса */
any ret = nil;
if (propose) ret = SshdAutoSequence();
else ret = CommandLine::Run(cmdline_description);
y2debug("ret=%1", ret);
/* Конец */
y2milestone("Sshd module finished");
y2milestone("----------------------------------------");
return ret;
/* Конец файла */
}
К этому модулю подключен файл wizard.ycp с описанием последовательности диалогов. Клиент запускает бинарный файл YaST, вызывает CommandLine::Run() which, в данном случае, SshdSequence() диалогового мастера wizard.ycp.
ЗАМЕЧАНИЕ: Существуют также и другие клиенты: <имя-модуля>_auto.ycp для AutoYaST и <имя-модуля>_ proposal.ycp для предложенной установки, но они ненужны в данном примере.
Файл wizards.ycp
Содержит описание последовательности диалогов, которые используются клиентами.
Мастера сохранены в каталоге /usr/share/YaST2/include/<module-name>/.
/**
* Файл: include/sshd/wizards.ycp
...
*/
{
textdomain "sshd";
import "Sequencer";
import "Wizard";
include "sshd/complex.ycp";
include "sshd/dialogs.ycp";
/**
* Добавить в конфигуратор sshd
* полученную последовательность диалогов
*/
any AddSequence() {
...
}
/**
* Основной мастер конфигуратора sshd
* создает последовательность диалогов
*/
any MainSequence() {
map aliases = $[
"summary" : ``( SummaryDialog() ),
"overview" : ``( OverviewDialog() ),
"add" : [ ``( AddSequence() ), true ],
"edit" : [ ``( AddSequence() ), true ]
];
map sequence = $[
"ws_start" : "summary",
"summary" : $[
`abort : `abort,
`next : `next,
`overview : "overview",
],
"overview" : $[
`abort : `abort,
`next : `next,
`add : "add",
`edit : "edit",
],
"add" : $[
`abort : `abort,
`next : "overview",
],
"edit" : $[
`abort : `abort,
`next : "overview",
]
];
any ret = Sequencer::Run(aliases, sequence);
return ret;
}
/**
* Возвращается по результатам обработки последовательности диалогов
* вся конфигурация sshd
*/
any SshdSequence() {
map aliases = $[
"read" : [ ``( ReadDialog() ), true ],
"main" : ``( MainSequence() ),
"write" : [ ``( WriteDialog() ), true ]
];
map sequence = $[
"ws_start" : "read",
"read" : $[
`abort : `abort,
`next : "main"
],
"main" : $[
`abort : `abort,
`next : "write"
],
"write" : $[
`abort : `abort,
`next : `next
]
];
Wizard::CreateDialog();
any ret = Sequencer::Run(aliases, sequence);
UI::CloseDialog();
return ret;
}
/**
* Возвращает вся конфигурацию sshd, но не для чтения или записи, а
* для автоустановки
*/
any SshdAutoSequence() {
...
}
/* Конец файла */
}
В начале модуля подключены файлы complex.ycp и dialogs.ycp, потому что они содержат диалоги, которые используются в последовательностях диалогов.
Ниже в карте псевдонимов (aliases map) определены псевдонимы (aliases) к этим диалогам, а последовательности (sequences), использующие эти псевдонимы, определены в карте последовательностей (sequence map).
В обработчике последовательности диалогов определена связь между событием (event) генерируемым диалоговой функцией и действием (action) вызванным последовательностью.
SshdSequence() вызывается в конце клиента sshd.ycp.
ВАЖНО: Не надо создавать событие для кнопки «Назад» (Back), диалоговый мастер обрабатывает его самостоятельно.
ЗАМЕЧАНИЕ:
Стоит отметить что последовательности могут вызывать другие последовательности: SshdSequence() вызывает MainSequence() и все вызывают AddSequence().
Последовательность вызванная клиентом должна содержать команды Wizard::CreateDialog() и UI::CloseDialog(), так как пользователь должен увидеть интерфейс как можно раньше. Кроме этого команда Wizard::CreateDialog() создает классический окно мастера с текстом справки с левой стороны, пустым пространством на другой стороне и кнопками «Назад» (Back), «Отмена» (Abort) и «Далее» (Next).
Скриншот окна Мастера:
Файл dialogs.ycp
В этом файле определены диалоги и их простейшие обработчики.
Диалоги хранятся в каталоге /usr/share/YaST2/include/<имя-модуля>/
/**
* Файл: include/sshd/dialogs.ycp
...
*/
{
textdomain "sshd";
import "Label";
import "Wizard";
import "Sshd";
include "sshd/helps.ycp";
/**
* Configure1Dialog
* возвращает результат диалога
*/
any Configure1Dialog () {
/* Sshd configure1 dialog caption */
string caption = _("Sshd Configuration");
/* Sshd configure1 dialog contents */
term contents = `Label (_("First part of configuration of sshd"));
Wizard::SetContentsButtons(caption, contents, HELPS["c1"]:"",
Label::BackButton(), Label::NextButton());
any ret = nil;
while (true) {
ret = UI::UserInput();
/* abort? */
if(ret == `abort || ret == `cancel) {
if(ReallyAbort()) break;
else continue;
}
else if(ret == `next || ret == `back) {
break;
}
else {
y2error("unexpected retcode: %1", ret);
continue;
}
}
return ret;
}
/**
* Configure2Dialog
* возвращает результат диалога
*/
any Configure2Dialog () {
...
}
/* Конец файла */
}
В этом файле определены две функции диалогов Configure1Dialog() и Configure2Dialog(). Поведение диалогов определяется в функцией Wizard::SetContentsButtons().Эта дополнительная функция доступна потому что в файле wizard.ycp используется Wizard::CreateDialog()
После того как создан диалог, запускается цикл в котором обрабатываются действия пользователя.
Текст помощи находиться в файле helps.ycp в виде справочной карты (HELPS map).
ЗАМЕЧАНИЕ: Все строки которые нуждаются в переводе записаны следующим образом _("...") . Перед каждой такой строкой должен быть комментарий для переводчика. Здесь используется стандартный стиль gettext.
Переводы жестко связаны с текстовыми доменами (textdomain) внутри файла.
File complex.ycp
Модуль содержит сложные функции для обработки диалогов.
/**
* Файл: include/sshd/complex.ycp
...
*/
{
textdomain "sshd";
import "Label";
...
import "Sshd";
include "sshd/helps.ycp";
...
/**
* Диалог чтения настроек
* возвращает`abort в случае отмены и `next в противном случае
*/
symbol ReadDialog() {
Wizard::RestoreHelp(HELPS["read"]:"");
// Sshd::AbortFunction = PollAbort;
if (!Confirm::MustBeRoot()) return `abort;
boolean ret = Sshd::Read();
return ret ? `next : `abort;
}
/**
* Диалог записи настроек
* возвращает`abort в случае отмены и `next в противном случае
*/
symbol WriteDialog() {
...
}
/**
* SummaryDialog
* возвращает результат диалога
*/
any SummaryDialog() {
..
}
/**
* OverviewDialog
* возвращает результат диалога
*/
any OverviewDialog() {
/* заголовок диалогового окна */
string caption = _("Sshd Overview");
list overview = Sshd::Overview();
/* шапка таблицы FIXME */
term contents = Wizard_hw::ConfiguredContent(
/* шапка таблицы */
`header(_("Number"), _("Sshd")),
overview, nil, nil, nil, nil );
contents = Wizard_hw::SpacingAround(contents, 1.5, 1.5, 1.0, 1.0);
Wizard::SetContentsButtons(caption, contents, HELPS["overview"]:"",
Label::BackButton(), Label::FinishButton());
any ret = nil;
while (true) {
ret = UI::UserInput();
/* отмена? */
if(ret == `abort || ret == `cancel) {
if(ReallyAbort()) break;
else continue;
}
/* добавить */
else if(ret == `add_button) {
ret = `add;
break;
}
/* править */
else if(ret == `edit_button) {
ret = `edit;
break;
}
/* удалить */
else if(ret == `delete_button) {
continue;
}
else if(ret == `next || ret == `back) {
break;
}
else {
y2error("unexpected retcode: %1", ret);
continue;
}
}
return ret;
}
/* Конец файла */
}
Этот файл очень похож на dialogs.ycp, но здесь находятся более сложные диалоги с обработчиками.
Самое важное здесь это обращение функции ReadDialog() к Sshd::Read() – это глобальная функция YCP модуля Sshd.
При просмотре файла wizards.ycp можно увидеть что в его карте псевдонимов используются диалоговые функции, определенные в модуле complex.ycp.
Файл helps.ycp
Содержит тексты справки для каждого диалога.
Справка храниться в каталоге /usr/share/YaST2/include/<имя-модуля>/
/**
* Файл: include/sshd/helps.ycp
...
*/
{
textdomain "sshd";
/**
* Вся справка здесь
*/
map HELPS = $[
/* Справка диалога чтения 1/2 */
"read" : _("<p><b><big>Initializing sshd Configuration</big></b><br>
Please wait...<br></p>
") +
/* Справка диалога чтения 2/2 */
_("<p><b><big>Aborting Initialization:</big></b><br>
Safely abort the configuration utility by pressing <b>Abort</b> now.</p>
"),
...
/* Справка диалога Configure1 1/2 */
"c1" : _("<p><b><big>Configuration Part One</big></b><br>
Press <b>Next</b> to continue.
<br></p>") +
/* Справка диалога Configure1 2/2 */
_("<p><b><big>Selecting Something</big></b><br>
It is not possible. You must code it first. :-)
</p>"),
...
];
/* Конец файла */
}
Этот файл включен в модули dialogs.ycp и complex.ycp. Все тексты определены в карте HELPS.
Все справочные тексты пишутся на HTML и они должны соответствовать Text Style Guide.
ЗАМЕЧАНИЕ: Тексты справки логически делятся на небольшие части. Так после изменений переводчикам легче переводить небольшие блоки текста.
Файл Sshd.ycp
YCP модуль с глобальным API, который может использоваться другими модулями или проектами.
Все модули хранятся в каталоге /usr/share/YaST2/modules/.
/**
* File: modules/Sshd.ycp
* Package: Конфигуратор sshd
* Summary: Настройки sshd, функции ввода и вывода
* Authors: John The Fish <john@thesmallfish.net>
*
* $Id: Sshd.ycp 27914 2006-02-13 14:32:08Z jtf $
*
* Представление конфигурации sshd.
* Процедуры ввода и вывода.
*/
{
module "Sshd";
textdomain "sshd";
...
/**
* Чтение всех настроек sshd
* возвращает true в случае удачи
*/
global boolean Read() {
/* Заголовок диалога чтения sshd */
string caption = _("Initializing sshd Configuration");
...
integer steps = 4;
...
Progress::New( caption, " ", steps, [
...
], [
...
],
""
);
...
Progress::NextStage();
...
if(Abort()) return false;
modified = false;
return true;
}
/**
* Запись всех настроек sshd
* возвращает true в случае удачи
*/
global boolean Write() {
/* Заголовок диалога записи sshd */
string caption = _("Saving sshd Configuration");
...
integer steps = 4;
...
Progress::New( caption, " ", steps, [
...
], [
...
],
""
);
...
Progress::NextStage();
...
if(Abort()) return false;
return true;
}
...
}
Ключевое слово module устанавливает пространство имен модуля. Для доступа к глобальным функциям и переменным используется префикс имя_модуля:: (например Sshd:: для модуля Sshd).
В глобальных функциях Read и Write так же определена полоса прогресса (Progress bar), которая изменяется вызовом функции Progress::NextStage().
Функции Read и Write всегда должны сообщать результат работы возвращая значение типа boolean (выполнено, не выполнено).
ЗАМЕЧАНИЕ: Проект так же содержит файл Sshd.pm представляющий собой модуль написанный на языке Perl. Модуль Perl используется так же как и модуль YCP, но его внутренний синтаксис отличается. Создание Perl модулей в этом руководстве не рассматривается.

