Commit f24542bb by Scott

Coding style (app posts/admin)

parent 5a94ef80
...@@ -20,18 +20,18 @@ ...@@ -20,18 +20,18 @@
More about this license: http://www.question2answer.org/license.php 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 if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../'); header('Location: ../');
exit; exit;
} }
function qa_admin_check_privileges(&$qa_content) /**
/* * Return true if user is logged in with admin privileges. If not, return false
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
and set up $qa_content with the appropriate title and error message */
*/ function qa_admin_check_privileges(&$qa_content)
{ {
if (!qa_is_logged_in()) { if (!qa_is_logged_in()) {
require_once QA_INCLUDE_DIR.'app/format.php'; require_once QA_INCLUDE_DIR.'app/format.php';
...@@ -52,14 +52,14 @@ ...@@ -52,14 +52,14 @@
} }
return true; return true;
} }
/** /**
* Return a sorted array of available languages, [short code] => [long name] * Return a sorted array of available languages, [short code] => [long name]
*/ */
function qa_admin_language_options() function qa_admin_language_options()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
/** /**
...@@ -133,14 +133,14 @@ ...@@ -133,14 +133,14 @@
asort($options, SORT_STRING); asort($options, SORT_STRING);
return $options; return $options;
} }
/** /**
* Return a sorted array of available themes, [theme name] => [theme name] * Return a sorted array of available themes, [theme name] => [theme name]
*/ */
function qa_admin_theme_options() function qa_admin_theme_options()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$metadataUtil = new Q2A_Util_Metadata(); $metadataUtil = new Q2A_Util_Metadata();
...@@ -157,14 +157,14 @@ ...@@ -157,14 +157,14 @@
asort($options, SORT_STRING); asort($options, SORT_STRING);
return $options; return $options;
} }
function qa_admin_place_options() /**
/* * Return an array of widget placement options, with keys matching the database value
Return an array of widget placement options, with keys matching the database value */
*/ function qa_admin_place_options()
{ {
return array( return array(
'FT' => qa_lang_html('options/place_full_top'), 'FT' => qa_lang_html('options/place_full_top'),
'FH' => qa_lang_html('options/place_full_below_nav'), 'FH' => qa_lang_html('options/place_full_below_nav'),
...@@ -179,14 +179,14 @@ ...@@ -179,14 +179,14 @@
'SL' => qa_lang_html('options/place_side_low'), 'SL' => qa_lang_html('options/place_side_low'),
'SB' => qa_lang_html('options/place_side_last'), '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]
Return an array of page size options up to $maximum, [page size] => [page size] */
*/ function qa_admin_page_size_options($maximum)
{ {
$rawoptions=array(5, 10, 15, 20, 25, 30, 40, 50, 60, 80, 100, 120, 150, 200, 250, 300, 400, 500, 600, 800, 1000); $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(); $options=array();
...@@ -198,14 +198,14 @@ ...@@ -198,14 +198,14 @@
} }
return $options; return $options;
} }
function qa_admin_match_options() /**
/* * Return an array of options representing matching precision, [value] => [label]
Return an array of options representing matching precision, [value] => [label] */
*/ function qa_admin_match_options()
{ {
return array( return array(
5 => qa_lang_html('options/match_5'), 5 => qa_lang_html('options/match_5'),
4 => qa_lang_html('options/match_4'), 4 => qa_lang_html('options/match_4'),
...@@ -213,15 +213,15 @@ ...@@ -213,15 +213,15 @@
2 => qa_lang_html('options/match_2'), 2 => qa_lang_html('options/match_2'),
1 => qa_lang_html('options/match_1'), 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]
Return an array of options representing permission restrictions, [value] => [label] * ranging from $widest to $narrowest. Set $doconfirms to whether email confirmations are on
ranging from $widest to $narrowest. Set $doconfirms to whether email confirmations are on */
*/ function qa_admin_permit_options($widest, $narrowest, $doconfirms=true, $dopoints=true)
{ {
require_once QA_INCLUDE_DIR.'app/options.php'; require_once QA_INCLUDE_DIR.'app/options.php';
$options=array( $options=array(
...@@ -260,14 +260,14 @@ ...@@ -260,14 +260,14 @@
} }
return $options; return $options;
} }
function qa_admin_sub_navigation() /**
/* * Return the sub navigation structure common to admin pages
Return the sub navigation structure common to admin pages */
*/ function qa_admin_sub_navigation()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$navigation=array(); $navigation=array();
...@@ -399,14 +399,14 @@ ...@@ -399,14 +399,14 @@
} }
return $navigation; return $navigation;
} }
function qa_admin_page_error() /**
/* * Return the error that needs to displayed on all admin pages, or null if none
Return the error that needs to displayed on all admin pages, or null if none */
*/ function qa_admin_page_error()
{ {
if (file_exists(QA_INCLUDE_DIR.'db/install.php')) // file can be removed for extra security if (file_exists(QA_INCLUDE_DIR.'db/install.php')) // file can be removed for extra security
include_once QA_INCLUDE_DIR.'db/install.php'; include_once QA_INCLUDE_DIR.'db/install.php';
...@@ -425,23 +425,23 @@ ...@@ -425,23 +425,23 @@
else else
return null; return null;
} }
function qa_admin_url_test_html() /**
/* * Return an HTML fragment to display for a URL test which has passed
Return an HTML fragment to display for a URL test which has passed */
*/ function qa_admin_url_test_html()
{ {
return '; font-size:9px; color:#060; font-weight:bold; font-family:arial,sans-serif; border-color:#060;">OK<'; 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
Returns whether a URL path beginning with $requestpart is reserved by the engine or a plugin page module */
*/ function qa_admin_is_slug_reserved($requestpart)
{ {
$requestpart=trim(strtolower($requestpart)); $requestpart=trim(strtolower($requestpart));
$routing=qa_page_routing(); $routing=qa_page_routing();
...@@ -471,15 +471,15 @@ ...@@ -471,15 +471,15 @@
return true; return true;
return false; 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
Returns true if admin (hidden/flagged/approve/moderate) page $action performed on $entityid is permitted by the * logged in user and was processed successfully
logged in user and was processed successfully */
*/ function qa_admin_single_click($entityid, $action)
{ {
$userid=qa_get_logged_in_userid(); $userid=qa_get_logged_in_userid();
if ( (!QA_FINAL_EXTERNAL_USERS) && (($action=='userapprove') || ($action=='userblock')) ) { // approve/block moderated users if ( (!QA_FINAL_EXTERNAL_USERS) && (($action=='userapprove') || ($action=='userblock')) ) { // approve/block moderated users
...@@ -561,14 +561,14 @@ ...@@ -561,14 +561,14 @@
} }
return false; 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)
Checks for a POSTed click on an admin (hidden/flagged/approve/moderate) page, and refresh the page if processed successfully (non Ajax) */
*/ function qa_admin_check_clicks()
{ {
if (qa_is_http_post()) if (qa_is_http_post())
foreach ($_POST as $field => $value) foreach ($_POST as $field => $value)
if (strpos($field, 'admin_')===0) { if (strpos($field, 'admin_')===0) {
...@@ -583,16 +583,16 @@ ...@@ -583,16 +583,16 @@
} }
return null; return null;
} }
/** /**
* Retrieve metadata information from the $contents of a qa-theme.php or qa-plugin.php file, mapping via $fields. * 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. * @deprecated Deprecated from 1.7; use `qa_addon_metadata($contents, $type)` instead.
*/ */
function qa_admin_addon_metadata($contents, $fields) function qa_admin_addon_metadata($contents, $fields)
{ {
$metadata=array(); $metadata=array();
foreach ($fields as $key => $field) foreach ($fields as $key => $field)
...@@ -600,43 +600,38 @@ ...@@ -600,43 +600,38 @@
$metadata[$key]=trim($matches[1]); $metadata[$key]=trim($matches[1]);
return $metadata; return $metadata;
} }
/** /**
* Return the hash code for the plugin in $directory (without trailing slash), used for in-page navigation on admin/plugins page * 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) function qa_admin_plugin_directory_hash($directory)
{ {
$pluginManager = new Q2A_Plugin_PluginManager(); $pluginManager = new Q2A_Plugin_PluginManager();
$hashes = $pluginManager->getHashesForPlugins(array($directory)); $hashes = $pluginManager->getHashesForPlugins(array($directory));
return reset($hashes); return reset($hashes);
} }
/** /**
* Return the URL (relative to the current page) to navigate to the options panel for the plugin in $directory (without trailing slash) * 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) function qa_admin_plugin_options_path($directory)
{ {
$hash = qa_admin_plugin_directory_hash($directory); $hash = qa_admin_plugin_directory_hash($directory);
return qa_path_html('admin/plugins', array('show' => $hash), null, null, $hash); 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 * 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) function qa_admin_module_options_path($type, $name)
{ {
$info = qa_get_module_info($type, $name); $info = qa_get_module_info($type, $name);
$dir = rtrim($info['directory'], '/'); $dir = rtrim($info['directory'], '/');
return qa_admin_plugin_options_path($dir); return qa_admin_plugin_options_path($dir);
} }
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -20,43 +20,63 @@ ...@@ -20,43 +20,63 @@
More about this license: http://www.question2answer.org/license.php 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 if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../'); header('Location: ../');
exit; exit;
} }
require_once QA_INCLUDE_DIR.'db/maxima.php'; require_once QA_INCLUDE_DIR . 'db/maxima.php';
require_once QA_INCLUDE_DIR.'db/post-create.php'; require_once QA_INCLUDE_DIR . 'db/post-create.php';
require_once QA_INCLUDE_DIR.'db/points.php'; require_once QA_INCLUDE_DIR . 'db/points.php';
require_once QA_INCLUDE_DIR.'db/hotness.php'; require_once QA_INCLUDE_DIR . 'db/hotness.php';
require_once QA_INCLUDE_DIR.'util/string.php'; require_once QA_INCLUDE_DIR . 'util/string.php';
function qa_combine_notify_email($userid, $notify, $email) /**
/* * Return value to store in database combining $notify and $email values entered by user $userid (or null for anonymous)
Return value to store in database combining $notify and $email values entered by user $userid (or null for anonymous) * @param $userid
*/ * @param $notify
{ * @param $email
* @return null|string
*/
function qa_combine_notify_email($userid, $notify, $email)
{
return $notify ? (empty($email) ? (isset($userid) ? '@' : null) : $email) : null; return $notify ? (empty($email) ? (isset($userid) ? '@' : null) : $email) : null;
} }
function qa_question_create($followanswer, $userid, $handle, $cookieid, $title, $content, $format, $text, $tagstring, $notify, $email, /**
$categoryid=null, $extravalue=null, $queued=false, $name=null) * Add a question (application level) - create record, update appropriate counts, index it, send notifications.
/* * If question is follow-on from an answer, $followanswer should contain answer database record, otherwise null.
Add a question (application level) - create record, update appropriate counts, index it, send notifications. * See qa-app-posts.php for a higher-level function which is easier to use.
If question is follow-on from an answer, $followanswer should contain answer database record, otherwise null. * @param $followanswer
See qa-app-posts.php for a higher-level function which is easier to use. * @param $userid
*/ * @param $handle
{ * @param $cookieid
require_once QA_INCLUDE_DIR.'db/selects.php'; * @param $title
* @param $content
$postid=qa_db_post_create($queued ? 'Q_QUEUED' : 'Q', @$followanswer['postid'], $userid, isset($userid) ? null : $cookieid, * @param $format
* @param $text
* @param $tagstring
* @param $notify
* @param $email
* @param $categoryid
* @param $extravalue
* @param bool $queued
* @param $name
* @return mixed
*/
function qa_question_create($followanswer, $userid, $handle, $cookieid, $title, $content, $format, $text, $tagstring, $notify, $email,
$categoryid = null, $extravalue = null, $queued = false, $name = null)
{
require_once QA_INCLUDE_DIR . 'db/selects.php';
$postid = qa_db_post_create($queued ? 'Q_QUEUED' : 'Q', @$followanswer['postid'], $userid, isset($userid) ? null : $cookieid,
qa_remote_ip_address(), $title, $content, $format, $tagstring, qa_combine_notify_email($userid, $notify, $email), qa_remote_ip_address(), $title, $content, $format, $tagstring, qa_combine_notify_email($userid, $notify, $email),
$categoryid, isset($userid) ? null : $name); $categoryid, isset($userid) ? null : $name);
if (isset($extravalue)) { if (isset($extravalue)) {
require_once QA_INCLUDE_DIR.'db/metas.php'; require_once QA_INCLUDE_DIR . 'db/metas.php';
qa_db_postmeta_set($postid, 'qa_q_extra', $extravalue); qa_db_postmeta_set($postid, 'qa_q_extra', $extravalue);
} }
...@@ -89,14 +109,15 @@ ...@@ -89,14 +109,15 @@
)); ));
return $postid; return $postid;
} }
function qa_update_counts_for_q($postid) /**
/* * Perform various common cached count updating operations to reflect changes in the question whose id is $postid
Perform various common cached count updating operations to reflect changes in the question whose id is $postid * @param $postid
*/ */
{ function qa_update_counts_for_q($postid)
{
if (isset($postid)) // post might no longer exist if (isset($postid)) // post might no longer exist
qa_db_category_path_qcount_update(qa_db_post_get_category_path($postid)); qa_db_category_path_qcount_update(qa_db_post_get_category_path($postid));
...@@ -104,63 +125,90 @@ ...@@ -104,63 +125,90 @@
qa_db_unaqcount_update(); qa_db_unaqcount_update();
qa_db_unselqcount_update(); qa_db_unselqcount_update();
qa_db_unupaqcount_update(); qa_db_unupaqcount_update();
} }
function qa_array_filter_by_keys($inarray, $keys) /**
/* * Return an array containing the elements of $inarray whose key is in $keys
Return an array containing the elements of $inarray whose key is in $keys * @param $inarray
*/ * @param $keys
{ * @return array
$outarray=array(); */
function qa_array_filter_by_keys($inarray, $keys)
{
$outarray = array();
foreach ($keys as $key) foreach ($keys as $key) {
if (isset($inarray[$key])) if (isset($inarray[$key]))
$outarray[$key]=$inarray[$key]; $outarray[$key] = $inarray[$key];
}
return $outarray; return $outarray;
} }
function qa_suspend_post_indexing($suspend=true) /**
/* * Suspend the indexing (and unindexing) of posts via qa_post_index(...) and qa_post_unindex(...)
Suspend the indexing (and unindexing) of posts via qa_post_index(...) and qa_post_unindex(...) * if $suspend is true, otherwise reinstate it. A counter is kept to allow multiple calls.
if $suspend is true, otherwise reinstate it. A counter is kept to allow multiple calls. * @param bool $suspend
*/ */
{ function qa_suspend_post_indexing($suspend = true)
{
global $qa_post_indexing_suspended; global $qa_post_indexing_suspended;
$qa_post_indexing_suspended+=($suspend ? 1 : -1); $qa_post_indexing_suspended += ($suspend ? 1 : -1);
} }
function qa_post_index($postid, $type, $questionid, $parentid, $title, $content, $format, $text, $tagstring, $categoryid) /**
/* * Add post $postid (which comes under $questionid) of $type (Q/A/C) to the database index, with $title, $text,
Add post $postid (which comes under $questionid) of $type (Q/A/C) to the database index, with $title, $text, * $tagstring and $categoryid. Calls through to all installed search modules.
$tagstring and $categoryid. Calls through to all installed search modules. * @param $postid
*/ * @param $type
{ * @param $questionid
* @param $parentid
* @param $title
* @param $content
* @param $format
* @param $text
* @param $tagstring
* @param $categoryid
*/
function qa_post_index($postid, $type, $questionid, $parentid, $title, $content, $format, $text, $tagstring, $categoryid)
{
global $qa_post_indexing_suspended; global $qa_post_indexing_suspended;
if ($qa_post_indexing_suspended>0) if ($qa_post_indexing_suspended > 0)
return; return;
// Send through to any search modules for indexing // Send through to any search modules for indexing
$searches=qa_load_modules_with('search', 'index_post'); $searches = qa_load_modules_with('search', 'index_post');
foreach ($searches as $search) foreach ($searches as $search)
$search->index_post($postid, $type, $questionid, $parentid, $title, $content, $format, $text, $tagstring, $categoryid); $search->index_post($postid, $type, $questionid, $parentid, $title, $content, $format, $text, $tagstring, $categoryid);
} }
function qa_answer_create($userid, $handle, $cookieid, $content, $format, $text, $notify, $email, $question, $queued=false, $name=null) /**
/* * Add an answer (application level) - create record, update appropriate counts, index it, send notifications.
Add an answer (application level) - create record, update appropriate counts, index it, send notifications. * $question should contain database record for the question this is an answer to.
$question should contain database record for the question this is an answer to. * See qa-app-posts.php for a higher-level function which is easier to use.
See qa-app-posts.php for a higher-level function which is easier to use. * @param $userid
*/ * @param $handle
{ * @param $cookieid
$postid=qa_db_post_create($queued ? 'A_QUEUED' : 'A', $question['postid'], $userid, isset($userid) ? null : $cookieid, * @param $content
* @param $format
* @param $text
* @param $notify
* @param $email
* @param $question
* @param bool $queued
* @param $name
* @return mixed
*/
function qa_answer_create($userid, $handle, $cookieid, $content, $format, $text, $notify, $email, $question, $queued = false, $name = null)
{
$postid = qa_db_post_create($queued ? 'A_QUEUED' : 'A', $question['postid'], $userid, isset($userid) ? null : $cookieid,
qa_remote_ip_address(), null, $content, $format, null, qa_combine_notify_email($userid, $notify, $email), qa_remote_ip_address(), null, $content, $format, null, qa_combine_notify_email($userid, $notify, $email),
$question['categoryid'], isset($userid) ? null : $name); $question['categoryid'], isset($userid) ? null : $name);
...@@ -170,7 +218,7 @@ ...@@ -170,7 +218,7 @@
qa_db_queuedcount_update(); qa_db_queuedcount_update();
} else { } else {
if ($question['type']=='Q') // don't index answer if parent question is hidden or queued if ($question['type'] == 'Q') // don't index answer if parent question is hidden or queued
qa_post_index($postid, 'A', $question['postid'], $question['postid'], null, $content, $format, $text, null, $question['categoryid']); qa_post_index($postid, 'A', $question['postid'], $question['postid'], null, $content, $format, $text, null, $question['categoryid']);
qa_update_q_counts_for_a($question['postid']); qa_update_q_counts_for_a($question['postid']);
...@@ -191,41 +239,56 @@ ...@@ -191,41 +239,56 @@
)); ));
return $postid; return $postid;
} }
function qa_update_q_counts_for_a($questionid) /**
/* * Perform various common cached count updating operations to reflect changes in an answer of question $questionid
Perform various common cached count updating operations to reflect changes in an answer of question $questionid * @param $questionid
*/ */
{ function qa_update_q_counts_for_a($questionid)
{
qa_db_post_acount_update($questionid); qa_db_post_acount_update($questionid);
qa_db_hotness_update($questionid); qa_db_hotness_update($questionid);
qa_db_acount_update(); qa_db_acount_update();
qa_db_unaqcount_update(); qa_db_unaqcount_update();
qa_db_unupaqcount_update(); qa_db_unupaqcount_update();
} }
function qa_comment_create($userid, $handle, $cookieid, $content, $format, $text, $notify, $email, $question, $parent, $commentsfollows, $queued=false, $name=null) /**
/* * Add a comment (application level) - create record, update appropriate counts, index it, send notifications.
Add a comment (application level) - create record, update appropriate counts, index it, send notifications. * $question should contain database record for the question this is part of (as direct or comment on Q's answer).
$question should contain database record for the question this is part of (as direct or comment on Q's answer). * If this is a comment on an answer, $answer should contain database record for the answer, otherwise null.
If this is a comment on an answer, $answer should contain database record for the answer, otherwise null. * $commentsfollows should contain database records for all previous comments on the same question or answer,
$commentsfollows should contain database records for all previous comments on the same question or answer, * but it can also contain other records that are ignored.
but it can also contain other records that are ignored. * See qa-app-posts.php for a higher-level function which is easier to use.
See qa-app-posts.php for a higher-level function which is easier to use. * @param $userid
*/ * @param $handle
{ * @param $cookieid
require_once QA_INCLUDE_DIR.'app/emails.php'; * @param $content
require_once QA_INCLUDE_DIR.'app/options.php'; * @param $format
require_once QA_INCLUDE_DIR.'app/format.php'; * @param $text
require_once QA_INCLUDE_DIR.'util/string.php'; * @param $notify
* @param $email
* @param $question
* @param $parent
* @param $commentsfollows
* @param bool $queued
* @param $name
* @return mixed
*/
function qa_comment_create($userid, $handle, $cookieid, $content, $format, $text, $notify, $email, $question, $parent, $commentsfollows, $queued = false, $name = null)
{
require_once QA_INCLUDE_DIR . 'app/emails.php';
require_once QA_INCLUDE_DIR . 'app/options.php';
require_once QA_INCLUDE_DIR . 'app/format.php';
require_once QA_INCLUDE_DIR . 'util/string.php';
if (!isset($parent)) if (!isset($parent))
$parent=$question; // for backwards compatibility with old answer parameter $parent = $question; // for backwards compatibility with old answer parameter
$postid=qa_db_post_create($queued ? 'C_QUEUED' : 'C', $parent['postid'], $userid, isset($userid) ? null : $cookieid, $postid = qa_db_post_create($queued ? 'C_QUEUED' : 'C', $parent['postid'], $userid, isset($userid) ? null : $cookieid,
qa_remote_ip_address(), null, $content, $format, null, qa_combine_notify_email($userid, $notify, $email), qa_remote_ip_address(), null, $content, $format, null, qa_combine_notify_email($userid, $notify, $email),
$question['categoryid'], isset($userid) ? null : $name); $question['categoryid'], isset($userid) ? null : $name);
...@@ -235,18 +298,19 @@ ...@@ -235,18 +298,19 @@
qa_db_queuedcount_update(); qa_db_queuedcount_update();
} else { } else {
if ( ($question['type']=='Q') && (($parent['type']=='Q') || ($parent['type']=='A')) ) // only index if antecedents fully visible if (($question['type'] == 'Q') && (($parent['type'] == 'Q') || ($parent['type'] == 'A'))) // only index if antecedents fully visible
qa_post_index($postid, 'C', $question['postid'], $parent['postid'], null, $content, $format, $text, null, $question['categoryid']); qa_post_index($postid, 'C', $question['postid'], $parent['postid'], null, $content, $format, $text, null, $question['categoryid']);
qa_db_points_update_ifuser($userid, 'cposts'); qa_db_points_update_ifuser($userid, 'cposts');
qa_db_ccount_update(); qa_db_ccount_update();
} }
$thread=array(); $thread = array();
foreach ($commentsfollows as $comment) foreach ($commentsfollows as $comment) {
if (($comment['type']=='C') && ($comment['parentid']==$parent['postid'])) // find just those for this parent, fully visible if (($comment['type'] == 'C') && ($comment['parentid'] == $parent['postid'])) // find just those for this parent, fully visible
$thread[]=$comment; $thread[] = $comment;
}
qa_report_event($queued ? 'c_queue' : 'c_post', $userid, $handle, $cookieid, array( qa_report_event($queued ? 'c_queue' : 'c_post', $userid, $handle, $cookieid, array(
'postid' => $postid, 'postid' => $postid,
...@@ -266,9 +330,4 @@ ...@@ -266,9 +330,4 @@
)); ));
return $postid; return $postid;
} }
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -20,34 +20,48 @@ ...@@ -20,34 +20,48 @@
More about this license: http://www.question2answer.org/license.php 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 if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../'); header('Location: ../');
exit; exit;
} }
require_once QA_INCLUDE_DIR.'app/post-create.php'; require_once QA_INCLUDE_DIR . 'app/post-create.php';
require_once QA_INCLUDE_DIR.'app/updates.php'; require_once QA_INCLUDE_DIR . 'app/updates.php';
require_once QA_INCLUDE_DIR.'db/post-create.php'; require_once QA_INCLUDE_DIR . 'db/post-create.php';
require_once QA_INCLUDE_DIR.'db/post-update.php'; require_once QA_INCLUDE_DIR . 'db/post-update.php';
require_once QA_INCLUDE_DIR.'db/points.php'; require_once QA_INCLUDE_DIR . 'db/points.php';
require_once QA_INCLUDE_DIR.'db/hotness.php'; require_once QA_INCLUDE_DIR . 'db/hotness.php';
define('QA_POST_STATUS_NORMAL', 0); define('QA_POST_STATUS_NORMAL', 0);
define('QA_POST_STATUS_HIDDEN', 1); define('QA_POST_STATUS_HIDDEN', 1);
define('QA_POST_STATUS_QUEUED', 2); define('QA_POST_STATUS_QUEUED', 2);
function qa_question_set_content($oldquestion, $title, $content, $format, $text, $tagstring, $notify, $userid, $handle, $cookieid, $extravalue=null, $name=null, $remoderate=false, $silent=false) /**
/* * Change the fields of a question (application level) to $title, $content, $format, $tagstring, $notify, $extravalue
Change the fields of a question (application level) to $title, $content, $format, $tagstring, $notify, $extravalue * and $name, then reindex based on $text. For backwards compatibility if $name is null then the name will not be
and $name, then reindex based on $text. For backwards compatibility if $name is null then the name will not be * changed. Pass the question's database record before changes in $oldquestion and details of the user doing this in
changed. Pass the question's database record before changes in $oldquestion and details of the user doing this in * $userid, $handle and $cookieid. Set $remoderate to true if the question should be requeued for moderation if
$userid, $handle and $cookieid. Set $remoderate to true if the question should be requeued for moderation if * modified. Set $silent to true to not mark the question as edited. Reports event as appropriate. See qa-app-posts.php
modified. Set $silent to true to not mark the question as edited. Reports event as appropriate. See qa-app-posts.php * for a higher-level function which is easier to use.
for a higher-level function which is easier to use. * @param $oldquestion
*/ * @param $title
{ * @param $content
* @param $format
* @param $text
* @param $tagstring
* @param $notify
* @param $userid
* @param $handle
* @param $cookieid
* @param $extravalue
* @param $name
* @param bool $remoderate
* @param bool $silent
*/
function qa_question_set_content($oldquestion, $title, $content, $format, $text, $tagstring, $notify, $userid, $handle, $cookieid, $extravalue = null, $name = null, $remoderate = false, $silent = false)
{
qa_post_unindex($oldquestion['postid']); qa_post_unindex($oldquestion['postid']);
$wasqueued = ($oldquestion['type'] == 'Q_QUEUED'); $wasqueued = ($oldquestion['type'] == 'Q_QUEUED');
...@@ -61,12 +75,12 @@ ...@@ -61,12 +75,12 @@
($titlechanged || $contentchanged) ? QA_UPDATE_CONTENT : QA_UPDATE_TAGS, $name); ($titlechanged || $contentchanged) ? QA_UPDATE_CONTENT : QA_UPDATE_TAGS, $name);
if (isset($extravalue)) { if (isset($extravalue)) {
require_once QA_INCLUDE_DIR.'db/metas.php'; require_once QA_INCLUDE_DIR . 'db/metas.php';
qa_db_postmeta_set($oldquestion['postid'], 'qa_q_extra', $extravalue); qa_db_postmeta_set($oldquestion['postid'], 'qa_q_extra', $extravalue);
} }
if ($setupdated && $remoderate) { if ($setupdated && $remoderate) {
require_once QA_INCLUDE_DIR.'app/posts.php'; require_once QA_INCLUDE_DIR . 'app/posts.php';
$answers = qa_post_get_question_answers($oldquestion['postid']); $answers = qa_post_get_question_answers($oldquestion['postid']);
$commentsfollows = qa_post_get_question_commentsfollows($oldquestion['postid']); $commentsfollows = qa_post_get_question_commentsfollows($oldquestion['postid']);
...@@ -91,12 +105,11 @@ ...@@ -91,12 +105,11 @@
if ($oldquestion['flagcount']) if ($oldquestion['flagcount'])
qa_db_flaggedcount_update(); qa_db_flaggedcount_update();
} } elseif ($oldquestion['type'] == 'Q') { // not hidden or queued
elseif ($oldquestion['type'] == 'Q') { // not hidden or queued
qa_post_index($oldquestion['postid'], 'Q', $oldquestion['postid'], $oldquestion['parentid'], $title, $content, $format, $text, $tagstring, $oldquestion['categoryid']); qa_post_index($oldquestion['postid'], 'Q', $oldquestion['postid'], $oldquestion['parentid'], $title, $content, $format, $text, $tagstring, $oldquestion['categoryid']);
} }
$eventparams=array( $eventparams = array(
'postid' => $oldquestion['postid'], 'postid' => $oldquestion['postid'],
'title' => $title, 'title' => $title,
'content' => $content, 'content' => $content,
...@@ -121,18 +134,24 @@ ...@@ -121,18 +134,24 @@
if ($setupdated && $remoderate) if ($setupdated && $remoderate)
qa_report_event('q_requeue', $userid, $handle, $cookieid, $eventparams); qa_report_event('q_requeue', $userid, $handle, $cookieid, $eventparams);
} }
function qa_question_set_selchildid($userid, $handle, $cookieid, $oldquestion, $selchildid, $answers) /**
/* * Set the selected answer (application level) of $oldquestion to $selchildid. Pass details of the user doing this
Set the selected answer (application level) of $oldquestion to $selchildid. Pass details of the user doing this * in $userid, $handle and $cookieid, and the database records for the selected and deselected answers in $answers.
in $userid, $handle and $cookieid, and the database records for the selected and deselected answers in $answers. * Handles user points values and notifications.
Handles user points values and notifications. * See qa-app-posts.php for a higher-level function which is easier to use.
See qa-app-posts.php for a higher-level function which is easier to use. * @param $userid
*/ * @param $handle
{ * @param $cookieid
$oldselchildid=$oldquestion['selchildid']; * @param $oldquestion
* @param $selchildid
* @param $answers
*/
function qa_question_set_selchildid($userid, $handle, $cookieid, $oldquestion, $selchildid, $answers)
{
$oldselchildid = $oldquestion['selchildid'];
qa_db_post_set_selchildid($oldquestion['postid'], isset($selchildid) ? $selchildid : null, $userid, qa_remote_ip_address()); qa_db_post_set_selchildid($oldquestion['postid'], isset($selchildid) ? $selchildid : null, $userid, qa_remote_ip_address());
qa_db_points_update_ifuser($oldquestion['userid'], 'aselects'); qa_db_points_update_ifuser($oldquestion['userid'], 'aselects');
...@@ -159,20 +178,25 @@ ...@@ -159,20 +178,25 @@
'answer' => $answers[$selchildid], 'answer' => $answers[$selchildid],
)); ));
} }
} }
function qa_question_close_clear($oldquestion, $oldclosepost, $userid, $handle, $cookieid) /**
/* * Reopen $oldquestion if it was closed. Pass details of the user doing this in $userid, $handle and $cookieid, and the
Reopen $oldquestion if it was closed. Pass details of the user doing this in $userid, $handle and $cookieid, and the * $oldclosepost (to match $oldquestion['closedbyid']) if any.
$oldclosepost (to match $oldquestion['closedbyid']) if any. * See qa-app-posts.php for a higher-level function which is easier to use.
See qa-app-posts.php for a higher-level function which is easier to use. * @param $oldquestion
*/ * @param $oldclosepost
{ * @param $userid
* @param $handle
* @param $cookieid
*/
function qa_question_close_clear($oldquestion, $oldclosepost, $userid, $handle, $cookieid)
{
if (isset($oldquestion['closedbyid'])) { if (isset($oldquestion['closedbyid'])) {
qa_db_post_set_closed($oldquestion['postid'], null, $userid, qa_remote_ip_address()); qa_db_post_set_closed($oldquestion['postid'], null, $userid, qa_remote_ip_address());
if (isset($oldclosepost) && ($oldclosepost['parentid']==$oldquestion['postid'])) { if (isset($oldclosepost) && ($oldclosepost['parentid'] == $oldquestion['postid'])) {
qa_post_unindex($oldclosepost['postid']); qa_post_unindex($oldclosepost['postid']);
qa_db_post_delete($oldclosepost['postid']); qa_db_post_delete($oldclosepost['postid']);
} }
...@@ -182,16 +206,22 @@ ...@@ -182,16 +206,22 @@
'oldquestion' => $oldquestion, 'oldquestion' => $oldquestion,
)); ));
} }
} }
function qa_question_close_duplicate($oldquestion, $oldclosepost, $originalpostid, $userid, $handle, $cookieid) /**
/* * Close $oldquestion as a duplicate of the question with id $originalpostid. Pass details of the user doing this in
Close $oldquestion as a duplicate of the question with id $originalpostid. Pass details of the user doing this in * $userid, $handle and $cookieid, and the $oldclosepost (to match $oldquestion['closedbyid']) if any. See
$userid, $handle and $cookieid, and the $oldclosepost (to match $oldquestion['closedbyid']) if any. See * qa-app-posts.php for a higher-level function which is easier to use.
qa-app-posts.php for a higher-level function which is easier to use. * @param $oldquestion
*/ * @param $oldclosepost
{ * @param $originalpostid
* @param $userid
* @param $handle
* @param $cookieid
*/
function qa_question_close_duplicate($oldquestion, $oldclosepost, $originalpostid, $userid, $handle, $cookieid)
{
qa_question_close_clear($oldquestion, $oldclosepost, $userid, $handle, $cookieid); qa_question_close_clear($oldquestion, $oldclosepost, $userid, $handle, $cookieid);
qa_db_post_set_closed($oldquestion['postid'], $originalpostid, $userid, qa_remote_ip_address()); qa_db_post_set_closed($oldquestion['postid'], $originalpostid, $userid, qa_remote_ip_address());
...@@ -202,24 +232,30 @@ ...@@ -202,24 +232,30 @@
'reason' => 'duplicate', 'reason' => 'duplicate',
'originalid' => $originalpostid, 'originalid' => $originalpostid,
)); ));
} }
function qa_question_close_other($oldquestion, $oldclosepost, $note, $userid, $handle, $cookieid) /**
/* * Close $oldquestion with the reason given in $note. Pass details of the user doing this in $userid, $handle and
Close $oldquestion with the reason given in $note. Pass details of the user doing this in $userid, $handle and * $cookieid, and the $oldclosepost (to match $oldquestion['closedbyid']) if any.
$cookieid, and the $oldclosepost (to match $oldquestion['closedbyid']) if any. * See qa-app-posts.php for a higher-level function which is easier to use.
See qa-app-posts.php for a higher-level function which is easier to use. * @param $oldquestion
*/ * @param $oldclosepost
{ * @param $note
* @param $userid
* @param $handle
* @param $cookieid
*/
function qa_question_close_other($oldquestion, $oldclosepost, $note, $userid, $handle, $cookieid)
{
qa_question_close_clear($oldquestion, $oldclosepost, $userid, $handle, $cookieid); qa_question_close_clear($oldquestion, $oldclosepost, $userid, $handle, $cookieid);
$postid=qa_db_post_create('NOTE', $oldquestion['postid'], $userid, isset($userid) ? null : $cookieid, $postid = qa_db_post_create('NOTE', $oldquestion['postid'], $userid, isset($userid) ? null : $cookieid,
qa_remote_ip_address(), null, $note, '', null, null, $oldquestion['categoryid']); qa_remote_ip_address(), null, $note, '', null, null, $oldquestion['categoryid']);
qa_db_posts_calc_category_path($postid); qa_db_posts_calc_category_path($postid);
if ($oldquestion['type']=='Q') if ($oldquestion['type'] == 'Q')
qa_post_index($postid, 'NOTE', $oldquestion['postid'], $oldquestion['postid'], null, $note, '', $note, null, $oldquestion['categoryid']); qa_post_index($postid, 'NOTE', $oldquestion['postid'], $oldquestion['postid'], null, $note, '', $note, null, $oldquestion['categoryid']);
qa_db_post_set_closed($oldquestion['postid'], $postid, $userid, qa_remote_ip_address()); qa_db_post_set_closed($oldquestion['postid'], $postid, $userid, qa_remote_ip_address());
...@@ -230,79 +266,97 @@ ...@@ -230,79 +266,97 @@
'reason' => 'other', 'reason' => 'other',
'note' => $note, 'note' => $note,
)); ));
} }
function qa_question_set_hidden($oldquestion, $hidden, $userid, $handle, $cookieid, $answers, $commentsfollows, $closepost=null) /**
/* * Set $oldquestion to hidden if $hidden is true, visible/normal if otherwise. All other parameters are as for qa_question_set_status(...)
Set $oldquestion to hidden if $hidden is true, visible/normal if otherwise. All other parameters are as for qa_question_set_status(...) * This function is included mainly for backwards compatibility.
This function is included mainly for backwards compatibility. * @param $oldquestion
*/ * @param $hidden
{ * @param $userid
* @param $handle
* @param $cookieid
* @param $answers
* @param $commentsfollows
* @param $closepost
*/
function qa_question_set_hidden($oldquestion, $hidden, $userid, $handle, $cookieid, $answers, $commentsfollows, $closepost = null)
{
qa_question_set_status($oldquestion, $hidden ? QA_POST_STATUS_HIDDEN : QA_POST_STATUS_NORMAL, $userid, $handle, $cookieid, $answers, $commentsfollows, $closepost); qa_question_set_status($oldquestion, $hidden ? QA_POST_STATUS_HIDDEN : QA_POST_STATUS_NORMAL, $userid, $handle, $cookieid, $answers, $commentsfollows, $closepost);
} }
function qa_question_set_status($oldquestion, $status, $userid, $handle, $cookieid, $answers, $commentsfollows, $closepost=null) /**
/* * Set the status (application level) of $oldquestion to $status, one of the QA_POST_STATUS_* constants above. Pass
Set the status (application level) of $oldquestion to $status, one of the QA_POST_STATUS_* constants above. Pass * details of the user doing this in $userid, $handle and $cookieid, the database records for all answers to the
details of the user doing this in $userid, $handle and $cookieid, the database records for all answers to the * question in $answers, the database records for all comments on the question or the question's answers in
question in $answers, the database records for all comments on the question or the question's answers in * $commentsfollows ($commentsfollows can also contain records for follow-on questions which are ignored), and
$commentsfollows ($commentsfollows can also contain records for follow-on questions which are ignored), and * $closepost to match $oldquestion['closedbyid'] (if any). Handles indexing, user points, cached counts and event
$closepost to match $oldquestion['closedbyid'] (if any). Handles indexing, user points, cached counts and event * reports. See qa-app-posts.php for a higher-level function which is easier to use.
reports. See qa-app-posts.php for a higher-level function which is easier to use. * @param $oldquestion
*/ * @param $status
{ * @param $userid
require_once QA_INCLUDE_DIR.'app/format.php'; * @param $handle
require_once QA_INCLUDE_DIR.'app/updates.php'; * @param $cookieid
* @param $answers
$washidden=($oldquestion['type']=='Q_HIDDEN'); * @param $commentsfollows
$wasqueued=($oldquestion['type']=='Q_QUEUED'); * @param $closepost
$wasrequeued=$wasqueued && isset($oldquestion['updated']); */
function qa_question_set_status($oldquestion, $status, $userid, $handle, $cookieid, $answers, $commentsfollows, $closepost = null)
{
require_once QA_INCLUDE_DIR . 'app/format.php';
require_once QA_INCLUDE_DIR . 'app/updates.php';
$washidden = ($oldquestion['type'] == 'Q_HIDDEN');
$wasqueued = ($oldquestion['type'] == 'Q_QUEUED');
$wasrequeued = $wasqueued && isset($oldquestion['updated']);
qa_post_unindex($oldquestion['postid']); qa_post_unindex($oldquestion['postid']);
foreach ($answers as $answer) foreach ($answers as $answer) {
qa_post_unindex($answer['postid']); qa_post_unindex($answer['postid']);
}
foreach ($commentsfollows as $comment) foreach ($commentsfollows as $comment) {
if ($comment['basetype']=='C') if ($comment['basetype'] == 'C')
qa_post_unindex($comment['postid']); qa_post_unindex($comment['postid']);
}
if (@$closepost['parentid']==$oldquestion['postid']) if (@$closepost['parentid'] == $oldquestion['postid'])
qa_post_unindex($closepost['postid']); qa_post_unindex($closepost['postid']);
$setupdated=false; $setupdated = false;
$event=null; $event = null;
if ($status==QA_POST_STATUS_QUEUED) { if ($status == QA_POST_STATUS_QUEUED) {
$newtype='Q_QUEUED'; $newtype = 'Q_QUEUED';
if (!$wasqueued) if (!$wasqueued)
$event='q_requeue'; // same event whether it was hidden or shown before $event = 'q_requeue'; // same event whether it was hidden or shown before
} elseif ($status==QA_POST_STATUS_HIDDEN) { } elseif ($status == QA_POST_STATUS_HIDDEN) {
$newtype='Q_HIDDEN'; $newtype = 'Q_HIDDEN';
if (!$washidden) { if (!$washidden) {
$event=$wasqueued ? 'q_reject' : 'q_hide'; $event = $wasqueued ? 'q_reject' : 'q_hide';
if (!$wasqueued) if (!$wasqueued)
$setupdated=true; $setupdated = true;
} }
} elseif ($status==QA_POST_STATUS_NORMAL) { } elseif ($status == QA_POST_STATUS_NORMAL) {
$newtype='Q'; $newtype = 'Q';
if ($wasqueued) if ($wasqueued)
$event='q_approve'; $event = 'q_approve';
elseif ($washidden) { elseif ($washidden) {
$event='q_reshow'; $event = 'q_reshow';
$setupdated=true; $setupdated = true;
} }
} else } else
qa_fatal_error('Unknown status in qa_question_set_status(): '.$status); qa_fatal_error('Unknown status in qa_question_set_status(): ' . $status);
qa_db_post_set_type($oldquestion['postid'], $newtype, $setupdated ? $userid : null, $setupdated ? qa_remote_ip_address() : null, QA_UPDATE_VISIBLE); qa_db_post_set_type($oldquestion['postid'], $newtype, $setupdated ? $userid : null, $setupdated ? qa_remote_ip_address() : null, QA_UPDATE_VISIBLE);
if ( $wasqueued && ($status==QA_POST_STATUS_NORMAL) && qa_opt('moderate_update_time') ) { // ... for approval of a post, can set time to now instead if ($wasqueued && $status == QA_POST_STATUS_NORMAL && qa_opt('moderate_update_time')) { // ... for approval of a post, can set time to now instead
if ($wasrequeued) // reset edit time to now if there was one, since we're approving the edit... if ($wasrequeued) // reset edit time to now if there was one, since we're approving the edit...
qa_db_post_set_updated($oldquestion['postid'], null); qa_db_post_set_updated($oldquestion['postid'], null);
...@@ -315,36 +369,41 @@ ...@@ -315,36 +369,41 @@
qa_update_counts_for_q($oldquestion['postid']); qa_update_counts_for_q($oldquestion['postid']);
qa_db_points_update_ifuser($oldquestion['userid'], array('qposts', 'aselects')); qa_db_points_update_ifuser($oldquestion['userid'], array('qposts', 'aselects'));
if ($wasqueued || ($status==QA_POST_STATUS_QUEUED)) if ($wasqueued || ($status == QA_POST_STATUS_QUEUED))
qa_db_queuedcount_update(); qa_db_queuedcount_update();
if ($oldquestion['flagcount']) if ($oldquestion['flagcount'])
qa_db_flaggedcount_update(); qa_db_flaggedcount_update();
if ($status==QA_POST_STATUS_NORMAL) { if ($status == QA_POST_STATUS_NORMAL) {
qa_post_index($oldquestion['postid'], 'Q', $oldquestion['postid'], $oldquestion['parentid'], $oldquestion['title'], $oldquestion['content'], qa_post_index($oldquestion['postid'], 'Q', $oldquestion['postid'], $oldquestion['parentid'], $oldquestion['title'], $oldquestion['content'],
$oldquestion['format'], qa_viewer_text($oldquestion['content'], $oldquestion['format']), $oldquestion['tags'], $oldquestion['categoryid']); $oldquestion['format'], qa_viewer_text($oldquestion['content'], $oldquestion['format']), $oldquestion['tags'], $oldquestion['categoryid']);
foreach ($answers as $answer) foreach ($answers as $answer) {
if ($answer['type']=='A') // even if question visible, don't index hidden or queued answers if ($answer['type'] == 'A') { // even if question visible, don't index hidden or queued answers
qa_post_index($answer['postid'], $answer['type'], $oldquestion['postid'], $answer['parentid'], null, qa_post_index($answer['postid'], $answer['type'], $oldquestion['postid'], $answer['parentid'], null,
$answer['content'], $answer['format'], qa_viewer_text($answer['content'], $answer['format']), null, $answer['categoryid']); $answer['content'], $answer['format'], qa_viewer_text($answer['content'], $answer['format']), null, $answer['categoryid']);
}
}
foreach ($commentsfollows as $comment) foreach ($commentsfollows as $comment) {
if ($comment['type']=='C') { if ($comment['type'] == 'C') {
$answer=@$answers[$comment['parentid']]; $answer = @$answers[$comment['parentid']];
if ( (!isset($answer)) || ($answer['type']=='A') ) // don't index comment if it or its parent is hidden if ((!isset($answer)) || ($answer['type'] == 'A')) { // don't index comment if it or its parent is hidden
qa_post_index($comment['postid'], $comment['type'], $oldquestion['postid'], $comment['parentid'], null, qa_post_index($comment['postid'], $comment['type'], $oldquestion['postid'], $comment['parentid'], null,
$comment['content'], $comment['format'], qa_viewer_text($comment['content'], $comment['format']), null, $comment['categoryid']); $comment['content'], $comment['format'], qa_viewer_text($comment['content'], $comment['format']), null, $comment['categoryid']);
} }
}
}
if ($closepost['parentid']==$oldquestion['postid']) if ($closepost['parentid'] == $oldquestion['postid']) {
qa_post_index($closepost['postid'], $closepost['type'], $oldquestion['postid'], $closepost['parentid'], null, qa_post_index($closepost['postid'], $closepost['type'], $oldquestion['postid'], $closepost['parentid'], null,
$closepost['content'], $closepost['format'], qa_viewer_text($closepost['content'], $closepost['format']), null, $closepost['categoryid']); $closepost['content'], $closepost['format'], qa_viewer_text($closepost['content'], $closepost['format']), null, $closepost['categoryid']);
} }
}
$eventparams=array( $eventparams = array(
'postid' => $oldquestion['postid'], 'postid' => $oldquestion['postid'],
'parentid' => $oldquestion['parentid'], 'parentid' => $oldquestion['parentid'],
'parent' => isset($oldquestion['parentid']) ? qa_db_single_select(qa_db_full_post_selectspec(null, $oldquestion['parentid'])) : null, 'parent' => isset($oldquestion['parentid']) ? qa_db_single_select(qa_db_full_post_selectspec(null, $oldquestion['parentid'])) : null,
...@@ -357,14 +416,15 @@ ...@@ -357,14 +416,15 @@
'name' => $oldquestion['name'], 'name' => $oldquestion['name'],
); );
if (isset($event)) if (isset($event)) {
qa_report_event($event, $userid, $handle, $cookieid, $eventparams + array( qa_report_event($event, $userid, $handle, $cookieid, $eventparams + array(
'oldquestion' => $oldquestion, 'oldquestion' => $oldquestion,
)); ));
}
if ($wasqueued && ($status==QA_POST_STATUS_NORMAL) && !$wasrequeued) { if ($wasqueued && ($status == QA_POST_STATUS_NORMAL) && !$wasrequeued) {
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
require_once QA_INCLUDE_DIR.'util/string.php'; require_once QA_INCLUDE_DIR . 'util/string.php';
qa_report_event('q_post', $oldquestion['userid'], $oldquestion['handle'], $oldquestion['cookieid'], $eventparams + array( qa_report_event('q_post', $oldquestion['userid'], $oldquestion['handle'], $oldquestion['cookieid'], $eventparams + array(
'notify' => isset($oldquestion['notify']), 'notify' => isset($oldquestion['notify']),
...@@ -372,48 +432,60 @@ ...@@ -372,48 +432,60 @@
'delayed' => $oldquestion['created'], 'delayed' => $oldquestion['created'],
)); ));
} }
} }
function qa_question_set_category($oldquestion, $categoryid, $userid, $handle, $cookieid, $answers, $commentsfollows, $closepost=null, $silent=false) /**
/* * Sets the category (application level) of $oldquestion to $categoryid. Pass details of the user doing this in
Sets the category (application level) of $oldquestion to $categoryid. Pass details of the user doing this in * $userid, $handle and $cookieid, the database records for all answers to the question in $answers, the database
$userid, $handle and $cookieid, the database records for all answers to the question in $answers, the database * records for all comments on the question or the question's answers in $commentsfollows ($commentsfollows can also
records for all comments on the question or the question's answers in $commentsfollows ($commentsfollows can also * contain records for follow-on questions which are ignored), and $closepost to match $oldquestion['closedbyid'] (if any).
contain records for follow-on questions which are ignored), and $closepost to match $oldquestion['closedbyid'] (if any). * Set $silent to true to not mark the question as edited. Handles cached counts and event reports and will reset category
Set $silent to true to not mark the question as edited. Handles cached counts and event reports and will reset category * IDs and paths for all answers and comments. See qa-app-posts.php for a higher-level function which is easier to use.
IDs and paths for all answers and comments. See qa-app-posts.php for a higher-level function which is easier to use. * @param $oldquestion
*/ * @param $categoryid
{ * @param $userid
$oldpath=qa_db_post_get_category_path($oldquestion['postid']); * @param $handle
* @param $cookieid
* @param $answers
* @param $commentsfollows
* @param $closepost
* @param bool $silent
*/
function qa_question_set_category($oldquestion, $categoryid, $userid, $handle, $cookieid, $answers, $commentsfollows, $closepost = null, $silent = false)
{
$oldpath = qa_db_post_get_category_path($oldquestion['postid']);
qa_db_post_set_category($oldquestion['postid'], $categoryid, $silent ? null : $userid, $silent ? null : qa_remote_ip_address()); qa_db_post_set_category($oldquestion['postid'], $categoryid, $silent ? null : $userid, $silent ? null : qa_remote_ip_address());
qa_db_posts_calc_category_path($oldquestion['postid']); qa_db_posts_calc_category_path($oldquestion['postid']);
$newpath=qa_db_post_get_category_path($oldquestion['postid']); $newpath = qa_db_post_get_category_path($oldquestion['postid']);
qa_db_category_path_qcount_update($oldpath); qa_db_category_path_qcount_update($oldpath);
qa_db_category_path_qcount_update($newpath); qa_db_category_path_qcount_update($newpath);
$otherpostids=array(); $otherpostids = array();
foreach ($answers as $answer) foreach ($answers as $answer) {
$otherpostids[]=$answer['postid']; $otherpostids[] = $answer['postid'];
}
foreach ($commentsfollows as $comment) foreach ($commentsfollows as $comment) {
if ($comment['basetype']=='C') if ($comment['basetype'] == 'C')
$otherpostids[]=$comment['postid']; $otherpostids[] = $comment['postid'];
}
if (@$closepost['parentid']==$oldquestion['postid']) if (@$closepost['parentid'] == $oldquestion['postid'])
$otherpostids[]=$closepost['postid']; $otherpostids[] = $closepost['postid'];
qa_db_posts_set_category_path($otherpostids, $newpath); qa_db_posts_set_category_path($otherpostids, $newpath);
$searchmodules=qa_load_modules_with('search', 'move_post'); $searchmodules = qa_load_modules_with('search', 'move_post');
foreach ($searchmodules as $searchmodule) { foreach ($searchmodules as $searchmodule) {
$searchmodule->move_post($oldquestion['postid'], $categoryid); $searchmodule->move_post($oldquestion['postid'], $categoryid);
foreach ($otherpostids as $otherpostid) foreach ($otherpostids as $otherpostid) {
$searchmodule->move_post($otherpostid, $categoryid); $searchmodule->move_post($otherpostid, $categoryid);
} }
}
qa_report_event('q_move', $userid, $handle, $cookieid, array( qa_report_event('q_move', $userid, $handle, $cookieid, array(
'postid' => $oldquestion['postid'], 'postid' => $oldquestion['postid'],
...@@ -421,20 +493,25 @@ ...@@ -421,20 +493,25 @@
'categoryid' => $categoryid, 'categoryid' => $categoryid,
'oldcategoryid' => $oldquestion['categoryid'], 'oldcategoryid' => $oldquestion['categoryid'],
)); ));
} }
function qa_question_delete($oldquestion, $userid, $handle, $cookieid, $oldclosepost=null) /**
/* * Permanently delete a question (application level) from the database. The question must not have any answers or
Permanently delete a question (application level) from the database. The question must not have any answers or * comments on it. Pass details of the user doing this in $userid, $handle and $cookieid, and $closepost to match
comments on it. Pass details of the user doing this in $userid, $handle and $cookieid, and $closepost to match * $oldquestion['closedbyid'] (if any). Handles unindexing, votes, points, cached counts and event reports.
$oldquestion['closedbyid'] (if any). Handles unindexing, votes, points, cached counts and event reports. * See qa-app-posts.php for a higher-level function which is easier to use.
See qa-app-posts.php for a higher-level function which is easier to use. * @param $oldquestion
*/ * @param $userid
{ * @param $handle
require_once QA_INCLUDE_DIR.'db/votes.php'; * @param $cookieid
* @param $oldclosepost
if ($oldquestion['type']!='Q_HIDDEN') */
function qa_question_delete($oldquestion, $userid, $handle, $cookieid, $oldclosepost = null)
{
require_once QA_INCLUDE_DIR . 'db/votes.php';
if ($oldquestion['type'] != 'Q_HIDDEN')
qa_fatal_error('Tried to delete a non-hidden question'); qa_fatal_error('Tried to delete a non-hidden question');
$params = array( $params = array(
...@@ -444,14 +521,14 @@ ...@@ -444,14 +521,14 @@
qa_report_event('q_delete_before', $userid, $handle, $cookieid, $params); qa_report_event('q_delete_before', $userid, $handle, $cookieid, $params);
if (isset($oldclosepost) && ($oldclosepost['parentid']==$oldquestion['postid'])) { if (isset($oldclosepost) && ($oldclosepost['parentid'] == $oldquestion['postid'])) {
qa_db_post_set_closed($oldquestion['postid'], null); // for foreign key constraint qa_db_post_set_closed($oldquestion['postid'], null); // for foreign key constraint
qa_post_unindex($oldclosepost['postid']); qa_post_unindex($oldclosepost['postid']);
qa_db_post_delete($oldclosepost['postid']); qa_db_post_delete($oldclosepost['postid']);
} }
$useridvotes=qa_db_uservote_post_get($oldquestion['postid']); $useridvotes = qa_db_uservote_post_get($oldquestion['postid']);
$oldpath=qa_db_post_get_category_path($oldquestion['postid']); $oldpath = qa_db_post_get_category_path($oldquestion['postid']);
qa_post_unindex($oldquestion['postid']); qa_post_unindex($oldquestion['postid']);
qa_db_post_delete($oldquestion['postid']); // also deletes any related voteds due to foreign key cascading qa_db_post_delete($oldquestion['postid']); // also deletes any related voteds due to foreign key cascading
...@@ -459,21 +536,26 @@ ...@@ -459,21 +536,26 @@
qa_db_category_path_qcount_update($oldpath); // don't do inside qa_update_counts_for_q() since post no longer exists qa_db_category_path_qcount_update($oldpath); // don't do inside qa_update_counts_for_q() since post no longer exists
qa_db_points_update_ifuser($oldquestion['userid'], array('qposts', 'aselects', 'qvoteds', 'upvoteds', 'downvoteds')); qa_db_points_update_ifuser($oldquestion['userid'], array('qposts', 'aselects', 'qvoteds', 'upvoteds', 'downvoteds'));
foreach ($useridvotes as $voteruserid => $vote) foreach ($useridvotes as $voteruserid => $vote) {
qa_db_points_update_ifuser($voteruserid, ($vote>0) ? 'qupvotes' : 'qdownvotes');
// could do this in one query like in qa_db_users_recalc_points() but this will do for now - unlikely to be many votes // could do this in one query like in qa_db_users_recalc_points() but this will do for now - unlikely to be many votes
qa_db_points_update_ifuser($voteruserid, ($vote > 0) ? 'qupvotes' : 'qdownvotes');
}
qa_report_event('q_delete', $userid, $handle, $cookieid, $params); qa_report_event('q_delete', $userid, $handle, $cookieid, $params);
} }
function qa_question_set_userid($oldquestion, $userid, $handle, $cookieid) /**
/* * Set the author (application level) of $oldquestion to $userid and also pass $handle and $cookieid
Set the author (application level) of $oldquestion to $userid and also pass $handle and $cookieid * of user. Updates points and reports events as appropriate.
of user. Updates points and reports events as appropriate. * @param $oldquestion
*/ * @param $userid
{ * @param $handle
require_once QA_INCLUDE_DIR.'db/votes.php'; * @param $cookieid
*/
function qa_question_set_userid($oldquestion, $userid, $handle, $cookieid)
{
require_once QA_INCLUDE_DIR . 'db/votes.php';
$postid = $oldquestion['postid']; $postid = $oldquestion['postid'];
...@@ -488,54 +570,69 @@ ...@@ -488,54 +570,69 @@
'postid' => $postid, 'postid' => $postid,
'oldquestion' => $oldquestion, 'oldquestion' => $oldquestion,
)); ));
} }
function qa_post_unindex($postid) /**
/* * Remove post $postid from our index and update appropriate word counts. Calls through to all search modules.
Remove post $postid from our index and update appropriate word counts. Calls through to all search modules. * @param $postid
*/ */
{ function qa_post_unindex($postid)
{
global $qa_post_indexing_suspended; global $qa_post_indexing_suspended;
if ($qa_post_indexing_suspended>0) if ($qa_post_indexing_suspended > 0)
return; return;
// Send through to any search modules for unindexing // Send through to any search modules for unindexing
$searchmodules=qa_load_modules_with('search', 'unindex_post'); $searchmodules = qa_load_modules_with('search', 'unindex_post');
foreach ($searchmodules as $searchmodule) foreach ($searchmodules as $searchmodule) {
$searchmodule->unindex_post($postid); $searchmodule->unindex_post($postid);
} }
}
function qa_answer_set_content($oldanswer, $content, $format, $text, $notify, $userid, $handle, $cookieid, $question, $name=null, $remoderate=false, $silent=false)
/* /**
Change the fields of an answer (application level) to $content, $format, $notify and $name, then reindex based on * Change the fields of an answer (application level) to $content, $format, $notify and $name, then reindex based on
$text. For backwards compatibility if $name is null then the name will not be changed. Pass the answer's database * $text. For backwards compatibility if $name is null then the name will not be changed. Pass the answer's database
record before changes in $oldanswer, the question's in $question, and details of the user doing this in $userid, * record before changes in $oldanswer, the question's in $question, and details of the user doing this in $userid,
$handle and $cookieid. Set $remoderate to true if the question should be requeued for moderation if modified. Set * $handle and $cookieid. Set $remoderate to true if the question should be requeued for moderation if modified. Set
$silent to true to not mark the question as edited. Handle indexing and event reports as appropriate. See * $silent to true to not mark the question as edited. Handle indexing and event reports as appropriate. See
qa-app-posts.php for a higher-level function which is easier to use. * qa-app-posts.php for a higher-level function which is easier to use.
*/ * @param $oldanswer
{ * @param $content
* @param $format
* @param $text
* @param $notify
* @param $userid
* @param $handle
* @param $cookieid
* @param $question
* @param $name
* @param bool $remoderate
* @param bool $silent
*/
function qa_answer_set_content($oldanswer, $content, $format, $text, $notify, $userid, $handle, $cookieid, $question, $name = null, $remoderate = false, $silent = false)
{
qa_post_unindex($oldanswer['postid']); qa_post_unindex($oldanswer['postid']);
$wasqueued=($oldanswer['type']=='A_QUEUED'); $wasqueued = ($oldanswer['type'] == 'A_QUEUED');
$contentchanged=strcmp($oldanswer['content'], $content) || strcmp($oldanswer['format'], $format); $contentchanged = strcmp($oldanswer['content'], $content) || strcmp($oldanswer['format'], $format);
$setupdated=$contentchanged && (!$wasqueued) && !$silent; $setupdated = $contentchanged && (!$wasqueued) && !$silent;
qa_db_post_set_content($oldanswer['postid'], $oldanswer['title'], $content, $format, $oldanswer['tags'], $notify, qa_db_post_set_content($oldanswer['postid'], $oldanswer['title'], $content, $format, $oldanswer['tags'], $notify,
$setupdated ? $userid : null, $setupdated ? qa_remote_ip_address() : null, QA_UPDATE_CONTENT, $name); $setupdated ? $userid : null, $setupdated ? qa_remote_ip_address() : null, QA_UPDATE_CONTENT, $name);
if ($setupdated && $remoderate) { if ($setupdated && $remoderate) {
require_once QA_INCLUDE_DIR.'app/posts.php'; require_once QA_INCLUDE_DIR . 'app/posts.php';
$commentsfollows=qa_post_get_answer_commentsfollows($oldanswer['postid']); $commentsfollows = qa_post_get_answer_commentsfollows($oldanswer['postid']);
foreach ($commentsfollows as $comment) foreach ($commentsfollows as $comment) {
if ( ($comment['basetype']=='C') && ($comment['parentid']==$oldanswer['postid']) ) if (($comment['basetype'] == 'C') && ($comment['parentid'] == $oldanswer['postid']))
qa_post_unindex($comment['postid']); qa_post_unindex($comment['postid']);
}
qa_db_post_set_type($oldanswer['postid'], 'A_QUEUED'); qa_db_post_set_type($oldanswer['postid'], 'A_QUEUED');
qa_update_q_counts_for_a($question['postid']); qa_update_q_counts_for_a($question['postid']);
...@@ -545,11 +642,11 @@ ...@@ -545,11 +642,11 @@
if ($oldanswer['flagcount']) if ($oldanswer['flagcount'])
qa_db_flaggedcount_update(); qa_db_flaggedcount_update();
} elseif ( ($oldanswer['type']=='A') && ($question['type']=='Q') ) { // don't index if question or answer are hidden/queued } elseif (($oldanswer['type'] == 'A') && ($question['type'] == 'Q')) { // don't index if question or answer are hidden/queued
qa_post_index($oldanswer['postid'], 'A', $question['postid'], $oldanswer['parentid'], null, $content, $format, $text, null, $oldanswer['categoryid']); qa_post_index($oldanswer['postid'], 'A', $question['postid'], $oldanswer['parentid'], null, $content, $format, $text, null, $oldanswer['categoryid']);
} }
$eventparams=array( $eventparams = array(
'postid' => $oldanswer['postid'], 'postid' => $oldanswer['postid'],
'parentid' => $oldanswer['parentid'], 'parentid' => $oldanswer['parentid'],
'parent' => $question, 'parent' => $question,
...@@ -569,75 +666,90 @@ ...@@ -569,75 +666,90 @@
if ($setupdated && $remoderate) if ($setupdated && $remoderate)
qa_report_event('a_requeue', $userid, $handle, $cookieid, $eventparams); qa_report_event('a_requeue', $userid, $handle, $cookieid, $eventparams);
} }
function qa_answer_set_hidden($oldanswer, $hidden, $userid, $handle, $cookieid, $question, $commentsfollows) /**
/* * Set $oldanswer to hidden if $hidden is true, visible/normal if otherwise. All other parameters are as for qa_answer_set_status(...)
Set $oldanswer to hidden if $hidden is true, visible/normal if otherwise. All other parameters are as for qa_answer_set_status(...) * This function is included mainly for backwards compatibility.
This function is included mainly for backwards compatibility. * @param $oldanswer
*/ * @param $hidden
{ * @param $userid
* @param $handle
* @param $cookieid
* @param $question
* @param $commentsfollows
*/
function qa_answer_set_hidden($oldanswer, $hidden, $userid, $handle, $cookieid, $question, $commentsfollows)
{
qa_answer_set_status($oldanswer, $hidden ? QA_POST_STATUS_HIDDEN : QA_POST_STATUS_NORMAL, $userid, $handle, $cookieid, $question, $commentsfollows); qa_answer_set_status($oldanswer, $hidden ? QA_POST_STATUS_HIDDEN : QA_POST_STATUS_NORMAL, $userid, $handle, $cookieid, $question, $commentsfollows);
} }
function qa_answer_set_status($oldanswer, $status, $userid, $handle, $cookieid, $question, $commentsfollows) /**
/* * Set the status (application level) of $oldanswer to $status, one of the QA_POST_STATUS_* constants above. Pass
Set the status (application level) of $oldanswer to $status, one of the QA_POST_STATUS_* constants above. Pass * details of the user doing this in $userid, $handle and $cookieid, the database record for the question in $question,
details of the user doing this in $userid, $handle and $cookieid, the database record for the question in $question, * and the database records for all comments on the answer in $commentsfollows ($commentsfollows can also contain other
and the database records for all comments on the answer in $commentsfollows ($commentsfollows can also contain other * records which are ignored). Handles indexing, user points, cached counts and event reports. See qa-app-posts.php for
records which are ignored). Handles indexing, user points, cached counts and event reports. See qa-app-posts.php for * a higher-level function which is easier to use.
a higher-level function which is easier to use. * @param $oldanswer
*/ * @param $status
{ * @param $userid
require_once QA_INCLUDE_DIR.'app/format.php'; * @param $handle
* @param $cookieid
$washidden=($oldanswer['type']=='A_HIDDEN'); * @param $question
$wasqueued=($oldanswer['type']=='A_QUEUED'); * @param $commentsfollows
$wasrequeued=$wasqueued && isset($oldanswer['updated']); */
function qa_answer_set_status($oldanswer, $status, $userid, $handle, $cookieid, $question, $commentsfollows)
{
require_once QA_INCLUDE_DIR . 'app/format.php';
$washidden = ($oldanswer['type'] == 'A_HIDDEN');
$wasqueued = ($oldanswer['type'] == 'A_QUEUED');
$wasrequeued = $wasqueued && isset($oldanswer['updated']);
qa_post_unindex($oldanswer['postid']); qa_post_unindex($oldanswer['postid']);
foreach ($commentsfollows as $comment) foreach ($commentsfollows as $comment) {
if ( ($comment['basetype']=='C') && ($comment['parentid']==$oldanswer['postid']) ) if (($comment['basetype'] == 'C') && ($comment['parentid'] == $oldanswer['postid']))
qa_post_unindex($comment['postid']); qa_post_unindex($comment['postid']);
}
$setupdated=false; $setupdated = false;
$event=null; $event = null;
if ($status==QA_POST_STATUS_QUEUED) { if ($status == QA_POST_STATUS_QUEUED) {
$newtype='A_QUEUED'; $newtype = 'A_QUEUED';
if (!$wasqueued) if (!$wasqueued)
$event='a_requeue'; // same event whether it was hidden or shown before $event = 'a_requeue'; // same event whether it was hidden or shown before
} elseif ($status==QA_POST_STATUS_HIDDEN) { } elseif ($status == QA_POST_STATUS_HIDDEN) {
$newtype='A_HIDDEN'; $newtype = 'A_HIDDEN';
if (!$washidden) { if (!$washidden) {
$event=$wasqueued ? 'a_reject' : 'a_hide'; $event = $wasqueued ? 'a_reject' : 'a_hide';
if (!$wasqueued) if (!$wasqueued)
$setupdated=true; $setupdated = true;
} }
if ($question['selchildid'] == $oldanswer['postid']) { // remove selected answer if ($question['selchildid'] == $oldanswer['postid']) { // remove selected answer
qa_question_set_selchildid(null, null, null, $question, null, array($oldanswer['postid'] => $oldanswer)); qa_question_set_selchildid(null, null, null, $question, null, array($oldanswer['postid'] => $oldanswer));
} }
} elseif ($status==QA_POST_STATUS_NORMAL) { } elseif ($status == QA_POST_STATUS_NORMAL) {
$newtype='A'; $newtype = 'A';
if ($wasqueued) if ($wasqueued)
$event='a_approve'; $event = 'a_approve';
elseif ($washidden) { elseif ($washidden) {
$event='a_reshow'; $event = 'a_reshow';
$setupdated=true; $setupdated = true;
} }
} else } else
qa_fatal_error('Unknown status in qa_answer_set_status(): '.$status); qa_fatal_error('Unknown status in qa_answer_set_status(): ' . $status);
qa_db_post_set_type($oldanswer['postid'], $newtype, $setupdated ? $userid : null, $setupdated ? qa_remote_ip_address() : null, QA_UPDATE_VISIBLE); qa_db_post_set_type($oldanswer['postid'], $newtype, $setupdated ? $userid : null, $setupdated ? qa_remote_ip_address() : null, QA_UPDATE_VISIBLE);
if ( $wasqueued && ($status==QA_POST_STATUS_NORMAL) && qa_opt('moderate_update_time') ) { // ... for approval of a post, can set time to now instead if ($wasqueued && ($status == QA_POST_STATUS_NORMAL) && qa_opt('moderate_update_time')) { // ... for approval of a post, can set time to now instead
if ($wasrequeued) if ($wasrequeued)
qa_db_post_set_updated($oldanswer['postid'], null); qa_db_post_set_updated($oldanswer['postid'], null);
else else
...@@ -647,23 +759,25 @@ ...@@ -647,23 +759,25 @@
qa_update_q_counts_for_a($question['postid']); qa_update_q_counts_for_a($question['postid']);
qa_db_points_update_ifuser($oldanswer['userid'], array('aposts', 'aselecteds')); qa_db_points_update_ifuser($oldanswer['userid'], array('aposts', 'aselecteds'));
if ($wasqueued || ($status==QA_POST_STATUS_QUEUED)) if ($wasqueued || $status == QA_POST_STATUS_QUEUED)
qa_db_queuedcount_update(); qa_db_queuedcount_update();
if ($oldanswer['flagcount']) if ($oldanswer['flagcount'])
qa_db_flaggedcount_update(); qa_db_flaggedcount_update();
if (($question['type']=='Q') && ($status==QA_POST_STATUS_NORMAL)) { // even if answer visible, don't index if question is hidden or queued if (($question['type'] == 'Q') && ($status == QA_POST_STATUS_NORMAL)) { // even if answer visible, don't index if question is hidden or queued
qa_post_index($oldanswer['postid'], 'A', $question['postid'], $oldanswer['parentid'], null, $oldanswer['content'], qa_post_index($oldanswer['postid'], 'A', $question['postid'], $oldanswer['parentid'], null, $oldanswer['content'],
$oldanswer['format'], qa_viewer_text($oldanswer['content'], $oldanswer['format']), null, $oldanswer['categoryid']); $oldanswer['format'], qa_viewer_text($oldanswer['content'], $oldanswer['format']), null, $oldanswer['categoryid']);
foreach ($commentsfollows as $comment) foreach ($commentsfollows as $comment) {
if ( ($comment['type']=='C') && ($comment['parentid']==$oldanswer['postid']) ) // and don't index hidden/queued comments if (($comment['type'] == 'C') && ($comment['parentid'] == $oldanswer['postid'])) { // and don't index hidden/queued comments
qa_post_index($comment['postid'], $comment['type'], $question['postid'], $comment['parentid'], null, $comment['content'], qa_post_index($comment['postid'], $comment['type'], $question['postid'], $comment['parentid'], null, $comment['content'],
$comment['format'], qa_viewer_text($comment['content'], $comment['format']), null, $comment['categoryid']); $comment['format'], qa_viewer_text($comment['content'], $comment['format']), null, $comment['categoryid']);
} }
}
}
$eventparams=array( $eventparams = array(
'postid' => $oldanswer['postid'], 'postid' => $oldanswer['postid'],
'parentid' => $oldanswer['parentid'], 'parentid' => $oldanswer['parentid'],
'parent' => $question, 'parent' => $question,
...@@ -674,13 +788,14 @@ ...@@ -674,13 +788,14 @@
'name' => $oldanswer['name'], 'name' => $oldanswer['name'],
); );
if (isset($event)) if (isset($event)) {
qa_report_event($event, $userid, $handle, $cookieid, $eventparams + array( qa_report_event($event, $userid, $handle, $cookieid, $eventparams + array(
'oldanswer' => $oldanswer, 'oldanswer' => $oldanswer,
)); ));
}
if ($wasqueued && ($status==QA_POST_STATUS_NORMAL) && !$wasrequeued) { if ($wasqueued && ($status == QA_POST_STATUS_NORMAL) && !$wasrequeued) {
require_once QA_INCLUDE_DIR.'util/string.php'; require_once QA_INCLUDE_DIR . 'util/string.php';
qa_report_event('a_post', $oldanswer['userid'], $oldanswer['handle'], $oldanswer['cookieid'], $eventparams + array( qa_report_event('a_post', $oldanswer['userid'], $oldanswer['handle'], $oldanswer['cookieid'], $eventparams + array(
'notify' => isset($oldanswer['notify']), 'notify' => isset($oldanswer['notify']),
...@@ -688,23 +803,28 @@ ...@@ -688,23 +803,28 @@
'delayed' => $oldanswer['created'], 'delayed' => $oldanswer['created'],
)); ));
} }
} }
function qa_answer_delete($oldanswer, $question, $userid, $handle, $cookieid) /**
/* * Permanently delete an answer (application level) from the database. The answer must not have any comments or
Permanently delete an answer (application level) from the database. The answer must not have any comments or * follow-on questions. Pass the database record for the question in $question and details of the user doing this
follow-on questions. Pass the database record for the question in $question and details of the user doing this * in $userid, $handle and $cookieid. Handles unindexing, votes, points, cached counts and event reports.
in $userid, $handle and $cookieid. Handles unindexing, votes, points, cached counts and event reports. * See qa-app-posts.php for a higher-level function which is easier to use.
See qa-app-posts.php for a higher-level function which is easier to use. * @param $oldanswer
*/ * @param $question
{ * @param $userid
require_once QA_INCLUDE_DIR.'db/votes.php'; * @param $handle
* @param $cookieid
if ($oldanswer['type']!='A_HIDDEN') */
function qa_answer_delete($oldanswer, $question, $userid, $handle, $cookieid)
{
require_once QA_INCLUDE_DIR . 'db/votes.php';
if ($oldanswer['type'] != 'A_HIDDEN')
qa_fatal_error('Tried to delete a non-hidden answer'); qa_fatal_error('Tried to delete a non-hidden answer');
$useridvotes=qa_db_uservote_post_get($oldanswer['postid']); $useridvotes = qa_db_uservote_post_get($oldanswer['postid']);
$params = array( $params = array(
'postid' => $oldanswer['postid'], 'postid' => $oldanswer['postid'],
...@@ -717,7 +837,7 @@ ...@@ -717,7 +837,7 @@
qa_post_unindex($oldanswer['postid']); qa_post_unindex($oldanswer['postid']);
qa_db_post_delete($oldanswer['postid']); // also deletes any related voteds due to cascading qa_db_post_delete($oldanswer['postid']); // also deletes any related voteds due to cascading
if ($question['selchildid']==$oldanswer['postid']) { if ($question['selchildid'] == $oldanswer['postid']) {
qa_db_post_set_selchildid($question['postid'], null); qa_db_post_set_selchildid($question['postid'], null);
qa_db_points_update_ifuser($question['userid'], 'aselects'); qa_db_points_update_ifuser($question['userid'], 'aselects');
qa_db_unselqcount_update(); qa_db_unselqcount_update();
...@@ -726,21 +846,26 @@ ...@@ -726,21 +846,26 @@
qa_update_q_counts_for_a($question['postid']); qa_update_q_counts_for_a($question['postid']);
qa_db_points_update_ifuser($oldanswer['userid'], array('aposts', 'aselecteds', 'avoteds', 'upvoteds', 'downvoteds')); qa_db_points_update_ifuser($oldanswer['userid'], array('aposts', 'aselecteds', 'avoteds', 'upvoteds', 'downvoteds'));
foreach ($useridvotes as $voteruserid => $vote) foreach ($useridvotes as $voteruserid => $vote) {
qa_db_points_update_ifuser($voteruserid, ($vote>0) ? 'aupvotes' : 'adownvotes');
// could do this in one query like in qa_db_users_recalc_points() but this will do for now - unlikely to be many votes // could do this in one query like in qa_db_users_recalc_points() but this will do for now - unlikely to be many votes
qa_db_points_update_ifuser($voteruserid, ($vote > 0) ? 'aupvotes' : 'adownvotes');
}
qa_report_event('a_delete', $userid, $handle, $cookieid, $params); qa_report_event('a_delete', $userid, $handle, $cookieid, $params);
} }
function qa_answer_set_userid($oldanswer, $userid, $handle, $cookieid) /**
/* * Set the author (application level) of $oldanswer to $userid and also pass $handle and $cookieid
Set the author (application level) of $oldanswer to $userid and also pass $handle and $cookieid * of user. Updates points and reports events as appropriate.
of user. Updates points and reports events as appropriate. * @param $oldanswer
*/ * @param $userid
{ * @param $handle
require_once QA_INCLUDE_DIR.'db/votes.php'; * @param $cookieid
*/
function qa_answer_set_userid($oldanswer, $userid, $handle, $cookieid)
{
require_once QA_INCLUDE_DIR . 'db/votes.php';
$postid = $oldanswer['postid']; $postid = $oldanswer['postid'];
...@@ -756,28 +881,41 @@ ...@@ -756,28 +881,41 @@
'parentid' => $oldanswer['parentid'], 'parentid' => $oldanswer['parentid'],
'oldanswer' => $oldanswer, 'oldanswer' => $oldanswer,
)); ));
} }
function qa_comment_set_content($oldcomment, $content, $format, $text, $notify, $userid, $handle, $cookieid, $question, $parent, $name=null, $remoderate=false, $silent=false) /**
/* * Change the fields of a comment (application level) to $content, $format, $notify and $name, then reindex based on
Change the fields of a comment (application level) to $content, $format, $notify and $name, then reindex based on * $text. For backwards compatibility if $name is null then the name will not be changed. Pass the comment's database
$text. For backwards compatibility if $name is null then the name will not be changed. Pass the comment's database * record before changes in $oldcomment, details of the user doing this in $userid, $handle and $cookieid, the
record before changes in $oldcomment, details of the user doing this in $userid, $handle and $cookieid, the * antecedent question in $question and the answer's database record in $answer if this is a comment on an answer,
antecedent question in $question and the answer's database record in $answer if this is a comment on an answer, * otherwise null. Set $remoderate to true if the question should be requeued for moderation if modified. Set $silent
otherwise null. Set $remoderate to true if the question should be requeued for moderation if modified. Set $silent * to true to not mark the question as edited. Handles unindexing and event reports. See qa-app-posts.php for a
to true to not mark the question as edited. Handles unindexing and event reports. See qa-app-posts.php for a * higher-level function which is easier to use.
higher-level function which is easier to use. * @param $oldcomment
*/ * @param $content
{ * @param $format
* @param $text
* @param $notify
* @param $userid
* @param $handle
* @param $cookieid
* @param $question
* @param $parent
* @param $name
* @param bool $remoderate
* @param bool $silent
*/
function qa_comment_set_content($oldcomment, $content, $format, $text, $notify, $userid, $handle, $cookieid, $question, $parent, $name = null, $remoderate = false, $silent = false)
{
if (!isset($parent)) if (!isset($parent))
$parent=$question; // for backwards compatibility with old answer parameter $parent = $question; // for backwards compatibility with old answer parameter
qa_post_unindex($oldcomment['postid']); qa_post_unindex($oldcomment['postid']);
$wasqueued=($oldcomment['type']=='C_QUEUED'); $wasqueued = ($oldcomment['type'] == 'C_QUEUED');
$contentchanged=strcmp($oldcomment['content'], $content) || strcmp($oldcomment['format'], $format); $contentchanged = strcmp($oldcomment['content'], $content) || strcmp($oldcomment['format'], $format);
$setupdated=$contentchanged && (!$wasqueued) && !$silent; $setupdated = $contentchanged && (!$wasqueued) && !$silent;
qa_db_post_set_content($oldcomment['postid'], $oldcomment['title'], $content, $format, $oldcomment['tags'], $notify, qa_db_post_set_content($oldcomment['postid'], $oldcomment['title'], $content, $format, $oldcomment['tags'], $notify,
$setupdated ? $userid : null, $setupdated ? qa_remote_ip_address() : null, QA_UPDATE_CONTENT, $name); $setupdated ? $userid : null, $setupdated ? qa_remote_ip_address() : null, QA_UPDATE_CONTENT, $name);
...@@ -791,11 +929,11 @@ ...@@ -791,11 +929,11 @@
if ($oldcomment['flagcount']) if ($oldcomment['flagcount'])
qa_db_flaggedcount_update(); qa_db_flaggedcount_update();
} elseif ( ($oldcomment['type']=='C') && ($question['type']=='Q') && (($parent['type']=='Q') || ($parent['type']=='A')) ) { // all must be visible } elseif ($oldcomment['type'] == 'C' && $question['type'] == 'Q' && ($parent['type'] == 'Q' || $parent['type'] == 'A')) { // all must be visible
qa_post_index($oldcomment['postid'], 'C', $question['postid'], $oldcomment['parentid'], null, $content, $format, $text, null, $oldcomment['categoryid']); qa_post_index($oldcomment['postid'], 'C', $question['postid'], $oldcomment['parentid'], null, $content, $format, $text, null, $oldcomment['categoryid']);
} }
$eventparams=array( $eventparams = array(
'postid' => $oldcomment['postid'], 'postid' => $oldcomment['postid'],
'parentid' => $oldcomment['parentid'], 'parentid' => $oldcomment['parentid'],
'parenttype' => $parent['basetype'], 'parenttype' => $parent['basetype'],
...@@ -818,35 +956,50 @@ ...@@ -818,35 +956,50 @@
if ($setupdated && $remoderate) if ($setupdated && $remoderate)
qa_report_event('c_requeue', $userid, $handle, $cookieid, $eventparams); qa_report_event('c_requeue', $userid, $handle, $cookieid, $eventparams);
} }
function qa_answer_to_comment($oldanswer, $parentid, $content, $format, $text, $notify, $userid, $handle, $cookieid, $question, $answers, $commentsfollows, $name=null, $remoderate=false, $silent=false) /**
/* * Convert an answer to a comment (application level) and set its fields to $content, $format, $notify and $name. For
Convert an answer to a comment (application level) and set its fields to $content, $format, $notify and $name. For * backwards compatibility if $name is null then the name will not be changed. Pass the answer's database record before
backwards compatibility if $name is null then the name will not be changed. Pass the answer's database record before * changes in $oldanswer, the new comment's $parentid to be, details of the user doing this in $userid, $handle and
changes in $oldanswer, the new comment's $parentid to be, details of the user doing this in $userid, $handle and * $cookieid, the antecedent question's record in $question, the records for all answers to that question in $answers,
$cookieid, the antecedent question's record in $question, the records for all answers to that question in $answers, * and the records for all comments on the (old) answer and questions following from the (old) answer in
and the records for all comments on the (old) answer and questions following from the (old) answer in * $commentsfollows ($commentsfollows can also contain other records which are ignored). Set $remoderate to true if the
$commentsfollows ($commentsfollows can also contain other records which are ignored). Set $remoderate to true if the * question should be requeued for moderation if modified. Set $silent to true to not mark the question as edited.
question should be requeued for moderation if modified. Set $silent to true to not mark the question as edited. * Handles indexing (based on $text), user points, cached counts and event reports.
Handles indexing (based on $text), user points, cached counts and event reports. * @param $oldanswer
*/ * @param $parentid
{ * @param $content
require_once QA_INCLUDE_DIR.'db/votes.php'; * @param $format
* @param $text
$parent=isset($answers[$parentid]) ? $answers[$parentid] : $question; * @param $notify
* @param $userid
* @param $handle
* @param $cookieid
* @param $question
* @param $answers
* @param $commentsfollows
* @param $name
* @param bool $remoderate
* @param bool $silent
*/
function qa_answer_to_comment($oldanswer, $parentid, $content, $format, $text, $notify, $userid, $handle, $cookieid, $question, $answers, $commentsfollows, $name = null, $remoderate = false, $silent = false)
{
require_once QA_INCLUDE_DIR . 'db/votes.php';
$parent = isset($answers[$parentid]) ? $answers[$parentid] : $question;
qa_post_unindex($oldanswer['postid']); qa_post_unindex($oldanswer['postid']);
$wasqueued=($oldanswer['type']=='A_QUEUED'); $wasqueued = ($oldanswer['type'] == 'A_QUEUED');
$contentchanged=strcmp($oldanswer['content'], $content) || strcmp($oldanswer['format'], $format); $contentchanged = strcmp($oldanswer['content'], $content) || strcmp($oldanswer['format'], $format);
$setupdated=$contentchanged && (!$wasqueued) && !$silent; $setupdated = $contentchanged && (!$wasqueued) && !$silent;
if ($setupdated && $remoderate) if ($setupdated && $remoderate)
$newtype='C_QUEUED'; $newtype = 'C_QUEUED';
else else
$newtype=substr_replace($oldanswer['type'], 'C', 0, 1); $newtype = substr_replace($oldanswer['type'], 'C', 0, 1);
qa_db_post_set_type($oldanswer['postid'], $newtype, ($wasqueued || $silent) ? null : $userid, qa_db_post_set_type($oldanswer['postid'], $newtype, ($wasqueued || $silent) ? null : $userid,
($wasqueued || $silent) ? null : qa_remote_ip_address(), QA_UPDATE_TYPE); ($wasqueued || $silent) ? null : qa_remote_ip_address(), QA_UPDATE_TYPE);
...@@ -854,18 +1007,20 @@ ...@@ -854,18 +1007,20 @@
qa_db_post_set_content($oldanswer['postid'], $oldanswer['title'], $content, $format, $oldanswer['tags'], $notify, qa_db_post_set_content($oldanswer['postid'], $oldanswer['title'], $content, $format, $oldanswer['tags'], $notify,
$setupdated ? $userid : null, $setupdated ? qa_remote_ip_address() : null, QA_UPDATE_CONTENT, $name); $setupdated ? $userid : null, $setupdated ? qa_remote_ip_address() : null, QA_UPDATE_CONTENT, $name);
foreach ($commentsfollows as $commentfollow) foreach ($commentsfollows as $commentfollow) {
if ($commentfollow['parentid']==$oldanswer['postid']) // do same thing for comments and follows if ($commentfollow['parentid'] == $oldanswer['postid']) // do same thing for comments and follows
qa_db_post_set_parent($commentfollow['postid'], $parentid); qa_db_post_set_parent($commentfollow['postid'], $parentid);
}
qa_update_q_counts_for_a($question['postid']); qa_update_q_counts_for_a($question['postid']);
qa_db_ccount_update(); qa_db_ccount_update();
qa_db_points_update_ifuser($oldanswer['userid'], array('aposts', 'aselecteds', 'cposts', 'avoteds')); qa_db_points_update_ifuser($oldanswer['userid'], array('aposts', 'aselecteds', 'cposts', 'avoteds'));
$useridvotes=qa_db_uservote_post_get($oldanswer['postid']); $useridvotes = qa_db_uservote_post_get($oldanswer['postid']);
foreach ($useridvotes as $voteruserid => $vote) foreach ($useridvotes as $voteruserid => $vote) {
qa_db_points_update_ifuser($voteruserid, ($vote>0) ? 'aupvotes' : 'adownvotes');
// could do this in one query like in qa_db_users_recalc_points() but this will do for now - unlikely to be many votes // could do this in one query like in qa_db_users_recalc_points() but this will do for now - unlikely to be many votes
qa_db_points_update_ifuser($voteruserid, ($vote > 0) ? 'aupvotes' : 'adownvotes');
}
if ($setupdated && $remoderate) { if ($setupdated && $remoderate) {
qa_db_queuedcount_update(); qa_db_queuedcount_update();
...@@ -873,14 +1028,14 @@ ...@@ -873,14 +1028,14 @@
if ($oldanswer['flagcount']) if ($oldanswer['flagcount'])
qa_db_flaggedcount_update(); qa_db_flaggedcount_update();
} elseif ( ($oldanswer['type']=='A') && ($question['type']=='Q') && (($parent['type']=='Q') || ($parent['type']=='A')) ) // only if all fully visible } elseif (($oldanswer['type'] == 'A') && ($question['type'] == 'Q') && (($parent['type'] == 'Q') || ($parent['type'] == 'A'))) // only if all fully visible
qa_post_index($oldanswer['postid'], 'C', $question['postid'], $parentid, null, $content, $format, $text, null, $oldanswer['categoryid']); qa_post_index($oldanswer['postid'], 'C', $question['postid'], $parentid, null, $content, $format, $text, null, $oldanswer['categoryid']);
if ($question['selchildid'] == $oldanswer['postid']) { // remove selected answer if ($question['selchildid'] == $oldanswer['postid']) { // remove selected answer
qa_question_set_selchildid(null, null, null, $question, null, array($oldanswer['postid'] => $oldanswer)); qa_question_set_selchildid(null, null, null, $question, null, array($oldanswer['postid'] => $oldanswer));
} }
$eventparams=array( $eventparams = array(
'postid' => $oldanswer['postid'], 'postid' => $oldanswer['postid'],
'parentid' => $parentid, 'parentid' => $parentid,
'parenttype' => $parent['basetype'], 'parenttype' => $parent['basetype'],
...@@ -901,72 +1056,87 @@ ...@@ -901,72 +1056,87 @@
'contentchanged' => $contentchanged, 'contentchanged' => $contentchanged,
)); ));
if ($setupdated && $remoderate) if ($setupdated && $remoderate) {
qa_report_event('c_requeue', $userid, $handle, $cookieid, $eventparams);
// a-to-c conversion can be detected by presence of $event['oldanswer'] instead of $event['oldcomment'] // a-to-c conversion can be detected by presence of $event['oldanswer'] instead of $event['oldcomment']
qa_report_event('c_requeue', $userid, $handle, $cookieid, $eventparams);
} }
}
function qa_comment_set_hidden($oldcomment, $hidden, $userid, $handle, $cookieid, $question, $parent)
/* /**
Set $oldcomment to hidden if $hidden is true, visible/normal if otherwise. All other parameters are as for qa_comment_set_status(...) * Set $oldcomment to hidden if $hidden is true, visible/normal if otherwise. All other parameters are as for qa_comment_set_status(...)
This function is included mainly for backwards compatibility. * This function is included mainly for backwards compatibility.
*/ * @param $oldcomment
{ * @param $hidden
* @param $userid
* @param $handle
* @param $cookieid
* @param $question
* @param $parent
*/
function qa_comment_set_hidden($oldcomment, $hidden, $userid, $handle, $cookieid, $question, $parent)
{
qa_comment_set_status($oldcomment, $hidden ? QA_POST_STATUS_HIDDEN : QA_POST_STATUS_NORMAL, $userid, $handle, $cookieid, $question, $parent); qa_comment_set_status($oldcomment, $hidden ? QA_POST_STATUS_HIDDEN : QA_POST_STATUS_NORMAL, $userid, $handle, $cookieid, $question, $parent);
} }
function qa_comment_set_status($oldcomment, $status, $userid, $handle, $cookieid, $question, $parent) /**
/* * Set the status (application level) of $oldcomment to $status, one of the QA_POST_STATUS_* constants above. Pass the
Set the status (application level) of $oldcomment to $status, one of the QA_POST_STATUS_* constants above. Pass the * antecedent question's record in $question, details of the user doing this in $userid, $handle and $cookieid, and the
antecedent question's record in $question, details of the user doing this in $userid, $handle and $cookieid, and the * answer's database record in $answer if this is a comment on an answer, otherwise null. Handles indexing, user
answer's database record in $answer if this is a comment on an answer, otherwise null. Handles indexing, user * points, cached counts and event reports. See qa-app-posts.php for a higher-level function which is easier to use.
points, cached counts and event reports. See qa-app-posts.php for a higher-level function which is easier to use. * @param $oldcomment
*/ * @param $status
{ * @param $userid
require_once QA_INCLUDE_DIR.'app/format.php'; * @param $handle
* @param $cookieid
* @param $question
* @param $parent
*/
function qa_comment_set_status($oldcomment, $status, $userid, $handle, $cookieid, $question, $parent)
{
require_once QA_INCLUDE_DIR . 'app/format.php';
if (!isset($parent)) if (!isset($parent))
$parent=$question; // for backwards compatibility with old answer parameter $parent = $question; // for backwards compatibility with old answer parameter
$washidden=($oldcomment['type']=='C_HIDDEN'); $washidden = ($oldcomment['type'] == 'C_HIDDEN');
$wasqueued=($oldcomment['type']=='C_QUEUED'); $wasqueued = ($oldcomment['type'] == 'C_QUEUED');
$wasrequeued=$wasqueued && isset($oldcomment['updated']); $wasrequeued = $wasqueued && isset($oldcomment['updated']);
qa_post_unindex($oldcomment['postid']); qa_post_unindex($oldcomment['postid']);
$setupdated=false; $setupdated = false;
$event=null; $event = null;
if ($status==QA_POST_STATUS_QUEUED) { if ($status == QA_POST_STATUS_QUEUED) {
$newtype='C_QUEUED'; $newtype = 'C_QUEUED';
if (!$wasqueued) if (!$wasqueued)
$event='c_requeue'; // same event whether it was hidden or shown before $event = 'c_requeue'; // same event whether it was hidden or shown before
} elseif ($status==QA_POST_STATUS_HIDDEN) { } elseif ($status == QA_POST_STATUS_HIDDEN) {
$newtype='C_HIDDEN'; $newtype = 'C_HIDDEN';
if (!$washidden) { if (!$washidden) {
$event=$wasqueued ? 'c_reject' : 'c_hide'; $event = $wasqueued ? 'c_reject' : 'c_hide';
if (!$wasqueued) if (!$wasqueued)
$setupdated=true; $setupdated = true;
} }
} elseif ($status==QA_POST_STATUS_NORMAL) { } elseif ($status == QA_POST_STATUS_NORMAL) {
$newtype='C'; $newtype = 'C';
if ($wasqueued) if ($wasqueued)
$event='c_approve'; $event = 'c_approve';
elseif ($washidden) { elseif ($washidden) {
$event='c_reshow'; $event = 'c_reshow';
$setupdated=true; $setupdated = true;
} }
} else } else
qa_fatal_error('Unknown status in qa_comment_set_status(): '.$status); qa_fatal_error('Unknown status in qa_comment_set_status(): ' . $status);
qa_db_post_set_type($oldcomment['postid'], $newtype, $setupdated ? $userid : null, $setupdated ? qa_remote_ip_address() : null, QA_UPDATE_VISIBLE); qa_db_post_set_type($oldcomment['postid'], $newtype, $setupdated ? $userid : null, $setupdated ? qa_remote_ip_address() : null, QA_UPDATE_VISIBLE);
if ( $wasqueued && ($status==QA_POST_STATUS_NORMAL) && qa_opt('moderate_update_time') ) { // ... for approval of a post, can set time to now instead if ($wasqueued && ($status == QA_POST_STATUS_NORMAL) && qa_opt('moderate_update_time')) { // ... for approval of a post, can set time to now instead
if ($wasrequeued) if ($wasrequeued)
qa_db_post_set_updated($oldcomment['postid'], null); qa_db_post_set_updated($oldcomment['postid'], null);
else else
...@@ -976,17 +1146,19 @@ ...@@ -976,17 +1146,19 @@
qa_db_ccount_update(); qa_db_ccount_update();
qa_db_points_update_ifuser($oldcomment['userid'], array('cposts')); qa_db_points_update_ifuser($oldcomment['userid'], array('cposts'));
if ($wasqueued || ($status==QA_POST_STATUS_QUEUED)) if ($wasqueued || $status == QA_POST_STATUS_QUEUED)
qa_db_queuedcount_update(); qa_db_queuedcount_update();
if ($oldcomment['flagcount']) if ($oldcomment['flagcount'])
qa_db_flaggedcount_update(); qa_db_flaggedcount_update();
if ( ($question['type']=='Q') && (($parent['type']=='Q') || ($parent['type']=='A')) && ($status==QA_POST_STATUS_NORMAL)) // only index if none of the things it depends on are hidden or queued if ($question['type'] == 'Q' && ($parent['type'] == 'Q' || $parent['type'] == 'A') && $status == QA_POST_STATUS_NORMAL) {
// only index if none of the things it depends on are hidden or queued
qa_post_index($oldcomment['postid'], 'C', $question['postid'], $oldcomment['parentid'], null, $oldcomment['content'], qa_post_index($oldcomment['postid'], 'C', $question['postid'], $oldcomment['parentid'], null, $oldcomment['content'],
$oldcomment['format'], qa_viewer_text($oldcomment['content'], $oldcomment['format']), null, $oldcomment['categoryid']); $oldcomment['format'], qa_viewer_text($oldcomment['content'], $oldcomment['format']), null, $oldcomment['categoryid']);
}
$eventparams=array( $eventparams = array(
'postid' => $oldcomment['postid'], 'postid' => $oldcomment['postid'],
'parentid' => $oldcomment['parentid'], 'parentid' => $oldcomment['parentid'],
'parenttype' => $parent['basetype'], 'parenttype' => $parent['basetype'],
...@@ -1000,21 +1172,23 @@ ...@@ -1000,21 +1172,23 @@
'name' => $oldcomment['name'], 'name' => $oldcomment['name'],
); );
if (isset($event)) if (isset($event)) {
qa_report_event($event, $userid, $handle, $cookieid, $eventparams + array( qa_report_event($event, $userid, $handle, $cookieid, $eventparams + array(
'oldcomment' => $oldcomment, 'oldcomment' => $oldcomment,
)); ));
}
if ($wasqueued && ($status==QA_POST_STATUS_NORMAL) && !$wasrequeued) { if ($wasqueued && $status == QA_POST_STATUS_NORMAL && !$wasrequeued) {
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
require_once QA_INCLUDE_DIR.'util/string.php'; require_once QA_INCLUDE_DIR . 'util/string.php';
$commentsfollows=qa_db_single_select(qa_db_full_child_posts_selectspec(null, $oldcomment['parentid'])); $commentsfollows = qa_db_single_select(qa_db_full_child_posts_selectspec(null, $oldcomment['parentid']));
$thread=array(); $thread = array();
foreach ($commentsfollows as $comment) foreach ($commentsfollows as $comment) {
if (($comment['type']=='C') && ($comment['parentid']==$parent['postid'])) if ($comment['type'] == 'C' && $comment['parentid'] == $parent['postid'])
$thread[]=$comment; $thread[] = $comment;
}
qa_report_event('c_post', $oldcomment['userid'], $oldcomment['handle'], $oldcomment['cookieid'], $eventparams + array( qa_report_event('c_post', $oldcomment['userid'], $oldcomment['handle'], $oldcomment['cookieid'], $eventparams + array(
'thread' => $thread, 'thread' => $thread,
...@@ -1023,21 +1197,27 @@ ...@@ -1023,21 +1197,27 @@
'delayed' => $oldcomment['created'], 'delayed' => $oldcomment['created'],
)); ));
} }
} }
function qa_comment_delete($oldcomment, $question, $parent, $userid, $handle, $cookieid) /**
/* * Permanently delete a comment in $oldcomment (application level) from the database. Pass the database question in $question
Permanently delete a comment in $oldcomment (application level) from the database. Pass the database question in $question * and the answer's database record in $answer if this is a comment on an answer, otherwise null. Pass details of the user
and the answer's database record in $answer if this is a comment on an answer, otherwise null. Pass details of the user * doing this in $userid, $handle and $cookieid. Handles unindexing, points, cached counts and event reports.
doing this in $userid, $handle and $cookieid. Handles unindexing, points, cached counts and event reports. * See qa-app-posts.php for a higher-level function which is easier to use.
See qa-app-posts.php for a higher-level function which is easier to use. * @param $oldcomment
*/ * @param $question
{ * @param $parent
* @param $userid
* @param $handle
* @param $cookieid
*/
function qa_comment_delete($oldcomment, $question, $parent, $userid, $handle, $cookieid)
{
if (!isset($parent)) if (!isset($parent))
$parent=$question; // for backwards compatibility with old answer parameter $parent = $question; // for backwards compatibility with old answer parameter
if ($oldcomment['type']!='C_HIDDEN') if ($oldcomment['type'] != 'C_HIDDEN')
qa_fatal_error('Tried to delete a non-hidden comment'); qa_fatal_error('Tried to delete a non-hidden comment');
$params = array( $params = array(
...@@ -1056,16 +1236,20 @@ ...@@ -1056,16 +1236,20 @@
qa_db_ccount_update(); qa_db_ccount_update();
qa_report_event('c_delete', $userid, $handle, $cookieid, $params); qa_report_event('c_delete', $userid, $handle, $cookieid, $params);
} }
function qa_comment_set_userid($oldcomment, $userid, $handle, $cookieid) /**
/* * Set the author (application level) of $oldcomment to $userid and also pass $handle and $cookieid
Set the author (application level) of $oldcomment to $userid and also pass $handle and $cookieid * of user. Updates points and reports events as appropriate.
of user. Updates points and reports events as appropriate. * @param $oldcomment
*/ * @param $userid
{ * @param $handle
require_once QA_INCLUDE_DIR.'db/votes.php'; * @param $cookieid
*/
function qa_comment_set_userid($oldcomment, $userid, $handle, $cookieid)
{
require_once QA_INCLUDE_DIR . 'db/votes.php';
$postid = $oldcomment['postid']; $postid = $oldcomment['postid'];
...@@ -1081,8 +1265,4 @@ ...@@ -1081,8 +1265,4 @@
'parentid' => $oldcomment['parentid'], 'parentid' => $oldcomment['parentid'],
'oldcomment' => $oldcomment, 'oldcomment' => $oldcomment,
)); ));
} }
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -56,28 +56,28 @@ ...@@ -56,28 +56,28 @@
[but these are not entirely redundant since they can contain historical information no longer in ^posts] [but these are not entirely redundant since they can contain historical information no longer in ^posts]
*/ */
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../'); header('Location: ../');
exit; exit;
} }
require_once QA_INCLUDE_DIR.'db/recalc.php'; require_once QA_INCLUDE_DIR.'db/recalc.php';
require_once QA_INCLUDE_DIR.'db/post-create.php'; require_once QA_INCLUDE_DIR.'db/post-create.php';
require_once QA_INCLUDE_DIR.'db/points.php'; require_once QA_INCLUDE_DIR.'db/points.php';
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR.'db/selects.php';
require_once QA_INCLUDE_DIR.'db/admin.php'; require_once QA_INCLUDE_DIR.'db/admin.php';
require_once QA_INCLUDE_DIR.'db/users.php'; require_once QA_INCLUDE_DIR.'db/users.php';
require_once QA_INCLUDE_DIR.'app/options.php'; require_once QA_INCLUDE_DIR.'app/options.php';
require_once QA_INCLUDE_DIR.'app/post-create.php'; require_once QA_INCLUDE_DIR.'app/post-create.php';
require_once QA_INCLUDE_DIR.'app/post-update.php'; require_once QA_INCLUDE_DIR.'app/post-update.php';
function qa_recalc_perform_step(&$state) /**
/* * Advance the recalculation operation represented by $state by a single step.
Advance the recalculation operation represented by $state by a single step. * $state can also be the name of a recalculation operation on its own.
$state can also be the name of a recalculation operation on its own. */
*/ function qa_recalc_perform_step(&$state)
{ {
$continue=false; $continue=false;
@list($operation, $length, $next, $done)=explode("\t", $state); @list($operation, $length, $next, $done)=explode("\t", $state);
...@@ -528,27 +528,27 @@ ...@@ -528,27 +528,27 @@
$state=$operation."\t".$length."\t".$next."\t".$done; $state=$operation."\t".$length."\t".$next."\t".$done;
return $continue && ($done<$length); return $continue && ($done<$length);
} }
function qa_recalc_transition(&$state, $operation) /**
/* * Change the $state to represent the beginning of a new $operation
Change the $state to represent the beginning of a new $operation */
*/ function qa_recalc_transition(&$state, $operation)
{ {
$length=qa_recalc_stage_length($operation); $length=qa_recalc_stage_length($operation);
$next=(QA_FINAL_EXTERNAL_USERS && ($operation=='dorecalcpoints_recalc')) ? '' : 0; $next=(QA_FINAL_EXTERNAL_USERS && ($operation=='dorecalcpoints_recalc')) ? '' : 0;
$done=0; $done=0;
$state=$operation."\t".$length."\t".$next."\t".$done; $state=$operation."\t".$length."\t".$next."\t".$done;
} }
function qa_recalc_stage_length($operation) /**
/* * Return how many steps there will be in recalculation $operation
Return how many steps there will be in recalculation $operation */
*/ function qa_recalc_stage_length($operation)
{ {
switch ($operation) { switch ($operation) {
case 'doreindexcontent_pagereindex': case 'doreindexcontent_pagereindex':
$length=qa_db_count_pages(); $length=qa_db_count_pages();
...@@ -607,10 +607,10 @@ ...@@ -607,10 +607,10 @@
} }
return $length; return $length;
} }
/** /**
* Return the translated language ID string replacing the progress and total in it. * Return the translated language ID string replacing the progress and total in it.
* @access private * @access private
* @param string $langId Language string ID that contains 2 placeholders (^1 and ^2) * @param string $langId Language string ID that contains 2 placeholders (^1 and ^2)
...@@ -620,20 +620,20 @@ ...@@ -620,20 +620,20 @@
* @return string Returns the language string ID with their placeholders replaced with * @return string Returns the language string ID with their placeholders replaced with
* the formatted progress and total numbers * the formatted progress and total numbers
*/ */
function qa_recalc_progress_lang($langId, $progress, $total) function qa_recalc_progress_lang($langId, $progress, $total)
{ {
return strtr(qa_lang($langId), array( return strtr(qa_lang($langId), array(
'^1' => qa_format_number($progress), '^1' => qa_format_number($progress),
'^2' => qa_format_number($total) '^2' => qa_format_number($total)
)); ));
} }
function qa_recalc_get_message($state) /**
/* * Return a string which gives a user-viewable version of $state
Return a string which gives a user-viewable version of $state */
*/ function qa_recalc_get_message($state)
{ {
require_once QA_INCLUDE_DIR . 'app/format.php'; require_once QA_INCLUDE_DIR . 'app/format.php';
@list($operation, $length, $next, $done) = explode("\t", $state); @list($operation, $length, $next, $done) = explode("\t", $state);
...@@ -745,9 +745,4 @@ ...@@ -745,9 +745,4 @@
} }
return $message; return $message;
} }
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -20,58 +20,56 @@ ...@@ -20,58 +20,56 @@
More about this license: http://www.question2answer.org/license.php 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 if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../'); header('Location: ../');
exit; exit;
} }
define('QA_USER_LEVEL_BASIC', 0); define('QA_USER_LEVEL_BASIC', 0);
define('QA_USER_LEVEL_APPROVED', 10); define('QA_USER_LEVEL_APPROVED', 10);
define('QA_USER_LEVEL_EXPERT', 20); define('QA_USER_LEVEL_EXPERT', 20);
define('QA_USER_LEVEL_EDITOR', 50); define('QA_USER_LEVEL_EDITOR', 50);
define('QA_USER_LEVEL_MODERATOR', 80); define('QA_USER_LEVEL_MODERATOR', 80);
define('QA_USER_LEVEL_ADMIN', 100); define('QA_USER_LEVEL_ADMIN', 100);
define('QA_USER_LEVEL_SUPER', 120); define('QA_USER_LEVEL_SUPER', 120);
define('QA_USER_FLAGS_EMAIL_CONFIRMED', 1); define('QA_USER_FLAGS_EMAIL_CONFIRMED', 1);
define('QA_USER_FLAGS_USER_BLOCKED', 2); define('QA_USER_FLAGS_USER_BLOCKED', 2);
define('QA_USER_FLAGS_SHOW_AVATAR', 4); define('QA_USER_FLAGS_SHOW_AVATAR', 4);
define('QA_USER_FLAGS_SHOW_GRAVATAR', 8); define('QA_USER_FLAGS_SHOW_GRAVATAR', 8);
define('QA_USER_FLAGS_NO_MESSAGES', 16); define('QA_USER_FLAGS_NO_MESSAGES', 16);
define('QA_USER_FLAGS_NO_MAILINGS', 32); define('QA_USER_FLAGS_NO_MAILINGS', 32);
define('QA_USER_FLAGS_WELCOME_NOTICE', 64); define('QA_USER_FLAGS_WELCOME_NOTICE', 64);
define('QA_USER_FLAGS_MUST_CONFIRM', 128); define('QA_USER_FLAGS_MUST_CONFIRM', 128);
define('QA_USER_FLAGS_NO_WALL_POSTS', 256); define('QA_USER_FLAGS_NO_WALL_POSTS', 256);
define('QA_USER_FLAGS_MUST_APPROVE', 512); define('QA_USER_FLAGS_MUST_APPROVE', 512);
define('QA_FIELD_FLAGS_MULTI_LINE', 1); define('QA_FIELD_FLAGS_MULTI_LINE', 1);
define('QA_FIELD_FLAGS_LINK_URL', 2); define('QA_FIELD_FLAGS_LINK_URL', 2);
define('QA_FIELD_FLAGS_ON_REGISTER', 4); define('QA_FIELD_FLAGS_ON_REGISTER', 4);
@define('QA_FORM_EXPIRY_SECS', 86400); // how many seconds a form is valid for submission @define('QA_FORM_EXPIRY_SECS', 86400); // how many seconds a form is valid for submission
@define('QA_FORM_KEY_LENGTH', 32); @define('QA_FORM_KEY_LENGTH', 32);
if (QA_FINAL_EXTERNAL_USERS) { if (QA_FINAL_EXTERNAL_USERS) {
// If we're using single sign-on integration (WordPress or otherwise), load PHP file for that // If we're using single sign-on integration (WordPress or otherwise), load PHP file for that
if (defined('QA_FINAL_WORDPRESS_INTEGRATE_PATH')) { if (defined('QA_FINAL_WORDPRESS_INTEGRATE_PATH')) {
require_once QA_INCLUDE_DIR.'util/external-users-wp.php'; require_once QA_INCLUDE_DIR . 'util/external-users-wp.php';
} } elseif (defined('QA_FINAL_JOOMLA_INTEGRATE_PATH')) {
elseif (defined('QA_FINAL_JOOMLA_INTEGRATE_PATH')) { require_once QA_INCLUDE_DIR . 'util/external-users-joomla.php';
require_once QA_INCLUDE_DIR.'util/external-users-joomla.php'; } else {
} require_once QA_EXTERNAL_DIR . 'qa-external-users.php';
else {
require_once QA_EXTERNAL_DIR.'qa-external-users.php';
} }
// Access functions for user information // Access functions for user information
function qa_get_logged_in_user_cache() /**
/* * Return array of information about the currently logged in user, cache to ensure only one call to external code
Return array of information about the currently logged in user, cache to ensure only one call to external code
*/ */
function qa_get_logged_in_user_cache()
{ {
global $qa_cached_logged_in_user; global $qa_cached_logged_in_user;
...@@ -81,8 +79,7 @@ ...@@ -81,8 +79,7 @@
if (isset($user)) { if (isset($user)) {
$user['flags'] = isset($user['blocked']) ? QA_USER_FLAGS_USER_BLOCKED : 0; $user['flags'] = isset($user['blocked']) ? QA_USER_FLAGS_USER_BLOCKED : 0;
$qa_cached_logged_in_user = $user; $qa_cached_logged_in_user = $user;
} } else
else
$qa_cached_logged_in_user = false; $qa_cached_logged_in_user = false;
} }
...@@ -90,47 +87,53 @@ ...@@ -90,47 +87,53 @@
} }
function qa_get_logged_in_user_field($field) /**
/* * Return $field of the currently logged in user, or null if not available
Return $field of the currently logged in user, or null if not available * @param $field
* @return null
*/ */
function qa_get_logged_in_user_field($field)
{ {
$user=qa_get_logged_in_user_cache(); $user = qa_get_logged_in_user_cache();
return isset($user[$field]) ? $user[$field] : null; return isset($user[$field]) ? $user[$field] : null;
} }
function qa_get_logged_in_userid() /**
/* * Return the userid of the currently logged in user, or null if none
Return the userid of the currently logged in user, or null if none
*/ */
function qa_get_logged_in_userid()
{ {
return qa_get_logged_in_user_field('userid'); return qa_get_logged_in_user_field('userid');
} }
function qa_get_logged_in_points() /**
/* * Return the number of points of the currently logged in user, or null if none is logged in
Return the number of points of the currently logged in user, or null if none is logged in
*/ */
function qa_get_logged_in_points()
{ {
global $qa_cached_logged_in_points; global $qa_cached_logged_in_points;
if (!isset($qa_cached_logged_in_points)) { if (!isset($qa_cached_logged_in_points)) {
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
$qa_cached_logged_in_points=qa_db_select_with_pending(qa_db_user_points_selectspec(qa_get_logged_in_userid(), true)); $qa_cached_logged_in_points = qa_db_select_with_pending(qa_db_user_points_selectspec(qa_get_logged_in_userid(), true));
} }
return $qa_cached_logged_in_points['points']; return $qa_cached_logged_in_points['points'];
} }
function qa_get_external_avatar_html($userid, $size, $padding=false) /**
/* * Return HTML to display for the avatar of $userid, constrained to $size pixels, with optional $padding to that size
Return HTML to display for the avatar of $userid, constrained to $size pixels, with optional $padding to that size * @param $userid
* @param $size
* @param bool $padding
* @return mixed|null|string
*/ */
function qa_get_external_avatar_html($userid, $size, $padding = false)
{ {
if (function_exists('qa_avatar_html_from_userid')) if (function_exists('qa_avatar_html_from_userid'))
return qa_avatar_html_from_userid($userid, $size, $padding); return qa_avatar_html_from_userid($userid, $size, $padding);
...@@ -139,12 +142,12 @@ ...@@ -139,12 +142,12 @@
} }
} else { } else {
function qa_start_session() /**
/* * Open a PHP session if one isn't opened already
Open a PHP session if one isn't opened already
*/ */
function qa_start_session()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
...@@ -157,52 +160,58 @@ ...@@ -157,52 +160,58 @@
} }
function qa_session_var_suffix() /**
/* * Returns a suffix to be used for names of session variables to prevent them being shared between multiple Q2A sites on the same server
Returns a suffix to be used for names of session variables to prevent them being shared between multiple Q2A sites on the same server
*/ */
function qa_session_var_suffix()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_session_suffix; global $qa_session_suffix;
if (!$qa_session_suffix) { if (!$qa_session_suffix) {
$prefix=defined('QA_MYSQL_USERS_PREFIX') ? QA_MYSQL_USERS_PREFIX : QA_MYSQL_TABLE_PREFIX; $prefix = defined('QA_MYSQL_USERS_PREFIX') ? QA_MYSQL_USERS_PREFIX : QA_MYSQL_TABLE_PREFIX;
$qa_session_suffix = md5(QA_FINAL_MYSQL_HOSTNAME.'/'.QA_FINAL_MYSQL_USERNAME.'/'.QA_FINAL_MYSQL_PASSWORD.'/'.QA_FINAL_MYSQL_DATABASE.'/'.$prefix); $qa_session_suffix = md5(QA_FINAL_MYSQL_HOSTNAME . '/' . QA_FINAL_MYSQL_USERNAME . '/' . QA_FINAL_MYSQL_PASSWORD . '/' . QA_FINAL_MYSQL_DATABASE . '/' . $prefix);
} }
return $qa_session_suffix; return $qa_session_suffix;
} }
function qa_session_verify_code($userid) /**
/* * Returns a verification code used to ensure that a user session can't be generated by another PHP script running on the same server
Returns a verification code used to ensure that a user session can't be generated by another PHP script running on the same server * @param $userid
* @return mixed|string
*/ */
function qa_session_verify_code($userid)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return sha1($userid.'/'.QA_MYSQL_TABLE_PREFIX.'/'.QA_FINAL_MYSQL_DATABASE.'/'.QA_FINAL_MYSQL_PASSWORD.'/'.QA_FINAL_MYSQL_USERNAME.'/'.QA_FINAL_MYSQL_HOSTNAME); return sha1($userid . '/' . QA_MYSQL_TABLE_PREFIX . '/' . QA_FINAL_MYSQL_DATABASE . '/' . QA_FINAL_MYSQL_PASSWORD . '/' . QA_FINAL_MYSQL_USERNAME . '/' . QA_FINAL_MYSQL_HOSTNAME);
} }
function qa_set_session_cookie($handle, $sessioncode, $remember) /**
/* * Set cookie in browser for username $handle with $sessioncode (in database).
Set cookie in browser for username $handle with $sessioncode (in database). * Pass true if user checked 'Remember me' (either now or previously, as learned from cookie).
Pass true if user checked 'Remember me' (either now or previously, as learned from cookie). * @param $handle
* @param $sessioncode
* @param $remember
* @return mixed
*/ */
function qa_set_session_cookie($handle, $sessioncode, $remember)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
// if $remember is true, store in browser for a month, otherwise store only until browser is closed // if $remember is true, store in browser for a month, otherwise store only until browser is closed
setcookie('qa_session', $handle.'/'.$sessioncode.'/'.($remember ? 1 : 0), $remember ? (time()+2592000) : 0, '/', QA_COOKIE_DOMAIN, (bool)ini_get('session.cookie_secure'), true); setcookie('qa_session', $handle . '/' . $sessioncode . '/' . ($remember ? 1 : 0), $remember ? (time() + 2592000) : 0, '/', QA_COOKIE_DOMAIN, (bool)ini_get('session.cookie_secure'), true);
} }
function qa_clear_session_cookie() /**
/* * Remove session cookie from browser
Remove session cookie from browser
*/ */
function qa_clear_session_cookie()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
...@@ -210,46 +219,54 @@ ...@@ -210,46 +219,54 @@
} }
function qa_set_session_user($userid, $source) /**
/* * Set the session variables to indicate that $userid is logged in from $source
Set the session variables to indicate that $userid is logged in from $source * @param $userid
* @param $source
* @return mixed
*/ */
function qa_set_session_user($userid, $source)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$suffix=qa_session_var_suffix(); $suffix = qa_session_var_suffix();
$_SESSION['qa_session_userid_'.$suffix]=$userid; $_SESSION['qa_session_userid_' . $suffix] = $userid;
$_SESSION['qa_session_source_'.$suffix]=$source; $_SESSION['qa_session_source_' . $suffix] = $source;
$_SESSION['qa_session_verify_'.$suffix]=qa_session_verify_code($userid);
// prevents one account on a shared server being able to create a log in a user to Q2A on another account on same server // prevents one account on a shared server being able to create a log in a user to Q2A on another account on same server
$_SESSION['qa_session_verify_' . $suffix] = qa_session_verify_code($userid);
} }
function qa_clear_session_user() /**
/* * Clear the session variables indicating that a user is logged in
Clear the session variables indicating that a user is logged in
*/ */
function qa_clear_session_user()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$suffix=qa_session_var_suffix(); $suffix = qa_session_var_suffix();
unset($_SESSION['qa_session_userid_'.$suffix]); unset($_SESSION['qa_session_userid_' . $suffix]);
unset($_SESSION['qa_session_source_'.$suffix]); unset($_SESSION['qa_session_source_' . $suffix]);
unset($_SESSION['qa_session_verify_'.$suffix]); unset($_SESSION['qa_session_verify_' . $suffix]);
} }
function qa_set_logged_in_user($userid, $handle='', $remember=false, $source=null) /**
/* * Call for successful log in by $userid and $handle or successful log out with $userid=null.
Call for successful log in by $userid and $handle or successful log out with $userid=null. * $remember states if 'Remember me' was checked in the login form.
$remember states if 'Remember me' was checked in the login form. * @param $userid
* @param string $handle
* @param bool $remember
* @param $source
* @return mixed
*/ */
function qa_set_logged_in_user($userid, $handle = '', $remember = false, $source = null)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'app/cookies.php'; require_once QA_INCLUDE_DIR . 'app/cookies.php';
qa_start_session(); qa_start_session();
...@@ -260,19 +277,19 @@ ...@@ -260,19 +277,19 @@
// Logging in from a second browser will make the previous browser's 'Remember me' no longer // Logging in from a second browser will make the previous browser's 'Remember me' no longer
// work - I'm not sure if this is the right behavior - could see it either way. // work - I'm not sure if this is the right behavior - could see it either way.
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
$userinfo=qa_db_single_select(qa_db_user_account_selectspec($userid, true)); $userinfo = qa_db_single_select(qa_db_user_account_selectspec($userid, true));
// if we have logged in before, and are logging in the same way as before, we don't need to change the sessioncode/source // if we have logged in before, and are logging in the same way as before, we don't need to change the sessioncode/source
// this means it will be possible to automatically log in (via cookies) to the same account from more than one browser // this means it will be possible to automatically log in (via cookies) to the same account from more than one browser
if (empty($userinfo['sessioncode']) || ($source!==$userinfo['sessionsource'])) { if (empty($userinfo['sessioncode']) || ($source !== $userinfo['sessionsource'])) {
$sessioncode=qa_db_user_rand_sessioncode(); $sessioncode = qa_db_user_rand_sessioncode();
qa_db_user_set($userid, 'sessioncode', $sessioncode); qa_db_user_set($userid, 'sessioncode', $sessioncode);
qa_db_user_set($userid, 'sessionsource', $source); qa_db_user_set($userid, 'sessionsource', $source);
} else } else
$sessioncode=$userinfo['sessioncode']; $sessioncode = $userinfo['sessioncode'];
qa_db_user_logged_in($userid, qa_remote_ip_address()); qa_db_user_logged_in($userid, qa_remote_ip_address());
qa_set_session_cookie($handle, $sessioncode, $remember); qa_set_session_cookie($handle, $sessioncode, $remember);
...@@ -280,8 +297,8 @@ ...@@ -280,8 +297,8 @@
qa_report_event('u_login', $userid, $userinfo['handle'], qa_cookie_get()); qa_report_event('u_login', $userid, $userinfo['handle'], qa_cookie_get());
} else { } else {
$olduserid=qa_get_logged_in_userid(); $olduserid = qa_get_logged_in_userid();
$oldhandle=qa_get_logged_in_handle(); $oldhandle = qa_get_logged_in_handle();
qa_clear_session_cookie(); qa_clear_session_cookie();
qa_clear_session_user(); qa_clear_session_user();
...@@ -291,41 +308,45 @@ ...@@ -291,41 +308,45 @@
} }
function qa_log_in_external_user($source, $identifier, $fields) /**
/* * Call to log in a user based on an external identity provider $source with external $identifier
Call to log in a user based on an external identity provider $source with external $identifier * A new user is created based on $fields if it's a new combination of $source and $identifier
A new user is created based on $fields if it's a new combination of $source and $identifier * @param $source
* @param $identifier
* @param $fields
* @return mixed
*/ */
function qa_log_in_external_user($source, $identifier, $fields)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'db/users.php'; require_once QA_INCLUDE_DIR . 'db/users.php';
$users=qa_db_user_login_find($source, $identifier); $users = qa_db_user_login_find($source, $identifier);
$countusers=count($users); $countusers = count($users);
if ($countusers>1) if ($countusers > 1)
qa_fatal_error('External login mapped to more than one user'); // should never happen qa_fatal_error('External login mapped to more than one user'); // should never happen
if ($countusers) // user exists so log them in if ($countusers) // user exists so log them in
qa_set_logged_in_user($users[0]['userid'], $users[0]['handle'], false, $source); qa_set_logged_in_user($users[0]['userid'], $users[0]['handle'], false, $source);
else { // create and log in user else { // create and log in user
require_once QA_INCLUDE_DIR.'app/users-edit.php'; require_once QA_INCLUDE_DIR . 'app/users-edit.php';
qa_db_user_login_sync(true); qa_db_user_login_sync(true);
$users=qa_db_user_login_find($source, $identifier); // check again after table is locked $users = qa_db_user_login_find($source, $identifier); // check again after table is locked
if (count($users)==1) { if (count($users) == 1) {
qa_db_user_login_sync(false); qa_db_user_login_sync(false);
qa_set_logged_in_user($users[0]['userid'], $users[0]['handle'], false, $source); qa_set_logged_in_user($users[0]['userid'], $users[0]['handle'], false, $source);
} else { } else {
$handle=qa_handle_make_valid(@$fields['handle']); $handle = qa_handle_make_valid(@$fields['handle']);
if (strlen(@$fields['email'])) { // remove email address if it will cause a duplicate if (strlen(@$fields['email'])) { // remove email address if it will cause a duplicate
$emailusers=qa_db_user_find_by_email($fields['email']); $emailusers = qa_db_user_find_by_email($fields['email']);
if (count($emailusers)) { if (count($emailusers)) {
qa_redirect('login', array('e' => $fields['email'], 'ee' => '1')); qa_redirect('login', array('e' => $fields['email'], 'ee' => '1'));
unset($fields['email']); unset($fields['email']);
...@@ -333,17 +354,18 @@ ...@@ -333,17 +354,18 @@
} }
} }
$userid=qa_create_new_user((string)@$fields['email'], null /* no password */, $handle, $userid = qa_create_new_user((string)@$fields['email'], null /* no password */, $handle,
isset($fields['level']) ? $fields['level'] : QA_USER_LEVEL_BASIC, @$fields['confirmed']); isset($fields['level']) ? $fields['level'] : QA_USER_LEVEL_BASIC, @$fields['confirmed']);
qa_db_user_login_add($userid, $source, $identifier); qa_db_user_login_add($userid, $source, $identifier);
qa_db_user_login_sync(false); qa_db_user_login_sync(false);
$profilefields=array('name', 'location', 'website', 'about'); $profilefields = array('name', 'location', 'website', 'about');
foreach ($profilefields as $fieldname) foreach ($profilefields as $fieldname) {
if (strlen(@$fields[$fieldname])) if (strlen(@$fields[$fieldname]))
qa_db_user_profile_set($userid, $fieldname, $fields[$fieldname]); qa_db_user_profile_set($userid, $fieldname, $fields[$fieldname]);
}
if (strlen(@$fields['avatar'])) if (strlen(@$fields['avatar']))
qa_set_user_avatar($userid, $fields['avatar']); qa_set_user_avatar($userid, $fields['avatar']);
...@@ -354,39 +376,39 @@ ...@@ -354,39 +376,39 @@
} }
function qa_get_logged_in_userid() /**
/* * Return the userid of the currently logged in user, or null if none logged in
Return the userid of the currently logged in user, or null if none logged in
*/ */
function qa_get_logged_in_userid()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_logged_in_userid_checked; global $qa_logged_in_userid_checked;
$suffix=qa_session_var_suffix(); $suffix = qa_session_var_suffix();
if (!$qa_logged_in_userid_checked) { // only check once if (!$qa_logged_in_userid_checked) { // only check once
qa_start_session(); // this will load logged in userid from the native PHP session, but that's not enough qa_start_session(); // this will load logged in userid from the native PHP session, but that's not enough
$sessionuserid=@$_SESSION['qa_session_userid_'.$suffix]; $sessionuserid = @$_SESSION['qa_session_userid_' . $suffix];
if (isset($sessionuserid)) // check verify code matches if (isset($sessionuserid)) // check verify code matches
if (!hash_equals(qa_session_verify_code($sessionuserid), @$_SESSION['qa_session_verify_'.$suffix])) if (!hash_equals(qa_session_verify_code($sessionuserid), @$_SESSION['qa_session_verify_' . $suffix]))
qa_clear_session_user(); qa_clear_session_user();
if (!empty($_COOKIE['qa_session'])) { if (!empty($_COOKIE['qa_session'])) {
@list($handle, $sessioncode, $remember)=explode('/', $_COOKIE['qa_session']); @list($handle, $sessioncode, $remember) = explode('/', $_COOKIE['qa_session']);
if ($remember) if ($remember)
qa_set_session_cookie($handle, $sessioncode, $remember); // extend 'remember me' cookies each time qa_set_session_cookie($handle, $sessioncode, $remember); // extend 'remember me' cookies each time
$sessioncode=trim($sessioncode); // trim to prevent passing in blank values to match uninitiated DB rows $sessioncode = trim($sessioncode); // trim to prevent passing in blank values to match uninitiated DB rows
// Try to recover session from the database if PHP session has timed out // Try to recover session from the database if PHP session has timed out
if ( (!isset($_SESSION['qa_session_userid_'.$suffix])) && (!empty($handle)) && (!empty($sessioncode)) ) { if ((!isset($_SESSION['qa_session_userid_' . $suffix])) && (!empty($handle)) && (!empty($sessioncode))) {
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
$userinfo=qa_db_single_select(qa_db_user_account_selectspec($handle, false)); // don't get any pending $userinfo = qa_db_single_select(qa_db_user_account_selectspec($handle, false)); // don't get any pending
if (strtolower(trim($userinfo['sessioncode'])) == strtolower($sessioncode)) if (strtolower(trim($userinfo['sessioncode'])) == strtolower($sessioncode))
qa_set_session_user($userinfo['userid'], $userinfo['sessionsource']); qa_set_session_user($userinfo['userid'], $userinfo['sessionsource']);
...@@ -395,25 +417,25 @@ ...@@ -395,25 +417,25 @@
} }
} }
$qa_logged_in_userid_checked=true; $qa_logged_in_userid_checked = true;
} }
return @$_SESSION['qa_session_userid_'.$suffix]; return @$_SESSION['qa_session_userid_' . $suffix];
} }
function qa_get_logged_in_source() /**
/* * Get the source of the currently logged in user, from call to qa_log_in_external_user() or null if logged in normally
Get the source of the currently logged in user, from call to qa_log_in_external_user() or null if logged in normally
*/ */
function qa_get_logged_in_source()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$userid=qa_get_logged_in_userid(); $userid = qa_get_logged_in_userid();
$suffix=qa_session_var_suffix(); $suffix = qa_session_var_suffix();
if (isset($userid)) if (isset($userid))
return @$_SESSION['qa_session_source_'.$suffix]; return @$_SESSION['qa_session_source_' . $suffix];
} }
...@@ -428,7 +450,7 @@ ...@@ -428,7 +450,7 @@
$userid = qa_get_logged_in_userid(); $userid = qa_get_logged_in_userid();
if (isset($userid)) { if (isset($userid)) {
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
$qa_cached_logged_in_user = qa_db_get_pending_result('loggedinuser', qa_db_user_account_selectspec($userid, true)); $qa_cached_logged_in_user = qa_db_get_pending_result('loggedinuser', qa_db_user_account_selectspec($userid, true));
if (!isset($qa_cached_logged_in_user)) { if (!isset($qa_cached_logged_in_user)) {
...@@ -445,6 +467,8 @@ ...@@ -445,6 +467,8 @@
/** /**
* Return $field of the currently logged in user * Return $field of the currently logged in user
* @param $field
* @return mixed|null
*/ */
function qa_get_logged_in_user_field($field) function qa_get_logged_in_user_field($field)
{ {
...@@ -456,10 +480,10 @@ ...@@ -456,10 +480,10 @@
} }
function qa_get_logged_in_points() /**
/* * Return the number of points of the currently logged in user, or null if none is logged in
Return the number of points of the currently logged in user, or null if none is logged in
*/ */
function qa_get_logged_in_points()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
...@@ -467,10 +491,10 @@ ...@@ -467,10 +491,10 @@
} }
function qa_get_mysql_user_column_type() /**
/* * Return column type to use for users (if not using single sign-on integration)
Return column type to use for users (if not using single sign-on integration)
*/ */
function qa_get_mysql_user_column_type()
{ {
return 'INT UNSIGNED'; return 'INT UNSIGNED';
} }
...@@ -515,20 +539,20 @@ ...@@ -515,20 +539,20 @@
* @param bool $favorited Show the user as favorited. * @param bool $favorited Show the user as favorited.
* @return string The user HTML. * @return string The user HTML.
*/ */
function qa_get_one_user_html($handle, $microdata=false, $favorited=false) function qa_get_one_user_html($handle, $microdata = false, $favorited = false)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
if (!strlen($handle)) if (!strlen($handle))
return ''; return '';
$url = qa_path_html('user/'.$handle); $url = qa_path_html('user/' . $handle);
$favclass = $favorited ? ' qa-user-favorited' : ''; $favclass = $favorited ? ' qa-user-favorited' : '';
$mfAttr = $microdata ? ' itemprop="name"' : ''; $mfAttr = $microdata ? ' itemprop="name"' : '';
$mfPrefix = $microdata ? '<span itemprop="author" itemscope itemtype="http://schema.org/Person">' : ''; $mfPrefix = $microdata ? '<span itemprop="author" itemscope itemtype="http://schema.org/Person">' : '';
$mfSuffix = $microdata ? '</span>' : ''; $mfSuffix = $microdata ? '</span>' : '';
return $mfPrefix . '<a href="'.$url.'" class="qa-user-link'.$favclass.'"'.$mfAttr.'>'.qa_html($handle).'</a>' . $mfSuffix; return $mfPrefix . '<a href="' . $url . '" class="qa-user-link' . $favclass . '"' . $mfAttr . '>' . qa_html($handle) . '</a>' . $mfSuffix;
} }
...@@ -543,7 +567,6 @@ ...@@ -543,7 +567,6 @@
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$link = 'https://www.gravatar.com/avatar/%s'; $link = 'https://www.gravatar.com/avatar/%s';
$params = array(md5(strtolower(trim($email)))); $params = array(md5(strtolower(trim($email))));
...@@ -657,60 +680,70 @@ ...@@ -657,60 +680,70 @@
} }
function qa_get_user_email($userid) /**
/* * Return email address for user $userid (if not using single sign-on integration)
Return email address for user $userid (if not using single sign-on integration) * @param $userid
* @return
*/ */
function qa_get_user_email($userid)
{ {
$userinfo=qa_db_select_with_pending(qa_db_user_account_selectspec($userid, true)); $userinfo = qa_db_select_with_pending(qa_db_user_account_selectspec($userid, true));
return $userinfo['email']; return $userinfo['email'];
} }
function qa_user_report_action($userid, $action) /**
/* * Called after a database write $action performed by a user $userid
Called after a database write $action performed by a user $userid * @param $userid
* @param $action
* @return mixed
*/ */
function qa_user_report_action($userid, $action)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'db/users.php'; require_once QA_INCLUDE_DIR . 'db/users.php';
qa_db_user_written($userid, qa_remote_ip_address()); qa_db_user_written($userid, qa_remote_ip_address());
} }
function qa_user_level_string($level) /**
/* * Return textual representation of the user $level
Return textual representation of the user $level * @param $level
* @return mixed|string
*/ */
function qa_user_level_string($level)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
if ($level>=QA_USER_LEVEL_SUPER) if ($level >= QA_USER_LEVEL_SUPER)
$string='users/level_super'; $string = 'users/level_super';
elseif ($level>=QA_USER_LEVEL_ADMIN) elseif ($level >= QA_USER_LEVEL_ADMIN)
$string='users/level_admin'; $string = 'users/level_admin';
elseif ($level>=QA_USER_LEVEL_MODERATOR) elseif ($level >= QA_USER_LEVEL_MODERATOR)
$string='users/level_moderator'; $string = 'users/level_moderator';
elseif ($level>=QA_USER_LEVEL_EDITOR) elseif ($level >= QA_USER_LEVEL_EDITOR)
$string='users/level_editor'; $string = 'users/level_editor';
elseif ($level>=QA_USER_LEVEL_EXPERT) elseif ($level >= QA_USER_LEVEL_EXPERT)
$string='users/level_expert'; $string = 'users/level_expert';
elseif ($level>=QA_USER_LEVEL_APPROVED) elseif ($level >= QA_USER_LEVEL_APPROVED)
$string='users/approved_user'; $string = 'users/approved_user';
else else
$string='users/registered_user'; $string = 'users/registered_user';
return qa_lang($string); return qa_lang($string);
} }
function qa_get_login_links($rooturl, $tourl) /**
/* * Return an array of links to login, register, email confirm and logout pages (if not using single sign-on integration)
Return an array of links to login, register, email confirm and logout pages (if not using single sign-on integration) * @param $rooturl
* @param $tourl
* @return array
*/ */
function qa_get_login_links($rooturl, $tourl)
{ {
return array( return array(
'login' => qa_path('login', isset($tourl) ? array('to' => $tourl) : null, $rooturl), 'login' => qa_path('login', isset($tourl) ? array('to' => $tourl) : null, $rooturl),
...@@ -720,235 +753,260 @@ ...@@ -720,235 +753,260 @@
); );
} }
} // end of: if (QA_FINAL_EXTERNAL_USERS) { ... } else { ... } } // end of: if (QA_FINAL_EXTERNAL_USERS) { ... } else { ... }
function qa_is_logged_in() /**
/* * Return whether someone is logged in at the moment
Return whether someone is logged in at the moment */
*/ function qa_is_logged_in()
{ {
$userid=qa_get_logged_in_userid(); $userid = qa_get_logged_in_userid();
return isset($userid); return isset($userid);
} }
function qa_get_logged_in_handle() /**
/* * Return displayable handle/username of currently logged in user, or null if none
Return displayable handle/username of currently logged in user, or null if none */
*/ function qa_get_logged_in_handle()
{ {
return qa_get_logged_in_user_field(QA_FINAL_EXTERNAL_USERS ? 'publicusername' : 'handle'); return qa_get_logged_in_user_field(QA_FINAL_EXTERNAL_USERS ? 'publicusername' : 'handle');
} }
function qa_get_logged_in_email() /**
/* * Return email of currently logged in user, or null if none
Return email of currently logged in user, or null if none */
*/ function qa_get_logged_in_email()
{ {
return qa_get_logged_in_user_field('email'); return qa_get_logged_in_user_field('email');
} }
function qa_get_logged_in_level() /**
/* * Return level of currently logged in user, or null if none
Return level of currently logged in user, or null if none */
*/ function qa_get_logged_in_level()
{ {
return qa_get_logged_in_user_field('level'); return qa_get_logged_in_user_field('level');
} }
function qa_get_logged_in_flags() /**
/* * Return flags (see QA_USER_FLAGS_*) of currently logged in user, or null if none
Return flags (see QA_USER_FLAGS_*) of currently logged in user, or null if none */
*/ function qa_get_logged_in_flags()
{ {
if (QA_FINAL_EXTERNAL_USERS) if (QA_FINAL_EXTERNAL_USERS)
return qa_get_logged_in_user_field('blocked') ? QA_USER_FLAGS_USER_BLOCKED : 0; return qa_get_logged_in_user_field('blocked') ? QA_USER_FLAGS_USER_BLOCKED : 0;
else else
return qa_get_logged_in_user_field('flags'); return qa_get_logged_in_user_field('flags');
} }
function qa_get_logged_in_levels() /**
/* * Return an array of all the specific (e.g. per category) level privileges for the logged in user, retrieving from the database if necessary
Return an array of all the specific (e.g. per category) level privileges for the logged in user, retrieving from the database if necessary */
*/ function qa_get_logged_in_levels()
{ {
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
return qa_db_get_pending_result('userlevels', qa_db_user_levels_selectspec(qa_get_logged_in_userid(), true)); return qa_db_get_pending_result('userlevels', qa_db_user_levels_selectspec(qa_get_logged_in_userid(), true));
} }
function qa_userids_to_handles($userids) /**
/* * Return an array mapping each userid in $userids to that user's handle (public username), or to null if not found
Return an array mapping each userid in $userids to that user's handle (public username), or to null if not found * @param $userids
*/ * @return array
{ */
function qa_userids_to_handles($userids)
{
if (QA_FINAL_EXTERNAL_USERS) if (QA_FINAL_EXTERNAL_USERS)
$rawuseridhandles=qa_get_public_from_userids($userids); $rawuseridhandles = qa_get_public_from_userids($userids);
else { else {
require_once QA_INCLUDE_DIR.'db/users.php'; require_once QA_INCLUDE_DIR . 'db/users.php';
$rawuseridhandles=qa_db_user_get_userid_handles($userids); $rawuseridhandles = qa_db_user_get_userid_handles($userids);
} }
$gotuseridhandles=array(); $gotuseridhandles = array();
foreach ($userids as $userid) foreach ($userids as $userid)
$gotuseridhandles[$userid]=@$rawuseridhandles[$userid]; $gotuseridhandles[$userid] = @$rawuseridhandles[$userid];
return $gotuseridhandles; return $gotuseridhandles;
} }
function qa_userid_to_handle($userid) /**
/* * Return an string mapping the received userid to that user's handle (public username), or to null if not found
Return an string mapping the received userid to that user's handle (public username), or to null if not found * @param $userid
*/ * @return mixed|null
{ */
$handles=qa_userids_to_handles(array($userid)); function qa_userid_to_handle($userid)
{
$handles = qa_userids_to_handles(array($userid));
return empty($handles) ? null : $handles[$userid]; return empty($handles) ? null : $handles[$userid];
} }
function qa_handles_to_userids($handles, $exactonly=false) /**
/* * Return an array mapping each handle in $handles the user's userid, or null if not found. If $exactonly is true then
Return an array mapping each handle in $handles the user's userid, or null if not found. If $exactonly is true then * $handles must have the correct case and accents. Otherwise, handles are case- and accent-insensitive, and the keys
$handles must have the correct case and accents. Otherwise, handles are case- and accent-insensitive, and the keys * of the returned array will match the $handles provided, not necessary those in the DB.
of the returned array will match the $handles provided, not necessary those in the DB. * @param $handles
*/ * @param bool $exactonly
{ * @return array
require_once QA_INCLUDE_DIR.'util/string.php'; */
function qa_handles_to_userids($handles, $exactonly = false)
{
require_once QA_INCLUDE_DIR . 'util/string.php';
if (QA_FINAL_EXTERNAL_USERS) if (QA_FINAL_EXTERNAL_USERS)
$rawhandleuserids=qa_get_userids_from_public($handles); $rawhandleuserids = qa_get_userids_from_public($handles);
else { else {
require_once QA_INCLUDE_DIR.'db/users.php'; require_once QA_INCLUDE_DIR . 'db/users.php';
$rawhandleuserids=qa_db_user_get_handle_userids($handles); $rawhandleuserids = qa_db_user_get_handle_userids($handles);
} }
$gothandleuserids=array(); $gothandleuserids = array();
if ($exactonly) { // only take the exact matches if ($exactonly) { // only take the exact matches
foreach ($handles as $handle) foreach ($handles as $handle)
$gothandleuserids[$handle]=@$rawhandleuserids[$handle]; $gothandleuserids[$handle] = @$rawhandleuserids[$handle];
} else { // normalize to lowercase without accents, and then find matches } else { // normalize to lowercase without accents, and then find matches
$normhandleuserids=array(); $normhandleuserids = array();
foreach ($rawhandleuserids as $handle => $userid) foreach ($rawhandleuserids as $handle => $userid)
$normhandleuserids[qa_string_remove_accents(qa_strtolower($handle))]=$userid; $normhandleuserids[qa_string_remove_accents(qa_strtolower($handle))] = $userid;
foreach ($handles as $handle) foreach ($handles as $handle)
$gothandleuserids[$handle]=@$normhandleuserids[qa_string_remove_accents(qa_strtolower($handle))]; $gothandleuserids[$handle] = @$normhandleuserids[qa_string_remove_accents(qa_strtolower($handle))];
} }
return $gothandleuserids; return $gothandleuserids;
} }
function qa_handle_to_userid($handle) /**
/* * Return the userid corresponding to $handle (not case- or accent-sensitive)
Return the userid corresponding to $handle (not case- or accent-sensitive) * @param $handle
*/ * @return mixed|null
{ */
function qa_handle_to_userid($handle)
{
if (QA_FINAL_EXTERNAL_USERS) if (QA_FINAL_EXTERNAL_USERS)
$handleuserids=qa_get_userids_from_public(array($handle)); $handleuserids = qa_get_userids_from_public(array($handle));
else { else {
require_once QA_INCLUDE_DIR.'db/users.php'; require_once QA_INCLUDE_DIR . 'db/users.php';
$handleuserids=qa_db_user_get_handle_userids(array($handle)); $handleuserids = qa_db_user_get_handle_userids(array($handle));
} }
if (count($handleuserids)==1) if (count($handleuserids) == 1)
return reset($handleuserids); // don't use $handleuserids[$handle] since capitalization might be different return reset($handleuserids); // don't use $handleuserids[$handle] since capitalization might be different
return null; return null;
} }
function qa_user_level_for_categories($categoryids) /**
/* * Return the level of the logged in user for a post with $categoryids (expressing the full hierarchy to the final category)
Return the level of the logged in user for a post with $categoryids (expressing the full hierarchy to the final category) * @param $categoryids
*/ * @return mixed|null
{ */
function qa_user_level_for_categories($categoryids)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'app/updates.php'; require_once QA_INCLUDE_DIR . 'app/updates.php';
$level=qa_get_logged_in_level(); $level = qa_get_logged_in_level();
if (count($categoryids)) { if (count($categoryids)) {
$userlevels=qa_get_logged_in_levels(); $userlevels = qa_get_logged_in_levels();
$categorylevels=array(); // create a map $categorylevels = array(); // create a map
foreach ($userlevels as $userlevel) foreach ($userlevels as $userlevel) {
if ($userlevel['entitytype']==QA_ENTITY_CATEGORY) if ($userlevel['entitytype'] == QA_ENTITY_CATEGORY)
$categorylevels[$userlevel['entityid']]=$userlevel['level']; $categorylevels[$userlevel['entityid']] = $userlevel['level'];
}
foreach ($categoryids as $categoryid) foreach ($categoryids as $categoryid) {
$level=max($level, @$categorylevels[$categoryid]); $level = max($level, @$categorylevels[$categoryid]);
}
} }
return $level; return $level;
} }
function qa_user_level_for_post($post) /**
/* * Return the level of the logged in user for $post, as retrieved from the database
Return the level of the logged in user for $post, as retrieved from the database * @param $post
*/ * @return mixed|null
{ */
function qa_user_level_for_post($post)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
if (strlen(@$post['categoryids'])) if (strlen(@$post['categoryids']))
return qa_user_level_for_categories(explode(',', $post['categoryids'])); return qa_user_level_for_categories(explode(',', $post['categoryids']));
return null; return null;
} }
function qa_user_level_maximum() /**
/* * Return the maximum possible level of the logged in user in any context (i.e. for any category)
Return the maximum possible level of the logged in user in any context (i.e. for any category) */
*/ function qa_user_level_maximum()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$level=qa_get_logged_in_level(); $level = qa_get_logged_in_level();
$userlevels=qa_get_logged_in_levels(); $userlevels = qa_get_logged_in_levels();
foreach ($userlevels as $userlevel) foreach ($userlevels as $userlevel) {
$level=max($level, $userlevel['level']); $level = max($level, $userlevel['level']);
}
return $level; return $level;
} }
function qa_user_post_permit_error($permitoption, $post, $limitaction=null, $checkblocks=true) /**
/* * Check whether the logged in user has permission to perform $permitoption on post $post (from the database)
Check whether the logged in user has permission to perform $permitoption on post $post (from the database) * Other parameters and the return value are as for qa_user_permit_error(...)
Other parameters and the return value are as for qa_user_permit_error(...) * @param $permitoption
*/ * @param $post
{ * @param $limitaction
* @param bool $checkblocks
* @return bool|string
*/
function qa_user_post_permit_error($permitoption, $post, $limitaction = null, $checkblocks = true)
{
return qa_user_permit_error($permitoption, $limitaction, qa_user_level_for_post($post), $checkblocks); return qa_user_permit_error($permitoption, $limitaction, qa_user_level_for_post($post), $checkblocks);
} }
function qa_user_maximum_permit_error($permitoption, $limitaction=null, $checkblocks=true) /**
/* * Check whether the logged in user would have permittion to perform $permitoption in any context (i.e. for any category)
Check whether the logged in user would have permittion to perform $permitoption in any context (i.e. for any category) * Other parameters and the return value are as for qa_user_permit_error(...)
Other parameters and the return value are as for qa_user_permit_error(...) * @param $permitoption
*/ * @param $limitaction
{ * @param bool $checkblocks
* @return bool|string
*/
function qa_user_maximum_permit_error($permitoption, $limitaction = null, $checkblocks = true)
{
return qa_user_permit_error($permitoption, $limitaction, qa_user_level_maximum(), $checkblocks); return qa_user_permit_error($permitoption, $limitaction, qa_user_level_maximum(), $checkblocks);
} }
/** /**
* Check whether the logged in user has permission to perform an action. * Check whether the logged in user has permission to perform an action.
* *
* @param string $permitoption The permission to check (if null, this simply checks whether the user is blocked). * @param string $permitoption The permission to check (if null, this simply checks whether the user is blocked).
...@@ -968,11 +1026,11 @@ ...@@ -968,11 +1026,11 @@
* 'limit' => the user or IP address has reached a rate limit (if $limitaction specified) * 'limit' => the user or IP address has reached a rate limit (if $limitaction specified)
* false => the operation can go ahead * false => the operation can go ahead
*/ */
function qa_user_permit_error($permitoption=null, $limitaction=null, $userlevel=null, $checkblocks=true, $userfields=null) function qa_user_permit_error($permitoption = null, $limitaction = null, $userlevel = null, $checkblocks = true, $userfields = null)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'app/limits.php'; require_once QA_INCLUDE_DIR . 'app/limits.php';
if (!isset($userfields)) if (!isset($userfields))
$userfields = qa_get_logged_in_user_cache(); $userfields = qa_get_logged_in_user_cache();
...@@ -1003,10 +1061,10 @@ ...@@ -1003,10 +1061,10 @@
} }
return $error; return $error;
} }
/** /**
* Check whether user can perform $permitoption. Result as for qa_user_permit_error(...). * Check whether user can perform $permitoption. Result as for qa_user_permit_error(...).
* *
* @param string $permitoption Permission option name (from database) for action. * @param string $permitoption Permission option name (from database) for action.
...@@ -1017,8 +1075,8 @@ ...@@ -1017,8 +1075,8 @@
* *
* @return string|bool Reason the user is not permitted, or false if the operation can go ahead. * @return string|bool Reason the user is not permitted, or false if the operation can go ahead.
*/ */
function qa_permit_error($permitoption, $userid, $userlevel, $userflags, $userpoints=null) function qa_permit_error($permitoption, $userid, $userlevel, $userflags, $userpoints = null)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$permit = isset($permitoption) ? qa_opt($permitoption) : QA_PERMIT_ALL; $permit = isset($permitoption) ? qa_opt($permitoption) : QA_PERMIT_ALL;
...@@ -1038,10 +1096,10 @@ ...@@ -1038,10 +1096,10 @@
} }
return qa_permit_value_error($permit, $userid, $userlevel, $userflags); return qa_permit_value_error($permit, $userid, $userlevel, $userflags);
} }
/** /**
* Check whether user can reach the permission level. Result as for qa_user_permit_error(...). * Check whether user can reach the permission level. Result as for qa_user_permit_error(...).
* *
* @param int $permit Permission constant. * @param int $permit Permission constant.
...@@ -1051,8 +1109,8 @@ ...@@ -1051,8 +1109,8 @@
* *
* @return string|bool Reason the user is not permitted, or false if the operation can go ahead * @return string|bool Reason the user is not permitted, or false if the operation can go ahead
*/ */
function qa_permit_value_error($permit, $userid, $userlevel, $userflags) function qa_permit_value_error($permit, $userid, $userlevel, $userflags)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
if (!isset($userid) && $permit < QA_PERMIT_ALL) if (!isset($userid) && $permit < QA_PERMIT_ALL)
...@@ -1083,8 +1141,7 @@ ...@@ -1083,8 +1141,7 @@
!$confirmed // actual confirmation !$confirmed // actual confirmation
) )
return 'confirm'; return 'confirm';
} } elseif ($permit >= QA_PERMIT_APPROVED) {
elseif ($permit >= QA_PERMIT_APPROVED) {
if ( if (
qa_opt('moderate_users') && // if this option off, we can't ask it of the user qa_opt('moderate_users') && // if this option off, we can't ask it of the user
$userlevel < QA_USER_LEVEL_APPROVED // user has not been approved $userlevel < QA_USER_LEVEL_APPROVED // user has not been approved
...@@ -1093,107 +1150,115 @@ ...@@ -1093,107 +1150,115 @@
} }
return false; return false;
} }
function qa_user_captcha_reason($userlevel=null) /**
/* * Return whether a captcha is required for posts submitted by the current user. You can pass in a QA_USER_LEVEL_*
Return whether a captcha is required for posts submitted by the current user. You can pass in a QA_USER_LEVEL_* * constant in $userlevel to consider the user at a different level to usual (e.g. if they are performing this action
constant in $userlevel to consider the user at a different level to usual (e.g. if they are performing this action * in a category for which they have elevated privileges).
in a category for which they have elevated privileges). *
* Possible results:
Possible results: * 'login' => captcha required because the user is not logged in
'login' => captcha required because the user is not logged in * 'approve' => captcha required because the user has not been approved
'approve' => captcha required because the user has not been approved * 'confirm' => captcha required because the user has not confirmed their email address
'confirm' => captcha required because the user has not confirmed their email address * false => captcha is not required
false => captcha is not required * @param $userlevel
*/ * @return bool|mixed|string
{ */
function qa_user_captcha_reason($userlevel = null)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$reason=false; $reason = false;
if (!isset($userlevel)) if (!isset($userlevel))
$userlevel=qa_get_logged_in_level(); $userlevel = qa_get_logged_in_level();
if ($userlevel < QA_USER_LEVEL_APPROVED) { // approved users and above aren't shown captchas if ($userlevel < QA_USER_LEVEL_APPROVED) { // approved users and above aren't shown captchas
$userid=qa_get_logged_in_userid(); $userid = qa_get_logged_in_userid();
if (qa_opt('captcha_on_anon_post') && !isset($userid)) if (qa_opt('captcha_on_anon_post') && !isset($userid))
$reason='login'; $reason = 'login';
elseif (qa_opt('moderate_users') && qa_opt('captcha_on_unapproved')) elseif (qa_opt('moderate_users') && qa_opt('captcha_on_unapproved'))
$reason='approve'; $reason = 'approve';
elseif (qa_opt('confirm_user_emails') && qa_opt('captcha_on_unconfirmed') && !(qa_get_logged_in_flags() & QA_USER_FLAGS_EMAIL_CONFIRMED) ) elseif (qa_opt('confirm_user_emails') && qa_opt('captcha_on_unconfirmed') && !(qa_get_logged_in_flags() & QA_USER_FLAGS_EMAIL_CONFIRMED))
$reason='confirm'; $reason = 'confirm';
} }
return $reason; return $reason;
} }
function qa_user_use_captcha($userlevel=null) /**
/* * Return whether a captcha should be presented to the logged in user for writing posts. You can pass in a
Return whether a captcha should be presented to the logged in user for writing posts. You can pass in a * QA_USER_LEVEL_* constant in $userlevel to consider the user at a different level to usual.
QA_USER_LEVEL_* constant in $userlevel to consider the user at a different level to usual. * @param $userlevel
*/ * @return bool|mixed
{ */
function qa_user_use_captcha($userlevel = null)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return qa_user_captcha_reason($userlevel)!=false; return qa_user_captcha_reason($userlevel) != false;
} }
function qa_user_moderation_reason($userlevel=null) /**
/* * Return whether moderation is required for posts submitted by the current user. You can pass in a QA_USER_LEVEL_*
Return whether moderation is required for posts submitted by the current user. You can pass in a QA_USER_LEVEL_* * constant in $userlevel to consider the user at a different level to usual (e.g. if they are performing this action
constant in $userlevel to consider the user at a different level to usual (e.g. if they are performing this action * in a category for which they have elevated privileges).
in a category for which they have elevated privileges). *
* Possible results:
Possible results: * 'login' => moderation required because the user is not logged in
'login' => moderation required because the user is not logged in * 'approve' => moderation required because the user has not been approved
'approve' => moderation required because the user has not been approved * 'confirm' => moderation required because the user has not confirmed their email address
'confirm' => moderation required because the user has not confirmed their email address * 'points' => moderation required because the user has insufficient points
'points' => moderation required because the user has insufficient points * false => moderation is not required
false => moderation is not required * @param $userlevel
*/ * @return bool|string
{ */
function qa_user_moderation_reason($userlevel = null)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$reason=false; $reason = false;
if (!isset($userlevel)) if (!isset($userlevel))
$userlevel=qa_get_logged_in_level(); $userlevel = qa_get_logged_in_level();
if ( if (
($userlevel < QA_USER_LEVEL_EXPERT) && // experts and above aren't moderated ($userlevel < QA_USER_LEVEL_EXPERT) && // experts and above aren't moderated
qa_user_permit_error('permit_moderate') // if the user can approve posts, no point in moderating theirs qa_user_permit_error('permit_moderate') // if the user can approve posts, no point in moderating theirs
) { ) {
$userid=qa_get_logged_in_userid(); $userid = qa_get_logged_in_userid();
if (isset($userid)) { if (isset($userid)) {
if (qa_opt('moderate_users') && qa_opt('moderate_unapproved') && ($userlevel<QA_USER_LEVEL_APPROVED)) if (qa_opt('moderate_users') && qa_opt('moderate_unapproved') && ($userlevel < QA_USER_LEVEL_APPROVED))
$reason='approve'; $reason = 'approve';
elseif (qa_opt('confirm_user_emails') && qa_opt('moderate_unconfirmed') && !(qa_get_logged_in_flags() & QA_USER_FLAGS_EMAIL_CONFIRMED) ) elseif (qa_opt('confirm_user_emails') && qa_opt('moderate_unconfirmed') && !(qa_get_logged_in_flags() & QA_USER_FLAGS_EMAIL_CONFIRMED))
$reason='confirm'; $reason = 'confirm';
elseif (qa_opt('moderate_by_points') && (qa_get_logged_in_points() < qa_opt('moderate_points_limit'))) elseif (qa_opt('moderate_by_points') && (qa_get_logged_in_points() < qa_opt('moderate_points_limit')))
$reason='points'; $reason = 'points';
} elseif (qa_opt('moderate_anon_post')) } elseif (qa_opt('moderate_anon_post'))
$reason='login'; $reason = 'login';
} }
return $reason; return $reason;
} }
function qa_user_userfield_label($userfield) /**
/* * Return the label to display for $userfield as retrieved from the database, using default if no name set
Return the label to display for $userfield as retrieved from the database, using default if no name set * @param $userfield
*/ * @return string
{ */
function qa_user_userfield_label($userfield)
{
if (isset($userfield['content'])) if (isset($userfield['content']))
return $userfield['content']; return $userfield['content'];
else { else {
$defaultlabels=array( $defaultlabels = array(
'name' => 'users/full_name', 'name' => 'users/full_name',
'about' => 'users/about', 'about' => 'users/about',
'location' => 'users/location', 'location' => 'users/location',
...@@ -1205,136 +1270,144 @@ in a category for which they have elevated privileges). ...@@ -1205,136 +1270,144 @@ in a category for which they have elevated privileges).
} }
return ''; return '';
} }
function qa_set_form_security_key() /**
/* * Set or extend the cookie in browser of non logged-in users which identifies them for the purposes of form security (anti-CSRF protection)
Set or extend the cookie in browser of non logged-in users which identifies them for the purposes of form security (anti-CSRF protection) */
*/ function qa_set_form_security_key()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_form_key_cookie_set; global $qa_form_key_cookie_set;
if ( (!qa_is_logged_in()) && !@$qa_form_key_cookie_set) { if ((!qa_is_logged_in()) && !@$qa_form_key_cookie_set) {
$qa_form_key_cookie_set=true; $qa_form_key_cookie_set = true;
if (strlen(@$_COOKIE['qa_key'])!=QA_FORM_KEY_LENGTH) { if (strlen(@$_COOKIE['qa_key']) != QA_FORM_KEY_LENGTH) {
require_once QA_INCLUDE_DIR.'util/string.php'; require_once QA_INCLUDE_DIR . 'util/string.php';
$_COOKIE['qa_key']=qa_random_alphanum(QA_FORM_KEY_LENGTH); $_COOKIE['qa_key'] = qa_random_alphanum(QA_FORM_KEY_LENGTH);
} }
setcookie('qa_key', $_COOKIE['qa_key'], time()+2*QA_FORM_EXPIRY_SECS, '/', QA_COOKIE_DOMAIN, (bool)ini_get('session.cookie_secure'), true); // extend on every page request setcookie('qa_key', $_COOKIE['qa_key'], time() + 2 * QA_FORM_EXPIRY_SECS, '/', QA_COOKIE_DOMAIN, (bool)ini_get('session.cookie_secure'), true); // extend on every page request
}
} }
}
function qa_calc_form_security_hash($action, $timestamp) /**
/* * Return the form security (anti-CSRF protection) hash for an $action (any string), that can be performed within
Return the form security (anti-CSRF protection) hash for an $action (any string), that can be performed within * QA_FORM_EXPIRY_SECS of $timestamp (in unix seconds) by the current user.
QA_FORM_EXPIRY_SECS of $timestamp (in unix seconds) by the current user. * @param $action
*/ * @param $timestamp
{ * @return mixed|string
*/
function qa_calc_form_security_hash($action, $timestamp)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$salt=qa_opt('form_security_salt'); $salt = qa_opt('form_security_salt');
if (qa_is_logged_in()) if (qa_is_logged_in())
return sha1($salt.'/'.$action.'/'.$timestamp.'/'.qa_get_logged_in_userid().'/'.qa_get_logged_in_user_field('passsalt')); return sha1($salt . '/' . $action . '/' . $timestamp . '/' . qa_get_logged_in_userid() . '/' . qa_get_logged_in_user_field('passsalt'));
else else
return sha1($salt.'/'.$action.'/'.$timestamp.'/'.@$_COOKIE['qa_key']); // lower security for non logged in users - code+cookie can be transferred return sha1($salt . '/' . $action . '/' . $timestamp . '/' . @$_COOKIE['qa_key']); // lower security for non logged in users - code+cookie can be transferred
} }
function qa_get_form_security_code($action) /**
/* * Return the full form security (anti-CSRF protection) code for an $action (any string) performed within
Return the full form security (anti-CSRF protection) code for an $action (any string) performed within * QA_FORM_EXPIRY_SECS of now by the current user.
QA_FORM_EXPIRY_SECS of now by the current user. * @param $action
*/ * @return mixed|string
{ */
function qa_get_form_security_code($action)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
qa_set_form_security_key(); qa_set_form_security_key();
$timestamp=qa_opt('db_time'); $timestamp = qa_opt('db_time');
return (int)qa_is_logged_in().'-'.$timestamp.'-'.qa_calc_form_security_hash($action, $timestamp); return (int)qa_is_logged_in() . '-' . $timestamp . '-' . qa_calc_form_security_hash($action, $timestamp);
} }
function qa_check_form_security_code($action, $value) /**
/* * Return whether $value matches the expected form security (anti-CSRF protection) code for $action (any string) and
Return whether $value matches the expected form security (anti-CSRF protection) code for $action (any string) and * that the code has not expired (if more than QA_FORM_EXPIRY_SECS have passed). Logs causes for suspicion.
that the code has not expired (if more than QA_FORM_EXPIRY_SECS have passed). Logs causes for suspicion. * @param $action
*/ * @param $value
{ * @return bool
*/
function qa_check_form_security_code($action, $value)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$reportproblems=array(); $reportproblems = array();
$silentproblems=array(); $silentproblems = array();
if (!isset($value)) if (!isset($value)) {
$silentproblems[]='code missing'; $silentproblems[] = 'code missing';
elseif (!strlen($value)) } elseif (!strlen($value)) {
$silentproblems[]='code empty'; $silentproblems[] = 'code empty';
else { } else {
$parts=explode('-', $value); $parts = explode('-', $value);
if (count($parts)==3) { if (count($parts) == 3) {
$loggedin=$parts[0]; $loggedin = $parts[0];
$timestamp=$parts[1]; $timestamp = $parts[1];
$hash=$parts[2]; $hash = $parts[2];
$timenow=qa_opt('db_time'); $timenow = qa_opt('db_time');
if ($timestamp>$timenow) if ($timestamp > $timenow) {
$reportproblems[]='time '.($timestamp-$timenow).'s in future'; $reportproblems[] = 'time ' . ($timestamp - $timenow) . 's in future';
elseif ($timestamp<($timenow-QA_FORM_EXPIRY_SECS)) } elseif ($timestamp < ($timenow - QA_FORM_EXPIRY_SECS)) {
$silentproblems[]='timeout after '.($timenow-$timestamp).'s'; $silentproblems[] = 'timeout after ' . ($timenow - $timestamp) . 's';
}
if (qa_is_logged_in()) { if (qa_is_logged_in()) {
if (!$loggedin) if (!$loggedin) {
$silentproblems[]='now logged in'; $silentproblems[] = 'now logged in';
}
} else { } else {
if ($loggedin) if ($loggedin) {
$silentproblems[]='now logged out'; $silentproblems[] = 'now logged out';
} else {
else { $key = @$_COOKIE['qa_key'];
$key=@$_COOKIE['qa_key'];
if (!isset($key)) if (!isset($key)) {
$silentproblems[]='key cookie missing'; $silentproblems[] = 'key cookie missing';
elseif (!strlen($key)) } elseif (!strlen($key)) {
$silentproblems[]='key cookie empty'; $silentproblems[] = 'key cookie empty';
elseif (strlen($key)!=QA_FORM_KEY_LENGTH) } elseif (strlen($key) != QA_FORM_KEY_LENGTH) {
$reportproblems[]='key cookie '.$key.' invalid'; $reportproblems[] = 'key cookie ' . $key . ' invalid';
}
} }
} }
if (empty($silentproblems) && empty($reportproblems)) if (empty($silentproblems) && empty($reportproblems)) {
if (!hash_equals(strtolower(qa_calc_form_security_hash($action, $timestamp)), strtolower($hash))) if (!hash_equals(strtolower(qa_calc_form_security_hash($action, $timestamp)), strtolower($hash))) {
$reportproblems[]='code mismatch'; $reportproblems[] = 'code mismatch';
}
}
} else } else {
$reportproblems[]='code '.$value.' malformed'; $reportproblems[] = 'code ' . $value . ' malformed';
}
} }
if (!empty($reportproblems) && QA_DEBUG_PERFORMANCE) if (!empty($reportproblems) && QA_DEBUG_PERFORMANCE) {
@error_log( @error_log(
'PHP Question2Answer form security violation for '.$action. 'PHP Question2Answer form security violation for ' . $action .
' by '.(qa_is_logged_in() ? ('userid '.qa_get_logged_in_userid()) : 'anonymous'). ' by ' . (qa_is_logged_in() ? ('userid ' . qa_get_logged_in_userid()) : 'anonymous') .
' ('.implode(', ', array_merge($reportproblems, $silentproblems)).')'. ' (' . implode(', ', array_merge($reportproblems, $silentproblems)) . ')' .
' on '.@$_SERVER['REQUEST_URI']. ' on ' . @$_SERVER['REQUEST_URI'] .
' via '.@$_SERVER['HTTP_REFERER'] ' via ' . @$_SERVER['HTTP_REFERER']
); );
return (empty($silentproblems) && empty($reportproblems));
} }
return (empty($silentproblems) && empty($reportproblems));
/* }
Omit PHP closing tag to help avoid accidental output
*/
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment