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

	File: qa-include/qa-app-admin.php
	Description: Functions used in the admin center pages


	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
*/

	if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
		header('Location: ../');
		exit;
	}


	function qa_admin_check_privileges(&$qa_content)
/*
	Return true if user is logged in with admin privileges. If not, return false
	and set up $qa_content with the appropriate title and error message
*/
	{
		if (!qa_is_logged_in()) {
			require_once QA_INCLUDE_DIR.'app/format.php';

			$qa_content=qa_content_prepare();

			$qa_content['title']=qa_lang_html('admin/admin_title');
			$qa_content['error']=qa_insert_login_links(qa_lang_html('admin/not_logged_in'), qa_request());

			return false;

		} elseif (qa_get_logged_in_level()<QA_USER_LEVEL_ADMIN) {
			$qa_content=qa_content_prepare();

			$qa_content['title']=qa_lang_html('admin/admin_title');
			$qa_content['error']=qa_lang_html('admin/no_privileges');

			return false;
		}

		return true;
	}


	/**
	 *	Return a sorted array of available languages, [short code] => [long name]
	 */
	function qa_admin_language_options()
	{
		if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }

		/**
		 * @deprecated The hardcoded language ids will be removed in favor of language metadata files.
		 * See qa-lang/en-GB directory for a clear example of how to use them.
		 */
		$codetolanguage = array( // 2-letter language codes as per ISO 639-1
			'ar' => 'Arabic - العربية',
			'az' => 'Azerbaijani - Azərbaycanca',
			'bg' => 'Bulgarian - Български',
			'bn' => 'Bengali - বাংলা',
			'ca' => 'Catalan - Català',
			'cs' => 'Czech - Čeština',
			'cy' => 'Welsh - Cymraeg',
			'da' => 'Danish - Dansk',
			'de' => 'German - Deutsch',
			'el' => 'Greek - Ελληνικά',
			'en-GB' => 'English (UK)',
			'es' => 'Spanish - Español',
			'et' => 'Estonian - Eesti',
			'fa' => 'Persian - فارسی',
			'fi' => 'Finnish - Suomi',
			'fr' => 'French - Français',
			'he' => 'Hebrew - עברית',
			'hr' => 'Croatian - Hrvatski',
			'hu' => 'Hungarian - Magyar',
			'id' => 'Indonesian - Bahasa Indonesia',
			'is' => 'Icelandic - Íslenska',
			'it' => 'Italian - Italiano',
			'ja' => 'Japanese - 日本語',
			'ka' => 'Georgian - ქართული ენა',
			'kh' => 'Khmer - ភាសាខ្មែរ',
			'ko' => 'Korean - 한국어',
			'ku-CKB' => 'Kurdish Central - کورد',
			'lt' => 'Lithuanian - Lietuvių',
			'lv' => 'Latvian - Latviešu',
			'nl' => 'Dutch - Nederlands',
			'no' => 'Norwegian - Norsk',
			'pl' => 'Polish - Polski',
			'pt' => 'Portuguese - Português',
			'ro' => 'Romanian - Română',
			'ru' => 'Russian - Русский',
			'sk' => 'Slovak - Slovenčina',
			'sl' => 'Slovenian - Slovenščina',
			'sq' => 'Albanian - Shqip',
			'sr' => 'Serbian - Српски',
			'sv' => 'Swedish - Svenska',
			'th' => 'Thai - ไทย',
			'tr' => 'Turkish - Türkçe',
			'ug' => 'Uyghur - ئۇيغۇرچە',
			'uk' => 'Ukrainian - Українська',
			'uz' => 'Uzbek - ўзбек',
			'vi' => 'Vietnamese - Tiếng Việt',
			'zh-TW' => 'Chinese Traditional - 繁體中文',
			'zh' => 'Chinese Simplified - 简体中文',
		);

		$options = array('' => 'English (US)');

		// find all language folders
		$metadataUtil = new Q2A_Util_Metadata();
		foreach (glob(QA_LANG_DIR . '*', GLOB_ONLYDIR) as $directory) {
			$code = basename($directory);
			$metadata = $metadataUtil->fetchFromAddonPath($directory);
			if (isset($metadata['name']))
				$options[$code] = $metadata['name'];
			// otherwise use an entry from above
			elseif (isset($codetolanguage[$code]))
				$options[$code] = $codetolanguage[$code];
		}

		asort($options, SORT_STRING);
		return $options;
	}


	/**
	 * Return a sorted array of available themes, [theme name] => [theme name]
	 */
	function qa_admin_theme_options()
	{
		if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }

		$metadataUtil = new Q2A_Util_Metadata();
		foreach (glob(QA_THEME_DIR . '*', GLOB_ONLYDIR) as $directory) {
			$theme = basename($directory);
			$metadata = $metadataUtil->fetchFromAddonPath($directory);
			if (empty($metadata)) {
				// limit theme parsing to first 8kB
				$contents = file_get_contents($directory . '/qa-styles.css', false, null, -1, 8192);
				$metadata = qa_addon_metadata($contents, 'Theme');
			}
			$options[$theme] = isset($metadata['name']) ? $metadata['name'] : $theme;
		}

		asort($options, SORT_STRING);
		return $options;
	}


	function qa_admin_place_options()
/*
	Return an array of widget placement options, with keys matching the database value
*/
	{
		return array(
			'FT' => qa_lang_html('options/place_full_top'),
			'FH' => qa_lang_html('options/place_full_below_nav'),
			'FL' => qa_lang_html('options/place_full_below_content'),
			'FB' => qa_lang_html('options/place_full_below_footer'),
			'MT' => qa_lang_html('options/place_main_top'),
			'MH' => qa_lang_html('options/place_main_below_title'),
			'ML' => qa_lang_html('options/place_main_below_lists'),
			'MB' => qa_lang_html('options/place_main_bottom'),
			'ST' => qa_lang_html('options/place_side_top'),
			'SH' => qa_lang_html('options/place_side_below_sidebar'),
			'SL' => qa_lang_html('options/place_side_below_categories'),
			'SB' => qa_lang_html('options/place_side_last'),
		);
	}


	function qa_admin_page_size_options($maximum)
/*
	Return an array of page size options up to $maximum, [page size] => [page size]
*/
	{
		$rawoptions=array(5, 10, 15, 20, 25, 30, 40, 50, 60, 80, 100, 120, 150, 200, 250, 300, 400, 500, 600, 800, 1000);

		$options=array();
		foreach ($rawoptions as $rawoption) {
			if ($rawoption>$maximum)
				break;

			$options[$rawoption]=$rawoption;
		}

		return $options;
	}


	function qa_admin_match_options()
/*
	Return an array of options representing matching precision, [value] => [label]
*/
	{
		return array(
			5 => qa_lang_html('options/match_5'),
			4 => qa_lang_html('options/match_4'),
			3 => qa_lang_html('options/match_3'),
			2 => qa_lang_html('options/match_2'),
			1 => qa_lang_html('options/match_1'),
		);
	}


	function qa_admin_permit_options($widest, $narrowest, $doconfirms=true, $dopoints=true)
/*
	Return an array of options representing permission restrictions, [value] => [label]
	ranging from $widest to $narrowest. Set $doconfirms to whether email confirmations are on
*/
	{
		require_once QA_INCLUDE_DIR.'app/options.php';

		$options=array(
			QA_PERMIT_ALL => qa_lang_html('options/permit_all'),
			QA_PERMIT_USERS => qa_lang_html('options/permit_users'),
			QA_PERMIT_CONFIRMED => qa_lang_html('options/permit_confirmed'),
			QA_PERMIT_POINTS => qa_lang_html('options/permit_points'),
			QA_PERMIT_POINTS_CONFIRMED => qa_lang_html('options/permit_points_confirmed'),
			QA_PERMIT_APPROVED => qa_lang_html('options/permit_approved'),
			QA_PERMIT_APPROVED_POINTS => qa_lang_html('options/permit_approved_points'),
			QA_PERMIT_EXPERTS => qa_lang_html('options/permit_experts'),
			QA_PERMIT_EDITORS => qa_lang_html('options/permit_editors'),
			QA_PERMIT_MODERATORS => qa_lang_html('options/permit_moderators'),
			QA_PERMIT_ADMINS => qa_lang_html('options/permit_admins'),
			QA_PERMIT_SUPERS => qa_lang_html('options/permit_supers'),
		);

		foreach ($options as $key => $label)
			if (($key<$narrowest) || ($key>$widest))
				unset($options[$key]);

		if (!$doconfirms) {
			unset($options[QA_PERMIT_CONFIRMED]);
			unset($options[QA_PERMIT_POINTS_CONFIRMED]);
		}

		if (!$dopoints) {
			unset($options[QA_PERMIT_POINTS]);
			unset($options[QA_PERMIT_POINTS_CONFIRMED]);
			unset($options[QA_PERMIT_APPROVED_POINTS]);
		}

		if (QA_FINAL_EXTERNAL_USERS || !qa_opt('moderate_users')) {
			unset($options[QA_PERMIT_APPROVED]);
			unset($options[QA_PERMIT_APPROVED_POINTS]);
		}

		return $options;
	}


	function qa_admin_sub_navigation()
