#!/usr/bin/perl
# ----------------------------------------------------------------------------
# This software is Copyright (C) 2007, All Rights Reserved
# ----------------------------------------------------------------------------
# This program is protected by local and international copyright laws. Any
# use of this program is subject to the the terms of the license agreement
# included as part of this distribution archive. Any other uses are strictly
# prohibited without the written permission of the Vendor and all
# other rights are reserved.
# -----------------------------------------------------------------------------
# Warning : Modifying this program violates your license agreement!
# -----------------------------------------------------------------------------

package IT::ArtMan::CustomMenus::Publish::ArticleLists;

# load modules
use strict;

use IT::Globals;

# ------------------------------------------------------------------------------
# Function    : publish
# Description :
# Usage       : &IT::ArtMan::CustomMenus::Publish::ArticleLists::publish($rule, $publishUpdatedOnly, $stepRef, $subStepRef);
# ------------------------------------------------------------------------------

sub publish {
  my $rule               = shift || die "no rule specified!\n";
  my $publishUpdatedOnly = shift;
  my $stepRef            = shift || die "no currentStepRef defined!\n";
  my $subStepRef         = shift || die "no currentSubStepRef defined!\n";

  # get publish rule type
  my $listType       = $rule->{'articleListPublishGlobalOrCategoryIndexes'};
  my $isStepComplete = 0;
  if    ($listType eq 'global')   { $isStepComplete = &publishGlobalIndex($rule, $publishUpdatedOnly, $stepRef, $subStepRef); }
  elsif ($listType eq 'category') { $isStepComplete = &publishCategoryIndexes($rule, $publishUpdatedOnly, $stepRef, $subStepRef); }
  else { die "articleListPublishGlobalOrCategoryIndexes must be 'global' or 'category' not '$listType'!"; }

  #
  return $isStepComplete;
}

# ------------------------------------------------------------------------------
# Function    : publishGlobalIndex
# Description :
# Usage       : &publishGlobalIndex()
# ------------------------------------------------------------------------------

sub publishGlobalIndex {
  my $rule               = shift || die "no rule specified!\n";
  my $publishUpdatedOnly = shift;
  my $stepRef            = shift || die "no currentStepRef defined!\n";
  my $subStepRef         = shift || die "no currentSubStepRef defined!\n";

  # error checking
  if ($rule->{'articleListPublishGlobalOrCategoryIndexes'} ne 'global') { die "articleListPublishGlobalOrCategoryIndexes must be 'global'!"; }

  # get included categories
  my $selectedCategoryNums   = &IT::ArtMan::CustomMenus::Admin::PublishRules::getSelectedCategoryNumsForPublishRule($rule);

  # does rule need publishing?
  my $ruleNeedsPublish    = 0;
  my $publishAll          = !$publishUpdatedOnly;
  my $updatedCategoryNums = &IT::ArtMan::CustomMenus::Publish::getCategoryNumsWithPublishFlag($selectedCategoryNums, 'any');
  if ($publishAll || $rule->{'alwaysPublish'}) { $ruleNeedsPublish = 1; }
  else                                         { $ruleNeedsPublish = (@{$updatedCategoryNums} > 0); }

  # display status message
  if ($ruleNeedsPublish) { &IT::MultiStepOperations::addStatusMessage("Publishing: $rule->{'name'} (global $rule->{'type'})\n"); }
  else                   { &IT::MultiStepOperations::addStatusMessage("No updates for: $rule->{'name'} (global $rule->{'type'})\n"); }

  # publish list
  if ($ruleNeedsPublish) {
    my $currentArticle  = {};
    my $currentCategory = {};
    my $html            = &getHtmlForList({
                            'template'             => $rule->{'template'},
                            'categoryNums'         => $selectedCategoryNums,
                            'includeChildArticles' => 0,  # not applicable for global index
                            'sortBy'               => &_getArticleListSortArrayFromRule($rule),
                            'limit'                => &_getArticleListLimitArrayFromRule($rule),
                            'where'                => '',
                            'pageNumber'           => 1,
                            'ruleNumber'           => $rule->{'num'},
                           });
    my $filepath        = &IT::ArtMan::CustomMenus::Publish::getFilePathAndCreateDirFor($rule, $currentCategory, $currentArticle);
    &IT::File::writeFileFast($filepath, $html);
  }

  #
  my $isStageComplete = 1;
  return $isStageComplete;

}

