Csomagátírás

OTRS 4-ről 5-re
Átszerkesztett Kernel/Output/HTML
Elő-kimenetszűrők
IE 8 és IE 9
Általános felület API változás a „Ticket” csatlakozóban
Előnézeti függvények a dinamikus statisztikákban
Eldobott HTML nyomtatás
Továbbfejlesztett fordítási szöveg kinyerés
OTRS 3.3-ról 4-re
Új objektumkezelés
Eltávolított CacheInternalObject
Áthelyezett ütemező háttérprogram fájlok
Kódszakaszok frissítése az SOPM fájlokban
Új sablonmotor
Új FontAwesome verzió
Egységtesztek
Egyéni jegy előzmény típusok

Az OTRS minden új hibajavító vagy fő verziójával át kell írnia a csomagjait, és meg kell győződnie arról, hogy azok továbbra is működnek az OTRS API-val.

OTRS 4-ről 5-re

Ez a szakasz azokat a változtatásokat sorolja fel, amelyeket meg kell vizsgálnia, amikor átírja a csomagját az OTRS 4-ről 5-re.

Átszerkesztett Kernel/Output/HTML

Az OTRS 5-ben a Kernel/Output/HTML átszerkesztésre került. Az összes Perl modul (kivéve a Layout.pm fájlt) alkönyvtárakba került áthelyezésre (minden modulréteghez egybe). A sablonfájlok (témák) is áthelyezésre kerültek a Kernel/Output/HTML/Standard könyvtárból a Kernel/Output/HTML/Templates/Standard könyvtárba. Végezze el ezeket a költöztetéseket a saját kódjában is.

Elő-kimenetszűrők

Az OTRS 5-tel többé nincs támogatás a pre kimenetszűrőkhöz. Ezek a szűrők azelőtt változtatták meg a sablon tartalmát, mielőtt az feldolgozásra került volna, és potenciálisan rossz teljesítményproblémákhoz vezethettek, ugyanis a sablonokat többé nem lehetett gyorstárazni, és minden alkalommal fel kellett dolgozni és le kellett fordítani.

Egyszerűen váltson a pre kimenetszűrőről a post kimenetszűrőre. A tartalom lefordításához futtathatja közvetlenül a $LayoutObject->Translate() függvényt. Ha egyéb sablonszolgáltatásokra van szüksége, akkor egyszerűen határozzon meg egy kis sablonfájlt a kimenetszűrőhöz, és használja azt a tartalom megjelenítéséhez, mielőtt beültetné azt a fő adatokba. Néhány esetben hasznos lehet a jQuery DOM műveletek használata is a képernyőn lévő tartalom sorrendjének megváltoztatásához vagy cseréjéhez a reguláris kifejezések használata helyett. Ebben az esetben láthatatlan tartalomként kellene beültetnie az új kódot valahova az oldalba (például a Hidden osztállyal), majd ezután áthelyezni a jQuery használatával a megfelelő helyre a DOM-ban, és megjeleníteni azt.

Az utó-kimenetszűrők használatának megkönnyítéséhez létezik egy új mechanizmus is a HTML megjegyzéshorgok lekéréséhez bizonyos sablonoknál vagy blokkoknál. Hozzáadhatja a modulbeállító XML-be a következőhöz hasonlóan:

<ConfigItem
Name="Frontend::Template::GenerateBlockHooks###100-OTRSBusiness-ContactWithData"
Required="1" Valid="1">
    <Description Translatable="1">Generate HTML comment hooks for
the specified blocks so that filters can use them.</Description>
    <Group>OTRSBusiness</Group>
    <SubGroup>Core</SubGroup>
    <Setting>
        <Hash>
            <Item Key="AgentTicketZoom">
                <Array>
                    <Item>CustomerTable</Item>
                </Array>
            </Item>
        </Hash>
    </Setting>
</ConfigItem>
                

Ez azt fogja okozni, hogy az AgentTicketZoom.tt fájlban lévő CustomerTable blokk át lesz alakítva a HTML megjegyzésekben minden alkalommal, amikor megjelenítésre kerül:

<!--HookStartCustomerTable-->
... blokk kimenet ...
<!--HookEndCustomerTable-->
                

Ezzel a mechanizmussal minden csomag csak azokat a blokkhorgokat kérheti, amelyekre szüksége van, és következetesen kerülnek megjelenítésre. Ezek a HTML megjegyzések használhatók ezután a kimenetszűrőben az egyszerű reguláris kifejezés illesztéshez.

