#!/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::Install;

  # load modules
  use strict;

  use IT::Globals;

# ----------------------------------------------------------------------------
# Function    : installIfNeeded
# Description :
# Usage       : &IT::ArtMan::Install::installIfNeeded();
# ----------------------------------------------------------------------------

sub installIfNeeded {
  my $step       = $FORM->{'step'} || 1;
  my $isSaving   = $FORM->{'action'} eq 'save';

  # check if already installed
  if ($SETTINGS->{"isInstalled"}) { return; }

  # install form actions (only available if not installed)
  my $action       = $FORM->{'action'};
  my $validLicense = $SETTINGS->{'licenseProductId'} && &IT::App::License::isValidProductId($SETTINGS->{'licenseProductId'});
  if ($action eq 'showLicense')   { &IT::App::License::displayLicensePage(); }
  if (!$SETTINGS->{"isInstalled"} && $validLicense) { # only available if not installed yet and valid license has been entered
    if ($action eq 'browseForDir')   { &IT::App::BrowseForDirOrFile::browseFor('dir'); }
    if ($action eq 'browseForFile')  { &IT::App::BrowseForDirOrFile::browseFor('file'); }
    if ($action eq 'admin_testmail') { &IT::App::Menu::Admin::testMail(); }
  }

  # dispatch steps
  &IT::Template::setGlobalPlaceholder('totalSteps', 8);

  if ($step == 1) { &welcomePage($step, $isSaving);     $isSaving = 0; $step++; }
  if ($step == 2) { &acceptLicense($step, $isSaving);   $isSaving = 0; $step++; }
  if ($step == 3) { &productId($step, $isSaving);       $isSaving = 0; $step++; }
  if ($step == 4) { &timezoneOffset($step, $isSaving);  $isSaving = 0; $step++; }
  if ($step == 5) { &urls($step, $isSaving);            $isSaving = 0; $step++; }
  if ($step == 6) { &dirs($step, $isSaving);            $isSaving = 0; $step++; }
  if ($step == 7) { &mailSettings($step, $isSaving);    $isSaving = 0; $step++; }
  if ($step == 8) { &createAdminUser($step, $isSaving); $isSaving = 0; $step++; }
  exit;
}

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

sub welcomePage {
  my $step     = shift || die "no step specified!";
  my $isSaving = shift;

  ### SAVING - Check for errors and save info
  my $errors;
  if ($isSaving) {

    # error checking

    # save information and go to next step
    if (!$errors) {
      return;
    }
  }

  ### OUTPUT PAGE

  # define placeholders
  my $placeholders                          = {};
  $placeholders->{'step'}                   = $step;
  $placeholders->{'_isSublicensingAllowed'} = &IT::App::License::isSublicensingAllowed();

  # display page
  my $templateCell        = 'welcome';
  &_displayPage($templateCell, $placeholders, $errors);
  exit;
}

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

sub acceptLicense {
  my $step     = shift || die "no step specified!";
  my $isSaving = shift;

  ### SAVING - Check for errors and save info
  my $errors;
  if ($isSaving) {

    # error checking
    if ($FORM->{'answer'} ne 'agree') { $errors .= "You cannot install the software without agreeing the license agreement."; };

    # save information and go to next step
    if (!$errors) {
      return;
    }
  }

  ### OUTPUT PAGE

  # define placeholders
  my $placeholders                    = {};
  $placeholders->{'step'}             = $step;
  $placeholders->{'licenseAgreement'} = &IT::App::License::getLicenseHtml();

  # display page
  my $templateCell = 'license';
  &_displayPage($templateCell, $placeholders, $errors);
  exit;
}

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

