612 lines
18 KiB
Perl
612 lines
18 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::Modules::CustomerFAQZoom;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use Kernel::Language qw(Translatable);
|
|
use Kernel::System::VariableCheck qw(:all);
|
|
|
|
our $ObjectManagerDisabled = 1;
|
|
|
|
sub new {
|
|
my ( $Type, %Param ) = @_;
|
|
|
|
# allocate new hash for object
|
|
my $Self = {%Param};
|
|
bless( $Self, $Type );
|
|
|
|
if ( !defined $Self->{DoNotShowBrowserLinkMessage} ) {
|
|
my %UserPreferences = $Kernel::OM->Get('Kernel::System::CustomerUser')->GetPreferences(
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
if ( $UserPreferences{UserCustomerDoNotShowBrowserLinkMessage} ) {
|
|
$Self->{DoNotShowBrowserLinkMessage} = 1;
|
|
}
|
|
else {
|
|
$Self->{DoNotShowBrowserLinkMessage} = 0;
|
|
}
|
|
}
|
|
|
|
return $Self;
|
|
}
|
|
|
|
sub Run {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
|
|
|
|
# get params
|
|
my %GetParam;
|
|
$GetParam{ItemID} = $ParamObject->GetParam( Param => 'ItemID' );
|
|
$GetParam{Rate} = $ParamObject->GetParam( Param => 'Rate' );
|
|
|
|
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
|
|
|
# save, if browser link message was closed
|
|
if ( $Self->{Subaction} eq 'BrowserLinkMessage' ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::CustomerUser')->SetPreferences(
|
|
UserID => $Self->{UserID},
|
|
Key => 'UserCustomerDoNotShowBrowserLinkMessage',
|
|
Value => 1,
|
|
);
|
|
|
|
return $LayoutObject->Attachment(
|
|
ContentType => 'text/html',
|
|
Content => 1,
|
|
Type => 'inline',
|
|
NoCache => 1,
|
|
);
|
|
}
|
|
|
|
if ( !$GetParam{ItemID} ) {
|
|
return $LayoutObject->CustomerFatalError(
|
|
Message => Translatable('Need ItemID!'),
|
|
);
|
|
}
|
|
|
|
my $FAQObject = $Kernel::OM->Get('Kernel::System::FAQ');
|
|
|
|
my %FAQData = $FAQObject->FAQGet(
|
|
ItemID => $GetParam{ItemID},
|
|
ItemFields => 1,
|
|
UserID => $Self->{UserID},
|
|
DynamicFields => 1,
|
|
);
|
|
if ( !%FAQData ) {
|
|
return $LayoutObject->CustomerFatalError();
|
|
}
|
|
|
|
# get the valid ids
|
|
my @ValidIDs = $Kernel::OM->Get('Kernel::System::Valid')->ValidIDsGet();
|
|
my %ValidIDLookup = map { $_ => 1 } @ValidIDs;
|
|
|
|
# check user permission
|
|
my $Permission = $FAQObject->CheckCategoryCustomerPermission(
|
|
CustomerUser => $Self->{UserLogin},
|
|
CategoryID => $FAQData{CategoryID},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
|
|
|
# get interface state list
|
|
my $InterfaceStates = $FAQObject->StateTypeList(
|
|
Types => $ConfigObject->Get('FAQ::Customer::StateTypes'),
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
# permission check
|
|
if (
|
|
!$Permission
|
|
|| !$FAQData{Approved}
|
|
|| !$ValidIDLookup{ $FAQData{ValidID} }
|
|
|| !$InterfaceStates->{ $FAQData{StateTypeID} }
|
|
)
|
|
{
|
|
return $LayoutObject->CustomerNoPermission( WithHeader => 'yes' );
|
|
}
|
|
|
|
# store the last screen in session
|
|
$Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID(
|
|
SessionID => $Self->{SessionID},
|
|
Key => 'LastScreenView',
|
|
Value => $Self->{RequestedURL},
|
|
);
|
|
|
|
# ---------------------------------------------------------- #
|
|
# HTMLView Subaction
|
|
# ---------------------------------------------------------- #
|
|
if ( $Self->{Subaction} eq 'HTMLView' ) {
|
|
|
|
# get params
|
|
my $Field = $ParamObject->GetParam( Param => "Field" );
|
|
|
|
for my $Needed (qw( ItemID Field )) {
|
|
if ( !$Needed ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Needed Param: $Needed!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# get the Field content
|
|
my $FieldContent = $FAQObject->ItemFieldGet(
|
|
ItemID => $GetParam{ItemID},
|
|
Field => $Field,
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
# rewrite handle and action
|
|
$FieldContent
|
|
=~ s{ index[.]pl [?] Action=AgentFAQZoom }{customer.pl?Action=CustomerFAQZoom}gxms;
|
|
|
|
# take care of old style before FAQ 2.0.x
|
|
$FieldContent =~ s{
|
|
index[.]pl [?] Action=AgentFAQ [&](amp;)? Subaction=Download [&](amp;)?
|
|
}{customer.pl?Action=CustomerFAQZoom;Subaction=DownloadAttachment;}gxms;
|
|
|
|
# build base URL for inline images
|
|
my $SessionID = '';
|
|
if ( $Self->{SessionID} && !$Self->{SessionIDCookie} ) {
|
|
$SessionID = ';' . $Self->{SessionName} . '=' . $Self->{SessionID};
|
|
$FieldContent =~ s{
|
|
(Action=CustomerFAQZoom;Subaction=DownloadAttachment;ItemID=\d+;FileID=\d+)
|
|
}{$1$SessionID}gmsx;
|
|
}
|
|
|
|
my $HTMLUtilsObject = $Kernel::OM->Get('Kernel::System::HTMLUtils');
|
|
|
|
# convert content to HTML if needed
|
|
if (
|
|
$Kernel::OM->Get('Kernel::Config')->Get('FAQ::Item::HTML')
|
|
&& $LayoutObject->{BrowserRichText}
|
|
&& $FAQData{ContentType} ne 'text/html'
|
|
)
|
|
{
|
|
$FieldContent = $HTMLUtilsObject->ToHTML(
|
|
String => $FieldContent,
|
|
) || '';
|
|
}
|
|
|
|
# add needed HTML headers
|
|
$FieldContent = $HTMLUtilsObject->DocumentComplete(
|
|
String => $FieldContent,
|
|
Charset => 'utf-8',
|
|
);
|
|
|
|
# return complete HTML as an attachment
|
|
return $LayoutObject->Attachment(
|
|
Type => 'inline',
|
|
ContentType => 'text/html',
|
|
Content => $FieldContent,
|
|
);
|
|
}
|
|
|
|
# ---------------------------------------------------------- #
|
|
# DownloadAttachment Subaction
|
|
# ---------------------------------------------------------- #
|
|
if ( $Self->{Subaction} eq 'DownloadAttachment' ) {
|
|
|
|
# manage parameters
|
|
$GetParam{FileID} = $ParamObject->GetParam( Param => 'FileID' );
|
|
if ( !defined $GetParam{FileID} ) {
|
|
return $LayoutObject->CustomerFatalError(
|
|
Message => Translatable('Need FileID!'),
|
|
);
|
|
}
|
|
|
|
# get attachments
|
|
my %File = $FAQObject->AttachmentGet(
|
|
ItemID => $GetParam{ItemID},
|
|
FileID => $GetParam{FileID},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
if (%File) {
|
|
return $LayoutObject->Attachment(%File);
|
|
}
|
|
else {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Message => "No such attachment ($GetParam{FileID})! May be an attack!!!",
|
|
Priority => 'error',
|
|
);
|
|
return $LayoutObject->CustomerFatalError();
|
|
}
|
|
}
|
|
|
|
my $Output = $LayoutObject->CustomerHeader(
|
|
Value => $FAQData{Title},
|
|
);
|
|
$Output .= $LayoutObject->CustomerNavigationBar();
|
|
|
|
# set default interface settings
|
|
my $Interface = $FAQObject->StateTypeGet(
|
|
Name => 'external',
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
# get voting default option
|
|
my $Voting = $ConfigObject->Get('FAQ::Voting');
|
|
|
|
# get FAQ vote information
|
|
my $VoteData;
|
|
if ($Voting) {
|
|
$VoteData = $FAQObject->VoteGet(
|
|
CreateBy => $Self->{UserID},
|
|
ItemID => $FAQData{ItemID},
|
|
Interface => $Interface->{StateID},
|
|
IP => $ENV{'REMOTE_ADDR'},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
}
|
|
|
|
# check if user already voted this FAQ item
|
|
my $AlreadyVoted;
|
|
if ($VoteData) {
|
|
|
|
my $ItemChangedSystemTime = $Kernel::OM->Create(
|
|
'Kernel::System::DateTime',
|
|
ObjectParams => {
|
|
String => $FAQData{Changed} || '',
|
|
}
|
|
)->ToEpoch();
|
|
|
|
my $VoteCreatedSystemTime = $Kernel::OM->Create(
|
|
'Kernel::System::DateTime',
|
|
ObjectParams => {
|
|
String => $VoteData->{Created} || '',
|
|
}
|
|
)->ToEpoch();
|
|
|
|
if ( $ItemChangedSystemTime <= $VoteCreatedSystemTime ) {
|
|
$AlreadyVoted = 1;
|
|
}
|
|
}
|
|
|
|
# ---------------------------------------------------------- #
|
|
# Vote Subaction
|
|
# ---------------------------------------------------------- #
|
|
if ( $Self->{Subaction} eq 'Vote' ) {
|
|
|
|
# customer can't use this sub-action if is not enabled
|
|
if ( !$Voting ) {
|
|
$LayoutObject->CustomerFatalError(
|
|
Message => Translatable('The voting mechanism is not enabled!'),
|
|
);
|
|
}
|
|
|
|
# user can vote only once per FAQ revision
|
|
if ($AlreadyVoted) {
|
|
$Output .= $LayoutObject->Notify(
|
|
Priority => 'Error',
|
|
Info => Translatable('You have already voted!'),
|
|
);
|
|
}
|
|
|
|
# set the vote if any
|
|
elsif ( defined $GetParam{Rate} ) {
|
|
|
|
# get rates config
|
|
my $VotingRates = $ConfigObject->Get('FAQ::Item::Voting::Rates');
|
|
my $Rate = $GetParam{Rate};
|
|
|
|
# send error if rate is not defined in config
|
|
if ( !$VotingRates->{$Rate} ) {
|
|
$LayoutObject->CustomerFatalError(
|
|
Message => Translatable('The vote rate is not defined!'),
|
|
);
|
|
}
|
|
|
|
# otherwise add the vote
|
|
else {
|
|
$FAQObject->VoteAdd(
|
|
CreatedBy => $Self->{UserID},
|
|
ItemID => $GetParam{ItemID},
|
|
IP => $ENV{'REMOTE_ADDR'},
|
|
Interface => $Interface->{StateID},
|
|
Rate => $GetParam{Rate},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
# do not show the voting form
|
|
$AlreadyVoted = 1;
|
|
|
|
# refresh FAQ item data
|
|
%FAQData = $FAQObject->FAQGet(
|
|
ItemID => $GetParam{ItemID},
|
|
ItemFields => 1,
|
|
UserID => $Self->{UserID},
|
|
);
|
|
if ( !%FAQData ) {
|
|
return $LayoutObject->CustomerFatalError();
|
|
}
|
|
|
|
$Output .= $LayoutObject->Notify(
|
|
Info => Translatable('Thanks for your vote!'),
|
|
);
|
|
}
|
|
}
|
|
|
|
# user is able to vote but no rate has been selected
|
|
else {
|
|
$Output .= $LayoutObject->Notify(
|
|
Priority => 'Error',
|
|
Info => Translatable('No rate selected!'),
|
|
);
|
|
}
|
|
}
|
|
|
|
# prepare fields data (Still needed for PlainText)
|
|
FIELD:
|
|
for my $Field (qw(Field1 Field2 Field3 Field4 Field5 Field6)) {
|
|
next FIELD if !$FAQData{$Field};
|
|
|
|
# rewrite links to embedded images for customer interface
|
|
if ( $Interface->{Name} eq 'external' ) {
|
|
|
|
# rewrite handle and action
|
|
$FAQData{$Field}
|
|
=~ s{ index[.]pl [?] Action=AgentFAQZoom }{customer.pl?Action=CustomerFAQZoom}gxms;
|
|
|
|
# take care of old style before FAQ 2.0.x
|
|
$FAQData{$Field} =~ s{
|
|
index[.]pl [?] Action=AgentFAQ [&](amp;)? Subaction=Download [&](amp;)?
|
|
}{customer.pl?Action=CustomerFAQZoom;Subaction=DownloadAttachment;}gxms;
|
|
}
|
|
|
|
# no quoting if HTML view is enabled
|
|
next FIELD if $ConfigObject->Get('FAQ::Item::HTML');
|
|
|
|
# HTML quoting
|
|
$FAQData{$Field} = $LayoutObject->Ascii2Html(
|
|
NewLine => 0,
|
|
Text => $FAQData{$Field},
|
|
VMax => 5000,
|
|
HTMLResultMode => 1,
|
|
LinkFeature => 1,
|
|
);
|
|
}
|
|
|
|
# set voting results
|
|
$Param{VotingResultColor} = $LayoutObject->GetFAQItemVotingRateColor(
|
|
Rate => $FAQData{VoteResult},
|
|
);
|
|
|
|
if ( !$Param{VotingResultColor} || $FAQData{Votes} eq '0' ) {
|
|
$Param{VotingResultColor} = 'Gray';
|
|
}
|
|
|
|
# show back link
|
|
$LayoutObject->Block(
|
|
Name => 'Back',
|
|
Data => \%Param,
|
|
);
|
|
|
|
# get multi-language default option
|
|
my $MultiLanguage = $ConfigObject->Get('FAQ::MultiLanguage');
|
|
|
|
# show language
|
|
if ($MultiLanguage) {
|
|
$LayoutObject->Block(
|
|
Name => 'Language',
|
|
Data => {%FAQData},
|
|
);
|
|
}
|
|
|
|
# show votes
|
|
if ($Voting) {
|
|
|
|
# always displays Votes result even if its 0
|
|
$LayoutObject->Block(
|
|
Name => 'ViewVotes',
|
|
Data => {%FAQData},
|
|
);
|
|
}
|
|
|
|
# show FAQ path
|
|
my $ShowFAQPath = $LayoutObject->FAQPathShow(
|
|
FAQObject => $FAQObject,
|
|
CategoryID => $FAQData{CategoryID},
|
|
PathForItem => 1,
|
|
UserID => $Self->{UserID},
|
|
);
|
|
if ($ShowFAQPath) {
|
|
$LayoutObject->Block(
|
|
Name => 'FAQPathItemElement',
|
|
Data => {%FAQData},
|
|
);
|
|
}
|
|
|
|
# show keywords as search links
|
|
if ( $FAQData{Keywords} ) {
|
|
|
|
# replace commas and semicolons
|
|
$FAQData{Keywords} =~ s/,/ /g;
|
|
$FAQData{Keywords} =~ s/;/ /g;
|
|
|
|
my @Keywords = split /\s+/, $FAQData{Keywords};
|
|
for my $Keyword (@Keywords) {
|
|
$LayoutObject->Block(
|
|
Name => 'Keywords',
|
|
Data => {
|
|
Keyword => $Keyword,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
# output rating stars
|
|
if ($Voting) {
|
|
$LayoutObject->FAQRatingStarsShow(
|
|
VoteResult => $FAQData{VoteResult},
|
|
Votes => $FAQData{Votes},
|
|
);
|
|
}
|
|
|
|
# output attachments if any
|
|
my @AttachmentIndex = $FAQObject->AttachmentIndex(
|
|
ItemID => $GetParam{ItemID},
|
|
ShowInline => 0,
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
# output attachments
|
|
if (@AttachmentIndex) {
|
|
$LayoutObject->Block(
|
|
Name => 'AttachmentHeader',
|
|
);
|
|
for my $Attachment (@AttachmentIndex) {
|
|
$LayoutObject->Block(
|
|
Name => 'AttachmentRow',
|
|
Data => {
|
|
%FAQData,
|
|
%{$Attachment},
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
# show message about links in iframes, if user didn't close it already
|
|
if ( !$Self->{DoNotShowBrowserLinkMessage} ) {
|
|
$LayoutObject->Block(
|
|
Name => 'BrowserLinkMessage',
|
|
);
|
|
}
|
|
|
|
# show FAQ Content
|
|
$LayoutObject->FAQContentShow(
|
|
FAQObject => $FAQObject,
|
|
InterfaceStates => $InterfaceStates,
|
|
FAQData => {%FAQData},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
# get config of frontend module
|
|
my $Config = $ConfigObject->Get("FAQ::Frontend::$Self->{Action}");
|
|
|
|
# get the dynamic fields for this screen
|
|
my $DynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
|
|
Valid => 1,
|
|
ObjectType => 'FAQ',
|
|
FieldFilter => $Config->{DynamicField} || {},
|
|
);
|
|
|
|
my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
|
|
|
|
DYNAMICFIELD:
|
|
for my $DynamicFieldConfig ( @{$DynamicField} ) {
|
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
|
|
|
my $Value = $DynamicFieldBackendObject->ValueGet(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
ObjectID => $GetParam{ItemID},
|
|
);
|
|
|
|
# get print string for this dynamic field
|
|
my $ValueStrg = $DynamicFieldBackendObject->DisplayValueRender(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
Value => $Value,
|
|
ValueMaxChars => 250,
|
|
LayoutObject => $LayoutObject,
|
|
);
|
|
|
|
my $Label = $DynamicFieldConfig->{Label};
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'FAQDynamicField',
|
|
Data => {
|
|
Label => $Label,
|
|
Value => $ValueStrg->{Value},
|
|
Title => $ValueStrg->{Title},
|
|
},
|
|
);
|
|
|
|
# example of dynamic fields order customization
|
|
$LayoutObject->Block(
|
|
Name => 'FAQDynamicField_' . $DynamicFieldConfig->{Name},
|
|
Data => {
|
|
Label => $Label,
|
|
Value => $ValueStrg->{Value},
|
|
Title => $ValueStrg->{Title},
|
|
},
|
|
);
|
|
}
|
|
|
|
# show FAQ Voting
|
|
if ($Voting) {
|
|
|
|
# get voting config
|
|
my $ShowVotingConfig = $ConfigObject->Get('FAQ::Item::Voting::Show');
|
|
if ( $ShowVotingConfig->{ $Interface->{Name} } ) {
|
|
|
|
# check if the user already voted after last change
|
|
if ( !$AlreadyVoted ) {
|
|
$Self->_FAQVoting( FAQData => {%FAQData} );
|
|
}
|
|
}
|
|
}
|
|
|
|
# log access to this FAQ item
|
|
$FAQObject->FAQLogAdd(
|
|
ItemID => $ParamObject->GetParam( Param => 'ItemID' ),
|
|
Interface => $Interface->{Name},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
$Output .= $LayoutObject->Output(
|
|
TemplateFile => 'CustomerFAQZoom',
|
|
Data => {
|
|
%FAQData,
|
|
%GetParam,
|
|
%Param,
|
|
},
|
|
);
|
|
$Output .= $LayoutObject->CustomerFooter();
|
|
|
|
return $Output;
|
|
}
|
|
|
|
sub _FAQVoting {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my %FAQData = %{ $Param{FAQData} };
|
|
|
|
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'FAQVoting',
|
|
Data => {%FAQData},
|
|
);
|
|
|
|
# get Voting rates setting
|
|
my $VotingRates = $Kernel::OM->Get('Kernel::Config')->Get('FAQ::Item::Voting::Rates');
|
|
for my $RateValue ( sort { $a <=> $b } keys %{$VotingRates} ) {
|
|
|
|
# create data structure for output
|
|
my %Data = (
|
|
Value => $RateValue,
|
|
Title => $VotingRates->{$RateValue},
|
|
);
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'FAQVotingRateRow',
|
|
Data => {%Data},
|
|
);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
1;
|