Prosty dialog
W artykule tym został opisany przykład prostego okna dialogowego z wykorzystaniem kilku pól formularza oraz podstawowymi przyciskami zapisz i anuluj.
Widok
Przykład do pobrania oraz listing klasy
Poniżej znajduje się plik z przykładem klasy dialoga dodania/edycji Ticketa.
Tutaj go możesz pobrać: TicketDialog.inc
<?php
namespace ReadyApp\Taskboard\Forms;
use DialogForm;
use Application;
use Translator;
use JScript;
use WidgetException;
use DialogComposer;
use TextInput2;
use SysContext;
use ExtSelect;
use LookupWidget2;
use ContactSearchEngine;
use EDokApi;
use ContactPersonsSearchEngine;
use ContactPersonsLookupManager;
use ReadyApp\Taskboard\Services\TicketService;
use ReadyApp\Taskboard\Shared\TicketData;
use Exception;
use PgManager;
final class TicketDialog extends DialogForm {
/**
* Lista method do których możemy się odwołać przez JS-a
* @var string[]
*/
protected static $cMethods = [
'toHtml' => 'toHtml',
];
/**
* @var TicketService
*/
private $service;
/**
* @var mixed
*/
private $dscrptToCopy;
/**
* @var bool
*/
private $ticketIsHelp = FALSE;
private $dscrptFromEmail = FALSE;
/**
* TicketDialog constructor.
*
* @param $name
* @param null $caption
* @param int $dstyle
*/
public function __construct($name, $caption = NULL, $dstyle = BS_DIALOG) {
parent::__construct($name, NULL, BS_DIALOG);
$this->path = Application::makeRelativePath(__FILE__);
$this->HWND = $this->createHWND();
$this->withoutCaption = TRUE;
$this->width = '500px';
$this->height = '650px';
$this->service = (new TicketService());
if (($this->isOpened()) && (!$this->noNeedForCreate())) {
$this->create();
}
}
/**
* @return bool[]
*/
protected function getSupportedParams(): array {
return ['eml_id' => TRUE,];
}
/**
* @param array $params
* @param array $data
*
* @return bool|void
*/
public function open($params = NULL, &$data = NULL) {
$data = [];
if (!UserRights::checkSysAcc('bswfms.knowledgebase')) {
throw new SweetException(Translator::translateJS('Brak uprawnień do '));
}
parent::open();
$this->dscrptToCopy = $defaultData['dscrpt'];
$this->ticketIsHelp = TRUE;
$this->dscrptFromEmail = TRUE;
$this->setSData(['eml_id' => $eml, 'ticketIsHelp' => $this->ticketIsHelp, 'dscrptFromEmail' => $this->dscrptFromEmail, 'optime' => $defaultData['optime']]);
$this->create();
$this->setWData([
'contid' => $defaultData['contid'],
'copeid' => $defaultData['copeid'],
]);
}
/**
* @return bool|void
* @throws WidgetException
* @throws \Exception
*/
public function save() {
$sData = $this->getSData();
$wData = $this->getWData();
// Wywołanie walidacji danych
$this->validate($wData);
// Zrob wywołanie serwisu w ceu wywolania danych
$this->jsClose(TRUE);
}
/**
* @return false|void
*/
protected function create() {
/**
* Sprawdzamy czy dialog został już wcześniej otworzony
*/
if (!$this->isOpened()) {
return FALSE;
}
$data = $this->getSData();
$composer = new DialogComposer($this);
$composer->addModernHeader(Translator::translate('Utwórz nowy ticket'), 'eic eic-e-service', '#2c3e50');
$this->dscrpt = new TextInput2($this->name.'dscrpt');
$this->dscrpt->setLabel(Translator::translate('Opis'));
if ($this->dscrptFromEmail && $this->ticketIsHelp) {
$this->dscrpt->labelWidget->width = 'calc(100% - '.(\Dialog::SPACE_LEFT + \Dialog::SPACE_RIGHT).'px)';
$this->dscrpt->labelWidget->text .= ('<a class="ActionLink" onclick ="document.getElementById(\''.$this->dscrpt->getName().'\').innerHTML = \''.$this->dscrptToCopy.'\';" style="position:absolute; display:inline-block; right:0px;">Wstaw z maila</a>');
$this->dscrpt->getName();
}
$this->dscrpt->height = (2 * self::DEFAULT_LINE_HEIGHT).'px';
$this->dscrpt->setRequired(TRUE);
$composer->addNext($this->dscrpt, 'dscrpt');
/* Typ / dział do wyboru - teczka */
$this->dsexid = new ExtSelect($this->name.'dsexid');
$this->dsexid->withTitles = TRUE;
$this->dsexid->cutTitles = FALSE;
$this->dsexid->width = 'calc(50% - '.(1.5 * self::SPACE_LEFT).'px)';
$this->dsexid->setLabel(Translator::translate('Dział/Typ'));
$this->dsexid->setRequired(TRUE);
if (isset(SysContext::$usr_info['orunid'][0])) {
$this->dsexid->setData(NULL);
$places = ($this->ticketIsHelp) ? $this->service->brifcaseData(['\''.TicketData::PROCESS_HELPDESK.'\'']) : $this->service->brifcaseData(['\''.TicketData::PROCESS_ENHANCEMENT.'\',\''.TicketData::PROCESS_FEATURE.'\',\''.TicketData::PROCESS_DEFECT.'\', \''.TicketData::PROCESS_DEVOPS.'\'']);
foreach ($places as $v) {
$item = [];
$item['value'] = $v['dsexid'];
$item['caption'] = $v['text'];
if (isset($v['fxtrid']) and $v['fxtrid']) {
$item['data'] = ['fxtrvl' => $v['fxtrvl']];
}
if (isset($v['title'])) {
$item['title'] = $v['title'];
}
$this->dsexid->addItem($v['dsexid'], $item, ($v['grp'] ?? NULL));
}
}
$composer->addNext($this->dsexid, 'dsexid');
/* Kategoria SLA */
$this->ft_prior = new ExtSelect($this->name.'ftprior');
$this->ft_prior->setLabel(Translator::translate('Kategoria SLA'));
$this->ft_prior->setRequired(FALSE);
$this->ft_prior->query = 'SELECT sla_problem_cat AS value, name AS caption FROM cregisters.creg_sla_categories WHERE NOT is_del';
$this->ft_prior->update();
$this->ft_prior->width = 'calc(50% - '.(1.5 * self::SPACE_LEFT).'px)';
$composer->addNext($this->ft_prior, 'ftprior', self::P_TO_RIGHT);
/* Środowisko */
$this->ftenv = new ExtSelect($this->name.'ftenv');
$this->ftenv->query = 'SELECT fo.ftopid AS value, fod.ftopnm AS caption FROM features f
INNER JOIN features_options fo ON fo.featid = f.featid INNER JOIN features_options_def fod ON fod.fodfid = fo.fodfid
WHERE f.ftsmbo=\''.TicketData::ENVIRONMENT.'\' AND NOT is_del ORDER BY fod.ftopnm';
$this->ftenv->update();
$this->ftenv->setLabel(Translator::translate('Środowisko'));
$this->ftenv->setRequired();
$this->ftenv->width = 'calc(50% - '.(1.5 * self::SPACE_LEFT).'px)';
$composer->addNext($this->ftenv, 'ftenv', self::P_TO_LEFT);
/* Komponent / aplikacja */
$this->ftcomp = new ExtSelect($this->name.'ftcomp');
$this->ftcomp->query = 'SELECT app_id AS value, appnam AS caption FROM cregisters.creg_applications WHERE NOT is_del ORDER BY app_id';
$this->ftcomp->update();
$this->ftcomp->setLabel(Translator::translate('Komponent'));
$this->ftcomp->setRequired();
$this->ftcomp->width = 'calc(50% - '.(1.5 * self::SPACE_LEFT).'px)';
$composer->addNext($this->ftcomp, 'ftcomp', self::P_TO_LEFT);
/* Wersja komponentu */
$this->ftvers = new TextInput2($this->name.'ftvers');
$this->ftvers->setLabel(Translator::translate('Wersja komponentu'));
$this->ftvers->setRequired(FALSE);
$this->ftvers->right = 'calc( 22% )';
$this->ftvers->width = 'calc( 30% - '.(1 * self::SPACE_LEFT).'px)';
$composer->addNext($this->ftvers, 'ftvers', self::P_TO_RIGHT);
/* Klient kopiowany z maila */
$this->contid = new LookupWidget2($this->name.'contid', new ContactSearchEngine(), FALSE, 1);
$this->contid->setLabel(Translator::translate('Klient'));
$this->contid->width = 'calc( 100% - '.(2 * DialogForm::SPACE_LEFT).'px )';
$composer->addNext($this->contid, 'contid', DialogForm::P_TO_LEFT);
/* Osoba kontaktowa z maila */
$this->copeid = new LookupWidget2($this->name.'_copeid', new ContactPersonsSearchEngine(), FALSE, TRUE);
$this->copeid->setFilterString('defcnt IS NOT NULL');
$this->copeid->setLabel(Translator::translate('Osoba kontaktowa'));
require_once('./classes/LookupWidget/ContactPersons/ContactPersonsLookupManager.inc');
ContactPersonsLookupManager::manage($this->copeid, ['contid' => $this->contid->name]);
$composer->addNext($this->copeid, 'copeid');
/* Uwagi */
$this->fixinf = new TextInput2($this->name.'fixinf');
$this->fixinf->setLabel(Translator::translate('Uwagi'));
$this->fixinf->setRequired(FALSE);
$this->fixinf->height = ((2 * self::DEFAULT_LINE_HEIGHT) + (self::DEFAULT_WIDGET_HEIGHT * 2)).'px';
$this->fixinf->width = 'calc( 100% - '.(2 * self::SPACE_LEFT).'px)';
$composer->addNext($this->fixinf, 'fixinf', self::P_TO_LEFT);
/**
* Dodanie przycisków Zapisu i Anuluj
*/
$composer->addModernButtonsPanel([
'bSave',
'bCancel',
]);
$this->add($composer);
}
/**
* Walidaca danych z formularza
* @param array $data
*
* @return void
*/
private function validate(array $data) {
// W tym miejscu zrob wywolanie walidatora
}
}
cMethods
Metoda Jeśli potrzebujemy odwołać się po jakimś zdarzeniu JS-owym na przycisku/pola, musimy zadeklarować metodę w tablicy cMethods
/**
* Lista method do których możemy się odwołać przez JS-a
* @var string[]
*/
protected static $cMethods = [
'toHtml' => 'toHtml',
];
Open
Metoda Jest odpowiedzialna za sprawdzenie czy użytkownik ma prawo wejść do danego modułu, sprawdzamy to za pomocą klasy UserRights
.
Poniższy przykład sprawdza prawa dostępu do modułu Baza wiedzy
if (!UserRights::checkSysAcc('bswfms.knowledgebase')) {
JScript::add('alertMessage(\''.Translator::translateJS('Brak uprawnień do bazy wiedzy').'\');');
return FALSE;
}
- Następnie powinniśmy w metodzie
open
wywołać rodzica metody i wywołać metodecreate
by utworzył się nam formularz. Wykonujemy to wszystko za pomocą:
parent::open();
$this->create();
- Jeśli potrzebujemy przesłać do formularza jakieś dane, to należy przed wywołaniem metody
create
użyć z klasyDialogForm
metodysetSData()
. Ustawienie statycznych danych na dialogu, dzięki którym, np. możemy przesłać dane nie od użytkownika, tylko systemowe, takie dane, które wskażą nam jednoznacznie Beana.
$this->setSData(['ticketIsHelp' => $this->ticketIsHelp]);
- Jeśli potrzebujesz dane do formularza użyj metody
setWData()
z klasyDialogForm
. Jest ona odpowiedzialna za dodanie danych do Widgetu np. gdy chcesz by pole miało już jakąś deflautową wartość w value.
$this->setWData([
'contid' => $defaultData['contid'],
'copeid' => $defaultData['copeid'],
]);
create
Metoda Jest odpowiedzialna za utworzenie ciała dialog-a, to tutaj deklarujemy różne typy pól do formularza.
By poprawnie utworzyć metodę create
pierw należy sprawdzić czy dialog został otworzony. Sprawdzamy to po przez:
if (!$this->isOpened()) {
return FALSE;
}
save
Metoda Jak można się domyślać tworzy nowy, bądź edytuje już istniejący zasób.
Tutaj także walidujemy dane wejściowe
Możesz znaleźć przykład prostego walidatora w przykładowym pliku. Link do pliku znajdziesz na początku tego dokumentu.
Dane możesz pobrać dane przez:
$sData = $this->getSData();
$wData = $this->getWData();
ale także pobrać wszystkie dane skomasowane w jedną tablicę po przez użycie
$this->getData();
Dodawanie pol w formularzu
By dodać pola do formularza, pierw trzeba utworzyć obiekt typu DialogComposer
, a później dodajemy widgety dla określonych typów danych/pol. Jeśli chcesz zobaczyć przykład, wróć się do przykładowego pliku TicketDialog.inc
i spójrz na linie 210 w chronionej metodzie create.
Dodanie przycisków Zapisz i Analuj
Dodanie przycisków jest bardzo proste. Wystarczy na nowo utworzony obiekt klasy DialogComposer
odwołać się do metody addModernButtonsPanel
.
Pierwszy parametr w tej metodzie jest ustawiony na wartości deflautowe 'bOk','bCancel'
$dialogComposer->addModernButtonsPanel([
'bSave',
'bCancel',
]);
Deklaracja Dialogu w Service
Deklaracja statyczna
Akcje statyczne / korowe, to akcje które zostały już zaimplementowane w podstawowej wersji systemu. Plik odpowiedzialny za mapowanie serwisów to MapService.inc
Przykład
self::FKRCPELEMENT => [
'dscrpt' => Translator::translate('Pozycja produktu na karcie RCP'),
'path' => MOD_PATH.'Evidence/forms/FKElementRCPDialog.inc',
'class' => 'FKElementRCPDialog',
'dscrptColumn' => 'commnt',
'beanPath' => MOD_PATH.'Evidence/beans/FKElementRCPBean.inc',
'beanClass' => 'FKElementRCPBean',
'caption' => '',
'params' => ['fkelid' => 0],
],
- Zadeklaruj stałą, z nawą oraz wartością pisaną z dużych liter, np.
const FKRCPELEMENT = 'FKRCPELEMENT';
- Utwórz na końcu tablicy statycznej
map
, która znajduje się w metodzie statycznejinit
, tablice której kluczem będzie twoja stała a wartością tablica z parametrami. Przed wszystkim dodaj:path
z ścieszką do pliku który chcesz załadować.Notatka
Tutaj należy trzymać się konwencji rozdzielenia typu klasy na różne katalogi oraz ustawiania poprawnych nazw pliku/klasy.
- Plik typu lista dajemy do głównego katalogu. Ten typ pliku jest odpowiedzialny za wyświetlenie listy np. użytkowników,
- Inne typy plików trzymamy w osobnych katalogach oraz ustawiamy nazwę pliku/klasy tak by mówiła czego dotyczy ale nazwa nie może zawierać typu pliku.class
z nazwą klasy, którą chcesz użyć, (mamy pliki gdzie możemy znaleźć pare klas w 1 pliku)gicon
ikonki, które znajdziesz w guideline,beanPath
path do potrzebnego beana / zasobu bazy danych. Powinien znajdować się w katalogu beans i nazwa klasy oraz pliku nie powinna zawierać słowa beanbeanClass
tjw. z class,
Notatka
Pamiętaj, że Dialog jest odpowiedzialny za załadowanie formularza do dialoga/okienta, a formularz pracuje na danych oraz różnymi typami pól.
Deklaracja akcji specjalnej
Akcje specjalne są tworzone dla klienta gdy chce jakąś modyfikację, czyli są one tworzone per projekt.
Plik odpowiedzialny za mapowanie serwisów specjalnych to CustomMapService.inc
Ładuje on dane z pliku typu JSON, jego budowa jest taka sana jak w MapService
Przykład
"CUSTOM_EXACTORIS_TESTS_RUN": {
"caption": "Uruchomienie testów",
"icon": "goal.svg",
"path": "{SCRIPTS_PATH}Exactoris\/View/Form\/TestsRunDialog.inc",
"class": "\\ReadyApp\\Exactoris\\View\\Form\\TestsRunDialog",
"params": {
"prc_id": 0
}
}
- Nazwa klucza składa się z:
- Wymaganego 1 segmentu
CUSTOM
, np. CUSTOM_ EXACTORIS_TESTS_RUN - Drugi segment nazwy, jest to
Nazwa projektu
, np. CUSTOM_ EXACTORIS _TESTS_RUN - Trzeci segment to
Nazwa akcji
, np. CUSTOM_EXACTORIS_ TESTS_RUN
- Wymaganego 1 segmentu
- W
params
można od razu przesłać, typ akcji przez parameter 'mode', np:"mode": 'new'