# ------------------------------------------------------------------------------
# Function    :
# Description :
# Usage       :
# ------------------------------------------------------------------------------

sub publishCategoryIndexes {
  my $rule               = shift || die "no rule specified!\n";
  my $publishUpdatedOnly = shift;
  my $stepRef            = shift || die "no currentStepRef defined!\n";
  my $subStepRef         = shift || die "no currentSubStepRef defined!\n";

  # error checking
  if ($rule->{'articleListPublishGlobalOrCategoryIndexes'} ne 'category') { die "articleListPublishGlobalOrCategoryIndexes must be 'global'!"; }

  # get category nums to publish
  my @categoryNumsToPublish;
  my $publishAll          = !$publishUpdatedOnly;
  my $ruleCategoryNums    = &IT::ArtMan::CustomMenus::Admin::PublishRules::getSelectedCategoryNumsForPublishRule($rule);
  my $updatedCategoryNums = &IT::ArtMan::CustomMenus::Publish::getCategoryNumsWithPublishFlag($ruleCategoryNums, 'any');
  if ($publishAll || $rule->{'alwaysPublish'}) { @categoryNumsToPublish = @$ruleCategoryNums; }
  else                                         { @categoryNumsToPublish = @$updatedCategoryNums; }

  # add categories grandparents if using includeChildArticles setting
  my $categoryRecords = &IT::ArtMan::CustomMenus::Category::getHashOfCategoryRecords();
  if ($rule->{'includeChildArticles'}) {
    my %categoryNumsAndParentNums;
    foreach my $categoryNum (@categoryNumsToPublish) {
      foreach my $parentNum (split /:/, $categoryRecords->{$categoryNum}{'lineage'}) {
        unless ($parentNum) { next; }
        $categoryNumsAndParentNums{$parentNum} = 1;
      }
    }
    @categoryNumsToPublish = keys %categoryNumsAndParentNums;
  }

  ### sort category nums
  # publish categories in category order because keys() isn't guarenteed to return results in same order every time and publishing
  # categories in orer they're listed in category manager is what users would expect.
  @categoryNumsToPublish = sort { $categoryRecords->{$a}{'globalOrder'} <=> $categoryRecords->{$b}{'globalOrder'} } @categoryNumsToPublish;

  # on first step - display status message
  my $isFirstStep = ($$stepRef == 0);
  if    (@categoryNumsToPublish && $isFirstStep) { &IT::MultiStepOperations::addStatusMessage("Publishing: $rule->{'name'} (category $rule->{'type'})\n"); }
  elsif (!@categoryNumsToPublish)                { &IT::MultiStepOperations::addStatusMessage("No updates for: $rule->{'name'} (category $rule->{'type'})\n"); }

  # on last step
  my $totalSteps      = @categoryNumsToPublish;
  my $noMoreRecords   = ($$stepRef >= $totalSteps);
  if ($noMoreRecords) {
    return 1;     # stage is complete
  }


  ### publish article list

  my $categoryNum     = $categoryNumsToPublish[ $$stepRef ] || die "Couldn't find category num for current step!";
  my $currentCategory = $categoryRecords->{$categoryNum} || {};
  my $currentArticle  = {};
  my $filepath        = &IT::ArtMan::CustomMenus::Publish::getFilePathAndCreateDirFor($rule, $currentCategory, $currentArticle);
  my $categoryNums    = [ $categoryNum ];

  # get html and publish file
  my $isPublished  = $currentCategory->{'type'} ne 'disabled';
  if ($isPublished) {
    &IT::MultiStepOperations::addStatusMessage(" - category: $currentCategory->{'name'}\n");
    my $html = &getHtmlForList({
                 'template'             => $rule->{'template'},
                 'categoryNums'         => $categoryNums,
                 'includeChildArticles' => $rule->{'includeChildArticles'},
                 'sortBy'               => &_getArticleListSortArrayFromRule($rule),
                 'limit'                => &_getArticleListLimitArrayFromRule($rule),
                 'where'                => '',
                 'pageNumber'           => 1,
                 'ruleNumber'           => $rule->{'num'},
                });
    &IT::File::writeFileFast($filepath, $html);
  }

  # update stage and step counters
  $$stepRef++;

  # return 0 for stage not complete - return 1 is above when there are no more records
  return 0;
}