IE 8 és IE 9

Az IE 8 és IE 9 támogatást eldobták. Eltávolíthat minden kerülőmegoldást a kódjából, amelyet ezekhez a platformokhoz készített, valamint az összes olyan régi <CSS_IE7> vagy <CSS_IE8> betöltő címkét, amely még esetleg megbújik az XML beállítófájljaiban.

Általános felület API változás a „Ticket” csatlakozóban

A TicketGet() művelet másképpen ad vissza dinamikus mező adatokat a jegyből és a bejegyzésből mint az OTRS 4-ben. Mostantól ezek tisztán el vannak választva a többi statikus jegy- és bejegyzésmezőktől - innentől kezdve csoportosítva vannak egy DynamicField nevű listába. Eszerint alakítson át minden olyan alkalmazást, amely ezt a műveletet használja.

# megváltoztatva erről:

Ticket => [
    {
        TicketNumber       => '20101027000001',
        Title              => 'valamilyen cím',
        ...
        DynamicField_X     => 'x_ertek',
    },
]

# erre:

Ticket => [
    {
        TicketNumber       => '20101027000001',
        Title              => 'valamilyen cím',
        ...
        DynamicField => [
            {
                Name  => 'valamilyen név',
                Value => 'valamilyen érték',
            },
        ],
    },
]
                

Előnézeti függvények a dinamikus statisztikákban

Az új statisztikák grafikus felhasználói felülete egy előnézetet biztosít a jelenlegi beállításhoz. Ezt meg kell valósítani a statisztikák moduljaiban, és általában hamis vagy véletlenszerű adatokat adnak vissza sebességi okokból. Így minden olyan dinamikus (mátrix) statisztikánál, amely a GetStatElement() metódust biztosítja, hozzá kell adnia egy GetStatElementPreview() metódust is, valamint minden olyan dinamikus (tábla) statisztikánál, amely a GetStatTable() metódust biztosítja, hozzá kell adnia egy GetStatTablePreview() metódust ennek megfelelően. Egyébként az új statisztikák grafikus felhasználói felületén lévő előnézet nem fog működni a saját statisztikáinál. Példamegvalósításokat találhat az alapértelmezett OTRS statisztikákban.

Eldobott HTML nyomtatás

Az OTRS 5-ig a PDF::API2 Perl modul nem volt megtalálható minden rendszeren. Ezért létezett egy tartalék HTML nyomtatási mód. Az OTRS 5-tel a modul mostantól mellékelve van, és a HTML nyomtatás eldobásra került. A $LayoutObject->PrintHeader() és a PrintFooter() többé nem érhető el. Távolítsa el a tartalék HTML nyomtatást a kódjából, és változtassa meg a PDF előállításához, ha szükséges.

Továbbfejlesztett fordítási szöveg kinyerés

Az OTRS 5-ig a lefordítható szövegeket nem lehetett kinyerni a Perl kódból és az adatbázis XML meghatározásokból. Ez mostantól lehetséges, és elavulttá tette az olyan üres sablonokat, mint például az AAA*.tt sablont. A részletekért nézze meg ezt a szakaszt.

OTRS 3.3-ról 4-re

Ez a szakasz azokat a változtatásokat sorolja fel, amelyeket meg kell vizsgálnia, amikor átírja a csomagját az OTRS 3.3-ról 4-re.

Új objektumkezelés

Az OTRS 4-ig az objektumokat mind központilag, mind helyileg létre kellett hozni, és ekkor az összes objektumot le kellett kezelni a konstruktornak átadva azokat. Az OTRS 4-es és későbbi verzióinál mostantól létezik egy ObjectManager objektum, amely központosítja az egyke objektum létrehozását és hozzáférését.

Ez mindenek előtt azt fogja igényelni, hogy változtassa meg az összes felső szintű Perl parancsfájlt (csak a .pl fájlokat!) az ObjectManager betöltéséhez és biztosításához az összes OTRS objektumnál. Példaként nézzük meg az otrs.CheckDB.pl parancsfájlt az OTRS 3.3-ból:

use strict;
use warnings;

use File::Basename;
use FindBin qw($RealBin);
use lib dirname($RealBin);
use lib dirname($RealBin) . '/Kernel/cpan-lib';
use lib dirname($RealBin) . '/Custom';

