
#######
Article
#######


****
NAME
****


Kernel::System::Ticket::Article - functions to manage ticket articles


***********
DESCRIPTION
***********


Since OTRS 6, article data is split in a neutral part for all articles (in the \ ``article``\  database table),
and back end specific data in custom tables (such as \ ``article_data_mime``\  for the \ ``MIME``\  based back ends).

This class only manages back end neutral article data, like listing articles with `ArticleList()`_ or manipulating
article metadata like `ArticleFlagSet()`_.

For all operations involving back end specific article data (like \ ``ArticleCreate``\  and \ ``ArticleGet``\ ),
please call `BackendForArticle()`_ or `BackendForChannel()`_ to get to the correct article back end.
See `ArticleList()`_ for an example of looping over all article data of a ticket.

See `Kernel::System::Ticket::Article::Backend::Base` for the definition of the basic interface of all
article back ends.


****************
PUBLIC INTERFACE
****************


new()
=====


Don't use the constructor directly, use the ObjectManager instead:


.. code-block:: perl

     my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');



BackendForArticle()
===================


Returns the correct back end for a given article, or the
`Invalid` back end, so that you can always expect
a back end object instance that can be used for chain-calling.


.. code-block:: perl

     my $ArticleBackendObject = $ArticleObject->BackendForArticle( TicketID => 42, ArticleID => 123 );


Alternatively, you can pass in a hash with base article data as returned by `ArticleList()`_, this will avoid the
lookup for the \ ``CommunicationChannelID``\  of the article:


.. code-block:: perl

     my $ArticleBackendObject = $ArticleObject->BackendForArticle( %BaseArticle );


See `Kernel::System::Ticket::Article::Backend::Base` for the definition of the basic interface of all
article back ends.


BackendForChannel()
===================


Returns the correct back end for a given communication channel, or the \ ``Invalid``\  back end, so that you can always expect
a back end object instance that can be used for chain-calling.


.. code-block:: perl

     my $ArticleBackendObject = $ArticleObject->BackendForChannel( ChannelName => 'Email' );


See `Kernel::System::Ticket::Article::Backend::Base` for the definition of the basic interface of all
article back ends.


ArticleList()
=============


Returns an filtered array of base article data for a ticket.


.. code-block:: perl

     my @Articles = $ArticleObject->ArticleList(
 
         TicketID               => 123,
 
         # Optional filters, these can be combined:
 
         ArticleID              => 234,                # optional, limit to one article (if present on a ticket)
         CommunicationChannel   => 'Email',            # optional, to limit to a certain CommunicationChannel
         CommunicationChannelID => 2,                  # optional, to limit to a certain CommunicationChannelID
         SenderType             => 'customer',         # optional, to limit to a certain article SenderType
         SenderTypeID           => 2,                  # optional, to limit to a certain article SenderTypeID
         IsVisibleForCustomer   => 0,                  # optional, to limit to a certain visibility
 
         # After filtering, you can also limit to first or last found article only:
 
         OnlyFirst              => 0,                  # optional, only return first match
         OnlyLast               => 0,                  # optional, only return last match
     );


Returns a list with base article data (no back end related data included):


.. code-block:: perl

     (
         {
             ArticleID              => 1,
             TicketID               => 2,
             ArticleNumber          => 1,                        # sequential number of article in the ticket
             CommunicationChannelID => 1,
             SenderTypeID           => 1,
             IsVisibleForCustomer   => 0,
             CreateBy               => 1,
             CreateTime             => '2017-03-01 00:00:00',
             ChangeBy               => 1,
             ChangeTime             => '2017-03-01 00:00:00',
         },
         { ... }
     )


Please note that you need to use `ArticleGet()` via the
article backend objects to access the full backend-specific article data hash for each article.


.. code-block:: perl

     for my $MetaArticle (@Articles) {
         my %Article = $ArticleObject->BackendForArticle( %{$MetaArticle} )->ArticleGet( %{$MetaArticle} );
     }



TicketIDLookup()
================


Get a ticket ID for supplied article ID.


