admin.php 27.1 KB
Newer Older
Scott committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
<?php
/*
	Question2Answer by Gideon Greenspan and contributors
	http://www.question2answer.org/

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

Scott committed
22
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
23
	header('Location: ../../');
Scott committed
24 25
	exit;
}
Scott committed
26 27


Scott committed
28 29 30
/**
 * 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
Scott committed
31
 * @param array $qa_content
Scott committed
32
 * @return bool
Scott committed
33 34 35 36
 */
function qa_admin_check_privileges(&$qa_content)
{
	if (!qa_is_logged_in()) {
Scott committed
37
		require_once QA_INCLUDE_DIR . 'app/format.php';
Scott committed
38

Scott committed
39
		$qa_content = qa_content_prepare();
Scott committed
40

Scott committed
41 42
		$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());
Scott committed
43

Scott committed
44
		return false;
Scott committed
45

Scott committed
46 47
	} elseif (qa_get_logged_in_level() < QA_USER_LEVEL_ADMIN) {
		$qa_content = qa_content_prepare();
Scott committed
48

Scott committed
49 50
		$qa_content['title'] = qa_lang_html('admin/admin_title');
		$qa_content['error'] = qa_lang_html('admin/no_privileges');
Scott committed
51

Scott committed
52
		return false;
Scott committed
53 54
	}

Scott committed
55 56 57 58 59 60
	return true;
}


/**
 *	Return a sorted array of available languages, [short code] => [long name]
Scott committed
61
 *	@return array
Scott committed
62 63 64 65
 */
function qa_admin_language_options()
{
	if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
Scott committed
66

67
	/**
Scott committed
68 69
	 * @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.
70
	 */
Scott committed
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
	$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);
Scott committed
129
		if (isset($metadata['name'])) {
Scott committed
130
			$options[$code] = $metadata['name'];
Scott committed
131 132
		} elseif (isset($codetolanguage[$code])) {
			// otherwise use an entry from above
Scott committed
133
			$options[$code] = $codetolanguage[$code];
Scott committed
134
		}
Scott committed
135
	}
Scott committed
136

Scott committed
137 138 139 140 141 142 143
	asort($options, SORT_STRING);
	return $options;
}


/**
 * Return a sorted array of available themes, [theme name] => [theme name]
Scott committed
144
 * @return array
Scott committed
145 146 147 148 149 150 151 152 153 154 155
 */
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
156
			$contents = @file_get_contents($directory . '/qa-styles.css', false, null, 0, 8192);
Scott committed
157
			$metadata = qa_addon_metadata($contents, 'Theme');
Scott committed
158
		}
Scott committed
159
		$options[$theme] = isset($metadata['name']) ? $metadata['name'] : $theme;
Scott committed
160 161
	}

Scott committed
162 163 164 165 166 167 168
	asort($options, SORT_STRING);
	return $options;
}


/**
 * Return an array of widget placement options, with keys matching the database value
Scott committed
169
 * @return array
Scott committed
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
 */
function qa_admin_place_options()
{
	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_low'),
		'SB' => qa_lang_html('options/place_side_last'),
	);
}


/**
 * Return an array of page size options up to $maximum, [page size] => [page size]
Scott committed
192
 * @param int $maximum
Scott committed
193
 * @return array
Scott committed
194 195 196
 */
function qa_admin_page_size_options($maximum)
{
Scott committed
197
	$rawoptions = array(5, 10, 15, 20, 25, 30, 40, 50, 60, 80, 100, 120, 150, 200, 250, 300, 400, 500, 600, 800, 1000);
Scott committed
198

Scott committed
199
	$options = array();
Scott committed
200
	foreach ($rawoptions as $rawoption) {
Scott committed
201
		if ($rawoption > $maximum)
Scott committed
202 203
			break;

Scott committed
204
		$options[$rawoption] = $rawoption;
Scott committed
205
	}
Scott committed
206

Scott committed
207 208 209 210 211 212
	return $options;
}


/**
 * Return an array of options representing matching precision, [value] => [label]
Scott committed
213
 * @return array
Scott committed
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
 */
function qa_admin_match_options()
{
	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'),
	);
}


/**
 * Return an array of options representing permission restrictions, [value] => [label]
 * ranging from $widest to $narrowest. Set $doconfirms to whether email confirmations are on
Scott committed
230 231
 * @param int $widest
 * @param int $narrowest
Scott committed
232 233 234
 * @param bool $doconfirms
 * @param bool $dopoints
 * @return array
Scott committed
235
 */