sub productId {
  my $step     = shift || die "no step specified!";
  my $isSaving = shift;

  ### SAVING - Check for errors and save info
  my $errors;
  if ($isSaving) {

    # error checking
    if (!$FORM->{'companyName'})                                       { $errors .= "Please enter your 'Company Name'.\n"; };
    if (!$FORM->{'domainName'})                                        { $errors .= "Please enter your 'Domain Name'.\n"; };
    if (!$FORM->{'productId'})                                         { $errors .= "Please enter your 'Product Id'.\n"; }
    elsif (!&IT::App::License::isValidProductId($FORM->{'productId'})) { $errors .= "Invalid Product ID!\n"; }
    if (!$FORM->{'agreeToOneInstall'})                                 { $errors .= "Please check 'I agree not to install more than one copy...'\n"; };
    if (!int $FORM->{'howManyWebsites'})                               { $errors .= "Please enter a value for 'How many websites...'\n"; }
    elsif (int $FORM->{'howManyWebsites'} > 1)                         { $errors .= "Using your product ID or more than one website isn't allowed!\n"; };

    # save information and go to next step
    if (!$errors) {
      $SETTINGS->{'licenseCompanyName'} = $FORM->{'companyName'};
      $SETTINGS->{'licenseDomainName'}  = $FORM->{'domainName'};
      $SETTINGS->{'licenseProductId'}   = $FORM->{'productId'};
      &IT::App::Menu::Admin::saveAdminSettingsToFile($SETTINGS);

      return;
    }
  }

  ### OUTPUT PAGE

  # define placeholders
  my $placeholders                      = {};
  $placeholders->{'step'}               = $step;
  $placeholders->{'companyName'}        = $FORM->{'companyName'};
  $placeholders->{'domainName'}         = $FORM->{'domainName'} || $ENV{'HTTP_HOST'} || $ENV{'SERVER_NAME'};
  $placeholders->{'productId'}          = $FORM->{'productId'};
  $placeholders->{'agreeToOneInstall'}  = $FORM->{'agreeToOneInstall'};
  $placeholders->{'howManyWebsites'}    = $FORM->{'howManyWebsites'} ne '' ? int $FORM->{'howManyWebsites'} : '';

  # display page
  my $templateCell = 'productId';
  &_displayPage($templateCell, $placeholders, $errors);
  exit;
}

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

sub timezoneOffset {
  my $step     = shift || die "no step specified!";
  my $isSaving = shift;

  ### SAVING - Check for errors and save info
  my $errors;
  if ($isSaving) {

    # error checking
    if    ($FORM->{'localtime_hour_offset'} =~ m/[^0-9]/)   { $errors .= "Hour offset can only contain 0-9!\n"; }
    if    ($FORM->{'localtime_minute_offset'} =~ m/[^0-9]/) { $errors .= "Minute offset can only contain 0-9!\n"; }

    # save information and go to next step
    if (!$errors) {
      $SETTINGS->{'localtime_hour_adjustment'}   = $FORM->{'localtime_hour_adjustment'};
      $SETTINGS->{'localtime_minute_adjustment'} = $FORM->{'localtime_minute_adjustment'};
      $SETTINGS->{'localtime_hour_offset'}       = $FORM->{'localtime_hour_offset'};
      $SETTINGS->{'localtime_minute_offset'}     = $FORM->{'localtime_minute_offset'};
      $SETTINGS->{'localtimeDstAdjustment'}      = $FORM->{'localtimeDstAdjustment'};
      &IT::App::Menu::Admin::saveAdminSettingsToFile($SETTINGS);

      if (!$FORM->{'refreshPage'}) {
        return;
      }
    }
  }

  ### OUTPUT PAGE

  # define placeholders
  my $time              = time();
  my $adjustedLocalTime = &IT::App::Common::getTimeAdjustedToMatchGMT($time);
  my $placeholders      = {};

  $placeholders->{'step'}                        = $step;
  $placeholders->{'gmtDateAndTime'}              = &IT::Template::createDatePlaceholder( $time );
  $placeholders->{'localDateAndTime'}            = &IT::Template::createDatePlaceholder($adjustedLocalTime);
  $placeholders->{'localtime_hour_adjustment'}   = $SETTINGS->{'localtime_hour_adjustment'};
  $placeholders->{'localtime_minute_adjustment'} = $SETTINGS->{'localtime_minute_adjustment'};
  $placeholders->{'localtime_hour_offset'}       = $SETTINGS->{'localtime_hour_offset'};
  $placeholders->{'localtime_minute_offset'}     = $SETTINGS->{'localtime_minute_offset'};
  $placeholders->{'localtimeDstAdjustment'}      = $SETTINGS->{'localtimeDstAdjustment'};

  # display page
  my $templateCell = 'timezoneOffset';
  &_displayPage($templateCell, $placeholders, $errors);
  exit;
}

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