/*
	Return the sub navigation structure common to admin pages
*/
	{
		if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }

		$navigation=array();
		$level=qa_get_logged_in_level();

		if ($level>=QA_USER_LEVEL_ADMIN) {
			$navigation['admin/general']=array(
				'label' => qa_lang_html('admin/general_title'),
				'url' => qa_path_html('admin/general'),
			);

			$navigation['admin/emails']=array(
				'label' => qa_lang_html('admin/emails_title'),
				'url' => qa_path_html('admin/emails'),
			);

			$navigation['admin/users']=array(
				'label' => qa_lang_html('admin/users_title'),
				'url' => qa_path_html('admin/users'),
				'selected_on' => array('admin/users$', 'admin/userfields$', 'admin/usertitles$'),
			);

			$navigation['admin/layout']=array(
				'label' => qa_lang_html('admin/layout_title'),
				'url' => qa_path_html('admin/layout'),
			);

			$navigation['admin/posting']=array(
				'label' => qa_lang_html('admin/posting_title'),
				'url' => qa_path_html('admin/posting'),
			);

			$navigation['admin/viewing']=array(
				'label' => qa_lang_html('admin/viewing_title'),
				'url' => qa_path_html('admin/viewing'),
			);

			$navigation['admin/lists']=array(
				'label' => qa_lang_html('admin/lists_title'),
				'url' => qa_path_html('admin/lists'),
			);

			if (qa_using_categories())
				$navigation['admin/categories']=array(
					'label' => qa_lang_html('admin/categories_title'),
					'url' => qa_path_html('admin/categories'),
				);

			$navigation['admin/permissions']=array(
				'label' => qa_lang_html('admin/permissions_title'),
				'url' => qa_path_html('admin/permissions'),
			);

			$navigation['admin/pages']=array(
				'label' => qa_lang_html('admin/pages_title'),
				'url' => qa_path_html('admin/pages'),
			);

			$navigation['admin/feeds']=array(
				'label' => qa_lang_html('admin/feeds_title'),
				'url' => qa_path_html('admin/feeds'),
			);

			$navigation['admin/points']=array(
				'label' => qa_lang_html('admin/points_title'),
				'url' => qa_path_html('admin/points'),
			);

			$navigation['admin/spam']=array(
				'label' => qa_lang_html('admin/spam_title'),
				'url' => qa_path_html('admin/spam'),
			);

			$navigation['admin/stats']=array(
				'label' => qa_lang_html('admin/stats_title'),
				'url' => qa_path_html('admin/stats'),
			);

			if (!QA_FINAL_EXTERNAL_USERS)
				$navigation['admin/mailing']=array(
					'label' => qa_lang_html('admin/mailing_title'),
					'url' => qa_path_html('admin/mailing'),
				);

			$navigation['admin/plugins']=array(
				'label' => qa_lang_html('admin/plugins_title'),
				'url' => qa_path_html('admin/plugins'),
			);
		}

		if (!qa_user_maximum_permit_error('permit_moderate')) {
			$count=qa_user_permit_error('permit_moderate') ? null : qa_opt('cache_queuedcount'); // if only in some categories don't show cached count

			$navigation['admin/moderate']=array(
				'label' => qa_lang_html('admin/moderate_title').($count ? (' ('.$count.')') : ''),
				'url' => qa_path_html('admin/moderate'),
			);
		}

		if (qa_opt('flagging_of_posts') && !qa_user_maximum_permit_error('permit_hide_show')) {
			$count=qa_user_permit_error('permit_hide_show') ? null : qa_opt('cache_flaggedcount'); // if only in some categories don't show cached count

			$navigation['admin/flagged']=array(
				'label' => qa_lang_html('admin/flagged_title').($count ? (' ('.$count.')') : ''),
				'url' => qa_path_html('admin/flagged'),
			);
		}

		if ( (!qa_user_maximum_permit_error('permit_hide_show')) || (!qa_user_maximum_permit_error('permit_delete_hidden')) )
			$navigation['admin/hidden']=array(
				'label' => qa_lang_html('admin/hidden_title'),
				'url' => qa_path_html('admin/hidden'),
			);

		if ( (!QA_FINAL_EXTERNAL_USERS) && qa_opt('moderate_users') && ($level>=QA_USER_LEVEL_MODERATOR)) {
			$count=qa_opt('cache_uapprovecount');

			$navigation['admin/approve']=array(
				'label' => qa_lang_html('admin/approve_users_title').($count ? (' ('.$count.')') : ''),
				'url' => qa_path_html('admin/approve'),
			);
		}

		return $navigation;
	}


	function qa_admin_page_error()