use Kernel::Config;
use Kernel::System::Encode;
use Kernel::System::Log;
use Kernel::System::Main;
use Kernel::System::DB;

# a szokásos objektumok létrehozása
my %CommonObject = ();
$CommonObject{ConfigObject} = Kernel::Config->new();
$CommonObject{EncodeObject} = Kernel::System::Encode->new(%CommonObject);
$CommonObject{LogObject}    = Kernel::System::Log->new(
    LogPrefix    => 'OTRS-otrs.CheckDB.pl',
    ConfigObject => $CommonObject{ConfigObject},
);
$CommonObject{MainObject} = Kernel::System::Main->new(%CommonObject);
$CommonObject{DBObject}   = Kernel::System::DB->new(%CommonObject);
                

Láthatjuk, hogy rengeteg kódot használnak a csomagok betöltéséhez és a gyakori objektumok létrehozásához, amelyet a parancsfájlban át kell adni a használandó OTRS objektumoknak. Az OTRS 4-gyel ez egy kicsit máshogy néz ki:

use strict;
use warnings;

use File::Basename;
use FindBin qw($RealBin);
use lib dirname($RealBin);
use lib dirname($RealBin) . '/Kernel/cpan-lib';
use lib dirname($RealBin) . '/Custom';

use Kernel::System::ObjectManager;

# a szokásos objektumok létrehozása
local $Kernel::OM = Kernel::System::ObjectManager->new(
    'Kernel::System::Log' => {
        LogPrefix => 'OTRS-otrs.CheckDB.pl',
    },
);

# az adatbázis-objektum lekérése
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
                

Az új kód egy kicsivel rövidebb mint a régi. Többé nem szükséges az összes csomag betöltése, elég csak az ObjectManager objektumot. Azután a $Kernel::OM->Get('Sajat::Perl::Csomag') használható az objektumok példányainak lekéréséhez, amelyeket csak egyszer kell létrehozni. A LogPrefix beállítás vezérli azokat a naplóüzeneteket, amelyeket a Kernel::System::Log ír ki, így az szintén elhagyható.

Ebből a példából kikövetkeztetheti az általános átírási irányelvet is, amikor az objektumok hozzáférése jön: többé ne tárolja azokat a $Self változóban (hacsak bizonyos okokból ez nem szükséges). Egyszerűen kérje le és használja az objektumokat igény szerint, úgymint $Kernel::OM->Get('Kernel::System::Log')->Log(...). Ennek megvan az az előnye is, hogy a Log objektumot csak akkor kell majd létrehozni, ha valamit naplózni kell. Néha hasznos lehet helyi változókat is létrehozni, ha egy objektumot többször használnak egy függvényben, mint például a fenti példában lévő $DBObject objektumot.

Nem kell sokkal többet tudnia az olyan csomagok átírásakor, amelyeknek betölthetőnek kell lenniük az ObjectManager használatával. Meg kell határozniuk azokat a modulokat, amelyeket használnak (a $Kernel::OM->Get() függvényen keresztül) ehhez hasonlóan:

our @ObjectDependencies = (
    'Kernel::Config',
    'Kernel::System::Log',
    'Kernel::System::Main',
);
                

Az @ObjectDependencies meghatározás szükséges az ObjectManager objektumhoz a helyes sorrend megtartásához az objektumok megsemmisítésekor.

Nézzük meg a Valid.pm fájlt az OTRS 3.3-ból és a 4-ből, hogy lássuk a különbséget. A régi:

package Kernel::System::Valid;

use strict;
use warnings;

use Kernel::System::CacheInternal;

...

sub new {
    my ( $Type, %Param ) = @_;

    # új kivonat lefoglalása az objektumhoz
    my $Self = {};
    bless( $Self, $Type );

    # a szükséges objektumok ellenőrzése
    for my $Object (qw(DBObject ConfigObject LogObject EncodeObject MainObject)) {
        $Self->{$Object} = $Param{$Object} || die "Nincs $Object!";
    }

    $Self->{CacheInternalObject} = Kernel::System::CacheInternal->new(
        %{$Self},
        Type => 'Valid',
        TTL  => 60 * 60 * 24 * 20,
    );

    return $Self;
}

...