.. code-block:: perl

     my $TicketID = $ArticleObject->TicketIDLookup(
         ArticleID => 123,   # required
     );


Returns ID of a ticket that article belongs to:


.. code-block:: perl

     $TicketID = 123;


NOTE: Usage of this lookup function is strongly discouraged, since its result is not cached.
Where possible, use \ ``ArticleList()``\  instead.


ArticleFlagSet()
================


Set article flags.


.. code-block:: perl

     my $Success = $ArticleObject->ArticleFlagSet(
         TicketID  => 123,
         ArticleID => 123,
         Key       => 'Seen',
         Value     => 1,
         UserID    => 123,
     );


Events:
    ArticleFlagSet


ArticleFlagDelete()
===================


Delete an article flag.


.. code-block:: perl

     my $Success = $ArticleObject->ArticleFlagDelete(
         TicketID  => 123,
         ArticleID => 123,
         Key       => 'seen',
         UserID    => 123,
     );
 
     my $Success = $ArticleObject->ArticleFlagDelete(
         TicketID  => 123,
         ArticleID => 123,
         Key       => 'seen',
         AllUsers  => 1,         # delete for all users
     );


Events:
    ArticleFlagDelete


ArticleFlagGet()
================


Get article flags.


.. code-block:: perl

     my %Flags = $ArticleObject->ArticleFlagGet(
         ArticleID => 123,
         UserID    => 123,
     );



ArticleFlagsOfTicketGet()
=========================


Get all article flags of a ticket.


.. code-block:: perl

     my %Flags = $ArticleObject->ArticleFlagsOfTicketGet(
         TicketID  => 123,
         UserID    => 123,
     );
 
     returns (
         123 => {                    # ArticleID
             'Seen'  => 1,
             'Other' => 'something',
         },
     )



ArticleAccountedTimeGet()
=========================


Returns the accounted time of a article.


.. code-block:: perl

     my $AccountedTime = $ArticleObject->ArticleAccountedTimeGet(
         ArticleID => $ArticleID,
     );



ArticleAccountedTimeDelete()
============================


Delete accounted time of an article.


.. code-block:: perl

     my $Success = $ArticleObject->ArticleAccountedTimeDelete(
         ArticleID => $ArticleID,
     );



ArticleColorGet()
=================


Get article color.


.. code-block:: perl

     my %ArticleColor = $ArticleObject->ArticleColorGet(
         Name => 'agent::Email::VisibleForCustomer',     # required, Name or ID of the article color
         # or
         ID => '1',                                      # required, ID or Name of the article color
     );


Returns:


.. code-block:: perl

     my %ArticleColor = (
         ID                   => 1,
         Name                 => 'agent::Email::VisibleForCustomer', # Name of the article color
         SenderType           => 'agent',                            # agent|customer|system
         CommunicationChannel => 'Email',                            # Email|Phone|Chat|...
         IsVisibleForCustomer => 'VisibleForCustomer',               # VisibleForCustomer|NotVisibleForCustomer
         Color                => '#D1E8D1',
         CreateTime           => '2017-03-01 00:00:00',
         CreateBy             => 1,
         ChangeTime           => '2017-03-01 00:00:00',
         ChangeBy             => 1,
     );



ArticleColorSet()
=================


Set article color.


.. code-block:: perl

     my $ArticleColorID = $ArticleObject->ArticleColorSet(
         Name                 => 'agent::Email::VisibleForCustomer',     # required, Name of the article color
         # or
         SenderType           => 'agent',                                # agent|customer|system
         CommunicationChannel => 'Email',                                # Email|Phone|Chat|...
         IsVisibleForCustomer => 1,                                      # 1|VisibleForCustomer or 0|NotVisibleForCustomer
 
         Color                => '#FFCCDD',                              # required, Color of the article
         UserID               => 1,
     );


Returns:


.. code-block:: perl

     my $ArticleColorID = 1;



ArticleColorInit()
==================


Add all article combination of sender types, communication channel and customer visibility to the database if they do not exist yet.


