This commit is contained in:
2024-10-14 00:08:40 +02:00
parent dbfba56f66
commit 1462d52e13
4572 changed files with 2658864 additions and 0 deletions

View File

@@ -0,0 +1,157 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::Output::HTML::Layout::AJAX;
use strict;
use warnings;
use Kernel::System::JSON ();
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::Output::HTML::Layout::AJAX - all AJAX-related HTML functions
=head1 DESCRIPTION
All AJAX-related HTML functions
=head1 PUBLIC INTERFACE
=head2 BuildSelectionJSON()
build a JSON output which can be used for e. g. data for pull downs
my $JSON = $LayoutObject->BuildSelectionJSON(
[
Data => $ArrayRef, # use $HashRef, $ArrayRef or $ArrayHashRef (see below)
Name => 'TheName', # name of element
SelectedID => [1, 5, 3], # (optional) use integer or arrayref (unable to use with ArrayHashRef)
SelectedValue => 'test', # (optional) use string or arrayref (unable to use with ArrayHashRef)
Sort => 'NumericValue', # (optional) (AlphanumericValue|NumericValue|AlphanumericKey|NumericKey|TreeView) unable to use with ArrayHashRef
SortReverse => 0, # (optional) reverse the list
Translation => 1, # (optional) default 1 (0|1) translate value
PossibleNone => 0, # (optional) default 0 (0|1) add a leading empty selection
Max => 100, # (optional) default 100 max size of the shown value
],
[
# ...
]
);
=cut
sub BuildSelectionJSON {
my ( $Self, $Array ) = @_;
my %DataHash;
for my $Data ( @{$Array} ) {
my %Param = %{$Data};
# log object
my $LogObject = $Kernel::OM->Get('Kernel::System::Log');
# check needed stuff
for (qw(Name)) {
if ( !defined $Param{$_} ) {
$LogObject->Log(
Priority => 'error',
Message => "Need $_!"
);
return;
}
}
if ( !defined( $Param{Data} ) ) {
if ( !$Param{PossibleNone} ) {
$LogObject->Log(
Priority => 'error',
Message => "Need Data!"
);
return;
}
$DataHash{''} = '-';
}
elsif ( ref $Param{Data} eq '' ) {
$DataHash{ $Param{Name} } = $Param{Data};
}
elsif ( defined $Param{KeepData} && $Param{KeepData} ) {
$DataHash{ $Param{Name} } = $Param{Data};
}
else {
# create OptionRef
my $OptionRef = $Self->_BuildSelectionOptionRefCreate(
%Param,
HTMLQuote => 0,
);
# create AttributeRef
my $AttributeRef = $Self->_BuildSelectionAttributeRefCreate(%Param);
# create DataRef
my $DataRef = $Self->_BuildSelectionDataRefCreate(
Data => $Param{Data},
AttributeRef => $AttributeRef,
OptionRef => $OptionRef,
);
# create data structure
if ( $AttributeRef && $DataRef ) {
my @DataArray;
for my $Row ( @{$DataRef} ) {
my $Key = '';
if ( defined $Row->{Key} ) {
$Key = $Row->{Key};
}
my $Value = '';
if ( defined $Row->{Value} ) {
$Value = $Row->{Value};
}
# DefaultSelected parameter for JavaScript New Option
my $DefaultSelected = Kernel::System::JSON::False();
# to set a disabled option (Disabled is not included in JavaScript New Option)
my $Disabled = Kernel::System::JSON::False();
if ( $Row->{Selected} ) {
$DefaultSelected = Kernel::System::JSON::True();
}
elsif ( $Row->{Disabled} ) {
$DefaultSelected = Kernel::System::JSON::False();
$Disabled = Kernel::System::JSON::True();
}
# Selected parameter for JavaScript NewOption
my $Selected = $DefaultSelected;
push @DataArray, [ $Key, $Value, $DefaultSelected, $Selected, $Disabled ];
}
$DataHash{ $AttributeRef->{name} } = \@DataArray;
}
}
}
return $Self->JSONEncode(
Data => \%DataHash,
);
}
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<https://otrs.org/>).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (GPL). If you
did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
=cut

View File