# ------------------------------------------------------------------------------
# Function    : _getArticleListSortArrayFromRule
# Description :
# Usage       : my $sortBy = &_getArticleListSortArrayFromRule($rule);
# ------------------------------------------------------------------------------

sub _getArticleListSortArrayFromRule {
  my $rule               = shift || die "no rule specified!";

  # create record sorting string
  my $sortBy;
  if ($rule->{'useCustomSorting'}) {
    $sortBy = [];
    for my $num (1..10) {
      my $fieldName    = $rule->{"customSortField$num"} || next; # skip blank fields
      my $reverseOrder = $rule->{"customSortReverse$num"};
      my $sortField    = $reverseOrder ? "-$fieldName" : $fieldName;
      push @$sortBy, $sortField;
    }
  }

  return $sortBy;
}
# ------------------------------------------------------------------------------
# Function    : _getArticleListLimitArrayFromRule
# Description :
# Usage       : my $limit = &_getArticleListLimitArrayFromRule($rule);
# ------------------------------------------------------------------------------

sub _getArticleListLimitArrayFromRule {
  my $rule = shift || die "no rule specified!";

  my $limitArray;
  my $offset  = $rule->{'limitResultsRangeStart'} - 1;
  my $records = ($rule->{'limitResultsRangeEnd'} - $rule->{'limitResultsRangeStart'} + 1);
  $limitArray = [$offset, $records];

  return $limitArray;
}

# ------------------------------------------------------------------------------
# Function    : getHtmlForList
# Description : Return html for an article list.
#
# Usage       : my $html = &IT::ArtMan::CustomMenus::Publish::ArticleLists::getHtmlForList({
#                            'template'             => $template,
#                            'categoryNums'         => $categoryNums,
#                            'includeChildArticles' => $includeChildArticles,
#                            'sortBy'               => $sortByArray,
#                            'limit'                => $limitArray,
#                            'where'                => $whereQuery,
#                            'pageNumber'           => $pageNumber,
#                            'ruleNumber'           => $ruleNumber,
#                           });
# ------------------------------------------------------------------------------