Scott committed
236
function qa_admin_permit_options($widest, $narrowest, $doconfirms = true, $dopoints = true)
Scott committed
237
{
Scott committed
238
	require_once QA_INCLUDE_DIR . 'app/options.php';
Scott committed
239

Scott committed
240
	$options = array(
Scott committed
241 242 243 244 245 246 247 248 249 250 251 252 253 254
		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'),
	);

Scott committed
255 256
	foreach ($options as $key => $label) {
		if ($key < $narrowest || $key > $widest)
Scott committed
257
			unset($options[$key]);
Scott committed
258
	}
Scott committed
259 260 261 262

	if (!$doconfirms) {
		unset($options[QA_PERMIT_CONFIRMED]);
		unset($options[QA_PERMIT_POINTS_CONFIRMED]);
Scott committed
263 264
	}

Scott committed
265 266 267 268 269
	if (!$dopoints) {
		unset($options[QA_PERMIT_POINTS]);
		unset($options[QA_PERMIT_POINTS_CONFIRMED]);
		unset($options[QA_PERMIT_APPROVED_POINTS]);
	}
Scott committed
270

Scott committed
271 272 273
	if (QA_FINAL_EXTERNAL_USERS || !qa_opt('moderate_users')) {
		unset($options[QA_PERMIT_APPROVED]);
		unset($options[QA_PERMIT_APPROVED_POINTS]);
Scott committed
274 275
	}

Scott committed
276 277
	return $options;
}
Scott committed
278 279


Scott committed
280 281
/**
 * Return the sub navigation structure common to admin pages
Scott committed
282
 * @return array
Scott committed
283 284 285 286
 */