sub urls {
  my $step     = shift || die "no step specified!";
  my $isSaving = shift;

  ### SAVING - Check for errors and save info
  my $errors;
  if ($isSaving) {

    # error checking
    if    (!$FORM->{'webfolder_url'})            { $errors .= "Please enter your 'Program Web Url'.\n"; }
    elsif ($FORM->{'webfolder_url'} !~ m|/$|)    { $errors .= "'Program Web Url' must end with a trailing slash ('/').\n"; }

    # save information and go to next step
    if (!$errors) {
      my $cgifolder_url = &IT::App::Install::getCgiFolderUrl();
      $SETTINGS->{'searchUrl'}        = "${cgifolder_url}search.cgi";

      $SETTINGS->{'webfolder_url'}    = $FORM->{'webfolder_url'};
      $SETTINGS->{'upload_url'}       = "$FORM->{'webfolder_url'}uploads/";
      $SETTINGS->{'publish_url'}      = "$FORM->{'webfolder_url'}publish/";

      # set wysiwyg folder url
      my $webfolderUrlWithoutDomain = $FORM->{'webfolder_url'};
      $webfolderUrlWithoutDomain   =~ s|^\w+://[^/]*||;
      $SETTINGS->{'wysiwygFolderUrl'} = "${webfolderUrlWithoutDomain}wysiwyg/";

      &IT::App::Menu::Admin::saveAdminSettingsToFile($SETTINGS);
      return;
    }
  }

  ### OUTPUT PAGE

  # define placeholders
  my $placeholders                      = {};
  $placeholders->{'step'}               = $step;
  $placeholders->{'cgifolder_url'}      = &IT::App::Install::getCgiFolderUrl();
  $placeholders->{'webfolder_url'}      = $FORM->{'webfolder_url'} || &IT::App::Install::guessWebFolderUrl();

  # display page
  my $templateCell = 'urls';
  &_displayPage($templateCell, $placeholders, $errors);
  exit;
}

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

sub dirs {
  my $step     = shift || die "no step specified!";
  my $isSaving = shift;

  # update global placeholders (new settings values saved in last step won't be global yet)
  &main::defineGlobalPlaceholders();

  ### SAVING - Check for errors and save info
  my $errors;
  if ($isSaving) {

    # error checking
    if    (!$FORM->{'website_root_dir'})         { $errors .= "Please enter your 'Website Root Directory'.\n"; }
    elsif (!-d $FORM->{'website_root_dir'})       { $errors .= "'Website Root Directory' doesn't exist or isn't a directory!\n"; }
    elsif ($FORM->{'website_root_dir'} !~ m|/$|) { $errors .= "'Website Root Directory' must end with a trailing slash ('/').\n"; }
    if    (!$FORM->{'webfolder_dir'})            { $errors .= "Please enter your 'Program Web Dir'.\n"; }
    elsif (!-d $FORM->{'webfolder_dir'})         { $errors .= "'Program Web Dir' doesn't exist or isn't a directory!\n"; }
    elsif ($FORM->{'webfolder_dir'} !~ m|/$|)    { $errors .= "'Program Web Dir' must end with a trailing slash ('/').\n"; }
    elsif (!-w $FORM->{'webfolder_dir'})         { $errors .= "'Program Web Dir' isn't writable (change file permissions).\n"; }

    # save information and go to next step
    if (!$errors) {
      $SETTINGS->{'website_root_dir'} = $FORM->{'website_root_dir'};
      $SETTINGS->{'upload_dir'}       = "$FORM->{'webfolder_dir'}uploads/";
      $SETTINGS->{'publish_dir'}      = "$FORM->{'webfolder_dir'}publish/";

      &IT::App::Menu::Admin::saveAdminSettingsToFile($SETTINGS);
      return;
    }
  }

  ### OUTPUT PAGE

  # define placeholders
  my $placeholders                      = {};
  $placeholders->{'step'}               = $step;
  $placeholders->{'website_root_dir'}   = $FORM->{'website_root_dir'} || &IT::App::Install::guessWebsiteRootPath();
  $placeholders->{'webfolder_dir'}      = $FORM->{'webfolder_dir'}    || &IT::App::Install::guessWebFolderDir();

  # display page
  my $templateCell = 'dirs';
  &_displayPage($templateCell, $placeholders, $errors);
  exit;
}


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