sub ValidList {
    my ( $Self, %Param ) = @_;

    # gyorsítótár olvasása
    my $CacheKey = 'ValidList';
    my $Cache = $Self->{CacheInternalObject}->Get( Key => $CacheKey );
    return %{$Cache} if $Cache;

    # lista lekérése az adatbázisból
    return if !$Self->{DBObject}->Prepare( SQL => 'SELECT id, name FROM valid' );

    # az eredmény lekérése
    my %Data;
    while ( my @Row = $Self->{DBObject}->FetchrowArray() ) {
        $Data{ $Row[0] } = $Row[1];
    }

    # gyorsítótár beállítása
    $Self->{CacheInternalObject}->Set( Key => $CacheKey, Value => \%Data );

    return %Data;
}
                

Az új:

package Kernel::System::Valid;

use strict;
use warnings;

our @ObjectDependencies = (
    'Kernel::System::Cache',
    'Kernel::System::DB',
    'Kernel::System::Log',
);

...

sub new {
    my ( $Type, %Param ) = @_;

    # új kivonat lefoglalása az objektumhoz
    my $Self = {};
    bless( $Self, $Type );

    $Self->{CacheType} = 'Valid';
    $Self->{CacheTTL}  = 60 * 60 * 24 * 20;

    return $Self;
}

...

sub ValidList {
    my ( $Self, %Param ) = @_;

    # gyorsítótár olvasása
    my $CacheKey = 'ValidList';
    my $Cache    = $Kernel::OM->Get('Kernel::System::Cache')->Get(
        Type => $Self->{CacheType},
        Key  => $CacheKey,
    );
    return %{$Cache} if $Cache;

    # adatbázis-objektum lekérése
    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');

    # lista lekérése az adatbázisból
    return if !$DBObject->Prepare( SQL => 'SELECT id, name FROM valid' );

    # az eredmény lekérése
    my %Data;
    while ( my @Row = $DBObject->FetchrowArray() ) {
        $Data{ $Row[0] } = $Row[1];
    }

    # gyorsítótár beállítása
    $Kernel::OM->Get('Kernel::System::Cache')->Set(
        Type  => $Self->{CacheType},
        TTL   => $Self->{CacheTTL},
        Key   => $CacheKey,
        Value => \%Data
    );

    return %Data;
}
                

Láthatja, hogy meg vannak határozva a függőségek, és az objektumok csak igény szerint vannak lekérve. A következő szakaszban a CacheInternalObject objektumról fogunk beszélni.

Eltávolított CacheInternalObject

Mivel a Kernel::System::Cache mostantól képes a memóriában is gyorstárazni, a Kernel::System::CacheInternal eldobásra került. Nézze meg az előző példát, hogy a kódot hogyan kell átköltöztetni: a globális Cache objektumot kell használnia, és át kell adnia a Type beállítást a Get(), Set(), Delete() és CleanUp() függvények minden egyes hívásához. A TTL paraméter mostantól elhagyható, és alapértelmezetten 20 nap, így csak akkor kell megadnia a Get() függvényben, ha eltérő TTL értékre van szüksége.

Figyelem

Különösen fontos a Type paraméter hozzáadása a CleanUp() függvényhez, mivel különben nem csak a jelenlegi gyorsítótártípus, hanem a teljes gyorsítótár törölve lehet.

Áthelyezett ütemező háttérprogram fájlok

Az ütemező háttérprogram fájljai áthelyezésre kerültek a Kernel/Scheduler mappából a Kernel/System/Scheduler mappába. Ha valamilyen egyéni feladatkezelő moduljai vannak, akkor azokat is át kell helyeznie.

Kódszakaszok frissítése az SOPM fájlokban

Az SOPM fájlokban lévő kódcímkéket is frissíteni kell. Többé ne használja a $Self változót. Régebben ezt használták az olyan OTRS objektumokhoz való hozzáféréshez, mint például a MainObject. Mostantól használja az ObjectManager objektumot. Itt egy példa a régi stílusra:

<CodeInstall Type="post">

# függvénynév meghatározása
my $FunctionName = 'CodeInstall';

# a csomagnév létrehozása
my $CodeModule = 'var::packagesetup::' . $Param{Structure}->{Name}->{Content};