function qa_admin_sub_navigation()
{
	if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
Scott committed
287

Scott committed
288 289
	$navigation = array();
	$level = qa_get_logged_in_level();
Scott committed
290

Scott committed
291 292
	if ($level >= QA_USER_LEVEL_ADMIN) {
		$navigation['admin/general'] = array(
Scott committed
293 294 295
			'label' => qa_lang_html('admin/general_title'),
			'url' => qa_path_html('admin/general'),
		);
Scott committed
296

Scott committed
297
		$navigation['admin/emails'] = array(
Scott committed
298 299 300
			'label' => qa_lang_html('admin/emails_title'),
			'url' => qa_path_html('admin/emails'),
		);
Scott committed
301

Scott committed
302
		$navigation['admin/users'] = array(
Scott committed
303 304 305
			'label' => qa_lang_html('admin/users_title'),
			'url' => qa_path_html('admin/users'),
			'selected_on' => array('admin/users$', 'admin/userfields$', 'admin/usertitles$'),
Scott committed
306 307
		);

Scott committed
308
		$navigation['admin/layout'] = array(
Scott committed
309 310 311
			'label' => qa_lang_html('admin/layout_title'),
			'url' => qa_path_html('admin/layout'),
		);
Scott committed
312

Scott committed
313
		$navigation['admin/posting'] = array(
Scott committed
314 315
			'label' => qa_lang_html('admin/posting_title'),
			'url' => qa_path_html('admin/posting'),
Scott committed
316 317
		);

Scott committed
318
		$navigation['admin/viewing'] = array(
Scott committed
319 320 321
			'label' => qa_lang_html('admin/viewing_title'),
			'url' => qa_path_html('admin/viewing'),
		);
Scott committed
322

Scott committed
323
		$navigation['admin/lists'] = array(
Scott committed
324 325 326
			'label' => qa_lang_html('admin/lists_title'),
			'url' => qa_path_html('admin/lists'),
		);
Scott committed
327

Scott committed
328
		if (qa_using_categories())
Scott committed
329
			$navigation['admin/categories'] = array(
Scott committed
330 331 332
				'label' => qa_lang_html('admin/categories_title'),
				'url' => qa_path_html('admin/categories'),
			);
Scott committed
333

Scott committed
334
		$navigation['admin/permissions'] = array(
Scott committed
335 336 337
			'label' => qa_lang_html('admin/permissions_title'),
			'url' => qa_path_html('admin/permissions'),
		);
Scott committed
338

Scott committed
339
		$navigation['admin/pages'] = array(
Scott committed
340 341 342
			'label' => qa_lang_html('admin/pages_title'),
			'url' => qa_path_html('admin/pages'),
		);
Scott committed
343

Scott committed
344
		$navigation['admin/feeds'] = array(
Scott committed
345 346 347
			'label' => qa_lang_html('admin/feeds_title'),
			'url' => qa_path_html('admin/feeds'),
		);
Scott committed
348

Scott committed
349
		$navigation['admin/points'] = array(
Scott committed
350 351 352
			'label' => qa_lang_html('admin/points_title'),
			'url' => qa_path_html('admin/points'),
		);
Scott committed
353

Scott committed
354
		$navigation['admin/spam'] = array(
Scott committed
355 356 357
			'label' => qa_lang_html('admin/spam_title'),
			'url' => qa_path_html('admin/spam'),
		);
Scott committed
358

359 360 361 362
		$navigation['admin/caching'] = array(
			'label' => qa_lang_html('admin/caching_title'),
			'url' => qa_path_html('admin/caching'),
		);
Scott committed
363

Scott committed
364
		$navigation['admin/stats'] = array(
Scott committed
365 366 367
			'label' => qa_lang_html('admin/stats_title'),
			'url' => qa_path_html('admin/stats'),
		);
Scott committed
368

Scott committed
369
		if (!QA_FINAL_EXTERNAL_USERS)
Scott committed
370
			$navigation['admin/mailing'] = array(
Scott committed
371 372
				'label' => qa_lang_html('admin/mailing_title'),
				'url' => qa_path_html('admin/mailing'),
Scott committed
373 374
			);

Scott committed
375
		$navigation['admin/plugins'] = array(
Scott committed
376 377 378 379
			'label' => qa_lang_html('admin/plugins_title'),
			'url' => qa_path_html('admin/plugins'),
		);
	}
Scott committed
380

Scott committed
381
	if (!qa_user_maximum_permit_error('permit_moderate')) {
382
		$count = qa_user_permit_error('permit_moderate') ? 0 : (int)qa_opt('cache_queuedcount'); // if only in some categories don't show cached count
Scott committed
383

Scott committed
384
		$navigation['admin/moderate'] = array(
385
			'label' => qa_lang_html_sub('admin/moderate_title', '<span class="qa-nav-sub-counter-moderate">' . qa_html(qa_format_number($count)) . '</span>'),
Scott committed
386 387 388
			'url' => qa_path_html('admin/moderate'),
		);
	}
Scott committed
389

Scott committed
390
	if (qa_opt('flagging_of_posts') && !qa_user_maximum_permit_error('permit_hide_show')) {
391
		$count = qa_user_permit_error('permit_hide_show') ? 0 : (int)qa_opt('cache_flaggedcount'); // if only in some categories don't show cached count
Scott committed
392

Scott committed
393
		$navigation['admin/flagged'] = array(
394
			'label' => qa_lang_html_sub('admin/flagged_title', '<span class="qa-nav-sub-counter-flagged">' . qa_html(qa_format_number($count)) . '</span>'),
Scott committed
395 396 397
			'url' => qa_path_html('admin/flagged'),
		);
	}
Scott committed
398

Scott committed
399
	if (!qa_user_maximum_permit_error('permit_hide_show') || !qa_user_maximum_permit_error('permit_delete_hidden')) {
400 401
		$count = qa_user_permit_error('permit_hide_show') ? 0 : (int)qa_opt('cache_hiddencount');

Scott committed
402
		$navigation['admin/hidden'] = array(
403
			'label' => qa_lang_html_sub('admin/hidden_title', '<span class="qa-nav-sub-counter-hidden">' . qa_html(qa_format_number($count)) . '</span>'),
Scott committed
404 405
			'url' => qa_path_html('admin/hidden'),
		);
Scott committed
406
	}
Scott committed
407

Scott committed
408
	if (!QA_FINAL_EXTERNAL_USERS && qa_opt('moderate_users') && $level >= QA_USER_LEVEL_MODERATOR) {
409
		$count = (int)qa_opt('cache_uapprovecount');
Scott committed
410

Scott committed
411
		$navigation['admin/approve'] = array(
412
			'label' => qa_lang_html_sub('admin/approve_users_title', '<span class="qa-nav-sub-counter-approve">' . qa_html(qa_format_number($count)) . '</span>'),
Scott committed
413 414 415
			'url' => qa_path_html('admin/approve'),
		);
	}
Scott committed
416

Scott committed
417 418
	return $navigation;
}
Scott committed
419 420