sub mailSettings {
  my $step     = shift || die "no step specified!";
  my $isSaving = shift;

  # update global placeholders (new settings values saved in last step won't be global yet)
  &main::defineGlobalPlaceholders();


  ### SAVING - Check for errors and save info
  my $errors;
  if ($isSaving) {

    # error checking
    if    (!$FORM->{'sender_name'})                                 { $errors .= "Please enter your 'Sender Name'.\n"; }
    if    (!$FORM->{'sender_email'})                                { $errors .= "Please enter your 'Sender Email'.\n"; }
    elsif (!&IT::Validation::isValidEmail($FORM->{'sender_email'})) { $errors .= "'Sender Email' isn't a valid email address (example user\@example.com)!\n"; }
    if    (!$FORM->{'admin_email'})                                 { $errors .= "Please enter your 'Admin Email'.\n"; }
    elsif (!&IT::Validation::isValidEmail($FORM->{'admin_email'}))  { $errors .= "'Admin Email' isn't a valid email address (example user\@example.com)!\n"; }
    if    ($FORM->{'send_mail_with'} ne 'SMTP' &&
           $FORM->{'send_mail_with'} ne 'SENDMAIL')                 { $errors .= "'Send Mail With' must be either sendmail or smtp!\n"; }
    if ($FORM->{'send_mail_with'} eq 'SMTP') {
      if    (!$FORM->{'smtp_hostname'})                                       { $errors .= "Please enter your 'SMTP Server'.\n"; }
      elsif (!&IT::Validation::isValidHostnameOrIP($FORM->{'smtp_hostname'})) { $errors .= "'SMTP Server' isn't a valid hostname or IP address (example: server.example.com or 127.0.0.1)!\n"; }
    }
    if ($FORM->{'send_mail_with'} eq 'SMTP' && $FORM->{'pop3_login_required_before_smtp'}) {
      if    (!$FORM->{'pop3auth_hostname'})                                       { $errors .= "Please enter your 'POP3 Hostname'.\n"; }
      elsif (!&IT::Validation::isValidHostnameOrIP($FORM->{'pop3auth_hostname'})) { $errors .= "'POP3 Hostname' isn't a valid hostname or IP address (example: server.example.com or 127.0.0.1)!\n"; }
      if    (!$FORM->{'pop3auth_username'})                                       { $errors .= "Please enter your 'POP3 Username'.\n"; }
      if    (!$FORM->{'pop3auth_password'})                                       { $errors .= "Please enter your 'POP3 Password'.\n"; }
    }
    if ($FORM->{'send_mail_with'} eq 'SENDMAIL') {
      if    (!$FORM->{'sendmail_path'})    { $errors .= "Please enter your 'Sendmail Path'.\n"; }
      elsif (!-e $FORM->{'sendmail_path'}) { $errors .= "'Sendmail Path' could not be found!\n"; }
    }

    # save information
    if (!$errors) {
      $SETTINGS->{'sender_name'}                     = $FORM->{'sender_name'};
      $SETTINGS->{'sender_email'}                    = $FORM->{'sender_email'};
      $SETTINGS->{'admin_email'}                     = $FORM->{'admin_email'};
      $SETTINGS->{'send_mail_with'}                  = $FORM->{'send_mail_with'};
      $SETTINGS->{'sendmail_path'}                   = $FORM->{'sendmail_path'};
      $SETTINGS->{'smtp_hostname'}                   = $FORM->{'smtp_hostname'};
      $SETTINGS->{'pop3_login_required_before_smtp'} = $FORM->{'pop3_login_required_before_smtp'};
      $SETTINGS->{'pop3auth_hostname'}               = $FORM->{'pop3auth_hostname'};
      $SETTINGS->{'pop3auth_username'}               = $FORM->{'pop3auth_username'};
      $SETTINGS->{'pop3auth_password'}               = $FORM->{'pop3auth_password'};
      &IT::App::Menu::Admin::saveAdminSettingsToFile($SETTINGS);
      return;
    }

    # go to next step
    if (!$errors) { return; }
  }

  ### OUTPUT PAGE

  # define placeholders
  my @sendmailPaths = &IT::App::Install::findSendmailPaths();
  my $placeholders                                   = {};
  $placeholders->{'step'}                            = $step;
  $placeholders->{'sender_name'}                     = $FORM->{'sender_name'}    || $SETTINGS->{'programName'};
  $placeholders->{'sender_email'}                    = $FORM->{'sender_email'}   || $ENV{'SERVER_ADMIN'};
  $placeholders->{'admin_email'}                     = $FORM->{'admin_email'}    || $ENV{'SERVER_ADMIN'};
  $placeholders->{'sendmail_path'}                   = $FORM->{'sendmail_path'}  || $sendmailPaths[0];
  $placeholders->{'smtp_hostname'}                   = $FORM->{'smtp_hostname'}  || &IT::App::Install::guessSmtpHostname();
  $placeholders->{'send_mail_with'}                  = $FORM->{'send_mail_with'};
  $placeholders->{'send_mail_with'}                ||= 'SENDMAIL' if $placeholders->{'sendmail_path'};
  $placeholders->{'send_mail_with'}                ||= 'SMTP'     if $placeholders->{'smtp_hostname'};
  $placeholders->{'pop3_login_required_before_smtp'} = $FORM->{'pop3_login_required_before_smtp'};
  $placeholders->{'pop3auth_hostname'}               = $FORM->{'pop3auth_hostname'};
  $placeholders->{'pop3auth_username'}               = $FORM->{'pop3auth_username'};
  $placeholders->{'pop3auth_password'}               = $FORM->{'pop3auth_password'};
  $placeholders->{'valid_sendmail_locations'} = '';
  foreach my $sendmailPath (@sendmailPaths) {
    $placeholders->{'valid_sendmail_locations'} .= &templateCell('admin/menus/install.html','sendmailrow', { 'path' => $sendmailPath });
  }


  # display page
  my $templateCell = 'mailSettings';
  &_displayPage($templateCell, $placeholders, $errors);
  exit;
}


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