/*
	Return the error that needs to displayed on all admin pages, or null if none
*/
	{
		if (file_exists(QA_INCLUDE_DIR.'db/install.php')) // file can be removed for extra security
			include_once QA_INCLUDE_DIR.'db/install.php';

		if (defined('QA_DB_VERSION_CURRENT') && (qa_opt('db_version')<QA_DB_VERSION_CURRENT) && (qa_get_logged_in_level()>=QA_USER_LEVEL_ADMIN))
			return strtr(
				qa_lang_html('admin/upgrade_db'),

				array(
					'^1' => '<a href="'.qa_path_html('install').'">',
					'^2' => '</a>',
				)
			);

		elseif (defined('QA_BLOBS_DIRECTORY') && !is_writable(QA_BLOBS_DIRECTORY))
			return qa_lang_html_sub('admin/blobs_directory_error', qa_html(QA_BLOBS_DIRECTORY));

		else
			return null;
	}


	function qa_admin_url_test_html()
/*
	Return an HTML fragment to display for a URL test which has passed
*/
	{
		return '; font-size:9px; color:#060; font-weight:bold; font-family:arial,sans-serif; border-color:#060;">OK<';
	}


	function qa_admin_is_slug_reserved($requestpart)
/*
	Returns whether a URL path beginning with $requestpart is reserved by the engine or a plugin page module
*/
	{
		$requestpart=trim(strtolower($requestpart));
		$routing=qa_page_routing();

		if (isset($routing[$requestpart]) || isset($routing[$requestpart.'/']) || is_numeric($requestpart))
			return true;

		$pathmap=qa_get_request_map();

		foreach ($pathmap as $mappedrequest)
			if (trim(strtolower($mappedrequest)) == $requestpart)
				return true;

		switch ($requestpart) {
			case '':
			case 'qa':
			case 'feed':
			case 'install':
			case 'url':
			case 'image':
			case 'ajax':
				return true;
		}

		$pagemodules=qa_load_modules_with('page', 'match_request');
		foreach ($pagemodules as $pagemodule)
			if ($pagemodule->match_request($requestpart))
				return true;

		return false;
	}


	function qa_admin_single_click($entityid, $action)