.. code-block:: perl

     my $Success = $ArticleObject->ArticleColorInit();


Returns:


.. code-block:: perl

     my $Success = 1;



ArticleColorList()
==================


List all article combination of sender types, communication channel and customer visibility.


.. code-block:: perl

     my @ArticleColorList = $ArticleObject->ArticleColorList();


Returns:


.. code-block:: perl

     my @ArticleColorList = (
         {
             Name                 => 'agent::Email::VisibleForCustomer',     # Name of the article color
             SenderType           => 'agent',                                # agent|customer|system
             CommunicationChannel => 'Email',                                # Email|Phone|Chat|...
             IsVisibleForCustomer => 'VisibleForCustomer',                   # VisibleForCustomer|NotVisibleForCustomer
         },
         {}
     )



ArticleSenderTypeList()
=======================


List all article sender types.


.. code-block:: perl

     my %ArticleSenderTypeList = $ArticleObject->ArticleSenderTypeList();


Returns:


.. code-block:: perl

     (
         1 => 'agent',
         2 => 'customer',
         3 => 'system',
     )



ArticleSenderTypeLookup()
=========================


Lookup an article sender type id or name.


.. code-block:: perl

     my $SenderTypeID = $ArticleObject->ArticleSenderTypeLookup(
         SenderType => 'customer', # customer|system|agent
     );
 
     my $SenderType = $ArticleObject->ArticleSenderTypeLookup(
         SenderTypeID => 1,
     );



ArticleSearchIndexRebuildFlagSet()
==================================


Set the article flags to indicate if the article search index needs to be rebuilt.


.. code-block:: perl

     my $Success = $ArticleObject->ArticleSearchIndexRebuildFlagSet(
         ArticleIDs => [ 123, 234, 345 ]   # (Either 'ArticleIDs' or 'All' must be provided) The ArticleIDs to be updated.
         All        => 1,                  # (Either 'ArticleIDs' or 'All' must be provided) Set all articles to $Value. Default: 0,
         Value      => 1, # 0/1 default 0
     );



ArticleSearchIndexRebuildFlagList()
===================================


Get a list of ArticleIDs and TicketIDs for a given flag (either needs rebuild or not)


.. code-block:: perl

     my %ArticleTicketIDs = $ArticleObject->ArticleSearchIndexRebuildFlagList(
         Value => 1,     # (optional) 0/1 default 0
         Limit => 10000, # (optional) default: 20000
     );


Returns:


.. code-block:: perl

     %ArticleIDs = (
         1 => 2, # ArticleID => TicketID
         3 => 4,
         5 => 6,
         # ...
     );



ArticleSearchIndexStatus()
==========================


gets an article indexing status hash.


.. code-block:: perl

     my %Status = $ArticleObject->ArticleSearchIndexStatus();


Returns:


.. code-block:: perl

     %Status = (
         ArticlesTotal      => 443,
         ArticlesIndexed    => 420,
         ArticlesNotIndexed =>  23,
     );



ArticleSearchIndexBuild()
=========================


Rebuilds the current article search index table content. Existing article entries will be replaced.


.. code-block:: perl

     my $Success = $ArticleObject->ArticleSearchIndexBuild(
         TicketID  => 123,
         ArticleID => 123,
         UserID    => 1,
     );


Returns:


.. code-block:: perl

     True if indexing process was successfully finished, False if not.



ArticleSearchIndexDelete()
==========================


Deletes entries from the article search index table base on supplied \ ``ArticleID``\  or \ ``TicketID``\ .


.. code-block:: perl

     my $Success = $ArticleObject->ArticleSearchIndexDelete(
         ArticleID => 123,   # required, deletes search index for single article
                             # or
         TicketID  => 123,   # required, deletes search index for all ticket articles
 
         UserID    => 1,     # required
     );


Returns:


.. code-block:: perl

     True if delete process was successfully finished, False if not.



ArticleSearchIndexSQLJoinNeeded()
=================================


Checks the given search parameters for used article backend fields.