# a modul betöltése
if ( $Self->{MainObject}->Require($CodeModule) ) {

    # új példány létrehozása
    my $CodeObject = $CodeModule->new( %{$Self} );

    if ($CodeObject) {

        # metódus elindítása
        if ( !$CodeObject->$FunctionName(%{$Self}) ) {
            $Self->{LogObject}->Log(
                Priority => 'error',
                Message  => "Nem sikerült meghívni a(z) $FunctionName() metódust ebben: $CodeModule.pm."
            );
        }
    }

    # hibakezelés
    else {
        $Self->{LogObject}->Log(
            Priority => 'error',
            Message  => "Nem sikerült meghívni a new() metódust ebben: $CodeModule.pm."
        );
    }
}

</CodeInstall>
                

Most ezt a következővel kell helyettesíteni:

<CodeInstall Type="post"><![CDATA[
$Kernel::OM->Get('var::packagesetup::SajatCsomag')->CodeInstall();
]]></CodeInstall>
                

Új sablonmotor

Az OTRS 4-gyel a DTL sablonmotort a Template::Toolkit váltotta. A részletekért nézze meg a sablonozó szakaszt, hogy hogyan néz ki az új sablonszintaxis.

Ezek azok a változtatások, amelyet alkalmaznia kell, amikor a meglévő DTL sablonokat az új Template::Toolkit szintaxisra alakítja át:

4.1. táblázat - Sablonváltoztatások az OTRS 3.3-ról 4-re

DTL címke Template::Toolkit címke
$Data{"Name"} [% Data.Name %]
$Data{"Complex-Name"} [% Data.item("Complex-Name") %]
$QData{"Name"} [% Data.Name | html %]
$QData{"Name", "$Length"} [% Data.Name | truncate($Length) | html %]
$LQData{"Name"} [% Data.Name | uri %]
$Quote{"Szöveg", "$Length"} nem lehet közvetlenül lecserélni, lásd a lenti példákat
$Quote{"$Config{"Name"}"} [% Config("Name") | html %]
$Quote{"$Data{"Name"}", "$Length"} [% Data.Name | truncate($Length) | html %]
$Quote{"$Data{"Content"}","$QData{"MaxLength"}"} [% Data.Name | truncate(Data.MaxLength) | html %]
$Quote{"$Text{"$Data{"Content"}"}","$QData{"MaxLength"}"} [% Data.Content | Translate | truncate(Data.MaxLength) | html %]
$Config{"Name"} [% Config("Name") %]
$Env{"Name"} [% Env("Name") %]
$QEnv{"Name"} [% Env("Name") | html %]
$Text{"Szöveg %s helykitöltőkkel", "String"} [% Translate("Szöveg %s helykitöltőkkel", "String") | html %]
$Text{"Szöveg dinamikus %s helykitöltőkkel", "$QData{Name}"} [% Translate("Szöveg dinamikus %s helykitöltőkkel", Data.Name) | html %]
'$JSText{"Szöveg dinamikus %s helykitöltőkkel", "$QData{Name}"}' [% Translate("Szöveg dinamikus %s helykitöltőkkel", Data.Name) | JSON %]
"$JSText{"Szöveg dinamikus %s helykitöltőkkel", "$QData{Name}"}" [% Translate("Szöveg dinamikus %s helykitöltőkkel", Data.Name) | JSON %]
$TimeLong{"$Data{"CreateTime"}"} [% Data.CreateTime | Localize("TimeLong") %]
$TimeShort{"$Data{"CreateTime"}"} [% Data.CreateTime | Localize("TimeShort") %]
$Date{"$Data{"CreateTime"}"} [% Data.CreateTime | Localize("Date") %]
<-- dtl:block:Name -->...<-- dtl:block:Name --> [% RenderBlockStart("Name") %]...[% RenderBlockEnd("Name") %]
<-- dtl:js_on_document_complete -->...<-- dtl:js_on_document_complete --> [% WRAPPER JSOnDocumentComplete %]...[% END %]
<-- dtl:js_on_document_complete_placeholder --> [% PROCESS JSOnDocumentCompleteInsert %]
$Include{"Copyright"} [% InsertTemplate("Copyright") %]

Létezik egy bin/otrs.MigrateDTLtoTT.pl segítő parancsfájl is, amely automatikusan át fogja írni önnek a DTL-fájlokat a Template::Toolkit szintaxisra. Sikertelen lehet, ha hibák találhatók a DTL-jében, ezért először javítsa ki ezeket, és azután futtassa újra a parancsfájlt.