@@ -0,0 +1,572 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::Output::HTML::Layout::Article;
use strict;
use warnings;
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::Output::HTML::Layout::Article - Helper functions for article rendering.
=head1 PUBLIC INTERFACE
=head2 ArticleFields()
Get article fields as returned by specific article backend.
my %ArticleFields = $LayoutObject->ArticleFields(
TicketID => 123, # (required)
ArticleID => 123, # (required)
);
Returns article fields hash:
%ArticleFields = (
Sender => { # mandatory
Label => 'Sender',
Value => 'John Smith',
Prio => 100,
},
Subject => { # mandatory
Label => 'Subject',
Value => 'Message',
Prio => 200,
},
...
);
=cut
sub ArticleFields {
my ( $Self, %Param ) = @_;
# Check needed stuff.
for my $Needed (qw(TicketID ArticleID)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
my $BackendObject = $Self->_BackendGet(%Param);
# Return backend response.
return $BackendObject->ArticleFields(
%Param,
);
}
=head2 ArticlePreview()
Get article content preview as returned by specific article backend.
my $ArticlePreview = $LayoutObject->ArticlePreview(
TicketID => 123, # (required)
ArticleID => 123, # (required)
ResultType => 'plain', # (optional) plain|HTML, default: HTML
MaxLength => 50, # (optional) performs trimming (for plain result only)
);
Returns article preview in scalar form:
$ArticlePreview = 'Hello, world!';
=cut
sub ArticlePreview {
my ( $Self, %Param ) = @_;
# Check needed stuff.
for my $Needed (qw(TicketID ArticleID)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
my $BackendObject = $Self->_BackendGet(%Param);
# Return backend response.
return $BackendObject->ArticlePreview(
%Param,
);
}
=head2 ArticleActions()
Get available article actions as returned by specific article backend.
my @Actions = $LayoutObject->ArticleActions(
TicketID => 123, # (required)
ArticleID => 123, # (required)
);
Returns article action array:
@Actions = (
{
ItemType => 'Dropdown',
DropdownType => 'Reply',
StandardResponsesStrg => $StandardResponsesStrg,
Name => 'Reply',
Class => 'AsPopup PopupType_TicketAction',
Action => 'AgentTicketCompose',
FormID => 'Reply' . $Article{ArticleID},
ResponseElementID => 'ResponseID',
Type => $Param{Type},
},
{
ItemType => 'Link',
Description => 'Forward article via mail',
Name => 'Forward',
Class => 'AsPopup PopupType_TicketAction',
Link =>
"Action=AgentTicketForward;TicketID=$Ticket{TicketID};ArticleID=$Article{ArticleID}"
},
...
);
=cut
sub ArticleActions {
my ( $Self, %Param ) = @_;
# Check needed stuff.
for my $Needed (qw(TicketID ArticleID)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
my $BackendObject = $Self->_BackendGet(%Param);
# Return backend response.
return $BackendObject->ArticleActions(
%Param,
UserID => $Self->{UserID},
);
}
=head2 ArticleCustomerRecipientsGet()
Get customer users from an article to use as recipients.
my @CustomerUserIDs = $LayoutObject->ArticleCustomerRecipientsGet(
TicketID => 123, # (required)
ArticleID => 123, # (required)
);
Returns array of customer user IDs who should receive a message:
@CustomerUserIDs = (
'customer-1',
'customer-2',
...
);
=cut
sub ArticleCustomerRecipientsGet {
my ( $Self, %Param ) = @_;
for my $Needed (qw(TicketID ArticleID)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
my $BackendObject = $Self->_BackendGet(%Param);
# Return backend response.
return $BackendObject->ArticleCustomerRecipientsGet(
%Param,
UserID => $Self->{UserID},
);
}
=head2 ArticleQuote()
get body and attach e. g. inline documents and/or attach all attachments to
upload cache
for forward or split, get body and attach all attachments
my $HTMLBody = $LayoutObject->ArticleQuote(
TicketID => 123,
ArticleID => 123,
FormID => $Self->{FormID},
UploadCacheObject => $Self->{UploadCacheObject},
AttachmentsInclude => 1,
);
or just for including inline documents to upload cache
my $HTMLBody = $LayoutObject->ArticleQuote(
TicketID => 123,
ArticleID => 123,
FormID => $Self->{FormID},
UploadCacheObject => $Self->{UploadCacheObject},
AttachmentsInclude => 0,
);
Both will also work without rich text (if $ConfigObject->Get('Frontend::RichText')
is false), return param will be text/plain instead.
=cut
sub ArticleQuote {
my ( $Self, %Param ) = @_;
for my $Needed (qw(TicketID ArticleID FormID UploadCacheObject)) {
if ( !$Param{$Needed} ) {
$Self->FatalError( Message => "Need $Needed!" );
}
}
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
my $ArticleBackendObject = $ArticleObject->BackendForArticle(
ArticleID => $Param{ArticleID},
TicketID => $Param{TicketID}
);
# body preparation for plain text processing
if ( $ConfigObject->Get('Frontend::RichText') ) {
my $Body = '';
my %NotInlineAttachments;
my %QuoteArticle = $ArticleBackendObject->ArticleGet(
TicketID => $Param{TicketID},
ArticleID => $Param{ArticleID},
DynamicFields => 0,
);
# Get the attachments without message bodies.
$QuoteArticle{Atms} = {
$ArticleBackendObject->ArticleAttachmentIndex(
ArticleID => $Param{ArticleID},
ExcludePlainText => 1,
ExcludeHTMLBody => 1,
)
};
# Check if there is HTML body attachment.
my %AttachmentIndexHTMLBody = $ArticleBackendObject->ArticleAttachmentIndex(
ArticleID => $Param{ArticleID},
OnlyHTMLBody => 1,
);
my ($HTMLBodyAttachmentID) = sort keys %AttachmentIndexHTMLBody;
if ($HTMLBodyAttachmentID) {
my %AttachmentHTML = $ArticleBackendObject->ArticleAttachment(
TicketID => $QuoteArticle{TicketID},
ArticleID => $QuoteArticle{ArticleID},
FileID => $HTMLBodyAttachmentID,
);
my $Charset = $AttachmentHTML{ContentType} || '';
$Charset =~ s/.+?charset=("|'|)(\w+)/$2/gi;
$Charset =~ s/"|'//g;
$Charset =~ s/(.+?);.*/$1/g;
# convert html body to correct charset
$Body = $Kernel::OM->Get('Kernel::System::Encode')->Convert(
Text => $AttachmentHTML{Content},
From => $Charset,
To => $Self->{UserCharset},
Check => 1,
);
# get HTML utils object
my $HTMLUtilsObject = $Kernel::OM->Get('Kernel::System::HTMLUtils');
# add url quoting
$Body = $HTMLUtilsObject->LinkQuote(
String => $Body,
);
# strip head, body and meta elements
$Body = $HTMLUtilsObject->DocumentStrip(
String => $Body,
);
# display inline images if exists
my $SessionID = '';
if ( $Self->{SessionID} && !$Self->{SessionIDCookie} ) {
$SessionID = ';' . $Self->{SessionName} . '=' . $Self->{SessionID};
}
my $AttachmentLink = $Self->{Baselink}
. 'Action=PictureUpload'
. ';FormID='
. $Param{FormID}
. $SessionID
. ';ContentID=';
# search inline documents in body and add it to upload cache
my %Attachments = %{ $QuoteArticle{Atms} };
my %AttachmentAlreadyUsed;
$Body =~ s{
(=|"|')cid:(.*?)("|'|>|\/>|\s)
}
{
my $Start= $1;
my $ContentID = $2;
my $End = $3;
# improve html quality
if ( $Start ne '"' && $Start ne '\'' ) {
$Start .= '"';
}
if ( $End ne '"' && $End ne '\'' ) {
$End = '"' . $End;
}
# find attachment to include
ATMCOUNT:
for my $AttachmentID ( sort keys %Attachments ) {
if ( lc $Attachments{$AttachmentID}->{ContentID} ne lc "<$ContentID>" ) {
next ATMCOUNT;
}
# get whole attachment
my %AttachmentPicture = $ArticleBackendObject->ArticleAttachment(
TicketID => $Param{TicketID},
ArticleID => $Param{ArticleID},
FileID => $AttachmentID,
);
# content id cleanup
$AttachmentPicture{ContentID} =~ s/^<//;
$AttachmentPicture{ContentID} =~ s/>$//;
# find cid, add attachment URL and remember, file is already uploaded
$ContentID = $AttachmentLink . $Self->LinkEncode( $AttachmentPicture{ContentID} );
# add to upload cache if not uploaded and remember
if (!$AttachmentAlreadyUsed{$AttachmentID}) {
# remember
$AttachmentAlreadyUsed{$AttachmentID} = 1;
# write attachment to upload cache
$Param{UploadCacheObject}->FormIDAddFile(
FormID => $Param{FormID},
Disposition => 'inline',
%{ $Attachments{$AttachmentID} },
%AttachmentPicture,
);
}
}
# return link
$Start . $ContentID . $End;
}egxi;
# find inline images using Content-Location instead of Content-ID
ATTACHMENT:
for my $AttachmentID ( sort keys %Attachments ) {
next ATTACHMENT if !$Attachments{$AttachmentID}->{ContentID};
# get whole attachment
my %AttachmentPicture = $ArticleBackendObject->ArticleAttachment(
TicketID => $Param{TicketID},
ArticleID => $Param{ArticleID},
FileID => $AttachmentID,
);
# content id cleanup
$AttachmentPicture{ContentID} =~ s/^<//;
$AttachmentPicture{ContentID} =~ s/>$//;
$Body =~ s{
("|')(\Q$AttachmentPicture{ContentID}\E)("|'|>|\/>|\s)
}
{
my $Start= $1;
my $ContentID = $2;
my $End = $3;
# find cid, add attachment URL and remember, file is already uploaded
$ContentID = $AttachmentLink . $Self->LinkEncode( $AttachmentPicture{ContentID} );
# add to upload cache if not uploaded and remember
if (!$AttachmentAlreadyUsed{$AttachmentID}) {
# remember
$AttachmentAlreadyUsed{$AttachmentID} = 1;
# write attachment to upload cache
$Param{UploadCacheObject}->FormIDAddFile(
FormID => $Param{FormID},
Disposition => 'inline',
%{ $Attachments{$AttachmentID} },
%AttachmentPicture,
);
}
# return link
$Start . $ContentID . $End;
}egxi;
}
# find not inline images
ATTACHMENT:
for my $AttachmentID ( sort keys %Attachments ) {
next ATTACHMENT if $AttachmentAlreadyUsed{$AttachmentID};
$NotInlineAttachments{$AttachmentID} = 1;
}
}
# attach also other attachments on article forward
if ( $Body && $Param{AttachmentsInclude} ) {
for my $AttachmentID ( sort keys %NotInlineAttachments ) {
my %Attachment = $ArticleBackendObject->ArticleAttachment(
TicketID => $Param{TicketID},
ArticleID => $Param{ArticleID},
FileID => $AttachmentID,
);
# add attachment
$Param{UploadCacheObject}->FormIDAddFile(
FormID => $Param{FormID},
%Attachment,
Disposition => 'attachment',
);
}
}
# Fallback for non-MIMEBase articles: get article HTML content if it exists.
if ( !$Body ) {
$Body = $Self->ArticlePreview(
TicketID => $Param{TicketID},
ArticleID => $Param{ArticleID},
);
}
return $Body if $Body;
}
# as fallback use text body for quote
my %Article = $ArticleBackendObject->ArticleGet(
TicketID => $Param{TicketID},
ArticleID => $Param{ArticleID},
DynamicFields => 0,
);
# check if original content isn't text/plain or text/html, don't use it
if ( !$Article{ContentType} ) {
$Article{ContentType} = 'text/plain';
}
if ( $Article{ContentType} !~ /text\/(plain|html)/i ) {
$Article{Body} = '-> no quotable message <-';
$Article{ContentType} = 'text/plain';
}
else {
$Article{Body} = $Self->WrapPlainText(
MaxCharacters => $ConfigObject->Get('Ticket::Frontend::TextAreaEmail') || 82,
PlainText => $Article{Body},
);
}
# attach attachments
if ( $Param{AttachmentsInclude} ) {
my %ArticleIndex = $ArticleBackendObject->ArticleAttachmentIndex(
ArticleID => $Param{ArticleID},
ExcludePlainText => 1,
ExcludeHTMLBody => 1,
);
for my $Index ( sort keys %ArticleIndex ) {
my %Attachment = $ArticleBackendObject->ArticleAttachment(
TicketID => $Param{TicketID},
ArticleID => $Param{ArticleID},
FileID => $Index,
);
# add attachment
$Param{UploadCacheObject}->FormIDAddFile(
FormID => $Param{FormID},
%Attachment,
Disposition => 'attachment',
);
}
}
# return body as html
if ( $ConfigObject->Get('Frontend::RichText') ) {
$Article{Body} = $Self->Ascii2Html(
Text => $Article{Body},
HTMLResultMode => 1,
LinkFeature => 1,
);
}
# return body as plain text
return $Article{Body};
}
sub _BackendGet {
my ( $Self, %Param ) = @_;
# Check needed stuff.
for my $Needed (qw(TicketID ArticleID)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
my $ArticleBackendObject = $Kernel::OM->Get('Kernel::System::Ticket::Article')->BackendForArticle(%Param);
# Determine channel name for this article.
my $ChannelName = $ArticleBackendObject->ChannelNameGet();
my $Loaded = $Kernel::OM->Get('Kernel::System::Main')->Require(
"Kernel::Output::HTML::Article::$ChannelName",
);
return if !$Loaded;
return $Kernel::OM->Get("Kernel::Output::HTML::Article::$ChannelName");
}
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<https://otrs.org/>).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (GPL). If you
did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
=cut

View File

@@ -0,0 +1,209 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::Output::HTML::Layout::CustomerUser;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::Output::HTML::Layout::CustomerUser - all CustomerUser related HTML functions
=head1 PUBLIC INTERFACE
=head2 CustomerUserAddressBookListShow()
Returns a list of customer user as sort-able list with pagination.
This function is similar to L<Kernel::Output::HTML::Layout::CustomerUser::CustomerUserAddressBookListShow()>
in F<Kernel/Output/HTML/Layout/CustomerUser.pm>.
my $Output = $LayoutObject->CustomerUserAddressBookListShow(
CustomerUserIDs => $CustomerUserIDsRef, # total list of customer user ids, that can be listed
Total => scalar @{ $CustomerUserIDsRef }, # total number of customer user ids
View => $Self->{View}, # optional, the default value is 'AddressBook'
Filter => 'All',
Filters => \%NavBarFilter,
LinkFilter => $LinkFilter,
TitleName => 'Overview: CustomerUsers',
TitleValue => $Self->{Filter},
Env => $Self,
LinkPage => $LinkPage,
LinkSort => $LinkSort,
Frontend => 'Agent', # optional (Agent|Customer), default: Agent, indicates from which frontend this function was called
);
=cut
sub CustomerUserAddressBookListShow {
my ( $Self, %Param ) = @_;
# Take object ref to local, remove it from %Param (prevent memory leak).
my $Env = delete $Param{Env};
my $Frontend = $Param{Frontend} || 'Agent';
# Set defaut view mode to 'AddressBook'.
my $View = $Param{View} || 'AddressBook';
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# get backend from config
my $Backends = $ConfigObject->Get('CustomerUser::Frontend::Overview');
if ( !$Backends ) {
return $Self->FatalError(
Message => 'Need config option CustomerUser::Frontend::Overview',
);
}
# check for hash-ref
if ( ref $Backends ne 'HASH' ) {
return $Self->FatalError(
Message => 'Config option CustomerUser::Frontend::Overview needs to be a HASH ref!',
);
}
# check for config key
if ( !$Backends->{$View} ) {
return $Self->FatalError(
Message => "No config option found for the view '$View'!",
);
}
# nav bar
my $StartHit = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam(
Param => 'StartHit',
) || 1;
# get personal page shown count
my $PageShown = $ConfigObject->Get("CustomerUser::Frontend::$Self->{Action}")->{PageShown} || 100;
# check start option, if higher then elements available, set
# it to the last overview page (Thanks to Stefan Schmidt!)
if ( $StartHit > $Param{Total} ) {
my $Pages = int( ( $Param{Total} / $PageShown ) + 0.99999 );
$StartHit = ( ( $Pages - 1 ) * $PageShown ) + 1;
}
# set page limit and build page nav
my $Limit = $Param{Limit} || 20_000;
my %PageNav = $Self->PageNavBar(
Limit => $Limit,
StartHit => $StartHit,
PageShown => $PageShown,
AllHits => $Param{Total} || 0,
Action => 'Action=' . $Self->{Action},
Link => $Param{LinkPage},
);
# build navbar content
$Self->Block(
Name => 'OverviewNavBar',
Data => \%Param,
);
# back link
if ( $Param{LinkBack} ) {
$Self->Block(
Name => 'OverviewNavBarPageBack',
Data => \%Param,
);
}
# check if page nav is available
if (%PageNav) {
$Self->Block(
Name => 'OverviewNavBarPageNavBar',
Data => \%PageNav,
);
# don't show context settings in AJAX case (e. g. in customer ticket history),
# because the submit with page reload will not work there
if ( !$Param{AJAX} ) {
$Self->Block(
Name => 'ContextSettings',
Data => {
%PageNav,
%Param,
},
);
}
}
# build html content
my $OutputNavBar = $Self->Output(
TemplateFile => 'AgentCustomerUserAddressBookOverviewNavBar',
Data => {%Param},
);
# create output
my $OutputRaw = '';
if ( !$Param{Output} ) {
$Self->Print(
Output => \$OutputNavBar,
);
}
else {
$OutputRaw .= $OutputNavBar;
}
# load module
if ( !$Kernel::OM->Get('Kernel::System::Main')->Require( $Backends->{$View}->{Module} ) ) {
return $Self->FatalError();
}
# check for backend object
my $Object = $Backends->{$View}->{Module}->new( %{$Env} );
return if !$Object;
# run module
my $Output = $Object->Run(
%Param,
Limit => $Limit,
StartHit => $StartHit,
PageShown => $PageShown,
AllHits => $Param{Total} || 0,
Frontend => $Frontend,
);
# create output
if ( !$Param{Output} ) {
$Self->Print(
Output => \$Output,
);
}
else {
$OutputRaw .= $Output;
}
# create overview nav bar
$Self->Block(
Name => 'OverviewNavBar',
Data => {%Param},
);
# return content if available
return $OutputRaw;
}
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<https://otrs.org/>).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (GPL). If you
did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
=cut

View File

@@ -0,0 +1,85 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::Output::HTML::Layout::Datepicker;
use strict;
use warnings;
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::Output::HTML::Layout::Datepicker - Datepicker data
=head1 DESCRIPTION
All valid functions.
=head1 PUBLIC INTERFACE
=head2 DatepickerGetVacationDays()
Returns a hash of all vacation days defined in the system.
$LayoutObject->DatepickerGetVacationDays();
=cut
sub DatepickerGetVacationDays {
my ( $Self, %Param ) = @_;
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# get the defined vacation days
my $TimeVacationDays = $ConfigObject->Get('TimeVacationDays');
my $TimeVacationDaysOneTime = $ConfigObject->Get('TimeVacationDaysOneTime');
if ( $Param{Calendar} ) {
if ( $ConfigObject->Get( "TimeZone::Calendar" . $Param{Calendar} . "Name" ) ) {
$TimeVacationDays = $ConfigObject->Get( "TimeVacationDays::Calendar" . $Param{Calendar} );
$TimeVacationDaysOneTime = $ConfigObject->Get(
"TimeVacationDaysOneTime::Calendar" . $Param{Calendar}
);
}
}
# translate the vacation description if possible
for my $Month ( sort keys %{$TimeVacationDays} ) {
for my $Day ( sort keys %{ $TimeVacationDays->{$Month} } ) {
$TimeVacationDays->{$Month}->{$Day}
= $Self->{LanguageObject}->Translate( $TimeVacationDays->{$Month}->{$Day} );
}
}
for my $Year ( sort keys %{$TimeVacationDaysOneTime} ) {
for my $Month ( sort keys %{ $TimeVacationDaysOneTime->{$Year} } ) {
for my $Day ( sort keys %{ $TimeVacationDaysOneTime->{$Year}->{$Month} } ) {
$TimeVacationDaysOneTime->{$Year}->{$Month}->{$Day} = $Self->{LanguageObject}->Translate(
$TimeVacationDaysOneTime->{$Year}->{$Month}->{$Day}
);
}
}
}
return {
'TimeVacationDays' => $TimeVacationDays,
'TimeVacationDaysOneTime' => $TimeVacationDaysOneTime,
};
}
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<https://otrs.org/>).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (GPL). If you
did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
=cut

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,737 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::Output::HTML::Layout::ITSMConfigItem;
use strict;
use warnings;
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::Output::HTML::Layout::ITSMConfigItem - all ConfigItem-related HTML functions
=head1 DESCRIPTION
All ITSM Configuration Management-related HTML functions
=head1 PUBLIC INTERFACE
=head2 ITSMConfigItemOutputStringCreate()
returns an output string
my $String = $LayoutObject->ITSMConfigItemOutputStringCreate(
Value => 11, # (optional)
Item => $ItemRef,
Print => 1, # (optional, default 0)
);
=cut
sub ITSMConfigItemOutputStringCreate {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{Item} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need Item!',
);
return;
}
# load backend
my $BackendObject = $Self->_ITSMLoadLayoutBackend(
Type => $Param{Item}->{Input}->{Type},
);
return '' if !$BackendObject;
# generate output string
my $String = $BackendObject->OutputStringCreate(%Param);
return $String;
}
=head2 ITSMConfigItemFormDataGet()
returns the values from the html form as hash reference
my $FormDataRef = $LayoutObject->ITSMConfigItemFormDataGet(
Key => 'Item::1::Node::3',
Item => $ItemRef,
ConfigItemID => 123,
);
=cut
sub ITSMConfigItemFormDataGet {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Argument (qw(Key Item ConfigItemID)) {
if ( !$Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!"
);
return;
}
}
# load backend
my $BackendObject = $Self->_ITSMLoadLayoutBackend(
Type => $Param{Item}->{Input}->{Type},
);
return {} if !$BackendObject;
# get form data
my $FormData = $BackendObject->FormDataGet(%Param);
return $FormData;
}
=head2 ITSMConfigItemInputCreate()
returns a input field html string
my $String = $LayoutObject->ITSMConfigItemInputCreate(
Key => 'Item::1::Node::3',
Value => 11, # (optional)
Item => $ItemRef,
);
=cut
sub ITSMConfigItemInputCreate {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Argument (qw(Key Item)) {
if ( !$Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!"
);
return;
}
}
# load backend
my $BackendObject = $Self->_ITSMLoadLayoutBackend(
Type => $Param{Item}->{Input}->{Type},
);
return '' if !$BackendObject;
# lookup item value
my $String = $BackendObject->InputCreate(%Param);
return $String;
}
=head2 ITSMConfigItemSearchFormDataGet()
returns the values from the search html form
my $ArrayRef = $LayoutObject->ITSMConfigItemSearchFormDataGet(
Key => 'Item::1::Node::3',
Item => $ItemRef,
);
=cut
sub ITSMConfigItemSearchFormDataGet {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Argument (qw(Key Item)) {
if ( !$Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!"
);
return;
}
}
# load backend
my $BackendObject = $Self->_ITSMLoadLayoutBackend(
Type => $Param{Item}->{Input}->{Type},
);
return [] if !$BackendObject;
# get form data
my $Values = $BackendObject->SearchFormDataGet(%Param);
return $Values;
}
=head2 ITSMConfigItemSearchInputCreate()
returns a search input field html string
my $String = $LayoutObject->ITSMConfigItemSearchInputCreate(
Key => 'Item::1::Node::3',
Item => $ItemRef,
);
=cut
sub ITSMConfigItemSearchInputCreate {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Argument (qw(Key Item)) {
if ( !$Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!"
);
return;
}
}
# load backend
my $BackendObject = $Self->_ITSMLoadLayoutBackend(
Type => $Param{Item}->{Input}->{Type},
);
return '' if !$BackendObject;
# lookup item value
my $String = $BackendObject->SearchInputCreate(%Param);
return $String;
}
=head2 _ITSMLoadLayoutBackend()
load a input type backend module
$BackendObject = $LayoutObject->_ITSMLoadLayoutBackend(
Type => 'GeneralCatalog',
);
=cut
sub _ITSMLoadLayoutBackend {
my ( $Self, %Param ) = @_;
# get log object
my $LogObject = $Kernel::OM->Get('Kernel::System::Log');
if ( !$Param{Type} ) {
$LogObject->Log(
Priority => 'error',
Message => 'Need Type!',
);
return;
}
my $GenericModule = "Kernel::Output::HTML::ITSMConfigItem::Layout$Param{Type}";
# load the backend module
if ( !$Kernel::OM->Get('Kernel::System::Main')->Require($GenericModule) ) {
$LogObject->Log(
Priority => 'error',
Message => "Can't load backend module $Param{Type}!"
);
return;
}
# create new instance
my $BackendObject = $GenericModule->new(
%{$Self},
%Param,
LayoutObject => $Self,
);
if ( !$BackendObject ) {
$LogObject->Log(
Priority => 'error',
Message => "Can't create a new instance of backend module $Param{Type}!",
);
return;
}
return $BackendObject;
}
=head2 ITSMConfigItemListShow()
Returns a list of configuration items with sort and pagination capabilities.
This function is similar to L<Kernel::Output::HTML::LayoutTicket::TicketListShow()>
in F<Kernel/Output/HTML/LayoutTicket.pm>.
my $Output = $LayoutObject->ITSMConfigItemListShow(
ConfigItemIDs => $ConfigItemIDsRef, # total list of config item ids, that can be listed
Total => scalar @{ $ConfigItemIDsRef }, # total number of list items, config items in this case
View => $Self->{View}, # optional, the default value is 'Small'
Filter => 'All',
Filters => \%NavBarFilter,
FilterLink => $LinkFilter,
TitleName => 'Overview: Config Item: Computer',
TitleValue => $Self->{Filter},
Env => $Self,
LinkPage => $LinkPage,
LinkSort => $LinkSort,
Frontend => 'Agent', # optional (Agent|Customer), default: Agent, indicates from which frontend this function was called
);
=cut
sub ITSMConfigItemListShow {
my ( $Self, %Param ) = @_;
# take object ref to local, remove it from %Param (prevent memory leak)
my $Env = delete $Param{Env};
# lookup latest used view mode
if ( !$Param{View} && $Self->{ 'UserITSMConfigItemOverview' . $Env->{Action} } ) {
$Param{View} = $Self->{ 'UserITSMConfigItemOverview' . $Env->{Action} };
}
# set frontend
my $Frontend = $Param{Frontend} || 'Agent';
# set defaut view mode to 'small'
my $View = $Param{View} || 'Small';
# store latest view mode
$Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID(
SessionID => $Self->{SessionID},
Key => 'UserITSMConfigItemOverview' . $Env->{Action},
Value => $View,
);
# get needed objects
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
# get backend from config
my $Backends = $ConfigObject->Get('ITSMConfigItem::Frontend::Overview');
if ( !$Backends ) {
return $LayoutObject->FatalError(
Message => 'Need config option ITSMConfigItem::Frontend::Overview',
);
}
# check for hash-ref
if ( ref $Backends ne 'HASH' ) {
return $LayoutObject->FatalError(
Message => 'Config option ITSMConfigItem::Frontend::Overview needs to be a HASH ref!',
);
}
# check for config key
if ( !$Backends->{$View} ) {
return $LayoutObject->FatalError(
Message => "No config option found for the view '$View'!",
);
}
# nav bar
my $StartHit = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam(
Param => 'StartHit',
) || 1;
# get personal page shown count
my $PageShownPreferencesKey = 'UserConfigItemOverview' . $View . 'PageShown';
my $PageShown = $Self->{$PageShownPreferencesKey} || 10;
my $Group = 'ConfigItemOverview' . $View . 'PageShown';
# check start option, if higher then elements available, set
# it to the last overview page (Thanks to Stefan Schmidt!)
if ( $StartHit > $Param{Total} ) {
my $Pages = int( ( $Param{Total} / $PageShown ) + 0.99999 );
$StartHit = ( ( $Pages - 1 ) * $PageShown ) + 1;
}
# get data selection
my %Data;
my $Config = $ConfigObject->Get('PreferencesGroups');
if ( $Config && $Config->{$Group} && $Config->{$Group}->{Data} ) {
%Data = %{ $Config->{$Group}->{Data} };
}
# set page limit and build page nav
my $Limit = $Param{Limit} || 20_000;
my %PageNav = $LayoutObject->PageNavBar(
Limit => $Limit,
StartHit => $StartHit,
PageShown => $PageShown,
AllHits => $Param{Total} || 0,
Action => 'Action=' . $Env->{Action},
Link => $Param{LinkPage},
);
# build shown ticket a page
$Param{RequestedURL} = $Param{RequestedURL} || "Action=$Self->{Action};$Param{LinkPage}";
$Param{Group} = $Group;
$Param{PreferencesKey} = $PageShownPreferencesKey;
$Param{PageShownString} = $Self->BuildSelection(
Name => $PageShownPreferencesKey,
SelectedID => $PageShown,
Data => \%Data,
Translation => 0,
Sort => 'NumericValue',
Class => 'Modernize',
);
# build navbar content
$LayoutObject->Block(
Name => 'OverviewNavBar',
Data => \%Param,
);
# back link
if ( $Param{LinkBack} ) {
$LayoutObject->Block(
Name => 'OverviewNavBarPageBack',
Data => \%Param,
);
$LayoutObject->AddJSData(
Key => 'ITSMConfigItemSearch',
Value => {
Profile => $Param{Profile},
ClassID => $Param{ClassID},
},
);
}
# get filters
if ( $Param{Filters} ) {
# get given filters
my @NavBarFilters;
for my $Prio ( sort keys %{ $Param{Filters} } ) {
push @NavBarFilters, $Param{Filters}->{$Prio};
}
# build filter content
$LayoutObject->Block(
Name => 'OverviewNavBarFilter',
Data => {
%Param,
},
);
# loop over filters
my $Count = 0;
for my $Filter (@NavBarFilters) {
# increment filter count and build filter item
$Count++;
$LayoutObject->Block(
Name => 'OverviewNavBarFilterItem',
Data => {
%Param,
%{$Filter},
},
);
# filter is selected
if ( $Filter->{Filter} eq $Param{Filter} ) {
$LayoutObject->Block(
Name => 'OverviewNavBarFilterItemSelected',
Data => {
%Param,
%{$Filter},
},
);
}
else {
$LayoutObject->Block(
Name => 'OverviewNavBarFilterItemSelectedNot',
Data => {
%Param,
%{$Filter},
},
);
}
}
}
# loop over configured backends
for my $Backend ( sort keys %{$Backends} ) {
# build navbar view mode
$LayoutObject->Block(
Name => 'OverviewNavBarViewMode',
Data => {
%Param,
%{ $Backends->{$Backend} },
Filter => $Param{Filter},
View => $Backend,
},
);
# current view is configured in backend
if ( $View eq $Backend ) {
$LayoutObject->Block(
Name => 'OverviewNavBarViewModeSelected',
Data => {
%Param,
%{ $Backends->{$Backend} },
Filter => $Param{Filter},
View => $Backend,
},
);
}
else {
$LayoutObject->Block(
Name => 'OverviewNavBarViewModeNotSelected',
Data => {
%Param,
%{ $Backends->{$Backend} },
Filter => $Param{Filter},
View => $Backend,
},
);
}
}
# check if page nav is available
if (%PageNav) {
$LayoutObject->Block(
Name => 'OverviewNavBarPageNavBar',
Data => \%PageNav,
);
# don't show context settings in AJAX case (e. g. in customer ticket history),
# because the submit with page reload will not work there
if ( !$Param{AJAX} ) {
$LayoutObject->Block(
Name => 'ContextSettings',
Data => {
%PageNav,
%Param,
},
);
}
}
# check if bulk feature is enabled
my $BulkFeature = 0;
if ( $ConfigObject->Get('ITSMConfigItem::Frontend::BulkFeature') ) {
my @Groups;
if ( $ConfigObject->Get('ITSMConfigItem::Frontend::BulkFeatureGroup') ) {
@Groups = @{ $ConfigObject->Get('ITSMConfigItem::Frontend::BulkFeatureGroup') };
}
if ( !@Groups ) {
$BulkFeature = 1;
}
else {
GROUP:
for my $Group (@Groups) {
next GROUP if !$Kernel::OM->Get('Kernel::System::Group')->PermissionCheck(
UserID => $Self->{UserID},
GroupName => $Group,
Type => 'rw',
);
$BulkFeature = 1;
last GROUP;
}
}
}
# show the bulk action button if feature is enabled
if ($BulkFeature) {
$LayoutObject->Block(
Name => 'BulkAction',
Data => {
%PageNav,
%Param,
},
);
}
# build html content
my $OutputNavBar = $LayoutObject->Output(
TemplateFile => 'AgentITSMConfigItemOverviewNavBar',
Data => {%Param},
);
# create output
my $OutputRaw = '';
if ( !$Param{Output} ) {
$LayoutObject->Print(
Output => \$OutputNavBar,
);
}
else {
$OutputRaw .= $OutputNavBar;
}
# load module
if ( !$Kernel::OM->Get('Kernel::System::Main')->Require( $Backends->{$View}->{Module} ) ) {
return $LayoutObject->FatalError();
}
# check for backend object
my $Object = $Backends->{$View}->{Module}->new( %{$Env} );
return if !$Object;
# run module
my $Output = $Object->Run(
%Param,
Limit => $Limit,
StartHit => $StartHit,
PageShown => $PageShown,
AllHits => $Param{Total} || 0,
Frontend => $Frontend,
);
# create output
if ( !$Param{Output} ) {
$LayoutObject->Print(
Output => \$Output,
);
}
else {
$OutputRaw .= $Output;
}
# create overview nav bar
$LayoutObject->Block(
Name => 'OverviewNavBar',
Data => {%Param},
);
# return content if available
return $OutputRaw;
}
=head2 XMLData2Hash()
returns a hash reference with the requested attributes data for a config item
Return
$Data = {
'HardDisk::2' => {
Value => 'HD2',
Name => 'Hard Disk',
},
'CPU::1' => {
Value => '',
Name => 'CPU',
},
'HardDisk::2::Capacity::1' => {
Value => '780 GB',
Name => 'Capacity',
},
};
my $Data = $LayoutObject->XMLData2Hash(
XMLDefinition => $Version->{XMLDefinition},
XMLData => $Version->{XMLData}->[1]->{Version}->[1],
Attributes => ['CPU::1', 'HardDrive::2::Capacity::1', ...],
Data => \%DataHashRef, # optional
Prefix => 'HardDisk::1', # optional
);
=cut
sub XMLData2Hash {
my ( $Self, %Param ) = @_;
# check needed stuff
return if !$Param{XMLData};
return if !$Param{XMLDefinition};
return if !$Param{Attributes};
return if ref $Param{XMLData} ne 'HASH';
return if ref $Param{XMLDefinition} ne 'ARRAY';
return if ref $Param{Attributes} ne 'ARRAY';
# to store the return data
my $Data = $Param{Data} || {};
# create a lookup structure
my %RelevantAttributes = map { $_ => 1 } @{ $Param{Attributes} };
ITEM:
for my $Item ( @{ $Param{XMLDefinition} } ) {
my $CountMax = $Item->{CountMax} || 1;
COUNTER:
for my $Counter ( 1 .. $CountMax ) {
# add prefix
my $Prefix = $Item->{Key} . '::' . $Counter;
if ( $Param{Prefix} ) {
$Prefix = $Param{Prefix} . '::' . $Prefix;
}
# skip not needed elements and sub elements
next COUNTER if !grep { $_ =~ m{\A$Prefix} } @{ $Param{Attributes} };
# lookup value
my $Value = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->XMLValueLookup(
Item => $Item,
Value => $Param{XMLData}->{ $Item->{Key} }->[$Counter]->{Content} // '',
);
# only if value is defined
if ( defined $Value ) {
# create output string
$Value = $Self->ITSMConfigItemOutputStringCreate(
Value => $Value,
Item => $Item,
Print => $Param{Print},
);
}
if ( $RelevantAttributes{$Prefix} ) {
# store the item in hash
$Data->{$Prefix} = {
Name => $Item->{Name},
Value => $Value // '',
};
}
# start recursion, if "Sub" was found
if ( $Item->{Sub} ) {
$Data = $Self->XMLData2Hash(
XMLDefinition => $Item->{Sub},
XMLData => $Param{XMLData}->{ $Item->{Key} }->[$Counter],
Prefix => $Prefix,
Data => $Data,
Attributes => $Param{Attributes},
);
}
}
}
return $Data;
}
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<https://otrs.org/>).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (GPL). If you
did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
=cut

View File

@@ -0,0 +1,285 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::Output::HTML::Layout::ITSMTemplate;
use strict;
use warnings;
use Kernel::Language qw(Translatable);
use POSIX qw(ceil);
our $ObjectManagerDisabled = 1;
=head2 ITSMTemplateListShow()
Returns a list of templates as C<sortable> list with pagination.
This function is similar to L<Kernel::Output::HTML::Layout::ITSMChange::ITMChangeListShow()>
in F<Kernel/Output/HTML/LayoutITSMChange.pm>.
my $Output = $LayoutObject->ITSMTemplateListShow(
TemplateIDs => $TemplateIDsRef, # total list of template ids, that can be listed
Total => scalar @{ $TemplateIDsRef }, # total number of list items, templates in this case
Filter => 'All',
Filters => \%NavBarFilter,
FilterLink => $LinkFilter,
TitleName => 'Overview: Template',
TitleValue => $Self->{Filter},
Env => $Self,
LinkPage => $LinkPage,
LinkSort => $LinkSort,
);
=cut
sub ITSMTemplateListShow {
my ( $Self, %Param ) = @_;
# take object ref to local, remove it from %Param (prevent memory leak)
my $Env = delete $Param{Env};
# for now there is only the 'Small' view
my $View = 'Small';
# get layout object
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
# get backend from config
my $Backends = $Kernel::OM->Get('Kernel::Config')->Get('ITSMChange::Frontend::TemplateOverview');
if ( !$Backends ) {
return $LayoutObject->FatalError(
Message => $LayoutObject->{LanguageObject}->Translate(
'Need config option %s!',
'ITSMChange::Frontend::TemplateOverview',
),
Comment => Translatable('Please contact the administrator.'),
);
}
# check for hash-ref
if ( ref $Backends ne 'HASH' ) {
return $LayoutObject->FatalError(
Message => $LayoutObject->{LanguageObject}->Translate(
'Config option %s needs to be a HASH ref!',
'ITSMChange::Frontend::TemplateOverview',
),
Comment => Translatable('Please contact the administrator.'),
);
}
# check for config key
if ( !$Backends->{$View} ) {
return $LayoutObject->FatalError(
Message => $LayoutObject->{LanguageObject}->Translate( 'No config option found for the view "%s"!', $View ),
Comment => Translatable('Please contact the administrator.'),
);
}
# nav bar
my $StartHit = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => 'StartHit' ) || 1;
# check start option, if higher then elements available, set
# it to the last overview page (Thanks to Stefan Schmidt!)
my $PageShown = $Backends->{$View}->{PageShown};
if ( $StartHit > $Param{Total} ) {
my $Pages = int( ( $Param{Total} / $PageShown ) + 0.99999 );
$StartHit = ( ( $Pages - 1 ) * $PageShown ) + 1;
}
# set page limit and build page nav
my $Limit = $Param{Limit} || 20_000;
my %PageNav = $LayoutObject->PageNavBar(
Limit => $Limit,
StartHit => $StartHit,
PageShown => $PageShown,
AllHits => $Param{Total} || 0,
Action => 'Action=' . $LayoutObject->{Action},
Link => $Param{LinkPage},
);
# build navbar content
$LayoutObject->Block(
Name => 'OverviewNavBar',
Data => \%Param,
);
# back link
if ( $Param{LinkBack} ) {
$LayoutObject->Block(
Name => 'OverviewNavBarPageBack',
Data => \%Param,
);
}
# get filters
if ( $Param{Filters} ) {
# get given filters
my @NavBarFilters;
for my $Prio ( sort keys %{ $Param{Filters} } ) {
push @NavBarFilters, $Param{Filters}->{$Prio};
}
# build filter content
$LayoutObject->Block(
Name => 'OverviewNavBarFilter',
Data => {
%Param,
},
);
# loop over filters
my $Count = 0;
for my $Filter (@NavBarFilters) {
# increment filter count and build filter item
$Count++;
$LayoutObject->Block(
Name => 'OverviewNavBarFilterItem',
Data => {
%Param,
%{$Filter},
},
);
# filter is selected
if ( $Filter->{Filter} eq $Param{Filter} ) {
$LayoutObject->Block(
Name => 'OverviewNavBarFilterItemSelected',
Data => {
%Param,
%{$Filter},
},
);
}
else {
$LayoutObject->Block(
Name => 'OverviewNavBarFilterItemSelectedNot',
Data => {
%Param,
%{$Filter},
},
);
}
}
}
# loop over configured backends, when there is more than a single backend
for my $Backend ( sort keys %{$Backends} ) {
# build navbar view mode
$LayoutObject->Block(
Name => 'OverviewNavBarViewMode',
Data => {
%Param,
%{ $Backends->{$Backend} },
Filter => $Param{Filter},
View => $Backend,
},
);
# current view is configured in backend
if ( $View eq $Backend ) {
$LayoutObject->Block(
Name => 'OverviewNavBarViewModeSelected',
Data => {
%Param,
%{ $Backends->{$Backend} },
Filter => $Param{Filter},
View => $Backend,
},
);
}
else {
$LayoutObject->Block(
Name => 'OverviewNavBarViewModeNotSelected',
Data => {
%Param,
%{ $Backends->{$Backend} },
Filter => $Param{Filter},
View => $Backend,
},
);
}
}
# check if page nav is available
if (%PageNav) {
$LayoutObject->Block(
Name => 'OverviewNavBarPageNavBar',
Data => \%PageNav,
);
}
# check if nav bar is available
if ( $Param{NavBar} ) {
if ( $Param{NavBar}->{MainName} ) {
$LayoutObject->Block(
Name => 'OverviewNavBarMain',
Data => $Param{NavBar},
);
}
}
# build html content
my $OutputNavBar = $LayoutObject->Output(
TemplateFile => 'AgentITSMTemplateOverviewNavBar',
Data => {%Param},
);
# create output
my $OutputRaw = '';
if ( !$Param{Output} ) {
$LayoutObject->Print(
Output => \$OutputNavBar,
);
}
else {
$OutputRaw .= $OutputNavBar;
}
# load module
if ( !$Kernel::OM->Get('Kernel::System::Main')->Require( $Backends->{$View}->{Module} ) ) {
return $LayoutObject->FatalError();
}
# check for backend object
my $Object = $Backends->{$View}->{Module}->new( %{$Env} );
return if !$Object;
# run module
my $Output = $Object->Run(
%Param,
Limit => $Limit,
StartHit => $StartHit,
PageShown => $PageShown,
AllHits => $Param{Total} || 0,
);
# create output
if ( !$Param{Output} ) {
$LayoutObject->Print(
Output => \$Output,
);
}
else {
$OutputRaw .= $Output;
}
# create overview nav bar
$LayoutObject->Block(
Name => 'OverviewNavBar',
Data => {%Param},
);
# return content if available
return $OutputRaw;
}
1;

View File

@@ -0,0 +1,158 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::Output::HTML::Layout::ImportExport;
use strict;
use warnings;
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::Output::HTML::Layout::ImportExport
=cut
=head2 ImportExportFormInputCreate()
Returns a input field html string
my $String = $LayoutObject->ImportExportFormInputCreate(
Item => $ItemRef,
Value => 'Value', # (optional)
);
=cut
sub ImportExportFormInputCreate {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{Item} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need Item!'
);
return;
}
# load backend
my $BackendObject = $Self->_ImportExportLoadLayoutBackend(
Type => $Param{Item}->{Input}->{Type},
);
return '' if !$BackendObject;
# lookup item value
my $String = $BackendObject->FormInputCreate(%Param);
return $String;
}
=head2 ImportExportFormDataGet()
Returns the values from the html form as hash reference
my $FormData = $LayoutObject->ImportExportFormDataGet(
Item => $ItemRef,
);
=cut
sub ImportExportFormDataGet {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{Item} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need Item!'
);
return;
}
# load backend
my $BackendObject = $Self->_ImportExportLoadLayoutBackend(
Type => $Param{Item}->{Input}->{Type},
);
return if !$BackendObject;
# get form data
my $FormData = $BackendObject->FormDataGet(%Param);
return $FormData;
}
=head2 _ImportExportLoadLayoutBackend()
To load a import/export layout backend module
my $Backend = $LayoutObject->_ImportExportLoadLayoutBackend(
Type => 'Selection',
);
An instance of the loaded backend module is returned.
=cut
sub _ImportExportLoadLayoutBackend {
my ( $Self, %Param ) = @_;
# get log object
my $LogObject = $Kernel::OM->Get('Kernel::System::Log');
if ( !$Param{Type} ) {
$LogObject->Log(
Priority => 'error',
Message => 'Need Type!',
);
return;
}
my $GenericModule = "Kernel::Output::HTML::ImportExport::Layout$Param{Type}";
# load the backend module
if ( !$Kernel::OM->Get('Kernel::System::Main')->Require($GenericModule) ) {
$LogObject->Log(
Priority => 'error',
Message => "Can't load backend module $Param{Type}!",
);
return;
}
# create new instance
my $BackendObject = $GenericModule->new(
%{$Self},
%Param,
LayoutObject => $Self,
);
if ( !$BackendObject ) {
$LogObject->Log(
Priority => 'error',
Message => "Can't create a new instance of backend module $Param{Type}!",
);
return;
}
return $BackendObject;
}
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<https://otrs.org/>).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (GPL). If you
did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
=cut

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,924 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::Output::HTML::Layout::Loader;
use strict;
use warnings;
use File::stat;
use Digest::MD5;
use Kernel::Language qw(Translatable);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::Output::HTML::Layout::Loader - CSS/JavaScript
=head1 DESCRIPTION
All valid functions.
=head1 PUBLIC INTERFACE
=head2 LoaderCreateAgentCSSCalls()
Generate the minified CSS files and the tags referencing them,
taking a list from the Loader::Agent::CommonCSS config item.
$LayoutObject->LoaderCreateAgentCSSCalls(
Skin => 'MySkin', # optional, if not provided skin is the configured by default
);
=cut
sub LoaderCreateAgentCSSCalls {
my ( $Self, %Param ) = @_;
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# get host based default skin configuration
my $SkinSelectedHostBased;
my $DefaultSkinHostBased = $ConfigObject->Get('Loader::Agent::DefaultSelectedSkin::HostBased');
if ( $DefaultSkinHostBased && $ENV{HTTP_HOST} ) {
REGEXP:
for my $RegExp ( sort keys %{$DefaultSkinHostBased} ) {
# do not use empty regexp or skin directories
next REGEXP if !$RegExp;
next REGEXP if !$DefaultSkinHostBased->{$RegExp};
# check if regexp is matching
if ( $ENV{HTTP_HOST} =~ /$RegExp/i ) {
$SkinSelectedHostBased = $DefaultSkinHostBased->{$RegExp};
}
}
}
# determine skin
# 1. use UserSkin setting from Agent preferences, if available
# 2. use HostBased skin setting, if available
# 3. use default skin from configuration
my $SkinSelected = $Self->{'UserSkin'};
# check if the skin is valid
my $SkinValid = 0;
if ($SkinSelected) {
$SkinValid = $Self->SkinValidate(
SkinType => 'Agent',
Skin => $SkinSelected,
);
}
if ( !$SkinValid ) {
$SkinSelected = $SkinSelectedHostBased
|| $ConfigObject->Get('Loader::Agent::DefaultSelectedSkin')
|| 'default';
}
# save selected skin
$Self->{SkinSelected} = $SkinSelected;
my $SkinHome = $ConfigObject->Get('Home') . '/var/httpd/htdocs/skins';
my $DoMinify = $ConfigObject->Get('Loader::Enabled::CSS');
my $ToolbarModuleSettings = $ConfigObject->Get('Frontend::ToolBarModule');
my $CustomerUserItemSettings = $ConfigObject->Get('Frontend::CustomerUser::Item');
{
my @FileList;
# get global css
my $CommonCSSList = $ConfigObject->Get('Loader::Agent::CommonCSS');
for my $Key ( sort keys %{$CommonCSSList} ) {
push @FileList, @{ $CommonCSSList->{$Key} };
}
# get toolbar module css
for my $Key ( sort keys %{$ToolbarModuleSettings} ) {
if ( $ToolbarModuleSettings->{$Key}->{CSS} ) {
push @FileList, $ToolbarModuleSettings->{$Key}->{CSS};
}
}
# get customer user item css
for my $Key ( sort keys %{$CustomerUserItemSettings} ) {
if ( $CustomerUserItemSettings->{$Key}->{CSS} ) {
push @FileList, $CustomerUserItemSettings->{$Key}->{CSS};
}
}
$Self->_HandleCSSList(
List => \@FileList,
DoMinify => $DoMinify,
BlockName => 'CommonCSS',
SkinHome => $SkinHome,
SkinType => 'Agent',
Skin => $SkinSelected,
);
}
# now handle module specific CSS
my $LoaderAction = $Self->{Action} || 'Login';
$LoaderAction = 'Login' if ( $LoaderAction eq 'Logout' );
{
my $Setting = $ConfigObject->Get("Loader::Module::$LoaderAction") || {};
my @FileList;
MODULE:
for my $Module ( sort keys %{$Setting} ) {
next MODULE if ref $Setting->{$Module}->{CSS} ne 'ARRAY';
@FileList = ( @FileList, @{ $Setting->{$Module}->{CSS} || [] } );
}
$Self->_HandleCSSList(
List => \@FileList,
DoMinify => $DoMinify,
BlockName => 'ModuleCSS',
SkinHome => $SkinHome,
SkinType => 'Agent',
Skin => $SkinSelected,
);
}
# handle the responsive CSS
{
my @FileList;
my $ResponsiveCSSList = $ConfigObject->Get('Loader::Agent::ResponsiveCSS');
for my $Key ( sort keys %{$ResponsiveCSSList} ) {
push @FileList, @{ $ResponsiveCSSList->{$Key} };
}
$Self->_HandleCSSList(
List => \@FileList,
DoMinify => $DoMinify,
BlockName => 'ResponsiveCSS',
SkinHome => $SkinHome,
SkinType => 'Agent',
Skin => $SkinSelected,
);
}
#print STDERR "Time: " . Time::HiRes::tv_interval([$t0]);
return 1;
}
=head2 LoaderCreateAgentJSCalls()
Generate the minified JavaScript files and the tags referencing them,
taking a list from the Loader::Agent::CommonJS config item.
$LayoutObject->LoaderCreateAgentJSCalls();
=cut
sub LoaderCreateAgentJSCalls {
my ( $Self, %Param ) = @_;
#use Time::HiRes;
#my $t0 = Time::HiRes::gettimeofday();
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $JSHome = $ConfigObject->Get('Home') . '/var/httpd/htdocs/js';
my $DoMinify = $ConfigObject->Get('Loader::Enabled::JS');
{
my @FileList;
# get global js
my $CommonJSList = $ConfigObject->Get('Loader::Agent::CommonJS');
KEY:
for my $Key ( sort keys %{$CommonJSList} ) {
next KEY if $Key eq '100-CKEditor' && !$ConfigObject->Get('Frontend::RichText');
push @FileList, @{ $CommonJSList->{$Key} };
}
# get toolbar module js
my $ToolbarModuleSettings = $ConfigObject->Get('Frontend::ToolBarModule');
for my $Key ( sort keys %{$ToolbarModuleSettings} ) {
if ( $ToolbarModuleSettings->{$Key}->{JavaScript} ) {
push @FileList, $ToolbarModuleSettings->{$Key}->{JavaScript};
}
}
$Self->_HandleJSList(
List => \@FileList,
DoMinify => $DoMinify,
BlockName => 'CommonJS',
JSHome => $JSHome,
);
}
# now handle module specific JavaScript
{
my $LoaderAction = $Self->{Action} || 'Login';
$LoaderAction = 'Login' if ( $LoaderAction eq 'Logout' );
my $Setting = $ConfigObject->Get("Loader::Module::$LoaderAction") || {};
my @FileList;
MODULE:
for my $Module ( sort keys %{$Setting} ) {
next MODULE if ref $Setting->{$Module}->{JavaScript} ne 'ARRAY';
@FileList = ( @FileList, @{ $Setting->{$Module}->{JavaScript} || [] } );
}
$Self->_HandleJSList(
List => \@FileList,
DoMinify => $DoMinify,
BlockName => 'ModuleJS',
JSHome => $JSHome,
);
}
return 1;
}
=head2 LoaderCreateJavaScriptTemplateData()
Generate a minified file for the template data that
needs to be present on the client side for JavaScript based templates.
$LayoutObject->LoaderCreateJavaScriptTemplateData();
=cut
sub LoaderCreateJavaScriptTemplateData {
my ( $Self, %Param ) = @_;
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# load theme
my $Theme = $Self->{UserTheme} || $ConfigObject->Get('DefaultTheme') || Translatable('Standard');
# force a theme based on host name
my $DefaultThemeHostBased = $ConfigObject->Get('DefaultTheme::HostBased');
if ( $DefaultThemeHostBased && $ENV{HTTP_HOST} ) {
THEME:
for my $RegExp ( sort keys %{$DefaultThemeHostBased} ) {
# do not use empty regexp or theme directories
next THEME if !$RegExp;
next THEME if $RegExp eq '';
next THEME if !$DefaultThemeHostBased->{$RegExp};
# check if regexp is matching
if ( $ENV{HTTP_HOST} =~ /$RegExp/i ) {
$Theme = $DefaultThemeHostBased->{$RegExp};
}
}
}
# locate template files
my $JSStandardTemplateDir = $ConfigObject->Get('TemplateDir') . '/JavaScript/Templates/' . 'Standard';
my $JSTemplateDir = $ConfigObject->Get('TemplateDir') . '/JavaScript/Templates/' . $Theme;
# Check if 'Standard' fallback exists
if ( !-e $JSStandardTemplateDir ) {
$Self->FatalDie(
Message =>
"No existing template directory found ('$JSTemplateDir')! Check your Home in Kernel/Config.pm."
);
}
if ( !-e $JSTemplateDir ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message =>
"No existing template directory found ('$JSTemplateDir')!.
Default theme used instead.",
);
# Set TemplateDir to 'Standard' as a fallback.
$Theme = 'Standard';
$JSTemplateDir = $JSStandardTemplateDir;
}
my $JSCustomStandardTemplateDir = $ConfigObject->Get('CustomTemplateDir') . '/JavaScript/Templates/' . 'Standard';
my $JSCustomTemplateDir = $ConfigObject->Get('CustomTemplateDir') . '/JavaScript/Templates/' . $Theme;
my @TemplateFolders = (
"$JSCustomTemplateDir",
"$JSCustomStandardTemplateDir",
"$JSTemplateDir",
"$JSStandardTemplateDir",
);
my $JSHome = $ConfigObject->Get('Home') . '/var/httpd/htdocs/js';
my $TargetFilenamePrefix = "TemplateJS";
my $TemplateChecksum;
my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
my $CacheType = 'Loader';
my $CacheKey = "LoaderCreateJavaScriptTemplateData:${Theme}:" . $ConfigObject->ConfigChecksum();
# Even getting the list of files recursively from the directories is expensive,
# so cache the checksum to avoid that.
$TemplateChecksum = $CacheObject->Get(
Type => $CacheType,
Key => $CacheKey,
);
if ( !$TemplateChecksum ) {
my %ChecksumData;
TEMPLATEFOLDER:
for my $TemplateFolder (@TemplateFolders) {
next TEMPLATEFOLDER if !-e $TemplateFolder;
# get main object
my @Templates = $MainObject->DirectoryRead(
Directory => $TemplateFolder,
Filter => '*.tmpl',
Recursive => 1,
);
TEMPLATE:
for my $Template ( sort @Templates ) {
next TEMPLATE if !-e $Template;
my $Key = $Template;
$Key =~ s/^$TemplateFolder\///xmsg;
$Key =~ s/\.(\w+)\.tmpl$//xmsg;
# check if a template with this name does already exist
next TEMPLATE if $ChecksumData{$Key};
# get file metadata
my $Stat = stat($Template);
if ( !$Stat ) {
print STDERR "Error: cannot stat file '$Template': $!";
next TEMPLATE;
}
$ChecksumData{$Key} = $Template . $Stat->mtime();
}
}
# generate a checksum only of the actual used files
for my $Checksum ( sort keys %ChecksumData ) {
$TemplateChecksum .= $ChecksumData{$Checksum};
}
$TemplateChecksum = Digest::MD5::md5_hex($TemplateChecksum);
$CacheObject->Set(
Type => $CacheType,
Key => $CacheKey,
TTL => 60 * 60 * 24,
Value => $TemplateChecksum,
);
}
# Check if cache already exists.
if ( -e "$JSHome/js-cache/${TargetFilenamePrefix}_$TemplateChecksum.js" ) {
$Self->Block(
Name => 'CommonJS',
Data => {
JSDirectory => 'js-cache/',
Filename => "${TargetFilenamePrefix}_$TemplateChecksum.js",
},
);
return 1;
}
# if it doesnt exist, go through the folders and get the template content
my %TemplateData;
TEMPLATEFOLDER:
for my $TemplateFolder (@TemplateFolders) {
next TEMPLATEFOLDER if !-e $TemplateFolder;
# get main object
my @Templates = $MainObject->DirectoryRead(
Directory => $TemplateFolder,
Filter => '*.tmpl',
Recursive => 1,
);
TEMPLATE:
for my $Template ( sort @Templates ) {
next TEMPLATE if !-e $Template;
my $Key = $Template;
$Key =~ s/^$TemplateFolder\///xmsg;
$Key =~ s/\.(\w+)\.tmpl$//xmsg;
# check if a template with this name does already exist
next TEMPLATE if $TemplateData{$Key};
my $TemplateContent = ${
$MainObject->FileRead(
Location => $Template,
Result => 'SCALAR',
)
};
# Remove DTL-style comments (lines starting with #)
$TemplateContent =~ s/^#.*\n//gm;
$TemplateData{$Key} = $TemplateContent;
}
}
my $TemplateDataJSON = $Kernel::OM->Get('Kernel::System::JSON')->Encode(
Data => \%TemplateData,
Pretty => 0,
);
my $Content = <<"EOF";
// The content of this file is automatically generated, do not edit.
Core.Template.Load($TemplateDataJSON);
EOF
my $MinifiedFile = $Kernel::OM->Get('Kernel::System::Loader')->MinifyFiles(
Checksum => $TemplateChecksum,
Content => $Content,
Type => 'JavaScript',
TargetDirectory => "$JSHome/js-cache/",
TargetFilenamePrefix => $TargetFilenamePrefix,
);
$Self->Block(
Name => 'CommonJS',
Data => {
JSDirectory => 'js-cache/',
Filename => $MinifiedFile,
},
);
return 1;
}
=head2 LoaderCreateJavaScriptTranslationData()
Generate a minified file for the translation data that
needs to be present on the client side for JavaScript based translations.
$LayoutObject->LoaderCreateJavaScriptTranslationData();
=cut
sub LoaderCreateJavaScriptTranslationData {
my ( $Self, %Param ) = @_;
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $JSHome = $ConfigObject->Get('Home') . '/var/httpd/htdocs/js';
my $UserLanguage = $Self->{UserLanguage};
my $LanguageObject = $Self->{LanguageObject};
my $LanguageChecksum = $LanguageObject->LanguageChecksum();
my $TargetFilenamePrefix = "TranslationJS_$UserLanguage";
# Check if cache already exists.
if ( -e "$JSHome/js-cache/${TargetFilenamePrefix}_$LanguageChecksum.js" ) {
$Self->Block(
Name => 'CommonJS',
Data => {
JSDirectory => 'js-cache/',
Filename => "${TargetFilenamePrefix}_$LanguageChecksum.js",
},
);
return 1;
}
# Now create translation data for JavaScript.
my %TranslationData;
STRING:
for my $String ( @{ $LanguageObject->{JavaScriptStrings} // [] } ) {
next STRING if $TranslationData{$String};
$TranslationData{$String} = $LanguageObject->{Translation}->{$String};
}
my %LanguageMetaData = (
LanguageCode => $UserLanguage,
DateFormat => $LanguageObject->{DateFormat},
DateFormatLong => $LanguageObject->{DateFormatLong},
DateFormatShort => $LanguageObject->{DateFormatShort},
DateInputFormat => $LanguageObject->{DateInputFormat},
DateInputFormatLong => $LanguageObject->{DateInputFormatLong},
Completeness => $LanguageObject->{Completeness},
Separator => $LanguageObject->{Separator},
DecimalSeparator => $LanguageObject->{DecimalSeparator},
);
my $LanguageMetaDataJSON = $Kernel::OM->Get('Kernel::System::JSON')->Encode(
Data => \%LanguageMetaData,
SortKeys => 1,
Pretty => 0,
);
my $TranslationDataJSON = $Kernel::OM->Get('Kernel::System::JSON')->Encode(
Data => \%TranslationData,
SortKeys => 1,
Pretty => 0,
);
my $Content = <<"EOF";
// The content of this file is automatically generated, do not edit.
Core.Language.Load($LanguageMetaDataJSON, $TranslationDataJSON);
EOF
my $MinifiedFile = $Kernel::OM->Get('Kernel::System::Loader')->MinifyFiles(
Checksum => $LanguageChecksum,
Content => $Content,
Type => 'JavaScript',
TargetDirectory => "$JSHome/js-cache/",
TargetFilenamePrefix => $TargetFilenamePrefix,
);
$Self->Block(
Name => 'CommonJS',
Data => {
JSDirectory => 'js-cache/',
Filename => $MinifiedFile,
},
);
return 1;
}
=head2 LoaderCreateCustomerCSSCalls()
Generate the minified CSS files and the tags referencing them,
taking a list from the Loader::Customer::CommonCSS config item.
$LayoutObject->LoaderCreateCustomerCSSCalls();
=cut
sub LoaderCreateCustomerCSSCalls {
my ( $Self, %Param ) = @_;
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $SkinSelected = $ConfigObject->Get('Loader::Customer::SelectedSkin')
|| 'default';
# force a skin based on host name
my $DefaultSkinHostBased = $ConfigObject->Get('Loader::Customer::SelectedSkin::HostBased');
if ( $DefaultSkinHostBased && $ENV{HTTP_HOST} ) {
REGEXP:
for my $RegExp ( sort keys %{$DefaultSkinHostBased} ) {
# do not use empty regexp or skin directories
next REGEXP if !$RegExp;
next REGEXP if !$DefaultSkinHostBased->{$RegExp};
# check if regexp is matching
if ( $ENV{HTTP_HOST} =~ /$RegExp/i ) {
$SkinSelected = $DefaultSkinHostBased->{$RegExp};
}
}
}
my $SkinHome = $ConfigObject->Get('Home') . '/var/httpd/htdocs/skins';
my $DoMinify = $ConfigObject->Get('Loader::Enabled::CSS');
{
my $CommonCSSList = $ConfigObject->Get('Loader::Customer::CommonCSS');
my @FileList;
for my $Key ( sort keys %{$CommonCSSList} ) {
push @FileList, @{ $CommonCSSList->{$Key} };
}
$Self->_HandleCSSList(
List => \@FileList,
DoMinify => $DoMinify,
BlockName => 'CommonCSS',
SkinHome => $SkinHome,
SkinType => 'Customer',
Skin => $SkinSelected,
);
}
# now handle module specific CSS
my $LoaderAction = $Self->{Action} || 'Login';
$LoaderAction = 'Login' if ( $LoaderAction eq 'Logout' );
{
my $Setting = $ConfigObject->Get("Loader::Module::$LoaderAction") || {};
my @FileList;
MODULE:
for my $Module ( sort keys %{$Setting} ) {
next MODULE if ref $Setting->{$Module}->{CSS} ne 'ARRAY';
@FileList = ( @FileList, @{ $Setting->{$Module}->{CSS} || [] } );
}
$Self->_HandleCSSList(
List => \@FileList,
DoMinify => $DoMinify,
BlockName => 'ModuleCSS',
SkinHome => $SkinHome,
SkinType => 'Customer',
Skin => $SkinSelected,
);
}
# handle the responsive CSS
{
my @FileList;
my $ResponsiveCSSList = $ConfigObject->Get('Loader::Customer::ResponsiveCSS');
for my $Key ( sort keys %{$ResponsiveCSSList} ) {
push @FileList, @{ $ResponsiveCSSList->{$Key} };
}
$Self->_HandleCSSList(
List => \@FileList,
DoMinify => $DoMinify,
BlockName => 'ResponsiveCSS',
SkinHome => $SkinHome,
SkinType => 'Customer',
Skin => $SkinSelected,
);
}
#print STDERR "Time: " . Time::HiRes::tv_interval([$t0]);
return 1;
}
=head2 LoaderCreateCustomerJSCalls()
Generate the minified JavaScript files and the tags referencing them,
taking a list from the Loader::Customer::CommonJS config item.
$LayoutObject->LoaderCreateCustomerJSCalls();
=cut
sub LoaderCreateCustomerJSCalls {
my ( $Self, %Param ) = @_;
#use Time::HiRes;
#my $t0 = Time::HiRes::gettimeofday();
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $JSHome = $ConfigObject->Get('Home') . '/var/httpd/htdocs/js';
my $DoMinify = $ConfigObject->Get('Loader::Enabled::JS');
{
my $CommonJSList = $ConfigObject->Get('Loader::Customer::CommonJS');
my @FileList;
KEY:
for my $Key ( sort keys %{$CommonJSList} ) {
next KEY if $Key eq '100-CKEditor' && !$ConfigObject->Get('Frontend::RichText');
push @FileList, @{ $CommonJSList->{$Key} };
}
$Self->_HandleJSList(
List => \@FileList,
DoMinify => $DoMinify,
BlockName => 'CommonJS',
JSHome => $JSHome,
);
}
# now handle module specific JS
{
my $LoaderAction = $Self->{Action} || 'CustomerLogin';
$LoaderAction = 'CustomerLogin' if ( $LoaderAction eq 'Logout' );
my $Setting = $ConfigObject->Get("Loader::Module::$LoaderAction") || {};
my @FileList;
MODULE:
for my $Module ( sort keys %{$Setting} ) {
next MODULE if ref $Setting->{$Module}->{JavaScript} ne 'ARRAY';
@FileList = ( @FileList, @{ $Setting->{$Module}->{JavaScript} || [] } );
}
$Self->_HandleJSList(
List => \@FileList,
DoMinify => $DoMinify,
BlockName => 'ModuleJS',
JSHome => $JSHome,
);
}
#print STDERR "Time: " . Time::HiRes::tv_interval([$t0]);
return;
}
sub _HandleCSSList {
my ( $Self, %Param ) = @_;
my @Skins = ('default');
# validating selected custom skin, if any
if ( $Param{Skin} && $Param{Skin} ne 'default' && $Self->SkinValidate(%Param) ) {
push @Skins, $Param{Skin};
}
#load default css files
for my $Skin (@Skins) {
my @FileList;
CSSFILE:
for my $CSSFile ( @{ $Param{List} } ) {
my $SkinFile = "$Param{SkinHome}/$Param{SkinType}/$Skin/css/$CSSFile";
next CSSFILE if ( !-e $SkinFile );
if ( $Param{DoMinify} ) {
push @FileList, $SkinFile;
}
else {
$Self->Block(
Name => $Param{BlockName},
Data => {
Skin => $Skin,
CSSDirectory => 'css',
Filename => $CSSFile,
},
);
}
}
if ( $Param{DoMinify} && @FileList ) {
my $MinifiedFile = $Kernel::OM->Get('Kernel::System::Loader')->MinifyFiles(
List => \@FileList,
Type => 'CSS',
TargetDirectory => "$Param{SkinHome}/$Param{SkinType}/$Skin/css-cache/",
TargetFilenamePrefix => $Param{BlockName},
);
$Self->Block(
Name => $Param{BlockName},
Data => {
Skin => $Skin,
CSSDirectory => 'css-cache',
Filename => $MinifiedFile,
},
);
}
}
return 1;
}
sub _HandleJSList {
my ( $Self, %Param ) = @_;
my $Content = $Param{Content};
return if !$Param{List} && !$Content;
my %UsedFiles;
my @FileList;
JSFILE:
for my $JSFile ( @{ $Param{List} // [] } ) {
next JSFILE if $UsedFiles{$JSFile};
if ( $Param{DoMinify} ) {
push @FileList, "$Param{JSHome}/$JSFile";
}
else {
$Self->Block(
Name => $Param{BlockName},
Data => {
JSDirectory => '',
Filename => $JSFile,
},
);
}
# Save it for checking duplicates.
$UsedFiles{$JSFile} = 1;
}
return 1 if $Param{List} && !@FileList;
if ( $Param{DoMinify} ) {
my $MinifiedFile;
if (@FileList) {
$MinifiedFile = $Kernel::OM->Get('Kernel::System::Loader')->MinifyFiles(
List => \@FileList,
Type => 'JavaScript',
TargetDirectory => "$Param{JSHome}/js-cache/",
TargetFilenamePrefix => $Param{FilenamePrefix} // $Param{BlockName},
);
}
else {
$MinifiedFile = $Kernel::OM->Get('Kernel::System::Loader')->MinifyFiles(
Content => $Content,
Type => 'JavaScript',
TargetDirectory => "$Param{JSHome}/js-cache/",
TargetFilenamePrefix => $Param{FilenamePrefix} // $Param{BlockName},
);
}
$Self->Block(
Name => $Param{BlockName},
Data => {
JSDirectory => 'js-cache/',
Filename => $MinifiedFile,
},
);
}
return 1;
}
=head2 SkinValidate()
Returns 1 if skin is available for Agent or Customer frontends and 0 if not.
my $SkinIsValid = $LayoutObject->SkinValidate(
UserType => 'Agent' # Agent or Customer,
Skin => 'ExampleSkin',
);
=cut
sub SkinValidate {
my ( $Self, %Param ) = @_;
for my $Needed ( 'SkinType', 'Skin' ) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Message => "Needed param: $Needed!",
Priority => 'error',
);
return;
}
}
if ( exists $Self->{SkinValidateCache}->{ $Param{SkinType} . '::' . $Param{Skin} } ) {
return $Self->{SkinValidateCache}->{ $Param{SkinType} . '::' . $Param{Skin} };
}
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $SkinType = $Param{SkinType};
my $PossibleSkins = $ConfigObject->Get("Loader::${SkinType}::Skin") || {};
my $Home = $ConfigObject->Get('Home');
my %ActiveSkins;
# prepare the list of active skins
for my $PossibleSkin ( values %{$PossibleSkins} ) {
if ( $PossibleSkin->{InternalName} eq $Param{Skin} ) {
my $SkinDir = $Home . "/var/httpd/htdocs/skins/$SkinType/" . $PossibleSkin->{InternalName};
if ( -d $SkinDir ) {
$Self->{SkinValidateCache}->{ $Param{SkinType} . '::' . $Param{Skin} } = 1;
return 1;
}
}
}
$Self->{SkinValidateCache}->{ $Param{SkinType} . '::' . $Param{Skin} } = undef;
return;
}
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<https://otrs.org/>).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (GPL). If you
did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
=cut