/*
	Returns true if admin (hidden/flagged/approve/moderate) page $action performed on $entityid is permitted by the
	logged in user and was processed successfully
*/
	{
		$userid=qa_get_logged_in_userid();

		if ( (!QA_FINAL_EXTERNAL_USERS) && (($action=='userapprove') || ($action=='userblock')) ) { // approve/block moderated users
			require_once QA_INCLUDE_DIR.'db/selects.php';

			$useraccount=qa_db_select_with_pending(qa_db_user_account_selectspec($entityid, true));

			if ( isset($useraccount) && (qa_get_logged_in_level()>=QA_USER_LEVEL_MODERATOR) )
				switch ($action) {
					case 'userapprove':
						if ($useraccount['level']<=QA_USER_LEVEL_APPROVED) { // don't demote higher level users
							require_once QA_INCLUDE_DIR.'app/users-edit.php';
							qa_set_user_level($useraccount['userid'], $useraccount['handle'], QA_USER_LEVEL_APPROVED, $useraccount['level']);
							return true;
						}
						break;

					case 'userblock':
						require_once QA_INCLUDE_DIR.'app/users-edit.php';
						qa_set_user_blocked($useraccount['userid'], $useraccount['handle'], true);
						return true;
						break;
				}

		} else { // something to do with a post
			require_once QA_INCLUDE_DIR.'app/posts.php';

			$post=qa_post_get_full($entityid);

			if (isset($post)) {
				$queued=(substr($post['type'], 1)=='_QUEUED');

				switch ($action) {
					case 'approve':
						if ($queued && !qa_user_post_permit_error('permit_moderate', $post)) {
							qa_post_set_hidden($entityid, false, $userid);
							return true;
						}
						break;

					case 'reject':
						if ($queued && !qa_user_post_permit_error('permit_moderate', $post)) {
							qa_post_set_hidden($entityid, true, $userid);
							return true;
						}
						break;

					case 'hide':
						if ((!$queued) && !qa_user_post_permit_error('permit_hide_show', $post)) {
							qa_post_set_hidden($entityid, true, $userid);
							return true;
						}
						break;

					case 'reshow':
						if ($post['hidden'] && !qa_user_post_permit_error('permit_hide_show', $post)) {
							qa_post_set_hidden($entityid, false, $userid);
							return true;
						}
						break;

					case 'delete':
						if ($post['hidden'] && !qa_user_post_permit_error('permit_delete_hidden', $post)) {
							qa_post_delete($entityid);
							return true;
						}
						break;

					case 'clearflags':
						require_once QA_INCLUDE_DIR.'app/votes.php';

						if (!qa_user_post_permit_error('permit_hide_show', $post)) {
							qa_flags_clear_all($post, $userid, qa_get_logged_in_handle(), null);
							return true;
						}
						break;
				}
			}
		}

		return false;
	}


	function qa_admin_check_clicks()
/*
	Checks for a POSTed click on an admin (hidden/flagged/approve/moderate) page, and refresh the page if processed successfully (non Ajax)
*/
	{
		if (qa_is_http_post())
			foreach ($_POST as $field => $value)
				if (strpos($field, 'admin_')===0) {
					@list($dummy, $entityid, $action)=explode('_', $field);

					if (strlen($entityid) && strlen($action)) {
						if (!qa_check_form_security_code('admin/click', qa_post_text('code')))
							return qa_lang_html('misc/form_security_again');
						elseif (qa_admin_single_click($entityid, $action))
							qa_redirect(qa_request());
					}
				}

		return null;
	}


	/**
	 * Retrieve metadata information from the $contents of a qa-theme.php or qa-plugin.php file, mapping via $fields.
	 *
	 * @deprecated Deprecated from 1.7; use `qa_addon_metadata($contents, $type)` instead.
	 */
	function qa_admin_addon_metadata($contents, $fields)
	{
		$metadata=array();

		foreach ($fields as $key => $field)
			if (preg_match('/'.str_replace(' ', '[ \t]*', preg_quote($field, '/')).':[ \t]*([^\n\f]*)[\n\f]/i', $contents, $matches))
				$metadata[$key]=trim($matches[1]);

		return $metadata;
	}


	/**
	 * Return the hash code for the plugin in $directory (without trailing slash), used for in-page navigation on admin/plugins page
	 */
	function qa_admin_plugin_directory_hash($directory)
	{
		return md5($directory);
	}


	/**
	 * Return the URL (relative to the current page) to navigate to the options panel for the plugin in $directory (without trailing slash)
	 */
	function qa_admin_plugin_options_path($directory)
	{
		$hash = qa_admin_plugin_directory_hash($directory);
		return qa_path_html('admin/plugins', array('show' => $hash), null, null, $hash);
	}


	/**
	 * Return the URL (relative to the current page) to navigate to the options panel for plugin module $name of $type
	 */
	function qa_admin_module_options_path($type, $name)
	{
		$info = qa_get_module_info($type, $name);
		$dir = rtrim($info['directory'], '/');

		return qa_admin_plugin_options_path($dir);
	}


/*
	Omit PHP closing tag to help avoid accidental output
*/