# -- # 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::System::FAQ::Category; use strict; use warnings; use Kernel::System::VariableCheck qw(:all); our @ObjectDependencies = ( 'Kernel::System::Cache', 'Kernel::System::CustomerGroup', 'Kernel::System::DB', 'Kernel::System::Group', 'Kernel::System::Log', 'Kernel::System::Valid' ); =head1 NAME Kernel::System::FAQ::Category - sub module of Kernel::System::FAQ =head1 DESCRIPTION All FAQ category functions. =head1 PUBLIC INTERFACE =head2 CategoryAdd() add a category my $CategoryID = $FAQObject->CategoryAdd( Name => 'CategoryA', Comment => 'Some comment', # Optional ParentID => 2, # Mandatory, but could be 0 ValidID => 1, UserID => 1, ); Returns: $CategoryID = 34; # or undef if category could not be added =cut sub CategoryAdd { my ( $Self, %Param ) = @_; for my $Argument (qw(Name UserID ValidID)) { if ( !$Param{$Argument} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Argument!", ); return; } } if ( !defined $Param{ParentID} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need ParentID!", ); return; } # check that ParentID is not an empty string but number 0 is allowed if ( $Param{ParentID} eq '' ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "ParentID cannot be empty!", ); return; } my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); # insert record return if !$DBObject->Do( SQL => ' INSERT INTO faq_category (name, parent_id, comments, valid_id, created, created_by, changed, changed_by) VALUES ( ?, ?, ?, ?, current_timestamp, ?, current_timestamp, ?)', Bind => [ \$Param{Name}, \$Param{ParentID}, \$Param{Comment}, \$Param{ValidID}, \$Param{UserID}, \$Param{UserID}, ], ); # get new category id return if !$DBObject->Prepare( SQL => ' SELECT id FROM faq_category WHERE name = ? AND parent_id = ?', Bind => [ \$Param{Name}, \$Param{ParentID} ], Limit => 1, ); my $CategoryID; while ( my @Row = $DBObject->FetchrowArray() ) { $CategoryID = $Row[0]; } # log notice $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'notice', Message => "FAQCategory: '$Param{Name}' CategoryID: '$CategoryID' " . "created successfully ($Param{UserID})!", ); return $CategoryID; } =head2 CategoryCount() Count the number of categories. my $CategoryCount = $FAQObject->CategoryCount( ParentIDs => [ 1, 2, 3, 4 ], UserID => 1, ); Returns: $CategoryCount = 6; =cut sub CategoryCount { my ( $Self, %Param ) = @_; if ( !$Param{UserID} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => 'Need UserID!', ); return; } if ( !defined $Param{ParentIDs} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => 'Need ParentIDs!', ); return; } # build SQL my $SQL = ' SELECT COUNT(*) FROM faq_category WHERE valid_id IN (' . join ', ', $Kernel::OM->Get('Kernel::System::Valid')->ValidIDsGet() . ')'; my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); # parent ids are given if ( defined $Param{ParentIDs} ) { # integer quote the parent ids for my $ParentID ( @{ $Param{ParentIDs} } ) { $ParentID = $DBObject->Quote( $ParentID, 'Integer' ); } # create string my $InString = join ', ', @{ $Param{ParentIDs} }; $SQL .= ' AND parent_id IN (' . $InString . ')'; } # add group by $SQL .= ' GROUP BY parent_id'; return if !$DBObject->Prepare( SQL => $SQL, Limit => 200, ); my $Count = 0; while ( my @Row = $DBObject->FetchrowArray() ) { $Count = $Row[0]; } return $Count; } =head2 CategoryDelete() Delete a category. my $DeleteSuccess = $FAQObject->CategoryDelete( CategoryID => 123, UserID => 1, ); Returns: DeleteSuccess = 1; # or undef if category could not be deleted =cut sub CategoryDelete { my ( $Self, %Param ) = @_; for my $Attribute (qw(CategoryID UserID)) { if ( !$Param{$Attribute} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Attribute!", ); return; } } my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); # delete the category return if !$DBObject->Do( SQL => ' DELETE FROM faq_category WHERE id = ?', Bind => [ \$Param{CategoryID} ], ); # delete the category groups return if !$DBObject->Do( SQL => ' DELETE FROM faq_category_group WHERE category_id = ?', Bind => [ \$Param{CategoryID} ], ); return 1; } =head2 CategoryDuplicateCheck() check a category for duplicate name under the same parent my $Exists = $FAQObject->CategoryDuplicateCheck( CategoryID => 1, Name => 'Some Name', ParentID => 1, UserID => 1, ); Returns: $Exists = 1; # if category name already exists with the same parent # or 0 if the name does not exists with the same parent =cut sub CategoryDuplicateCheck { my ( $Self, %Param ) = @_; if ( !$Param{UserID} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need UserID!", ); return; } # set defaults $Param{Name} //= ''; $Param{ParentID} ||= 0; my @Values; push @Values, \$Param{Name}; push @Values, \$Param{ParentID}; my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); # db quote $Param{ParentID} = $DBObject->Quote( $Param{ParentID}, 'Integer' ); # build SQL my $SQL = ' SELECT id FROM faq_category WHERE name = ? AND parent_id = ? '; if ( defined $Param{CategoryID} ) { $SQL .= " AND id != ?"; push @Values, \$Param{CategoryID}; } # prepare SQL statement return if !$DBObject->Prepare( SQL => $SQL, Bind => \@Values, Limit => 1, ); # fetch the result my $Exists; while ( my @Row = $DBObject->FetchrowArray() ) { $Exists = 1; } return $Exists; } =head2 CategoryGet() get a category as hash my %Category = $FAQObject->CategoryGet( CategoryID => 1, UserID => 1, ); Returns: %Category = (, CategoryID => 2, ParentID => 0, Name => 'My Category', Comment => 'This is my first category.', ValidID => 1, ); =cut sub CategoryGet { my ( $Self, %Param ) = @_; if ( !$Param{UserID} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need UserID!", ); return; } if ( !defined $Param{CategoryID} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => 'Need CategoryID!', ); return; } # check cache my $CacheKey = 'CategoryGet::' . $Param{CategoryID}; # get cache object my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); my $Cache = $CacheObject->Get( Type => 'FAQ', Key => $CacheKey, ); return %{$Cache} if $Cache; my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); # SQL return if !$DBObject->Prepare( SQL => ' SELECT id, parent_id, name, comments, valid_id FROM faq_category WHERE id = ?', Bind => [ \$Param{CategoryID} ], Limit => 1, ); my %Data; while ( my @Row = $DBObject->FetchrowArray() ) { %Data = ( CategoryID => $Row[0], ParentID => $Row[1], Name => $Row[2], Comment => $Row[3], ValidID => $Row[4], ); } # cache result $CacheObject->Set( Type => 'FAQ', Key => $CacheKey, Value => \%Data, TTL => 60 * 60 * 24 * 2, ); return %Data; } =head2 CategoryGroupGet() get groups of a category my $GroupArrayRef = $FAQObject->CategoryGroupGet( CategoryID => 3, UserID => 1, ); Returns: $GroupArrayRef = [ 2, 9, 10, ]; =cut sub CategoryGroupGet { my ( $Self, %Param ) = @_; for my $Argument (qw(CategoryID UserID)) { if ( !$Param{$Argument} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Argument!", ); return; } } my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); # get groups return if !$DBObject->Prepare( SQL => ' SELECT group_id FROM faq_category_group WHERE category_id = ?', Bind => [ \$Param{CategoryID} ], ); my @Groups; while ( my @Row = $DBObject->FetchrowArray() ) { push @Groups, $Row[0]; } return \@Groups; } =head2 CategoryGroupGetAll() get all category-groups my $AllCategoryGroupHashRef = $FAQObject->CategoryGroupGetAll( UserID => 1, ); Returns: $AllCategoryGroupHashRef = { 1 => { 2 => 1, }, 2 => { 2 => 1, 9 => 1, 10 => 1, }, 3 => { 2 => 1, 9 => 1, 10 => 1, }, 4 => { 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1, 9 => 1, 10 => 1, }, }; =cut sub CategoryGroupGetAll { my ( $Self, %Param ) = @_; if ( !$Param{UserID} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need UserID!", ); return; } # check cache if ( $Self->{Cache}->{CategoryGroupGetAll} ) { return $Self->{Cache}->{CategoryGroupGetAll}; } my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); # get groups return if !$DBObject->Prepare( SQL => ' SELECT group_id, category_id FROM faq_category_group', ); my %Groups; while ( my @Row = $DBObject->FetchrowArray() ) { $Groups{ $Row[1] }->{ $Row[0] } = 1; } # cache $Self->{Cache}->{CategoryGroupGetAll} = \%Groups; return \%Groups; } =head2 CategoryList() get the category list as hash my $CategoryHashRef = $FAQObject->CategoryList( Valid => 1, # (optional) UserID => 1, ); Returns: $CategoryHashRef = { 0 => { 1 => 'Misc', 2 => 'My Category', }, 2 => { 3 => 'Sub Category A', 4 => 'Sub Category B', }, }; =cut sub CategoryList { my ( $Self, %Param ) = @_; if ( !$Param{UserID} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need UserID!", ); return; } # set default my $Valid = 0; if ( defined $Param{Valid} ) { $Valid = $Param{Valid}; } # check cache if ( $Self->{Cache}->{CategoryList}->{$Valid} ) { return $Self->{Cache}->{CategoryList}->{$Valid}; } # build SQL my $SQL = ' SELECT id, parent_id, name FROM faq_category'; if ($Valid) { # get the valid ids $SQL .= ' WHERE valid_id IN (' . join ', ', $Kernel::OM->Get('Kernel::System::Valid')->ValidIDsGet() . ')'; } my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); # prepare SQL statement return if !$DBObject->Prepare( SQL => $SQL ); # fetch the result my %Data; while ( my @Row = $DBObject->FetchrowArray() ) { $Data{ $Row[1] }->{ $Row[0] } = $Row[2]; } # cache $Self->{Cache}->{CategoryList}->{$Valid} = \%Data; return \%Data; } =head2 CategorySearch() get the category search as an array ref my $CategoryIDArrayRef = $FAQObject->CategorySearch( Name => 'Test', ParentID => 3, ParentIDs => [ 1, 3, 8 ], CategoryIDs => [ 2, 5, 7 ], OrderBy => 'Name', SortBy => 'down', UserID => 1, ); Returns: $CategoryIDArrayRef = [ 2, ]; =cut sub CategorySearch { my ( $Self, %Param ) = @_; if ( !$Param{UserID} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need UserID!", ); return; } # SQL my $SQL = ' SELECT id FROM faq_category WHERE valid_id IN (' . join ', ', $Kernel::OM->Get('Kernel::System::Valid')->ValidIDsGet() . ')'; my $Ext = ''; my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); # search for name if ( defined $Param{Name} ) { # db like quote $Param{Name} = $DBObject->Quote( $Param{Name}, 'Like' ); $Ext .= " AND name LIKE '%" . $Param{Name} . "%' $Self->{LikeEscapeString}"; } # search for parent id elsif ( defined $Param{ParentID} ) { # db integer quote $Param{ParentID} = $DBObject->Quote( $Param{ParentID}, 'Integer' ); $Ext .= ' AND parent_id = ' . $Param{ParentID}; } # search for parent ids elsif ( defined $Param{ParentIDs} && ref $Param{ParentIDs} eq 'ARRAY' && @{ $Param{ParentIDs} } ) { # integer quote the parent ids for my $ParentID ( @{ $Param{ParentIDs} } ) { $ParentID = $DBObject->Quote( $ParentID, 'Integer' ); } # create string my $InString = join ', ', @{ $Param{ParentIDs} }; $Ext = ' AND parent_id IN (' . $InString . ')'; } # search for category ids elsif ( defined $Param{CategoryIDs} && ref $Param{CategoryIDs} eq 'ARRAY' && @{ $Param{CategoryIDs} } ) { # integer quote the category ids for my $CategoryID ( @{ $Param{CategoryIDs} } ) { $CategoryID = $DBObject->Quote( $CategoryID, 'Integer' ); } # create string my $InString = join ', ', @{ $Param{CategoryIDs} }; $Ext = ' AND id IN (' . $InString . ')'; } # ORDER BY if ( $Param{OrderBy} ) { $Ext .= " ORDER BY name"; # set the default sort order $Param{SortBy} ||= 'up'; # SORT if ( $Param{SortBy} ) { if ( $Param{SortBy} eq 'up' ) { $Ext .= " ASC"; } elsif ( $Param{SortBy} eq 'down' ) { $Ext .= " DESC"; } } } # SQL STATEMENT $SQL .= $Ext; return if !$DBObject->Prepare( SQL => $SQL, Limit => 500, ); my @List; while ( my @Row = $DBObject->FetchrowArray() ) { push @List, $Row[0]; } return \@List; } =head2 CategorySubCategoryIDList() get all subcategory ids of a category my $SubCategoryIDArrayRef = $FAQObject->CategorySubCategoryIDList( ParentID => 1, Mode => 'Public', # (Agent, Customer, Public) CustomerUser => 'tt', UserID => 1, ); Returns: $SubCategoryIDArrayRef = [ 3, 4, 5, 6, ]; =cut sub CategorySubCategoryIDList { my ( $Self, %Param ) = @_; if ( !$Param{UserID} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need UserID!", ); return; } if ( !defined $Param{ParentID} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => 'Need ParentID!', ); return; } my $Categories = {}; if ( $Param{Mode} && $Param{Mode} eq 'Agent' ) { # get agents categories $Categories = $Self->GetUserCategories( Type => 'ro', UserID => $Param{UserID}, ); } elsif ( $Param{Mode} && $Param{Mode} eq 'Customer' ) { # get customer categories $Categories = $Self->GetCustomerCategories( Type => 'ro', CustomerUser => $Param{CustomerUser}, UserID => $Param{UserID}, ); } else { # get all categories $Categories = $Self->CategoryList( Valid => 1, UserID => $Param{UserID}, ); } my @SubCategoryIDs; my @TempSubCategoryIDs = keys %{ $Categories->{ $Param{ParentID} } }; SUBCATEGORYID: while (@TempSubCategoryIDs) { # get next subcategory id my $SubCategoryID = shift @TempSubCategoryIDs; # add to result push @SubCategoryIDs, $SubCategoryID; # check if subcategory has own subcategories next SUBCATEGORYID if !$Categories->{$SubCategoryID}; # add new subcategories push @TempSubCategoryIDs, keys %{ $Categories->{$SubCategoryID} }; } # sort subcategories numerically @SubCategoryIDs = sort { $a <=> $b } @SubCategoryIDs; return \@SubCategoryIDs; } =head2 CategoryTreeList() get all categories as tree (with their long names) my $CategoryTree = $FAQObject->CategoryTreeList( Valid => 0, # (0|1, optional) UserID => 1, ); Returns: $CategoryTree = { 1 => 'Misc', 2 => 'My Category', 3 => 'My Category::Sub Category A', 4 => 'My Category::Sub Category B', }; =cut sub CategoryTreeList { my ( $Self, %Param ) = @_; if ( !$Param{UserID} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need UserID!", ); return; } # set default my $Valid = 0; if ( $Param{Valid} ) { $Valid = $Param{Valid}; } # check cache if ( $Self->{Cache}->{GetCategoryTree}->{$Valid} ) { return $Self->{Cache}->{GetCategoryTree}->{$Valid}; } # build SQL my $SQL = ' SELECT id, parent_id, name FROM faq_category'; # add where clause for valid categories if ($Valid) { $SQL .= ' WHERE valid_id IN (' . join ', ', $Kernel::OM->Get('Kernel::System::Valid')->ValidIDsGet() . ')'; } my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); # prepare SQL return if !$DBObject->Prepare( SQL => $SQL, ); # fetch result my %CategoryMap; my %CategoryNameLookup; my %ParentIDLookup; while ( my @Row = $DBObject->FetchrowArray() ) { $CategoryMap{ $Row[1] }->{ $Row[0] } = $Row[2]; $CategoryNameLookup{ $Row[0] } = $Row[2]; $ParentIDLookup{ $Row[0] } = $Row[1]; } # to store the category tree my %CategoryTree; # check all parent IDs for my $ParentID ( sort { $a <=> $b } keys %CategoryMap ) { # get subcategories and names for this parent id while ( my ( $CategoryID, $CategoryName ) = each %{ $CategoryMap{$ParentID} } ) { # lookup the parents name my $NewParentID = $ParentID; while ($NewParentID) { # pre-append parents category name if ( $CategoryNameLookup{$NewParentID} ) { $CategoryName = $CategoryNameLookup{$NewParentID} . '::' . $CategoryName; } # get up one parent level $NewParentID = $ParentIDLookup{$NewParentID} || 0; } # add category to tree $CategoryTree{$CategoryID} = $CategoryName; } } # cache $Self->{Cache}->{GetCategoryTree}->{$Valid} = \%CategoryTree; return \%CategoryTree; } =head2 CategoryUpdate() update a category my $Success = $FAQObject->CategoryUpdate( CategoryID => 2, ParentID => 1, Name => 'Some Category', Comment => 'some comment', UserID => 1, ); Returns: $Success = 1; # or undef if category could not be updated =cut sub CategoryUpdate { my ( $Self, %Param ) = @_; for my $Argument (qw(Name UserID)) { if ( !$Param{$Argument} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Argument!", ); return; } } for my $Argument (qw(CategoryID ParentID)) { if ( !defined $Param{$Argument} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Argument!", ); return; } } # check that ParentID is not an empty string but number 0 is allowed if ( $Param{ParentID} eq '' ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "ParentID cannot be empty!", ); return; } my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); # SQL return if !$DBObject->Do( SQL => ' UPDATE faq_category SET parent_id = ?, name = ?, comments = ?, valid_id = ?, changed = current_timestamp, changed_by = ? WHERE id = ?', Bind => [ \$Param{ParentID}, \$Param{Name}, \$Param{Comment}, \$Param{ValidID}, \$Param{UserID}, \$Param{CategoryID}, ], ); # log notice $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'notice', Message => "FAQCategory: '$Param{Name}' " . "ID: '$Param{CategoryID}' updated successfully ($Param{UserID})!", ); # delete all cache, as FAQGet() will be also affected. $Kernel::OM->Get('Kernel::System::Cache')->CleanUp( Type => 'FAQ', ); return 1; } =head2 AgentCategorySearch() get the category search as array ref my $CategoryIDArrayRef = $FAQObject->AgentCategorySearch( ParentID => 3, # (optional, default 0) GetSubCategories => 1, # (optional, default 0) UserID => 1, ); Returns: $CategoryIDArrayRef = [ '4', '8', ]; =cut sub AgentCategorySearch { my ( $Self, %Param ) = @_; if ( !$Param{UserID} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => 'Need UserID!', ); return; } # set default parent id my $ParentID = $Param{ParentID} ? $Param{ParentID} : 0; my $Categories = $Self->GetUserCategories( Type => 'ro', UserID => $Param{UserID}, ); return [] if !IsHashRefWithData($Categories); my %Category = %{ $Categories->{$ParentID} }; my @CategoryIDs = sort { $Category{$a} cmp $Category{$b} } ( keys %Category ); return \@CategoryIDs if !$Param{GetSubCategories}; # Check if some IDs have a subcategory and add this also to the list. for my $CategoryID (@CategoryIDs) { # get all subcategory ids for this category my $SubCategoryIDs = $Self->CategorySubCategoryIDList( ParentID => $CategoryID, Mode => 'Agent', UserID => $Param{UserID}, ); # Add the sub categories to the category ids. push @CategoryIDs, @{$SubCategoryIDs}; } # Remove any duplicate IDs from the lest my %Seen; @CategoryIDs = grep { !$Seen{$_}++ } @CategoryIDs; return \@CategoryIDs; } =head2 CustomerCategorySearch() get the category search as hash my $CategoryIDArrayRef = @{$FAQObject->CustomerCategorySearch( CustomerUser => 'tt', ParentID => 3, # (optional, default 0) GetSubCategories => 1, # (optional, default 0) Mode => 'Customer', UserID => 1, )}; Returns: $CategoryIDArrayRef = [ '4', '8', ]; =cut sub CustomerCategorySearch { my ( $Self, %Param ) = @_; for my $Argument (qw(CustomerUser Mode UserID)) { if ( !$Param{$Argument} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Argument!", ); return; } } # set default parent id my $ParentID = $Param{ParentID} ? $Param{ParentID} : 0; my $Categories = $Self->GetCustomerCategories( CustomerUser => $Param{CustomerUser}, Type => 'ro', UserID => $Param{UserID}, ); my %Category = %{ $Categories->{$ParentID} }; my @CategoryIDs = sort { $Category{$a} cmp $Category{$b} } ( keys %Category ); my @AllowedCategoryIDs; my %Articles; # check cache my $CacheKey = 'CustomerCategorySearch::Articles'; if ( $Self->{Cache}->{$CacheKey} ) { %Articles = %{ $Self->{Cache}->{$CacheKey} }; } else { # build valid id string my $ValidIDsString = join ', ', $Kernel::OM->Get('Kernel::System::Valid')->ValidIDsGet(); my $SQL = " SELECT faq_item.id, faq_item.category_id FROM faq_item, faq_state_type, faq_state WHERE faq_state.id = faq_item.state_id AND faq_state.type_id = faq_state_type.id AND faq_state_type.name != 'internal' AND faq_item.valid_id IN ($ValidIDsString) AND faq_item.approved = 1"; my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); return if !$DBObject->Prepare( SQL => $SQL, ); while ( my @Row = $DBObject->FetchrowArray() ) { $Articles{ $Row[1] }++; } # cache $Self->{Cache}->{$CacheKey} = \%Articles; } return \@CategoryIDs if !$Param{GetSubCategories}; for my $CategoryID (@CategoryIDs) { # get all subcategory ids for this category my $SubCategoryIDs = $Self->CategorySubCategoryIDList( ParentID => $CategoryID, Mode => $Param{Mode}, CustomerUser => $Param{CustomerUser}, UserID => $Param{UserID}, ); # add this category id my @IDs = ( $CategoryID, @{$SubCategoryIDs} ); # check if category contains articles with state external or public ID: for my $ID (@IDs) { next ID if !$Articles{$ID}; push @AllowedCategoryIDs, $ID; } } return \@AllowedCategoryIDs; } =head2 PublicCategorySearch() get the category search as hash my $CategoryIDArrayRef = $FAQObject->PublicCategorySearch( ParentID => 3, # (optional, default 0) Mode => 'Public', UserID => 1, ); Returns: $CategoryIDArrayRef = [ '4', '8', ]; =cut sub PublicCategorySearch { my ( $Self, %Param ) = @_; for my $Argument (qw(Mode UserID)) { if ( !$Param{$Argument} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Argument!", ); return; } } if ( !defined $Param{ParentID} ) { $Param{ParentID} = 0; } my $CategoryListCategories = $Self->CategoryList( Valid => 1, UserID => $Param{UserID}, ); return [] if !$CategoryListCategories->{ $Param{ParentID} }; my %Category = %{ $CategoryListCategories->{ $Param{ParentID} } }; my @CategoryIDs = sort { $Category{$a} cmp $Category{$b} } ( keys %Category ); my @AllowedCategoryIDs; # build valid id string my $ValidIDsString = join ', ', $Kernel::OM->Get('Kernel::System::Valid')->ValidIDsGet(); my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); for my $CategoryID (@CategoryIDs) { # get all subcategory ids for this category my $SubCategoryIDs = $Self->CategorySubCategoryIDList( ParentID => $CategoryID, Mode => $Param{Mode}, CustomerUser => $Param{CustomerUser}, UserID => $Param{UserID}, ); # add this category id my @IDs = ( $CategoryID, @{$SubCategoryIDs} ); # check if category contains articles with state public my $FoundArticle = 0; my $SQL = " SELECT faq_item.id FROM faq_item, faq_state_type, faq_state WHERE faq_item.category_id = ? AND faq_item.valid_id IN ($ValidIDsString) AND faq_state.id = faq_item.state_id AND faq_state.type_id = faq_state_type.id AND faq_state_type.name = 'public' AND faq_item.approved = 1"; ID: for my $ID (@IDs) { return if !$DBObject->Prepare( SQL => $SQL, Bind => [ \$ID ], Limit => 1, ); while ( my @Row = $DBObject->FetchrowArray() ) { $FoundArticle = $Row[0]; } last ID if $FoundArticle; } # an article was found if ($FoundArticle) { push @AllowedCategoryIDs, $CategoryID; } } return \@AllowedCategoryIDs; } =head2 GetUserCategories() get user category-groups my $UserCategoryGroupHashRef = $FAQObject->GetUserCategories( Type => 'rw', UserID => 1, ); Returns: $UserCategoryGroupHashRef = { 1 => {}, 0 => { 1 => 'Misc', 2 => 'My Category', }, 2 => { 3 => 'Sub Category A', 4 => 'Sub Category B', }, 3 => {}, 4 => {}, }; =cut sub GetUserCategories { my ( $Self, %Param ) = @_; for my $Argument (qw(Type UserID)) { if ( !$Param{$Argument} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Argument!", ); return; } } my $Categories = $Self->CategoryList( Valid => 1, UserID => $Param{UserID}, ); my $CategoryGroups = $Self->CategoryGroupGetAll( UserID => $Param{UserID}, ); my %UserGroups = $Kernel::OM->Get('Kernel::System::Group')->GroupMemberList( UserID => $Param{UserID}, Type => $Param{Type}, Result => 'HASH', ); my $UserCategories = $Self->_UserCategories( Categories => $Categories, CategoryGroups => $CategoryGroups, UserGroups => \%UserGroups, UserID => $Param{UserID}, ); return $UserCategories; } =head2 GetUserCategoriesLongNames() get user category-groups (show category long names) my $UserCategoryGroupHashRef = $FAQObject->GetUserCategoriesLongNames( Type => 'rw', UserID => 1, ); Returns: $UserCategoryGroupHashRef = { 1 => 'Misc', 2 => 'My Category', 3 => 'My Category::Sub Category A', 4 => 'My Category::Sub Category A', }; =cut sub GetUserCategoriesLongNames { my ( $Self, %Param ) = @_; for my $Argument (qw(Type UserID)) { if ( !$Param{$Argument} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Argument!", ); return; } } # get categories where user has rights my $UserCategories = $Self->GetUserCategories( Type => $Param{Type}, UserID => $Param{UserID}, ); # get all categories with their long names my $CategoryTree = $Self->CategoryTreeList( Valid => 1, UserID => $Param{UserID}, ); # to store the user categories with their long names my %UserCategoriesLongNames; # get the long names of the categories where user has rights PARENTID: for my $ParentID ( sort keys %{$UserCategories} ) { next PARENTID if !$UserCategories->{$ParentID}; next PARENTID if ref $UserCategories->{$ParentID} ne 'HASH'; next PARENTID if !%{ $UserCategories->{$ParentID} }; for my $CategoryID ( sort keys %{ $UserCategories->{$ParentID} } ) { $UserCategoriesLongNames{$CategoryID} = $CategoryTree->{$CategoryID}; } } return \%UserCategoriesLongNames; } =head2 GetCustomerCategories() get customer user categories my $CustomerUserCategoryHashRef = $FAQObject->GetCustomerCategories( CustomerUser => 'hans', Type => 'rw', UserID => 1, ); Returns: $CustomerUserCategoryHashRef = { 1 => {}, 0 => { 1 => 'Misc', 2 => 'My Category', }, 2 => { 3 => 'Sub Category A', 4 => 'Sub Category B', }, 3 => {}, 4 => {}, }; =cut sub GetCustomerCategories { my ( $Self, %Param ) = @_; for my $Argument (qw(CustomerUser Type UserID)) { if ( !$Param{$Argument} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Argument!", ); return; } } # check cache my $CacheKey = 'GetCustomerCategories::CustomerUser::' . $Param{CustomerUser}; if ( defined $Self->{Cache}->{$CacheKey} ) { return $Self->{Cache}->{$CacheKey}; } # get all valid categories my $Categories = $Self->CategoryList( Valid => 1, UserID => $Param{UserID}, ); my $CategoryGroups = $Self->CategoryGroupGetAll( UserID => $Param{UserID}, ); my %UserGroups = $Kernel::OM->Get('Kernel::System::CustomerGroup')->GroupMemberList( UserID => $Param{CustomerUser}, Type => 'ro', Result => 'HASH', ); my $CustomerCategories = $Self->_UserCategories( Categories => $Categories, CategoryGroups => $CategoryGroups, UserGroups => \%UserGroups, UserID => $Param{UserID}, ); # cache $Self->{Cache}->{$CacheKey} = $CustomerCategories; return $CustomerCategories; } =head2 GetCustomerCategoriesLongNames() get customer category-groups (show category long names) my $CustomerCategoryGroupHashRef = $FAQObject->GetCustomerCategoriesLongNames( CustomerUser => 'hans', Type => 'rw', UserID => 1, ); Returns: $CustomerCategoryGroupHashRef = { 1 => 'Misc', 2 => 'My Category', 3 => 'My Category::Sub Category A', 4 => 'My Category::Sub Category A', }; =cut sub GetCustomerCategoriesLongNames { my ( $Self, %Param ) = @_; for my $Argument (qw(CustomerUser Type UserID)) { if ( !$Param{$Argument} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Argument!", ); return; } } # get categories where user has rights my $CustomerCategories = $Self->GetCustomerCategories( CustomerUser => $Param{CustomerUser}, Type => $Param{Type}, UserID => $Param{UserID}, ); # extract category ids my %AllCategoryIDs; for my $ParentID ( sort keys %{$CustomerCategories} ) { for my $CategoryID ( sort keys %{ $CustomerCategories->{$ParentID} } ) { $AllCategoryIDs{$CategoryID} = 1; } } # get all customer category ids my @CustomerCategoryIDs; for my $CategoryID ( 0, keys %AllCategoryIDs ) { push @CustomerCategoryIDs, @{ $Self->CustomerCategorySearch( ParentID => $CategoryID, CustomerUser => $Param{CustomerUser}, Mode => 'Customer', UserID => $Param{UserID}, ) }; } # build customer category hash $CustomerCategories = {}; for my $CategoryID (@CustomerCategoryIDs) { my %Category = $Self->CategoryGet( CategoryID => $CategoryID, UserID => $Param{UserID}, ); $CustomerCategories->{ $Category{ParentID} }->{ $Category{CategoryID} } = $Category{Name}; } # get all categories with their long names my $CategoryTree = $Self->CategoryTreeList( Valid => 1, UserID => $Param{UserID}, ); # to store the user categories with their long names my %CustomerCategoriesLongNames; # get the long names of the categories where user has rights PARENTID: for my $ParentID ( sort keys %{$CustomerCategories} ) { next PARENTID if !$CustomerCategories->{$ParentID}; next PARENTID if ref $CustomerCategories->{$ParentID} ne 'HASH'; next PARENTID if !%{ $CustomerCategories->{$ParentID} }; for my $CategoryID ( sort keys %{ $CustomerCategories->{$ParentID} } ) { $CustomerCategoriesLongNames{$CategoryID} = $CategoryTree->{$CategoryID}; } } return \%CustomerCategoriesLongNames; } =head2 GetPublicCategoriesLongNames() get public category-groups (show category long names) my $PublicCategoryGroupHashRef = $FAQObject->GetPublicCategoriesLongNames( Type => 'rw', UserID => 1, ); Returns: $PublicCategoryGroupHashRef = { 1 => 'Misc', 2 => 'My Category', 3 => 'My Category::Sub Category A', 4 => 'My Category::Sub Category A', }; =cut sub GetPublicCategoriesLongNames { my ( $Self, %Param ) = @_; for my $Argument (qw(Type UserID)) { if ( !$Param{$Argument} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Argument!", ); return; } } # get all categories my $PublicCategories = $Self->CategoryList( UserID => $Param{UserID} ); # extract category ids my %AllCategoryIDs; for my $ParentID ( sort keys %{$PublicCategories} ) { for my $CategoryID ( sort keys %{ $PublicCategories->{$ParentID} } ) { $AllCategoryIDs{$CategoryID} = 1; } } # get all public category ids my @PublicCategoryIDs; for my $CategoryID ( 0, keys %AllCategoryIDs ) { push @PublicCategoryIDs, @{ $Self->PublicCategorySearch( ParentID => $CategoryID, Mode => 'Public', UserID => $Param{UserID}, ) }; } # build public category hash $PublicCategories = {}; for my $CategoryID (@PublicCategoryIDs) { my %Category = $Self->CategoryGet( CategoryID => $CategoryID, UserID => $Param{UserID}, ); $PublicCategories->{ $Category{ParentID} }->{ $Category{CategoryID} } = $Category{Name}; } # get all categories with their long names my $CategoryTree = $Self->CategoryTreeList( Valid => 1, UserID => $Param{UserID}, ); # to store the user categories with their long names my %PublicCategoriesLongNames; # get the long names of the categories where user has rights PARENTID: for my $ParentID ( sort keys %{$PublicCategories} ) { next PARENTID if !$PublicCategories->{$ParentID}; next PARENTID if ref $PublicCategories->{$ParentID} ne 'HASH'; next PARENTID if !%{ $PublicCategories->{$ParentID} }; for my $CategoryID ( sort keys %{ $PublicCategories->{$ParentID} } ) { $PublicCategoriesLongNames{$CategoryID} = $CategoryTree->{$CategoryID}; } } return \%PublicCategoriesLongNames; } =head2 CheckCategoryUserPermission() get user permission for a category my $PermissionString = $FAQObject->CheckCategoryUserPermission( CategoryID => '123', Type => 'rw', # (optional) rw or ro, default ro UserID => 1, ); Returns: $PermissionString = 'rw'; # or 'ro' or '' =cut sub CheckCategoryUserPermission { my ( $Self, %Param ) = @_; for my $Argument (qw(CategoryID UserID)) { if ( !$Param{$Argument} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Argument!", ); return; } } if ( !$Param{Type} ) { $Param{Type} = 'ro'; } $Param{Type} = lc $Param{Type}; if ( $Param{Type} ne 'rw' && $Param{Type} ne 'ro' ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Type is invalid!", ); } my $UserCategories = $Self->GetUserCategories( Type => $Param{Type}, UserID => $Param{UserID}, ); for my $ParentID ( sort keys %{$UserCategories} ) { my $Categories = $UserCategories->{$ParentID}; for my $CategoryID ( sort keys %{$Categories} ) { if ( $CategoryID == $Param{CategoryID} ) { return $Param{Type}; } } } return ''; } =head2 CheckCategoryCustomerPermission() get customer user permission for a category my $PermissionString $FAQObject->CheckCategoryCustomerPermission( CustomerUser => 'mm', CategoryID => '123', UserID => 1, ); Returns: $PermissionString = 'rw'; # or 'ro' or '' =cut sub CheckCategoryCustomerPermission { my ( $Self, %Param ) = @_; for my $Argument (qw(CustomerUser CategoryID UserID)) { if ( !$Param{$Argument} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Argument!", ); return; } } for my $Permission (qw(rw ro)) { my $CustomerCategories = $Self->GetCustomerCategories( CustomerUser => $Param{CustomerUser}, Type => 'ro', UserID => $Param{UserID}, ); for my $ParentID ( sort keys %{$CustomerCategories} ) { my $Categories = $CustomerCategories->{$ParentID}; for my $CategoryID ( sort keys %{$Categories} ) { if ( $CategoryID == $Param{CategoryID} ) { return $Permission; } } } } return ''; } =head2 SetCategoryGroup() set groups to a category my $Success = $FAQObject->SetCategoryGroup( CategoryID => 3, GroupIDs => [ 2,4,1,5,77 ], UserID => 1, ); Returns: $Success = 1; # or undef if groups could not be set to a category =cut sub SetCategoryGroup { my ( $Self, %Param ) = @_; for my $Argument (qw(CategoryID GroupIDs UserID)) { if ( !$Param{$Argument} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Argument!", ); return; } } my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); # delete old groups return if !$DBObject->Do( SQL => ' DELETE FROM faq_category_group WHERE category_id = ?', Bind => [ \$Param{CategoryID} ], ); # insert groups $Param{CategoryID} = $DBObject->Quote( $Param{CategoryID}, 'Integer' ); for my $GroupID ( @{ $Param{GroupIDs} } ) { # db quote $GroupID = $DBObject->Quote( $GroupID, 'Integer' ); my $SQL = " INSERT INTO faq_category_group (category_id, group_id, changed, changed_by, created, created_by) VALUES ($Param{CategoryID}, $GroupID, current_timestamp, $Param{UserID}, current_timestamp, $Param{UserID})"; # write attachment to db return if !$DBObject->Do( SQL => $SQL ); } return 1; } =head1 PRIVATE FUNCTIONS =head2 _UserCategories() reduces the categories ( from CategoryList() ) to only the ones where the user has privileges. my $UserCategories = $FAQObject->_UserCategories( Categories => $CategoryHashRef, # as returned form CategoryList() CategoryGroups => $CategoryGroupHashRef, # as returned from CategoryGroupGetAll UserGroups => $UserGroupsHashRef, UserID => 123, ); Returns: $UserCategoies = { 0 => { 1 => 'Misc', 2 => 'My Category', }, 2 => { 3 => 'Sub Category A', }, }; =cut sub _UserCategories { my ( $Self, %Param ) = @_; for my $Argument (qw(Categories UserID)) { if ( !$Param{$Argument} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $Argument!", ); return; } } my %UserCategories; PARENTID: for my $ParentID ( sort { $a <=> $b } keys %{ $Param{Categories} } ) { my %SubCategories; CATEGORYID: for my $CategoryID ( sort keys %{ $Param{Categories}->{$ParentID} } ) { # check category groups next CATEGORYID if !defined $Param{CategoryGroups}->{$CategoryID}; # check user groups GROUPID: for my $GroupID ( sort keys %{ $Param{CategoryGroups}->{$CategoryID} } ) { next GROUPID if !defined $Param{UserGroups}->{$GroupID}; # add category $SubCategories{$CategoryID} = $Param{Categories}->{$ParentID}->{$CategoryID}; # add empty hash if category has no subcategories if ( !$UserCategories{$CategoryID} ) { $UserCategories{$CategoryID} = {}; } last GROUPID; } } $UserCategories{$ParentID} = \%SubCategories; } return \%UserCategories; } 1; =head1 TERMS AND CONDITIONS This software is part of the OTRS project (L). 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. =cut