Scott committed
421 422
/**
 * Return the error that needs to displayed on all admin pages, or null if none
Scott committed
423
 * @return string|null
Scott committed
424 425 426
 */
function qa_admin_page_error()
{
Scott committed
427 428
	if (file_exists(QA_INCLUDE_DIR . 'db/install.php')) // file can be removed for extra security
		include_once QA_INCLUDE_DIR . 'db/install.php';
429

Scott committed
430
	if (defined('QA_DB_VERSION_CURRENT') && qa_opt('db_version') < QA_DB_VERSION_CURRENT && qa_get_logged_in_level() >= QA_USER_LEVEL_ADMIN) {
Scott committed
431 432 433 434
		return strtr(
			qa_lang_html('admin/upgrade_db'),

			array(
Scott committed
435
				'^1' => '<a href="' . qa_path_html('install') . '">',
Scott committed
436 437 438
				'^2' => '</a>',
			)
		);
Scott committed
439

Scott committed
440
	} elseif (defined('QA_BLOBS_DIRECTORY') && !is_writable(QA_BLOBS_DIRECTORY)) {
Scott committed
441
		return qa_lang_html_sub('admin/blobs_directory_error', qa_html(QA_BLOBS_DIRECTORY));
Scott committed
442
	}
Scott committed
443

Scott committed
444
	return null;
Scott committed
445
}
Scott committed
446 447


Scott committed
448 449
/**
 * Return an HTML fragment to display for a URL test which has passed
Scott committed
450
 * @return string
Scott committed
451 452 453 454 455
 */
function qa_admin_url_test_html()
{
	return '; font-size:9px; color:#060; font-weight:bold; font-family:arial,sans-serif; border-color:#060;">OK<';
}
Scott committed
456 457


Scott committed
458 459
/**
 * Returns whether a URL path beginning with $requestpart is reserved by the engine or a plugin page module
Scott committed
460
 * @param string $requestpart
Scott committed
461
 * @return bool
Scott committed
462 463 464
 */
function qa_admin_is_slug_reserved($requestpart)
{
Scott committed
465 466
	$requestpart = trim(strtolower($requestpart));
	$routing = qa_page_routing();
Scott committed
467

Scott committed
468
	if (isset($routing[$requestpart]) || isset($routing[$requestpart . '/']) || is_numeric($requestpart))
Scott committed
469
		return true;
Scott committed
470

Scott committed
471
	$pathmap = qa_get_request_map();
Scott committed
472

Scott committed
473
	foreach ($pathmap as $mappedrequest) {
Scott committed
474 475
		if (trim(strtolower($mappedrequest)) == $requestpart)
			return true;
Scott committed
476
	}
Scott committed
477

Scott committed
478 479 480 481 482 483 484 485 486
	switch ($requestpart) {
		case '':
		case 'qa':
		case 'feed':
		case 'install':
		case 'url':
		case 'image':
		case 'ajax':
			return true;
Scott committed
487 488
	}

Scott committed
489 490
	$pagemodules = qa_load_modules_with('page', 'match_request');
	foreach ($pagemodules as $pagemodule) {
Scott committed
491 492
		if ($pagemodule->match_request($requestpart))
			return true;
Scott committed
493
	}
Scott committed
494

Scott committed
495 496
	return false;
}
Scott committed
497 498


Scott committed
499 500 501
/**
 * Returns true if admin (hidden/flagged/approve/moderate) page $action performed on $entityid is permitted by the
 * logged in user and was processed successfully
Scott committed
502 503
 * @param int $entityid
 * @param string $action
Scott committed
504
 * @return bool
Scott committed
505 506 507
 */