sub getHtmlForList {
  my $args = shift;

  # error checking
  if (ref $args ne "HASH")                    { die "first argument must be a hash ref!"; }
  if (keys %$args != 8)                       { die "Exactly 8 arguments are required!"; }
  if (!$args->{'template'})                   { die "no template specified!"; }
  if (!$args->{'limit'})                      { die "no limit array specified!"; }
  if (!$args->{'categoryNums'})               { die "no categoryNums specified!"; }
  if (ref $args->{'categoryNums'} ne 'ARRAY') { die "categoryNums must be an array ref!"; }
  if (@{$args->{'categoryNums'}} == 0)        { die "categoryNums does not contain any categories!"; }
  if (!$args->{'pageNumber'})                 { die "no pageNumber specified!"; }

  # set defaults and vars
  unless ($args->{'sortBy'}) {
    $args->{'sortBy'} = &IT::DB::Schema::getTableAttributes($TABLE_PREFIX . "article", 'defaultSort');
  }

  # add table prefixes to sortby fields
  foreach (@{$args->{'sortBy'}}) {
    unless (m/^-?article\./) { s/^(-?)/${1}article./; }
  }

  my $articleTable     = "${TABLE_PREFIX}article";
  my $categoryRecords  = &IT::ArtMan::CustomMenus::Category::getHashOfCategoryRecords();

  ### get category nums to list
  my @categoryNumsToDisplay = @{$args->{'categoryNums'}};

  # add child nums
  if ($args->{'includeChildArticles'}) {
    my $childCategoryNums = &IT::ArtMan::CustomMenus::Category::getChildCategoryNumsFor(@{$args->{'categoryNums'}});
    foreach my $categoryNum (@$childCategoryNums) {
      my $childCategory  = $categoryRecords->{ $categoryNum };
      my $parentCategory = $categoryRecords->{ $childCategory->{'parentNum'} };
      if ($parentCategory->{'type'} ne 'unlinked' && $childCategory->{'type'} eq 'unlinked') { next; } # don't add articles from subcategories that are unlinked unless parent is unlinked too
      if ($childCategory->{'type'} eq 'disabled') { next; } # don't add articles from subcategories that are disabled
      push @categoryNumsToDisplay, $categoryNum;
    }
  }

  # create whereQuery
  my $where = $args->{'where'};
  $where   .= " AND " if ($where);
  $where   .= "article.status = 'visible'";
  $where   .= " AND ";
  $where   .= &IT::ArtMan::CustomMenus::Publish::getWhereForFilteringByCategoryField(\@categoryNumsToDisplay);

  # get article records and count
  &IT::FileLock::getLock('shared');

  my $sth = &IT::DB::select2({
    'tableNames' => $articleTable,
    'where'      => $where,
    'sortBy'     => $args->{'sortBy'},
    'limit'      => $args->{'limit'},
  });

  my $articles = [];
  while (my $record = $sth->fetchrow_hashref('NO_FIELDNAME_PREFIXES')) {
    push @$articles, $record;
  }

  my $countWhere  = $where;
  $countWhere     =~ s/\b(article)\.//g; # remove table prefixes
  my $recordCount = &IT::DB::count($articleTable, $countWhere); # get matching record count

  &IT::FileLock::releaseLock();

  # create placeholders
  my($startNum, $recordLimit) = @{$args->{'limit'}};
  my $perPage         = $recordLimit;
  my $pageCount       = &IT::Common::mathCeiling( $recordCount / $perPage );
  my $currentArticle  = {};
  my $primaryCategory = (@{$args->{'categoryNums'}} == 1) ? $categoryRecords->{ $args->{'categoryNums'}->[0] } : {};  # only define category for page if there is only one category
  my $placeholders    = &IT::ArtMan::CustomMenus::Publish::createPlaceholders($primaryCategory, $currentArticle, $args->{'template'}, $args->{'ruleNumber'});

  &IT::Template::addPlaceholderToWhitelist('articleList');
  $placeholders->{'articleList'}                   = '';
  $placeholders->{'articleList.template'}          = $args->{'template'};
  $placeholders->{'articleList.perPage'}           = $perPage;
  $placeholders->{'articleList.totalPages'}        = $pageCount;
  $placeholders->{'articleList.prevPageNumber'}    = ($args->{'pageNumber'} > 1) ? $args->{'pageNumber'}-1 : '';
  $placeholders->{'articleList.thisPageNumber'}    = $args->{'pageNumber'};
  $placeholders->{'articleList.nextPageNumber'}    = ($args->{'pageNumber'} < $pageCount) ? $args->{'pageNumber'}+1 : '';
  $placeholders->{'articleList.includeSubcats'}    = $args->{'includeChildArticles'} ? 1 : 0;
  $placeholders->{'articleList.totalResults'}      = $recordCount;
  $placeholders->{'articleList.firstResultNumber'} = @$articles ? $startNum + 1 : 0;
  $placeholders->{'articleList.lastResultNumber'}  = $startNum + int scalar @$articles;

  # create article list
  my $rowCounter = 0;
  foreach my $article (@$articles) {
    my $primaryCategory = $categoryRecords->{$article->{'categoryNum'}} || {};

    # add category, article, and published placeholders
    my $rowPlaceholders = &IT::ArtMan::CustomMenus::Publish::createPlaceholders($primaryCategory, $article, $args->{'template'}, $args->{'ruleNumber'});
    $rowPlaceholders = { %$placeholders, %$rowPlaceholders }; # copy placeholder values
    $rowPlaceholders->{'articleList.currentResultCounter'} = ++$rowCounter;
    $rowPlaceholders->{'articleList.currentResultNumber'}  = ($placeholders->{'articleList.firstResultNumber'} - 1) + $rowCounter;

    # add row
    $placeholders->{'articleList'} .= &templateCell($args->{'template'}, 'articleRow', $rowPlaceholders);
  };

  # get html
  my $html = &template($args->{'template'}, $placeholders);
  return $html;

}

# ----------------------------------------------------------------------------
# end of file
1;

