1152 lines
33 KiB
Perl
1152 lines
33 KiB
Perl
# --
|
|
# 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::FAQ;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use Kernel::Language qw(Translatable);
|
|
|
|
our @ObjectDependencies = (
|
|
'Kernel::Config',
|
|
'Kernel::System::AuthSession',
|
|
'Kernel::System::HTMLUtils',
|
|
'Kernel::System::Log',
|
|
'Kernel::System::Main',
|
|
'Kernel::System::Web::Request',
|
|
'Kernel::Output::HTML::Layout',
|
|
);
|
|
|
|
=head1 NAME
|
|
|
|
Kernel::Output::HTML::Layout::FAQ - all FAQ-related HTML functions
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
All FAQ-related HTML functions
|
|
|
|
=head1 PUBLIC INTERFACE
|
|
|
|
=head2 GetFAQItemVotingRateColor()
|
|
Returns a color depending on the FAQ rate
|
|
|
|
my $VotingResultColor = $LayoutObject->GetFAQItemVotingRateColor(
|
|
Rate => '20',
|
|
);
|
|
|
|
Returns:
|
|
|
|
$VotingResultColor = 'red' # or 'orange' or 'green'
|
|
|
|
=cut
|
|
|
|
sub GetFAQItemVotingRateColor {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
if ( !defined $Param{Rate} ) {
|
|
return $Self->FatalError(
|
|
Message => Translatable('Need rate!'),
|
|
);
|
|
}
|
|
my $CssTmp = '';
|
|
my $VotingResultColors = $Kernel::OM->Get('Kernel::Config')->Get('FAQ::Explorer::ItemList::VotingResultColors');
|
|
|
|
for my $Key ( sort { $b <=> $a } keys %{$VotingResultColors} ) {
|
|
if ( $Param{Rate} <= $Key ) {
|
|
$CssTmp = $VotingResultColors->{$Key};
|
|
}
|
|
}
|
|
return $CssTmp;
|
|
}
|
|
|
|
=head2 FAQListShow()
|
|
|
|
Returns a list of FAQ items as sort-able list with pagination.
|
|
|
|
This function is similar to L<Kernel::Output::HTML::LayoutTicket::TicketListShow()>
|
|
in F<Kernel/Output/HTML/Layout/Ticket.pm>.
|
|
|
|
my $Output = $LayoutObject->FAQListShow(
|
|
FAQIDs => $FAQIDsRef, # total list of FAQIDs, that can be listed
|
|
Total => scalar @{ $FAQIDsRef }, # total number of list items, in this case
|
|
View => $Self->{View}, # optional, the default value is 'Small'
|
|
Filter => 'All',
|
|
Filters => \%NavBarFilter,
|
|
FilterLink => $LinkFilter,
|
|
TitleName => 'Overview: FAQ',
|
|
TitleValue => $Self->{Filter},
|
|
Env => $Self,
|
|
LinkPage => $LinkPage,
|
|
LinkSort => $LinkSort,
|
|
Frontend => 'Agent', # optional (Agent|Customer|Public), default: Agent, indicates from which frontend this function was called
|
|
);
|
|
|
|
=cut
|
|
|
|
sub FAQListShow {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# take object ref to local, remove it from %Param (prevent memory leak)
|
|
my $Env = $Param{Env};
|
|
delete $Param{Env};
|
|
|
|
# lookup latest used view mode
|
|
if ( !$Param{View} && $Self->{ 'UserFAQOverview' . $Env->{Action} } ) {
|
|
$Param{View} = $Self->{ 'UserFAQOverview' . $Env->{Action} };
|
|
}
|
|
|
|
# set default view mode to 'small'
|
|
my $View = $Param{View} || 'Small';
|
|
|
|
# store latest view mode
|
|
$Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID(
|
|
SessionID => $Self->{SessionID},
|
|
Key => 'UserFAQOverview' . $Env->{Action},
|
|
Value => $View,
|
|
);
|
|
|
|
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
|
|
|
# get backend from config
|
|
my $Backends = $ConfigObject->Get('FAQ::Frontend::Overview') || '';
|
|
if ( !$Backends ) {
|
|
return $Self->FatalError(
|
|
Message => Translatable('Need config option FAQ::Frontend::Overview'),
|
|
);
|
|
}
|
|
|
|
# check for hash-ref
|
|
if ( ref $Backends ne 'HASH' ) {
|
|
return $Self->FatalError(
|
|
Message => Translatable('Config option FAQ::Frontend::Overview needs to be a HASH ref!'),
|
|
);
|
|
}
|
|
|
|
# check for config key
|
|
if ( !$Backends->{$View} ) {
|
|
return $Self->FatalError(
|
|
Message => $Self->{LanguageObject}->Translate( 'No config option found for the view "%s"!', $View ),
|
|
);
|
|
}
|
|
|
|
# navigation bar
|
|
my $StartHit = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam(
|
|
Param => 'StartHit',
|
|
) || 1;
|
|
|
|
# get personal page shown count
|
|
my $PageShownPreferencesKey = 'UserFAQOverview' . $View . 'PageShown';
|
|
my $PageShown = $Self->{$PageShownPreferencesKey} || 10;
|
|
my $Group = 'FAQOverview' . $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 navigation
|
|
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 shown FAQ articles on a page
|
|
$Param{RequestedURL} = "Action=$Self->{Action}";
|
|
$Param{Group} = $Group;
|
|
$Param{PreferencesKey} = $PageShownPreferencesKey;
|
|
$Param{PageShownString} = $Self->BuildSelection(
|
|
Name => $PageShownPreferencesKey,
|
|
SelectedID => $PageShown,
|
|
Data => \%Data,
|
|
Translation => 0,
|
|
Sort => 'NumericValue',
|
|
Class => 'Modernize',
|
|
);
|
|
|
|
# build navigation bar content
|
|
$Self->Block(
|
|
Name => 'OverviewNavBar',
|
|
Data => \%Param,
|
|
);
|
|
|
|
my $LinkBackID = 'FAQSearch';
|
|
if ( $Param{Nav} && $Param{Nav} eq 'None' ) {
|
|
$LinkBackID .= 'Small';
|
|
}
|
|
|
|
# back link
|
|
if ( $Param{LinkBack} ) {
|
|
$Self->Block(
|
|
Name => 'OverviewNavBarPageBack',
|
|
Data => {
|
|
LinkBackID => $LinkBackID,
|
|
%Param,
|
|
},
|
|
);
|
|
}
|
|
|
|
# Send data to JS.
|
|
$Self->AddJSData(
|
|
Key => 'FAQSearchProfile',
|
|
Value => $Param{Profile},
|
|
);
|
|
|
|
# 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
|
|
$Self->Block(
|
|
Name => 'OverviewNavBarFilter',
|
|
Data => {
|
|
%Param,
|
|
},
|
|
);
|
|
|
|
# loop over filters
|
|
my $Count = 0;
|
|
for my $Filter (@NavBarFilters) {
|
|
|
|
# increment filter count and build filter item
|
|
$Count++;
|
|
$Self->Block(
|
|
Name => 'OverviewNavBarFilterItem',
|
|
Data => {
|
|
%Param,
|
|
%{$Filter},
|
|
},
|
|
);
|
|
|
|
# filter is selected
|
|
if ( $Filter->{Filter} eq $Param{Filter} ) {
|
|
$Self->Block(
|
|
Name => 'OverviewNavBarFilterItemSelected',
|
|
Data => {
|
|
%Param,
|
|
%{$Filter},
|
|
},
|
|
);
|
|
|
|
}
|
|
else {
|
|
$Self->Block(
|
|
Name => 'OverviewNavBarFilterItemSelectedNot',
|
|
Data => {
|
|
%Param,
|
|
%{$Filter},
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
# loop over configured back-ends
|
|
for my $Backend ( sort keys %{$Backends} ) {
|
|
|
|
# build navigation bar view mode
|
|
$Self->Block(
|
|
Name => 'OverviewNavBarViewMode',
|
|
Data => {
|
|
%Param,
|
|
%{ $Backends->{$Backend} },
|
|
Filter => $Param{Filter},
|
|
View => $Backend,
|
|
},
|
|
);
|
|
|
|
# current view is configured in backend
|
|
if ( $View eq $Backend ) {
|
|
$Self->Block(
|
|
Name => 'OverviewNavBarViewModeSelected',
|
|
Data => {
|
|
%Param,
|
|
%{ $Backends->{$Backend} },
|
|
Filter => $Param{Filter},
|
|
View => $Backend,
|
|
},
|
|
);
|
|
}
|
|
else {
|
|
$Self->Block(
|
|
Name => 'OverviewNavBarViewModeNotSelected',
|
|
Data => {
|
|
%Param,
|
|
%{ $Backends->{$Backend} },
|
|
Filter => $Param{Filter},
|
|
View => $Backend,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
# check if page navigation is available
|
|
if (%PageNav) {
|
|
$Self->Block(
|
|
Name => 'OverviewNavBarPageNavBar',
|
|
Data => \%PageNav,
|
|
);
|
|
|
|
# don't show context settings in AJAX case (e. g. in customer FAQ 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 => 'AgentFAQOverviewNavBar',
|
|
Data => {
|
|
View => $View,
|
|
%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 => $Param{Frontend} || 'Agent',
|
|
Nav => $Param{Nav} || '',
|
|
TitleSize => $Param{FAQTitleSize},
|
|
);
|
|
|
|
# create output
|
|
if ( !$Param{Output} ) {
|
|
$Self->Print(
|
|
Output => \$Output,
|
|
);
|
|
}
|
|
else {
|
|
$OutputRaw .= $Output;
|
|
}
|
|
|
|
# create overview navigation bar
|
|
$Self->Block(
|
|
Name => 'OverviewNavBar',
|
|
Data => {%Param},
|
|
);
|
|
|
|
# return content if available
|
|
return $OutputRaw;
|
|
}
|
|
|
|
=head2 FAQContentShow()
|
|
|
|
Outputs the necessary blocks to display the FAQ item fields for the supplied FAQ item ID.
|
|
The fields displayed are also restricted by the permissions represented by the supplied interface
|
|
|
|
If exist ReturnContent parameter it returns the FAQ items fields on a HTML formatted string
|
|
|
|
$LayoutObject->FAQContentShow(
|
|
FAQObject => $FAQObject, # needed for core module interaction
|
|
FAQData => %{ $FAQData },
|
|
InterfaceStates => $Self->{InterfaceStates},
|
|
UserID => 1,
|
|
);
|
|
|
|
my $Content = $LayoutObject->FAQContentShow(
|
|
FAQObject => $FAQObject, # needed for core module interaction
|
|
FAQData => %{ $FAQData },
|
|
InterfaceStates => $Self->{InterfaceStates},
|
|
UserID => 1,
|
|
ReturnContent => 1,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub FAQContentShow {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
for my $ParamName (qw(FAQObject FAQData InterfaceStates UserID)) {
|
|
if ( !$Param{$ParamName} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $ParamName!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# store FAQ object locally
|
|
my $FAQObject = $Param{FAQObject};
|
|
|
|
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
|
|
|
# get the config of FAQ fields that should be shown
|
|
my %Fields;
|
|
FIELD:
|
|
for my $Number ( 1 .. 6 ) {
|
|
|
|
# get config of FAQ field
|
|
my $Config = $ConfigObject->Get( 'FAQ::Item::Field' . $Number );
|
|
|
|
# skip over not shown fields
|
|
next FIELD if !$Config->{Show};
|
|
|
|
# store only the config of fields that should be shown
|
|
$Fields{"Field$Number"} = $Config;
|
|
}
|
|
|
|
my $FullContent;
|
|
|
|
# sort shown fields by priority
|
|
FIELD:
|
|
for my $Field ( sort { $Fields{$a}->{Prio} <=> $Fields{$b}->{Prio} } keys %Fields ) {
|
|
|
|
# get the state type data of this field
|
|
my $StateTypeData = $FAQObject->StateTypeGet(
|
|
Name => $Fields{$Field}->{Show},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# do not show fields that are not allowed in the given interface
|
|
next FIELD if !$Param{InterfaceStates}->{ $StateTypeData->{StateID} };
|
|
|
|
my $Caption = $Fields{$Field}->{'Caption'};
|
|
my $Content = $Param{FAQData}->{$Field} || '';
|
|
|
|
# remove active HTML content (scripts, applets, etc...)
|
|
my %SafeContent = $Kernel::OM->Get('Kernel::System::HTMLUtils')->Safety(
|
|
String => $Content,
|
|
NoApplet => 1,
|
|
NoObject => 1,
|
|
NoEmbed => 1,
|
|
NoIntSrcLoad => 0,
|
|
NoExtSrcLoad => 0,
|
|
NoJavaScript => 1,
|
|
);
|
|
|
|
# take the safe content if necessary
|
|
if ( $SafeContent{Replace} ) {
|
|
$Content = $SafeContent{String};
|
|
}
|
|
|
|
# show the field
|
|
$Self->Block(
|
|
Name => 'FAQContent',
|
|
Data => {
|
|
ItemID => $Param{FAQData}->{ItemID},
|
|
Field => $Field,
|
|
Caption => $Caption,
|
|
StateName => $StateTypeData->{Name},
|
|
Content => $Content,
|
|
},
|
|
);
|
|
|
|
if ( $ConfigObject->Get('FAQ::Item::HTML') && $Self->{BrowserRichText} ) {
|
|
|
|
$Self->Block(
|
|
Name => 'FAQContentHTML',
|
|
Data => {
|
|
ItemID => $Param{FAQData}->{ItemID},
|
|
Field => $Field,
|
|
},
|
|
);
|
|
}
|
|
else {
|
|
|
|
# convert fields ASCII if needed
|
|
if ( $Param{FAQData}->{ContentType} ne 'text/plain' ) {
|
|
$Content = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToAscii(
|
|
String => $Content,
|
|
) || '';
|
|
}
|
|
|
|
$Self->Block(
|
|
Name => 'FAQContentPlain',
|
|
Data => {
|
|
Content => $Content,
|
|
},
|
|
);
|
|
}
|
|
|
|
# Send config to JS.
|
|
$Self->AddJSData(
|
|
Key => 'AgentHTMLFieldHeightDefault',
|
|
Value => $ConfigObject->Get('FAQ::Frontend::AgentHTMLFieldHeightDefault'),
|
|
);
|
|
$Self->AddJSData(
|
|
Key => 'AgentHTMLFieldHeightMax',
|
|
Value => $ConfigObject->Get('FAQ::Frontend::AgentHTMLFieldHeightMax'),
|
|
);
|
|
|
|
# store the field to return all FAQ Body
|
|
if ( $Param{ReturnContent} && $Content ) {
|
|
|
|
# get the internal state type
|
|
my $InternalStateType = $FAQObject->StateTypeGet(
|
|
Name => 'internal',
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# check if current field is internal
|
|
my $IsInternal;
|
|
if ( $StateTypeData->{StateID} == $InternalStateType->{StateID} ) {
|
|
$IsInternal = 1;
|
|
}
|
|
|
|
# get configuration options for Ticket Compose
|
|
my $TicketComposeConfig = $ConfigObject->Get('FAQ::TicketCompose');
|
|
|
|
# Check if field should be part of the returning string
|
|
if ( $TicketComposeConfig->{IncludeInternal} || !$IsInternal ) {
|
|
|
|
# Check if field name should be returned
|
|
if ( $TicketComposeConfig->{ShowFieldNames} ) {
|
|
$FullContent .= $Self->{LanguageObject}->Translate($Caption) . ' <br/> ';
|
|
}
|
|
$FullContent .= $Content . ' <br/> ';
|
|
}
|
|
}
|
|
}
|
|
|
|
# return all the (permitted) FAQ body
|
|
if ( $Param{ReturnContent} ) {
|
|
if ($FullContent) {
|
|
return $FullContent;
|
|
}
|
|
return $Self->{LanguageObject}->Translate('This article is empty!');
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
=head2 FAQPathShow()
|
|
|
|
if its allowed by the configuration, outputs the necessary blocks to display the FAQ item path,
|
|
and returns the value 1.
|
|
|
|
my $ShowPathOk = $LayoutObject->FAQPathShow(
|
|
FAQObject => $FAQObject, # needed for core module interaction
|
|
CategoryID => 5,
|
|
UserID => 1,
|
|
PathForItem => 1, # optional (default 0)
|
|
Nav => 'none', # optional
|
|
);
|
|
|
|
=cut
|
|
|
|
sub FAQPathShow {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check parameters
|
|
for my $ParamName (qw(FAQObject UserID)) {
|
|
if ( !$Param{$ParamName} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $ParamName!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
$Param{PathForItem} ||= 0;
|
|
|
|
# check parameters
|
|
if ( !defined $Param{CategoryID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need CategoryID!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
|
|
|
my $Block = 'FAQPathCategoryElement';
|
|
if ( $Param{CategoryID} eq '0' ) {
|
|
$Block = 'FAQPathCategoryElementNoLink';
|
|
}
|
|
|
|
# output category root
|
|
$Self->Block(
|
|
Name => $Block,
|
|
Data => {
|
|
Name => $ConfigObject->Get('FAQ::Default::RootCategoryName'),
|
|
CategoryID => 0,
|
|
Nav => $Param{Nav},
|
|
},
|
|
);
|
|
|
|
# get Show FAQ Path setting
|
|
my $ShowPath = $ConfigObject->Get('FAQ::Explorer::Path::Show');
|
|
|
|
# do not display the path if setting is off
|
|
return if !$ShowPath;
|
|
|
|
my $FAQObject = $Param{FAQObject};
|
|
|
|
my $CategoryList = $FAQObject->FAQPathListGet(
|
|
CategoryID => $Param{CategoryID},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# output subcategories
|
|
for my $CategoryData ( @{$CategoryList} ) {
|
|
|
|
$Block = 'FAQPathCategoryElement';
|
|
if (
|
|
$CategoryData->{CategoryID} == $CategoryList->[-1]->{CategoryID}
|
|
&& !$Param{PathForItem}
|
|
)
|
|
{
|
|
$Block = 'FAQPathCategoryElementNoLink';
|
|
}
|
|
|
|
$Self->Block(
|
|
Name => $Block,
|
|
Data => {
|
|
Nav => $Param{Nav},
|
|
%{$CategoryData},
|
|
},
|
|
);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
=head2 FAQRatingStarsShow()
|
|
|
|
Outputs the necessary blocks to represent the FAQ item rating
|
|
as "Stars" in the scale from 1 to 5.
|
|
|
|
$LayoutObject->FAQRatingStarsShow(
|
|
VoteResult => $FAQData->{VoteResult},
|
|
Votes => $FAQData ->{Votes},
|
|
);
|
|
|
|
=cut
|
|
|
|
sub FAQRatingStarsShow {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
for my $ParamName (qw(VoteResult Votes)) {
|
|
if ( !defined $Param{$ParamName} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $ParamName!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# get stars by multiply by 5 and divide by 100
|
|
# 100 because Vote result is a %
|
|
# 5 because we have only 5 stars
|
|
my $StarCounter = int( $Param{VoteResult} * 0.05 );
|
|
if ( $StarCounter < 5 ) {
|
|
|
|
# add 1 because lowest value should be 1
|
|
$StarCounter++;
|
|
}
|
|
|
|
# the number of stars can't be grater that 5
|
|
elsif ( $StarCounter > 5 ) {
|
|
$StarCounter = 5;
|
|
}
|
|
|
|
# output rating block
|
|
$Self->Block(
|
|
Name => 'ViewRating',
|
|
Data => {
|
|
%Param,
|
|
},
|
|
);
|
|
|
|
# do not output any star if this FAQ has been not voted
|
|
if ( $Param{Votes} eq '0' ) {
|
|
$StarCounter = 0;
|
|
}
|
|
|
|
# show stars only if the FAQ item has been voted at least once even if the $VoteResult is 0
|
|
else {
|
|
|
|
# output stars
|
|
for ( 1 .. $StarCounter ) {
|
|
$Self->Block(
|
|
Name => 'RateStars',
|
|
);
|
|
}
|
|
}
|
|
|
|
# output stars text
|
|
$Self->Block(
|
|
Name => 'RateStarsCount',
|
|
Data => { Stars => $StarCounter },
|
|
);
|
|
|
|
return 1;
|
|
}
|
|
|
|
=head2 FAQShowLatestNewsBox()
|
|
|
|
Shows an info box with the last updated or last created FAQ articles.
|
|
Depending on the uses interface (agent, customer, public) only the appropriate
|
|
articles are shown here.
|
|
|
|
$LayoutObject->FAQShowLatestNewsBox(
|
|
FAQObject => $FAQObject, # needed for core module interaction
|
|
Type => 'LastCreate', # (LastCreate | LastChange)
|
|
Mode => 'Public', # (Agent, Customer, Public)
|
|
CategoryID => 5,
|
|
Interface => $Self->{Interface},
|
|
InterfaceStates => $Self->{InterfaceStates},
|
|
UserID => 1,
|
|
Nav => 'none', # optional
|
|
);
|
|
|
|
=cut
|
|
|
|
sub FAQShowLatestNewsBox {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
for my $ParamName (qw(FAQObject Type Mode Interface InterfaceStates UserID)) {
|
|
if ( !$Param{$ParamName} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $ParamName!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( !defined $Param{CategoryID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need CategoryID!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# check type
|
|
if ( $Param{Type} !~ m{ LastCreate | LastChange }xms ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Type must be either LastCreate or LastChange!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# check mode
|
|
if ( $Param{Mode} !~ m{ Agent | Customer | Public }xms ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Mode must be either Agent, Customer or Public!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# check CustomerUser
|
|
if ( $Param{Mode} eq 'Customer' && !$Param{CustomerUser} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need CustomerUser!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# set order by search parameter and header based on type
|
|
my $OrderBy;
|
|
my $Header;
|
|
my $RSSTitle;
|
|
if ( $Param{Type} eq 'LastCreate' ) {
|
|
$OrderBy = 'Created';
|
|
$Header = Translatable('Latest created FAQ articles');
|
|
$RSSTitle = Translatable('FAQ Articles (new created)');
|
|
}
|
|
elsif ( $Param{Type} eq 'LastChange' ) {
|
|
$OrderBy = 'Changed';
|
|
$Header = Translatable('Latest updated FAQ articles');
|
|
$RSSTitle = Translatable('FAQ Articles (recently changed)');
|
|
}
|
|
|
|
my $Result = -1;
|
|
|
|
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
|
|
|
# show last added/updated articles
|
|
my $Show = $ConfigObject->Get("FAQ::Explorer::$Param{Type}::Show");
|
|
if ( $Show->{ $Param{Interface}->{Name} } ) {
|
|
|
|
my $FAQObject = $Param{FAQObject};
|
|
|
|
# to store search param for categories
|
|
my %CategorySearchParam;
|
|
|
|
# if subcategories should also be shown
|
|
if ( $ConfigObject->Get("FAQ::Explorer::$Param{Type}::ShowSubCategoryItems") ) {
|
|
|
|
# find the subcategories of this category
|
|
my $SubCategoryIDsRef = $FAQObject->CategorySubCategoryIDList(
|
|
ParentID => $Param{CategoryID},
|
|
Mode => $Param{Mode},
|
|
ItemStates => $Param{InterfaceStates},
|
|
CustomerUser => $Param{CustomerUser} || '',
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# search in the given category and add the subcategory
|
|
$CategorySearchParam{CategoryIDs} = [ $Param{CategoryID}, @{$SubCategoryIDsRef} ];
|
|
}
|
|
|
|
# a category is given and subcategories should not be shown
|
|
elsif ( $Param{CategoryID} ) {
|
|
|
|
# search only in the given category
|
|
$CategorySearchParam{CategoryIDs} = [ $Param{CategoryID} ];
|
|
}
|
|
|
|
# search the FAQ articles
|
|
my @ItemIDs = $FAQObject->FAQSearch(
|
|
States => $Param{InterfaceStates},
|
|
OrderBy => [$OrderBy],
|
|
OrderByDirection => ['Down'],
|
|
Interface => $Param{Interface},
|
|
Limit => $ConfigObject->Get("FAQ::Explorer::$Param{Type}::Limit") || 5,
|
|
UserID => $Param{UserID},
|
|
%CategorySearchParam,
|
|
);
|
|
|
|
# there is something to show
|
|
if (@ItemIDs) {
|
|
|
|
$Result = 1;
|
|
|
|
# show the info box
|
|
$Self->Block(
|
|
Name => 'InfoBoxFAQMiniList',
|
|
Data => {
|
|
Header => $Header,
|
|
},
|
|
);
|
|
|
|
# show the RSS Feed icon
|
|
if ( $Param{Mode} eq 'Public' ) {
|
|
|
|
$Self->Block(
|
|
Name => 'InfoBoxFAQMiniListNewsRSS',
|
|
Data => {
|
|
Type => $OrderBy,
|
|
Title => $RSSTitle,
|
|
},
|
|
);
|
|
}
|
|
|
|
for my $ItemID (@ItemIDs) {
|
|
|
|
# get FAQ data
|
|
my %FAQData = $FAQObject->FAQGet(
|
|
ItemID => $ItemID,
|
|
ItemFields => 1,
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# show the article row
|
|
$Self->Block(
|
|
Name => 'InfoBoxFAQMiniListItemRow',
|
|
Data => {
|
|
Nav => $Param{Nav},
|
|
%FAQData,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $Result;
|
|
}
|
|
|
|
=head2 FAQShowTop10()
|
|
|
|
Shows an info box with the Top 10 FAQ articles.
|
|
Depending on the uses interface (agent, customer, public) only the appropriate
|
|
articles are shown here.
|
|
|
|
$LayoutObject->FAQShowTop10(
|
|
FAQObject => $FAQObject, # needed for core module interaction
|
|
Mode => 'Public', # (Agent, Customer, Public)
|
|
CategoryID => 5,
|
|
Interface => $Self->{Interface},
|
|
InterfaceStates => $Self->{InterfaceStates},
|
|
UserID => 1,
|
|
Nav => 'none', # optional
|
|
);
|
|
|
|
=cut
|
|
|
|
sub FAQShowTop10 {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
for my $ParamName (qw(FAQObject Mode Interface InterfaceStates UserID)) {
|
|
if ( !$Param{$ParamName} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $ParamName!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( !defined $Param{CategoryID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need CategoryID!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# check mode
|
|
if ( $Param{Mode} !~ m{ Agent | Customer | Public }xms ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Mode must be either Agent, Customer or Public!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# check CustomerUser
|
|
if ( $Param{Mode} eq 'Customer' && !$Param{CustomerUser} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need CustomerUser!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
my $Result = -1;
|
|
|
|
# get config object
|
|
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
|
|
|
# show last added/updated articles
|
|
my $Show = $ConfigObject->Get('FAQ::Explorer::Top10::Show');
|
|
if ( $Show->{ $Param{Interface}->{Name} } ) {
|
|
|
|
my $FAQObject = $Param{FAQObject};
|
|
|
|
# to store search param for categories
|
|
my %CategorySearchParam;
|
|
|
|
# if subcategories should also be shown
|
|
if ( $ConfigObject->Get('FAQ::Explorer::Top10::ShowSubCategoryItems') ) {
|
|
|
|
# find the subcategories of this category
|
|
my $SubCategoryIDsRef = $FAQObject->CategorySubCategoryIDList(
|
|
ParentID => $Param{CategoryID},
|
|
Mode => $Param{Mode},
|
|
ItemStates => $Param{InterfaceStates},
|
|
CustomerUser => $Param{CustomerUser} || '',
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# search in the given category and add the subcategory
|
|
$CategorySearchParam{CategoryIDs} = [ $Param{CategoryID}, @{$SubCategoryIDsRef} ];
|
|
}
|
|
|
|
# get the top 10 articles for categories with at least ro permissions
|
|
my $Top10ItemIDsRef = $FAQObject->FAQTop10Get(
|
|
Interface => $Param{Interface}->{Name},
|
|
Limit => $ConfigObject->Get('FAQ::Explorer::Top10::Limit') || 10,
|
|
UserID => $Param{UserID},
|
|
%CategorySearchParam,
|
|
) || [];
|
|
|
|
# there is something to show
|
|
if ( $Top10ItemIDsRef && ref $Top10ItemIDsRef eq 'ARRAY' && @{$Top10ItemIDsRef} ) {
|
|
|
|
$Result = 1;
|
|
|
|
# show the info box
|
|
$Self->Block(
|
|
Name => 'InfoBoxFAQMiniList',
|
|
Data => {
|
|
Header => Translatable('Top 10 FAQ articles'),
|
|
},
|
|
);
|
|
|
|
# show the RSS Feed icon
|
|
if ( $Param{Mode} eq 'Public' ) {
|
|
|
|
$Self->Block(
|
|
Name => 'InfoBoxFAQMiniListNewsRSS',
|
|
Data => {
|
|
Type => 'Top10',
|
|
Title => Translatable('FAQ Articles (Top 10)'),
|
|
},
|
|
);
|
|
}
|
|
|
|
my $Number;
|
|
for my $Top10Item ( @{$Top10ItemIDsRef} ) {
|
|
|
|
# increase the number
|
|
$Number++;
|
|
|
|
my %FAQData = $FAQObject->FAQGet(
|
|
ItemID => $Top10Item->{ItemID},
|
|
ItemFields => 1,
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# show the article row
|
|
$Self->Block(
|
|
Name => 'InfoBoxFAQMiniListItemRow',
|
|
Data => {
|
|
Nav => $Param{Nav},
|
|
%FAQData,
|
|
},
|
|
);
|
|
|
|
# show the Top-10 position number
|
|
$Self->Block(
|
|
Name => 'InfoBoxFAQMiniListItemRowPositionNumber',
|
|
Data => {
|
|
Number => $Number,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $Result;
|
|
}
|
|
|
|
=head2 FAQShowQuickSearch()
|
|
|
|
Shows an info box with the Quick Search.
|
|
|
|
$LayoutObject->FAQShowQuickSearch(
|
|
Mode => 'Public', # (Agent, Customer, Public)
|
|
Interface => $Self->{Interface},
|
|
InterfaceStates => $Self->{InterfaceStates},
|
|
UserID => 1,
|
|
Nav => 'none', # optional
|
|
SearchBackLink => $Base64EncodedUrl, # optional
|
|
);
|
|
|
|
=cut
|
|
|
|
sub FAQShowQuickSearch {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
for my $ParamName (qw(Mode Interface InterfaceStates UserID)) {
|
|
if ( !$Param{$ParamName} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $ParamName!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# check mode
|
|
if ( $Param{Mode} !~ m{ Agent | AgentSmall | Customer | Public }xms ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Mode must be either Agent, Customer or Public!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# set action module
|
|
my $Action;
|
|
if ( $Param{Mode} eq 'AgentSmall' ) {
|
|
$Action = 'AgentFAQSearchSmall';
|
|
}
|
|
else {
|
|
$Action = $Param{Mode} . 'FAQSearch';
|
|
}
|
|
|
|
# check CustomerUser
|
|
if ( $Param{Mode} eq 'Customer' && !$Param{CustomerUser} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need CustomerUser!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# show quick search
|
|
my $Show = $Kernel::OM->Get('Kernel::Config')->Get('FAQ::Explorer::QuickSearch::Show');
|
|
if ( $Show->{ $Param{Interface}->{Name} } || $Param{Mode} eq 'AgentSmall' ) {
|
|
|
|
# call QuickSearch block
|
|
$Self->Block(
|
|
Name => 'QuickSearch',
|
|
Data => {
|
|
Action => $Action,
|
|
Nav => $Param{Nav} || '',
|
|
SearchBackLink => $Param{SearchBackLink} || '',
|
|
},
|
|
);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
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
|