Van még további néhány dolog, amelyet tudomásul kell vennie a kód átírásakor az új sablonmotorra:

  • Az összes nyelvi fájlnak mostantól rendelkeznie kell a use utf8; kikötéssel.

  • A Layout::Get() mostantól elavult. Használja a Layout::Translate() függvényt helyette.

  • A Perl-kódban a $Text{""} összes előfordulását mostantól le kell cserélni a Layout::Translate() hívásaival.

    Ez azért van, mert a DTL-ben nem volt különválasztás a sablon és az adatok között. Ha DTL-címkék voltak beszúrva valamilyen adat részeként, akkor a motornak továbbra is fel kellene dolgozni azokat. Ez többé nincs a Template::Toolkit esetén, mert itt a sablon és az adatok szigorú különválasztása van.

    Tipp: ha valamikor interpolálnia kell a címkéket az adatokban, akkor ehhez használhatja az Interpolate szűrőt ([% Data.Name | Interpolate %]). Ez nem ajánlott biztonsági és teljesítménybeli okok miatt!

  • Hasonló okból a dtl:js_on_document_complete által körbezárt dinamikusan beágyazott JavaScript sem fog működni többé. Használja a Layout::AddJSOnDocumentComplete() függvényt ahelyett, hogy ezt sablonadatként ágyazná be.

    Erre találhat egy példát a Kernel/System/DynamicField/Driver/BaseSelect.pm fájlban.

  • Legyen óvatos a pre kimenetszűrőkkel (a Frontend::Output::FilterElementPre objektumban beállítottakkal). Ezek továbbra is működnek, de meg fogják akadályozni a sablont, hogy gyorstárazza azokat. Ez komoly teljesítményproblémákhoz vezethet. Határozottan ne legyen egyetlen olyan pre kimenetszűrője sem, amely az összes sablonnal dolgozik, hanem korlátozza azokat bizonyos sablonokra a konfigurációs beállításokon keresztül.

    A post kimenetszűrőknek (Frontend::Output::FilterElementPost) nincsenek ilyen erős negatív teljesítményhatásaik. Azonban ezeket is körültekintéssel kell használni, és nem minden sablonnál.

Új FontAwesome verzió

Az OTRS 4-gyel egy új verzióra frissítettük a FontAwesome betűkészletet is. Ennek következtében az ikonok CSS-osztályai megváltoztak. Miközben a korábbi ikonok egy icon-{ikonnév} szerű sémával voltak meghatározva, ezt mostantól a fa fa-{ikonnév} formában kell megadni.

Ezen változtatás miatt meg kell győződnie arról, hogy frissítette-e az összes olyan egyéni előtétprogram-modul regisztrációit, amelyek ikonokat használnak (például a felső navigációs sávnál) az új séma használatához. Ez igaz az olyan sablonoknál is, ahol ikonelemeket használ, mint például <i class="icon-{ikonnév}"></i>.

Egységtesztek

Az OTRS 4-gyel az egységtesztekben a $Self többé nem szolgáltat olyan gyakori objektumokat, mint például a MainObject. Mindig a $Kernel::OM->Get('...') függvényt használja ezen objektumok lekéréséhez.

Egyéni jegy előzmény típusok

Ha bármilyen egyéni jegy előzmény típusokat használ, akkor két lépést kell elvégeznie, hogy azok helyesen legyenek megjelenítve az OTRS 4+ AgentTicketHistory képernyőjén.

Először regisztrálnia kell az egyéni jegy előzmény típusait a rendszerbeállításokon keresztül. Ez így nézhet ki:

<ConfigItem Name="Ticket::Frontend::HistoryTypes###100-MyCustomModule" Required="1" Valid="1">
    <Description Translatable="1">Controls how to display the ticket history entries as readable values.</Description>
    <Group>Ticket</Group>
    <SubGroup>Frontend::Agent::Ticket::ViewHistory</SubGroup>
    <Setting>
        <Hash>
            <Item Key="MyCustomType" Translatable="1">Added information (%s)</Item>
        </Hash>
    </Setting>
</ConfigItem>
                

A második lépés az egyéni jegy előzmény típusnál biztosított angol szöveg lefordítása a nyelvi fájljaiban, ha szükséges. Ennyi!

Ha érdeklődik a részletek iránt, akkor nézze meg ezt a véglegesítést azon változtatásokkal kapcsolatos további információkról, amelyek az OTRS-ben történtek.