function qa_admin_single_click($entityid, $action)
{
Scott committed
508
	$userid = qa_get_logged_in_userid();
Scott committed
509

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

Scott committed
513
		$useraccount = qa_db_select_with_pending(qa_db_user_account_selectspec($entityid, true));
Scott committed
514

Scott committed
515
		if (isset($useraccount) && qa_get_logged_in_level() >= QA_USER_LEVEL_MODERATOR) {
Scott committed
516 517
			switch ($action) {
				case 'userapprove':
Scott committed
518 519
					if ($useraccount['level'] <= QA_USER_LEVEL_APPROVED) { // don't demote higher level users
						require_once QA_INCLUDE_DIR . 'app/users-edit.php';
Scott committed
520 521 522 523
						qa_set_user_level($useraccount['userid'], $useraccount['handle'], QA_USER_LEVEL_APPROVED, $useraccount['level']);
						return true;
					}
					break;
Scott committed
524

Scott committed
525
				case 'userblock':
Scott committed
526
					require_once QA_INCLUDE_DIR . 'app/users-edit.php';
Scott committed
527 528 529 530
					qa_set_user_blocked($useraccount['userid'], $useraccount['handle'], true);
					return true;
					break;
			}
Scott committed
531
		}
Scott committed
532

Scott committed
533
	} else { // something to do with a post
Scott committed
534
		require_once QA_INCLUDE_DIR . 'app/posts.php';
Scott committed
535

Scott committed
536
		$post = qa_post_get_full($entityid);
Scott committed
537

Scott committed
538
		if (isset($post)) {
Scott committed
539
			$queued = (substr($post['type'], 1) == '_QUEUED');
Scott committed
540

Scott committed
541 542 543
			switch ($action) {
				case 'approve':
					if ($queued && !qa_user_post_permit_error('permit_moderate', $post)) {
544
						qa_post_set_status($entityid, QA_POST_STATUS_NORMAL, $userid);
Scott committed
545 546 547
						return true;
					}
					break;
Scott committed
548

Scott committed
549 550
				case 'reject':
					if ($queued && !qa_user_post_permit_error('permit_moderate', $post)) {
551
						qa_post_set_status($entityid, QA_POST_STATUS_HIDDEN, $userid);
Scott committed
552 553 554
						return true;
					}
					break;
Scott committed
555

Scott committed
556
				case 'hide':
Scott committed
557
					if (!$queued && !qa_user_post_permit_error('permit_hide_show', $post)) {
558
						qa_post_set_status($entityid, QA_POST_STATUS_HIDDEN, $userid);
Scott committed
559 560 561
						return true;
					}
					break;
Scott committed
562

Scott committed
563 564
				case 'reshow':
					if ($post['hidden'] && !qa_user_post_permit_error('permit_hide_show', $post)) {
565
						qa_post_set_status($entityid, QA_POST_STATUS_NORMAL, $userid);
Scott committed
566 567 568
						return true;
					}
					break;
Scott committed
569

Scott committed
570 571 572 573 574 575
				case 'delete':
					if ($post['hidden'] && !qa_user_post_permit_error('permit_delete_hidden', $post)) {
						qa_post_delete($entityid);
						return true;
					}
					break;
Scott committed
576

Scott committed
577
				case 'clearflags':
Scott committed
578
					require_once QA_INCLUDE_DIR . 'app/votes.php';
Scott committed
579

Scott committed
580 581
					if (!qa_user_post_permit_error('permit_hide_show', $post)) {
						qa_flags_clear_all($post, $userid, qa_get_logged_in_handle(), null);
Scott committed
582
						return true;
Scott committed
583 584
					}
					break;
Scott committed
585 586 587 588
			}
		}
	}

Scott committed
589 590 591 592
	return false;
}


593 594 595
/**
 * Returns true if admin (hidden/flagged/approve/moderate) page $action performed on $entityid is permitted by the
 * logged in user and was processed successfully
Scott committed
596 597
 * @param int $entityid
 * @param string $action
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
 * @return array
 */
function qa_admin_single_click_array($entityid, $action)
{
	$userid = qa_get_logged_in_userid();

	$response = array();

	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
						$response['result'] = 'error';
						$response['error']['type'] = 'user-already-approved';
						$response['error']['message'] = qa_lang_html('main/general_error');
					} else {
						require_once QA_INCLUDE_DIR . 'app/users-edit.php';
						qa_set_user_level($useraccount['userid'], $useraccount['handle'], QA_USER_LEVEL_APPROVED, $useraccount['level']);

						$response['result'] = 'success';
623 624 625 626 627 628 629 630 631 632
						$response['domUpdates'] = array(
							array(
								'selector' => '.qa-nav-sub-counter-approve',
								'html' => max((int)qa_opt('cache_uapprovecount') - 1, 0),
							),
							array(
								'selector' => '#p' . $entityid,
								'action' => 'conceal',
							),
						);
633 634 635 636 637 638 639 640
					}
					break;

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

					$response['result'] = 'success';
