Sablonozó mechanizmus

Sablonparancsok
Dinamikus adatok beszúrása
[% Data.Name %]
[% Data.Name | html %]
[% Data.Name | uri %]
[% Data.Name | JSON %]
[% Env() %]
[% Config() %]
Honosítási parancsok
[% Translate() %]
[% Localize() %]
[% ReplacePlaceholders() %]
Sablonfeldolgozó parancsok
Megjegyzés
[% InsertTemplate("Copyright.tt") %]
[% RenderBlockStart %] / [% RenderBlockEnd %]
[% WRAPPER JSOnDocumentComplete %]...[% END %]
Egy sablonfájl használata

Belsőleg az OTRS egy sablonozó mechanizmust használ a HTML oldalak (és egyéb tartalom) dinamikus előállításához, miközben szétválasztva tartja a program logikáját (Perl) és a megjelenítést (HTML). Tipikusan egy előtétprogram modul egy saját sablonfájlt fog használni, át fog adni néhány adatot annak, és vissza fogja adni a megjelenített eredményt a felhasználónak.

A sablonfájlok itt találhatók: $OTRS_HOME/Kernel/Output/HTML/Standard/*.tt

Az OTRS a Template::Toolkit megjelenítő motorra támaszkodik. A teljes Template::Toolkit szintaxis használható az OTRS sablonokban. Ez a szakasz néhány példa használati esetet és OTRS kiterjesztést mutat be a Template::Toolkit szintaxishoz.

Sablonparancsok

Dinamikus adatok beszúrása

A sablonokba dinamikus adatokat kell beszúrni, idézni, stb. Ez a szakasz sorolja fel a fontos parancsokat ennek elvégzéséhez.

[% Data.Name %]

Ha az alkalmazásmodul adatparamétereket ad meg a sablonoknak, akkor ezeket az adatokat ki lehet íratni a sablonra. A [% Data.Name %] a legegyszerűbb, de a legveszélyesebb is. Azt az adatparamétert fogja további feldolgozás nélkül beszúrni a sablonba úgy ahogy van, amely neve Name.

Figyelem

A hiányzó HTML idézés miatt ez biztonsági problémákat eredményezhet. Sose írasson ki olyan adatokat, amelyeket egy felhasználó adott meg, anélkül, hogy idézné azokat a HTML környezetben. A felhasználó például egyszerűen beszúrhat egy <script> címkét, és az kiíródhat az OTRS által előállított HTML oldalon.

Amikor csak lehetséges, használjon [% Data.Name | html %] (HTML-ben) vagy [% Data.Name | uri %] (hivatkozásokban) paramétert helyette.

Példa: Amikor HTML kimenetet állítunk elő az alkalmazásban, akkor HTML idézés nélkül kell kiíratnunk azt a sablonba, mint például a <select> elemeket, amelyeket a Layout::BuildSelection() függvény állít elő az OTRS-ben.

<label for="Dropdown">Példa legördülő</label>
[% Data.DropdownString"]
                    

Ha speciális karaktereket tartalmazó, összetett nevű adatbejegyzései vannak, akkor nem használhatja a pont (.) jelölést az adathoz való hozzáféréshez. Az item() függvényt használja helyette: [% Data.item('Összetett-név') %].

[% Data.Name | html %]

Ennek a parancsnak ugyanaz a funkciója mint az előzőnek, de HTML idézést hajt végre az adatokon, amint beszúrásra kerülnek a sablonba.

A szerző neve [% Data.Name | html %].
                    

Lehetséges az érték legnagyobb hosszának megadása is. Ha például egy változónak csak 9 karakterét szeretné megjeleníteni (az eredmény „ValamiNév[...]” lesz), akkor használja a következőt:

A szerző nevének első 20 karaktere: [% Data.Name | truncate(20) | html %].
                    

[% Data.Name | uri %]

Ez a parancs URL-kódolást hajt végre az adatokon, amint az beszúrásra kerül a sablonba. Ezt kell használni az URL-ek egyedülálló paraméternevei vagy értékei kiíratásánál a biztonsági problémák megakadályozásához. Nem használható teljes URL-eknél, mert ki fogja maszkolni például az = karaktert is.

<a href="[% Env("Baselink") %];Location=[% Data.File | uri %]">[% Data.File | truncate(110) | html %]</a>
                    

[% Data.Name | JSON %]

Ez a parancs JavaScript JSON szövegként írat ki egy szöveget vagy más adatszerkezetet.

var Text = [% Data.Text | JSON %];
                    

Vegye figyelembe, hogy a szűrőjelölés csak egyszerű szövegeknél fog működni. Összetett adatok JSON szövegként való kiíratásához függvényként használja azt:

var TreeData = [% JSON(Data.TreeData) %];
                    

[% Env() %]

A LayoutObject által szolgáltatott környezeti változókat szúrja be. Néhány példa:

A jelenlegi felhasználó neve: [% Env("UserFullname") %]

Néhány egyéb gyakori előre meghatározott változó:

[% Env("Action") %] --> a jelenlegi művelet
[% Env("Baselink") %] --> az alaphivatkozás --> index.pl?SessionID=...
[% Env("CGIHandle") %] --> a jelenlegi CGI-kezelő, például index.pl
[% Env("SessionID") %] --> a jelenlegi munkamenet-azonosító
[% Env("Time") %] --> a jelenlegi idő, például Thu Dec 27 16:00:55 2001
[% Env("UserFullname") %] --> például Kovács János
[% Env("UserIsGroup[admin]") %] = Igen
[% Env("UserIsGroup[users]") %] = Igen --> felhasználócsoportok (hasznos saját hivatkozásoknál)
[% Env("UserLogin") %] --> például mgg@x11.org
                    

Figyelem

A hiányzó HTML idézés miatt ez biztonsági problémákat eredményezhet. Sose írasson ki olyan adatokat, amelyeket egy felhasználó adott meg, anélkül, hogy idézné azokat a HTML környezetben. A felhasználó például egyszerűen beszúrhat egy <script> címkét, és az kiíródhat az OTRS által előállított HTML oldalon.

Ne felejtse el a | html szűrőt hozzáadni, ahol az helyénvaló.

[% Config() %]

Beállítási változókat szúr be a sablonba. Nézzünk egy példa Kernel/Config.pm fájlt:

[Kernel/Config.pm]
    # FQDN
    # (A rendszer teljesen minősített tartományneve.)
    $Self->{FQDN} = 'otrs.example.com';
    # AdminEmail
    # (A rendszer adminisztrátorának e-mail címe.)
    $Self->{AdminEmail} = 'admin@example.com';
[...]
                    

Változók kiíratásához ebből fájlból a sablonba a következőt használja:

A gépnév „$Config{"FQDN"}”
Az adminisztrátori e-mail cím „[% Config("AdminEmail") %]”
                    

Figyelem

A hiányzó HTML idézés miatt ez biztonsági problémákat eredményezhet.

Ne felejtse el a | html szűrőt hozzáadni, ahol az helyénvaló.

Honosítási parancsok

[% Translate() %]

Lefordít egy szöveget a felhasználó által kiválasztott jelenlegi nyelve. Ha nem található fordítás, akkor az eredeti szöveget fogja használni.

Ezen szöveg lefordítása: [% Translate("Help") | html %]
                    

Lefordíthat dinamikus adatokat is a Translate szűrőként való használatával:

Adatok lefordítása az alkalmazásból: [% Data.Type | Translate | html %]
                    

Egy vagy több paramétert (%s) is megadhat a szövegen belül, amelyeket dinamikus adatokkal kell kicserélni:

Ezen szöveg lefordítása és a megadott adatok beszúrása: [% Translate("Change %s settings", Data.Type) | html %]
                    

A JavaScriptben lévő szövegek is lefordíthatók és feldolgozhatók a JSON szűrővel.

var Text = [% Translate("Change %s settings", Data.Type) | JSON %];
                    

[% Localize() %]

Kiírja az adatokat a jelenlegi nyelv vagy területi beállítás szerint.

Különböző kulturális területeken különböző egyezményt használnak a dátum és idő formázásához. Például ami Németországban 01.02.2010 formátum, annak az USA-ban 02/01/2010 formátumban kellene lennie. A [% Localize() %] függvény elvonatkoztatja ezt a sablontól. Nézzünk egy példát:

[% Data.CreateTime ǀ Localize("TimeLong") %]
# Eredmény az US English területi beállításnál:
06/09/2010 15:45:41
                    

Először is az adatok a Data segítségével kerülnek beszúrásra az alkalmazásmodulból. Itt mindig egy ISO UTC időbéleget (2010-06-09 15:45:41) kell átadni adatként a [% Localize() %] függvénynek. Ezután lesz kiíratva a jelenlegi területi beállítás dátum és idő meghatározása szerint.

A [% Localize() %] függvénynek átadott adatoknak UTC formátumban kell lenniük. Ha időzóna-eltolás van meghatározva a jelenlegi ügyintézőnél, akkor az alkalmazva lesz az UTC időbélyegen a kimenet előállítása előtt.

Különböző lehetséges dátum és idő kimeneti formátumok léteznek: TimeLong (teljes dátum és idő), TimeShort (nincsenek másodpercek) és Date (nincs idő).

[% Data.CreateTime ǀ Localize("TimeLong") %]
# Eredmény a US English területi beállításnál:
06/09/2010 15:45:41

[% Data.CreateTime ǀ Localize("TimeShort") ]") %]
# Eredmény a US English területi beállításnál:
06/09/2010 15:45

[% Data.CreateTime ǀ Localize("Date") %]
# Eredmény a US English területi beállításnál:
06/09/2010
                    

Ember által olvasható fájlméretek kimenete is elérhető lehetőségként Localize('Filesize') (egyszerűen adja át a nyers fájlméretet bájtokban).

[% Data.Filesize ǀ Localize("Filesize") %]
# Eredmény az US English területi beállításnál:
23 MB
                    

[% ReplacePlaceholders() %]

Kicseréli a helykitöltőket (%s) a szövegekben az átadott paraméterekre.

Bizonyos esetekben érdemes lehet HTML kódot beszúrni a fordításokba a helykitöltők helyett. Másrészt viszont gondoskodnia kellfertőtlenítésről, mivel a lefordított szövegekben nem szabad megbízni úgy, ahogy vannak. Ehhez először fordítsa le a szöveget, küldje át a HTML szűrőn, és végül cserélje ki a helykitöltőket statikus (biztonságos) HTML kóddal.

[% Translate("This is %s.") | html | ReplacePlaceholders('<strong>bold text</strong>') %]
                    

A ReplacePlaceholders() szűrőnek átadott paraméterek számának meg kell egyeznie az eredeti szövegben lévő helykitöltők számával.

Használhatja a [% ReplacePlaceholders() %] szűrőt függvény formában is abban az esetben, ha nem fordít le semmit sem. Ebben az esetben az első paraméter a célszöveg, és a benne talált bármilyen helykitöltő helyettesítve lesz az azt követő paraméterekkel.

[% ReplacePlaceholders("This string has both %s and %s.", '<strong>bold text</strong>, '<em>italic text</em>') %]
                    

Sablonfeldolgozó parancsok

Megjegyzés

Azok a sorok, amelyek # karakterrel kezdődnek az elején, nem lesznek láthatók a HTML kimeneten. Ez használható a sablonkód magyarázatához, vagy annak egyes részei letiltásához is.

# ez a szakasz átmenetileg le van tiltva
# <div class="AsBlock">
#     <a href="...">hivatkozás</a>
# </div>
                    

[% InsertTemplate("Copyright.tt") %]

Figyelem

Felhívjuk a figyelmét, hogy az InsertTemplate parancs azért lett hozzáadva, hogy jobb visszafelé kompatibilitást nyújtson a régi DTL rendszerhez. Ez esetleg elavulttá válhat az OTRS jövőbeli verzióiban, és később eltávolításra kerülhet. Ha nem használ blokk parancsokat a felvett sablonjában, akkor nincs szüksége az InsertTemplate parancsra, és használhatja helyette a szabványos Template::Toolkit szintaxist, úgymint INCLUDE/PROCESS.

Felvesz egy másik sablonfájlt a jelenlegibe. A felvett fájl is tartalmazhat sablonparancsokat.

# a Copyright.tt felvétele
[% InsertTemplate("Copyright") %]
                    

Felhívjuk a figyelmét, hogy ez nem ugyanaz mint a Template::Toolkit [% INCLUDE %] parancsa, amely csak feldolgozza a hivatkozott sablont. Az [% InsertTemplate() %] tulajdonképpen hozzáadja a hivatkozott sablon tartalmát a jelenlegi sablonhoz azért, hogy együtt legyenek feldolgozhatók. Ez lehetővé teszi a beágyazott sablon számára, hogy ugyanazon környezethez vagy adatokhoz férjen hozzá mint a fő sablon.

[% RenderBlockStart %] / [% RenderBlockEnd %]

Figyelem

Vegye figyelembe, hogy a blokk parancsok azért lettek hozzáadva, hogy jobb visszafelé kompatibilitást nyújtsanak a régi DTL rendszerhez. Ezek esetleg elavulttá válhatnak az OTRS jövőbeli verzióiban, és később eltávolításra kerülhetnek. Azt javasoljuk, hogy blokk parancsok használata nélkül fejlesszen bármilyen új kódot. Használhatja a szabványos Template::Toolkit szintaxist a feltételes sablonkimenethez, mint például IF/ELSE, LOOP-ok és egyéb hasznos dolgok.

Ezzel a paranccsal lehet megadni egy sablonfájl részeit blokként. Ezt a blokkot határozottan ki kell tölteni egy függvényhívással az alkalmazásból, hogy megjelenjen az előállított kimeneten. Az alkalmazás meghívhatja a blokkot 0-szor (nem fog megjelenni a kimeneten), illetve 1 vagy többször (esetleg mindegyiket a sablonnak átadott adatparaméterek különböző halmazával).

Egy gyakori használati eset egy táblázat kitöltése dinamikus adatokkal:

<table class="DataTable">
    <thead>
        <tr>
            <th>[% Translate("Name") | html %]</th>
            <th>[% Translate("Type") | html %]</th>
            <th>[% Translate("Comment") | html %]</th>
            <th>[% Translate("Validity") | html %]</th>
            <th>[% Translate("Changed") | html %]</th>
            <th>[% Translate("Created") | html %]</th>
        </tr>
    </thead>
    <tbody>
[% RenderBlockStart("NoDataFoundMsg") %]
        <tr>
            <td colspan="6">
                [% Translate("No data found.") | html %]
            </td>
        </tr>
[% RenderBlockEnd("NoDataFoundMsg") %]
[% RenderBlockStart("OverviewResultRow") %]
        <tr>
            <td><a class="AsBlock" href="[% Env("Baselink") %]Action=[% Env("Action") %];Subaction=Change;ID=[% Data.ID | uri %]">[% Data.Name | html %]</a></td>
            <td>[% Translate(Data.TypeName) | html %]</td>
            <td title="[% Data.Comment | html %]">[% Data.Comment | truncate(20) | html %]</td>
            <td>[% Translate(Data.Valid) | html %]</td>
            <td>[% Data.ChangeTime | Localize("TimeShort") %]</td>
            <td>[% Data.CreateTime | Localize("TimeShort") %]</td>
        </tr>
[% RenderBlockEnd("OverviewResultRow") %]
    </tbody>
</table>
                    

A körülvevő táblázat a fejléccel mindig elő lesz állítva. Ha nem található adat, akkor a NoDataFoundMsg blokk egyszer lesz meghívva egy olyan táblázatot eredményezve, amelynek egy adatsora van a „Nem található adat.” üzenettel.

Ha találhatók adatok, akkor minden egyes sornál egy függvényhívás történik az OverViewResultRow blokknál (minden alkalommal átadva az adatokat ehhez a bizonyos sorhoz) egy olyan táblázatot eredményezve, amelynek annyi sora van, ahány eredmény található.

Nézzük meg, hogyan vannak meghívva a blokkok az alkalmazásmodulból:

my %List = $Kernel::OM->Get('Kernel::System::State)->StateList(
    UserID => 1,
    Valid  => 0,
);

# ha van bármilyen állapot, akkor azok meg fognak jelenni
if (%List) {

    # érvényes lista beszerzése
    my %ValidList = $Kernel::OM->Get('Kernel::System::Valid')->ValidList();
    for my $ListKey ( sort { $List{$a} cmp $List{$b} } keys %List ) {

        my %Data = $Kernel::OM->Get('Kernel::System::State)->StateGet( ID => $ListKey );
        $Kernel::OM->Get('Kernel::Output::HTML::Layout')->Block(
            Name => 'OverviewResultRow',
            Data => {
                Valid => $ValidList{ $Data{ValidID} },
                %Data,
            },
        );
    }
}

# egyébként egy „Nem található adat” üzenet jelenik meg
else {
    $Kernel::OM->Get('Kernel::Output::HTML::Layout')->Block(
        Name => 'NoDataFoundMsg',
        Data => {},
    );
}
                    

Figyelje meg, hogy a blokkoknak hogyan kell átadniuk mind a nevüket, mind egy opcionális adathalmazt különálló paraméterekként a blokkfüggvény hívásnak. Az adatbeszúró parancsoknak egy blokkon belül mindig az ezen blokk blokkfüggvény hívásához megadott adatokra van szükségük, nem az általános sablonmegjelenítő híváshoz.

A részletekért nézze meg a Kernel::Output::HTML::Layout dokumentációját az otrs.github.io/doc oldalon.

[% WRAPPER JSOnDocumentComplete %]...[% END %]

Megjelöli azt a JavaScript kódot, amelyet azután kell lefuttatni, miután az összes CSS, JavaScript és egyéb külső tartalom betöltődött, és az alapvető JavaScript előkészítés befejeződött. Vessünk egy pillantást ismét egy példára:

<form action="[% Env("CGIHandle") %]" method="post" enctype="multipart/form-data" name="MoveTicketToQueue" class="Validate PreventMultipleSubmits" id="MoveTicketToQueue">
    <input type="hidden" name="Action"       value="[% Env("Action") %]"/>
    <input type="hidden" name="Subaction"    value="MoveTicket"/>

    ...

    <div class="Content">
        <fieldset class="TableLike FixedLabel">
            <label class="Mandatory" for="DestQueueID"><span class="Marker">*</span> [% Translate("New Queue") | html %]:</label>
            <div class="Field">
                [% Data.MoveQueuesStrg %]
                <div id="DestQueueIDError" class="TooltipErrorMessage" ><p>[% Translate("This field is required.") | html %]</p></div>
                <div id="DestQueueIDServerError" class="TooltipErrorMessage"><p>[% Translate("This field is required.") | html %]</p></div>
[% WRAPPER JSOnDocumentComplete %]
<script type="text/javascript">
    $('#DestQueueID').bind('change', function (Event) {
        $('#NoSubmit').val('1');
        Core.AJAX.FormUpdate($('#MoveTicketToQueue'), 'AJAXUpdate', 'DestQueueID', ['NewUserID', 'OldUserID', 'NewStateID', 'NewPriorityID' [% Data.DynamicFieldNamesStrg %]]);
    });
</script>
[% END %]
                    </div>
                    <div class="Clear"></div>
                

Ez a kódrészlet egy kicsi űrlapot hoz létre, és rátesz egy onchange kezelőt a <select> elemre, amely aktivál egy AJAX-alapú űrlapfrissítést.

Miért van szükség a JavaScript kód körbezárására a [% WRAPPER JSOnDocumentComplete %]...[% END %] blokkban? Az OTRS 3.0-tól kezdve a JavaScript betöltést teljesítmény okok miatt áthelyezték az oldal lábrészébe. Ez azt jelenti, hogy az oldal <body> részén belül még nincsenek JavaScript programkönyvtárak betöltve. A [% WRAPPER JSOnDocumentComplete %]...[% END %] blokkal lehet biztos abban, hogy ez a JavaScript áthelyezésre kerül a végső HTML dokumentumnak egy olyan részébe, ahol csak akkor kerül végrehajtásra, miután a teljes külső JavaScript és CSS tartalom sikeresen be lett töltve és elő lett készítve.

A [% WRAPPER JSOnDocumentComplete %]...[% END %] blokkon belül használhatja a <script> címkéket a JavaScript kód körbezárásához, de ezt nem kell megtennie. Előnyös lehet, mert engedélyezni fogja a helyes szintaxis-kiemelést az olyan integrált fejlesztői környezetekben, amelyek támogatják azt.

Egy sablonfájl használata

Rendben, de tulajdonképpen hogyan kell egy sablonfájlt feldolgozni és az eredményt előállítani? Ez igazán egyszerű:

# render AdminState.tt
$Output .= $Kernel::OM->Get('Kernel::Output::HTML::Layout')->Output(
    TemplateFile => 'AdminState',
    Data         => \%Param,
);
            

Az előtétprogram modulokban a Kernel::Output::HTML::Layout objektum Output() függvénye lesz meghívva (miután az összes szükséges blokk meg lett hívva ebben a sablonban) a végső kimenet előállításához. Adatparaméterek opcionális halmaza is átadásra kerül a sablonnak minden olyan adatbeszúró parancsnál, amelyek nincsenek egy blokk belsejében.