<?php
/**
* @version	$Id: page_helper.php 16513 2017-01-20 14:10:53Z alex $
* @package	In-Portal
* @copyright	Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
* @license      GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/

defined('FULL_PATH') or die('restricted access!');

class PageHelper extends kHelper {

	/**
	 * Returns page info
	 *
	 * @param int $page_id
	 * @return Array
	 */
	function getPageInfo($page_id)
	{
		list ($user_id, $history_permission) = $this->getHistoryPermissionAndUser($page_id);

		$users = $this->getEditors($page_id, $user_id);

		return array(
			'current_revision' => $this->getCurrentRevisionInfo(),
			'editors' => $users,
			'editors_warning' => $this->getEditorsWarning($users),
			'revisions' => $history_permission ? $this->getPageRevisions($page_id) : array(),
		);
	}

	/**
	 * Returns current admin user id (even, when called from front-end) and it's revision history view permission
	 *
	 * @param int $page_id
	 * @return Array
	 */
	protected function getHistoryPermissionAndUser($page_id)
	{
		/** @var kPermissionsHelper $perm_helper */
		$perm_helper = $this->Application->recallObject('PermissionsHelper');

		$user_id = (int)$this->Application->RecallVar($this->Application->isAdmin ? 'user_id' : 'admin_user_id');
		$history_permission = $perm_helper->CheckUserPermission($user_id, 'CATEGORY.REVISION.HISTORY.VIEW', 0, $page_id);

		return Array ($user_id, $history_permission);
	}

	/**
	 * Returns information about given page editors.
	 *
	 * @param integer $page_id Page, that is being edited.
	 * @param integer $user_id User, who is editing a page.
	 *
	 * @return array
	 */
	protected function getEditors($page_id, $user_id)
	{
		$where_clause = array(
			'pr.PageId = ' . $page_id,
			'pr.CreatedById <> ' . $user_id,
			'pr.IsDraft = 1',
		);

		$sql = 'SELECT CASE pr.CreatedById WHEN ' . USER_ROOT . ' THEN "root" WHEN ' . USER_GUEST . ' THEN "Guest" ELSE IF(u.Username = "", u.Email, u.Username) END
				FROM ' . $this->Application->getUnitOption('page-revision', 'TableName') . ' pr
				LEFT JOIN ' . TABLE_PREFIX . 'Users u ON u.PortalUserId = pr.CreatedById
				WHERE (' . implode(') AND (', $where_clause) . ')';

		return $this->Conn->GetCol($sql);
	}

	/**
	 * Returns information about current revision.
	 *
	 * @return array
	 */
	protected function getCurrentRevisionInfo()
	{
		/** @var kDBItem $revision */
		$revision = $this->Application->recallObject('page-revision.current');

		$status_label = $this->getRevisionStatusText($revision);

		$draft = $revision->GetDBField('IsDraft');
		$title = $this->getAdminPhrase($draft ? 'la_title_EditingDraft' : 'la_title_ViewingRevision');

		$current_revision_info = array(
			'title' => sprintf($title, $revision->GetDBField('RevisionNumber'), $status_label),
			'status' => $revision->GetDBField('Status'),
			'saved' => '',
			'toolbar_state' => $this->getToolbarButtonsState($revision),
		);

		$auto_save_time = $revision->GetDBField('AutoSavedOn');

		if ( $auto_save_time ) {
			$phrase = $this->getAdminPhrase($draft ? 'la_DraftSavedAt' : 'la_SavedAt');
			$current_revision_info['saved'] = sprintf($phrase, $revision->GetField('AutoSavedOn_time') . ' (' . $this->getAgoTime($auto_save_time) . ')');
		}

		return $current_revision_info;
	}

	/**
	 * Returns state of all CMS revision toolbar buttons.
	 *
	 * @param kDBItem $revision Page Revision.
	 *
	 * @return array
	 */
	public function getToolbarButtonsState(kDBItem $revision)
	{
		$ret = array();

		foreach ( $this->getToolbarButtons() as $toolbar_button ) {
			$ret[$toolbar_button] = $this->isToolbarButtonEnabled($toolbar_button, $revision);
		}

		return $ret;
	}

	/**
	 * Returns list of CMS revision toolbar buttons.
	 *
	 * @return array
	 */
	protected function getToolbarButtons()
	{
		return array('select', 'delete', 'approve', 'decline', 'preview', 'history');
	}

	/**
	 * Checks if given CMS revision toolbar button is enabled for given revision.
	 *
	 * @param string  $button_name Toolbar button name.
	 * @param kDBItem $revision    Revision to check against.
	 *
	 * @return boolean
	 */
	protected function isToolbarButtonEnabled($button_name, kDBItem $revision)
	{
		$is_draft = $revision->GetDBField('IsDraft');

		if ( $button_name == 'select' || $button_name == 'delete' || $button_name == 'preview' ) {
			return (bool)$is_draft;
		}

		if ( $button_name == 'approve' ) {
			return $revision->GetDBField('Status') != STATUS_ACTIVE && !$is_draft;
		}

		if ( $button_name == 'decline' ) {
			return $revision->GetDBField('Status') != STATUS_DISABLED && !$revision->GetDBField('IsLive') && !$is_draft;
		}

		return true;
	}

	/**
	 * Returns warning to be shown in case of parallel editing attempts.
	 *
	 * @param array $users Users, that are editing a page.
	 *
	 * @return string
	 */
	protected function getEditorsWarning(array $users)
	{
		/** @var kMultiLanguageHelper $ml_helper */
		$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');

		$ret = $ml_helper->getPluralPhrase(
			count($users),
			array(
				'phrase1' => 'la_PageCurrentlyEditing1',
				'phrase2' => 'la_PageCurrentlyEditing2',
				'phrase5' => 'la_PageCurrentlyEditing5',
			),
			false, true
		);

		return sprintf($ret, implode(', ', $users));
	}

	/**
	 * Returns information about given page revisions.
	 *
	 * @param integer $page_id Page, that is being edited.
	 *
	 * @return array
	 */
	protected function getPageRevisions($page_id)
	{
		$ret = Array ();

		$tag_params = Array ('per_page' => -1, 'skip_parent_filter' => 1, 'requery' => 1, 'page_id' => $page_id);

		/** @var kDBList $revisions */
		$revisions = $this->Application->recallObject('page-revision.list', 'page-revision_List', $tag_params);

		$revisions->Query();
		$revisions->GoFirst();

		while ( !$revisions->EOL() ) {
			$ret[ 'r' . $revisions->GetDBField('RevisionNumber') ] = array(
				'title' => $this->getRevisionTitle($revisions),
				'status' => $revisions->GetDBField('Status'),
				'status_label' => $this->getRevisionStatusText($revisions),
				'datetime' => $revisions->GetField('CreatedOn'),
				'author' => $this->getRevisionAuthor($revisions),
				'draft' => (int)$revisions->GetDBField('IsDraft'),
			);

			$revisions->GoNext();
		}

		return $ret;
	}

	/**
	 * Returns title for given revision.
	 *
	 * @param kDBBase $revision Page Revision.
	 *
	 * @return string
	 */
	protected function getRevisionTitle(kDBBase $revision)
	{
		if ( $revision->GetDBField('IsDraft') ) {
			return $this->getAdminPhrase('la_Draft');
		}

		$title = $this->getAdminPhrase('la_RevisionNumber');

		return sprintf($title, $revision->GetDBField('RevisionNumber'));
	}

	/**
	 * Returns status text for given revision.
	 *
	 * @param kDBBase $revision Page Revision.
	 *
	 * @return string
	 */
	protected function getRevisionStatusText(kDBBase $revision)
	{
		$status = $revision->GetDBField('Status');
		$options = $revision->GetFieldOptions('Status');

		return mb_strtolower($this->getAdminPhrase($options['options'][$status]));
	}

	/**
	 * Returns author of given revision.
	 *
	 * @param kDBBase $revision Page Revision.
	 *
	 * @return string
	 */
	protected function getRevisionAuthor(kDBBase $revision)
	{
		$by_label = $this->getAdminPhrase('la_By');

		return $by_label . ': ' . $revision->GetField('CreatedById');
	}

	/**
	 * Returns Admin's non-editable translation of given phrase.
	 *
	 * @param string $label Phrase label.
	 *
	 * @return string
	 */
	protected function getAdminPhrase($label)
	{
		return $this->Application->Phrase($label, false, true);
	}

	/**
	 * Returns time passed between 2 given dates in "X minutes Y seconds ago" format
	 *
	 * @param int $from_date
	 * @param int $to_date
	 * @param integer $max_levels
	 *
	 * @return string
	 */
	public function getAgoTime($from_date, $to_date = null, $max_levels = 1)
	{
		$blocks = Array (
			Array ('name' => 'year', 'amount' => 60*60*24*365),
			Array ('name' => 'month' ,'amount' => 60*60*24*31),
			Array ('name' => 'week', 'amount' => 60*60*24*7),
			Array ('name' => 'day', 'amount' => 60*60*24),
			Array ('name' => 'hour', 'amount' => 60*60),
			Array ('name' => 'minute', 'amount' => 60),
			Array ('name' => 'second', 'amount' => 1),
		);

		if ( !isset($to_date) ) {
			$to_date = adodb_mktime();
		}

		$diff = abs($to_date - $from_date);

		if ( $diff == 0 ) {
			return 'now';
		}

		$current_level = 1;
		$result = Array ();

		foreach ($blocks as $block) {
			if ($current_level > $max_levels) {
				break;
			}

			if ( $diff / $block['amount'] >= 1 ) {
				$amount = floor($diff / $block['amount']);
				$plural = $amount > 1 ? 's' : '';

				$result[] = $amount . ' ' . $block['name'] . $plural;
				$diff -= $amount * $block['amount'];
				$current_level++;
			}
		}

		return implode(' ', $result) . ' ago';
	}

	/**
	 * Returns where clause for loading correct revision for a given page
	 *
	 * @param int $page_id
	 * @param int $live_revision_number
	 * @param string $table_name
	 * @return string
	 */
	public function getRevsionWhereClause($page_id, $live_revision_number, $table_name = '')
	{
		$revision = (int)$this->Application->GetVar('revision');
		list ($user_id, $has_permission) = $this->getHistoryPermissionAndUser($page_id);

		if ( $has_permission && $revision ) {
			$revision_clause = $table_name . 'RevisionNumber = ' . $revision . ' AND ' . $table_name . 'IsDraft = 0';
		}
		else {
			$editing_mode = $this->Application->GetVar('editing_mode'); // not in a EDITING_MODE constant, while in admin console
			$revision_clause = $table_name . 'RevisionNumber = ' . $live_revision_number . ' AND ' . $table_name . 'IsDraft = 0';

			if ( $this->Application->ConfigValue('EnablePageContentRevisionControl') ) {
				if ( $this->Application->GetVar('preview') || $editing_mode == EDITING_MODE_CONTENT ) {
					$revision_clause = '(' . $table_name . 'CreatedById = ' . $user_id . ' AND ' . $table_name . 'IsDraft = 1) OR (' . $revision_clause . ')';
				}
			}
		}

		return $revision_clause;
	}

	/**
	 * Creates new content block in every revision that misses it. Plus creates first page revision
	 *
	 * @param int $page_id
	 * @param int $num
	 */
	public function createNewContentBlock($page_id, $num)
	{
		$sql = 'SELECT pc.PageContentId, pr.RevisionId
				FROM ' . TABLE_PREFIX . 'PageRevisions pr
				LEFT JOIN ' . TABLE_PREFIX . 'PageContent pc ON pc.RevisionId = pr.RevisionId AND pc.ContentNum = ' . $num . '
				WHERE pr.PageId = ' . $page_id;
		$revisions = $this->Conn->GetCol($sql, 'RevisionId');

		if ( !$revisions ) {
			// no revisions for a page -> create a live revision
			/** @var kDBItem $revision */
			$revision = $this->Application->recallObject('page-revision.live', null, Array ('skip_autoload' => true));

			$revision->SetDBField('PageId', $page_id);
			$revision->SetDBField('RevisionNumber', 1);
			$revision->SetDBField('Status', STATUS_ACTIVE);
			$revision->Create();

			$revisions[ $revision->GetID() ] = NULL;
		}

		/** @var kDBItem $content_block */
		$content_block = $this->Application->recallObject('content.new', null, Array ('skip_autoload' => true));

		$content_block->SetDBField('PageId', $page_id);
		$content_block->SetDBField('ContentNum', $num);

		foreach ($revisions as $revision_id => $content_block_id) {
			if ( is_numeric($content_block_id) ) {
				continue;
			}

			$content_block->SetDBField('RevisionId', $revision_id);
			$content_block->Create();
		}
	}

	/**
	 * Loads content block by it's number
	 *
	 * @param kDBItem $content_block
	 * @param CategoriesItem $page
	 * @param int $num
	 *
	 * @return bool
	 */
	public function loadContentBlock(&$content_block, &$page, $num)
	{
		$page_id = $page->GetID();

		if ( !EDITING_MODE && !$this->Application->GetVar('preview') ) {
			$revision_clause = 'pr.RevisionNumber = ' . $page->GetDBField('LiveRevisionNumber') . ' AND pr.IsDraft = 0';
		}
		else {
			$revision_clause = $this->getRevsionWhereClause($page_id, $page->GetDBField('LiveRevisionNumber'), 'pr.');
		}


		$sql = 	$content_block->GetSelectSQL() . '
				WHERE (' . $content_block->TableName . '.PageId = ' . $page_id . ') AND (' . $content_block->TableName . '.ContentNum = ' . $num . ') AND (' . $revision_clause . ')
				ORDER BY pr.IsDraft DESC, pr.RevisionNumber DESC';
		$content_data = $this->Conn->GetRow($sql);

		$content_block->LoadFromHash($content_data);

		return $content_block->isLoaded();
	}
}