641 642 643 644 645 646 647 648 649 650
					$response['domUpdates'] = array(
						array(
							'selector' => '.qa-nav-sub-counter-approve',
							'html' => max((int)qa_opt('cache_uapprovecount') - 1, 0),
						),
						array(
							'selector' => '#p' . $entityid,
							'action' => 'conceal',
						),
					);
651 652 653 654 655 656 657 658
					break;

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

659
		$post = qa_db_single_select(qa_db_full_post_selectspec(null, $entityid));
660

661 662
		// Handle non-existent posts
		if ($post === null) {
663 664 665
			switch ($action) {
				case 'approve':
				case 'reject':
666
					$entityCount = (int)qa_opt('cache_queuedcount');
667
					$selector = '.qa-nav-sub-counter-moderate';
668 669 670
					break;
				case 'reshow':
				case 'delete':
671
					$entityCount = (int)qa_opt('cache_hiddencount');
672
					$selector = '.qa-nav-sub-counter-hidden';
673 674 675
					break;
				case 'hide':
				case 'clearflags':
676
					$entityCount = (int)qa_opt('cache_flaggedcount');
677 678 679 680 681 682
					$selector = '.qa-nav-sub-counter-flagged';
					break;
				default:
					$selector = '';
					$entityCount = 0;
			}
683

684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701
			return array(
				'result' => 'error',
				'error' => array(
					'type' => 'post-not-found',
					'message' => qa_lang_html('main/general_error'),
				),
				'domUpdates' => array(
					array(
						'selector' => $selector,
						'html' => $entityCount,
					),
					array(
						'selector' => '#p' . $entityid,
						'action' => 'conceal',
					),
				),
			);
		}
702

703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774
		$queued = (substr($post['type'], 1) == '_QUEUED');

		switch ($action) {
			case 'approve':
			case 'reject':
				$entityCount = (int)qa_opt('cache_queuedcount');
				if (!$queued) {
					$response['result'] = 'error';
					$response['error']['type'] = 'post-not-queued';
					$response['error']['message'] = qa_lang_html('main/general_error');
					$response['domUpdates'] = array(
						array(
							'selector' => '.qa-nav-sub-counter-moderate',
							'html' => $entityCount,
						),
						array(
							'selector' => '#p' . $entityid,
							'action' => 'conceal',
						),
					);
				} elseif (qa_user_post_permit_error('permit_moderate', $post) !== false) {
					$response['result'] = 'error';
					$response['error']['type'] = 'no-permission';
					$response['error']['message'] = qa_lang_html('users/no_permission');
					$response['error']['severity'] = 'fatal';
				} else {
					$postStatus = $action === 'approve'
						? QA_POST_STATUS_NORMAL
						: QA_POST_STATUS_HIDDEN; // 'reject'
					qa_post_set_status($entityid, $postStatus, $userid);

					$response['result'] = 'success';
					$response['domUpdates'] = array(
						array(
							'selector' => '.qa-nav-sub-counter-moderate',
							'html' => max($entityCount - 1, 0),
						),
						array(
							'selector' => '#p' . $entityid,
							'action' => 'conceal',
						),
					);
				}
				break;

			case 'reshow':
			case 'delete':
				$entityCount = (int)qa_opt('cache_hiddencount');
				if (!$post['hidden']) {
					$response['result'] = 'error';
					$response['error']['type'] = 'post-not-hidden';
					$response['error']['message'] = qa_lang_html('main/general_error');
					$response['domUpdates'] = array(
						array(
							'selector' => '.qa-nav-sub-counter-hidden',
							'html' => $entityCount,
						),
						array(
							'selector' => '#p' . $entityid,
							'action' => 'conceal',
						),
					);
				} elseif (qa_user_post_permit_error('permit_hide_show', $post) !== false) {
					$response['result'] = 'error';
					$response['error']['type'] = 'no-permission';
					$response['error']['message'] = qa_lang_html('users/no_permission');
					$response['error']['severity'] = 'fatal';
				} else {
					if ($action === 'reshow') {
						qa_post_set_status($entityid, QA_POST_STATUS_NORMAL, $userid);
					} else { // 'delete'
						qa_post_delete($entityid);
775 776
					}

777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
					$response['result'] = 'success';
					$response['domUpdates'] = array(
						array(
							'selector' => '.qa-nav-sub-counter-hidden',
							'html' => max($entityCount - 1, 0),
						),
						array(
							'selector' => '#p' . $entityid,
							'action' => 'conceal',
						),
					);
				}
				break;

			case 'hide':
			case 'clearflags':
				$entityCount = (int)qa_opt('cache_flaggedcount');
				if ($action === 'hide' && $queued) {
					$response['result'] = 'error';
					$response['error']['type'] = 'post-queued';
					$response['error']['message'] = qa_lang_html('main/general_error');
					$response['domUpdates'] = array(
						array(
							'selector' => '.qa-nav-sub-counter-flagged',
							'html' => $entityCount,
						),
						array(
							'selector' => '#p' . $entityid,
							'action' => 'conceal',
						),
					);
				} elseif (qa_user_post_permit_error('permit_hide_show', $post) !== false) {
					$response['result'] = 'error';
					$response['error']['type'] = 'no-permission';
					$response['error']['message'] = qa_lang_html('users/no_permission');
					$response['error']['severity'] = 'fatal';
				} else {
					if ($action === 'hide') {
						qa_post_set_status($entityid, QA_POST_STATUS_HIDDEN, $userid);
					} else { // 'clearflags'
						require_once QA_INCLUDE_DIR . 'app/votes.php';

						qa_flags_clear_all($post, $userid, qa_get_logged_in_handle(), null);
					}

					$response['result'] = 'success';
					$response['domUpdates'] = array(
						array(
							'selector' => '.qa-nav-sub-counter-flagged',
							'html' => max($entityCount - 1, 0),
						),
						array(
							'selector' => '#p' . $entityid,
							'action' => 'conceal',
						),
					);
				}
				break;

			default:
837 838 839 840 841 842 843
		}
	}

	return $response;
}


