<?php
/*
	Question2Answer by Gideon Greenspan and contributors
	http://www.question2answer.org/

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	More about this license: http://www.question2answer.org/license.php
*/

namespace Q2A\Controllers\Admin;

use Q2A\Controllers\BaseController;
use Q2A\Database\DbConnection;
use Q2A\Middleware\Auth\MinimumUserLevel;

/**
 * Controller for admin page for editing categories.
 */
class Categories extends BaseController
{
	public function __construct(DbConnection $db)
	{
		require_once QA_INCLUDE_DIR . 'app/admin.php';
		require_once QA_INCLUDE_DIR . 'db/selects.php';
		require_once QA_INCLUDE_DIR . 'db/admin.php';
		require_once QA_INCLUDE_DIR . 'app/format.php';

		parent::__construct($db);

		$this->addMiddleware(new MinimumUserLevel(QA_USER_LEVEL_ADMIN));
	}

	public function index()
	{
		// Get relevant list of categories

		$editcategoryid = qa_post_text('edit');
		if (!isset($editcategoryid))
			$editcategoryid = qa_get('edit');
		if (!isset($editcategoryid))
			$editcategoryid = qa_get('addsub');

		$categories = qa_db_select_with_pending(qa_db_category_nav_selectspec($editcategoryid, true, false, true));


		// Check admin privileges (do late to allow one DB query)

		if (!qa_admin_check_privileges($qa_content))
			return $qa_content;


		// Work out the appropriate state for the page

		$editcategory = @$categories[$editcategoryid];

		if (isset($editcategory)) {
			$parentid = qa_get('addsub');
			if (isset($parentid))
				$editcategory = array('parentid' => $parentid);
		} else {
			if (qa_clicked('doaddcategory')) {
				$editcategory = array();
			} elseif (qa_clicked('dosavecategory')) {
				$parentid = qa_post_text('parent');
				$editcategory = array('parentid' => strlen($parentid) ? $parentid : null);
			}
		}

		$setmissing = qa_post_text('missing') || qa_get('missing');

		$setparent = !$setmissing && (qa_post_text('setparent') || qa_get('setparent')) && isset($editcategory['categoryid']);

		$hassubcategory = false;
		foreach ($categories as $category) {
			if (!strcmp($category['parentid'], $editcategoryid))
				$hassubcategory = true;
		}


		// Process saving options

		$savedoptions = false;
		$securityexpired = false;

		if (qa_clicked('dosaveoptions')) {
			if (!qa_check_form_security_code('admin/categories', qa_post_text('code'))) {
				$securityexpired = true;
			} else {
				qa_set_option('allow_no_category', (int)qa_post_text('option_allow_no_category'));
				qa_set_option('allow_no_sub_category', (int)qa_post_text('option_allow_no_sub_category'));
				$savedoptions = true;
			}
		}


		// Process saving an old or new category

		if (qa_clicked('docancel')) {
			if ($setmissing || $setparent)
				qa_redirect(qa_request(), array('edit' => $editcategory['categoryid']));
			elseif (isset($editcategory['categoryid']))
				qa_redirect(qa_request());
			else
				qa_redirect(qa_request(), array('edit' => @$editcategory['parentid']));
		} elseif (qa_clicked('dosetmissing')) {
			if (!qa_check_form_security_code('admin/categories', qa_post_text('code'))) {
				$securityexpired = true;
			} else {
				$inreassign = qa_get_category_field_value('reassign');
				qa_db_category_reassign($editcategory['categoryid'], $inreassign);
				qa_redirect(qa_request(), array('recalc' => 1, 'edit' => $editcategory['categoryid']));
			}
		} elseif (qa_clicked('dosavecategory')) {
			if (!qa_check_form_security_code('admin/categories', qa_post_text('code'))) {
				$securityexpired = true;
			} elseif (qa_post_text('dodelete')) {
				if (!$hassubcategory) {
					$inreassign = qa_get_category_field_value('reassign');
					qa_db_category_reassign($editcategory['categoryid'], $inreassign);
					qa_db_category_delete($editcategory['categoryid']);
					qa_redirect(qa_request(), array('recalc' => 1, 'edit' => $editcategory['parentid']));
				}
			} else {
				require_once QA_INCLUDE_DIR . 'util/string.php';

				$inname = qa_post_text('name');
				$incontent = qa_post_text('content');
				$inparentid = $setparent ? qa_get_category_field_value('parent') : $editcategory['parentid'];
				$inposition = qa_post_text('position');
				$errors = array();

				// Check the parent ID

				$incategories = qa_db_select_with_pending(qa_db_category_nav_selectspec($inparentid, true));

				// Verify the name is legitimate for that parent ID

				if (empty($inname)) {
					$errors['name'] = qa_lang('main/field_required');
				} elseif (qa_strlen($inname) > QA_DB_MAX_CAT_PAGE_TITLE_LENGTH) {
					$errors['name'] = qa_lang_sub('main/max_length_x', QA_DB_MAX_CAT_PAGE_TITLE_LENGTH);
				} else {
					foreach ($incategories as $category) {
						if (!strcmp($category['parentid'], $inparentid) &&
							strcmp($category['categoryid'], @$editcategory['categoryid']) &&
							qa_strtolower($category['title']) == qa_strtolower($inname)
						) {
							$errors['name'] = qa_lang('admin/category_already_used');
						}
					}
				}

				// Verify the slug is legitimate for that parent ID

				for ($attempt = 0; $attempt < 100; $attempt++) {
					switch ($attempt) {
						case 0:
							$inslug = qa_post_text('slug');
							if (!isset($inslug))
								$inslug = implode('-', qa_string_to_words($inname));
							break;

						case 1:
							$inslug = qa_lang_sub('admin/category_default_slug', $inslug);
							break;

						default:
							$inslug = qa_lang_sub('admin/category_default_slug', $attempt - 1);
							break;
					}

					$matchcategoryid = qa_db_category_slug_to_id($inparentid, $inslug); // query against DB since MySQL ignores accents, etc...

					if (!isset($inparentid))
						$matchpage = qa_db_single_select(qa_db_page_full_selectspec($inslug, false));
					else
						$matchpage = null;

					if (empty($inslug))
						$errors['slug'] = qa_lang('main/field_required');
					elseif (qa_strlen($inslug) > QA_DB_MAX_CAT_PAGE_TAGS_LENGTH)
						$errors['slug'] = qa_lang_sub('main/max_length_x', QA_DB_MAX_CAT_PAGE_TAGS_LENGTH);
					elseif (preg_match('/[\\+\\/]/', $inslug))
						$errors['slug'] = qa_lang_sub('admin/slug_bad_chars', '+ /');
					elseif (!isset($inparentid) && qa_admin_is_slug_reserved($inslug)) // only top level is a problem
						$errors['slug'] = qa_lang('admin/slug_reserved');
					elseif (isset($matchcategoryid) && strcmp($matchcategoryid, @$editcategory['categoryid']))
						$errors['slug'] = qa_lang('admin/category_already_used');
					elseif (isset($matchpage))
						$errors['slug'] = qa_lang('admin/page_already_used');
					else
						unset($errors['slug']);

					if (isset($editcategory['categoryid']) || !isset($errors['slug'])) // don't try other options if editing existing category
						break;
				}

				// Perform appropriate database action

				if (empty($errors)) {
					require_once QA_INCLUDE_DIR . 'app/cookies.php';
					if (isset($editcategory['categoryid'])) { // changing existing category
						qa_db_category_rename($editcategory['categoryid'], $inname, $inslug);

						$recalc = false;

						if ($setparent) {
							qa_db_category_set_parent($editcategory['categoryid'], $inparentid);
							$recalc = true;
						} else {
							qa_db_category_set_content($editcategory['categoryid'], $incontent);
							qa_db_category_set_position($editcategory['categoryid'], $inposition);
							$recalc = $hassubcategory && $inslug !== $editcategory['tags'];
						}

						qa_report_event('cat_edit', qa_get_logged_in_userid(), qa_get_logged_in_handle(), qa_cookie_get(), array(
							'categoryid' => $editcategory['categoryid'],
							'parentid' => isset($inparentid) ? $inparentid : null,
							'content' => isset($incontent) ? $incontent : null,
							'position' => isset($inposition) ? $inposition : null,
							'name' => $inname,
							'slug' => $inslug,
						));

						qa_redirect(qa_request(), array('edit' => $editcategory['categoryid'], 'saved' => true, 'recalc' => (int)$recalc));
					} else { // creating a new one
						$categoryid = qa_db_category_create($inparentid, $inname, $inslug);

						qa_db_category_set_content($categoryid, $incontent);

						if (isset($inposition))
							qa_db_category_set_position($categoryid, $inposition);

						qa_report_event('cat_new', qa_get_logged_in_userid(), qa_get_logged_in_handle(), qa_cookie_get(), array(
							'categoryid' => $categoryid,
							'parentid' => $inparentid,
							'content' => isset($incontent) ? $incontent : null,
							'position' => isset($inposition) ? $inposition : null,
							'name' => $inname,
							'slug' => $inslug,
						));

						qa_redirect(qa_request(), array('edit' => $inparentid, 'added' => true));
					}
				}
			}
		}


		// Prepare content for theme

		$qa_content = qa_content_prepare();

		$qa_content['title'] = qa_lang_html('admin/admin_title') . ' - ' . qa_lang_html('admin/categories_title');
		$qa_content['error'] = $securityexpired ? qa_lang_html('admin/form_security_expired') : qa_admin_page_error();

		if ($setmissing) {
			$qa_content['form'] = array(
				'tags' => 'method="post" action="' . qa_path_html(qa_request()) . '"',

				'style' => 'tall',

				'fields' => array(
					'reassign' => array(
						'label' => isset($editcategory)
							? qa_lang_html_sub('admin/category_no_sub_to', qa_html($editcategory['title']))
							: qa_lang_html('admin/category_none_to'),
						'loose' => true,
					),
				),

				'buttons' => array(
					'save' => array(
						'tags' => 'id="dosaveoptions"', // just used for qa_recalc_click()
						'label' => qa_lang_html('main/save_button'),
					),

					'cancel' => array(
						'tags' => 'name="docancel"',
						'label' => qa_lang_html('main/cancel_button'),
					),
				),

				'hidden' => array(
					'dosetmissing' => '1', // for IE
					'edit' => @$editcategory['categoryid'],
					'missing' => '1',
					'code' => qa_get_form_security_code('admin/categories'),
				),
			);

			qa_set_up_category_field(
				$qa_content,
				$qa_content['form']['fields']['reassign'],
				'reassign',
				$categories,
				@$editcategory['categoryid'],
				qa_opt('allow_no_category'),
				qa_opt('allow_no_sub_category')
			);
		} elseif (isset($editcategory)) {
			$qa_content['form'] = array(
				'tags' => 'method="post" action="' . qa_path_html(qa_request()) . '"',

				'style' => 'tall',

				'ok' => qa_get('saved') ? qa_lang_html('admin/category_saved') : (qa_get('added') ? qa_lang_html('admin/category_added') : null),

				'fields' => array(
					'name' => array(
						'id' => 'name_display',
						'tags' => 'name="name" id="name"',
						'label' => qa_lang_html(count($categories) ? 'admin/category_name' : 'admin/category_name_first'),
						'value' => qa_html(isset($inname) ? $inname : @$editcategory['title']),
						'error' => qa_html(@$errors['name']),
					),

					'questions' => array(),

					'delete' => array(),

					'reassign' => array(),

					'slug' => array(
						'id' => 'slug_display',
						'tags' => 'name="slug"',
						'label' => qa_lang_html('admin/category_slug'),
						'value' => qa_html(isset($inslug) ? $inslug : @$editcategory['tags']),
						'error' => qa_html(@$errors['slug']),
					),

					'content' => array(
						'id' => 'content_display',
						'tags' => 'name="content"',
						'label' => qa_lang_html('admin/category_description'),
						'value' => qa_html(isset($incontent) ? $incontent : @$editcategory['content']),
						'error' => qa_html(@$errors['content']),
						'rows' => 2,
					),
				),

				'buttons' => array(
					'save' => array(
						'tags' => 'id="dosaveoptions"', // just used for qa_recalc_click
						'label' => qa_lang_html(isset($editcategory['categoryid']) ? 'main/save_button' : 'admin/add_category_button'),
					),

					'cancel' => array(
						'tags' => 'name="docancel"',
						'label' => qa_lang_html('main/cancel_button'),
					),
				),

				'hidden' => array(
					'dosavecategory' => '1', // for IE
					'edit' => @$editcategory['categoryid'],
					'parent' => @$editcategory['parentid'],
					'setparent' => (int)$setparent,
					'code' => qa_get_form_security_code('admin/categories'),
				),
			);


			if ($setparent) {
				unset($qa_content['form']['fields']['delete']);
				unset($qa_content['form']['fields']['reassign']);
				unset($qa_content['form']['fields']['questions']);
				unset($qa_content['form']['fields']['content']);

				$qa_content['form']['fields']['parent'] = array(
					'label' => qa_lang_html('admin/category_parent'),
				);

				$childdepth = qa_db_category_child_depth($editcategory['categoryid']);

				qa_set_up_category_field(
					$qa_content,
					$qa_content['form']['fields']['parent'],
					'parent',
					isset($incategories) ? $incategories : $categories,
					isset($inparentid) ? $inparentid : @$editcategory['parentid'],
					true,
					true,
					QA_CATEGORY_DEPTH - 1 - $childdepth,
					@$editcategory['categoryid']
				);

				$qa_content['form']['fields']['parent']['options'][''] = qa_lang_html('admin/category_top_level');

				@$qa_content['form']['fields']['parent']['note'] .= qa_lang_html_sub('admin/category_max_depth_x', QA_CATEGORY_DEPTH);
			} elseif (isset($editcategory['categoryid'])) { // existing category
				if ($hassubcategory) {
					$qa_content['form']['fields']['name']['note'] = qa_lang_html('admin/category_no_delete_subs');
					unset($qa_content['form']['fields']['delete']);
					unset($qa_content['form']['fields']['reassign']);
				} else {
					$qa_content['form']['fields']['delete'] = array(
						'tags' => 'name="dodelete" id="dodelete"',
						'label' =>
							'<span id="reassign_shown">' . qa_lang_html('admin/delete_category_reassign') . '</span>' .
							'<span id="reassign_hidden" style="display:none;">' . qa_lang_html('admin/delete_category') . '</span>',
						'value' => 0,
						'type' => 'checkbox',
					);

					$qa_content['form']['fields']['reassign'] = array(
						'id' => 'reassign_display',
						'tags' => 'name="reassign"',
					);

					qa_set_up_category_field(
						$qa_content,
						$qa_content['form']['fields']['reassign'],
						'reassign',
						$categories,
						$editcategory['parentid'],
						true,
						true,
						null,
						$editcategory['categoryid']
					);
				}

				$qcountHtml = $editcategory['qcount'] == 1
					? qa_lang_html_sub('main/1_question', '1', '1')
					: qa_lang_html_sub('main/x_questions', qa_format_number($editcategory['qcount']));

				$qa_content['form']['fields']['questions'] = array(
					'label' => qa_lang_html('admin/total_qs'),
					'type' => 'static',
					'value' => '<a href="' . qa_path_html('questions/' . qa_category_path_request($categories, $editcategory['categoryid'])) . '">' . $qcountHtml . '</a>',
				);

				if ($hassubcategory && !qa_opt('allow_no_sub_category')) {
					$nosubcount = qa_db_count_categoryid_qs($editcategory['categoryid']);

					if ($nosubcount) {
						$qa_content['form']['fields']['questions']['error'] =
							strtr(qa_lang_html('admin/category_no_sub_error'), array(
								'^q' => qa_format_number($nosubcount),
								'^1' => '<a href="' . qa_path_html(qa_request(), array('edit' => $editcategory['categoryid'], 'missing' => 1)) . '">',
								'^2' => '</a>',
							));
					}
				}

				qa_set_display_rules($qa_content, array(
					'position_display' => '!dodelete',
					'slug_display' => '!dodelete',
					'content_display' => '!dodelete',
					'parent_display' => '!dodelete',
					'children_display' => '!dodelete',
					'reassign_display' => 'dodelete',
					'reassign_shown' => 'dodelete',
					'reassign_hidden' => '!dodelete',
				));
			} else { // new category
				unset($qa_content['form']['fields']['delete']);
				unset($qa_content['form']['fields']['reassign']);
				unset($qa_content['form']['fields']['slug']);
				unset($qa_content['form']['fields']['questions']);

				$qa_content['focusid'] = 'name';
			}

			if (!$setparent) {
				$pathhtml = qa_category_path_html($categories, @$editcategory['parentid']);

				if (count($categories)) {
					$qa_content['form']['fields']['parent'] = array(
						'id' => 'parent_display',
						'label' => qa_lang_html('admin/category_parent'),
						'type' => 'static',
						'value' => (strlen($pathhtml) ? $pathhtml : qa_lang_html('admin/category_top_level')),
					);

					$qa_content['form']['fields']['parent']['value'] =
						'<a href="' . qa_path_html(qa_request(), array('edit' => @$editcategory['parentid'])) . '">' .
						$qa_content['form']['fields']['parent']['value'] . '</a>';

					if (isset($editcategory['categoryid'])) {
						$qa_content['form']['fields']['parent']['value'] .= ' - ' .
							'<a href="' . qa_path_html(qa_request(), array('edit' => $editcategory['categoryid'], 'setparent' => 1)) .
							'" style="white-space: nowrap;">' . qa_lang_html('admin/category_move_parent') . '</a>';
					}
				}

				$positionoptions = array();

				$previous = null;
				$passedself = false;

				foreach ($categories as $key => $category) {
					if (!strcmp($category['parentid'], @$editcategory['parentid'])) {
						if (isset($previous))
							$positionhtml = qa_lang_html_sub('admin/after_x', qa_html($passedself ? $category['title'] : $previous['title']));
						else
							$positionhtml = qa_lang_html('admin/first');

						$positionoptions[$category['position']] = $positionhtml;

						if (!strcmp($category['categoryid'], @$editcategory['categoryid']))
							$passedself = true;

						$previous = $category;
					}
				}

				if (isset($editcategory['position'])) {
					$positionvalue = $positionoptions[$editcategory['position']];
				} else {
					$positionvalue = isset($previous) ? qa_lang_html_sub('admin/after_x', qa_html($previous['title'])) : qa_lang_html('admin/first');
					$positionoptions[1 + @max(array_keys($positionoptions))] = $positionvalue;
				}

				$qa_content['form']['fields']['position'] = array(
					'id' => 'position_display',
					'tags' => 'name="position"',
					'label' => qa_lang_html('admin/position'),
					'type' => 'select',
					'options' => $positionoptions,
					'value' => $positionvalue,
				);

				if (isset($editcategory['categoryid'])) {
					$catdepth = count(qa_category_path($categories, $editcategory['categoryid']));

					if ($catdepth < QA_CATEGORY_DEPTH) {
						$childrenhtml = '';

						foreach ($categories as $category) {
							if (!strcmp($category['parentid'], $editcategory['categoryid'])) {
								$childrenhtml .= (strlen($childrenhtml) ? ', ' : '') .
									'<a href="' . qa_path_html(qa_request(), array('edit' => $category['categoryid'])) . '">' . qa_html($category['title']) . '</a>' .
									' (' . $category['qcount'] . ')';
							}
						}

						if (!strlen($childrenhtml))
							$childrenhtml = qa_lang_html('admin/category_no_subs');

						$childrenhtml .= ' - <a href="' . qa_path_html(qa_request(), array('addsub' => $editcategory['categoryid'])) .
							'" style="white-space: nowrap;"><b>' . qa_lang_html('admin/category_add_sub') . '</b></a>';

						$qa_content['form']['fields']['children'] = array(
							'id' => 'children_display',
							'label' => qa_lang_html('admin/category_subs'),
							'type' => 'static',
							'value' => $childrenhtml,
						);
					} else {
						$qa_content['form']['fields']['name']['note'] = qa_lang_html_sub('admin/category_no_add_subs_x', QA_CATEGORY_DEPTH);
					}
				}
			}
		} else {
			$qa_content['form'] = array(
				'tags' => 'method="post" action="' . qa_path_html(qa_request()) . '"',

				'ok' => $savedoptions ? qa_lang_html('admin/options_saved') : null,

				'style' => 'tall',

				'fields' => array(
					'intro' => array(
						'label' => qa_lang_html('admin/categories_introduction'),
						'type' => 'static',
					),
				),

				'buttons' => array(
					'save' => array(
						'tags' => 'name="dosaveoptions" id="dosaveoptions"',
						'label' => qa_lang_html('main/save_button'),
					),

					'add' => array(
						'tags' => 'name="doaddcategory"',
						'label' => qa_lang_html('admin/add_category_button'),
					),
				),

				'hidden' => array(
					'code' => qa_get_form_security_code('admin/categories'),
				),
			);

			if (count($categories)) {
				unset($qa_content['form']['fields']['intro']);

				$navcategoryhtml = '';

				foreach ($categories as $category) {
					if (!isset($category['parentid'])) {
						$qcountHtml = $category['qcount'] == 1
							? qa_lang_html_sub('main/1_question', '1', '1')
							: qa_lang_html_sub('main/x_questions', qa_format_number($category['qcount']));
						$navcategoryhtml .=
							'<a href="' . qa_path_html('admin/categories', array('edit' => $category['categoryid'])) . '">' .
							qa_html($category['title']) . '</a> - ' . $qcountHtml . '<br/>';
					}
				}

				$qa_content['form']['fields']['nav'] = array(
					'label' => qa_lang_html('admin/top_level_categories'),
					'type' => 'static',
					'value' => $navcategoryhtml,
				);

				$qa_content['form']['fields']['allow_no_category'] = array(
					'label' => qa_lang_html('options/allow_no_category'),
					'tags' => 'name="option_allow_no_category"',
					'type' => 'checkbox',
					'value' => qa_opt('allow_no_category'),
				);

				if (!qa_opt('allow_no_category')) {
					$nocatcount = qa_db_count_categoryid_qs(null);

					if ($nocatcount) {
						$qa_content['form']['fields']['allow_no_category']['error'] =
							strtr(qa_lang_html('admin/category_none_error'), array(
								'^q' => qa_format_number($nocatcount),
								'^1' => '<a href="' . qa_path_html(qa_request(), array('missing' => 1)) . '">',
								'^2' => '</a>',
							));
					}
				}

				$qa_content['form']['fields']['allow_no_sub_category'] = array(
					'label' => qa_lang_html('options/allow_no_sub_category'),
					'tags' => 'name="option_allow_no_sub_category"',
					'type' => 'checkbox',
					'value' => qa_opt('allow_no_sub_category'),
				);
			} else {
				unset($qa_content['form']['buttons']['save']);
			}
		}

		if (qa_get('recalc')) {
			$qa_content['form']['ok'] = '<span id="recalc_ok">' . qa_lang_html('admin/recalc_categories') . '</span>';
			$qa_content['form']['hidden']['code_recalc'] = qa_get_form_security_code('admin/recalc');

			$qa_content['script_rel'][] = 'qa-content/qa-admin.js?' . QA_VERSION;
			$qa_content['script_var']['qa_warning_recalc'] = qa_lang('admin/stop_recalc_warning');

			$qa_content['script_onloads'][] = array(
				"qa_recalc_click('dorecalccategories', document.getElementById('dosaveoptions'), null, 'recalc_ok');"
			);
		}

		$qa_content['navigation']['sub'] = qa_admin_sub_navigation();


		return $qa_content;
	}
}