View File

@@ -0,0 +1,99 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::Output::HTML::Layout::Popup;
use strict;
use warnings;
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::Output::HTML::Layout::Popup - CSS/JavaScript
=head1 DESCRIPTION
All valid functions.
=head1 PUBLIC INTERFACE
=head2 PopupClose()
Generate a small HTML page which closes the pop-up window and
executes an action in the main window.
# load specific URL in main window
$LayoutObject->PopupClose(
URL => "Action=AgentTicketZoom;TicketID=$TicketID"
);
or
# reload main window
$Self->{LayoutObject}->PopupClose(
Reload => 1,
);
=cut
sub PopupClose {
my ( $Self, %Param ) = @_;
if ( !$Param{URL} && !$Param{Reload} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need URL or Reload!'
);
return;
}
# Generate the call Header() and Footer(
my $Output = $Self->Header( Type => 'Small' );
if ( $Param{URL} ) {
# add session if no cookies are enabled
if ( $Self->{SessionID} && !$Self->{SessionIDCookie} ) {
$Param{URL} .= ';' . $Self->{SessionName} . '=' . $Self->{SessionID};
}
# send data to JS
$Self->AddJSData(
Key => 'PopupClose',
Value => 'LoadParentURLAndClose',
);
$Self->AddJSData(
Key => 'PopupURL',
Value => $Param{URL},
);
}
else {
# send data to JS
$Self->AddJSData(
Key => 'PopupClose',
Value => 'ReloadParentAndClose',
);
}
$Output .= $Self->Footer( Type => 'Small' );
return $Output;
}
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<https://otrs.org/>).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (GPL). If you
did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
=cut