Scott committed
844 845
/**
 * Checks for a POSTed click on an admin (hidden/flagged/approve/moderate) page, and refresh the page if processed successfully (non Ajax)
Scott committed
846
 * @return string|null
Scott committed
847 848 849
 */
function qa_admin_check_clicks()
{
Scott committed
850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867
	if (!qa_is_http_post()) {
		return null;
	}

	foreach ($_POST as $field => $value) {
		if (strpos($field, 'admin_') !== 0) {
			continue;
		}

		@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());
		}
	}
Scott committed
868

Scott committed
869 870
	return null;
}
Scott committed
871 872


Scott committed
873 874 875 876
/**
 * 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.
Scott committed
877 878
 * @param string $contents
 * @param array $fields
Scott committed
879
 * @return array
Scott committed
880 881 882
 */
function qa_admin_addon_metadata($contents, $fields)
{
Scott committed
883
	$metadata = array();
Scott committed
884

Scott committed
885 886 887 888
	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]);
	}
Scott committed
889

Scott committed
890 891
	return $metadata;
}
892

Scott committed
893

Scott committed
894 895
/**
 * Return the hash code for the plugin in $directory (without trailing slash), used for in-page navigation on admin/plugins page
Scott committed
896
 * @param string $directory
Scott committed
897
 * @return mixed
Scott committed
898 899 900 901 902
 */
function qa_admin_plugin_directory_hash($directory)
{
	$pluginManager = new Q2A_Plugin_PluginManager();
	$hashes = $pluginManager->getHashesForPlugins(array($directory));
Scott committed
903

Scott committed
904 905
	return reset($hashes);
}
Scott committed
906 907


Scott committed
908 909
/**
 * Return the URL (relative to the current page) to navigate to the options panel for the plugin in $directory (without trailing slash)
Scott committed
910
 * @param string $directory
Scott committed
911
 * @return mixed|string
Scott committed
912 913 914 915 916 917
 */
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);
}
Scott committed
918

Scott committed
919

Scott committed
920 921
/**
 * Return the URL (relative to the current page) to navigate to the options panel for plugin module $name of $type
Scott committed
922 923
 * @param string $type
 * @param string $name
Scott committed
924
 * @return mixed|string
Scott committed
925 926 927 928
 */
function qa_admin_module_options_path($type, $name)
{
	$info = qa_get_module_info($type, $name);
Scott committed
929
	$dir = basename($info['directory']);
Scott committed
930

Scott committed
931 932
	return qa_admin_plugin_options_path($dir);
}