Commit ec46ded0 by Scott Vivian

Trim trailing space

parent 0961d5dd
......@@ -2,7 +2,7 @@
<head>
<meta http-equiv="refresh" content="2;url=http://www.question2answer.org/versions.php">
</head>
<body>
Redirecting... if nothing happens, <a href="http://www.question2answer.org/versions.php">click here</a>.
</body>
......
......@@ -2,7 +2,7 @@
<head>
<meta http-equiv="refresh" content="2;url=http://www.question2answer.org/license.php">
</head>
<body>
Redirecting... if nothing happens, <a href="http://www.question2answer.org/license.php">click here</a>.
</body>
......
......@@ -2,7 +2,7 @@
<head>
<meta http-equiv="refresh" content="2;url=http://www.question2answer.org/install.php">
</head>
<body>
Redirecting... if nothing happens, <a href="http://www.question2answer.org/install.php">click here</a>.
</body>
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-config-example.php
Version: See define()s at top of qa-include/qa-base.php
Description: After renaming, use this to set up database details and other stuff
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -34,10 +34,10 @@
define('QA_MYSQL_USERNAME', 'your-mysql-username');
define('QA_MYSQL_PASSWORD', 'your-mysql-password');
define('QA_MYSQL_DATABASE', 'your-mysql-db-name');
/*
Ultra-concise installation instructions:
1. Create a MySQL database.
2. Create a MySQL user with full permissions for that database.
3. Rename this file to qa-config.php.
......@@ -58,7 +58,7 @@
*/
define('QA_MYSQL_TABLE_PREFIX', 'qa_');
/*
If you wish, you can define QA_MYSQL_USERS_PREFIX separately from QA_MYSQL_TABLE_PREFIX.
If so, it is used instead of QA_MYSQL_TABLE_PREFIX as the prefix for tables containing
......@@ -76,16 +76,16 @@
also have the same value for QA_BLOBS_DIRECTORY.
If there are already some BLOBs stored in the database from previous uploads, click the
'Move BLOBs to disk' button in the 'Stats' section of the admin panel to move them to disk.
'Move BLOBs to disk' button in the 'Stats' section of the admin panel to move them to disk.
define('QA_BLOBS_DIRECTORY', '/path/to/writable_blobs_directory/');
*/
/*
If you wish, you can define QA_COOKIE_DOMAIN so that any cookies created by Q2A are assigned
to a specific domain name, instead of the full domain name of the request by default. This is
useful if you're running multiple Q2A sites on subdomains with a shared user base.
useful if you're running multiple Q2A sites on subdomains with a shared user base.
define('QA_COOKIE_DOMAIN', '.example.com'); // be sure to keep the leading period
*/
......@@ -94,7 +94,7 @@
The key of each array element should be the standard part of the path, e.g. 'questions',
and the value should be the replacement for that standard part, e.g. 'topics'. If you edit this
file in UTF-8 encoding you can also use non-ASCII characters in these URLs.
$QA_CONST_PATH_MAP=array(
'questions' => 'topics',
'categories' => 'sections',
......@@ -107,11 +107,11 @@
Set QA_EXTERNAL_USERS to true to use your user identification code in qa-external/qa-external-users.php
This allows you to integrate with your existing user database and management system. For more details,
consult the online documentation on installing Question2Answer with single sign-on.
The constants QA_EXTERNAL_LANG and QA_EXTERNAL_EMAILER are deprecated from Q2A 1.5 since the same
effect can now be achieved in plugins by using function overrides.
*/
define('QA_EXTERNAL_USERS', false);
/*
......@@ -119,26 +119,26 @@
database, define QA_WORDPRESS_INTEGRATE_PATH as the full path to the WordPress directory
containing wp-load.php. You do not need to set the QA_MYSQL_* constants above since these
will be taken from WordPress automatically. See online documentation for more details.
define('QA_WORDPRESS_INTEGRATE_PATH', '/PATH/TO/WORDPRESS');
*/
/*
Some settings to help optimize your Question2Answer site's performance.
If QA_HTML_COMPRESSION is true, HTML web pages will be output using Gzip compression, if
the user's browser indicates this is supported. This will increase the performance of your
site, but may make debugging harder if PHP does not complete execution.
QA_MAX_LIMIT_START is the maximum start parameter that can be requested, for paging through
long lists of questions, etc... As the start parameter gets higher, queries tend to get
slower, since MySQL must examine more information. Very high start numbers are usually only
requested by search engine robots anyway.
If a word is used QA_IGNORED_WORDS_FREQ times or more in a particular way, it is ignored
when searching or finding related questions. This saves time by ignoring words which are so
common that they are probably not worth matching on.
Set QA_ALLOW_UNINDEXED_QUERIES to true if you don't mind running some database queries which
are not indexed efficiently. For example, this will enable browsing unanswered questions per
category. If your database becomes large, these queries could become costly.
......@@ -146,15 +146,15 @@
Set QA_OPTIMIZE_LOCAL_DB to true if your web server and MySQL are running on the same box.
When viewing a page on your site, this will use many simple MySQL queries instead of fewer
complex ones, which makes sense since there is no latency for localhost access.
Set QA_OPTIMIZE_DISTANT_DB to true if your web server and MySQL are far enough apart to
create significant latency. This will minimize the number of database queries as much as
is possible, even at the cost of significant additional processing at each end.
Set QA_PERSISTENT_CONN_DB to true to use persistent database connections. Only use this if
you are absolutely sure it is a good idea under your setup - generally it is not.
For more information: http://www.php.net/manual/en/features.persistent-connections.php
Set QA_DEBUG_PERFORMANCE to true to show detailed performance profiling information at the
bottom of every Question2Answer page.
*/
......@@ -167,12 +167,12 @@
define('QA_OPTIMIZE_DISTANT_DB', false);
define('QA_PERSISTENT_CONN_DB', false);
define('QA_DEBUG_PERFORMANCE', false);
/*
And lastly... if you want to, you can predefine any constant from qa-db-maxima.php in this
file to override the default setting. Just make sure you know what you're doing!
*/
/*
Omit PHP closing tag to help avoid accidental output
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-ajax-answer.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax create answer requests
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -30,10 +30,10 @@
// Load relevant information about this question
$questionid=qa_post_text('a_questionid');
$userid=qa_get_logged_in_userid();
list($question, $childposts)=qa_db_select_with_pending(
qa_db_full_post_selectspec($userid, $questionid),
qa_db_full_child_posts_selectspec($userid, $questionid)
......@@ -52,35 +52,35 @@
// Try to create the new answer
$usecaptcha=qa_user_use_captcha(qa_user_level_for_post($question));
$answers=qa_page_q_load_as($question, $childposts);
$answerid=qa_page_q_add_a_submit($question, $answers, $usecaptcha, $in, $errors);
// If successful, page content will be updated via Ajax
if (isset($answerid)) {
$answer=qa_db_select_with_pending(qa_db_full_post_selectspec($userid, $answerid));
$question=$question+qa_page_q_post_rules($question, null, null, $childposts); // array union
$answer=$answer+qa_page_q_post_rules($answer, $question, $answers, null);
$usershtml=qa_userids_handles_html(array($answer), true);
$a_view=qa_page_q_answer_view($question, $answer, false, $usershtml, false);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-answer', null, null);
echo "QA_AJAX_RESPONSE\n1\n";
// Send back whether the 'answer' button should still be visible
echo (int)qa_opt('allow_multi_answers')."\n";
// Send back the count of answers
$countanswers=$question['acount']+1;
if ($countanswers==1)
......@@ -96,11 +96,11 @@
return;
}
}
echo "QA_AJAX_RESPONSE\n0\n"; // fall back to non-Ajax submission if there were any problems
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-ajax-asktitle.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax request based on ask a question title
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -26,7 +26,7 @@
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
require_once QA_INCLUDE_DIR.'qa-util-string.php';
// Collect the information we need from the database
......@@ -36,29 +36,29 @@
if ($doaskcheck || $doexampletags) {
$countqs=max($doexampletags ? QA_DB_RETRIEVE_ASK_TAG_QS : 0, $doaskcheck ? qa_opt('page_size_ask_check_qs') : 0);
$relatedquestions=qa_db_select_with_pending(
qa_db_search_posts_selectspec(null, qa_string_to_words($intitle), null, null, null, null, 0, false, $countqs)
);
}
// Collect example tags if appropriate
if ($doexampletags) {
require_once QA_INCLUDE_DIR.'qa-app-format.php';
$tagweight=array();
foreach ($relatedquestions as $question) {
$tags=qa_tagstring_to_tags($question['tags']);
foreach ($tags as $tag)
@$tagweight[$tag]+=exp($question['score']);
}
arsort($tagweight, SORT_NUMERIC);
$exampletags=array();
$minweight=exp(qa_match_to_min_score(qa_opt('match_example_tags')));
$maxcount=qa_opt('page_size_ask_tags');
......@@ -78,36 +78,36 @@
// Output the response header and example tags
echo "QA_AJAX_RESPONSE\n1\n";
echo strtr(qa_html(implode(',', $exampletags)), "\r\n", ' ')."\n";
// Collect and output the list of related questions
if ($doaskcheck) {
require_once QA_INCLUDE_DIR.'qa-app-format.php';
$count=0;
$minscore=qa_match_to_min_score(qa_opt('match_ask_check_qs'));
$maxcount=qa_opt('page_size_ask_check_qs');
foreach ($relatedquestions as $question) {
if ($question['score']<$minscore)
break;
if (!$count)
echo qa_lang_html('question/ask_same_q').'<br/>';
echo strtr(
'<a href="'.qa_q_path_html($question['postid'], $question['title']).'" target="_blank">'.qa_html($question['title']).'</a><br/>',
"\r\n", ' '
)."\n";
if ((++$count)>=$maxcount)
break;
}
}
/*
Omit PHP closing tag to help avoid accidental output
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-ajax-category.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax category information requests
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -25,24 +25,24 @@
*/
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
$categoryid=qa_post_text('categoryid');
if (!strlen($categoryid))
$categoryid=null;
list($fullcategory, $categories)=qa_db_select_with_pending(
qa_db_full_category_selectspec($categoryid, true),
qa_db_category_sub_selectspec($categoryid)
);
echo "QA_AJAX_RESPONSE\n1\n";
echo qa_html(strtr(@$fullcategory['content'], "\r\n", ' ')); // category description
foreach ($categories as $category)
echo "\n".$category['categoryid'].'/'.$category['title']; // subcategory information
/*
Omit PHP closing tag to help avoid accidental output
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-ajax-click-admin.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax single clicks on posts in admin section
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -38,8 +38,8 @@
echo "QA_AJAX_RESPONSE\n1\n";
else
echo "QA_AJAX_RESPONSE\n0\n".qa_lang('main/general_error');
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-ajax-click-answer.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax single clicks on answer
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -45,7 +45,7 @@
qa_db_full_child_posts_selectspec($userid, $questionid),
qa_db_full_child_posts_selectspec($userid, $answerid)
);
// Check if there was an operation that succeeded
......@@ -57,63 +57,63 @@
$question=$question+qa_page_q_post_rules($question, null, null, $qchildposts); // array union
$answer=$answer+qa_page_q_post_rules($answer, $question, $qchildposts, $achildposts);
if (qa_page_q_single_click_a($answer, $question, $answers, $achildposts, false, $error)) {
list($answer, $question)=qa_db_select_with_pending(
qa_db_full_post_selectspec($userid, $answerid),
qa_db_full_post_selectspec($userid, $questionid)
);
// If so, page content to be updated via Ajax
echo "QA_AJAX_RESPONSE\n1\n";
// Send back new count of answers
$countanswers=$question['acount'];
if ($countanswers==1)
echo qa_lang_html('question/1_answer_title');
else
echo qa_lang_html_sub('question/x_answers_title', $countanswers);
// If the answer was not deleted....
if (isset($answer)) {
$question=$question+qa_page_q_post_rules($question, null, null, $qchildposts); // array union
$answer=$answer+qa_page_q_post_rules($answer, $question, $qchildposts, $achildposts);
foreach ($achildposts as $key => $achildpost)
$achildposts[$key]=$achildpost+qa_page_q_post_rules($achildpost, $answer, $achildposts, null);
$usershtml=qa_userids_handles_html(array_merge(array($answer), $achildposts), true);
$a_view=qa_page_q_answer_view($question, $answer, ($answer['postid']==$question['selchildid']) && ($answer['type']=='A'),
$usershtml, false);
$a_view['c_list']=qa_page_q_comment_follow_list($question, $answer, $achildposts, false, $usershtml, false, null);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-answer', null, null);
// ... send back the HTML for it
echo "\n";
$themeclass->a_list_item($a_view);
}
return;
}
}
echo "QA_AJAX_RESPONSE\n0\n"; // fall back to non-Ajax submission if something failed
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-ajax-click-comment.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax single clicks on comments
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -47,7 +47,7 @@
qa_db_full_child_posts_selectspec($userid, $parentid)
);
// Check if there was an operation that succeeded
if (
......@@ -56,45 +56,45 @@
((@$parent['basetype']=='Q') || (@$parent['basetype']=='A'))
) {
$comment=$comment+qa_page_q_post_rules($comment, $parent, $children, null); // array union
if (qa_page_q_single_click_c($comment, $question, $parent, $error)) {
$comment=qa_db_select_with_pending(qa_db_full_post_selectspec($userid, $commentid));
// If so, page content to be updated via Ajax
echo "QA_AJAX_RESPONSE\n1";
// If the comment was not deleted...
if (isset($comment)) {
$parent=$parent+qa_page_q_post_rules($parent, ($questionid==$parentid) ? null : $question, null, $children);
// in theory we should retrieve the parent's siblings for the above, but they're not going to be relevant
$comment=$comment+qa_page_q_post_rules($comment, $parent, $children, null);
$usershtml=qa_userids_handles_html(array($comment), true);
$c_view=qa_page_q_comment_view($question, $parent, $comment, $usershtml, false);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-comment', null, null);
// ... send back the HTML for it
echo "\n";
$themeclass->c_list_item($c_view);
}
return;
}
}
echo "QA_AJAX_RESPONSE\n0\n"; // fall back to non-Ajax submission if something failed
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-ajax-click-wall.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax single clicks on wall posts
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -23,19 +23,19 @@
More about this license: http://www.question2answer.org/license.php
*/
require_once QA_INCLUDE_DIR.'qa-app-messages.php';
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-app-cookies.php';
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
$tohandle=qa_post_text('handle');
$start=(int)qa_post_text('start');
$usermessages=qa_db_select_with_pending(qa_db_recent_messages_selectspec(null, null, $tohandle, false, null, $start));
$usermessages=qa_wall_posts_add_rules($usermessages, $start);
foreach ($usermessages as $message)
if (qa_clicked('m'.$message['messageid'].'_dodelete') && $message['deleteable'])
if (qa_check_form_security_code('wall-'.$tohandle, qa_post_text('code'))) {
......@@ -43,8 +43,8 @@
echo "QA_AJAX_RESPONSE\n1\n";
return;
}
echo "QA_AJAX_RESPONSE\n0\n";
echo "QA_AJAX_RESPONSE\n0\n";
/*
Omit PHP closing tag to help avoid accidental output
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-ajax-comment.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax create comment requests
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -34,7 +34,7 @@
$questionid=qa_post_text('c_questionid');
$parentid=qa_post_text('c_parentid');
$userid=qa_get_logged_in_userid();
list($question, $parent, $children)=qa_db_select_with_pending(
qa_db_full_post_selectspec($userid, $questionid),
qa_db_full_post_selectspec($userid, $parentid),
......@@ -59,37 +59,37 @@
// Try to create the new comment
$usecaptcha=qa_user_use_captcha(qa_user_level_for_post($question));
$commentid=qa_page_q_add_c_submit($question, $parent, $children, $usecaptcha, $in, $errors);
// If successful, page content will be updated via Ajax
if (isset($commentid)) {
$children=qa_db_select_with_pending(qa_db_full_child_posts_selectspec($userid, $parentid));
$parent=$parent+qa_page_q_post_rules($parent, ($questionid==$parentid) ? null : $question, null, $children);
$parent=$parent+qa_page_q_post_rules($parent, ($questionid==$parentid) ? null : $question, null, $children);
// in theory we should retrieve the parent's siblings for the above, but they're not going to be relevant
foreach ($children as $key => $child)
$children[$key]=$child+qa_page_q_post_rules($child, $parent, $children, null);
$usershtml=qa_userids_handles_html($children, true);
qa_sort_by($children, 'created');
$c_list=qa_page_q_comment_follow_list($question, $parent, $children, true, $usershtml, false, null);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-comments', null, null);
echo "QA_AJAX_RESPONSE\n1\n";
// Send back the ID of the new comment
echo qa_anchor('C', $commentid)."\n";
// Send back the HTML
......@@ -98,10 +98,10 @@
return;
}
}
echo "QA_AJAX_RESPONSE\n0\n"; // fall back to non-Ajax submission if there were any problems
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-ajax-favorite.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax favorite requests
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -28,31 +28,31 @@
require_once QA_INCLUDE_DIR.'qa-app-cookies.php';
require_once QA_INCLUDE_DIR.'qa-app-favorites.php';
require_once QA_INCLUDE_DIR.'qa-app-format.php';
$entitytype=qa_post_text('entitytype');
$entityid=qa_post_text('entityid');
$setfavorite=qa_post_text('favorite');
$userid=qa_get_logged_in_userid();
if (!qa_check_form_security_code('favorite-'.$entitytype.'-'.$entityid, qa_post_text('code')))
echo "QA_AJAX_RESPONSE\n0\n".qa_lang('misc/form_security_reload');
elseif (isset($userid)) {
$cookieid=qa_cookie_get();
qa_user_favorite_set($userid, qa_get_logged_in_handle(), $cookieid, $entitytype, $entityid, $setfavorite);
$favoriteform=qa_favorite_form($entitytype, $entityid, $setfavorite, qa_lang($setfavorite ? 'main/remove_favorites' : 'main/add_favorites'));
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-favorite', null, null);
echo "QA_AJAX_RESPONSE\n1\n";
$themeclass->favorite_inner_html($favoriteform);
}
/*
Omit PHP closing tag to help avoid accidental output
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-ajax-mailing.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax mailing loop requests
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -27,28 +27,28 @@
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-app-mailing.php';
$continue=false;
if (qa_get_logged_in_level()>=QA_USER_LEVEL_ADMIN) {
$starttime=time();
qa_mailing_perform_step();
if ($starttime==time())
sleep(1); // make sure at least one second has passed
$message=qa_mailing_progress_message();
if (isset($message))
$continue=true;
else
$message=qa_lang('admin/mailing_complete');
} else
$message=qa_lang('admin/no_privileges');
echo "QA_AJAX_RESPONSE\n".(int)$continue."\n".qa_html($message);
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-ajax-notice.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax requests to close a notice
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -27,27 +27,27 @@
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-db-notices.php';
require_once QA_INCLUDE_DIR.'qa-db-users.php';
$noticeid=qa_post_text('noticeid');
$noticeid=qa_post_text('noticeid');
if (!qa_check_form_security_code('notice-'.$noticeid, qa_post_text('code')))
echo "QA_AJAX_RESPONSE\n0\n".qa_lang('misc/form_security_reload');
else {
if ($noticeid=='visitor')
setcookie('qa_noticed', 1, time()+86400*3650, '/', QA_COOKIE_DOMAIN);
else {
$userid=qa_get_logged_in_userid();
if ($noticeid=='welcome')
qa_db_user_set_flag($userid, QA_USER_FLAGS_WELCOME_NOTICE, false);
else
qa_db_usernotice_delete($userid, $noticeid);
}
echo "QA_AJAX_RESPONSE\n1";
}
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-ajax-recalc.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax admin recalculation requests
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -26,29 +26,29 @@
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-app-recalc.php';
if (qa_get_logged_in_level()>=QA_USER_LEVEL_ADMIN) {
if (!qa_check_form_security_code('admin/recalc', qa_post_text('code'))) {
$state='';
$message=qa_lang('misc/form_security_reload');
} else {
$state=qa_post_text('state');
$stoptime=time()+3;
while ( qa_recalc_perform_step($state) && (time()<$stoptime) )
;
$message=qa_recalc_get_message($state);
}
} else {
$state='';
$message=qa_lang('admin/no_privileges');
}
echo "QA_AJAX_RESPONSE\n1\n".$state."\n".qa_html($message);
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-ajax-show-comments.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax request to view full comment list
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -37,31 +37,31 @@
$questionid=qa_post_text('c_questionid');
$parentid=qa_post_text('c_parentid');
$userid=qa_get_logged_in_userid();
list($question, $parent, $children)=qa_db_select_with_pending(
qa_db_full_post_selectspec($userid, $questionid),
qa_db_full_post_selectspec($userid, $parentid),
qa_db_full_child_posts_selectspec($userid, $parentid)
);
if (isset($parent)) {
$parent=$parent+qa_page_q_post_rules($parent, null, null, $children);
$parent=$parent+qa_page_q_post_rules($parent, null, null, $children);
// in theory we should retrieve the parent's parent and siblings for the above, but they're not going to be relevant
foreach ($children as $key => $child)
$children[$key]=$child+qa_page_q_post_rules($child, $parent, $children, null);
$usershtml=qa_userids_handles_html($children, true);
qa_sort_by($children, 'created');
$c_list=qa_page_q_comment_follow_list($question, $parent, $children, true, $usershtml, false, null);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-comments', null, null);
echo "QA_AJAX_RESPONSE\n1\n";
// Send back the HTML
$themeclass->c_list_items($c_list['cs']);
......@@ -69,10 +69,10 @@
return;
}
echo "QA_AJAX_RESPONSE\n0\n";
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-ajax-version.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax version check requests
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -26,21 +26,21 @@
require_once QA_INCLUDE_DIR.'qa-app-admin.php';
$uri=qa_post_text('uri');
$versionkey=qa_post_text('versionkey');
$urikey=qa_post_text('urikey');
$version=qa_post_text('version');
$metadata=qa_admin_addon_metadata(qa_retrieve_url($uri), array(
'version' => $versionkey,
'uri' => $urikey,
// these two elements are only present for plugins, not themes, so we can hard code them here
'min_q2a' => 'Plugin Minimum Question2Answer Version',
'min_php' => 'Plugin Minimum PHP Version',
));
if (strlen(@$metadata['version'])) {
if (strcmp($metadata['version'], $version)) {
if (qa_qa_version_below(@$metadata['min_q2a']))
......@@ -57,20 +57,20 @@
else {
$response=qa_lang_html_sub('admin/version_get_x', qa_html('v'.$metadata['version']));
if (strlen(@$metadata['uri']))
$response='<a href="'.qa_html($metadata['uri']).'" style="color:#d00;">'.$response.'</a>';
}
} else
$response=qa_lang_html('admin/version_latest');
} else
$response=qa_lang_html('admin/version_latest_unknown');
echo "QA_AJAX_RESPONSE\n1\n".$response;
/*
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-ajax-vote.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax voting requests
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -30,32 +30,32 @@
require_once QA_INCLUDE_DIR.'qa-app-format.php';
require_once QA_INCLUDE_DIR.'qa-app-options.php';
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
$postid=qa_post_text('postid');
$vote=qa_post_text('vote');
$code=qa_post_text('code');
$userid=qa_get_logged_in_userid();
$cookieid=qa_cookie_get();
if (!qa_check_form_security_code('vote', $code))
$voteerror=qa_lang_html('misc/form_security_reload');
else {
$post=qa_db_select_with_pending(qa_db_full_post_selectspec($userid, $postid));
$voteerror=qa_vote_error_html($post, $vote, $userid, qa_request());
}
if ($voteerror===false) {
qa_vote_set($post, $userid, qa_get_logged_in_handle(), $cookieid, $vote);
$post=qa_db_select_with_pending(qa_db_full_post_selectspec($userid, $postid));
$fields=qa_post_html_fields($post, $userid, $cookieid, array(), null, array(
'voteview' => qa_get_vote_view($post, true), // behave as if on question page since the vote succeeded
));
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'voting', null, null);
echo "QA_AJAX_RESPONSE\n1\n";
......@@ -63,7 +63,7 @@
} else
echo "QA_AJAX_RESPONSE\n0\n".$voteerror;
/*
Omit PHP closing tag to help avoid accidental output
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-ajax-wallpost.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax wall post requests
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -23,46 +23,46 @@
More about this license: http://www.question2answer.org/license.php
*/
require_once QA_INCLUDE_DIR.'qa-app-messages.php';
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-app-cookies.php';
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
$message=qa_post_text('message');
$tohandle=qa_post_text('handle');
$morelink=qa_post_text('morelink');
$touseraccount=qa_db_select_with_pending(qa_db_user_account_selectspec($tohandle, false));
$loginuserid=qa_get_logged_in_userid();
$errorhtml=qa_wall_error_html($loginuserid, $touseraccount['userid'], $touseraccount['flags']);
if ($errorhtml || (!strlen($message)) || !qa_check_form_security_code('wall-'.$tohandle, qa_post_text('code')) )
echo "QA_AJAX_RESPONSE\n0"; // if there's an error, process in non-Ajax way
else {
$messageid=qa_wall_add_post($loginuserid, qa_get_logged_in_handle(), qa_cookie_get(),
$touseraccount['userid'], $touseraccount['handle'], $message, '');
$touseraccount['wallposts']++; // won't have been updated
$usermessages=qa_db_select_with_pending(qa_db_recent_messages_selectspec(null, null, $touseraccount['userid'], true, qa_opt('page_size_wall')));
$usermessages=qa_wall_posts_add_rules($usermessages, 0);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'wall', null, null);
echo "QA_AJAX_RESPONSE\n1\n";
echo 'm'.$messageid."\n"; // element in list to be revealed
foreach ($usermessages as $message)
$themeclass->message_item(qa_wall_post_view($message));
if ($morelink && ($touseraccount['wallposts']>count($usermessages)))
$themeclass->message_item(qa_wall_view_more_link($tohandle, count($usermessages)));
}
/*
Omit PHP closing tag to help avoid accidental output
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-ajax.php
Version: See define()s at top of qa-include/qa-base.php
Description: Front line of response to Ajax requests, routing as appropriate
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -39,14 +39,14 @@
require 'qa-base.php';
qa_report_process_stage('init_ajax');
// Get general Ajax parameters from the POST payload, and clear $_GET
qa_set_request(qa_post_text('qa_request'), qa_post_text('qa_root'));
$_GET=array(); // for qa_self_html()
// Database failure handler
......@@ -77,14 +77,14 @@
'wallpost' => 'qa-ajax-wallpost.php',
'click_wall' => 'qa-ajax-click-wall.php',
);
$operation=qa_post_text('qa_operation');
if (isset($routing[$operation])) {
qa_db_connect('qa_ajax_db_fail_handler');
require QA_INCLUDE_DIR.$routing[$operation];
qa_db_disconnect();
}
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-app-blobs.php
Version: See define()s at top of qa-include/qa-base.php
Description: Application-level blob-management functions
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -29,18 +29,18 @@
exit;
}
function qa_get_blob_url($blobid, $absolute=false)
/*
Return the URL which will output $blobid from the database when requested, $absolute or relative
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return qa_path('blob', array('qa_blobid' => $blobid), $absolute ? qa_opt('site_url') : null, QA_URL_FORMAT_PARAMS);
}
function qa_get_blob_directory($blobid)
/*
Return the full path to the on-disk directory for blob $blobid (subdirectories are named by the first 3 digits of $blobid)
......@@ -50,8 +50,8 @@
return rtrim(QA_BLOBS_DIRECTORY, '/').'/'.substr(str_pad($blobid, 20, '0', STR_PAD_LEFT), 0, 3);
}
function qa_get_blob_filename($blobid, $format)
/*
Return the full page and filename of blob $blobid which is in $format ($format is used as the file name suffix e.g. .jpg)
......@@ -61,8 +61,8 @@
return qa_get_blob_directory($blobid).'/'.$blobid.'.'.preg_replace('/[^A-Za-z0-9]/', '', $format);
}
function qa_create_blob($content, $format, $sourcefilename=null, $userid=null, $cookieid=null, $ip=null)
/*
Create a new blob (storing the content in the database or on disk as appropriate) with $content and $format, returning its blobid.
......@@ -70,9 +70,9 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'qa-db-blobs.php';
$blobid=qa_db_blob_create(defined('QA_BLOBS_DIRECTORY') ? null : $content, $format, $sourcefilename, $userid, $cookieid, $ip);
if (isset($blobid) && defined('QA_BLOBS_DIRECTORY'))
......@@ -81,8 +81,8 @@
return $blobid;
}
function qa_write_blob_file($blobid, $content, $format)
/*
Write the on-disk file for blob $blobid with $content and $format. Returns true if the write succeeded, false otherwise.
......@@ -91,45 +91,45 @@
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$written=false;
$directory=qa_get_blob_directory($blobid);
if (is_dir($directory) || mkdir($directory, fileperms(rtrim(QA_BLOBS_DIRECTORY, '/')) & 0777)) {
$filename=qa_get_blob_filename($blobid, $format);
$file=fopen($filename, 'xb');
if (is_resource($file)) {
if (fwrite($file, $content)>=strlen($content))
$written=true;
fclose($file);
if (!$written)
unlink($filename);
}
}
return $written;
return $written;
}
function qa_read_blob($blobid)
/*
Retrieve blob $blobid from the database, reading the content from disk if appropriate
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'qa-db-blobs.php';
$blob=qa_db_blob_read($blobid);
if (defined('QA_BLOBS_DIRECTORY') && !isset($blob['content']))
$blob['content']=qa_read_blob_file($blobid, $blob['format']);
return $blob;
}
function qa_read_blob_file($blobid, $format)
/*
Read the content of blob $blobid in $format from disk. On failure, it will return false.
......@@ -139,39 +139,39 @@
return file_get_contents(qa_get_blob_filename($blobid, $format));
}
function qa_delete_blob($blobid)
/*
Delete blob $blobid from the database, and remove the on-disk file if appropriate
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'qa-db-blobs.php';
if (defined('QA_BLOBS_DIRECTORY')) {
$blob=qa_db_blob_read($blobid);
if (isset($blob) && !isset($blob['content']))
unlink(qa_get_blob_filename($blobid, $blob['format']));
}
qa_db_blob_delete($blobid);
}
function qa_delete_blob_file($blobid, $format)
/*
Delete the on-disk file for blob $blobid in $format
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
unlink(qa_get_blob_filename($blobid, $format));
}
function qa_blob_exists($blobid)
/*
Check if blob $blobid exists
......@@ -180,10 +180,10 @@
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'qa-db-blobs.php';
return qa_db_blob_exists($blobid);
}
/*
Omit PHP closing tag to help avoid accidental output
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-app-captcha.php
Version: See define()s at top of qa-include/qa-base.php
Description: Wrapper functions and utilities for captcha modules
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -36,32 +36,32 @@
*/
{
$module=qa_load_module('captcha', qa_opt('captcha_module'));
return isset($module) && ( (!method_exists($module, 'allow_captcha')) || $module->allow_captcha());
}
function qa_captcha_reason_note($captchareason)
/*
Return an HTML string explaining $captchareason (from qa_user_captcha_reason()) to the user about why they are seeing a captcha
*/
{
$notehtml=null;
switch ($captchareason) {
case 'login':
$notehtml=qa_insert_login_links(qa_lang_html('misc/captcha_login_fix'));
break;
case 'confirm':
$notehtml=qa_insert_login_links(qa_lang_html('misc/captcha_confirm_fix'));
break;
case 'approve':
$notehtml=qa_lang_html('misc/captcha_approve_fix');
break;
break;
}
return $notehtml;
}
......@@ -73,16 +73,16 @@
{
if (qa_captcha_available()) {
$captcha=qa_load_module('captcha', qa_opt('captcha_module'));
$count=@++$qa_content['qa_captcha_count']; // work around fact that reCAPTCHA can only display per page
if ($count>1)
$html='[captcha placeholder]'; // single captcha will be moved about the page, to replace this
else {
$qa_content['script_var']['qa_captcha_in']='qa_captcha_div_1';
$html=$captcha->form_html($qa_content, @$errors['captcha']);
}
$fields['captcha']=array(
'type' => 'custom',
'label' => qa_lang_html('misc/captcha_label'),
......@@ -90,10 +90,10 @@
'error' => @array_key_exists('captcha', $errors) ? qa_lang_html('misc/captcha_error') : null,
'note' => $note,
);
return "if (qa_captcha_in!='qa_captcha_div_".$count."') { document.getElementById('qa_captcha_div_".$count."').innerHTML=document.getElementById(qa_captcha_in).innerHTML; document.getElementById(qa_captcha_in).innerHTML=''; qa_captcha_in='qa_captcha_div_".$count."'; }";
}
return '';
}
......@@ -105,13 +105,13 @@
{
if (qa_captcha_available()) {
$captcha=qa_load_module('captcha', qa_opt('captcha_module'));
if (!$captcha->validate_post($error)) {
$errors['captcha']=$error;
return false;
}
}
return true;
}
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-app-cookies.php
Version: See define()s at top of qa-include/qa-base.php
Description: User cookie management (application level) for tracking anonymous posts
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -36,11 +36,11 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return isset($_COOKIE['qa_id']) ? qa_gpc_to_string($_COOKIE['qa_id']) : null;
}
function qa_cookie_get_create()
/*
Return user identification cookie sent by browser if valid, or create a new one if not.
......@@ -48,23 +48,23 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'qa-db-cookies.php';
$cookieid=qa_cookie_get();
if (isset($cookieid) && qa_db_cookie_exists($cookieid))
; // cookie is valid
else
$cookieid=qa_db_cookie_create(qa_remote_ip_address());
setcookie('qa_id', $cookieid, time()+86400*365, '/', QA_COOKIE_DOMAIN);
$_COOKIE['qa_id']=$cookieid;
return $cookieid;
}
function qa_cookie_report_action($cookieid, $action)
/*
Called after a database write $action performed by a user identified by $cookieid,
......@@ -72,7 +72,7 @@
*/
{
require_once QA_INCLUDE_DIR.'qa-db-cookies.php';
qa_db_cookie_written($cookieid, qa_remote_ip_address());
}
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-app-emails.php
Version: See define()s at top of qa-include/qa-base.php
Description: Wrapper functions for sending email notifications to users
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -39,11 +39,11 @@
*/
{
global $qa_notifications_suspended;
$qa_notifications_suspended+=($suspend ? 1 : -1);
}
function qa_send_notification($userid, $email, $handle, $subject, $body, $subs)
/*
Send email to person with $userid and/or $email and/or $handle (null/invalid values are ignored or retrieved from
......@@ -52,50 +52,50 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_notifications_suspended;
if ($qa_notifications_suspended>0)
return false;
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
require_once QA_INCLUDE_DIR.'qa-util-string.php';
if (isset($userid)) {
$needemail=!qa_email_validate(@$email); // take from user if invalid, e.g. @ used in practice
$needhandle=empty($handle);
if ($needemail || $needhandle) {
if (QA_FINAL_EXTERNAL_USERS) {
if ($needhandle) {
$handles=qa_get_public_from_userids(array($userid));
$handle=@$handles[$userid];
}
if ($needemail)
$email=qa_get_user_email($userid);
} else {
$useraccount=qa_db_select_with_pending(
qa_db_user_account_selectspec($userid, true)
);
if ($needhandle)
$handle=@$useraccount['handle'];
if ($needemail)
$email=@$useraccount['email'];
}
}
}
if (isset($email) && qa_email_validate($email)) {
$subs['^site_title']=qa_opt('site_title');
$subs['^handle']=$handle;
$subs['^email']=$email;
$subs['^open']="\n";
$subs['^close']="\n";
return qa_send_email(array(
'fromemail' => qa_opt('from_email'),
'fromname' => qa_opt('site_title'),
......@@ -105,11 +105,11 @@
'body' => (empty($handle) ? '' : qa_lang_sub('emails/to_handle_prefix', $handle)).strtr($body, $subs),
'html' => false,
));
} else
return false;
}
function qa_send_email($params)
/*
......@@ -118,14 +118,14 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
// @error_log(print_r($params, true));
require_once QA_INCLUDE_DIR.'qa-class.phpmailer.php';
$mailer=new PHPMailer();
$mailer->CharSet='utf-8';
$mailer->From=$params['fromemail'];
$mailer->Sender=$params['fromemail'];
$mailer->FromName=$params['fromname'];
......@@ -135,22 +135,22 @@
if ($params['html'])
$mailer->IsHTML(true);
if (qa_opt('smtp_active')) {
$mailer->IsSMTP();
$mailer->Host=qa_opt('smtp_address');
$mailer->Port=qa_opt('smtp_port');
if (qa_opt('smtp_secure'))
$mailer->SMTPSecure=qa_opt('smtp_secure');
if (qa_opt('smtp_authenticate')) {
$mailer->SMTPAuth=true;
$mailer->Username=qa_opt('smtp_username');
$mailer->Password=qa_opt('smtp_password');
}
}
return $mailer->Send();
}
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-app-events.php
Version: See define()s at top of qa-include/qa-base.php
Description: Handles the submission of events to the database (application level)
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -31,7 +31,7 @@
require_once QA_INCLUDE_DIR.'qa-db-events.php';
require_once QA_INCLUDE_DIR.'qa-app-updates.php';
function qa_create_event_for_q_user($questionid, $lastpostid, $updatetype, $lastuserid, $otheruserid=null, $timestamp=null)
/*
......@@ -49,7 +49,7 @@
if (isset($otheruserid) && ($otheruserid!=$lastuserid))
qa_db_event_create_not_entity($otheruserid, $questionid, $lastpostid, $updatetype, $lastuserid, $timestamp); // possible other user to be informed
}
function qa_create_event_for_tags($tagstring, $questionid, $updatetype, $lastuserid, $timestamp=null)
/*
......@@ -66,7 +66,7 @@
foreach ($tagwordids as $wordid)
qa_db_event_create_for_entity(QA_ENTITY_TAG, $wordid, $questionid, $questionid, $updatetype, $lastuserid, $timestamp);
}
function qa_create_event_for_category($categoryid, $questionid, $updatetype, $lastuserid, $timestamp=null)
/*
......@@ -79,14 +79,14 @@
if (isset($categoryid)) {
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
require_once QA_INCLUDE_DIR.'qa-app-format.php';
$categories=qa_category_path(qa_db_single_select(qa_db_category_nav_selectspec($categoryid, true)), $categoryid);
foreach ($categories as $category)
qa_db_event_create_for_entity(QA_ENTITY_CATEGORY, $category['categoryid'], $questionid, $questionid, $updatetype, $lastuserid, $timestamp);
}
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-app-favorites.php
Version: See define()s at top of qa-include/qa-base.php
Description: Handles favoriting and unfavoriting (application level)
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -39,42 +39,42 @@
require_once QA_INCLUDE_DIR.'qa-db-favorites.php';
require_once QA_INCLUDE_DIR.'qa-app-limits.php';
require_once QA_INCLUDE_DIR.'qa-app-updates.php';
if ($favorite)
qa_db_favorite_create($userid, $entitytype, $entityid);
else
qa_db_favorite_delete($userid, $entitytype, $entityid);
switch ($entitytype) {
case QA_ENTITY_QUESTION:
$action=$favorite ? 'q_favorite' : 'q_unfavorite';
$params=array('postid' => $entityid);
break;
case QA_ENTITY_USER:
$action=$favorite ? 'u_favorite' : 'u_unfavorite';
$params=array('userid' => $entityid);
break;
case QA_ENTITY_TAG:
$action=$favorite ? 'tag_favorite' : 'tag_unfavorite';
$params=array('wordid' => $entityid);
break;
case QA_ENTITY_CATEGORY:
$action=$favorite ? 'cat_favorite' : 'cat_unfavorite';
$params=array('categoryid' => $entityid);
break;
default:
qa_fatal_error('Favorite type not recognized');
break;
}
qa_report_event($action, $userid, $handle, $cookieid, $params);
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-app-limits.php
Version: See define()s at top of qa-include/qa-base.php
Description: Monitoring and rate-limiting user actions (application level)
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -41,7 +41,7 @@
define('QA_LIMIT_MESSAGES', 'M'); // i.e. private messages
define('QA_LIMIT_WALL_POSTS', 'W');
function qa_user_limits_remaining($action)
/*
Return how many more times the logged in user (and requesting IP address) can perform $action this hour,
......@@ -50,11 +50,11 @@
{
$userlimits=qa_db_get_pending_result('userlimits', qa_db_user_limits_selectspec(qa_get_logged_in_userid()));
$iplimits=qa_db_get_pending_result('iplimits', qa_db_ip_limits_selectspec(qa_remote_ip_address()));
return qa_limits_calc_remaining($action, @$userlimits[$action], @$iplimits[$action]);
}
function qa_limits_remaining($userid, $action)
/*
Return how many more times user $userid and/or the requesting IP can perform $action this hour, where $action is one
......@@ -62,15 +62,15 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'qa-db-limits.php';
$dblimits=qa_db_limits_get($userid, qa_remote_ip_address(), $action);
return qa_limits_calc_remaining($action, @$dblimits['user'], @$dblimits['ip']);
}
function qa_limits_calc_remaining($action, $userlimits, $iplimits)
/*
Calculate how many more times $action can be performed this hour, based on $userlimits and $iplimits retrieved from the database
......@@ -81,12 +81,12 @@
$usermax=qa_opt('max_rate_user_qs');
$ipmax=qa_opt('max_rate_ip_qs');
break;
case QA_LIMIT_ANSWERS:
$usermax=qa_opt('max_rate_user_as');
$ipmax=qa_opt('max_rate_ip_as');
break;
case QA_LIMIT_COMMENTS:
$usermax=qa_opt('max_rate_user_cs');
$ipmax=qa_opt('max_rate_ip_cs');
......@@ -96,7 +96,7 @@
$usermax=qa_opt('max_rate_user_votes');
$ipmax=qa_opt('max_rate_ip_votes');
break;
case QA_LIMIT_REGISTRATIONS:
$usermax=1; // not really relevant
$ipmax=qa_opt('max_rate_ip_registers');
......@@ -106,28 +106,28 @@
$usermax=1; // not really relevant
$ipmax=qa_opt('max_rate_ip_logins');
break;
case QA_LIMIT_UPLOADS:
$usermax=qa_opt('max_rate_user_uploads');
$ipmax=qa_opt('max_rate_ip_uploads');
break;
case QA_LIMIT_FLAGS:
$usermax=qa_opt('max_rate_user_flags');
$ipmax=qa_opt('max_rate_ip_flags');
break;
case QA_LIMIT_MESSAGES:
case QA_LIMIT_WALL_POSTS:
$usermax=qa_opt('max_rate_user_messages');
$ipmax=qa_opt('max_rate_ip_messages');
break;
default:
qa_fatal_error('Unknown limit code in qa_limits_calc_remaining: '.$action);
break;
}
$period=(int)(qa_opt('db_time')/3600);
return max(0, min(
......@@ -135,36 +135,36 @@
$ipmax-((@$iplimits['period']==$period) ? $iplimits['count'] : 0)
));
}
function qa_is_ip_blocked()
/*
Return whether the requesting IP address has been blocked from write operations
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$blockipclauses=qa_block_ips_explode(qa_opt('block_ips_write'));
foreach ($blockipclauses as $blockipclause)
if (qa_block_ip_match(qa_remote_ip_address(), $blockipclause))
return true;
return false;
}
function qa_block_ips_explode($blockipstring)
/*
Return an array of the clauses within $blockipstring, each of which can contain hyphens or asterisks
*/
{
$blockipstring=preg_replace('/\s*\-\s*/', '-', $blockipstring); // special case for 'x.x.x.x - x.x.x.x'
return preg_split('/[^0-9\.\-\*]/', $blockipstring, -1, PREG_SPLIT_NO_EMPTY);
}
function qa_block_ip_match($ip, $blockipclause)
/*
Returns whether the ip address $ip is matched by the clause $blockipclause, which can contain a hyphen or asterisk
......@@ -176,26 +176,26 @@
$iplong=sprintf('%u', ip2long($ip));
$end1long=sprintf('%u', ip2long($matches[1]));
$end2long=sprintf('%u', ip2long($matches[2]));
return (($iplong>=$end1long) && ($iplong<=$end2long)) || (($iplong>=$end2long) && ($iplong<=$end1long));
}
} elseif (strlen($blockipclause))
return preg_match('/^'.str_replace('\\*', '[0-9]+', preg_quote($blockipclause, '/')).'$/', $ip) > 0;
// preg_quote misses hyphens but that is OK here
}
return false;
}
function qa_report_write_action($userid, $cookieid, $action, $questionid, $answerid, $commentid)
/*
Called after a database write $action performed by a user identified by $userid and/or $cookieid.
*/
{}
function qa_limits_increment($userid, $action)
/*
Take note for rate limits that user $userid and/or the requesting IP just performed $action,
......@@ -203,14 +203,14 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'qa-db-limits.php';
$period=(int)(qa_opt('db_time')/3600);
if (isset($userid))
qa_db_limits_user_add($userid, $action, $period, 1);
qa_db_limits_ip_add(qa_remote_ip_address(), $action, $period, 1);
}
......
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-app-mailing.php
Version: See define()s at top of qa-include/qa-base.php
Description: Functions for sending a mailing to all users
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -36,7 +36,7 @@
*/
{
require_once QA_INCLUDE_DIR.'qa-db-admin.php';
if (strlen(qa_opt('mailing_last_userid'))==0) {
qa_opt('mailing_last_timestamp', time());
qa_opt('mailing_last_userid', '0');
......@@ -45,7 +45,7 @@
}
}
function qa_mailing_stop()
/*
Stop a mailing to all users
......@@ -57,7 +57,7 @@
qa_opt('mailing_total_users', '');
}
function qa_mailing_perform_step()
/*
Allow the mailing to proceed forwards, for the appropriate amount of time and users, based on the options
......@@ -66,39 +66,39 @@
require_once QA_INCLUDE_DIR.'qa-db-users.php';
$lastuserid=qa_opt('mailing_last_userid');
if (strlen($lastuserid)) {
$thistime=time();
$lasttime=qa_opt('mailing_last_timestamp');
$perminute=qa_opt('mailing_per_minute');
if (($lasttime-$thistime)>60) // if it's been a while, we assume there hasn't been continuous mailing...
$lasttime=$thistime-1; // ... so only do 1 second's worth
else // otherwise...
$lasttime=max($lasttime, $thistime-6); // ... don't do more than 6 seconds' worth
$count=min(floor(($thistime-$lasttime)*$perminute/60), 100); // don't do more than 100 messages at a time
if ($count>0) {
qa_opt('mailing_last_timestamp', $thistime+30);
// prevents a parallel call to qa_mailing_perform_step() from sending messages, unless we're very unlucky with timing (poor man's mutex)
$sentusers=0;
$users=qa_db_users_get_mailing_next($lastuserid, $count);
if (count($users)) {
foreach ($users as $user)
$lastuserid=max($lastuserid, $user['userid']);
qa_opt('mailing_last_userid', $lastuserid);
qa_opt('mailing_done_users', qa_opt('mailing_done_users')+count($users));
foreach ($users as $user)
if (!($user['flags'] & QA_USER_FLAGS_NO_MAILINGS)) {
qa_mailing_send_one($user['userid'], $user['handle'], $user['email'], $user['emailcode']);
$sentusers++;
}
qa_opt('mailing_last_timestamp', $lasttime+$sentusers*60/$perminute); // can be floating point result, based on number of mails actually sent
} else
......@@ -107,7 +107,7 @@
}
}
function qa_mailing_send_one($userid, $handle, $email, $emailcode)
/*
Send a single message from the mailing, to $userid with $handle and $email.
......@@ -116,14 +116,14 @@
{
require_once QA_INCLUDE_DIR.'qa-app-emails.php';
require_once QA_INCLUDE_DIR.'qa-db-users.php';
if (!strlen(trim($emailcode))) {
$emailcode=qa_db_user_rand_emailcode();
qa_db_user_set($userid, 'emailcode', $emailcode);
}
$unsubscribeurl=qa_path_absolute('unsubscribe', array('c' => $emailcode, 'u' => $handle));
return qa_send_email(array(
'fromemail' => qa_opt('mailing_from_email'),
'fromname' => qa_opt('mailing_from_name'),
......@@ -150,7 +150,7 @@
return null;
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-app-messages.php
Version: See define()s at top of qa-include/qa-base.php
Description: Handling private or public messages (wall posts)
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -39,39 +39,39 @@
require_once QA_INCLUDE_DIR.'qa-app-limits.php';
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
if ((!QA_FINAL_EXTERNAL_USERS) && qa_opt('allow_user_walls')) {
if ( ($touserflags & QA_USER_FLAGS_NO_WALL_POSTS) && !(isset($fromuserid) && ($fromuserid==$touserid)) )
return qa_lang_html('profile/post_wall_blocked');
else
switch (qa_user_permit_error('permit_post_wall', QA_LIMIT_WALL_POSTS)) {
case 'limit':
return qa_lang_html('profile/post_wall_limit');
break;
case 'login':
return qa_insert_login_links(qa_lang_html('profile/post_wall_must_login'), qa_request());
break;
case 'confirm':
return qa_insert_login_links(qa_lang_html('profile/post_wall_must_confirm'), qa_request());
break;
case 'approve':
return qa_lang_html('profile/post_wall_must_be_approved');
break;
case false:
return false;
break;
}
}
return qa_lang_html('users/no_permission');
}
function qa_wall_add_post($userid, $handle, $cookieid, $touserid, $tohandle, $content, $format)
/*
Adds a post to the wall of user $touserid with handle $tohandle, containing $content in $format (e.g. '' for text or 'html')
......@@ -79,13 +79,13 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'qa-app-format.php';
require_once QA_INCLUDE_DIR.'qa-db-messages.php';
$messageid=qa_db_message_create($userid, $touserid, $content, $format, true);
qa_db_user_recount_posts($touserid);
qa_report_event('u_wall_post', $userid, $handle, $cookieid, array(
'userid' => $touserid,
'handle' => $tohandle,
......@@ -97,8 +97,8 @@
return $messageid;
}
function qa_wall_delete_post($userid, $handle, $cookieid, $message)
/*
Deletes the wall post described in $message (as obtained via qa_db_recent_messages_selectspec()). The deletion was performed
......@@ -106,17 +106,17 @@
*/
{
require_once QA_INCLUDE_DIR.'qa-db-messages.php';
qa_db_message_delete($message['messageid']);
qa_db_user_recount_posts($message['touserid']);
qa_report_event('u_wall_delete', $userid, $handle, $cookieid, array(
'messageid' => $message['messageid'],
'oldmessage' => $message,
));
}
function qa_wall_posts_add_rules($usermessages, $start)
/*
Return the list of messages in $usermessages (as obtained via qa_db_recent_messages_selectspec()) with additional
......@@ -125,26 +125,26 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$userid=qa_get_logged_in_userid();
$userdeleteall=!(qa_user_permit_error('permit_hide_show') || qa_user_permit_error('permit_delete_hidden'));
// reuse "Hiding or showing any post" and "Deleting hidden posts" permissions
$userrecent=($start==0) && isset($userid); // User can delete all of the recent messages they wrote on someone's wall...
foreach ($usermessages as $key => $message) {
if ($message['fromuserid']!=$userid)
$userrecent=false; // ... until we come across one that they didn't write (which could be a reply)
$usermessages[$key]['deleteable'] =
($message['touserid']==$userid) || // if it's this user's wall
($userrecent && ($message['fromuserid']==$userid)) || // if it's one the user wrote that no one replied to yet
$userdeleteall; // if the user has enough permissions to delete from any wall
}
return $usermessages;
}
function qa_wall_post_view($message)
/*
Returns an element to add to $qa_content['message_list']['messages'] for $message (as obtained via
......@@ -152,11 +152,11 @@
*/
{
require_once QA_INCLUDE_DIR.'qa-app-format.php';
$options=qa_message_html_defaults();
$htmlfields=qa_message_html_fields($message, $options);
if ($message['deleteable'])
$htmlfields['form']=array(
'style' => 'light',
......@@ -169,11 +169,11 @@
),
),
);
return $htmlfields;
}
function qa_wall_view_more_link($handle, $start)
/*
Returns an element to add to $qa_content['message_list']['messages'] with a link to view all wall posts
......
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-app-post-create.php
Version: See define()s at top of qa-include/qa-base.php
Description: Creating questions, answers and comments (application level)
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -34,8 +34,8 @@
require_once QA_INCLUDE_DIR.'qa-db-points.php';
require_once QA_INCLUDE_DIR.'qa-db-hotness.php';
require_once QA_INCLUDE_DIR.'qa-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)
......@@ -43,8 +43,8 @@
{
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)
/*
......@@ -58,15 +58,15 @@
$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),
$categoryid, isset($userid) ? null : $name);
if (isset($extravalue)) {
require_once QA_INCLUDE_DIR.'qa-db-metas.php';
qa_db_postmeta_set($postid, 'qa_q_extra', $extravalue);
}
qa_db_posts_calc_category_path($postid);
qa_db_hotness_update($postid);
if ($queued) {
qa_db_queuedcount_update();
......@@ -75,7 +75,7 @@
qa_update_counts_for_q($postid);
qa_db_points_update_ifuser($userid, 'qposts');
}
qa_report_event($queued ? 'q_queue' : 'q_post', $userid, $handle, $cookieid, array(
'postid' => $postid,
'parentid' => @$followanswer['postid'],
......@@ -91,11 +91,11 @@
'notify' => $notify,
'email' => $email,
));
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
......@@ -103,14 +103,14 @@
{
if (isset($postid)) // post might no longer exist
qa_db_category_path_qcount_update(qa_db_post_get_category_path($postid));
qa_db_qcount_update();
qa_db_unaqcount_update();
qa_db_unselqcount_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
......@@ -121,11 +121,11 @@
foreach ($keys as $key)
if (isset($inarray[$key]))
$outarray[$key]=$inarray[$key];
return $outarray;
}
function qa_suspend_post_indexing($suspend=true)
/*
Suspend the indexing (and unindexing) of posts via qa_post_index(...) and qa_post_unindex(...)
......@@ -133,11 +133,11 @@
*/
{
global $qa_post_indexing_suspended;
$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,
......@@ -145,18 +145,18 @@
*/
{
global $qa_post_indexing_suspended;
if ($qa_post_indexing_suspended>0)
return;
// Send through to any search modules for indexing
$searches=qa_load_modules_with('search', 'index_post');
foreach ($searches as $search)
$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.
......@@ -167,20 +167,20 @@
$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),
$question['categoryid'], isset($userid) ? null : $name);
qa_db_posts_calc_category_path($postid);
if ($queued) {
qa_db_queuedcount_update();
} else {
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_update_q_counts_for_a($question['postid']);
qa_db_points_update_ifuser($userid, 'aposts');
}
qa_report_event($queued ? 'a_queue' : 'a_post', $userid, $handle, $cookieid, array(
'postid' => $postid,
'parentid' => $question['postid'],
......@@ -193,11 +193,11 @@
'notify' => $notify,
'email' => $email,
));
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
......@@ -210,7 +210,7 @@
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.
......@@ -228,26 +228,26 @@
if (!isset($parent))
$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,
qa_remote_ip_address(), null, $content, $format, null, qa_combine_notify_email($userid, $notify, $email),
$question['categoryid'], isset($userid) ? null : $name);
qa_db_posts_calc_category_path($postid);
if ($queued) {
qa_db_queuedcount_update();
} else {
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_db_points_update_ifuser($userid, 'cposts');
qa_db_ccount_update();
}
$thread=array();
foreach ($commentsfollows as $comment)
if (($comment['type']=='C') && ($comment['parentid']==$parent['postid'])) // find just those for this parent, fully visible
$thread[]=$comment;
......@@ -268,10 +268,10 @@
'notify' => $notify,
'email' => $email,
));
return $postid;
}
/*
Omit PHP closing tag to help avoid accidental output
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-app-q-list.php
Version: See define()s at top of qa-include/qa-base.php
Description: Controller for most question listing pages, plus custom pages and plugin pages
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -29,7 +29,7 @@
exit;
}
function qa_q_list_page_content($questions, $pagesize, $start, $count, $sometitle, $nonetitle,
$navcategories, $categoryid, $categoryqcount, $categorypathprefix, $feedpathprefix, $suggest,
$pagelinkparams=null, $categoryparams=null, $dummy=null)
......@@ -48,76 +48,76 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'qa-app-format.php';
require_once QA_INCLUDE_DIR.'qa-app-updates.php';
$userid=qa_get_logged_in_userid();
// Chop down to size, get user information for display
if (isset($pagesize))
$questions=array_slice($questions, 0, $pagesize);
$usershtml=qa_userids_handles_html(qa_any_get_userids_handles($questions));
// Prepare content for theme
$qa_content=qa_content_prepare(true, array_keys(qa_category_path($navcategories, $categoryid)));
$qa_content['q_list']['form']=array(
'tags' => 'method="post" action="'.qa_self_html().'"',
'hidden' => array(
'code' => qa_get_form_security_code('vote'),
),
);
$qa_content['q_list']['qs']=array();
if (count($questions)) {
$qa_content['title']=$sometitle;
$defaults=qa_post_html_defaults('Q');
if (isset($categorypathprefix))
$defaults['categorypathprefix']=$categorypathprefix;
foreach ($questions as $question)
$qa_content['q_list']['qs'][]=qa_any_to_q_html_fields($question, $userid, qa_cookie_get(),
$usershtml, null, qa_post_html_options($question, $defaults));
} else
$qa_content['title']=$nonetitle;
if (isset($userid) && isset($categoryid)) {
$favoritemap=qa_get_favorite_non_qs_map();
$categoryisfavorite=@$favoritemap['category'][$navcategories[$categoryid]['backpath']] ? true : false;
$qa_content['favorite']=qa_favorite_form(QA_ENTITY_CATEGORY, $categoryid, $categoryisfavorite,
qa_lang_sub($categoryisfavorite ? 'main/remove_x_favorites' : 'main/add_category_x_favorites', $navcategories[$categoryid]['title']));
}
if (isset($count) && isset($pagesize))
$qa_content['page_links']=qa_html_page_links(qa_request(), $start, $pagesize, $count, qa_opt('pages_prev_next'), $pagelinkparams);
if (empty($qa_content['page_links']))
$qa_content['suggest_next']=$suggest;
if (qa_using_categories() && count($navcategories) && isset($categorypathprefix))
$qa_content['navigation']['cat']=qa_category_navigation($navcategories, $categoryid, $categorypathprefix, $categoryqcount, $categoryparams);
if (isset($feedpathprefix) && (qa_opt('feed_per_category') || !isset($categoryid)) )
$qa_content['feed']=array(
'url' => qa_path_html(qa_feed_request($feedpathprefix.(isset($categoryid) ? ('/'.qa_category_path_request($navcategories, $categoryid)) : ''))),
'label' => strip_tags($sometitle),
);
return $qa_content;
}
function qa_qs_sub_navigation($sort, $categoryslugs)
/*
Return the sub navigation structure common to question listing pages
......@@ -134,12 +134,12 @@
'label' => qa_lang('main/nav_most_recent'),
'url' => qa_path_html($request),
),
'hot' => array(
'label' => qa_lang('main/nav_hot'),
'url' => qa_path_html($request, array('sort' => 'hot')),
),
'votes' => array(
'label' => qa_lang('main/nav_most_votes'),
'url' => qa_path_html($request, array('sort' => 'votes')),
......@@ -155,19 +155,19 @@
'url' => qa_path_html($request, array('sort' => 'views')),
),
);
if (isset($navigation[$sort]))
$navigation[$sort]['selected']=true;
else
$navigation['recent']['selected']=true;
if (!qa_opt('do_count_q_views'))
unset($navigation['views']);
return $navigation;
}
function qa_unanswered_sub_navigation($by, $categoryslugs)
/*
Return the sub navigation structure common to unanswered pages
......@@ -178,29 +178,29 @@
if (isset($categoryslugs))
foreach ($categoryslugs as $slug)
$request.='/'.$slug;
$navigation=array(
'by-answers' => array(
'label' => qa_lang('main/nav_no_answer'),
'url' => qa_path_html($request),
),
'by-selected' => array(
'label' => qa_lang('main/nav_no_selected_answer'),
'url' => qa_path_html($request, array('by' => 'selected')),
),
'by-upvotes' => array(
'label' => qa_lang('main/nav_no_upvoted_answer'),
'url' => qa_path_html($request, array('by' => 'upvotes')),
),
);
if (isset($navigation['by-'.$by]))
$navigation['by-'.$by]['selected']=true;
else
$navigation['by-answers']['selected']=true;
if (!qa_opt('voting_on_as'))
unset($navigation['by-upvotes']);
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-app-search.php
Version: See define()s at top of qa-include/qa-base.php
Description: Wrapper functions and utilities for search modules
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -29,7 +29,7 @@
exit;
}
function qa_get_search_results($query, $start, $count, $userid, $absoluteurls, $fullcontent)
/*
Returns $count search results for $query performed by $userid, starting at offset $start. Set $absoluteurls to true
......@@ -40,36 +40,36 @@
{
// Identify which search module should be used
$searchmodules=qa_load_modules_with('search', 'process_search');
if (!count($searchmodules))
qa_fatal_error('No search engine is available');
$module=reset($searchmodules); // use first one by default
if (count($searchmodules)>1) {
$tryname=qa_opt('search_module'); // use chosen one if it's available
if (isset($searchmodules[$tryname]))
$module=$searchmodules[$tryname];
}
// Get the results
$results=$module->process_search($query, $start, $count, $userid, $absoluteurls, $fullcontent);
// Work out what additional information (if any) we need to retrieve for the results
$keypostidgetfull=array();
$keypostidgettype=array();
$keypostidgetquestion=array();
$keypageidgetpage=array();
foreach ($results as $result) {
if (isset($result['question_postid']) && !isset($result['question']))
$keypostidgetfull[$result['question_postid']]=true;
if (isset($result['match_postid'])) {
if (!( (isset($result['question_postid'])) || (isset($result['question'])) ))
$keypostidgetquestion[$result['match_postid']]=true; // we can also get $result['match_type'] from this
......@@ -77,22 +77,22 @@
elseif (!isset($result['match_type']))
$keypostidgettype[$result['match_postid']]=true;
}
if (isset($result['page_pageid']) && !isset($result['page']))
$keypageidgetpage[$result['page_pageid']]=true;
}
// Perform the appropriate database queries
list($postidfull, $postidtype, $postidquestion, $pageidpage)=qa_db_select_with_pending(
count($keypostidgetfull) ? qa_db_posts_selectspec($userid, array_keys($keypostidgetfull), $fullcontent) : null,
count($keypostidgettype) ? qa_db_posts_basetype_selectspec(array_keys($keypostidgettype)) : null,
count($keypostidgetquestion) ? qa_db_posts_to_qs_selectspec($userid, array_keys($keypostidgetquestion), $fullcontent) : null,
count($keypageidgetpage) ? qa_db_pages_selectspec(null, array_keys($keypageidgetpage)) : null
);
// Supplement the results as appropriate
foreach ($results as $key => $result) {
if (isset($result['question_postid']) && !isset($result['question']))
if (@$postidfull[$result['question_postid']]['basetype']=='Q')
......@@ -101,27 +101,27 @@
if (isset($result['match_postid'])) {
if (!( (isset($result['question_postid'])) || (isset($result['question'])) )) {
$result['question']=@$postidquestion[$result['match_postid']];
if (!isset($result['match_type']))
$result['match_type']=@$result['question']['obasetype'];
} elseif (!isset($result['match_type']))
$result['match_type']=@$postidtype[$result['match_postid']];
}
if (isset($result['question']) && !isset($result['question_postid']))
$result['question_postid']=$result['question']['postid'];
if (isset($result['page_pageid']) && !isset($result['page']))
$result['page']=@$pageidpage[$result['page_pageid']];
if (!isset($result['title'])) {
if (isset($result['question']))
$result['title']=$result['question']['title'];
elseif (isset($result['page']))
$result['title']=$result['page']['heading'];
}
if (!isset($result['url'])) {
if (isset($result['question']))
$result['url']=qa_q_path($result['question']['postid'], $result['question']['title'],
......@@ -129,15 +129,15 @@
elseif (isset($result['page']))
$result['url']=qa_path($result['page']['tags'], null, qa_opt('site_url'));
}
$results[$key]=$result;
}
// Return the results
return $results;
}
/*
Omit PHP closing tag to help avoid accidental output
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-app-updates.php
Version: See define()s at top of qa-include/qa-base.php
Description: Definitions relating to favorites and updates in the database tables
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -37,7 +37,7 @@
define('QA_ENTITY_TAG', 'T');
define('QA_ENTITY_CATEGORY', 'C');
define('QA_ENTITY_NONE', '-');
// Character codes for the different types of updates on a post (updatetype columns)
......@@ -52,7 +52,7 @@
// Character codes for types of update that only appear in the streams tables, not on the posts themselves
define('QA_UPDATE_FOLLOWS', 'F'); // if a new question was asked related to one of its answers, or for a comment that follows another
define('QA_UPDATE_C_FOR_Q', 'U'); // if comment created was on a question of the user whose stream this appears in
define('QA_UPDATE_C_FOR_A', 'N'); // if comment created was on an answer of the user whose stream this appears in
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-app-upload.php
Version: See define()s at top of qa-include/qa-base.php
Description: Application-level file upload functionality
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -29,18 +29,18 @@
exit;
}
function qa_get_max_upload_size()
/*
Return the maximum size of file that can be uploaded, based on database and PHP limits
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$mindb=16777215; // from MEDIUMBLOB column type
$minphp=trim(ini_get('upload_max_filesize'));
switch (strtolower(substr($minphp, -1))) {
case 'g':
$minphp*=1024;
......@@ -49,11 +49,11 @@
case 'k':
$minphp*=1024;
}
return min($mindb, $minphp);
}
function qa_upload_file($localfilename, $sourcefilename, $maxfilesize=null, $onlyimage=false, $imagemaxwidth=null, $imagemaxheight=null)
/*
Move an uploaded image or other file into blob storage. Pass the $localfilename where the file is currently stored
......@@ -72,20 +72,20 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$result=array();
// Check per-user upload limits
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-app-limits.php';
switch (qa_user_permit_error(null, QA_LIMIT_UPLOADS))
{
case 'limit':
$result['error']=qa_lang('main/upload_limit');
return $result;
case false:
qa_limits_increment(qa_get_logged_in_userid(), QA_LIMIT_UPLOADS);
break;
......@@ -94,72 +94,72 @@
$result['error']=qa_lang('users/no_permission');
return $result;
}
// Check the uploaded file is not too large
$filesize=filesize($localfilename);
if (isset($maxfilesize))
$maxfilesize=min($maxfilesize, qa_get_max_upload_size());
else
$maxfilesize=qa_get_max_upload_size();
if ( ($filesize<=0) || ($filesize>$maxfilesize) ) { // if file was too big for PHP, $filesize will be zero
$result['error']=qa_lang_sub('main/max_upload_size_x', number_format($maxfilesize/1048576, 1).'MB');
return $result;
}
// Find out what type of source file was uploaded and if appropriate, check it's an image and get preliminary size measure
$pathinfo=pathinfo($sourcefilename);
$format=strtolower(@$pathinfo['extension']);
$isimage=($format=='png') || ($format=='gif') || ($format=='jpeg') || ($format=='jpg'); // allowed image extensions
if ($isimage) {
$imagesize=@getimagesize($localfilename);
if (is_array($imagesize)) {
$result['width']=$imagesize[0];
$result['height']=$imagesize[1];
switch ($imagesize['2']) { // reassign format based on actual content, if we can
case IMAGETYPE_GIF:
$format='gif';
break;
case IMAGETYPE_JPEG:
$format='jpeg';
break;
case IMAGETYPE_PNG:
$format='png';
break;
}
}
}
$result['format']=$format;
if ($onlyimage)
if ( (!$isimage) || !is_array($imagesize) ) {
$result['error']=qa_lang_sub('main/image_not_read', 'GIF, JPG, PNG');
return $result;
}
// Read in the raw file contents
$content=file_get_contents($localfilename);
// If appropriate, get more accurate image size and apply constraints to it
require_once QA_INCLUDE_DIR.'qa-util-image.php';
if ($isimage && qa_has_gd_image()) {
$image=@imagecreatefromstring($content);
if (is_resource($image)) {
$result['width']=$width=imagesx($image);
$result['height']=$height=imagesy($image);
if (isset($imagemaxwidth) || isset($imagemaxheight))
if (qa_image_constrain(
$width, $height,
......@@ -175,30 +175,30 @@
$result['height']=$height;
}
}
if (is_resource($image)) // might have been lost
imagedestroy($image);
}
}
// Create the blob and return
require_once QA_INCLUDE_DIR.'qa-app-blobs.php';
$userid=qa_get_logged_in_userid();
$cookieid=isset($userid) ? qa_cookie_get() : qa_cookie_get_create();
$result['blobid']=qa_create_blob($content, $format, $sourcefilename, $userid, $cookieid, qa_remote_ip_address());
if (!isset($result['blobid'])) {
$result['error']=qa_lang('main/general_error');
return $result;
}
$result['bloburl']=qa_get_blob_url($result['blobid'], true);
return $result;
return $result;
}
function qa_upload_file_one($maxfilesize=null, $onlyimage=false, $imagemaxwidth=null, $imagemaxheight=null)
/*
......@@ -206,11 +206,11 @@
*/
{
$file=reset($_FILES);
return qa_upload_file($file['tmp_name'], $file['name'], $maxfilesize, $onlyimage, $imagemaxwidth, $imagemaxheight);
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-app-votes.php
Version: See define()s at top of qa-include/qa-base.php
Description: Handling incoming votes (application level)
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -37,13 +37,13 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
// The 'login', 'confirm', 'limit', 'userblock' and 'ipblock' permission errors are reported to the user here.
// Others ('approve', 'level') prevent the buttons being clickable in the first place, in qa_get_vote_view(...)
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-app-limits.php';
if (
is_array($post) &&
( ($post['basetype']=='Q') || ($post['basetype']=='A') ) &&
......@@ -51,37 +51,37 @@
( (!isset($post['userid'])) || (!isset($userid)) || ($post['userid']!=$userid) )
) {
$permiterror=qa_user_post_permit_error(($post['basetype']=='Q') ? 'permit_vote_q' : 'permit_vote_a', $post, QA_LIMIT_VOTES);
$errordownonly=(!$permiterror) && ($vote<0);
if ($errordownonly)
$permiterror=qa_user_post_permit_error('permit_vote_down', $post);
switch ($permiterror) {
case 'login':
return qa_insert_login_links(qa_lang_html('main/vote_must_login'), $topage);
break;
case 'confirm':
return qa_insert_login_links(qa_lang_html($errordownonly ? 'main/vote_down_must_confirm' : 'main/vote_must_confirm'), $topage);
break;
case 'limit':
return qa_lang_html('main/vote_limit');
break;
default:
return qa_lang_html('users/no_permission');
break;
case false:
return false;
}
} else
return qa_lang_html('main/vote_not_allowed'); // voting option should not have been presented (but could happen due to options change)
}
function qa_vote_set($post, $userid, $handle, $cookieid, $vote)
/*
Actually set (application level) the $vote (-1/0/1) by $userid (with $handle and $cookieid) on $postid.
......@@ -89,56 +89,56 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'qa-db-points.php';
require_once QA_INCLUDE_DIR.'qa-db-hotness.php';
require_once QA_INCLUDE_DIR.'qa-db-votes.php';
require_once QA_INCLUDE_DIR.'qa-db-post-create.php';
require_once QA_INCLUDE_DIR.'qa-app-limits.php';
$vote=(int)min(1, max(-1, $vote));
$oldvote=(int)qa_db_uservote_get($post['postid'], $userid);
qa_db_uservote_set($post['postid'], $userid, $vote);
qa_db_post_recount_votes($post['postid']);
$postisanswer=($post['basetype']=='A');
if ($postisanswer) {
qa_db_post_acount_update($post['parentid']);
qa_db_unupaqcount_update();
}
$columns=array();
if ( ($vote>0) || ($oldvote>0) )
$columns[]=$postisanswer ? 'aupvotes' : 'qupvotes';
if ( ($vote<0) || ($oldvote<0) )
$columns[]=$postisanswer ? 'adownvotes' : 'qdownvotes';
qa_db_points_update_ifuser($userid, $columns);
qa_db_points_update_ifuser($post['userid'], array($postisanswer ? 'avoteds' : 'qvoteds', 'upvoteds', 'downvoteds'));
if ($post['basetype']=='Q')
qa_db_hotness_update($post['postid']);
if ($vote<0)
$event=$postisanswer ? 'a_vote_down' : 'q_vote_down';
elseif ($vote>0)
$event=$postisanswer ? 'a_vote_up' : 'q_vote_up';
else
$event=$postisanswer ? 'a_vote_nil' : 'q_vote_nil';
qa_report_event($event, $userid, $handle, $cookieid, array(
'postid' => $post['postid'],
'vote' => $vote,
'oldvote' => $oldvote,
));
}
function qa_flag_error_html($post, $userid, $topage)
/*
Check if $userid can flag $post, on the page $topage.
......@@ -146,7 +146,7 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
// The 'login', 'confirm', 'limit', 'userblock' and 'ipblock' permission errors are reported to the user here.
// Others ('approve', 'level') prevent the flag button being shown, in qa_page_q_post_rules(...)
......@@ -160,32 +160,32 @@
qa_opt('flagging_of_posts') &&
( (!isset($post['userid'])) || (!isset($userid)) || ($post['userid']!=$userid) )
) {
switch (qa_user_post_permit_error('permit_flag', $post, QA_LIMIT_FLAGS)) {
case 'login':
return qa_insert_login_links(qa_lang_html('question/flag_must_login'), $topage);
break;
case 'confirm':
return qa_insert_login_links(qa_lang_html('question/flag_must_confirm'), $topage);
break;
case 'limit':
return qa_lang_html('question/flag_limit');
break;
default:
return qa_lang_html('users/no_permission');
break;
case false:
return false;
}
} else
return qa_lang_html('question/flag_not_allowed'); // flagging option should not have been presented
}
function qa_flag_set_tohide($oldpost, $userid, $handle, $cookieid, $question)
/*
......@@ -195,20 +195,20 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'qa-db-votes.php';
require_once QA_INCLUDE_DIR.'qa-app-limits.php';
require_once QA_INCLUDE_DIR.'qa-db-post-update.php';
qa_db_userflag_set($oldpost['postid'], $userid, true);
qa_db_post_recount_flags($oldpost['postid']);
qa_db_flaggedcount_update();
switch ($oldpost['basetype']) {
case 'Q':
$event='q_flag';
break;
case 'A':
$event='a_flag';
break;
......@@ -217,9 +217,9 @@
$event='c_flag';
break;
}
$post=qa_db_select_with_pending(qa_db_full_post_selectspec(null, $oldpost['postid']));
qa_report_event($event, $userid, $handle, $cookieid, array(
'postid' => $oldpost['postid'],
'oldpost' => $oldpost,
......@@ -227,7 +227,7 @@
'questionid' => $question['postid'],
'question' => $question,
));
return ($post['flagcount']>=qa_opt('flagging_hide_after')) && !$post['hidden'];
}
......@@ -239,20 +239,20 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'qa-db-votes.php';
require_once QA_INCLUDE_DIR.'qa-app-limits.php';
require_once QA_INCLUDE_DIR.'qa-db-post-update.php';
qa_db_userflag_set($oldpost['postid'], $userid, false);
qa_db_post_recount_flags($oldpost['postid']);
qa_db_flaggedcount_update();
switch ($oldpost['basetype']) {
case 'Q':
$event='q_unflag';
break;
case 'A':
$event='a_unflag';
break;
......@@ -261,14 +261,14 @@
$event='c_unflag';
break;
}
qa_report_event($event, $userid, $handle, $cookieid, array(
'postid' => $oldpost['postid'],
'oldpost' => $oldpost,
));
}
function qa_flags_clear_all($oldpost, $userid, $handle, $cookieid)
/*
Clear (application level) all flags on $oldpost by $userid (with $handle and $cookieid).
......@@ -276,11 +276,11 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'qa-db-votes.php';
require_once QA_INCLUDE_DIR.'qa-app-limits.php';
require_once QA_INCLUDE_DIR.'qa-db-post-update.php';
qa_db_userflags_clear_all($oldpost['postid']);
qa_db_post_recount_flags($oldpost['postid']);
qa_db_flaggedcount_update();
......@@ -289,7 +289,7 @@
case 'Q':
$event='q_clearflags';
break;
case 'A':
$event='a_clearflags';
break;
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-blob.php
Version: See define()s at top of qa-include/qa-base.php
Description: Response to blob requests, outputting blob from the database
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -34,7 +34,7 @@
header('HTTP/1.1 500 Internal Server Error');
qa_exit('error');
}
// Load the Q2A base file which sets up a bunch of crucial stuff
......@@ -48,44 +48,44 @@
require_once QA_INCLUDE_DIR.'qa-app-blobs.php';
qa_db_connect('qa_blob_db_fail_handler');
$blob=qa_read_blob(qa_get('qa_blobid'));
if (isset($blob)) {
header('Cache-Control: max-age=2592000, public'); // allows browsers and proxies to cache the blob
switch ($blob['format']) {
case 'jpeg':
case 'jpg':
header('Content-Type: image/jpeg');
break;
case 'gif':
header('Content-Type: image/gif');
break;
case 'png':
header('Content-Type: image/png');
break;
case 'swf':
header('Content-Type: application/x-shockwave-flash');
break;
default:
$filename=preg_replace('/[^A-Za-z0-9 \\._-]/', '-', $blob['filename']); // for compatibility with HTTP headers and all browsers
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.$filename.'"');
break;
}
}
echo $blob['content'];
} else
header('HTTP/1.0 404 Not Found');
qa_db_disconnect();
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-check-lang.php
Version: See define()s at top of qa-include/qa-base.php
Description: Development tool to see which language phrases are missing or unused
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -28,10 +28,10 @@
require 'qa-base.php';
require_once QA_INCLUDE_DIR.'qa-app-users.php';
if (qa_get_logged_in_level() < QA_USER_LEVEL_ADMIN)
qa_redirect('admin/general', null, qa_opt('site_url'));
header('Content-type: text/html; charset=utf-8');
?>
<html>
......@@ -52,17 +52,17 @@
if (preg_match_all('/\^(([0-9]+)|([a-z_]+)|)/', $phrase, $matches))
foreach ($matches[0] as $match)
@$substitutions[$match]++;
return $substitutions;
}
echo '<font color="#cc0000"><code>Dark red = important to review.</code></font><br>';
echo '<font color="#cc9999"><code>Light red = probably safe to ignore.</code></font>';
echo '<h1>Checking US English files in <code>qa-include</code>...</h1>';
$includefiles=array_merge(glob(QA_INCLUDE_DIR.'qa-*.php'), glob(QA_PLUGIN_DIR.'*/qa-*.php'));
$definite=array();
$probable=array();
$possible=array();
......@@ -70,14 +70,14 @@
$english=array();
$backmap=array();
$substitutions=array();
output_start_includes();
foreach ($includefiles as $includefile) {
$contents=file_get_contents($includefile);
preg_match_all('/qa_lang[a-z_]*\s*\(\s*[\'\"]([a-z]+)\/([0-9a-z_]+)[\'\"]/', $contents, $matches, PREG_SET_ORDER);
foreach ($matches as $matchparts)
if ($matchparts[2]=='date_month_') { // special case for month names
for ($month=1; $month<=12; $month++)
......@@ -85,7 +85,7 @@
} else
@$definite[$matchparts[1]][$matchparts[2]]++;
preg_match_all('/[\'\"]([a-z]+)\/([0-9a-z_]+)[\'\"]/', $contents, $matches, PREG_SET_ORDER);
foreach ($matches as $matchparts)
......@@ -93,10 +93,10 @@
if (preg_match('|/qa-include/qa-lang-([a-z]+)\.php$|', $includefile, $matches)) { // it's a lang file
$prefix=$matches[1];
output_reading_include($includefile);
$phrases=@include $includefile;
foreach ($phrases as $key => $value) {
@$defined[$prefix][$key]++;
$english[$prefix][$key]=$value;
......@@ -106,69 +106,69 @@
} else { // it's a different file
preg_match_all('/[\'\"\/]([0-9a-z_]+)[\'\"]/', $contents, $matches, PREG_SET_ORDER);
foreach ($matches as $matchparts)
@$possible[$matchparts[1]]++;
}
}
output_finish_includes();
foreach ($definite as $key => $valuecount)
foreach ($valuecount as $value => $count)
if (!@$defined[$key][$value])
output_lang_issue($key, $value, 'used by '.$count.' file/s but not defined');
foreach ($defined as $key => $valuecount)
foreach ($valuecount as $value => $count)
if ( (!@$definite[$key][$value]) && (!@$probable[$key][$value]) && (!@$possible[$value]) )
output_lang_issue($key, $value, 'defined but apparently not used');
foreach ($backmap as $phrase => $where)
if (count($where)>1)
foreach ($where as $onewhere)
output_lang_issue($onewhere['prefix'], $onewhere['key'], 'contains the shared phrase "'.$phrase.'"', false);
require_once QA_INCLUDE_DIR.'qa-app-admin.php';
$languages=qa_admin_language_options();
unset($languages['']);
foreach ($languages as $code => $language) {
echo '<h1>Checking '.$language.' files in <code>qa-lang/'.$code.'</code>...</h1>';
$langdefined=array();
$langdifferent=array();
$langsubstitutions=array();
$langincludefiles=glob(QA_LANG_DIR.$code.'/qa-*.php');
$langnewphrases=array();
output_start_includes();
foreach ($langincludefiles as $langincludefile)
if (preg_match('/qa-lang-([a-z]+)\.php$/', $langincludefile, $matches)) { // it's a lang file
$prefix=$matches[1];
output_reading_include($langincludefile);
$phrases=@include $langincludefile;
foreach ($phrases as $key => $value) {
@$langdefined[$prefix][$key]++;
$langdifferent[$prefix][$key]=($value!=@$english[$prefix][$key]);
$langsubstitutions[$prefix][$key]=get_phrase_substitutions($value);
}
}
output_finish_includes();
foreach ($langdefined as $key => $valuecount)
foreach ($valuecount as $value => $count) {
if (!@$defined[$key][$value])
output_lang_issue($key, $value, 'defined but not in US English files');
elseif (!$langdifferent[$key][$value])
output_lang_issue($key, $value, 'identical to US English files', false);
else
foreach ($substitutions[$key][$value] as $substitution => $subcount)
if (!@$langsubstitutions[$key][$value][$substitution])
......@@ -176,10 +176,10 @@
elseif ($subcount > @$langsubstitutions[$key][$value][$substitution])
output_lang_issue($key, $value, 'has fewer of the substitution '.$substitution);
}
foreach ($defined as $key => $valuecount) {
$showaserror=!(($key=='admin') || ($key=='options') || ($code=='en-GB'));
if (@$langdefined[$key]) {
if (count($langdefined[$key]) < (count($valuecount)/2)) { // only a few phrases defined
output_lang_issue($key, null, 'few translations provided so will use US English defaults', $showaserror);
......@@ -193,22 +193,22 @@
} else
output_lang_issue($key, null, 'no translations provided so will use US English defaults', $showaserror);
}
foreach ($langnewphrases as $prefix => $phrases) {
echo '<h2>'.$language.' phrases to add to <code>qa-lang/'.$code.'/qa-lang-'.$prefix.'.php</code>:</h2>';
echo 'Copy and paste this into the middle of <code>qa-lang/'.$code.'/qa-lang-'.$prefix.'.php</code> then translate the right-hand side after the <code>=></code> symbol.';
echo '<pre>';
foreach ($phrases as $key => $value)
echo '<span style="font-size:25%;">'."\t\t</span>'".$key."' => \"".strtr($value, array('\\' => '\\\\', '"' => '\"', '$' => '\$', "\n" => '\n', "\t" => '\t'))."\",\n";
echo '</pre>';
}
}
function output_lang_issue($prefix, $key, $issue, $error=true)
{
echo '<font color="'.($error ? '#cc0000' : '#cc9999').'"><code>';
......@@ -217,7 +217,7 @@
if (strlen($key))
echo "'<b>".qa_html($key)."</b>'";
echo '</code></font> &nbsp; '.qa_html($issue).'<br>';
}
......@@ -225,30 +225,30 @@
function output_start_includes()
{
global $oneread;
$oneread=false;
echo '<p style="font-size:80%; color:#999;">Reading: ';
}
function output_reading_include($file)
{
global $oneread;
echo ($oneread ? ', ' : '').htmlspecialchars(basename($file));
flush();
$oneread=true;
}
function output_finish_includes()
{
echo '</p>';
}
echo '<h1>Finished scanning for problems!</h1>';
?>
......
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-db-blobs.php
Version: See define()s at top of qa-include/qa-base.php
Description: Database-level access to blobs table for large chunks of data (e.g. images)
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -32,14 +32,14 @@
function qa_db_blob_create($content, $format, $sourcefilename=null, $userid=null, $cookieid=null, $ip=null)
/*
Create a new blob in the database with $content and $format, other fields as provided
Create a new blob in the database with $content and $format, other fields as provided
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
for ($attempt=0; $attempt<10; $attempt++) {
$blobid=qa_db_random_bigint();
if (qa_db_blob_exists($blobid))
continue;
......@@ -47,28 +47,28 @@
'INSERT INTO ^blobs (blobid, format, content, filename, userid, cookieid, createip, created) VALUES (#, $, $, $, $, #, INET_ATON($), NOW())',
$blobid, $format, $content, $sourcefilename, $userid, $cookieid, $ip
);
return $blobid;
}
return null;
}
function qa_db_blob_read($blobid)
/*
Get the information about blob $blobid from the database
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return qa_db_read_one_assoc(qa_db_query_sub(
'SELECT content, format, filename FROM ^blobs WHERE blobid=#',
$blobid
), true);
}
function qa_db_blob_set_content($blobid, $content)
/*
Change the content of blob $blobid in the database to $content (can also be null)
......@@ -79,29 +79,29 @@
$content, $blobid
);
}
function qa_db_blob_delete($blobid)
/*
Delete blob $blobid in the database
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
qa_db_query_sub(
'DELETE FROM ^blobs WHERE blobid=#',
$blobid
);
}
function qa_db_blob_exists($blobid)
/*
Check if blob $blobid exists in the database
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return qa_db_read_one_value(qa_db_query_sub(
'SELECT COUNT(*) FROM ^blobs WHERE blobid=#',
$blobid
......
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-db-cache.php
Version: See define()s at top of qa-include/qa-base.php
Description: Database-level access to cache table
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -38,7 +38,7 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
qa_db_query_sub(
'DELETE FROM ^cache WHERE lastread<NOW()-INTERVAL # SECOND',
QA_DB_MAX_CACHE_AGE
......@@ -49,29 +49,29 @@
$type, $cacheid, $content
);
}
function qa_db_cache_get($type, $cacheid)
/*
Retrieve the item ($type, $cacheid) from the database cache table
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$content=qa_db_read_one_value(qa_db_query_sub(
'SELECT content FROM ^cache WHERE type=$ AND cacheid=#',
$type, $cacheid
), true);
if (isset($content))
qa_db_query_sub(
'UPDATE ^cache SET lastread=NOW() WHERE type=$ AND cacheid=#',
$type, $cacheid
);
return $content;
}
/*
Omit PHP closing tag to help avoid accidental output
......
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-db-cookies.php
Version: See define()s at top of qa-include/qa-base.php
Description: Database access functions for user cookies
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -37,7 +37,7 @@
{
for ($attempt=0; $attempt<10; $attempt++) {
$cookieid=qa_db_random_bigint();
if (qa_db_cookie_exists($cookieid))
continue;
......@@ -46,14 +46,14 @@
'VALUES (#, NOW(), COALESCE(INET_ATON($), 0))',
$cookieid, $ipaddress
);
return $cookieid;
}
return null;
}
function qa_db_cookie_written($cookieid, $ipaddress)
/*
Note in database that a write operation has been done by user identified by $cookieid and from $ipaddress
......@@ -65,7 +65,7 @@
);
}
function qa_db_cookie_exists($cookieid)
/*
Return whether $cookieid exists in database
......
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-db-events.php
Version: See define()s at top of qa-include/qa-base.php
Description: Database-level access to userevents and sharedevents tables
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -41,77 +41,77 @@
{
require_once QA_INCLUDE_DIR.'qa-db-maxima.php';
require_once QA_INCLUDE_DIR.'qa-app-updates.php';
$updatedsql=isset($timestamp) ? ('FROM_UNIXTIME('.qa_db_argument_to_mysql($timestamp, false).')') : 'NOW()';
// Enter it into the appropriate shared event stream for that entity
qa_db_query_sub(
'INSERT INTO ^sharedevents (entitytype, entityid, questionid, lastpostid, updatetype, lastuserid, updated) '.
'VALUES ($, #, #, #, $, $, '.$updatedsql.')',
$entitytype, $entityid, $questionid, $lastpostid, $updatetype, $lastuserid
);
// If this is for a question entity, check the shared event stream doesn't have too many entries for that question
$questiontruncated=false;
if ($entitytype==QA_ENTITY_QUESTION) {
$truncate=qa_db_read_one_value(qa_db_query_sub(
'SELECT updated FROM ^sharedevents WHERE entitytype=$ AND entityid=# AND questionid=# ORDER BY updated DESC LIMIT #,1',
$entitytype, $entityid, $questionid, QA_DB_MAX_EVENTS_PER_Q
), true);
if (isset($truncate)) {
qa_db_query_sub(
'DELETE FROM ^sharedevents WHERE entitytype=$ AND entityid=# AND questionid=# AND updated<=$',
$entitytype, $entityid, $questionid, $truncate
);
$questiontruncated=true;
}
}
// If we didn't truncate due to a specific question, truncate the shared event stream for its overall length
if (!$questiontruncated) {
$truncate=qa_db_read_one_value(qa_db_query_sub(
'SELECT updated FROM ^sharedevents WHERE entitytype=$ AND entityid=$ ORDER BY updated DESC LIMIT #,1',
$entitytype, $entityid, (int)qa_opt('max_store_user_updates')
), true);
if (isset($truncate))
qa_db_query_sub(
'DELETE FROM ^sharedevents WHERE entitytype=$ AND entityid=$ AND updated<=$',
$entitytype, $entityid, $truncate
);
}
// See if we can identify a user who has favorited this entity, but is not using its shared event stream
$randomuserid=qa_db_read_one_value(qa_db_query_sub(
'SELECT userid FROM ^userfavorites WHERE entitytype=$ AND entityid=# AND nouserevents=0 ORDER BY RAND() LIMIT 1',
$entitytype, $entityid
), true);
if (isset($randomuserid)) {
// If one was found, this means we have one or more individual event streams, so update them all
qa_db_query_sub(
'INSERT INTO ^userevents (userid, entitytype, entityid, questionid, lastpostid, updatetype, lastuserid, updated) '.
'SELECT userid, $, #, #, #, $, $, '.$updatedsql.' FROM ^userfavorites WHERE entitytype=$ AND entityid=# AND nouserevents=0',
$entitytype, $entityid, $questionid, $lastpostid, $updatetype, $lastuserid, $entitytype, $entityid
);
// Now truncate the random individual event stream that was found earlier
// (in theory we should truncate them all, but truncation is just a 'housekeeping' activity, so it's not necessary)
qa_db_user_events_truncate($randomuserid, $questionid);
}
}
function qa_db_event_create_not_entity($userid, $questionid, $lastpostid, $updatetype, $lastuserid, $timestamp=null)
/*
Add an event to the event stream for $userid which is not related to an entity they are following (but rather a
......@@ -123,52 +123,52 @@
require_once QA_INCLUDE_DIR.'qa-app-updates.php';
$updatedsql=isset($timestamp) ? ('FROM_UNIXTIME('.qa_db_argument_to_mysql($timestamp, false).')') : 'NOW()';
qa_db_query_sub(
"INSERT INTO ^userevents (userid, entitytype, entityid, questionid, lastpostid, updatetype, lastuserid, updated) ".
"VALUES ($, $, 0, #, #, $, $, ".$updatedsql.")",
$userid, QA_ENTITY_NONE, $questionid, $lastpostid, $updatetype, $lastuserid
);
qa_db_user_events_truncate($userid, $questionid);
}
function qa_db_user_events_truncate($userid, $questionid=null)
/*
Trim the number of events in the event stream for $userid. If an event was just added for a particular question,
pass the question's id in $questionid (to help focus the truncation).
*/
{
// First try truncating based on there being too many events for this question
$questiontruncated=false;
if (isset($questionid)) {
$truncate=qa_db_read_one_value(qa_db_query_sub(
'SELECT updated FROM ^userevents WHERE userid=$ AND questionid=# ORDER BY updated DESC LIMIT #,1',
$userid, $questionid, QA_DB_MAX_EVENTS_PER_Q
), true);
if (isset($truncate)) {
qa_db_query_sub(
'DELETE FROM ^userevents WHERE userid=$ AND questionid=# AND updated<=$',
$userid, $questionid, $truncate
);
$questiontruncated=true;
}
}
// If that didn't happen, try truncating the stream in general based on its total length
if (!$questiontruncated) {
$truncate=qa_db_read_one_value(qa_db_query_sub(
'SELECT updated FROM ^userevents WHERE userid=$ ORDER BY updated DESC LIMIT #,1',
$userid, (int)qa_opt('max_store_user_updates')
), true);
if (isset($truncate))
qa_db_query_sub(
'DELETE FROM ^userevents WHERE userid=$ AND updated<=$',
......
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-db-favorites.php
Version: See define()s at top of qa-include/qa-base.php
Description: Database-level access to userfavorites table
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -53,11 +53,11 @@
background to manage the queuing and transport of these messages from publishers (event streams) to subscribers
(users' lists of news updates). While we could have a cron-style process to manage this, I'm avoiding it for as long
as possible since it complicates setup. It also means there can be delays in updating users' news feeds.
So instead we adopt a hybrid approach. For each event created in an entity's stream, we record a single copy of that
event in the entity's stream in the qa_sharedevents table. In addition, by default, we place a copy of that event into
the list of news updates for each user subscribed to the stream, via the qa_userevents table.
However, if there are more than a certain number of subscribers to the stream, we skip this second step, i.e. we
only record one copy in the qa_sharedevents table. This limits the cost of publishing an event.
......@@ -73,10 +73,10 @@
Note that this approach isn't aimed at reducing the total cost of keeping all users up-to-date on all events, but
rather ensuring that no individual operation (posting an event or retrieving a user's list of updates) takes too
long, since that would turn into a very slow response time for the corresponding HTTP request.
What should we use for the threshold T, so that if a stream has more than T subscribers, its events are only
recorded in the shared stream? One approach is as follows:
[this ignores stream length and truncation, which are constant factors]
T = our threshold
......@@ -102,7 +102,7 @@
number of (total) shared streams that any of those users are subscribed to. If this is above T, that means that our
maximum cost of retrieving a list of news updates is starting to go past our maximum cost of recording an event. So
we rebalance things out by increasing T as appropriate, for use in future cases.
Note that once an event stream has made this switch, to be accessed only via its shared stream, we don't go back.
*/
......@@ -117,59 +117,59 @@
$threshold=qa_opt('max_copy_user_updates'); // if this many users subscribe to it, create a shared stream
// Add in the favorite for this user, unshared events at first (will be switched later if appropriate)
qa_db_query_sub(
'INSERT IGNORE INTO ^userfavorites (userid, entitytype, entityid, nouserevents) VALUES ($, $, #, 0)',
$userid, $entitytype, $entityid
);
// See whether this entity already has another favoriter who uses its shared event stream
$useshared=qa_db_read_one_value(qa_db_query_sub(
'SELECT COUNT(*) FROM ^userfavorites WHERE entitytype=$ AND entityid=# AND nouserevents>0 LIMIT 1',
$entitytype, $entityid
));
// If not, check whether it's time to switch it over to a shared stream
if (!$useshared) {
$favoriters=qa_db_read_one_value(qa_db_query_sub(
'SELECT COUNT(*) FROM ^userfavorites WHERE entitytype=$ AND entityid=# LIMIT #',
$entitytype, $entityid, $threshold
));
$useshared=($favoriters >= $threshold);
}
// If we're going to use the shared stream...
if ($useshared) {
// ... for all the people for whom we're switching this to a shared stream, find the highest number of other shared streams they have
$maxshared=qa_db_read_one_value(qa_db_query_sub(
'SELECT MAX(c) FROM (SELECT COUNT(*) AS c FROM ^userfavorites AS shared JOIN ^userfavorites AS unshared '.
'WHERE shared.userid=unshared.userid AND shared.nouserevents>0 AND unshared.entitytype=$ AND unshared.entityid=# AND unshared.nouserevents=0 GROUP BY shared.userid) y',
$entitytype, $entityid
));
// ... if this number is greater than our current 'max_copy_user_updates' threshold, increase that threshold (see long comment above)
if (($maxshared+1)>$threshold)
qa_opt('max_copy_user_updates', $maxshared+1);
// ... now switch all unshared favoriters (including this new one) over to be shared
qa_db_query_sub(
'UPDATE ^userfavorites SET nouserevents=1 WHERE entitytype=$ AND entityid=# AND nouserevents=0',
$entitytype, $entityid
);
// Otherwise if we're going to record this in user-specific streams ...
} else {
require_once QA_INCLUDE_DIR.'qa-db-events.php';
// ... copy across recent events from the shared stream
qa_db_query_sub(
......@@ -178,14 +178,14 @@
'^sharedevents WHERE entitytype=$ AND entityid=#',
$userid, $entitytype, $entityid
);
// ... and truncate the user's stream as appropriate
qa_db_user_events_truncate($userid);
}
}
function qa_db_favorite_delete($userid, $entitytype, $entityid)
/*
Delete the entity $entitytype with $entityid from the favorites list of $userid, removing any corresponding events
......@@ -196,13 +196,13 @@
'DELETE FROM ^userfavorites WHERE userid=$ AND entitytype=$ AND entityid=#',
$userid, $entitytype, $entityid
);
qa_db_query_sub(
'DELETE FROM ^userevents WHERE userid=$ AND entitytype=$ AND entityid=#',
$userid, $entitytype, $entityid
);
}
/*
Omit PHP closing tag to help avoid accidental output
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-db-hotness.php
Version: See define()s at top of qa-include/qa-base.php
Description: Functions for dealing with question hotness in the database
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -28,8 +28,8 @@
header('Location: ../');
exit;
}
function qa_db_hotness_update($firstpostid, $lastpostid=null, $viewincrement=false)
/*
Recalculate the hotness in the database for posts $firstpostid to $lastpostid (if specified)
......@@ -37,11 +37,11 @@
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
if (qa_should_update_counts()) {
if (!isset($lastpostid))
$lastpostid=$firstpostid;
$query= "UPDATE ^posts AS x, (SELECT parents.postid, parents.created AS qcreated, COALESCE(MAX(children.created), parents.created) as acreated, COUNT(children.postid) AS acount, parents.netvotes, parents.views FROM ^posts AS parents LEFT JOIN ^posts AS children ON parents.postid=children.parentid AND children.type='A' WHERE parents.postid>=# AND parents.postid<=# AND LEFT(parents.type, 1)='Q' GROUP BY postid) AS a SET x.hotness=(".
'((TO_DAYS(a.qcreated)-734138)*86400.0+TIME_TO_SEC(a.qcreated))*# + '. // zero-point is Jan 1, 2010
'((TO_DAYS(a.acreated)-734138)*86400.0+TIME_TO_SEC(a.acreated))*# + '.
......@@ -49,9 +49,9 @@
'(a.netvotes+0.0)*# + '.
'(a.views+0.0+#)*#'.
')'.($viewincrement ? ', x.views=x.views+1, x.lastviewip=INET_ATON($)' : '').' WHERE x.postid=a.postid';
// Additional multiples based on empirical analysis of activity on Q2A meta site to give approx equal influence for all factors
$arguments=array(
$firstpostid,
$lastpostid,
......@@ -62,10 +62,10 @@
$viewincrement ? 1 : 0,
qa_opt('hot_weight_views')*4000,
);
if ($viewincrement)
$arguments[]=qa_remote_ip_address();
qa_db_query_raw(qa_db_apply_sub($query, $arguments));
}
}
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-db-limits.php
Version: See define()s at top of qa-include/qa-base.php
Description: Database-level access to tables which monitor rate limits
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -38,28 +38,28 @@
{
$selects=array();
$arguments=array();
if (isset($userid)) {
$selects[]="(SELECT 'user' AS limitkey, period, count FROM ^userlimits WHERE userid=$ AND action=$)";
$arguments[]=$userid;
$arguments[]=$action;
}
if (isset($ip)) {
$selects[]="(SELECT 'ip' AS limitkey, period, count FROM ^iplimits WHERE ip=COALESCE(INET_ATON($), 0) AND action=$)";
$arguments[]=$ip;
$arguments[]=$action;
}
if (count($selects)) {
$query=qa_db_apply_sub(implode(' UNION ALL ', $selects), $arguments);
return qa_db_read_all_assoc(qa_db_query_raw($query), 'limitkey');
} else
return array();
}
function qa_db_limits_user_add($userid, $action, $period, $count)
/*
Increment the database rate limit count for user $userid and $action by $count within $period
......@@ -72,7 +72,7 @@
);
}
function qa_db_limits_ip_add($ip, $action, $period, $count)
/*
Increment the database rate limit count for IP address $ip and $action by $count within $period
......
......@@ -5,7 +5,7 @@
http://www.question2answer.org/
File: qa-include/qa-db-maxima.php
Version: See define()s at top of qa-include/qa-base.php
Description: Definitions that determine database column size and rows retrieved
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -65,7 +65,7 @@
@define('QA_DB_RETRIEVE_COMPLETE_TAGS', 1000);
@define('QA_DB_RETRIEVE_MESSAGES', 20);
// Keep event streams trimmed - not worth storing too many events per question because we only display the
// most recent event for each question, that has not been invalidated due to hiding/unselection/etc...
......
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-db-messages.php
Version: See define()s at top of qa-include/qa-base.php
Description: Database-level access to messages table for private message history
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -40,10 +40,10 @@
'INSERT INTO ^messages (type, fromuserid, touserid, content, format, created) VALUES ($, #, #, $, $, NOW())',
$public ? 'PUBLIC' : 'PRIVATE', $fromuserid, $touserid, $content, $format
);
return qa_db_last_insert_id();
}
function qa_db_message_delete($messageid)
/*
......
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-db-metas.php
Version: See define()s at top of qa-include/qa-base.php
Description: Database-level access to metas tables
......@@ -15,7 +15,7 @@
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
......@@ -38,7 +38,7 @@
qa_db_meta_set('usermetas', 'userid', $userid, $key, $value);
}
function qa_db_usermeta_clear($userid, $key)
/*
Clear the metadata for user $userid with $key ($key can also be an array of keys)
......@@ -47,7 +47,7 @@
qa_db_meta_clear('usermetas', 'userid', $userid, $key);
}
function qa_db_usermeta_get($userid, $key)
/*
Return the metadata value for user $userid with $key ($key can also be an array of keys in which case this
......@@ -56,7 +56,7 @@
{
return qa_db_meta_get('usermetas', 'userid', $userid, $key);
}
function qa_db_postmeta_set($postid, $key, $value)
/*
......@@ -66,7 +66,7 @@
qa_db_meta_set('postmetas', 'postid', $postid, $key, $value);
}
function qa_db_postmeta_clear($postid, $key)
/*
Clear the metadata for post $postid with $key ($key can also be an array of keys)
......@@ -75,7 +75,7 @@
qa_db_meta_clear('postmetas', 'postid', $postid, $key);
}
function qa_db_postmeta_get($postid, $key)
/*
Return the metadata value for post $postid with $key ($key can also be an array of keys in which case this
......@@ -94,7 +94,7 @@
qa_db_meta_set('categorymetas', 'categoryid', $categoryid, $key, $value);
}
function qa_db_categorymeta_clear($categoryid, $key)
/*
Clear the metadata for category $categoryid with $key ($key can also be an array of keys)
......@@ -103,7 +103,7 @@
qa_db_meta_clear('categorymetas', 'categoryid', $categoryid, $key);
}
function qa_db_categorymeta_get($categoryid, $key)
/*
Return the metadata value for category $categoryid with $key ($key can also be an array of keys in which
......@@ -112,7 +112,7 @@
{
return qa_db_meta_get('categorymetas', 'categoryid', $categoryid, $key);
}
function qa_db_tagmeta_set($tag, $key, $value)
/*
......@@ -122,7 +122,7 @@
qa_db_meta_set('tagmetas', 'tag', $tag, $key, $value);
}
function qa_db_tagmeta_clear($tag, $key)
/*
Clear the metadata for tag $tag with $key ($key can also be an array of keys)
......@@ -131,7 +131,7 @@
qa_db_meta_clear('tagmetas', 'tag', $tag, $key);
}
function qa_db_tagmeta_get($tag, $key)
/*
Return the metadata value for tag $tag with $key ($key can also be an array of keys in which case this
......@@ -142,7 +142,7 @@
}
function qa_db_meta_set($metatable, $idcolumn, $idvalue, $title, $content)
function qa_db_meta_set($metatable, $idcolumn, $idvalue, $title, $content)
/*
Internal general function to set metadata
*/
......@@ -153,7 +153,7 @@
);
}
function qa_db_meta_clear($metatable, $idcolumn, $idvalue, $title)
/*
Internal general function to clear metadata
......@@ -165,7 +165,7 @@
'DELETE FROM ^'.$metatable.' WHERE '.$idcolumn.'=$ AND title IN ($)',
$idvalue, $title
);
} else
qa_db_query_sub(
'DELETE FROM ^'.$metatable.' WHERE '.$idcolumn.'=$ AND title=$',
......@@ -173,7 +173,7 @@
);
}
function qa_db_meta_get($metatable, $idcolumn, $idvalue, $title)
/*
Internal general function to return metadata
......@@ -187,7 +187,7 @@
), 'title', 'content');
else
return array();
} else
return qa_db_read_one_value(qa_db_query_sub(
'SELECT content FROM ^'.$metatable.' WHERE '.$idcolumn.'=$ AND title=$',
......@@ -195,7 +195,7 @@
), true);
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
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