sub createAdminUser {
  my $step     = shift || die "no step specified!";
  my $isSaving = shift;

  ### SAVING - Check for errors and save info
  my $errors;
  my $userKnowsExistingAdminAccount;
  if ($isSaving) {

    # check for required information from previous steps
    if (!&IT::App::License::isValidProductId($SETTINGS->{'licenseProductId'})) { $errors .= "No valid Product Id was entered!\n"; }

    # error checking
    if (!$FORM->{'fullname'})                        { $errors .= "Please enter your 'Admin Full Name'.\n"; }
    if (!$FORM->{'email'})                           { $errors .= "Please enter your 'Admin Email'.\n"; }
    elsif (!&IT::Validation::isValidEmail($FORM->{'email'})) { $errors .= "'Admin Email' isn't a valid email address (example user\@example.com)!\n"; }
    if (!$FORM->{'username'})                        { $errors .= "Please enter your 'Admin Username'.\n"; }
    if    (!$FORM->{'password'})                        { $errors .= "Please enter your 'Admin Password'.\n"; }
    elsif (!$FORM->{'password2'})                       { $errors .= "Please enter your 'Admin Password (again)'.\n"; }
    elsif ($FORM->{'password'} ne $FORM->{'password2'}) { $errors .= "Passwords don't match!\n"; }
    if (!$errors) {
      if ($FORM->{'password'} eq $FORM->{'username'}) { $errors .= "Your username and your password cannot be the same!\n"; }
    }

    # Check username isn't already in use
    if (!$errors) {
      &IT::FileLock::getLock('shared');
      my $escapedUsername = &IT::DB::escapeValueAndWildcards($FORM->{'username'});
      my $escapedEmail    = &IT::DB::escapeValueAndWildcards($FORM->{'email'});
      my $accountsWithSameName = &IT::DB::select({
        'tableName'  => $TABLE_PREFIX . "accounts",
        'whereQuery' => "username = '$escapedUsername' OR email = '$escapedEmail'",
      });
      &IT::FileLock::releaseLock();

      my $usernameOrEmailAlreadyExists = scalar @$accountsWithSameName;
      $userKnowsExistingAdminAccount = $accountsWithSameName->[0]{'username'} eq $FORM->{'username'} &&
                                       $accountsWithSameName->[0]{'password'} eq $FORM->{'password'} &&
                                       $accountsWithSameName->[0]{'accessLevel'} == 9;

      # if the user enters username and password for existing account skip errors and create user code
      if (!$userKnowsExistingAdminAccount && $usernameOrEmailAlreadyExists) { $errors .= "The specified username or email is already in use!\n"; }
    }


    # save information
    if (!$errors) {

      # Add user to database
      my $userRecord = {
        'createdTime'     => $^T,
        'lastUpdatedTime' => $^T,
        'accessLevel'     => 9,
        'fullname'        => $FORM->{'fullname'},
        'email'           => $FORM->{'email'},
        'username'        => $FORM->{'username'},
        'password'        => $FORM->{'password'},
      };
      if (!$userKnowsExistingAdminAccount) {
        &IT::FileLock::getLock('exclusive');
        &IT::DB::insert($TABLE_PREFIX.'accounts', [ $userRecord ]) || die "Error adding new user record! $!";
        &IT::FileLock::releaseLock();
      }

      # update settings
      $SETTINGS->{'isInstalled'}    = "1";
      $SETTINGS->{'programVersion'} = $PROGRAM_VERSION;
      $SETTINGS->{'programBuild'}   = $PROGRAM_BUILD;
      $SETTINGS->{'lastUpdated'}    = $^T;
      &IT::App::Menu::Admin::saveAdminSettingsToFile($SETTINGS);

      # clear old login cookies
      &IT::App::Menu::Login::eraseLoginCookies();

      # start program
      &IT::Common::redirectBrowserToURL("$CGIURL");
      exit;
    }
  }

  ### OUTPUT PAGE

  # define placeholders
  my($adminEmailUserNameOnly)  = split(/@/, $SETTINGS->{'admin_email'});
  my $placeholders             = {};
  $placeholders->{'step'}      = $step;
  $placeholders->{'fullname'}  = $FORM->{'fullname'}  || $adminEmailUserNameOnly;
  $placeholders->{'email'}     = $FORM->{'email'}     || $SETTINGS->{'admin_email'};
  $placeholders->{'username'}  = $FORM->{'username'}  || $adminEmailUserNameOnly;
  $placeholders->{'password'}  = $FORM->{'password'};
  $placeholders->{'password2'} = $FORM->{'password2'};

  # display page
  my $templateCell = 'createAdminUser';
  &_displayPage($templateCell, $placeholders, $errors);
  exit;
}

# ------------------------------------------------------------------------------
# Function    :
# Description :
# Usage       : &_displayPage($templateCell, $placeholders, $errors);
# ------------------------------------------------------------------------------

sub _displayPage {
  my $templateCell = shift;
  my $placeholders = shift || die "no placeholder specified!";
  my $errors       = shift;

  # defined placeholders
  $placeholders->{'alerts'}   = &IT::App::alert($errors); # get program alerts

  # display page
  my $template = "admin/menus/install.html";
  print &IT::Common::contentType('text/html');
  print &templateCell($template, 'header', $placeholders);
  print &templateCell($template, $templateCell, $placeholders);
  print &templateCell($template, 'footer', $placeholders);
  exit;
}

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