.. code-block:: perl

     my $Needed = $ArticleObject->ArticleSearchIndexSQLJoinNeeded(
         SearchParams => {
             # ...
             ConditionInline         => 1,
             ContentSearchPrefix     => '*',
             ContentSearchSuffix     => '*',
             MIMEBase_From           => '%spam@example.com%',
             MIMEBase_To             => '%service@example.com%',
             MIMEBase_Cc             => '%client@example.com%',
             MIMEBase_Subject        => '%VIRUS 32%',
             MIMEBase_Body           => '%VIRUS 32%',
             MIMEBase_AttachmentName => '%anyfile.txt%',
             # ...
         },
     );


Returns:


.. code-block:: perl

     True if article search index usage is needed, False if not.



ArticleSearchIndexSQLJoin()
===========================


Generates SQL string extensions, including the needed table joins for the article index search.


.. code-block:: perl

     my $SQLExtenion = $ArticleObject->ArticleSearchIndexSQLJoin(
         SearchParams => {
             # ...
             ConditionInline         => 1,
             ContentSearchPrefix     => '*',
             ContentSearchSuffix     => '*',
             MIMEBase_From           => '%spam@example.com%',
             MIMEBase_To             => '%service@example.com%',
             MIMEBase_Cc             => '%client@example.com%',
             MIMEBase_Subject        => '%VIRUS 32%',
             MIMEBase_Body           => '%VIRUS 32%',
             MIMEBase_AttachmentName => '%anyfile.txt%',
             # ...
         },
     );


Returns:


.. code-block:: perl

     $SQLExtension = 'LEFT JOIN article_search_index ArticleFulltext ON art.id = ArticleFulltext.article_id ';



ArticleSearchIndexWhereCondition()
==================================


Generates SQL query conditions for the used article fields, that may be used in the WHERE clauses of main
SQL queries to the database.


.. code-block:: perl

     my $SQLExtenion = $ArticleObject->ArticleSearchIndexWhereCondition(
         SearchParams => {
             # ...
             ConditionInline         => 1,
             ContentSearchPrefix     => '*',
             ContentSearchSuffix     => '*',
             MIMEBase_From           => '%spam@example.com%',
             MIMEBase_To             => '%service@example.com%',
             MIMEBase_Cc             => '%client@example.com%',
             MIMEBase_Subject        => '%VIRUS 32%',
             MIMEBase_Body           => '%VIRUS 32%',
             MIMEBase_AttachmentName => '%anyfile.txt%',
             # ...
         },
     );


Returns:


.. code-block:: perl

     $SQLConditions = " AND (MIMEBase_From.article_value LIKE '%spam@example.com%') ";



SearchStringStopWordsFind()
===========================


Find stop words within given search string.


.. code-block:: perl

     my $StopWords = $ArticleObject->SearchStringStopWordsFind(
         SearchStrings => {
             'Fulltext'      => '(this AND is) OR test',
             'MIMEBase_From' => 'myself',
         },
     );
 
     Returns Hashref with found stop words.



SearchStringStopWordsUsageWarningActive()
=========================================


Checks if warnings for stop words in search strings are active or not.


.. code-block:: perl

     my $WarningActive = $ArticleObject->SearchStringStopWordsUsageWarningActive();



ArticleSearchableFieldsList()
=============================


Get list of searchable fields across all article backends.


.. code-block:: perl

     my %SearchableFields = $ArticleObject->ArticleSearchableFieldsList();


Returns:


.. code-block:: perl

     %SearchableFields = (
         'MIMEBase_Body' => {
             Filterable => 1,
             Key        => 'MIMEBase_Body',
             Label      => 'Body',
             Type       => 'Text',
         },
         'MIMEBase_Subject' => {
             Filterable => 1,
             Key        => 'MIMEBase_Subject',
             Label      => 'Subject',
             Type       => 'Text',
         },
         # ...
     );




*****************
PRIVATE FUNCTIONS
*****************


_MetaArticleList()
==================


Returns an array-hash with the meta articles of the current ticket.


.. code-block:: perl

     my @MetaArticles = $ArticleObject->_MetaArticleList(
         TicketID => 123,
     );