View File

@@ -0,0 +1,365 @@
# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::Output::HTML::Layout::Template;
use strict;
use warnings;
use Scalar::Util qw();
use Template;
use Template::Stash::XS;
use Template::Context;
use Template::Plugins;
use Kernel::Output::Template::Provider;
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::Output::HTML::Layout::Template - template rendering engine based on Template::Toolkit
=head1 PUBLIC INTERFACE
=head2 Output()
generates HTML output based on a template file.
Using a template file:
my $HTML = $LayoutObject->Output(
TemplateFile => 'AdminLog.tt',
Data => \%Param,
);
Using a template string:
my $HTML = $LayoutObject->Output(
Template => '<b>[% Data.SomeKey | html %]</b>',
Data => \%Param,
);
Additional parameters:
AJAX - AJAX-specific adjustements: this causes [% WRAPPER JSOnDocumentComplete %] blocks NOT
to be replaced. This is important to be able to generate snippets which can be cached.
Also, JS data added with AddJSData() calls is appended to the output here.
my $HTML = $LayoutObject->Output(
TemplateFile => 'AdminLog.tt',
Data => \%Param,
AJAX => 1,
);
KeepScriptTags - DEPRECATED, please use the parameter "AJAX" instead
=cut
sub Output {
my ( $Self, %Param ) = @_;
$Param{Data} ||= {};
# get and check param Data
if ( ref $Param{Data} ne 'HASH' ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need HashRef in Param Data! Got: '" . ref( $Param{Data} ) . "'!",
);
$Self->FatalError();
}
# asure compatibility with old KeepScriptTags parameter
if ( $Param{KeepScriptTags} && !$Param{AJAX} ) {
$Param{AJAX} = $Param{KeepScriptTags};
}
# fill init Env
if ( !$Self->{EnvRef} ) {
%{ $Self->{EnvRef} } = %ENV;
# all $Self->{*}
for ( sort keys %{$Self} ) {
if ( defined $Self->{$_} && !ref $Self->{$_} ) {
$Self->{EnvRef}->{$_} = $Self->{$_};
}
}
}
# add new env
if ( $Self->{EnvNewRef} ) {
for my $Key ( sort keys %{ $Self->{EnvNewRef} } ) {
$Self->{EnvRef}->{$Key} = $Self->{EnvNewRef}->{$Key};
}
undef $Self->{EnvNewRef};
}
# if we use the HTML5 input type 'email' jQuery Validate will always validate
# we do not want that if CheckEmailAddresses is set to 'no' in SysConfig
$Self->{EnvRef}->{EmailFieldType}
= $Kernel::OM->Get('Kernel::Config')->Get('CheckEmailAddresses') ? 'email' : 'text';
my @TemplateFolders = (
"$Self->{CustomTemplateDir}",
"$Self->{CustomStandardTemplateDir}",
"$Self->{TemplateDir}",
"$Self->{StandardTemplateDir}",
);
my $TemplateString;
if ( $Param{TemplateFile} ) {
$Param{TemplateFileTT} .= "$Param{TemplateFile}.tt";
}
# take templates from string/array
elsif ( defined $Param{Template} && ref $Param{Template} eq 'ARRAY' ) {
for ( @{ $Param{Template} } ) {
$TemplateString .= $_;
}
}
elsif ( defined $Param{Template} ) {
$TemplateString = $Param{Template};
}
else {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need Template or TemplateFile Param!',
);
$Self->FatalError();
}
if ( !$Self->{TemplateObject} ) {
$Self->{TemplateProviderObject} = Kernel::Output::Template::Provider->new(
{
INCLUDE_PATH => \@TemplateFolders,
EVAL_PERL => 1,
COMPILE_EXT => '.ttc',
}
);
$Self->{TemplateProviderObject}->OTRSInit(
LayoutObject => $Self,
);
my $Plugins = Template::Plugins->new(
{
PLUGIN_BASE => 'Kernel::Output::Template::Plugin',
}
);
my $Context = Template::Context->new(
{
EVAL_PERL => 1,
STASH => Template::Stash::XS->new(),
LOAD_TEMPLATES => [ $Self->{TemplateProviderObject} ],
LOAD_PLUGINS => [$Plugins],
}
);
# Store a weak reference to the LayoutObject in the context
# to avoid ring references. We need it for the plugins.
$Context->{LayoutObject} = $Self;
Scalar::Util::weaken( $Context->{LayoutObject} );
my $Success = $Self->{TemplateObject} = Template->new(
{
CONTEXT => $Context,
#DEBUG => Template::Constants::DEBUG_ALL,
}
);
if ( !$Success ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "$Template::ERROR;",
);
# $Self->FatalError(); # Don't use FatalError here, might cause infinite recursion
die "$Template::ERROR\n";
}
}
my $Output;
my $Success = $Self->{TemplateObject}->process(
$Param{TemplateFileTT} // \$TemplateString,
{
Data => $Param{Data} // {},
global => {
BlockData => $Self->{BlockData} // [],
KeepScriptTags => $Param{AJAX} // 0,
},
},
\$Output,
);
if ( !$Success ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => $Self->{TemplateObject}->error(),
);
$Self->FatalError();
}
# If the browser does not send the session cookie, we need to append it to all links and image urls.
# We cannot do this in the template preprocessor because links are often dynamically generated.
if ( $Self->{SessionID} && !$Self->{SessionIDCookie} ) {
# rewrite a hrefs
$Output =~ s{
(<a.+?href=")(.+?)(\#.+?|)(".+?>)
}
{
my $AHref = $1;
my $Target = $2;
my $End = $3;
my $RealEnd = $4;
if ( lc($Target) =~ /^(http:|https:|#|ftp:)/ ||
$Target !~ /\.(pl|php|cgi|fcg|fcgi|fpl)(\?|$)/ ||
$Target =~ /(\?|&|;)\Q$Self->{SessionName}\E=/) {
$AHref.$Target.$End.$RealEnd;
}
else {
$AHref.$Target.';'.$Self->{SessionName}.'='.$Self->{SessionID}.$End.$RealEnd;
}
}iegxs;
# rewrite img and iframe src
$Output =~ s{
(<(?:img|iframe).+?src=")(.+?)(".+?>)
}
{
my $AHref = $1;
my $Target = $2;
my $End = $3;
if (lc($Target) =~ m{^http s? :}smx || !$Self->{SessionID} ||
$Target !~ /\.(pl|php|cgi|fcg|fcgi|fpl)(\?|$)/ ||
$Target =~ /\Q$Self->{SessionName}\E=/) {
$AHref.$Target.$End;
}
else {
$AHref.$Target.'&'.$Self->{SessionName}.'='.$Self->{SessionID}.$End;
}
}iegxs;
}
#
# "Post" Output filter handling
#
if ( $Self->{FilterElementPost} && ref $Self->{FilterElementPost} eq 'HASH' ) {
# extract filter list
my %FilterList = %{ $Self->{FilterElementPost} };
my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
FILTER:
for my $Filter ( sort keys %FilterList ) {
# extract filter config
my $FilterConfig = $FilterList{$Filter};
# extract template list
my %TemplateList = %{ $FilterConfig->{Templates} || {} };
next FILTER if !$Param{TemplateFile};
next FILTER if !$TemplateList{ $Param{TemplateFile} };
next FILTER if !$Kernel::OM->Get('Kernel::System::Main')->Require( $FilterConfig->{Module} );
# create new instance
my $Object = $FilterConfig->{Module}->new(
%{$Self},
LayoutObject => $Self,
);
next FILTER if !$Object;
# run output filter
$Object->Run(
%{$FilterConfig},
Data => \$Output,
TemplateFile => $Param{TemplateFile} || '',
);
}
}
#
# AddJSData() handling
#
if ( $Param{AJAX} ) {
my %Data = %{ $Self->{_JSData} // {} };
if (%Data) {
my $JSONString = $Kernel::OM->Get('Kernel::System::JSON')->Encode(
Data => \%Data,
SortKeys => 1,
);
$Output
.= "\n<script type=\"text/javascript\">//<![CDATA[\n\"use strict\";\nCore.Config.AddConfig($JSONString);\n//]]></script>";
}
delete $Self->{_JSData};
}
return $Output;
}
=head2 AddJSOnDocumentComplete()
dynamically add JavaScript code that should be executed in Core.App.Ready().
Call this for any dynamically generated code that is not in a template.
$LayoutObject->AddJSOnDocumentComplete(
Code => $MyCode,
);
=cut
sub AddJSOnDocumentComplete {
my ( $Self, %Param ) = @_;
$Self->{_JSOnDocumentComplete} //= [];
push @{ $Self->{_JSOnDocumentComplete} }, $Param{Code};
return;
}
=head2 AddJSData()
dynamically add JavaScript data that should be handed over to
JavaScript via Core.Config.
$LayoutObject->AddJSData(
Key => 'Key1', # the key to store this data
Value => { ... } # simple or complex data
);
=cut
sub AddJSData {
my ( $Self, %Param ) = @_;
return if !$Param{Key};
$Self->{_JSData} //= {};
$Self->{_JSData}->{ $Param{Key} } = $Param{Value};
return;
}
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<https://otrs.org/>).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (GPL). If you
did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
=cut

File diff suppressed because it is too large Load Diff