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 модулей в этом руководстве не рассматривается.