Returns:


.. code-block:: perl

     (
         {
             ArticleID              => 1,
             TicketID               => 2,
             ArticleNumber          => 1,                        # sequential number of article in the ticket
             CommunicationChannelID => 1,
             SenderTypeID           => 1,
             IsVisibleForCustomer   => 0,
             CreateBy               => 1,
             CreateTime             => '2017-03-01 00:00:00',
             ChangeBy               => 1,
             ChangeTime             => '2017-03-01 00:00:00',
         },
         { ... },
     )



_ArticleCacheClear()
====================


Removes all article caches related to specified ticket.


.. code-block:: perl

     my $Success = $ArticleObject->_ArticleCacheClear(
         TicketID => 123,
     );



ArticleIndex()
==============


returns an array with article IDs


.. code-block:: perl

     my @ArticleIDs = $ArticleObject->ArticleIndex(
         TicketID => 123,
     );
 
     my @ArticleIDs = $ArticleObject->ArticleIndex(
         SenderType => 'customer',                   # optional, to limit to a certain sender type
         TicketID   => 123,
     );



ArticleAttachmentIndex()
========================


returns an array with article IDs


.. code-block:: perl

     my %AttachmentIndex = $ArticleObject->ArticleAttachmentIndex(
         TicketID         => 123,
         ArticleID        => 123,
         ExcludePlainText => 1,       # (optional) Exclude plain text attachment
         ExcludeHTMLBody  => 1,       # (optional) Exclude HTML body attachment
         ExcludeInline    => 1,       # (optional) Exclude inline attachments
         OnlyHTMLBody     => 1,       # (optional) Return only HTML body attachment, return nothing if not found
     );


Returns:


.. code-block:: perl

     my %AttachmentIndex = (
         '1' => {
             'FilesizeRaw'        => '804764',
             'Disposition'        => 'attachment',
             'ContentType'        => 'image/jpeg',
             'ContentAlternative' => '',
             'Filename'           => 'blub.jpg',
             'ContentID'          => ''
         },
         # ...
     );



ArticleAttachment()
===================


Get article attachment from storage. This is a delegate method from active backend.


.. code-block:: perl

     my %Attachment = $ArticleObject->ArticleAttachment(
         TicketID  => 123,
         ArticleID => 123,
         FileID    => 1,   # as returned by ArticleAttachmentIndex
     );


Returns:


.. code-block:: perl

     %Attachment = (
         Content            => 'xxxx',     # actual attachment contents
         ContentAlternative => '',
         ContentID          => '',
         ContentType        => 'application/pdf',
         Filename           => 'StdAttachment-Test1.pdf',
         FilesizeRaw        => 4722,
         Disposition        => 'attachment',
     );



ArticleWriteAttachment()
========================


Write an article attachment to storage.


.. code-block:: perl

     my $Success = $ArticleBackendObject->ArticleWriteAttachment(
         TicketID           => 503,
         Content            => $ContentAsString,
         ContentType        => 'text/html; charset="iso-8859-15"',
         Filename           => 'lala.html',
         ContentID          => 'cid-1234',   # optional
         ContentAlternative => 0,            # optional, alternative content to shown as body
         Disposition        => 'attachment', # or 'inline'
         ArticleID          => 123,
         UserID             => 123,
     );



ArticleCount()
==============


Returns count of article.


.. code-block:: perl

     my $Count = $ArticleObject->ArticleCount(
         TicketID  => 123,
     );


Returns:


.. code-block:: perl

     my $Count = 1;



ArticleAttachmentCount()
========================


Returns count of article attachment.


.. code-block:: perl

     my $Count = $ArticleObject->ArticleAttachmentCount(
         TicketID  => 123,
         ArticleID => 123,
     );


Returns:


.. code-block:: perl

     my $Count = 1;



ArticleContentPathGet()
=======================


Get the stored content path of an article.


.. code-block:: perl

     my $Path = $BackendObject->ArticleContentPathGet(
         ArticleID => 123,
     );





