Commit c3681f4f by ProThoughts Committed by GitHub

Merge pull request #6 from q2a/1.8

merge 1.8
parents e0e124c4 e731787d
sudo: false sudo: false
dist: trusty
language: php language: php
php: php:
- '5.3'
- '5.4' - '5.4'
- '5.5' - '5.5'
- '5.6' - '5.6'
- '7.0' - '7.0'
- '7.1'
matrix:
include:
- php: '5.3'
dist: precise
notifications: notifications:
email: email:
...@@ -16,16 +23,14 @@ notifications: ...@@ -16,16 +23,14 @@ notifications:
before_script: before_script:
# PHP_CodeSniffer # PHP_CodeSniffer
- curl -o phpcs.phar https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar - curl -L -o phpcs.phar https://github.com/squizlabs/PHP_CodeSniffer/releases/download/2.9.1/phpcs.phar
# PHP Copy/Paste Detector # PHPUnit - manually download old version so that it works on PHP 7
- curl -o phpcpd.phar https://phar.phpunit.de/phpcpd.phar - curl -L -o phpunit.phar https://phar.phpunit.de/phpunit-4.8.35.phar
# Basic config required for PHPUnit # Basic config required for PHPUnit
- cp qa-tests/phpunit-qa-config.php qa-config.php - cp qa-tests/phpunit-qa-config.php qa-config.php
script: script:
# PHP_CodeSniffer (turned off for now) # PHP_CodeSniffer
#- php phpcs.phar --report=emacs --extensions=php --standard=qa-tests/phpcs/ruleset.xml . - php phpcs.phar --report=emacs --extensions=php --standard=qa-tests/phpcs/ruleset.xml .
# PHP Copy/Paste Detector
- php phpcpd.phar --exclude vendor .
# PHPUnit # PHPUnit
- phpunit --bootstrap qa-tests/autoload.php qa-tests - php phpunit.phar --bootstrap qa-tests/autoload.php qa-tests
...@@ -21,15 +21,11 @@ If you wish to implement a feature, you should start a discussion on the [Questi ...@@ -21,15 +21,11 @@ If you wish to implement a feature, you should start a discussion on the [Questi
## Coding style ## Coding style
From 1.7 onwards a new coding style is being implemented that is more in line with other projects. The core codebase is gradually being refactored, and any new code should use the guidelines below. When making changes it's encouraged to update the style of the surrounding code, e.g. the rest of the function being modified. From 1.7 onwards a new coding style has been implemented that is more in line with other projects. All PHP code should use these guidelines:
However, **please keep style-only changes to a separate commit!** For example if you fix a bug, do that first in one commit, then optionally reformat the rest of the function's code and perform a second commit.
### Guidelines
- PHP code should start with `<?php` (almost always the very first line). The closing tag `?>` should be omitted to avoid accidental output. - PHP code should start with `<?php` (almost always the very first line). The closing tag `?>` should be omitted to avoid accidental output.
- PHP files should use UTF-8 encoding without BOM (this is usually default in most text editors). - PHP files should use UTF-8 encoding without BOM (this is usually default in most text editors).
- Trailing whitespace (tabs or spaces at the end of lines) should be trimmed on save. Any advanced text editor should be able to do this. (For Sublime Text you can add the option `"trim_trailing_white_space_on_save": true` to your preferences. In Notepad++ you can press Alt+Shift+S.) - Trailing whitespace (tabs or spaces at the end of lines) should not be present. Any advanced text editor should be able to do this automatically when saving. (For Sublime Text you can add the option `"trim_trailing_white_space_on_save": true` to your preferences. In Notepad++ you can press Alt+Shift+S.)
- Use tabs for indenting. Each file should start at level 0 (i.e. no indentation). - Use tabs for indenting. Each file should start at level 0 (i.e. no indentation).
- Functions should use a DocBlock-style comment. - Functions should use a DocBlock-style comment.
- Operators (`=`, `+` etc) should have a space either side. - Operators (`=`, `+` etc) should have a space either side.
...@@ -37,66 +33,12 @@ However, **please keep style-only changes to a separate commit!** For example if ...@@ -37,66 +33,12 @@ However, **please keep style-only changes to a separate commit!** For example if
- Opening braces for classes and functions should be on the next line. - Opening braces for classes and functions should be on the next line.
- Opening braces for control structures should be on the same line. All control structures should use braces. - Opening braces for control structures should be on the same line. All control structures should use braces.
### Examples If in doubt, follow the style of the surrounding code. Code examples can be found in the [Q2A docs here](http://docs.question2answer.org/contribute/).
Here is an example of the old style. Even though the braces are technically optional (the foreach contains only one statement), they should be used here for clarity.
foreach ($thingarray as $thing)
if (isset($thing['id']))
if (strpos($thing['id'], 'Hello')===0)
$newthing='Goodbye';
elseif ($thing['id']=='World')
$newerthing='Galaxy';
else
return null;
It should be rewritten as:
foreach ($thingarray as $thing) {
if (isset($thing['id'])) {
if (strpos($thing['id'], 'Hello') === 0) {
$newthing = 'Goodbye';
} elseif ($thing['id'] == 'World') {
$newerthing = 'Galaxy';
}
} else {
return null;
}
}
Here is a class example showing the placement of braces, operators, and a DocBlock comment.
class qa_example
{
/**
* Adds 1 to the supplied number.
*
* @param int $number The number to increment.
*
* @return int Returns the new number.
*/
public function add_one($number)
{
$result = $number + 1;
return $result;
}
}
### New autoloaded classes
From version 1.7 some classes are autoloaded, so it's possible to use them without adding a `require_once` first. These loosely follow [PSR-0][PSR0] using faux namespaces. This is being done slowly and carefully to maintain backwards compatibility, and does not apply to plugins, themes, nor most of the core for that matter.
Classes are stored in the `qa-include/Q2A` folder, and then in subfolders depending on their categorization.
Class names should be of the form `Q2A_<Namespace>_<Class>`, e.g. `Q2A_Util_Debug`. There may be multiple "namespaces", e.g. `Q2A_Db_User_Messages`.
Classes are mapped to PHP files with the underscores converted to directory separators. The `Q2A_Util_Debug` class is in the file `qa-include/Q2A/Util/Debug.php`. A class named `Q2A_Db_User_Messages` would be in a file `qa-include/Q2A/Db/User/Messages.php`.
## Documentation ## Documentation
Please see the repository https://github.com/q2a/q2a.github.io/ which automatically produces the documentation website http://docs.question2answer.org/ Please see the repository [q2a.github.io](https://github.com/q2a/q2a.github.io/) which automatically produces the documentation website [docs.question2answer.org](http://docs.question2answer.org/).
[Home]: http://www.question2answer.org/ [Home]: http://www.question2answer.org/
......
Question2Answer Question2Answer
----------------------------- -----------------------------
[![Build Status](https://travis-ci.org/q2a/question2answer.png?branch=master)](https://travis-ci.org/q2a/question2answer) [![Build Status](https://travis-ci.org/q2a/question2answer.png?branch=1.8)](https://travis-ci.org/q2a/question2answer/branches)
[Question2Answer][Q2A] (Q2A) is a popular free open source Q&A platform for PHP/MySQL, used by over 16,000 sites in 40 languages. [Question2Answer][Q2A] (Q2A) is a popular free open source Q&A platform for PHP/MySQL, used by over 20,898 [sites] in 40 languages.
Q2A is highly customisable with many awesome features: Q2A is highly customisable with many awesome features:
...@@ -22,6 +22,7 @@ Q2A is highly customisable with many awesome features: ...@@ -22,6 +22,7 @@ Q2A is highly customisable with many awesome features:
- Private messages and public wall posts. - Private messages and public wall posts.
- Log in via Facebook or others (using plugins). - Log in via Facebook or others (using plugins).
- Out-of-the-box WordPress 3+ integration. - Out-of-the-box WordPress 3+ integration.
- Out-of-the-box Joomla! 3.0+ integration (in conjunction with a Joomla! extension).
- Custom single sign-on support for other sites. - Custom single sign-on support for other sites.
- PHP/MySQL scalable to millions of users and posts. - PHP/MySQL scalable to millions of users and posts.
- Safe from XSS, CSRF and SQL injection attacks. - Safe from XSS, CSRF and SQL injection attacks.
...@@ -58,3 +59,4 @@ Gideon ...@@ -58,3 +59,4 @@ Gideon
[Q2A]: http://www.question2answer.org/ [Q2A]: http://www.question2answer.org/
[1]: http://www.question2answer.org/qa/user/Scott [1]: http://www.question2answer.org/qa/user/Scott
[2]: https://github.com/q2a/question2answer/blob/master/CONTRIBUTING.md [2]: https://github.com/q2a/question2answer/blob/master/CONTRIBUTING.md
[sites]: http://www.question2answer.org/sites.php
1.7.4 1.8.0-beta1
\ No newline at end of file \ No newline at end of file
...@@ -22,11 +22,6 @@ ...@@ -22,11 +22,6 @@
// Set base path here so this works with symbolic links for multiple installations // Set base path here so this works with symbolic links for multiple installations
define('QA_BASE_DIR', dirname(empty($_SERVER['SCRIPT_FILENAME']) ? __FILE__ : $_SERVER['SCRIPT_FILENAME']).'/'); define('QA_BASE_DIR', dirname(empty($_SERVER['SCRIPT_FILENAME']) ? __FILE__ : $_SERVER['SCRIPT_FILENAME']) . '/');
require 'qa-include/qa-index.php'; require 'qa-include/qa-index.php';
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -86,6 +86,15 @@ ...@@ -86,6 +86,15 @@
*/ */
/* /*
If you wish to use caching, you must define QA_CACHE_DIRECTORY to store the cache files. The
directory must be writable by the web server. It also must be OUTSIDE the public root. For
example if your site resides in '/var/www/yoursite/public_html', then the cache directory could
be '/var/www/yoursite/qa-cache', but it cannot be '/var/www/yoursite/public_html/qa-cache'.
define('QA_CACHE_DIRECTORY', '/path/to/writable_cache_directory/');
*/
/*
If you wish, you can define QA_COOKIE_DOMAIN so that any cookies created by Q2A are assigned 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 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.
...@@ -99,7 +108,7 @@ ...@@ -99,7 +108,7 @@
and the value should be the replacement for that standard part, e.g. 'topics'. If you edit this 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. file in UTF-8 encoding you can also use non-ASCII characters in these URLs.
$QA_CONST_PATH_MAP=array( $QA_CONST_PATH_MAP = array(
'questions' => 'topics', 'questions' => 'topics',
'categories' => 'sections', 'categories' => 'sections',
'users' => 'contributors', 'users' => 'contributors',
...@@ -128,6 +137,17 @@ ...@@ -128,6 +137,17 @@
*/ */
/* /*
Out-of-the-box Joomla! 3.x integration - to integrate with your Joomla! site, define
QA_JOOMLA_INTEGRATE_PATH. as the full path to the Joomla! directory. If your Q2A
site is a subdirectory of your main Joomla site (recommended), you can specify
dirname(__DIR__) rather than the full path.
With this set, you do not need to set the QA_MYSQL_* constants above since these
will be taken from Joomla automatically. See online documentation for more details.
define('QA_JOOMLA_INTEGRATE_PATH', dirname(__DIR__));
*/
/*
Some settings to help optimize your Question2Answer site's performance. 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 If QA_HTML_COMPRESSION is true, HTML web pages will be output using Gzip compression, if
...@@ -147,13 +167,14 @@ ...@@ -147,13 +167,14 @@
are not indexed efficiently. For example, this will enable browsing unanswered questions per are not indexed efficiently. For example, this will enable browsing unanswered questions per
category. If your database becomes large, these queries could become costly. category. If your database becomes large, these queries could become costly.
Set QA_OPTIMIZE_LOCAL_DB to true if your web server and MySQL are running on the same box. Set QA_OPTIMIZE_DISTANT_DB to false 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 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. complex ones, which makes sense since there is no latency for localhost access.
Otherwise, set it 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_OPTIMIZE_DISTANT_DB to true if your web server and MySQL are far enough apart to The option QA_OPTIMIZE_LOCAL_DB is no longer used, since QA_OPTIMIZE_DISTANT_DB covers our uses.
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. Requires PHP 5.3. Set QA_PERSISTENT_CONN_DB to true to use persistent database connections. Requires PHP 5.3.
Only use this if you are absolutely sure it is a good idea under your setup - generally it is Only use this if you are absolutely sure it is a good idea under your setup - generally it is
...@@ -167,7 +188,6 @@ ...@@ -167,7 +188,6 @@
define('QA_MAX_LIMIT_START', 19999); define('QA_MAX_LIMIT_START', 19999);
define('QA_IGNORED_WORDS_FREQ', 10000); define('QA_IGNORED_WORDS_FREQ', 10000);
define('QA_ALLOW_UNINDEXED_QUERIES', false); define('QA_ALLOW_UNINDEXED_QUERIES', false);
define('QA_OPTIMIZE_LOCAL_DB', false);
define('QA_OPTIMIZE_DISTANT_DB', false); define('QA_OPTIMIZE_DISTANT_DB', false);
define('QA_PERSISTENT_CONN_DB', false); define('QA_PERSISTENT_CONN_DB', false);
define('QA_DEBUG_PERFORMANCE', false); define('QA_DEBUG_PERFORMANCE', false);
...@@ -176,8 +196,3 @@ ...@@ -176,8 +196,3 @@
And lastly... if you want to, you can predefine any constant from qa-db-maxima.php in this 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! file to override the default setting. Just make sure you know what you're doing!
*/ */
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
/* /*
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-content/qa-admin.js File: qa-content/qa-admin.js
Version: See define()s at top of qa-include/qa-base.php
Description: Javascript for admin pages to handle Ajax-triggered operations Description: Javascript for admin pages to handle Ajax-triggered operations
...@@ -22,14 +19,14 @@ ...@@ -22,14 +19,14 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
var qa_recalc_running=0; var qa_recalc_running = 0;
window.onbeforeunload=function(event) window.onbeforeunload = function(event)
{ {
if (qa_recalc_running>0) { if (qa_recalc_running > 0) {
event=event||window.event; event = event || window.event;
var message=qa_warning_recalc; var message = qa_warning_recalc;
event.returnValue=message; event.returnValue = message;
return message; return message;
} }
}; };
...@@ -37,17 +34,17 @@ window.onbeforeunload=function(event) ...@@ -37,17 +34,17 @@ window.onbeforeunload=function(event)
function qa_recalc_click(state, elem, value, noteid) function qa_recalc_click(state, elem, value, noteid)
{ {
if (elem.qa_recalc_running) { if (elem.qa_recalc_running) {
elem.qa_recalc_stopped=true; elem.qa_recalc_stopped = true;
} else { } else {
elem.qa_recalc_running=true; elem.qa_recalc_running = true;
elem.qa_recalc_stopped=false; elem.qa_recalc_stopped = false;
qa_recalc_running++; qa_recalc_running++;
document.getElementById(noteid).innerHTML=''; document.getElementById(noteid).innerHTML = '';
elem.qa_original_value=elem.value; elem.qa_original_value = elem.value;
if (value) if (value)
elem.value=value; elem.value = value;
qa_recalc_update(elem, state, noteid); qa_recalc_update(elem, state, noteid);
} }
...@@ -57,20 +54,23 @@ function qa_recalc_click(state, elem, value, noteid) ...@@ -57,20 +54,23 @@ function qa_recalc_click(state, elem, value, noteid)
function qa_recalc_update(elem, state, noteid) function qa_recalc_update(elem, state, noteid)
{ {
if (state) if (state) {
qa_ajax_post('recalc', {state:state, code:(elem.form.elements.code_recalc ? elem.form.elements.code_recalc.value : elem.form.elements.code.value)}, var recalcCode = elem.form.elements.code_recalc ? elem.form.elements.code_recalc.value : elem.form.elements.code.value;
qa_ajax_post(
'recalc',
{state: state, code: recalcCode},
function(lines) { function(lines) {
if (lines[0]=='1') { if (lines[0] == '1') {
if (lines[2]) if (lines[2])
document.getElementById(noteid).innerHTML=lines[2]; document.getElementById(noteid).innerHTML = lines[2];
if (elem.qa_recalc_stopped) if (elem.qa_recalc_stopped)
qa_recalc_cleanup(elem); qa_recalc_cleanup(elem);
else else
qa_recalc_update(elem, lines[1], noteid); qa_recalc_update(elem, lines[1], noteid);
} else if (lines[0]=='0') { } else if (lines[0] == '0') {
document.getElementById(noteid).innerHTML=lines[1]; document.getElementById(noteid).innerHTML = lines[1];
qa_recalc_cleanup(elem); qa_recalc_cleanup(elem);
} else { } else {
...@@ -79,29 +79,31 @@ function qa_recalc_update(elem, state, noteid) ...@@ -79,29 +79,31 @@ function qa_recalc_update(elem, state, noteid)
} }
} }
); );
} else {
else
qa_recalc_cleanup(elem); qa_recalc_cleanup(elem);
}
} }
function qa_recalc_cleanup(elem) function qa_recalc_cleanup(elem)
{ {
elem.value=elem.qa_original_value; elem.value = elem.qa_original_value;
elem.qa_recalc_running=null; elem.qa_recalc_running = null;
qa_recalc_running--; qa_recalc_running--;
} }
function qa_mailing_start(noteid, pauseid) function qa_mailing_start(noteid, pauseid)
{ {
qa_ajax_post('mailing', {}, qa_ajax_post('mailing', {},
function (lines) { function(lines) {
if (lines[0]=='1') { if (lines[0] == '1') {
document.getElementById(noteid).innerHTML=lines[1]; document.getElementById(noteid).innerHTML = lines[1];
window.setTimeout(function() { qa_mailing_start(noteid, pauseid); }, 1); // don't recurse window.setTimeout(function() {
qa_mailing_start(noteid, pauseid);
}, 1); // don't recurse
} else if (lines[0]=='0') { } else if (lines[0] == '0') {
document.getElementById(noteid).innerHTML=lines[1]; document.getElementById(noteid).innerHTML = lines[1];
document.getElementById(pauseid).style.display='none'; document.getElementById(pauseid).style.display = 'none';
} else { } else {
qa_ajax_error(); qa_ajax_error();
...@@ -112,16 +114,16 @@ function qa_mailing_start(noteid, pauseid) ...@@ -112,16 +114,16 @@ function qa_mailing_start(noteid, pauseid)
function qa_admin_click(target) function qa_admin_click(target)
{ {
var p=target.name.split('_'); var p = target.name.split('_');
var params={entityid:p[1], action:p[2]}; var params = {entityid: p[1], action: p[2]};
params.code=target.form.elements.code.value; params.code = target.form.elements.code.value;
qa_ajax_post('click_admin', params, qa_ajax_post('click_admin', params,
function (lines) { function(lines) {
if (lines[0]=='1') if (lines[0] == '1')
qa_conceal(document.getElementById('p'+p[1]), 'admin'); qa_conceal(document.getElementById('p' + p[1]), 'admin');
else if (lines[0]=='0') { else if (lines[0] == '0') {
alert(lines[1]); alert(lines[1]);
qa_hide_waiting(target); qa_hide_waiting(target);
} else } else
...@@ -134,14 +136,26 @@ function qa_admin_click(target) ...@@ -134,14 +136,26 @@ function qa_admin_click(target)
return false; return false;
} }
function qa_version_check(uri, version, elem) function qa_version_check(uri, version, elem, isCore)
{ {
var params={uri:uri, version:version}; var params = {uri: uri, version: version, isCore: isCore};
qa_ajax_post('version', params, qa_ajax_post('version', params,
function (lines) { function(lines) {
if (lines[0]=='1') if (lines[0] == '1')
document.getElementById(elem).innerHTML=lines[1]; document.getElementById(elem).innerHTML = lines[1];
} }
); );
} }
function qa_get_enabled_plugins_hashes()
{
var hashes = [];
$('[id^=plugin_enabled]:checked').each(
function(idx, elem) {
hashes.push(elem.id.replace("plugin_enabled_", ""));
}
);
$('[name=enabled_plugins_hashes]').val(hashes.join(';'));
}
/* /*
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-content/qa-page.js File: qa-content/qa-page.js
Version: See define()s at top of qa-include/qa-base.php Description: THIS FILE HAS BEEN DEPRECATED IN FAVOUR OF qa-global.js
Description: Common Javascript including voting, notices and favorites
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
...@@ -37,14 +34,14 @@ function qa_conceal(elem, type, callback) ...@@ -37,14 +34,14 @@ function qa_conceal(elem, type, callback)
function qa_set_inner_html(elem, type, html) function qa_set_inner_html(elem, type, html)
{ {
if (elem) if (elem)
elem.innerHTML=html; elem.innerHTML = html;
} }
function qa_set_outer_html(elem, type, html) function qa_set_outer_html(elem, type, html)
{ {
if (elem) { if (elem) {
var e=document.createElement('div'); var e = document.createElement('div');
e.innerHTML=html; e.innerHTML = html;
elem.parentNode.replaceChild(e.firstChild, elem); elem.parentNode.replaceChild(e.firstChild, elem);
} }
} }
...@@ -52,58 +49,58 @@ function qa_set_outer_html(elem, type, html) ...@@ -52,58 +49,58 @@ function qa_set_outer_html(elem, type, html)
function qa_show_waiting_after(elem, inside) function qa_show_waiting_after(elem, inside)
{ {
if (elem && !elem.qa_waiting_shown) { if (elem && !elem.qa_waiting_shown) {
var w=document.getElementById('qa-waiting-template'); var w = document.getElementById('qa-waiting-template');
if (w) { if (w) {
var c=w.cloneNode(true); var c = w.cloneNode(true);
c.id=null; c.id = null;
if (inside) if (inside)
elem.insertBefore(c, null); elem.insertBefore(c, null);
else else
elem.parentNode.insertBefore(c, elem.nextSibling); elem.parentNode.insertBefore(c, elem.nextSibling);
elem.qa_waiting_shown=c; elem.qa_waiting_shown = c;
} }
} }
} }
function qa_hide_waiting(elem) function qa_hide_waiting(elem)
{ {
var c=elem.qa_waiting_shown; var c = elem.qa_waiting_shown;
if (c) { if (c) {
c.parentNode.removeChild(c); c.parentNode.removeChild(c);
elem.qa_waiting_shown=null; elem.qa_waiting_shown = null;
} }
} }
function qa_vote_click(elem) function qa_vote_click(elem)
{ {
var ens=elem.name.split('_'); var ens = elem.name.split('_');
var postid=ens[1]; var postid = ens[1];
var vote=parseInt(ens[2]); var vote = parseInt(ens[2]);
var code=elem.form.elements.code.value; var code = elem.form.elements.code.value;
var anchor=ens[3]; var anchor = ens[3];
qa_ajax_post('vote', {postid:postid, vote:vote, code:code}, qa_ajax_post('vote', {postid: postid, vote: vote, code: code},
function(lines) { function(lines) {
if (lines[0]=='1') { if (lines[0] == '1') {
qa_set_inner_html(document.getElementById('voting_'+postid), 'voting', lines.slice(1).join("\n")); qa_set_inner_html(document.getElementById('voting_' + postid), 'voting', lines.slice(1).join("\n"));
} else if (lines[0]=='0') { } else if (lines[0] == '0') {
var mess=document.getElementById('errorbox'); var mess = document.getElementById('errorbox');
if (!mess) { if (!mess) {
var mess=document.createElement('div'); var mess = document.createElement('div');
mess.id='errorbox'; mess.id = 'errorbox';
mess.className='qa-error'; mess.className = 'qa-error';
mess.innerHTML=lines[1]; mess.innerHTML = lines[1];
mess.style.display='none'; mess.style.display = 'none';
} }
var postelem=document.getElementById(anchor); var postelem = document.getElementById(anchor);
var e=postelem.parentNode.insertBefore(mess, postelem); var e = postelem.parentNode.insertBefore(mess, postelem);
qa_reveal(e); qa_reveal(e);
} else } else
...@@ -116,14 +113,14 @@ function qa_vote_click(elem) ...@@ -116,14 +113,14 @@ function qa_vote_click(elem)
function qa_notice_click(elem) function qa_notice_click(elem)
{ {
var ens=elem.name.split('_'); var ens = elem.name.split('_');
var code=elem.form.elements.code.value; var code = elem.form.elements.code.value;
qa_ajax_post('notice', {noticeid:ens[1], code:code}, qa_ajax_post('notice', {noticeid: ens[1], code: code},
function(lines) { function(lines) {
if (lines[0]=='1') if (lines[0] == '1')
qa_conceal(document.getElementById('notice_'+ens[1]), 'notice'); qa_conceal(document.getElementById('notice_' + ens[1]), 'notice');
else if (lines[0]=='0') else if (lines[0] == '0')
alert(lines[1]); alert(lines[1]);
else else
qa_ajax_error(); qa_ajax_error();
...@@ -135,14 +132,14 @@ function qa_notice_click(elem) ...@@ -135,14 +132,14 @@ function qa_notice_click(elem)
function qa_favorite_click(elem) function qa_favorite_click(elem)
{ {
var ens=elem.name.split('_'); var ens = elem.name.split('_');
var code=elem.form.elements.code.value; var code = elem.form.elements.code.value;
qa_ajax_post('favorite', {entitytype:ens[1], entityid:ens[2], favorite:parseInt(ens[3]), code:code}, qa_ajax_post('favorite', {entitytype: ens[1], entityid: ens[2], favorite: parseInt(ens[3]), code: code},
function (lines) { function(lines) {
if (lines[0]=='1') if (lines[0] == '1')
qa_set_inner_html(document.getElementById('favoriting'), 'favoriting', lines.slice(1).join("\n")); qa_set_inner_html(document.getElementById('favoriting'), 'favoriting', lines.slice(1).join("\n"));
else if (lines[0]=='0') { else if (lines[0] == '0') {
alert(lines[1]); alert(lines[1]);
qa_hide_waiting(elem); qa_hide_waiting(elem);
} else } else
...@@ -157,21 +154,37 @@ function qa_favorite_click(elem) ...@@ -157,21 +154,37 @@ function qa_favorite_click(elem)
function qa_ajax_post(operation, params, callback) function qa_ajax_post(operation, params, callback)
{ {
jQuery.extend(params, {qa:'ajax', qa_operation:operation, qa_root:qa_root, qa_request:qa_request}); $.extend(params, {qa: 'ajax', qa_operation: operation, qa_root: qa_root, qa_request: qa_request});
jQuery.post(qa_root, params, function(response) { $.post(qa_root, params, function(response) {
var header='QA_AJAX_RESPONSE'; var header = 'QA_AJAX_RESPONSE';
var headerpos=response.indexOf(header); var headerpos = response.indexOf(header);
if (headerpos>=0) if (headerpos >= 0)
callback(response.substr(headerpos+header.length).replace(/^\s+/, '').split("\n")); callback(response.substr(headerpos + header.length).replace(/^\s+/, '').split("\n"));
else else
callback([]); callback([]);
}, 'text').fail(function(jqXHR) { if (jqXHR.readyState>0) callback([]) }); }, 'text').fail(function(jqXHR) {
if (jqXHR.readyState > 0)
callback([])
});
} }
function qa_ajax_error() function qa_ajax_error()
{ {
alert('Unexpected response from server - please try again or switch off Javascript.'); alert('Unexpected response from server - please try again or switch off Javascript.');
} }
function qa_display_rule_show(target, show, first)
{
var e = document.getElementById(target);
if (e) {
if (first || e.nodeName == 'SPAN')
e.style.display = (show ? '' : 'none');
else if (show)
$(e).fadeIn();
else
$(e).fadeOut();
}
}
/* /*
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-content/qa-question.js File: qa-content/qa-question.js
Version: See define()s at top of qa-include/qa-base.php Description: THIS FILE HAS BEEN DEPRECATED IN FAVOUR OF qa-global.js
Description: Javascript to handle question page actions
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
...@@ -22,18 +19,18 @@ ...@@ -22,18 +19,18 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
var qa_element_revealed=null; var qa_element_revealed = null;
function qa_toggle_element(elem) function qa_toggle_element(elem)
{ {
var e=elem ? document.getElementById(elem) : null; var e = elem ? document.getElementById(elem) : null;
if (e && e.qa_disabled) if (e && e.qa_disabled)
e=null; e = null;
if (e && (qa_element_revealed==e)) { if (e && (qa_element_revealed == e)) {
qa_conceal(qa_element_revealed, 'form'); qa_conceal(qa_element_revealed, 'form');
qa_element_revealed=null; qa_element_revealed = null;
} else { } else {
if (qa_element_revealed) if (qa_element_revealed)
...@@ -42,76 +39,74 @@ function qa_toggle_element(elem) ...@@ -42,76 +39,74 @@ function qa_toggle_element(elem)
if (e) { if (e) {
if (e.qa_load && !e.qa_loaded) { if (e.qa_load && !e.qa_loaded) {
e.qa_load(); e.qa_load();
e.qa_loaded=true; e.qa_loaded = true;
} }
if (e.qa_show) if (e.qa_show)
e.qa_show(); e.qa_show();
qa_reveal(e, 'form', function() { qa_reveal(e, 'form', function() {
var t=$(e).offset().top; var t = $(e).offset().top;
var h=$(e).height()+16; var h = $(e).height() + 16;
var wt=$(window).scrollTop(); var wt = $(window).scrollTop();
var wh=$(window).height(); var wh = $(window).height();
if ( (t<wt) || (t>(wt+wh)) ) if ((t < wt) || (t > (wt + wh)))
qa_scroll_page_to(t); qa_scroll_page_to(t);
else if ((t+h)>(wt+wh)) else if ((t + h) > (wt + wh))
qa_scroll_page_to(t+h-wh); qa_scroll_page_to(t + h - wh);
if (e.qa_focus) if (e.qa_focus)
e.qa_focus(); e.qa_focus();
}); });
} }
qa_element_revealed=e; qa_element_revealed = e;
} }
return !(e||!elem); // failed to find item return !(e || !elem); // failed to find item
} }
function qa_submit_answer(questionid, elem) function qa_submit_answer(questionid, elem)
{ {
var params=qa_form_params('a_form'); var params = qa_form_params('a_form');
params.a_questionid=questionid; params.a_questionid = questionid;
qa_ajax_post('answer', params, qa_ajax_post('answer', params,
function(lines) { function(lines) {
if (lines[0] == '1') {
if (lines[0]=='1') { if (lines[1] < 1) {
if (lines[1]<1) { var b = document.getElementById('q_doanswer');
var b=document.getElementById('q_doanswer');
if (b) if (b)
b.style.display='none'; b.style.display = 'none';
} }
var t=document.getElementById('a_list_title'); var t = document.getElementById('a_list_title');
qa_set_inner_html(t, 'a_list_title', lines[2]); qa_set_inner_html(t, 'a_list_title', lines[2]);
qa_reveal(t, 'a_list_title'); qa_reveal(t, 'a_list_title');
var e=document.createElement('div'); var e = document.createElement('div');
e.innerHTML=lines.slice(3).join("\n"); e.innerHTML = lines.slice(3).join("\n");
var c=e.firstChild; var c = e.firstChild;
c.style.display='none'; c.style.display = 'none';
var l=document.getElementById('a_list'); var l = document.getElementById('a_list');
l.insertBefore(c, l.firstChild); l.insertBefore(c, l.firstChild);
var a=document.getElementById('anew'); var a = document.getElementById('anew');
a.qa_disabled=true; a.qa_disabled = true;
qa_reveal(c, 'answer'); qa_reveal(c, 'answer');
qa_conceal(a, 'form'); qa_conceal(a, 'form');
} else if (lines[0]=='0') { } else if (lines[0] == '0') {
document.forms['a_form'].submit(); document.forms['a_form'].submit();
} else { } else {
qa_ajax_error(); qa_ajax_error();
} }
} }
); );
...@@ -122,32 +117,32 @@ function qa_submit_answer(questionid, elem) ...@@ -122,32 +117,32 @@ function qa_submit_answer(questionid, elem)
function qa_submit_comment(questionid, parentid, elem) function qa_submit_comment(questionid, parentid, elem)
{ {
var params=qa_form_params('c_form_'+parentid); var params = qa_form_params('c_form_' + parentid);
params.c_questionid=questionid; params.c_questionid = questionid;
params.c_parentid=parentid; params.c_parentid = parentid;
qa_ajax_post('comment', params, qa_ajax_post('comment', params,
function (lines) { function(lines) {
if (lines[0]=='1') { if (lines[0] == '1') {
var l=document.getElementById('c'+parentid+'_list'); var l = document.getElementById('c' + parentid + '_list');
l.innerHTML=lines.slice(2).join("\n"); l.innerHTML = lines.slice(2).join("\n");
l.style.display=''; l.style.display = '';
var a=document.getElementById('c'+parentid); var a = document.getElementById('c' + parentid);
a.qa_disabled=true; a.qa_disabled = true;
var c=document.getElementById(lines[1]); // id of comment var c = document.getElementById(lines[1]); // id of comment
if (c) { if (c) {
c.style.display='none'; c.style.display = 'none';
qa_reveal(c, 'comment'); qa_reveal(c, 'comment');
} }
qa_conceal(a, 'form'); qa_conceal(a, 'form');
} else if (lines[0]=='0') { } else if (lines[0] == '0') {
document.forms['c_form_'+parentid].submit(); document.forms['c_form_' + parentid].submit();
} else { } else {
qa_ajax_error(); qa_ajax_error();
...@@ -163,20 +158,20 @@ function qa_submit_comment(questionid, parentid, elem) ...@@ -163,20 +158,20 @@ function qa_submit_comment(questionid, parentid, elem)
function qa_answer_click(answerid, questionid, target) function qa_answer_click(answerid, questionid, target)
{ {
var params={}; var params = {};
params.answerid=answerid; params.answerid = answerid;
params.questionid=questionid; params.questionid = questionid;
params.code=target.form.elements.code.value; params.code = target.form.elements.code.value;
params[target.name]=target.value; params[target.name] = target.value;
qa_ajax_post('click_a', params, qa_ajax_post('click_a', params,
function (lines) { function(lines) {
if (lines[0]=='1') { if (lines[0] == '1') {
qa_set_inner_html(document.getElementById('a_list_title'), 'a_list_title', lines[1]); qa_set_inner_html(document.getElementById('a_list_title'), 'a_list_title', lines[1]);
var l=document.getElementById('a'+answerid); var l = document.getElementById('a' + answerid);
var h=lines.slice(2).join("\n"); var h = lines.slice(2).join("\n");
if (h.length) if (h.length)
qa_set_outer_html(l, 'answer', h); qa_set_outer_html(l, 'answer', h);
...@@ -184,7 +179,7 @@ function qa_answer_click(answerid, questionid, target) ...@@ -184,7 +179,7 @@ function qa_answer_click(answerid, questionid, target)
qa_conceal(l, 'answer'); qa_conceal(l, 'answer');
} else { } else {
target.form.elements.qa_click.value=target.name; target.form.elements.qa_click.value = target.name;
target.form.submit(); target.form.submit();
} }
} }
...@@ -197,27 +192,27 @@ function qa_answer_click(answerid, questionid, target) ...@@ -197,27 +192,27 @@ function qa_answer_click(answerid, questionid, target)
function qa_comment_click(commentid, questionid, parentid, target) function qa_comment_click(commentid, questionid, parentid, target)
{ {
var params={}; var params = {};
params.commentid=commentid; params.commentid = commentid;
params.questionid=questionid; params.questionid = questionid;
params.parentid=parentid; params.parentid = parentid;
params.code=target.form.elements.code.value; params.code = target.form.elements.code.value;
params[target.name]=target.value; params[target.name] = target.value;
qa_ajax_post('click_c', params, qa_ajax_post('click_c', params,
function (lines) { function(lines) {
if (lines[0]=='1') { if (lines[0] == '1') {
var l=document.getElementById('c'+commentid); var l = document.getElementById('c' + commentid);
var h=lines.slice(1).join("\n"); var h = lines.slice(1).join("\n");
if (h.length) if (h.length)
qa_set_outer_html(l, 'comment', h) qa_set_outer_html(l, 'comment', h);
else else
qa_conceal(l, 'comment'); qa_conceal(l, 'comment');
} else { } else {
target.form.elements.qa_click.value=target.name; target.form.elements.qa_click.value = target.name;
target.form.submit(); target.form.submit();
} }
} }
...@@ -230,17 +225,17 @@ function qa_comment_click(commentid, questionid, parentid, target) ...@@ -230,17 +225,17 @@ function qa_comment_click(commentid, questionid, parentid, target)
function qa_show_comments(questionid, parentid, elem) function qa_show_comments(questionid, parentid, elem)
{ {
var params={}; var params = {};
params.c_questionid=questionid; params.c_questionid = questionid;
params.c_parentid=parentid; params.c_parentid = parentid;
qa_ajax_post('show_cs', params, qa_ajax_post('show_cs', params,
function (lines) { function(lines) {
if (lines[0]=='1') { if (lines[0] == '1') {
var l=document.getElementById('c'+parentid+'_list'); var l = document.getElementById('c' + parentid + '_list');
l.innerHTML=lines.slice(1).join("\n"); l.innerHTML = lines.slice(1).join("\n");
l.style.display='none'; l.style.display = 'none';
qa_reveal(l, 'comments'); qa_reveal(l, 'comments');
} else { } else {
...@@ -256,15 +251,15 @@ function qa_show_comments(questionid, parentid, elem) ...@@ -256,15 +251,15 @@ function qa_show_comments(questionid, parentid, elem)
function qa_form_params(formname) function qa_form_params(formname)
{ {
var es=document.forms[formname].elements; var es = document.forms[formname].elements;
var params={}; var params = {};
for (var i=0; i<es.length; i++) { for (var i = 0; i < es.length; i++) {
var e=es[i]; var e = es[i];
var t=(e.type || '').toLowerCase(); var t = (e.type || '').toLowerCase();
if ( ((t!='checkbox') && (t!='radio')) || e.checked) if (((t != 'checkbox') && (t != 'radio')) || e.checked)
params[e.name]=e.value; params[e.name] = e.value;
} }
return params; return params;
......
/* /*
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-content/qa-user.js File: qa-content/qa-user.js
Version: See define()s at top of qa-include/qa-base.php Description: THIS FILE HAS BEEN DEPRECATED IN FAVOUR OF qa-global.js
Description: Javascript to handle user page actions
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
...@@ -24,32 +21,31 @@ ...@@ -24,32 +21,31 @@
function qa_submit_wall_post(elem, morelink) function qa_submit_wall_post(elem, morelink)
{ {
var params={}; var params = {};
params.message=document.forms.wallpost.message.value; params.message = document.forms.wallpost.message.value;
params.handle=document.forms.wallpost.handle.value; params.handle = document.forms.wallpost.handle.value;
params.start=document.forms.wallpost.start.value; params.start = document.forms.wallpost.start.value;
params.code=document.forms.wallpost.code.value; params.code = document.forms.wallpost.code.value;
params.morelink=morelink ? 1 : 0; params.morelink = morelink ? 1 : 0;
qa_ajax_post('wallpost', params, qa_ajax_post('wallpost', params,
function(lines) { function(lines) {
if (lines[0] == '1') {
var l = document.getElementById('wallmessages');
l.innerHTML = lines.slice(2).join("\n");
if (lines[0]=='1') { var c = document.getElementById(lines[1]); // id of new message
var l=document.getElementById('wallmessages');
l.innerHTML=lines.slice(2).join("\n");
var c=document.getElementById(lines[1]); // id of new message
if (c) { if (c) {
c.style.display='none'; c.style.display = 'none';
qa_reveal(c, 'wallpost'); qa_reveal(c, 'wallpost');
} }
document.forms.wallpost.message.value=''; document.forms.wallpost.message.value = '';
qa_hide_waiting(elem); qa_hide_waiting(elem);
} else if (lines[0]=='0') { } else if (lines[0] == '0') {
document.forms.wallpost.qa_click.value=elem.name; document.forms.wallpost.qa_click.value = elem.name;
document.forms.wallpost.submit(); document.forms.wallpost.submit();
} else { } else {
...@@ -63,31 +59,30 @@ function qa_submit_wall_post(elem, morelink) ...@@ -63,31 +59,30 @@ function qa_submit_wall_post(elem, morelink)
return false; return false;
} }
function qa_wall_post_click(messageid, target) function qa_wall_post_click(messageid, target)
{ {
var params={}; var params = {};
params.messageid=messageid; params.messageid = messageid;
params.handle=document.forms.wallpost.handle.value; params.handle = document.forms.wallpost.handle.value;
params.start=document.forms.wallpost.start.value; params.start = document.forms.wallpost.start.value;
params.code=document.forms.wallpost.code.value; params.code = document.forms.wallpost.code.value;
params[target.name]=target.value; params[target.name] = target.value;
qa_ajax_post('click_wall', params, qa_ajax_post('click_wall', params,
function (lines) { function(lines) {
if (lines[0]=='1') { if (lines[0] == '1') {
var l=document.getElementById('m'+messageid); var l = document.getElementById('m' + messageid);
var h=lines.slice(1).join("\n"); var h = lines.slice(1).join("\n");
if (h.length) if (h.length)
qa_set_outer_html(l, 'wallpost', h) qa_set_outer_html(l, 'wallpost', h);
else else
qa_conceal(l, 'wallpost'); qa_conceal(l, 'wallpost');
} else { } else {
document.forms.wallpost.qa_click.value=target.name; document.forms.wallpost.qa_click.value = target.name;
document.forms.wallpost.submit(); document.forms.wallpost.submit();
} }
} }
...@@ -98,7 +93,6 @@ function qa_wall_post_click(messageid, target) ...@@ -98,7 +93,6 @@ function qa_wall_post_click(messageid, target)
return false; return false;
} }
function qa_pm_click(messageid, target, box) function qa_pm_click(messageid, target, box)
{ {
var params = {}; var params = {};
...@@ -112,13 +106,13 @@ function qa_pm_click(messageid, target, box) ...@@ -112,13 +106,13 @@ function qa_pm_click(messageid, target, box)
params[target.name] = target.value; params[target.name] = target.value;
qa_ajax_post('click_pm', params, qa_ajax_post('click_pm', params,
function (lines) { function(lines) {
if (lines[0]=='1') { if (lines[0] == '1') {
var l = document.getElementById('m'+messageid); var l = document.getElementById('m' + messageid);
var h = lines.slice(1).join("\n"); var h = lines.slice(1).join("\n");
if (h.length) if (h.length)
qa_set_outer_html(l, 'pmessage', h) qa_set_outer_html(l, 'pmessage', h);
else else
qa_conceal(l, 'pmessage'); qa_conceal(l, 'pmessage');
......
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
File: qa-include/Q2A/Plugin/PluginManager.php
Description: Keeps track of the installed plugins
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
More about this license: http://www.question2answer.org/license.php
*/
class Q2A_Plugin_PluginManager
{
const PLUGIN_DELIMITER = ';';
const OPT_ENABLED_PLUGINS = 'enabled_plugins';
private $loadBeforeDbInit = array();
private $loadAfterDbInit = array();
public function readAllPluginMetadatas()
{
$pluginDirectories = $this->getFilesystemPlugins(true);
foreach ($pluginDirectories as $pluginDirectory) {
$pluginFile = $pluginDirectory . 'qa-plugin.php';
if (!file_exists($pluginFile)) {
continue;
}
$metadataUtil = new Q2A_Util_Metadata();
$metadata = $metadataUtil->fetchFromAddonPath($pluginDirectory);
if (empty($metadata)) {
// limit plugin parsing to first 8kB
$contents = file_get_contents($pluginFile, false, null, 0, 8192);
$metadata = qa_addon_metadata($contents, 'Plugin', true);
}
// skip plugin which requires a later version of Q2A
if (isset($metadata['min_q2a']) && qa_qa_version_below($metadata['min_q2a'])) {
continue;
}
// skip plugin which requires a later version of PHP
if (isset($metadata['min_php']) && qa_php_version_below($metadata['min_php'])) {
continue;
}
$pluginInfoKey = basename($pluginDirectory);
$pluginInfo = array(
'pluginfile' => $pluginFile,
'directory' => $pluginDirectory,
'urltoroot' => substr($pluginDirectory, strlen(QA_BASE_DIR)),
);
if (isset($metadata['load_order'])) {
switch ($metadata['load_order']) {
case 'after_db_init':
$this->loadAfterDbInit[$pluginInfoKey] = $pluginInfo;
break;
case 'before_db_init':
$this->loadBeforeDbInit[$pluginInfoKey] = $pluginInfo;
break;
default:
}
} else {
$this->loadBeforeDbInit[$pluginInfoKey] = $pluginInfo;
}
}
}
private function loadPlugins($pluginInfos)
{
global $qa_plugin_directory, $qa_plugin_urltoroot;
foreach ($pluginInfos as $pluginInfo) {
$qa_plugin_directory = $pluginInfo['directory'];
$qa_plugin_urltoroot = $pluginInfo['urltoroot'];
require_once $pluginInfo['pluginfile'];
}
$qa_plugin_directory = null;
$qa_plugin_urltoroot = null;
}
public function loadPluginsBeforeDbInit()
{
$this->loadPlugins($this->loadBeforeDbInit);
}
public function loadPluginsAfterDbInit()
{
$enabledPlugins = $this->getEnabledPlugins(false);
$enabledForAfterDbInit = array();
foreach ($enabledPlugins as $enabledPluginDirectory) {
if (isset($this->loadAfterDbInit[$enabledPluginDirectory])) {
$enabledForAfterDbInit[$enabledPluginDirectory] = $this->loadAfterDbInit[$enabledPluginDirectory];
}
}
$this->loadPlugins($enabledForAfterDbInit);
}
public function getEnabledPlugins($fullPath = false)
{
$pluginDirectories = $this->getEnabledPluginsOption();
if ($fullPath) {
foreach ($pluginDirectories as $key => &$pluginDirectory) {
$pluginDirectory = QA_PLUGIN_DIR . $pluginDirectory . '/';
}
}
return $pluginDirectories;
}
public function setEnabledPlugins($array)
{
$this->setEnabledPluginsOption($array);
}
public function getFilesystemPlugins($fullPath = false)
{
$result = array();
$fileSystemPluginFiles = glob(QA_PLUGIN_DIR . '*/qa-plugin.php');
foreach ($fileSystemPluginFiles as $pluginFile) {
$directory = dirname($pluginFile) . '/';
if (!$fullPath) {
$directory = basename($directory);
}
$result[] = $directory;
}
return $result;
}
public function getHashesForPlugins($pluginDirectories)
{
$result = array();
foreach ($pluginDirectories as $pluginDirectory) {
$result[$pluginDirectory] = md5($pluginDirectory);
}
return $result;
}
private function getEnabledPluginsOption()
{
return explode(self::PLUGIN_DELIMITER, qa_opt(self::OPT_ENABLED_PLUGINS));
}
private function setEnabledPluginsOption($array)
{
qa_opt(self::OPT_ENABLED_PLUGINS, implode(self::PLUGIN_DELIMITER, $array));
}
public function cleanRemovedPlugins()
{
$finalEnabledPlugins = array_intersect(
$this->getFilesystemPlugins(),
$this->getEnabledPlugins()
);
$this->setEnabledPluginsOption($finalEnabledPlugins);
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
File: qa-include/Q2A/Storage/FileCache.php
Description: Interface for drivers of caching system.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
More about this license: http://www.question2answer.org/license.php
*/
/**
* Interface for caching drivers.
*/
interface Q2A_Storage_CacheDriver
{
/**
* Get the cached data for the supplied key. Data can be any format but is usually an array.
* @param string $key The unique cache identifier.
*
* @return mixed The cached data, or null otherwise.
*/
public function get($key);
/**
* Store something in the cache along with the key and expiry time. Data gets 'serialized' to a string before storing.
* @param string $key The unique cache identifier.
* @param mixed $data The data to cache (in core Q2A this is usually an array).
* @param int $ttl Number of minutes for which to cache the data.
*
* @return bool Whether the file was successfully cached.
*/
public function set($key, $data, $ttl);
/**
* Delete an item from the cache.
* @param string $key The unique cache identifier.
*
* @return bool Whether the operation succeeded.
*/
public function delete($key);
/**
* Delete multiple items from the cache.
* @param int $limit Maximum number of items to process. 0 = unlimited
* @param int $start Offset from which to start (used for 'batching' deletes).
* @param bool $expiredOnly Delete cache only if it has expired.
*
* @return int Number of files deleted.
*/
public function clear($limit = 0, $start = 0, $expiredOnly = false);
/**
* Whether caching is available.
*
* @return bool
*/
public function isEnabled();
/**
* Get the last error.
*
* @return string
*/
public function getError();
/**
* Get current statistics for the cache.
*
* @return array Array of stats: 'files' => number of files, 'size' => total file size in bytes.
*/
public function getStats();
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
File: qa-include/Q2A/Storage/CacheManager.php
Description: Handler for caching system.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
More about this license: http://www.question2answer.org/license.php
*/
/**
* Caches data (typically from database queries) to the filesystem.
*/
class Q2A_Storage_CacheFactory
{
private static $cacheDriver = null;
/**
* Get the appropriate cache handler.
* @return Q2A_Storage_CacheInterface The cache handler.
*/
public static function getCacheDriver()
{
if (self::$cacheDriver === null) {
$config = array(
'enabled' => (int) qa_opt('caching_enabled') === 1,
'dir' => defined('QA_CACHE_DIRECTORY') ? QA_CACHE_DIRECTORY : null,
);
$driver = qa_opt('caching_driver');
switch($driver)
{
case 'memcached':
self::$cacheDriver = new Q2A_Storage_MemcachedDriver($config);
break;
case 'filesystem':
default:
self::$cacheDriver = new Q2A_Storage_FileCacheDriver($config);
break;
}
}
return self::$cacheDriver;
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
File: qa-include/Q2A/Storage/FileCacheDriver.php
Description: File-based driver for caching system.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
More about this license: http://www.question2answer.org/license.php
*/
/**
* Caches data (typically from database queries) to the filesystem.
*/
class Q2A_Storage_FileCacheDriver implements Q2A_Storage_CacheDriver
{
private $enabled = false;
private $error;
private $cacheDir;
/**
* Creates a new FileCache instance and checks we can write to the cache directory.
* @param array $config Configuration data, including cache storage directory.
*/
public function __construct($config)
{
if (!$config['enabled']) {
return;
}
if (isset($config['dir'])) {
$this->cacheDir = realpath($config['dir']);
if (!is_writable($this->cacheDir)) {
$this->error = qa_lang_html_sub('admin/caching_dir_error', $config['dir']);
} elseif (strpos($this->cacheDir, realpath($_SERVER['DOCUMENT_ROOT'])) === 0 || strpos($this->cacheDir, realpath(QA_BASE_DIR)) === 0) {
// check the folder is outside the public root - checks against server root and Q2A root, in order to handle symbolic links
$this->error = qa_lang_html_sub('admin/caching_dir_public', $config['dir']);
}
} else {
$this->error = qa_lang_html('admin/caching_dir_missing');
}
$this->enabled = empty($this->error);
}
/**
* Get the cached data for the supplied key.
* @param string $key The unique cache identifier.
*
* @return string The cached data, or null otherwise.
*/
public function get($key)
{
if (!$this->enabled) {
return null;
}
$file = $this->getFilename($key);
if (is_readable($file)) {
$lines = file($file, FILE_IGNORE_NEW_LINES);
$actualKey = array_shift($lines);
// double check this is the correct data
if ($key === $actualKey) {
$expiry = array_shift($lines);
if (is_numeric($expiry) && time() < $expiry) {
$encData = implode("\n", $lines);
// decode data, ignoring any notices
$data = @unserialize($encData);
if ($data !== false) {
return $data;
}
}
}
}
return null;
}
/**
* Store something in the cache along with the key and expiry time. Data gets 'serialized' to a string before storing.
* @param string $key The unique cache identifier.
* @param mixed $data The data to cache (in core Q2A this is usually an array).
* @param int $ttl Number of minutes for which to cache the data.
*
* @return bool Whether the file was successfully cached.
*/
public function set($key, $data, $ttl)
{
$success = false;
$ttl = (int) $ttl;
if ($this->enabled && $ttl > 0) {
$encData = serialize($data);
$expiry = time() + ($ttl * 60);
$cache = $key . "\n" . $expiry . "\n" . $encData;
$file = $this->getFilename($key);
$dir = dirname($file);
if (is_dir($dir) || mkdir($dir, 0777, true)) {
$success = @file_put_contents($file, $cache) !== false;
}
}
return $success;
}
/**
* Delete an item from the cache.
* @param string $key The unique cache identifier.
*
* @return bool Whether the operation succeeded.
*/
public function delete($key)
{
if ($this->enabled) {
$file = $this->getFilename($key);
$dir = dirname($key);
return $this->deleteFile($file);
}
return false;
}
/**
* Delete multiple items from the cache.
* @param int $limit Maximum number of items to process. 0 = unlimited
* @param int $start Offset from which to start (used for 'batching' deletes).
* @param bool $expiredOnly Delete cache only if it has expired.
*
* @return int Number of files deleted.
*/
public function clear($limit = 0, $start = 0, $expiredOnly = false)
{
$seek = $processed = $deleted = 0;
// fetch directories first to lower memory usage
$cacheDirs = glob($this->cacheDir . '/*/*', GLOB_ONLYDIR);
foreach ($cacheDirs as $dir) {
$cacheFiles = glob($dir . '/*');
foreach ($cacheFiles as $file) {
if ($seek < $start) {
$seek++;
continue;
}
$wasDeleted = false;
if ($expiredOnly) {
if (is_readable($file)) {
$fp = fopen($file, 'r');
$key = fgets($fp);
$expiry = (int) trim(fgets($fp));
if (is_numeric($expiry) && time() > $expiry) {
$wasDeleted = $this->deleteFile($file);
}
}
} else {
$wasDeleted = $this->deleteFile($file);
}
if ($wasDeleted) {
$deleted++;
}
$processed++;
if ($processed >= $limit) {
break 2;
}
}
}
// return how many files were deleted - caller can figure out how many to skip next time
return $deleted;
}
/**
* Whether caching is available.
*
* @return bool
*/
public function isEnabled()
{
return $this->enabled;
}
/**
* Get the last error.
*
* @return string
*/
public function getError()
{
return $this->error;
}
/**
* Get current statistics for the cache.
*
* @return array Array of stats: 'files' => number of files, 'size' => total file size in bytes.
*/
public function getStats()
{
if (!$this->enabled) {
return array('files' => 0, 'size' => 0);
}
$totalFiles = 0;
$totalBytes = 0;
$dirIter = new RecursiveDirectoryIterator($this->cacheDir);
foreach (new RecursiveIteratorIterator($dirIter) as $file) {
if (strpos($file->getFilename(), '.') === 0) {
// TODO: use FilesystemIterator::SKIP_DOTS once we're on minimum PHP 5.3
continue;
}
$totalFiles++;
$totalBytes += $file->getSize();
}
return array(
'files' => $totalFiles,
'size' => $totalBytes,
);
}
/**
* Delete a specific file
* @param string $file Filename to delete.
*
* @return bool Whether the file was deleted successfully.
*/
private function deleteFile($file)
{
if (is_writable($file)) {
return @unlink($file) === true;
}
return false;
}
/**
* Generates filename for cache key, of the form `1/23/123abc`
* @param string $key The unique cache key.
*
* @return string
*/
private function getFilename($key)
{
$filename = sha1($key);
return $this->cacheDir . '/' . substr($filename, 0, 1) . '/' . substr($filename, 1, 2) . '/' . $filename;
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
File: qa-include/Q2A/Storage/MemcachedDriver.php
Description: Memcached-based driver for caching system.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
More about this license: http://www.question2answer.org/license.php
*/
/**
* Caches data (typically from database queries) in memory using Memcached.
*/
class Q2A_Storage_MemcachedDriver implements Q2A_Storage_CacheDriver
{
private $memcached;
private $enabled = false;
private $error;
private $flushed = false;
const HOST = '127.0.0.1';
const PORT = 11211;
/**
* Creates a new Memcached instance and checks we can cache items.
* @param array $config Configuration data, including cache storage directory.
*
* @return void
*/
public function __construct($config)
{
if (!$config['enabled']) {
return;
}
if (extension_loaded('memcached')) {
$this->memcached = new Memcached;
$this->memcached->addServer(self::HOST, self::PORT);
if ($this->memcached->set('q2a.test', 'TEST')) {
$this->enabled = true;
} else {
$this->setMemcachedError();
}
} else {
$this->error = 'The Memcached PHP extension is not installed';
}
}
/**
* Get the cached data for the supplied key. Data can be any format but is usually an array.
* @param string $key The unique cache identifier.
*
* @return mixed The cached data, or null otherwise.
*/
public function get($key)
{
if (!$this->enabled) {
return null;
}
$result = $this->memcached->get($key);
if ($result === false) {
$this->setMemcachedError();
return null;
}
return $result;
}
/**
* Store something in the cache along with the key and expiry time. Data gets 'serialized' to a string before storing.
* @param string $key The unique cache identifier.
* @param mixed $data The data to cache (in core Q2A this is usually an array).
* @param int $ttl Number of minutes for which to cache the data.
*
* @return bool Whether the file was successfully cached.
*/
public function set($key, $data, $ttl)
{
if (!$this->enabled) {
return false;
}
$ttl = (int) $ttl;
$expiry = time() + ($ttl * 60);
$success = $this->memcached->set($key, $data, $expiry);
if (!$success) {
$this->setMemcachedError();
}
return $success;
}
/**
* Delete an item from the cache.
* @param string $key The unique cache identifier.
*
* @return bool Whether the operation succeeded.
*/
public function delete($key)
{
if (!$this->enabled) {
return false;
}
$success = $this->memcached->delete($key);
if (!$success) {
$this->setMemcachedError();
}
return $success;
}
/**
* Delete multiple items from the cache.
* @param int $limit Maximum number of items to process. 0 = unlimited
* @param int $start Offset from which to start (used for 'batching' deletes).
* @param bool $expiredOnly This parameter is ignored because Memcached automatically clears expired items.
*
* @return int Number of files deleted. For Memcached we return 0
*/
public function clear($limit = 0, $start = 0, $expiredOnly = false)
{
if ($this->enabled && !$expiredOnly && !$this->flushed) {
$success = $this->memcached->flush();
// avoid multiple calls to flush()
$this->flushed = true;
if (!$success) {
$this->setMemcachedError();
}
}
return 0;
}
/**
* Whether caching is available.
*
* @return bool
*/
public function isEnabled()
{
return $this->enabled;
}
/**
* Get the last error.
*
* @return string
*/
public function getError()
{
return $this->error;
}
/**
* Get current statistics for the cache.
*
* @return array Array of stats: 'files' => number of files, 'size' => total file size in bytes.
*/
public function getStats()
{
$totalFiles = 0;
$totalBytes = 0;
if ($this->enabled) {
$stats = $this->memcached->getStats();
$key = self::HOST . ':' . self::PORT;
$totalFiles = isset($stats[$key]['curr_items']) ? $stats[$key]['curr_items'] : 0;
$totalBytes = isset($stats[$key]['bytes']) ? $stats[$key]['bytes'] : 0;
}
return array(
'files' => $totalFiles,
'size' => $totalBytes,
);
}
/**
* Set current error to Memcached result message
*
* @return void
*/
private function setMemcachedError()
{
$this->error = 'Memcached error: ' . $this->memcached->getResultMessage();
}
}
...@@ -45,7 +45,7 @@ class Q2A_Util_Metadata ...@@ -45,7 +45,7 @@ class Q2A_Util_Metadata
* @param string $url URL linking to a metadata.json file * @param string $url URL linking to a metadata.json file
* @return array The metadata fetched from the file * @return array The metadata fetched from the file
*/ */
public function fetchFromUrl($url, $type='Plugin') public function fetchFromUrl($url, $type = 'Plugin')
{ {
$contents = qa_retrieve_url($url); $contents = qa_retrieve_url($url);
$metadata = $this->getArrayFromJson($contents); $metadata = $this->getArrayFromJson($contents);
......
...@@ -68,6 +68,7 @@ class Q2A_Util_Usage ...@@ -68,6 +68,7 @@ class Q2A_Util_Usage
/** /**
* Mark the beginning of a new stage of script execution and store usages accordingly. * Mark the beginning of a new stage of script execution and store usages accordingly.
* @param $stage
*/ */
public function mark($stage) public function mark($stage)
{ {
...@@ -78,6 +79,10 @@ class Q2A_Util_Usage ...@@ -78,6 +79,10 @@ class Q2A_Util_Usage
/** /**
* Logs query and updates database usage stats. * Logs query and updates database usage stats.
* @param $query
* @param $usedtime
* @param $gotrows
* @param $gotcolumns
*/ */
public function logDatabaseQuery($query, $usedtime, $gotrows, $gotcolumns) public function logDatabaseQuery($query, $usedtime, $gotrows, $gotcolumns)
{ {
...@@ -102,6 +107,8 @@ class Q2A_Util_Usage ...@@ -102,6 +107,8 @@ class Q2A_Util_Usage
public function output() public function output()
{ {
$totaldelta = $this->delta($this->startUsage, $this->getCurrent()); $totaldelta = $this->delta($this->startUsage, $this->getCurrent());
$stages = $this->stages;
$stages['total'] = $totaldelta;
?> ?>
<style> <style>
.debug-table { border-collapse: collapse; width: auto; margin: 20px auto; } .debug-table { border-collapse: collapse; width: auto; margin: 20px auto; }
...@@ -148,13 +155,7 @@ class Q2A_Util_Usage ...@@ -148,13 +155,7 @@ class Q2A_Util_Usage
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach ($stages as $stage => $stagedelta) : ?>
<?php
$stages = $this->stages;
$stages['total'] = $totaldelta;
foreach ($stages as $stage => $stagedelta):
?>
<tr> <tr>
<td class="row-heading"><?php echo ucfirst($stage); ?></td> <td class="row-heading"><?php echo ucfirst($stage); ?></td>
<td><?php echo sprintf('%.1f', $stagedelta['clock'] * 1000); ?></td> <td><?php echo sprintf('%.1f', $stagedelta['clock'] * 1000); ?></td>
...@@ -170,9 +171,7 @@ class Q2A_Util_Usage ...@@ -170,9 +171,7 @@ class Q2A_Util_Usage
<td><?php echo sprintf('%dk', $stagedelta['ram'] / 1024); ?></td> <td><?php echo sprintf('%dk', $stagedelta['ram'] / 1024); ?></td>
<td><?php echo sprintf('%d%%', $stagedelta['ram'] ? ($stagedelta['ram'] * 100 / $totaldelta['ram']) : 0); ?></td> <td><?php echo sprintf('%d%%', $stagedelta['ram'] ? ($stagedelta['ram'] * 100 / $totaldelta['ram']) : 0); ?></td>
</tr> </tr>
<?php <?php endforeach; ?>
endforeach;
?>
</tbody> </tbody>
</table> </table>
...@@ -197,6 +196,9 @@ class Q2A_Util_Usage ...@@ -197,6 +196,9 @@ class Q2A_Util_Usage
/** /**
* Return the difference between two resource usage arrays, as an array. * Return the difference between two resource usage arrays, as an array.
* @param $oldusage
* @param $newusage
* @return array
*/ */
private function delta($oldusage, $newusage) private function delta($oldusage, $newusage)
{ {
...@@ -207,5 +209,4 @@ class Q2A_Util_Usage ...@@ -207,5 +209,4 @@ class Q2A_Util_Usage
return $delta; return $delta;
} }
} }
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-ajax-answer.php
Description: Server-side response to Ajax create answer requests Description: Server-side response to Ajax create answer requests
...@@ -20,52 +19,52 @@ ...@@ -20,52 +19,52 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
require_once QA_INCLUDE_DIR.'app/users.php'; require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR.'app/limits.php'; require_once QA_INCLUDE_DIR . 'app/limits.php';
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
// Load relevant information about this question // Load relevant information about this question
$questionid=qa_post_text('a_questionid'); $questionid = qa_post_text('a_questionid');
$userid=qa_get_logged_in_userid(); $userid = qa_get_logged_in_userid();
list($question, $childposts)=qa_db_select_with_pending( list($question, $childposts) = qa_db_select_with_pending(
qa_db_full_post_selectspec($userid, $questionid), qa_db_full_post_selectspec($userid, $questionid),
qa_db_full_child_posts_selectspec($userid, $questionid) qa_db_full_child_posts_selectspec($userid, $questionid)
); );
// Check if the question exists, is not closed, and whether the user has permission to do this // Check if the question exists, is not closed, and whether the user has permission to do this
if ((@$question['basetype']=='Q') && (!isset($question['closedbyid'])) && !qa_user_post_permit_error('permit_post_a', $question, QA_LIMIT_ANSWERS)) { if (@$question['basetype'] == 'Q' && !isset($question['closedbyid']) && !qa_user_post_permit_error('permit_post_a', $question, QA_LIMIT_ANSWERS)) {
require_once QA_INCLUDE_DIR.'app/captcha.php'; require_once QA_INCLUDE_DIR . 'app/captcha.php';
require_once QA_INCLUDE_DIR.'app/format.php'; require_once QA_INCLUDE_DIR . 'app/format.php';
require_once QA_INCLUDE_DIR.'app/post-create.php'; require_once QA_INCLUDE_DIR . 'app/post-create.php';
require_once QA_INCLUDE_DIR.'app/cookies.php'; require_once QA_INCLUDE_DIR . 'app/cookies.php';
require_once QA_INCLUDE_DIR.'pages/question-view.php'; require_once QA_INCLUDE_DIR . 'pages/question-view.php';
require_once QA_INCLUDE_DIR.'pages/question-submit.php'; require_once QA_INCLUDE_DIR . 'pages/question-submit.php';
// Try to create the new answer // Try to create the new answer
$usecaptcha=qa_user_use_captcha(qa_user_level_for_post($question)); $usecaptcha = qa_user_use_captcha(qa_user_level_for_post($question));
$answers=qa_page_q_load_as($question, $childposts); $answers = qa_page_q_load_as($question, $childposts);
$answerid=qa_page_q_add_a_submit($question, $answers, $usecaptcha, $in, $errors); $answerid = qa_page_q_add_a_submit($question, $answers, $usecaptcha, $in, $errors);
// If successful, page content will be updated via Ajax // If successful, page content will be updated via Ajax
if (isset($answerid)) { if (isset($answerid)) {
$answer=qa_db_select_with_pending(qa_db_full_post_selectspec($userid, $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 $question = $question + qa_page_q_post_rules($question, null, null, $childposts); // array union
$answer=$answer+qa_page_q_post_rules($answer, $question, $answers, null); $answer = $answer + qa_page_q_post_rules($answer, $question, $answers, null);
$usershtml=qa_userids_handles_html(array($answer), true); $usershtml = qa_userids_handles_html(array($answer), true);
$a_view=qa_page_q_answer_view($question, $answer, false, $usershtml, false); $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); $themeclass = qa_load_theme_class(qa_get_site_theme(), 'ajax-answer', null, null);
$themeclass->initialize(); $themeclass->initialize();
echo "QA_AJAX_RESPONSE\n1\n"; echo "QA_AJAX_RESPONSE\n1\n";
...@@ -73,17 +72,18 @@ ...@@ -73,17 +72,18 @@
// Send back whether the 'answer' button should still be visible // Send back whether the 'answer' button should still be visible
echo (int)qa_opt('allow_multi_answers')."\n"; echo (int)qa_opt('allow_multi_answers') . "\n";
// Send back the count of answers // Send back the count of answers
$countanswers=$question['acount']+1; $countanswers = $question['acount'] + 1;
if ($countanswers==1) if ($countanswers == 1) {
echo qa_lang_html('question/1_answer_title')."\n"; echo qa_lang_html('question/1_answer_title') . "\n";
else } else {
echo qa_lang_html_sub('question/x_answers_title', $countanswers)."\n"; echo qa_lang_html_sub('question/x_answers_title', $countanswers) . "\n";
}
// Send back the HTML // Send back the HTML
...@@ -92,12 +92,7 @@ ...@@ -92,12 +92,7 @@
return; return;
} }
} }
echo "QA_AJAX_RESPONSE\n0\n"; // fall back to non-Ajax submission if there were any problems
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
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-ajax-asktitle.php
Description: Server-side response to Ajax request based on ask a question title Description: Server-side response to Ajax request based on ask a question title
...@@ -20,36 +19,37 @@ ...@@ -20,36 +19,37 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
require_once QA_INCLUDE_DIR.'util/string.php'; require_once QA_INCLUDE_DIR . 'util/string.php';
require_once QA_INCLUDE_DIR.'app/users.php'; require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR.'app/format.php'; require_once QA_INCLUDE_DIR . 'app/format.php';
// Collect the information we need from the database // Collect the information we need from the database
$intitle = qa_post_text('title'); $intitle = qa_post_text('title');
$doaskcheck = qa_opt('do_ask_check_qs'); $doaskcheck = qa_opt('do_ask_check_qs');
$doexampletags = qa_using_tags() && qa_opt('do_example_tags'); $doexampletags = qa_using_tags() && qa_opt('do_example_tags');
if ($doaskcheck || $doexampletags) { if ($doaskcheck || $doexampletags) {
$countqs = max($doexampletags ? QA_DB_RETRIEVE_ASK_TAG_QS : 0, $doaskcheck ? qa_opt('page_size_ask_check_qs') : 0); $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( $relatedquestions = qa_db_select_with_pending(
qa_db_search_posts_selectspec(null, qa_string_to_words($intitle), null, null, null, null, 0, false, $countqs) qa_db_search_posts_selectspec(null, qa_string_to_words($intitle), null, null, null, null, 0, false, $countqs)
); );
} }
// Collect example tags if appropriate // Collect example tags if appropriate
if ($doexampletags) { if ($doexampletags) {
$tagweight = array(); $tagweight = array();
foreach ($relatedquestions as $question) { foreach ($relatedquestions as $question) {
$tags = qa_tagstring_to_tags($question['tags']); $tags = qa_tagstring_to_tags($question['tags']);
foreach ($tags as $tag) foreach ($tags as $tag) {
@$tagweight[$tag] += exp($question['score']); @$tagweight[$tag] += exp($question['score']);
} }
}
arsort($tagweight, SORT_NUMERIC); arsort($tagweight, SORT_NUMERIC);
...@@ -66,21 +66,21 @@ ...@@ -66,21 +66,21 @@
if (count($exampletags) >= $maxcount) if (count($exampletags) >= $maxcount)
break; break;
} }
} } else {
else
$exampletags = array(); $exampletags = array();
}
// Output the response header and example tags // Output the response header and example tags
echo "QA_AJAX_RESPONSE\n1\n"; echo "QA_AJAX_RESPONSE\n1\n";
echo strtr(qa_html(implode(',', $exampletags)), "\r\n", ' ') . "\n"; echo strtr(qa_html(implode(',', $exampletags)), "\r\n", ' ') . "\n";
// Collect and output the list of related questions // Collect and output the list of related questions
if ($doaskcheck) { if ($doaskcheck) {
$minscore = qa_match_to_min_score(qa_opt('match_ask_check_qs')); $minscore = qa_match_to_min_score(qa_opt('match_ask_check_qs'));
$maxcount = qa_opt('page_size_ask_check_qs'); $maxcount = qa_opt('page_size_ask_check_qs');
...@@ -97,9 +97,4 @@ ...@@ -97,9 +97,4 @@
$themeclass = qa_load_theme_class(qa_get_site_theme(), 'ajax-asktitle', null, null); $themeclass = qa_load_theme_class(qa_get_site_theme(), 'ajax-asktitle', null, null);
$themeclass->initialize(); $themeclass->initialize();
$themeclass->q_ask_similar($limitedquestions, qa_lang_html('question/ask_same_q')); $themeclass->q_ask_similar($limitedquestions, qa_lang_html('question/ask_same_q'));
} }
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-ajax-category.php
Description: Server-side response to Ajax category information requests Description: Server-side response to Ajax category information requests
...@@ -20,26 +19,23 @@ ...@@ -20,26 +19,23 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
$categoryid=qa_post_text('categoryid'); $categoryid = qa_post_text('categoryid');
if (!strlen($categoryid)) if (!strlen($categoryid))
$categoryid=null; $categoryid = null;
list($fullcategory, $categories)=qa_db_select_with_pending( list($fullcategory, $categories) = qa_db_select_with_pending(
qa_db_full_category_selectspec($categoryid, true), qa_db_full_category_selectspec($categoryid, true),
qa_db_category_sub_selectspec($categoryid) qa_db_category_sub_selectspec($categoryid)
); );
echo "QA_AJAX_RESPONSE\n1\n"; echo "QA_AJAX_RESPONSE\n1\n";
echo qa_html(strtr(@$fullcategory['content'], "\r\n", ' ')); // category description echo qa_html(strtr(@$fullcategory['content'], "\r\n", ' ')); // category description
foreach ($categories as $category) foreach ($categories as $category) {
echo "\n".$category['categoryid'].'/'.$category['title']; // subcategory information // subcategory information
echo "\n" . $category['categoryid'] . '/' . $category['title'];
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-ajax-click-admin.php
Description: Server-side response to Ajax single clicks on posts in admin section Description: Server-side response to Ajax single clicks on posts in admin section
...@@ -20,22 +19,17 @@ ...@@ -20,22 +19,17 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
require_once QA_INCLUDE_DIR.'app/admin.php'; require_once QA_INCLUDE_DIR . 'app/admin.php';
require_once QA_INCLUDE_DIR.'app/users.php'; require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR.'app/cookies.php'; require_once QA_INCLUDE_DIR . 'app/cookies.php';
$entityid=qa_post_text('entityid'); $entityid = qa_post_text('entityid');
$action=qa_post_text('action'); $action = qa_post_text('action');
if (!qa_check_form_security_code('admin/click', qa_post_text('code'))) if (!qa_check_form_security_code('admin/click', qa_post_text('code')))
echo "QA_AJAX_RESPONSE\n0\n".qa_lang('misc/form_security_reload'); echo "QA_AJAX_RESPONSE\n0\n" . qa_lang('misc/form_security_reload');
elseif (qa_admin_single_click($entityid, $action)) // permission check happens in here elseif (qa_admin_single_click($entityid, $action)) // permission check happens in here
echo "QA_AJAX_RESPONSE\n1\n"; echo "QA_AJAX_RESPONSE\n1\n";
else else
echo "QA_AJAX_RESPONSE\n0\n".qa_lang('main/general_error'); 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
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-ajax-click-answer.php
Description: Server-side response to Ajax single clicks on answer Description: Server-side response to Ajax single clicks on answer
...@@ -20,42 +19,40 @@ ...@@ -20,42 +19,40 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
require_once QA_INCLUDE_DIR.'app/cookies.php'; require_once QA_INCLUDE_DIR . 'app/cookies.php';
require_once QA_INCLUDE_DIR.'app/format.php'; require_once QA_INCLUDE_DIR . 'app/format.php';
require_once QA_INCLUDE_DIR.'app/users.php'; require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
require_once QA_INCLUDE_DIR.'pages/question-view.php'; require_once QA_INCLUDE_DIR . 'pages/question-view.php';
require_once QA_INCLUDE_DIR.'pages/question-submit.php'; require_once QA_INCLUDE_DIR . 'pages/question-submit.php';
require_once QA_INCLUDE_DIR . 'util/sort.php';
// Load relevant information about this answer // Load relevant information about this answer
$answerid=qa_post_text('answerid'); $answerid = qa_post_text('answerid');
$questionid=qa_post_text('questionid'); $questionid = qa_post_text('questionid');
$userid=qa_get_logged_in_userid(); $userid = qa_get_logged_in_userid();
list($answer, $question, $qchildposts, $achildposts)=qa_db_select_with_pending( list($answer, $question, $qchildposts, $achildposts) = qa_db_select_with_pending(
qa_db_full_post_selectspec($userid, $answerid), qa_db_full_post_selectspec($userid, $answerid),
qa_db_full_post_selectspec($userid, $questionid), qa_db_full_post_selectspec($userid, $questionid),
qa_db_full_child_posts_selectspec($userid, $questionid), qa_db_full_child_posts_selectspec($userid, $questionid),
qa_db_full_child_posts_selectspec($userid, $answerid) qa_db_full_child_posts_selectspec($userid, $answerid)
); );
// Check if there was an operation that succeeded // Check if there was an operation that succeeded
if ( if (@$answer['basetype'] == 'A' && @$question['basetype'] == 'Q') {
(@$answer['basetype']=='A') && $answers = qa_page_q_load_as($question, $qchildposts);
(@$question['basetype']=='Q')
) {
$answers=qa_page_q_load_as($question, $qchildposts);
$question=$question+qa_page_q_post_rules($question, null, null, $qchildposts); // array union $question = $question + qa_page_q_post_rules($question, null, null, $qchildposts); // array union
$answer=$answer+qa_page_q_post_rules($answer, $question, $qchildposts, $achildposts); $answer = $answer + qa_page_q_post_rules($answer, $question, $qchildposts, $achildposts);
if (qa_page_q_single_click_a($answer, $question, $answers, $achildposts, false, $error)) { if (qa_page_q_single_click_a($answer, $question, $answers, $achildposts, false, $error)) {
list($answer, $question)=qa_db_select_with_pending( list($answer, $question) = qa_db_select_with_pending(
qa_db_full_post_selectspec($userid, $answerid), qa_db_full_post_selectspec($userid, $answerid),
qa_db_full_post_selectspec($userid, $questionid) qa_db_full_post_selectspec($userid, $questionid)
); );
...@@ -68,9 +65,9 @@ ...@@ -68,9 +65,9 @@
// Send back new count of answers // Send back new count of answers
$countanswers=$question['acount']; $countanswers = $question['acount'];
if ($countanswers==1) if ($countanswers == 1)
echo qa_lang_html('question/1_answer_title'); echo qa_lang_html('question/1_answer_title');
else else
echo qa_lang_html_sub('question/x_answers_title', $countanswers); echo qa_lang_html_sub('question/x_answers_title', $countanswers);
...@@ -79,22 +76,24 @@ ...@@ -79,22 +76,24 @@
// If the answer was not deleted.... // If the answer was not deleted....
if (isset($answer)) { if (isset($answer)) {
$question=$question+qa_page_q_post_rules($question, null, null, $qchildposts); // array union $question = $question + qa_page_q_post_rules($question, null, null, $qchildposts); // array union
$answer=$answer+qa_page_q_post_rules($answer, $question, $qchildposts, $achildposts); $answer = $answer + qa_page_q_post_rules($answer, $question, $qchildposts, $achildposts);
$commentsfollows=qa_page_q_load_c_follows($question, $qchildposts, $achildposts); $commentsfollows = qa_page_q_load_c_follows($question, $qchildposts, $achildposts);
foreach ($commentsfollows as $key => $commentfollow) foreach ($commentsfollows as $key => $commentfollow) {
$commentsfollows[$key]=$commentfollow+qa_page_q_post_rules($commentfollow, $answer, $commentsfollows, null); $commentsfollows[$key] = $commentfollow + qa_page_q_post_rules($commentfollow, $answer, $commentsfollows, null);
}
$usershtml=qa_userids_handles_html(array_merge(array($answer), $commentsfollows), true); $usershtml = qa_userids_handles_html(array_merge(array($answer), $commentsfollows), true);
qa_sort_by($commentsfollows, 'created');
$a_view=qa_page_q_answer_view($question, $answer, ($answer['postid']==$question['selchildid']) && ($answer['type']=='A'), $a_view = qa_page_q_answer_view($question, $answer, ($answer['postid'] == $question['selchildid'] && $answer['type'] == 'A'),
$usershtml, false); $usershtml, false);
$a_view['c_list']=qa_page_q_comment_follow_list($question, $answer, $commentsfollows, false, $usershtml, false, null); $a_view['c_list'] = qa_page_q_comment_follow_list($question, $answer, $commentsfollows, false, $usershtml, false, null);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-answer', null, null); $themeclass = qa_load_theme_class(qa_get_site_theme(), 'ajax-answer', null, null);
$themeclass->initialize(); $themeclass->initialize();
...@@ -107,12 +106,7 @@ ...@@ -107,12 +106,7 @@
return; return;
} }
} }
echo "QA_AJAX_RESPONSE\n0\n"; // fall back to non-Ajax submission if something failed
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
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-ajax-click-comment.php
Description: Server-side response to Ajax single clicks on comments Description: Server-side response to Ajax single clicks on comments
...@@ -20,60 +19,56 @@ ...@@ -20,60 +19,56 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
require_once QA_INCLUDE_DIR.'app/cookies.php'; require_once QA_INCLUDE_DIR . 'app/cookies.php';
require_once QA_INCLUDE_DIR.'app/format.php'; require_once QA_INCLUDE_DIR . 'app/format.php';
require_once QA_INCLUDE_DIR.'app/users.php'; require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
require_once QA_INCLUDE_DIR.'pages/question-view.php'; require_once QA_INCLUDE_DIR . 'pages/question-view.php';
require_once QA_INCLUDE_DIR.'pages/question-submit.php'; require_once QA_INCLUDE_DIR . 'pages/question-submit.php';
// Load relevant information about this comment // Load relevant information about this comment
$commentid=qa_post_text('commentid'); $commentid = qa_post_text('commentid');
$questionid=qa_post_text('questionid'); $questionid = qa_post_text('questionid');
$parentid=qa_post_text('parentid'); $parentid = qa_post_text('parentid');
$userid=qa_get_logged_in_userid(); $userid = qa_get_logged_in_userid();
list($comment, $question, $parent, $children)=qa_db_select_with_pending( list($comment, $question, $parent, $children) = qa_db_select_with_pending(
qa_db_full_post_selectspec($userid, $commentid), qa_db_full_post_selectspec($userid, $commentid),
qa_db_full_post_selectspec($userid, $questionid), qa_db_full_post_selectspec($userid, $questionid),
qa_db_full_post_selectspec($userid, $parentid), qa_db_full_post_selectspec($userid, $parentid),
qa_db_full_child_posts_selectspec($userid, $parentid) qa_db_full_child_posts_selectspec($userid, $parentid)
); );
// Check if there was an operation that succeeded // Check if there was an operation that succeeded
if ( if (@$comment['basetype'] == 'C' && @$question['basetype'] == 'Q' &&
(@$comment['basetype']=='C') && (@$parent['basetype'] == 'Q' || @$parent['basetype'] == 'A')
(@$question['basetype']=='Q') && ) {
((@$parent['basetype']=='Q') || (@$parent['basetype']=='A')) $comment = $comment + qa_page_q_post_rules($comment, $parent, $children, null); // array union
) {
$comment=$comment+qa_page_q_post_rules($comment, $parent, $children, null); // array union
if (qa_page_q_single_click_c($comment, $question, $parent, $error)) { if (qa_page_q_single_click_c($comment, $question, $parent, $error)) {
$comment=qa_db_select_with_pending(qa_db_full_post_selectspec($userid, $commentid)); $comment = qa_db_select_with_pending(qa_db_full_post_selectspec($userid, $commentid));
// If so, page content to be updated via Ajax // If so, page content to be updated via Ajax
echo "QA_AJAX_RESPONSE\n1"; echo "QA_AJAX_RESPONSE\n1";
// If the comment was not deleted... // If the comment was not deleted...
if (isset($comment)) { if (isset($comment)) {
$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 // 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); $comment = $comment + qa_page_q_post_rules($comment, $parent, $children, null);
$usershtml=qa_userids_handles_html(array($comment), true); $usershtml = qa_userids_handles_html(array($comment), true);
$c_view=qa_page_q_comment_view($question, $parent, $comment, $usershtml, false); $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); $themeclass = qa_load_theme_class(qa_get_site_theme(), 'ajax-comment', null, null);
$themeclass->initialize(); $themeclass->initialize();
...@@ -86,12 +81,7 @@ ...@@ -86,12 +81,7 @@
return; return;
} }
} }
echo "QA_AJAX_RESPONSE\n0\n"; // fall back to non-Ajax submission if something failed
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
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-ajax-click-pm.php
Description: Server-side response to Ajax single clicks on private messages Description: Server-side response to Ajax single clicks on private messages
...@@ -20,38 +19,38 @@ ...@@ -20,38 +19,38 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
require_once QA_INCLUDE_DIR.'app/messages.php'; require_once QA_INCLUDE_DIR . 'app/messages.php';
require_once QA_INCLUDE_DIR.'app/users.php'; require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR.'app/cookies.php'; require_once QA_INCLUDE_DIR . 'app/cookies.php';
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
$loginUserId = qa_get_logged_in_userid(); $loginUserId = qa_get_logged_in_userid();
$loginUserHandle = qa_get_logged_in_handle(); $loginUserHandle = qa_get_logged_in_handle();
$fromhandle = qa_post_text('handle'); $fromhandle = qa_post_text('handle');
$start = (int) qa_post_text('start'); $start = (int)qa_post_text('start');
$box = qa_post_text('box'); $box = qa_post_text('box');
$pagesize = qa_opt('page_size_pms'); $pagesize = qa_opt('page_size_pms');
if ( !isset($loginUserId) || $loginUserHandle !== $fromhandle || !in_array($box, array('inbox', 'outbox')) ) { if (!isset($loginUserId) || $loginUserHandle !== $fromhandle || !in_array($box, array('inbox', 'outbox'))) {
echo "QA_AJAX_RESPONSE\n0\n"; echo "QA_AJAX_RESPONSE\n0\n";
return; return;
} }
$func = 'qa_db_messages_'.$box.'_selectspec'; $func = 'qa_db_messages_' . $box . '_selectspec';
$pmSpec = $func('private', $loginUserId, true, $start, $pagesize); $pmSpec = $func('private', $loginUserId, true, $start, $pagesize);
$userMessages = qa_db_select_with_pending($pmSpec); $userMessages = qa_db_select_with_pending($pmSpec);
foreach ($userMessages as $message) { foreach ($userMessages as $message) {
if (qa_clicked('m'.$message['messageid'].'_dodelete')) { if (qa_clicked('m' . $message['messageid'] . '_dodelete')) {
if (qa_check_form_security_code('pm-'.$fromhandle, qa_post_text('code'))) { if (qa_check_form_security_code('pm-' . $fromhandle, qa_post_text('code'))) {
qa_pm_delete($loginUserId, qa_get_logged_in_handle(), qa_cookie_get(), $message, $box); qa_pm_delete($loginUserId, qa_get_logged_in_handle(), qa_cookie_get(), $message, $box);
echo "QA_AJAX_RESPONSE\n1\n"; echo "QA_AJAX_RESPONSE\n1\n";
return; return;
} }
} }
} }
echo "QA_AJAX_RESPONSE\n0\n"; echo "QA_AJAX_RESPONSE\n0\n";
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-ajax-click-wall.php
Description: Server-side response to Ajax single clicks on wall posts Description: Server-side response to Ajax single clicks on wall posts
...@@ -20,28 +19,26 @@ ...@@ -20,28 +19,26 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
require_once QA_INCLUDE_DIR.'app/messages.php'; require_once QA_INCLUDE_DIR . 'app/messages.php';
require_once QA_INCLUDE_DIR.'app/users.php'; require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR.'app/cookies.php'; require_once QA_INCLUDE_DIR . 'app/cookies.php';
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
$tohandle=qa_post_text('handle'); $tohandle = qa_post_text('handle');
$start=(int)qa_post_text('start'); $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_db_select_with_pending(qa_db_recent_messages_selectspec(null, null, $tohandle, false, null, $start));
$usermessages=qa_wall_posts_add_rules($usermessages, $start); $usermessages = qa_wall_posts_add_rules($usermessages, $start);
foreach ($usermessages as $message) foreach ($usermessages as $message) {
if (qa_clicked('m'.$message['messageid'].'_dodelete') && $message['deleteable']) if (qa_clicked('m' . $message['messageid'] . '_dodelete') && $message['deleteable']) {
if (qa_check_form_security_code('wall-'.$tohandle, qa_post_text('code'))) { if (qa_check_form_security_code('wall-' . $tohandle, qa_post_text('code'))) {
qa_wall_delete_post(qa_get_logged_in_userid(), qa_get_logged_in_handle(), qa_cookie_get(), $message); qa_wall_delete_post(qa_get_logged_in_userid(), qa_get_logged_in_handle(), qa_cookie_get(), $message);
echo "QA_AJAX_RESPONSE\n1\n"; echo "QA_AJAX_RESPONSE\n1\n";
return; return;
} }
}
}
echo "QA_AJAX_RESPONSE\n0\n"; echo "QA_AJAX_RESPONSE\n0\n";
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-ajax-comment.php
Description: Server-side response to Ajax create comment requests Description: Server-side response to Ajax create comment requests
...@@ -20,85 +19,77 @@ ...@@ -20,85 +19,77 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
require_once QA_INCLUDE_DIR.'app/users.php'; require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR.'app/limits.php'; require_once QA_INCLUDE_DIR . 'app/limits.php';
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
// Load relevant information about this question and the comment parent // Load relevant information about this question and the comment parent
$questionid=qa_post_text('c_questionid'); $questionid = qa_post_text('c_questionid');
$parentid=qa_post_text('c_parentid'); $parentid = qa_post_text('c_parentid');
$userid=qa_get_logged_in_userid(); $userid = qa_get_logged_in_userid();
list($question, $parent, $children)=qa_db_select_with_pending( list($question, $parent, $children) = qa_db_select_with_pending(
qa_db_full_post_selectspec($userid, $questionid), qa_db_full_post_selectspec($userid, $questionid),
qa_db_full_post_selectspec($userid, $parentid), qa_db_full_post_selectspec($userid, $parentid),
qa_db_full_child_posts_selectspec($userid, $parentid) qa_db_full_child_posts_selectspec($userid, $parentid)
); );
// Check if the question and parent exist, and whether the user has permission to do this // Check if the question and parent exist, and whether the user has permission to do this
if ( if (@$question['basetype'] == 'Q' && (@$parent['basetype'] == 'Q' || @$parent['basetype'] == 'A') &&
(@$question['basetype']=='Q') && !qa_user_post_permit_error('permit_post_c', $parent, QA_LIMIT_COMMENTS)
((@$parent['basetype']=='Q') || (@$parent['basetype']=='A')) && ) {
!qa_user_post_permit_error('permit_post_c', $parent, QA_LIMIT_COMMENTS)) require_once QA_INCLUDE_DIR . 'app/captcha.php';
{ require_once QA_INCLUDE_DIR . 'app/format.php';
require_once QA_INCLUDE_DIR.'app/captcha.php'; require_once QA_INCLUDE_DIR . 'app/post-create.php';
require_once QA_INCLUDE_DIR.'app/format.php'; require_once QA_INCLUDE_DIR . 'app/cookies.php';
require_once QA_INCLUDE_DIR.'app/post-create.php'; require_once QA_INCLUDE_DIR . 'pages/question-view.php';
require_once QA_INCLUDE_DIR.'app/cookies.php'; require_once QA_INCLUDE_DIR . 'pages/question-submit.php';
require_once QA_INCLUDE_DIR.'pages/question-view.php'; require_once QA_INCLUDE_DIR . 'util/sort.php';
require_once QA_INCLUDE_DIR.'pages/question-submit.php';
require_once QA_INCLUDE_DIR.'util/sort.php';
// Try to create the new comment // Try to create the new comment
$usecaptcha=qa_user_use_captcha(qa_user_level_for_post($question)); $usecaptcha = qa_user_use_captcha(qa_user_level_for_post($question));
$commentid=qa_page_q_add_c_submit($question, $parent, $children, $usecaptcha, $in, $errors); $commentid = qa_page_q_add_c_submit($question, $parent, $children, $usecaptcha, $in, $errors);
// If successful, page content will be updated via Ajax // If successful, page content will be updated via Ajax
if (isset($commentid)) { if (isset($commentid)) {
$children=qa_db_select_with_pending(qa_db_full_child_posts_selectspec($userid, $parentid)); $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 // 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) foreach ($children as $key => $child) {
$children[$key]=$child+qa_page_q_post_rules($child, $parent, $children, null); $children[$key] = $child + qa_page_q_post_rules($child, $parent, $children, null);
}
$usershtml=qa_userids_handles_html($children, true); $usershtml = qa_userids_handles_html($children, true);
qa_sort_by($children, 'created'); qa_sort_by($children, 'created');
$c_list=qa_page_q_comment_follow_list($question, $parent, $children, true, $usershtml, false, null); $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); $themeclass = qa_load_theme_class(qa_get_site_theme(), 'ajax-comments', null, null);
$themeclass->initialize(); $themeclass->initialize();
echo "QA_AJAX_RESPONSE\n1\n"; echo "QA_AJAX_RESPONSE\n1\n";
// Send back the ID of the new comment // send back the ID of the new comment
echo qa_anchor('C', $commentid) . "\n";
echo qa_anchor('C', $commentid)."\n";
// Send back the HTML // send back the HTML
$themeclass->c_list_items($c_list['cs']); $themeclass->c_list_items($c_list['cs']);
return; return;
} }
} }
echo "QA_AJAX_RESPONSE\n0\n"; // fall back to non-Ajax submission if there were any problems 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
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-ajax-favorite.php
Description: Server-side response to Ajax favorite requests Description: Server-side response to Ajax favorite requests
...@@ -20,37 +19,31 @@ ...@@ -20,37 +19,31 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
require_once QA_INCLUDE_DIR.'app/users.php'; require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR.'app/cookies.php'; require_once QA_INCLUDE_DIR . 'app/cookies.php';
require_once QA_INCLUDE_DIR.'app/favorites.php'; require_once QA_INCLUDE_DIR . 'app/favorites.php';
require_once QA_INCLUDE_DIR.'app/format.php'; require_once QA_INCLUDE_DIR . 'app/format.php';
$entitytype=qa_post_text('entitytype'); $entitytype = qa_post_text('entitytype');
$entityid=qa_post_text('entityid'); $entityid = qa_post_text('entityid');
$setfavorite=qa_post_text('favorite'); $setfavorite = qa_post_text('favorite');
$userid=qa_get_logged_in_userid(); $userid = qa_get_logged_in_userid();
if (!qa_check_form_security_code('favorite-'.$entitytype.'-'.$entityid, qa_post_text('code'))) 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'); echo "QA_AJAX_RESPONSE\n0\n" . qa_lang('misc/form_security_reload');
} elseif (isset($userid)) {
elseif (isset($userid)) { $cookieid = qa_cookie_get();
$cookieid=qa_cookie_get();
qa_user_favorite_set($userid, qa_get_logged_in_handle(), $cookieid, $entitytype, $entityid, $setfavorite); 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')); $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); $themeclass = qa_load_theme_class(qa_get_site_theme(), 'ajax-favorite', null, null);
$themeclass->initialize(); $themeclass->initialize();
echo "QA_AJAX_RESPONSE\n1\n"; echo "QA_AJAX_RESPONSE\n1\n";
$themeclass->favorite_inner_html($favoriteform); $themeclass->favorite_inner_html($favoriteform);
} }
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-ajax-mailing.php
Description: Server-side response to Ajax mailing loop requests Description: Server-side response to Ajax mailing loop requests
...@@ -20,34 +19,29 @@ ...@@ -20,34 +19,29 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
require_once QA_INCLUDE_DIR.'app/users.php'; require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR.'app/mailing.php'; require_once QA_INCLUDE_DIR . 'app/mailing.php';
$continue=false; $continue = false;
if (qa_get_logged_in_level()>=QA_USER_LEVEL_ADMIN) { if (qa_get_logged_in_level() >= QA_USER_LEVEL_ADMIN) {
$starttime=time(); $starttime = time();
qa_mailing_perform_step(); qa_mailing_perform_step();
if ($starttime==time()) if ($starttime == time())
sleep(1); // make sure at least one second has passed sleep(1); // make sure at least one second has passed
$message=qa_mailing_progress_message(); $message = qa_mailing_progress_message();
if (isset($message)) if (isset($message))
$continue=true; $continue = true;
else else
$message=qa_lang('admin/mailing_complete'); $message = qa_lang('admin/mailing_complete');
} else } else
$message=qa_lang('admin/no_privileges'); $message = qa_lang('admin/no_privileges');
echo "QA_AJAX_RESPONSE\n".(int)$continue."\n".qa_html($message); echo "QA_AJAX_RESPONSE\n" . (int)$continue . "\n" . qa_html($message);
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-ajax-notice.php
Description: Server-side response to Ajax requests to close a notice Description: Server-side response to Ajax requests to close a notice
...@@ -20,24 +19,24 @@ ...@@ -20,24 +19,24 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
require_once QA_INCLUDE_DIR.'app/users.php'; require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR.'db/notices.php'; require_once QA_INCLUDE_DIR . 'db/notices.php';
require_once QA_INCLUDE_DIR.'db/users.php'; require_once QA_INCLUDE_DIR . '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'))) if (!qa_check_form_security_code('notice-' . $noticeid, qa_post_text('code')))
echo "QA_AJAX_RESPONSE\n0\n".qa_lang('misc/form_security_reload'); echo "QA_AJAX_RESPONSE\n0\n" . qa_lang('misc/form_security_reload');
else { else {
if ($noticeid=='visitor') if ($noticeid == 'visitor')
setcookie('qa_noticed', 1, time()+86400*3650, '/', QA_COOKIE_DOMAIN); setcookie('qa_noticed', 1, time() + 86400 * 3650, '/', QA_COOKIE_DOMAIN, (bool)ini_get('session.cookie_secure'), true);
else { else {
$userid=qa_get_logged_in_userid(); $userid = qa_get_logged_in_userid();
if ($noticeid=='welcome') if ($noticeid == 'welcome')
qa_db_user_set_flag($userid, QA_USER_FLAGS_WELCOME_NOTICE, false); qa_db_user_set_flag($userid, QA_USER_FLAGS_WELCOME_NOTICE, false);
else else
qa_db_usernotice_delete($userid, $noticeid); qa_db_usernotice_delete($userid, $noticeid);
...@@ -45,9 +44,4 @@ ...@@ -45,9 +44,4 @@
echo "QA_AJAX_RESPONSE\n1"; echo "QA_AJAX_RESPONSE\n1";
} }
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-ajax-recalc.php
Description: Server-side response to Ajax admin recalculation requests Description: Server-side response to Ajax admin recalculation requests
...@@ -20,35 +19,30 @@ ...@@ -20,35 +19,30 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
require_once QA_INCLUDE_DIR.'app/users.php'; require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR.'app/recalc.php'; require_once QA_INCLUDE_DIR . 'app/recalc.php';
if (qa_get_logged_in_level()>=QA_USER_LEVEL_ADMIN) { if (qa_get_logged_in_level() >= QA_USER_LEVEL_ADMIN) {
if (!qa_check_form_security_code('admin/recalc', qa_post_text('code'))) { if (!qa_check_form_security_code('admin/recalc', qa_post_text('code'))) {
$state=''; $state = '';
$message=qa_lang('misc/form_security_reload'); $message = qa_lang('misc/form_security_reload');
} else { } else {
$state=qa_post_text('state'); $state = qa_post_text('state');
$stoptime=time()+3; $stoptime = time() + 3;
while ( qa_recalc_perform_step($state) && (time()<$stoptime) )
;
$message=qa_recalc_get_message($state); while (qa_recalc_perform_step($state) && time() < $stoptime) {
// wait
} }
} else { $message = qa_recalc_get_message($state);
$state='';
$message=qa_lang('admin/no_privileges');
} }
} else {
$state = '';
$message = qa_lang('admin/no_privileges');
}
echo "QA_AJAX_RESPONSE\n1\n".$state."\n".qa_html($message);
echo "QA_AJAX_RESPONSE\n1\n" . $state . "\n" . qa_html($message);
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-ajax-show-comments.php
Description: Server-side response to Ajax request to view full comment list Description: Server-side response to Ajax request to view full comment list
...@@ -20,40 +19,46 @@ ...@@ -20,40 +19,46 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
require_once QA_INCLUDE_DIR.'app/users.php'; require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR.'app/cookies.php'; require_once QA_INCLUDE_DIR . 'app/cookies.php';
require_once QA_INCLUDE_DIR.'app/format.php'; require_once QA_INCLUDE_DIR . 'app/format.php';
require_once QA_INCLUDE_DIR.'pages/question-view.php'; require_once QA_INCLUDE_DIR . 'pages/question-view.php';
require_once QA_INCLUDE_DIR.'util/sort.php'; require_once QA_INCLUDE_DIR . 'util/sort.php';
// Load relevant information about this question and check it exists // Load relevant information about this question and check it exists
$questionid=qa_post_text('c_questionid'); $questionid = qa_post_text('c_questionid');
$parentid=qa_post_text('c_parentid'); $parentid = qa_post_text('c_parentid');
$userid=qa_get_logged_in_userid(); $userid = qa_get_logged_in_userid();
list($question, $parent, $children)=qa_db_select_with_pending( list($question, $parent, $children, $duplicateposts) = qa_db_select_with_pending(
qa_db_full_post_selectspec($userid, $questionid), qa_db_full_post_selectspec($userid, $questionid),
qa_db_full_post_selectspec($userid, $parentid), qa_db_full_post_selectspec($userid, $parentid),
qa_db_full_child_posts_selectspec($userid, $parentid) qa_db_full_child_posts_selectspec($userid, $parentid),
); qa_db_post_duplicates_selectspec($questionid)
);
if (isset($parent)) { 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 + $duplicateposts);
// in theory we should retrieve the parent's parent and siblings for the above, but they're not going to be relevant // 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) foreach ($children as $key => $child) {
$children[$key]=$child+qa_page_q_post_rules($child, $parent, $children, null); $children[$key] = $child + qa_page_q_post_rules($child, $parent, $children, null);
}
$commentsfollows = $questionid == $parentid
? qa_page_q_load_c_follows($question, $children, array(), $duplicateposts)
: qa_page_q_load_c_follows($question, array(), $children);
$usershtml=qa_userids_handles_html($children, true); $usershtml = qa_userids_handles_html($commentsfollows, true);
qa_sort_by($children, 'created'); qa_sort_by($commentsfollows, 'created');
$c_list=qa_page_q_comment_follow_list($question, $parent, $children, true, $usershtml, false, null); $c_list = qa_page_q_comment_follow_list($question, $parent, $commentsfollows, true, $usershtml, false, null);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-comments', null, null); $themeclass = qa_load_theme_class(qa_get_site_theme(), 'ajax-comments', null, null);
$themeclass->initialize(); $themeclass->initialize();
echo "QA_AJAX_RESPONSE\n1\n"; echo "QA_AJAX_RESPONSE\n1\n";
...@@ -64,12 +69,7 @@ ...@@ -64,12 +69,7 @@
$themeclass->c_list_items($c_list['cs']); $themeclass->c_list_items($c_list['cs']);
return; return;
} }
echo "QA_AJAX_RESPONSE\n0\n";
echo "QA_AJAX_RESPONSE\n0\n";
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-ajax-version.php
Description: Server-side response to Ajax version check requests Description: Server-side response to Ajax version check requests
...@@ -20,56 +19,62 @@ ...@@ -20,56 +19,62 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
require_once QA_INCLUDE_DIR.'app/admin.php'; require_once QA_INCLUDE_DIR . 'app/admin.php';
require_once QA_INCLUDE_DIR.'app/users.php'; require_once QA_INCLUDE_DIR . 'app/users.php';
if (qa_get_logged_in_level() < QA_USER_LEVEL_ADMIN) { if (qa_get_logged_in_level() < QA_USER_LEVEL_ADMIN) {
echo "QA_AJAX_RESPONSE\n0\n" . qa_lang_html('admin/no_privileges'); echo "QA_AJAX_RESPONSE\n0\n" . qa_lang_html('admin/no_privileges');
return; return;
}
$uri = qa_post_text('uri');
$version = qa_post_text('version');
$isCore = qa_post_text('isCore') === "true";
if ($isCore) {
$contents = qa_retrieve_url($uri);
if (strlen($contents) > 0) {
if (qa_qa_version_below($contents)) {
$response =
'<a href="https://github.com/q2a/question2answer/releases" style="color:#d00;">' .
qa_lang_html_sub('admin/version_get_x', qa_html('v' . $contents)) .
'</a>';
} else {
$response = qa_html($contents); // Output the current version number
} }
} else {
$uri = qa_post_text('uri'); $response = qa_lang_html('admin/version_latest_unknown');
$version = qa_post_text('version'); }
} else {
$metadataUtil = new Q2A_Util_Metadata(); $metadataUtil = new Q2A_Util_Metadata();
$metadata = $metadataUtil->fetchFromUrl($uri); $metadata = $metadataUtil->fetchFromUrl($uri);
if (strlen(@$metadata['version'])) { if (strlen(@$metadata['version']) > 0) {
if (strcmp($metadata['version'], $version)) { if (strcmp($metadata['version'], $version)) {
if (qa_qa_version_below(@$metadata['min_q2a'])) { if (qa_qa_version_below(@$metadata['min_q2a'])) {
$response = strtr(qa_lang_html('admin/version_requires_q2a'), array( $response = strtr(qa_lang_html('admin/version_requires_q2a'), array(
'^1' => qa_html('v'.$metadata['version']), '^1' => qa_html('v' . $metadata['version']),
'^2' => qa_html($metadata['min_q2a']), '^2' => qa_html($metadata['min_q2a']),
)); ));
} } elseif (qa_php_version_below(@$metadata['min_php'])) {
elseif (qa_php_version_below(@$metadata['min_php'])) {
$response = strtr(qa_lang_html('admin/version_requires_php'), array( $response = strtr(qa_lang_html('admin/version_requires_php'), array(
'^1' => qa_html('v'.$metadata['version']), '^1' => qa_html('v' . $metadata['version']),
'^2' => qa_html($metadata['min_php']), '^2' => qa_html($metadata['min_php']),
)); ));
} } else {
$response = qa_lang_html_sub('admin/version_get_x', qa_html('v' . $metadata['version']));
else { if (strlen(@$metadata['uri'])) {
$response = qa_lang_html_sub('admin/version_get_x', qa_html('v'.$metadata['version'])); $response = '<a href="' . qa_html($metadata['uri']) . '" style="color:#d00;">' . $response . '</a>';
if (strlen(@$metadata['uri']))
$response = '<a href="'.qa_html($metadata['uri']).'" style="color:#d00;">'.$response.'</a>';
} }
} }
else } else {
$response = qa_lang_html('admin/version_latest'); $response = qa_lang_html('admin/version_latest');
} }
else } else {
$response = qa_lang_html('admin/version_latest_unknown'); $response = qa_lang_html('admin/version_latest_unknown');
}
}
echo "QA_AJAX_RESPONSE\n1\n" . $response;
echo "QA_AJAX_RESPONSE\n1\n".$response;
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-ajax-vote.php
Description: Server-side response to Ajax voting requests Description: Server-side response to Ajax voting requests
...@@ -20,48 +19,45 @@ ...@@ -20,48 +19,45 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
require_once QA_INCLUDE_DIR.'app/users.php'; require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR.'app/cookies.php'; require_once QA_INCLUDE_DIR . 'app/cookies.php';
require_once QA_INCLUDE_DIR.'app/votes.php'; require_once QA_INCLUDE_DIR . 'app/votes.php';
require_once QA_INCLUDE_DIR.'app/format.php'; require_once QA_INCLUDE_DIR . 'app/format.php';
require_once QA_INCLUDE_DIR.'app/options.php'; require_once QA_INCLUDE_DIR . 'app/options.php';
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
$postid=qa_post_text('postid'); $postid = qa_post_text('postid');
$vote=qa_post_text('vote'); $vote = qa_post_text('vote');
$code=qa_post_text('code'); $code = qa_post_text('code');
$userid=qa_get_logged_in_userid(); $userid = qa_get_logged_in_userid();
$cookieid=qa_cookie_get(); $cookieid = qa_cookie_get();
if (!qa_check_form_security_code('vote', $code)) if (!qa_check_form_security_code('vote', $code)) {
$voteerror=qa_lang_html('misc/form_security_reload'); $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());
}
else { if ($voteerror === false) {
$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); 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)); $post = qa_db_select_with_pending(qa_db_full_post_selectspec($userid, $postid));
$fields=qa_post_html_fields($post, $userid, $cookieid, array(), null, array( $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 '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); $themeclass = qa_load_theme_class(qa_get_site_theme(), 'voting', null, null);
$themeclass->initialize(); $themeclass->initialize();
echo "QA_AJAX_RESPONSE\n1\n"; echo "QA_AJAX_RESPONSE\n1\n";
$themeclass->voting_inner_html($fields); $themeclass->voting_inner_html($fields);
} else return;
echo "QA_AJAX_RESPONSE\n0\n".$voteerror;
}
/* echo "QA_AJAX_RESPONSE\n0\n" . $voteerror;
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-ajax-wallpost.php
Description: Server-side response to Ajax wall post requests Description: Server-side response to Ajax wall post requests
...@@ -20,47 +19,42 @@ ...@@ -20,47 +19,42 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
require_once QA_INCLUDE_DIR.'app/messages.php'; require_once QA_INCLUDE_DIR . 'app/messages.php';
require_once QA_INCLUDE_DIR.'app/users.php'; require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR.'app/cookies.php'; require_once QA_INCLUDE_DIR . 'app/cookies.php';
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
$message=qa_post_text('message'); $message = qa_post_text('message');
$tohandle=qa_post_text('handle'); $tohandle = qa_post_text('handle');
$morelink=qa_post_text('morelink'); $morelink = qa_post_text('morelink');
$touseraccount=qa_db_select_with_pending(qa_db_user_account_selectspec($tohandle, false)); $touseraccount = qa_db_select_with_pending(qa_db_user_account_selectspec($tohandle, false));
$loginuserid=qa_get_logged_in_userid(); $loginuserid = qa_get_logged_in_userid();
$errorhtml=qa_wall_error_html($loginuserid, $touseraccount['userid'], $touseraccount['flags']); $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')) ) 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 echo "QA_AJAX_RESPONSE\n0"; // if there's an error, process in non-Ajax way
} else {
else { $messageid = qa_wall_add_post($loginuserid, qa_get_logged_in_handle(), qa_cookie_get(),
$messageid=qa_wall_add_post($loginuserid, qa_get_logged_in_handle(), qa_cookie_get(),
$touseraccount['userid'], $touseraccount['handle'], $message, ''); $touseraccount['userid'], $touseraccount['handle'], $message, '');
$touseraccount['wallposts']++; // won't have been updated $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_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); $usermessages = qa_wall_posts_add_rules($usermessages, 0);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'wall', null, null); $themeclass = qa_load_theme_class(qa_get_site_theme(), 'wall', null, null);
$themeclass->initialize(); $themeclass->initialize();
echo "QA_AJAX_RESPONSE\n1\n"; echo "QA_AJAX_RESPONSE\n1\n";
echo 'm'.$messageid."\n"; // element in list to be revealed echo 'm' . $messageid . "\n"; // element in list to be revealed
foreach ($usermessages as $message) foreach ($usermessages as $message) {
$themeclass->message_item(qa_wall_post_view($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)));
} }
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 }
*/
\ No newline at end of file
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-app-blobs.php
Description: Application-level blob-management functions Description: Application-level blob-management functions
...@@ -20,82 +19,103 @@ ...@@ -20,82 +19,103 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../'); header('Location: ../../');
exit; exit;
} }
function qa_get_blob_url($blobid, $absolute=false) /**
/* * Return the URL which will output $blobid from the database when requested, $absolute or relative
Return the URL which will output $blobid from the database when requested, $absolute or relative * @param $blobid
*/ * @param bool $absolute
{ * @return mixed|string
*/
function qa_get_blob_url($blobid, $absolute = false)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return qa_path('blob', array('qa_blobid' => $blobid), $absolute ? qa_opt('site_url') : null, QA_URL_FORMAT_PARAMS); 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)
Return the full path to the on-disk directory for blob $blobid (subdirectories are named by the first 3 digits of $blobid) * @param $blobid
*/ * @return mixed|string
{ */
function qa_get_blob_directory($blobid)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return rtrim(QA_BLOBS_DIRECTORY, '/').'/'.substr(str_pad($blobid, 20, '0', STR_PAD_LEFT), 0, 3); 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)
Return the full page and filename of blob $blobid which is in $format ($format is used as the file name suffix e.g. .jpg) * @param $blobid
*/ * @param $format
{ * @return mixed|string
*/
function qa_get_blob_filename($blobid, $format)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return qa_get_blob_directory($blobid).'/'.$blobid.'.'.preg_replace('/[^A-Za-z0-9]/', '', $format); 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.
Create a new blob (storing the content in the database or on disk as appropriate) with $content and $format, returning its blobid. * Pass the original name of the file uploaded in $sourcefilename and the $userid, $cookieid and $ip of the user creating it
Pass the original name of the file uploaded in $sourcefilename and the $userid, $cookieid and $ip of the user creating it * @param $content
*/ * @param $format
{ * @param $sourcefilename
* @param $userid
* @param $cookieid
* @param $ip
* @return mixed|null|string
*/
function qa_create_blob($content, $format, $sourcefilename = null, $userid = null, $cookieid = null, $ip = null)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'db/blobs.php'; require_once QA_INCLUDE_DIR . 'db/blobs.php';
$blobid=qa_db_blob_create(defined('QA_BLOBS_DIRECTORY') ? null : $content, $format, $sourcefilename, $userid, $cookieid, $ip); $blobid = qa_db_blob_create(defined('QA_BLOBS_DIRECTORY') ? null : $content, $format, $sourcefilename, $userid, $cookieid, $ip);
if (isset($blobid) && defined('QA_BLOBS_DIRECTORY')) if (isset($blobid) && defined('QA_BLOBS_DIRECTORY')) {
// still write content to the database if writing to disk failed
if (!qa_write_blob_file($blobid, $content, $format)) if (!qa_write_blob_file($blobid, $content, $format))
qa_db_blob_set_content($blobid, $content); // still write content to the database if writing to disk failed qa_db_blob_set_content($blobid, $content);
return $blobid;
} }
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.
*/ /**
{ * Write the on-disk file for blob $blobid with $content and $format. Returns true if the write succeeded, false otherwise.
* @param $blobid
* @param $content
* @param $format
* @return bool|mixed
*/
function qa_write_blob_file($blobid, $content, $format)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$written=false; $written = false;
$directory=qa_get_blob_directory($blobid); $directory = qa_get_blob_directory($blobid);
if (is_dir($directory) || mkdir($directory, fileperms(rtrim(QA_BLOBS_DIRECTORY, '/')) & 0777)) { if (is_dir($directory) || mkdir($directory, fileperms(rtrim(QA_BLOBS_DIRECTORY, '/')) & 0777)) {
$filename=qa_get_blob_filename($blobid, $format); $filename = qa_get_blob_filename($blobid, $format);
$file=fopen($filename, 'xb'); $file = fopen($filename, 'xb');
if (is_resource($file)) { if (is_resource($file)) {
if (fwrite($file, $content)>=strlen($content)) if (fwrite($file, $content) >= strlen($content))
$written=true; $written = true;
fclose($file); fclose($file);
...@@ -105,32 +125,37 @@ ...@@ -105,32 +125,37 @@
} }
return $written; return $written;
} }
function qa_read_blob($blobid) /**
/* * Retrieve blob $blobid from the database, reading the content from disk if appropriate
Retrieve blob $blobid from the database, reading the content from disk if appropriate * @param $blobid
*/ * @return array|mixed|null
{ */
function qa_read_blob($blobid)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'db/blobs.php'; require_once QA_INCLUDE_DIR . 'db/blobs.php';
$blob=qa_db_blob_read($blobid); $blob = qa_db_blob_read($blobid);
if (isset($blob) && defined('QA_BLOBS_DIRECTORY') && !isset($blob['content'])) if (isset($blob) && defined('QA_BLOBS_DIRECTORY') && !isset($blob['content']))
$blob['content']=qa_read_blob_file($blobid, $blob['format']); $blob['content'] = qa_read_blob_file($blobid, $blob['format']);
return $blob; 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.
Read the content of blob $blobid in $format from disk. On failure, it will return false. * @param $blobid
*/ * @param $format
{ * @return mixed|null|string
*/
function qa_read_blob_file($blobid, $format)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$filename = qa_get_blob_filename($blobid, $format); $filename = qa_get_blob_filename($blobid, $format);
...@@ -138,53 +163,55 @@ ...@@ -138,53 +163,55 @@
return file_get_contents($filename); return file_get_contents($filename);
else else
return null; return null;
} }
function qa_delete_blob($blobid) /**
/* * Delete blob $blobid from the database, and remove the on-disk file if appropriate
Delete blob $blobid from the database, and remove the on-disk file if appropriate * @param $blobid
*/ * @return mixed
{ */
function qa_delete_blob($blobid)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'db/blobs.php'; require_once QA_INCLUDE_DIR . 'db/blobs.php';
if (defined('QA_BLOBS_DIRECTORY')) { if (defined('QA_BLOBS_DIRECTORY')) {
$blob=qa_db_blob_read($blobid); $blob = qa_db_blob_read($blobid);
if (isset($blob) && !isset($blob['content'])) if (isset($blob) && !isset($blob['content']))
unlink(qa_get_blob_filename($blobid, $blob['format'])); unlink(qa_get_blob_filename($blobid, $blob['format']));
} }
qa_db_blob_delete($blobid); qa_db_blob_delete($blobid);
} }
function qa_delete_blob_file($blobid, $format) /**
/* * Delete the on-disk file for blob $blobid in $format
Delete the on-disk file for blob $blobid in $format * @param $blobid
*/ * @param $format
{ * @return mixed
*/
function qa_delete_blob_file($blobid, $format)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
unlink(qa_get_blob_filename($blobid, $format)); unlink(qa_get_blob_filename($blobid, $format));
} }
function qa_blob_exists($blobid) /**
/* * Check if blob $blobid exists
Check if blob $blobid exists * @param $blobid
*/ * @return bool|mixed
{ */
function qa_blob_exists($blobid)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'db/blobs.php'; require_once QA_INCLUDE_DIR . 'db/blobs.php';
return qa_db_blob_exists($blobid); return qa_db_blob_exists($blobid);
} }
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-app-captcha.php
Description: Wrapper functions and utilities for captcha modules Description: Wrapper functions and utilities for captcha modules
...@@ -21,7 +20,7 @@ ...@@ -21,7 +20,7 @@
*/ */
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../'); header('Location: ../../');
exit; exit;
} }
...@@ -39,6 +38,8 @@ function qa_captcha_available() ...@@ -39,6 +38,8 @@ function qa_captcha_available()
/** /**
* Return an HTML string explaining $captchareason (from qa_user_captcha_reason()) to the user about why they are seeing a captcha * Return an HTML string explaining $captchareason (from qa_user_captcha_reason()) to the user about why they are seeing a captcha
* @param $captchareason
* @return mixed|null|string
*/ */
function qa_captcha_reason_note($captchareason) function qa_captcha_reason_note($captchareason)
{ {
...@@ -65,8 +66,13 @@ function qa_captcha_reason_note($captchareason) ...@@ -65,8 +66,13 @@ function qa_captcha_reason_note($captchareason)
/** /**
* Prepare $qa_content for showing a captcha, adding the element to $fields, given previous $errors, and a $note to display. * Prepare $qa_content for showing a captcha, adding the element to $fields, given previous $errors, and a $note to display.
* Returns JavaScript required to load CAPTCHA when field is shown by user (e.g. clicking comment button). * Returns JavaScript required to load CAPTCHA when field is shown by user (e.g. clicking comment button).
* @param $qa_content
* @param $fields
* @param $errors
* @param $note
* @return string
*/ */
function qa_set_up_captcha_field(&$qa_content, &$fields, $errors, $note=null) function qa_set_up_captcha_field(&$qa_content, &$fields, $errors, $note = null)
{ {
if (!qa_captcha_available()) if (!qa_captcha_available())
return ''; return '';
...@@ -79,8 +85,7 @@ function qa_set_up_captcha_field(&$qa_content, &$fields, $errors, $note=null) ...@@ -79,8 +85,7 @@ function qa_set_up_captcha_field(&$qa_content, &$fields, $errors, $note=null)
if ($count > 1) { if ($count > 1) {
// use blank captcha in order to load via JS // use blank captcha in order to load via JS
$html = ''; $html = '';
} } else {
else {
// first captcha is always loaded explicitly // first captcha is always loaded explicitly
$qa_content['script_var']['qa_captcha_in'] = 'qa_captcha_div_1'; $qa_content['script_var']['qa_captcha_in'] = 'qa_captcha_div_1';
$html = $captcha->form_html($qa_content, @$errors['captcha']); $html = $captcha->form_html($qa_content, @$errors['captcha']);
...@@ -89,17 +94,19 @@ function qa_set_up_captcha_field(&$qa_content, &$fields, $errors, $note=null) ...@@ -89,17 +94,19 @@ function qa_set_up_captcha_field(&$qa_content, &$fields, $errors, $note=null)
$fields['captcha'] = array( $fields['captcha'] = array(
'type' => 'custom', 'type' => 'custom',
'label' => qa_lang_html('misc/captcha_label'), 'label' => qa_lang_html('misc/captcha_label'),
'html' => '<div id="qa_captcha_div_'.$count.'">'.$html.'</div>', 'html' => '<div id="qa_captcha_div_' . $count . '">' . $html . '</div>',
'error' => @array_key_exists('captcha', $errors) ? qa_lang_html('misc/captcha_error') : null, 'error' => @array_key_exists('captcha', $errors) ? qa_lang_html('misc/captcha_error') : null,
'note' => $note, 'note' => $note,
); );
return "if (!document.getElementById('qa_captcha_div_".$count."').hasChildNodes()) { recaptcha_load('qa_captcha_div_".$count."'); }"; return "if (!document.getElementById('qa_captcha_div_" . $count . "').hasChildNodes()) { recaptcha_load('qa_captcha_div_" . $count . "'); }";
} }
/** /**
* Check if captcha is submitted correctly, and if not, set $errors['captcha'] to a descriptive string. * Check if captcha is submitted correctly, and if not, set $errors['captcha'] to a descriptive string.
* @param $errors
* @return bool
*/ */
function qa_captcha_validate_post(&$errors) function qa_captcha_validate_post(&$errors)
{ {
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-app-cookies.php
Description: User cookie management (application level) for tracking anonymous posts Description: User cookie management (application level) for tracking anonymous posts
...@@ -20,59 +19,56 @@ ...@@ -20,59 +19,56 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../'); header('Location: ../../');
exit; exit;
} }
function qa_cookie_get() /**
/* * Return the user identification cookie sent by the browser for this page request, or null if none
Return the user identification cookie sent by the browser for this page request, or null if none */
*/ function qa_cookie_get()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return isset($_COOKIE['qa_id']) ? qa_gpc_to_string($_COOKIE['qa_id']) : null; 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.
Return user identification cookie sent by browser if valid, or create a new one if not. * Either way, extend for another year (this is used when an anonymous post is created)
Either way, extend for another year (this is used when an anonymous post is created) */
*/ function qa_cookie_get_create()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'db/cookies.php'; require_once QA_INCLUDE_DIR . 'db/cookies.php';
$cookieid=qa_cookie_get(); $cookieid = qa_cookie_get();
if (isset($cookieid) && qa_db_cookie_exists($cookieid)) if (!isset($cookieid) || !qa_db_cookie_exists($cookieid)) {
; // cookie is valid // cookie is invalid
else $cookieid = qa_db_cookie_create(qa_remote_ip_address());
$cookieid=qa_db_cookie_create(qa_remote_ip_address()); }
setcookie('qa_id', $cookieid, time()+86400*365, '/', QA_COOKIE_DOMAIN); setcookie('qa_id', $cookieid, time() + 86400 * 365, '/', QA_COOKIE_DOMAIN, (bool)ini_get('session.cookie_secure'), true);
$_COOKIE['qa_id']=$cookieid; $_COOKIE['qa_id'] = $cookieid;
return $cookieid; return $cookieid;
} }
function qa_cookie_report_action($cookieid, $action) /**
/* * Called after a database write $action performed by a user identified by $cookieid,
Called after a database write $action performed by a user identified by $cookieid, * relating to $questionid, $answerid and/or $commentid
relating to $questionid, $answerid and/or $commentid * @param $cookieid
*/ * @param $action
{ */
require_once QA_INCLUDE_DIR.'db/cookies.php'; function qa_cookie_report_action($cookieid, $action)
{
require_once QA_INCLUDE_DIR . 'db/cookies.php';
qa_db_cookie_written($cookieid, qa_remote_ip_address()); qa_db_cookie_written($cookieid, qa_remote_ip_address());
} }
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-app-emails.php
Description: Wrapper functions for sending email notifications to users Description: Wrapper functions for sending email notifications to users
...@@ -20,59 +19,68 @@ ...@@ -20,59 +19,68 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../'); header('Location: ../../');
exit; exit;
} }
require_once QA_INCLUDE_DIR.'app/options.php'; require_once QA_INCLUDE_DIR . 'app/options.php';
function qa_suspend_notifications($suspend=true) /**
/* * Suspend the sending of all email notifications via qa_send_notification(...) if $suspend is true, otherwise
Suspend the sending of all email notifications via qa_send_notification(...) if $suspend is true, otherwise * reinstate it. A counter is kept to allow multiple calls.
reinstate it. A counter is kept to allow multiple calls. * @param bool $suspend
*/ */
{ function qa_suspend_notifications($suspend = true)
{
global $qa_notifications_suspended; global $qa_notifications_suspended;
$qa_notifications_suspended+=($suspend ? 1 : -1); $qa_notifications_suspended += ($suspend ? 1 : -1);
} }
function qa_send_notification($userid, $email, $handle, $subject, $body, $subs, $html = false) /**
/* * Send email to person with $userid and/or $email and/or $handle (null/invalid values are ignored or retrieved from
Send email to person with $userid and/or $email and/or $handle (null/invalid values are ignored or retrieved from * user database as appropriate). Email uses $subject and $body, after substituting each key in $subs with its
user database as appropriate). Email uses $subject and $body, after substituting each key in $subs with its * corresponding value, plus applying some standard substitutions such as ^site_title, ^handle and ^email.
corresponding value, plus applying some standard substitutions such as ^site_title, ^handle and ^email. * @param $userid
*/ * @param $email
{ * @param $handle
* @param $subject
* @param $body
* @param $subs
* @param bool $html
* @return bool
*/
function qa_send_notification($userid, $email, $handle, $subject, $body, $subs, $html = false)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_notifications_suspended; global $qa_notifications_suspended;
if ($qa_notifications_suspended>0) if ($qa_notifications_suspended > 0)
return false; return false;
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
require_once QA_INCLUDE_DIR.'util/string.php'; require_once QA_INCLUDE_DIR . 'util/string.php';
if (isset($userid)) { if (isset($userid)) {
$needemail=!qa_email_validate(@$email); // take from user if invalid, e.g. @ used in practice $needemail = !qa_email_validate(@$email); // take from user if invalid, e.g. @ used in practice
$needhandle=empty($handle); $needhandle = empty($handle);
if ($needemail || $needhandle) { if ($needemail || $needhandle) {
if (QA_FINAL_EXTERNAL_USERS) { if (QA_FINAL_EXTERNAL_USERS) {
if ($needhandle) { if ($needhandle) {
$handles=qa_get_public_from_userids(array($userid)); $handles = qa_get_public_from_userids(array($userid));
$handle=@$handles[$userid]; $handle = @$handles[$userid];
} }
if ($needemail) if ($needemail)
$email=qa_get_user_email($userid); $email = qa_get_user_email($userid);
} else { } else {
$useraccount=qa_db_select_with_pending( $useraccount = qa_db_select_with_pending(
array( array(
'columns' => array('email', 'handle'), 'columns' => array('email', 'handle'),
'source' => '^users WHERE userid = #', 'source' => '^users WHERE userid = #',
...@@ -82,20 +90,20 @@ ...@@ -82,20 +90,20 @@
); );
if ($needhandle) if ($needhandle)
$handle=@$useraccount['handle']; $handle = @$useraccount['handle'];
if ($needemail) if ($needemail)
$email=@$useraccount['email']; $email = @$useraccount['email'];
} }
} }
} }
if (isset($email) && qa_email_validate($email)) { if (isset($email) && qa_email_validate($email)) {
$subs['^site_title']=qa_opt('site_title'); $subs['^site_title'] = qa_opt('site_title');
$subs['^handle']=$handle; $subs['^handle'] = $handle;
$subs['^email']=$email; $subs['^email'] = $email;
$subs['^open']="\n"; $subs['^open'] = "\n";
$subs['^close']="\n"; $subs['^close'] = "\n";
return qa_send_email(array( return qa_send_email(array(
'fromemail' => qa_opt('from_email'), 'fromemail' => qa_opt('from_email'),
...@@ -103,76 +111,72 @@ ...@@ -103,76 +111,72 @@
'toemail' => $email, 'toemail' => $email,
'toname' => $handle, 'toname' => $handle,
'subject' => strtr($subject, $subs), 'subject' => strtr($subject, $subs),
'body' => (empty($handle) ? '' : qa_lang_sub('emails/to_handle_prefix', $handle)).strtr($body, $subs), 'body' => (empty($handle) ? '' : qa_lang_sub('emails/to_handle_prefix', $handle)) . strtr($body, $subs),
'html' => $html, 'html' => $html,
)); ));
}
} else
return false; return false;
} }
function qa_send_email($params) /**
/* * Send the email based on the $params array - the following keys are required (some can be empty): fromemail,
Send the email based on the $params array - the following keys are required (some can be empty): fromemail, * fromname, toemail, toname, subject, body, html
fromname, toemail, toname, subject, body, html * @param $params
*/ * @return bool
{ */
function qa_send_email($params)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
// @error_log(print_r($params, true)); // @error_log(print_r($params, true));
require_once QA_INCLUDE_DIR.'vendor/PHPMailer/PHPMailerAutoload.php'; require_once QA_INCLUDE_DIR . 'vendor/PHPMailer/PHPMailerAutoload.php';
$mailer=new PHPMailer(); $mailer = new PHPMailer();
$mailer->CharSet='utf-8'; $mailer->CharSet = 'utf-8';
$mailer->From=$params['fromemail']; $mailer->From = $params['fromemail'];
$mailer->Sender=$params['fromemail']; $mailer->Sender = $params['fromemail'];
$mailer->FromName=$params['fromname']; $mailer->FromName = $params['fromname'];
$mailer->addAddress($params['toemail'], $params['toname']); $mailer->addAddress($params['toemail'], $params['toname']);
if(!empty($params['replytoemail'])){ if (!empty($params['replytoemail'])) {
$mailer->addReplyTo($params['replytoemail'], $params['replytoname']); $mailer->addReplyTo($params['replytoemail'], $params['replytoname']);
} }
$mailer->Subject=$params['subject']; $mailer->Subject = $params['subject'];
$mailer->Body=$params['body']; $mailer->Body = $params['body'];
if ($params['html']) if ($params['html'])
$mailer->isHTML(true); $mailer->isHTML(true);
if (qa_opt('smtp_active')) { if (qa_opt('smtp_active')) {
$mailer->isSMTP(); $mailer->isSMTP();
$mailer->Host=qa_opt('smtp_address'); $mailer->Host = qa_opt('smtp_address');
$mailer->Port=qa_opt('smtp_port'); $mailer->Port = qa_opt('smtp_port');
if (qa_opt('smtp_secure')){ if (qa_opt('smtp_secure')) {
$mailer->SMTPSecure=qa_opt('smtp_secure'); $mailer->SMTPSecure = qa_opt('smtp_secure');
} } else {
else { $mailer->SMTPOptions = array(
$mailer->SMTPOptions=array(
'ssl' => array( 'ssl' => array(
'verify_peer' => false, 'verify_peer' => false,
'verify_peer_name' => false, 'verify_peer_name' => false,
'allow_self_signed' => true 'allow_self_signed' => true,
) ),
); );
} }
if (qa_opt('smtp_authenticate')) { if (qa_opt('smtp_authenticate')) {
$mailer->SMTPAuth=true; $mailer->SMTPAuth = true;
$mailer->Username=qa_opt('smtp_username'); $mailer->Username = qa_opt('smtp_username');
$mailer->Password=qa_opt('smtp_password'); $mailer->Password = qa_opt('smtp_password');
} }
} }
$send_status = $mailer->send(); $send_status = $mailer->send();
if(!$send_status){ if (!$send_status) {
@error_log('PHP Question2Answer email send error: '.$mailer->ErrorInfo); @error_log('PHP Question2Answer email send error: ' . $mailer->ErrorInfo);
} }
return $send_status; return $send_status;
} }
/*
Omit PHP closing tag to help avoid accidental output
*/
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-app-events.php
Description: Handles the submission of events to the database (application level) Description: Handles the submission of events to the database (application level)
...@@ -20,69 +19,82 @@ ...@@ -20,69 +19,82 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../'); header('Location: ../../');
exit; exit;
} }
require_once QA_INCLUDE_DIR.'db/events.php'; require_once QA_INCLUDE_DIR . 'db/events.php';
require_once QA_INCLUDE_DIR.'app/updates.php'; require_once QA_INCLUDE_DIR . 'app/updates.php';
function qa_create_event_for_q_user($questionid, $lastpostid, $updatetype, $lastuserid, $otheruserid=null, $timestamp=null) /**
/* * Add appropriate events to the database for an action performed on a question. The event of type $updatetype relates
Add appropriate events to the database for an action performed on a question. The event of type $updatetype relates * to $lastpostid whose antecedent question is $questionid, and was caused by $lastuserid. Pass a unix $timestamp for
to $lastpostid whose antecedent question is $questionid, and was caused by $lastuserid. Pass a unix $timestamp for * the event time or leave as null to use now. This will add an event to $questionid's and $lastuserid's streams. If
the event time or leave as null to use now. This will add an event to $questionid's and $lastuserid's streams. If * $otheruserid is set, it will also add an notification-style event for that user, unless they are the one who did it.
$otheruserid is set, it will also add an notification-style event for that user, unless they are the one who did it. * @param $questionid
*/ * @param $lastpostid
{ * @param $updatetype
* @param $lastuserid
* @param $otheruserid
* @param $timestamp
*/
function qa_create_event_for_q_user($questionid, $lastpostid, $updatetype, $lastuserid, $otheruserid = null, $timestamp = null)
{
qa_db_event_create_for_entity(QA_ENTITY_QUESTION, $questionid, $questionid, $lastpostid, $updatetype, $lastuserid, $timestamp); // anyone who favorited the question qa_db_event_create_for_entity(QA_ENTITY_QUESTION, $questionid, $questionid, $lastpostid, $updatetype, $lastuserid, $timestamp); // anyone who favorited the question
if (isset($lastuserid) && !QA_FINAL_EXTERNAL_USERS) if (isset($lastuserid) && !QA_FINAL_EXTERNAL_USERS)
qa_db_event_create_for_entity(QA_ENTITY_USER, $lastuserid, $questionid, $lastpostid, $updatetype, $lastuserid, $timestamp); // anyone who favorited the user who did it qa_db_event_create_for_entity(QA_ENTITY_USER, $lastuserid, $questionid, $lastpostid, $updatetype, $lastuserid, $timestamp); // anyone who favorited the user who did it
if (isset($otheruserid) && ($otheruserid!=$lastuserid)) if (isset($otheruserid) && ($otheruserid != $lastuserid))
qa_db_event_create_not_entity($otheruserid, $questionid, $lastpostid, $updatetype, $lastuserid, $timestamp); // possible other user to be informed 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) /**
/* * Add appropriate events to the database for an action performed on a set of tags in $tagstring (namely, a question
Add appropriate events to the database for an action performed on a set of tags in $tagstring (namely, a question * being created with those tags or having one of those tags added afterwards). The event of type $updatetype relates
being created with those tags or having one of those tags added afterwards). The event of type $updatetype relates * to the question $questionid, and was caused by $lastuserid. Pass a unix $timestamp for the event time or leave as
to the question $questionid, and was caused by $lastuserid. Pass a unix $timestamp for the event time or leave as * null to use now.
null to use now. * @param $tagstring
*/ * @param $questionid
{ * @param $updatetype
require_once QA_INCLUDE_DIR.'util/string.php'; * @param $lastuserid
require_once QA_INCLUDE_DIR.'db/post-create.php'; * @param $timestamp
*/
$tagwordids=qa_db_word_mapto_ids(array_unique(qa_tagstring_to_tags($tagstring))); function qa_create_event_for_tags($tagstring, $questionid, $updatetype, $lastuserid, $timestamp = null)
foreach ($tagwordids as $wordid) {
require_once QA_INCLUDE_DIR . 'util/string.php';
require_once QA_INCLUDE_DIR . 'db/post-create.php';
$tagwordids = qa_db_word_mapto_ids(array_unique(qa_tagstring_to_tags($tagstring)));
foreach ($tagwordids as $wordid) {
qa_db_event_create_for_entity(QA_ENTITY_TAG, $wordid, $questionid, $questionid, $updatetype, $lastuserid, $timestamp); 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)
/* /**
Add appropriate events to the database for an action performed on $categoryid (namely, a question being created in * Add appropriate events to the database for an action performed on $categoryid (namely, a question being created in
that category or being moved to it later on), along with all of its ancestor categories. The event of type * that category or being moved to it later on), along with all of its ancestor categories. The event of type
$updatetype relates to the question $questionid, and was caused by $lastuserid. Pass a unix $timestamp for the event * $updatetype relates to the question $questionid, and was caused by $lastuserid. Pass a unix $timestamp for the event
time or leave as null to use now. * time or leave as null to use now.
*/ * @param $categoryid
{ * @param $questionid
* @param $updatetype
* @param $lastuserid
* @param $timestamp
*/
function qa_create_event_for_category($categoryid, $questionid, $updatetype, $lastuserid, $timestamp = null)
{
if (isset($categoryid)) { if (isset($categoryid)) {
require_once QA_INCLUDE_DIR.'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
require_once QA_INCLUDE_DIR.'app/format.php'; require_once QA_INCLUDE_DIR . 'app/format.php';
$categories=qa_category_path(qa_db_single_select(qa_db_category_nav_selectspec($categoryid, true)), $categoryid); $categories = qa_category_path(qa_db_single_select(qa_db_category_nav_selectspec($categoryid, true)), $categoryid);
foreach ($categories as $category) foreach ($categories as $category) {
qa_db_event_create_for_entity(QA_ENTITY_CATEGORY, $category['categoryid'], $questionid, $questionid, $updatetype, $lastuserid, $timestamp); 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
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-app-favorites.php
Description: Handles favoriting and unfavoriting (application level) Description: Handles favoriting and unfavoriting (application level)
...@@ -20,13 +19,13 @@ ...@@ -20,13 +19,13 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../'); header('Location: ../../');
exit; exit;
} }
/** /**
* Set an entity to be favorited or removed from favorites. Handles event reporting. * Set an entity to be favorited or removed from favorites. Handles event reporting.
* *
* @param int $userid ID of user assigned to the favorite * @param int $userid ID of user assigned to the favorite
...@@ -36,11 +35,11 @@ ...@@ -36,11 +35,11 @@
* @param string $entityid ID of the entity being favorited (e.g. postid for questions) * @param string $entityid ID of the entity being favorited (e.g. postid for questions)
* @param bool $favorite Whether to add favorite (true) or remove favorite (false) * @param bool $favorite Whether to add favorite (true) or remove favorite (false)
*/ */
function qa_user_favorite_set($userid, $handle, $cookieid, $entitytype, $entityid, $favorite) function qa_user_favorite_set($userid, $handle, $cookieid, $entitytype, $entityid, $favorite)
{ {
require_once QA_INCLUDE_DIR.'db/favorites.php'; require_once QA_INCLUDE_DIR . 'db/favorites.php';
require_once QA_INCLUDE_DIR.'app/limits.php'; require_once QA_INCLUDE_DIR . 'app/limits.php';
require_once QA_INCLUDE_DIR.'app/updates.php'; require_once QA_INCLUDE_DIR . 'app/updates.php';
// Make sure the user is not favoriting themselves // Make sure the user is not favoriting themselves
if ($entitytype == QA_ENTITY_USER && $userid == $entityid) { if ($entitytype == QA_ENTITY_USER && $userid == $entityid) {
...@@ -54,23 +53,23 @@ ...@@ -54,23 +53,23 @@
switch ($entitytype) { switch ($entitytype) {
case QA_ENTITY_QUESTION: case QA_ENTITY_QUESTION:
$action=$favorite ? 'q_favorite' : 'q_unfavorite'; $action = $favorite ? 'q_favorite' : 'q_unfavorite';
$params=array('postid' => $entityid); $params = array('postid' => $entityid);
break; break;
case QA_ENTITY_USER: case QA_ENTITY_USER:
$action=$favorite ? 'u_favorite' : 'u_unfavorite'; $action = $favorite ? 'u_favorite' : 'u_unfavorite';
$params=array('userid' => $entityid); $params = array('userid' => $entityid);
break; break;
case QA_ENTITY_TAG: case QA_ENTITY_TAG:
$action=$favorite ? 'tag_favorite' : 'tag_unfavorite'; $action = $favorite ? 'tag_favorite' : 'tag_unfavorite';
$params=array('wordid' => $entityid); $params = array('wordid' => $entityid);
break; break;
case QA_ENTITY_CATEGORY: case QA_ENTITY_CATEGORY:
$action=$favorite ? 'cat_favorite' : 'cat_unfavorite'; $action = $favorite ? 'cat_favorite' : 'cat_unfavorite';
$params=array('categoryid' => $entityid); $params = array('categoryid' => $entityid);
break; break;
default: default:
...@@ -79,15 +78,18 @@ ...@@ -79,15 +78,18 @@
} }
qa_report_event($action, $userid, $handle, $cookieid, $params); qa_report_event($action, $userid, $handle, $cookieid, $params);
} }
function qa_favorite_q_list_view($questions, $usershtml) /**
/* * Returns content to set in $qa_content['q_list'] for a user's favorite $questions. Pre-generated
Returns content to set in $qa_content['q_list'] for a user's favorite $questions. Pre-generated * user HTML in $usershtml.
user HTML in $usershtml. * @param $questions
*/ * @param $usershtml
{ * @return array
*/
function qa_favorite_q_list_view($questions, $usershtml)
{
$q_list = array( $q_list = array(
'qs' => array(), 'qs' => array(),
); );
...@@ -96,7 +98,7 @@ ...@@ -96,7 +98,7 @@
return $q_list; return $q_list;
$q_list['form'] = array( $q_list['form'] = array(
'tags' => 'method="post" action="'.qa_self_html().'"', 'tags' => 'method="post" action="' . qa_self_html() . '"',
'hidden' => array( 'hidden' => array(
'code' => qa_get_form_security_code('vote'), 'code' => qa_get_form_security_code('vote'),
), ),
...@@ -110,23 +112,27 @@ ...@@ -110,23 +112,27 @@
} }
return $q_list; return $q_list;
} }
function qa_favorite_users_view($users, $usershtml) /**
/* * Returns content to set in $qa_content['ranking_users'] for a user's favorite $users. Pre-generated
Returns content to set in $qa_content['ranking_users'] for a user's favorite $users. Pre-generated * user HTML in $usershtml.
user HTML in $usershtml. * @param $users
*/ * @param $usershtml
{ * @return array|null
*/
function qa_favorite_users_view($users, $usershtml)
{
if (QA_FINAL_EXTERNAL_USERS) if (QA_FINAL_EXTERNAL_USERS)
return null; return null;
require_once QA_INCLUDE_DIR.'app/users.php'; require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR . 'app/format.php';
$ranking = array( $ranking = array(
'items' => array(), 'items' => array(),
'rows' => ceil(count($users)/qa_opt('columns_users')), 'rows' => ceil(count($users) / qa_opt('columns_users')),
'type' => 'users', 'type' => 'users',
); );
...@@ -137,66 +143,69 @@ ...@@ -137,66 +143,69 @@
$ranking['items'][] = array( $ranking['items'][] = array(
'avatar' => $avatarhtml, 'avatar' => $avatarhtml,
'label' => $usershtml[$user['userid']], 'label' => $usershtml[$user['userid']],
'score' => qa_html(number_format($user['points'])), 'score' => qa_html(qa_format_number($user['points'], 0, true)),
'raw' => $user, 'raw' => $user,
); );
} }
return $ranking; return $ranking;
} }
function qa_favorite_tags_view($tags) /**
/* * Returns content to set in $qa_content['ranking_tags'] for a user's favorite $tags.
Returns content to set in $qa_content['ranking_tagss'] for a user's favorite $tags. * @param $tags
*/ * @return array
{ */
function qa_favorite_tags_view($tags)
{
require_once QA_INCLUDE_DIR . 'app/format.php';
$ranking = array( $ranking = array(
'items' => array(), 'items' => array(),
'rows' => ceil(count($tags)/qa_opt('columns_tags')), 'rows' => ceil(count($tags) / qa_opt('columns_tags')),
'type' => 'tags' 'type' => 'tags',
); );
foreach ($tags as $tag) { foreach ($tags as $tag) {
$ranking['items'][] = array( $ranking['items'][] = array(
'label' => qa_tag_html($tag['word'], false, true), 'label' => qa_tag_html($tag['word'], false, true),
'count' => number_format($tag['tagcount']), 'count' => qa_html(qa_format_number($tag['tagcount'], 0, true)),
); );
} }
return $ranking; return $ranking;
} }
function qa_favorite_categories_view($categories) /**
/* * Returns content to set in $qa_content['nav_list_categories'] for a user's favorite $categories.
Returns content to set in $qa_content['nav_list_categories'] for a user's favorite $categories. * @param $categories
*/ * @return array
{ */
function qa_favorite_categories_view($categories)
{
require_once QA_INCLUDE_DIR . 'app/format.php';
$nav_list_categories = array( $nav_list_categories = array(
'nav' => array(), 'nav' => array(),
'type' => 'browse-cat', 'type' => 'browse-cat',
); );
foreach ($categories as $category) { foreach ($categories as $category) {
$cat_url = qa_path_html( 'questions/' . implode( '/', array_reverse(explode('/', $category['backpath'])) ) ); $cat_url = qa_path_html('questions/' . implode('/', array_reverse(explode('/', $category['backpath']))));
$cat_anchor = $category['qcount'] == 1 $cat_anchor = $category['qcount'] == 1
? qa_lang_html_sub('main/1_question', '1', '1') ? qa_lang_html_sub('main/1_question', '1', '1')
: qa_lang_html_sub('main/x_questions', number_format($category['qcount'])); : qa_lang_html_sub('main/x_questions', qa_format_number($category['qcount'], 0, true));
$cat_descr = strlen($category['content']) ? qa_html(' - '.$category['content']) : ''; $cat_descr = strlen($category['content']) ? qa_html(' - ' . $category['content']) : '';
$nav_list_categories['nav'][$category['categoryid']] = array( $nav_list_categories['nav'][$category['categoryid']] = array(
'label' => qa_html($category['title']), 'label' => qa_html($category['title']),
'state' => 'open', 'state' => 'open',
'favorited' => true, 'favorited' => true,
'note' => ' - <a href="'.$cat_url.'">'.$cat_anchor.'</a>'.$cat_descr, 'note' => ' - <a href="' . $cat_url . '">' . $cat_anchor . '</a>' . $cat_descr,
); );
} }
return $nav_list_categories; return $nav_list_categories;
} }
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-app-mailing.php
Description: Functions for sending a mailing to all users Description: Functions for sending a mailing to all users
...@@ -20,105 +19,112 @@ ...@@ -20,105 +19,112 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../'); header('Location: ../../');
exit; exit;
} }
function qa_mailing_start() /**
/* * Start a mailing to all users, unless one has already been started
Start a mailing to all users, unless one has already been started */
*/ function qa_mailing_start()
{ {
require_once QA_INCLUDE_DIR.'db/admin.php'; require_once QA_INCLUDE_DIR . 'db/admin.php';
if (strlen(qa_opt('mailing_last_userid'))==0) { if (strlen(qa_opt('mailing_last_userid')) == 0) {
qa_opt('mailing_last_timestamp', time()); qa_opt('mailing_last_timestamp', time());
qa_opt('mailing_last_userid', '0'); qa_opt('mailing_last_userid', '0');
qa_opt('mailing_total_users', qa_db_count_users()); qa_opt('mailing_total_users', qa_db_count_users());
qa_opt('mailing_done_users', 0); qa_opt('mailing_done_users', 0);
} }
} }
function qa_mailing_stop() /**
/* * Stop a mailing to all users
Stop a mailing to all users */
*/ function qa_mailing_stop()
{ {
qa_opt('mailing_last_timestamp', ''); qa_opt('mailing_last_timestamp', '');
qa_opt('mailing_last_userid', ''); qa_opt('mailing_last_userid', '');
qa_opt('mailing_done_users', ''); qa_opt('mailing_done_users', '');
qa_opt('mailing_total_users', ''); 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
Allow the mailing to proceed forwards, for the appropriate amount of time and users, based on the options */
*/ function qa_mailing_perform_step()
{ {
require_once QA_INCLUDE_DIR.'db/users.php'; require_once QA_INCLUDE_DIR . 'db/users.php';
$lastuserid=qa_opt('mailing_last_userid'); $lastuserid = qa_opt('mailing_last_userid');
if (strlen($lastuserid)) { if (strlen($lastuserid)) {
$thistime=time(); $thistime = time();
$lasttime=qa_opt('mailing_last_timestamp'); $lasttime = qa_opt('mailing_last_timestamp');
$perminute=qa_opt('mailing_per_minute'); $perminute = qa_opt('mailing_per_minute');
if (($lasttime-$thistime)>60) // if it's been a while, we assume there hasn't been continuous mailing... 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 $lasttime = $thistime - 1; // ... so only do 1 second's worth
else // otherwise... else // otherwise...
$lasttime=max($lasttime, $thistime-6); // ... don't do more than 6 seconds' worth $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 $count = min(floor(($thistime - $lasttime) * $perminute / 60), 100); // don't do more than 100 messages at a time
if ($count>0) { if ($count > 0) {
qa_opt('mailing_last_timestamp', $thistime+30); 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) // 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; $sentusers = 0;
$users=qa_db_users_get_mailing_next($lastuserid, $count); $users = qa_db_users_get_mailing_next($lastuserid, $count);
if (count($users)) { if (count($users)) {
foreach ($users as $user) foreach ($users as $user) {
$lastuserid=max($lastuserid, $user['userid']); $lastuserid = max($lastuserid, $user['userid']);
}
qa_opt('mailing_last_userid', $lastuserid); qa_opt('mailing_last_userid', $lastuserid);
qa_opt('mailing_done_users', qa_opt('mailing_done_users')+count($users)); qa_opt('mailing_done_users', qa_opt('mailing_done_users') + count($users));
foreach ($users as $user) foreach ($users as $user) {
if (!($user['flags'] & QA_USER_FLAGS_NO_MAILINGS)) { if (!($user['flags'] & QA_USER_FLAGS_NO_MAILINGS)) {
qa_mailing_send_one($user['userid'], $user['handle'], $user['email'], $user['emailcode']); qa_mailing_send_one($user['userid'], $user['handle'], $user['email'], $user['emailcode']);
$sentusers++; $sentusers++;
} }
}
qa_opt('mailing_last_timestamp', $lasttime+$sentusers*60/$perminute); // can be floating point result, based on number of mails actually sent qa_opt('mailing_last_timestamp', $lasttime + $sentusers * 60 / $perminute); // can be floating point result, based on number of mails actually sent
} else } else
qa_mailing_stop(); qa_mailing_stop();
} }
} }
} }
function qa_mailing_send_one($userid, $handle, $email, $emailcode) /**
/* * Send a single message from the mailing, to $userid with $handle and $email.
Send a single message from the mailing, to $userid with $handle and $email. * Pass the user's existing $emailcode if there is one, otherwise a new one will be set up
Pass the user's existing $emailcode if there is one, otherwise a new one will be set up * @param $userid
*/ * @param $handle
{ * @param $email
require_once QA_INCLUDE_DIR.'app/emails.php'; * @param $emailcode
require_once QA_INCLUDE_DIR.'db/users.php'; * @return bool
*/
function qa_mailing_send_one($userid, $handle, $email, $emailcode)
{
require_once QA_INCLUDE_DIR . 'app/emails.php';
require_once QA_INCLUDE_DIR . 'db/users.php';
if (!strlen(trim($emailcode))) { if (!strlen(trim($emailcode))) {
$emailcode=qa_db_user_rand_emailcode(); $emailcode = qa_db_user_rand_emailcode();
qa_db_user_set($userid, 'emailcode', $emailcode); qa_db_user_set($userid, 'emailcode', $emailcode);
} }
$unsubscribeurl=qa_path_absolute('unsubscribe', array('c' => $emailcode, 'u' => $handle)); $unsubscribeurl = qa_path_absolute('unsubscribe', array('c' => $emailcode, 'u' => $handle));
return qa_send_email(array( return qa_send_email(array(
'fromemail' => qa_opt('mailing_from_email'), 'fromemail' => qa_opt('mailing_from_email'),
...@@ -126,27 +132,25 @@ ...@@ -126,27 +132,25 @@
'toemail' => $email, 'toemail' => $email,
'toname' => $handle, 'toname' => $handle,
'subject' => qa_opt('mailing_subject'), 'subject' => qa_opt('mailing_subject'),
'body' => trim(qa_opt('mailing_body'))."\n\n\n".qa_lang('users/unsubscribe').' '.$unsubscribeurl, 'body' => trim(qa_opt('mailing_body')) . "\n\n\n" . qa_lang('users/unsubscribe') . ' ' . $unsubscribeurl,
'html' => false, 'html' => false,
)); ));
} }
function qa_mailing_progress_message() /**
/* * Return a message describing current progress in the mailing
Return a message describing current progress in the mailing */
*/ function qa_mailing_progress_message()
{ {
if (strlen(qa_opt('mailing_last_userid'))) require_once QA_INCLUDE_DIR . 'app/format.php';
if (strlen(qa_opt('mailing_last_userid'))) {
return strtr(qa_lang('admin/mailing_progress'), array( return strtr(qa_lang('admin/mailing_progress'), array(
'^1' => number_format(qa_opt('mailing_done_users')), '^1' => qa_format_number(qa_opt('mailing_done_users')),
'^2' => number_format(qa_opt('mailing_total_users')), '^2' => qa_format_number(qa_opt('mailing_total_users')),
)); ));
else
return null;
} }
return null;
/* }
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Question2Answer by Gideon Greenspan and contributors Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/ http://www.question2answer.org/
File: qa-include/qa-app-messages.php
Description: Handling private or public messages (wall posts) Description: Handling private or public messages (wall posts)
...@@ -20,24 +19,28 @@ ...@@ -20,24 +19,28 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../'); header('Location: ../../');
exit; exit;
} }
function qa_wall_error_html($fromuserid, $touserid, $touserflags) /**
/* * Returns an HTML string describing the reason why user $fromuserid cannot post on the wall of $touserid who has
Returns an HTML string describing the reason why user $fromuserid cannot post on the wall of $touserid who has * user flags $touserflags. If there is no such reason the function returns false.
user flags $touserflags. If there is no such reason the function returns false. * @param $fromuserid
*/ * @param $touserid
{ * @param $touserflags
require_once QA_INCLUDE_DIR.'app/limits.php'; * @return bool|mixed|string
*/
function qa_wall_error_html($fromuserid, $touserid, $touserflags)
{
require_once QA_INCLUDE_DIR . 'app/limits.php';
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
if (!QA_FINAL_EXTERNAL_USERS && qa_opt('allow_user_walls')) { if (!QA_FINAL_EXTERNAL_USERS && qa_opt('allow_user_walls')) {
if ( ($touserflags & QA_USER_FLAGS_NO_WALL_POSTS) && !(isset($fromuserid) && $fromuserid == $touserid) ) if (($touserflags & QA_USER_FLAGS_NO_WALL_POSTS) && !(isset($fromuserid) && $fromuserid == $touserid))
return qa_lang_html('profile/post_wall_blocked'); return qa_lang_html('profile/post_wall_blocked');
else { else {
...@@ -55,7 +58,10 @@ ...@@ -55,7 +58,10 @@
break; break;
case 'approve': case 'approve':
return qa_lang_html('profile/post_wall_must_be_approved'); return strtr(qa_lang_html('profile/post_wall_must_be_approved'), array(
'^1' => '<a href="' . qa_path_html('account') . '">',
'^2' => '</a>',
));
break; break;
case false: case false:
...@@ -66,19 +72,27 @@ ...@@ -66,19 +72,27 @@
} }
return qa_lang_html('users/no_permission'); 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')
Adds a post to the wall of user $touserid with handle $tohandle, containing $content in $format (e.g. '' for text or 'html') * The post is by user $userid with handle $handle, and $cookieid is the user's current cookie (used for reporting the event).
The post is by user $userid with handle $handle, and $cookieid is the user's current cookie (used for reporting the event). * @param $userid
*/ * @param $handle
{ * @param $cookieid
* @param $touserid
* @param $tohandle
* @param $content
* @param $format
* @return mixed
*/
function qa_wall_add_post($userid, $handle, $cookieid, $touserid, $tohandle, $content, $format)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'app/format.php'; require_once QA_INCLUDE_DIR . 'app/format.php';
require_once QA_INCLUDE_DIR.'db/messages.php'; require_once QA_INCLUDE_DIR . 'db/messages.php';
$messageid = qa_db_message_create($userid, $touserid, $content, $format, true); $messageid = qa_db_message_create($userid, $touserid, $content, $format, true);
qa_db_user_recount_posts($touserid); qa_db_user_recount_posts($touserid);
...@@ -93,16 +107,20 @@ ...@@ -93,16 +107,20 @@
)); ));
return $messageid; 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
Deletes the wall post described in $message (as obtained via qa_db_recent_messages_selectspec()). The deletion was performed * by user $userid with handle $handle, and $cookieid is the user's current cookie (all used for reporting the event).
by user $userid with handle $handle, and $cookieid is the user's current cookie (all used for reporting the event). * @param $userid
*/ * @param $handle
{ * @param $cookieid
require_once QA_INCLUDE_DIR.'db/messages.php'; * @param $message
*/
function qa_wall_delete_post($userid, $handle, $cookieid, $message)
{
require_once QA_INCLUDE_DIR . 'db/messages.php';
qa_db_message_delete($message['messageid']); qa_db_message_delete($message['messageid']);
qa_db_user_recount_posts($message['touserid']); qa_db_user_recount_posts($message['touserid']);
...@@ -111,21 +129,24 @@ ...@@ -111,21 +129,24 @@
'messageid' => $message['messageid'], 'messageid' => $message['messageid'],
'oldmessage' => $message, '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
Return the list of messages in $usermessages (as obtained via qa_db_recent_messages_selectspec()) with additional * fields indicating what actions can be performed on them by the current user. The messages were retrieved beginning
fields indicating what actions can be performed on them by the current user. The messages were retrieved beginning * at offset $start in the database. Currently only 'deleteable' is relevant.
at offset $start in the database. Currently only 'deleteable' is relevant. * @param $usermessages
*/ * @param $start
{ * @return mixed
*/
function qa_wall_posts_add_rules($usermessages, $start)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$userid = qa_get_logged_in_userid(); $userid = qa_get_logged_in_userid();
$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 // reuse "Hiding or showing any post" and "Deleting hidden posts" permissions
$userdeleteall = !(qa_user_permit_error('permit_hide_show') || qa_user_permit_error('permit_delete_hidden'));
$userrecent = $start == 0 && isset($userid); // User can delete all of the recent messages they wrote on someone's wall... $userrecent = $start == 0 && isset($userid); // User can delete all of the recent messages they wrote on someone's wall...
foreach ($usermessages as $key => $message) { foreach ($usermessages as $key => $message) {
...@@ -139,16 +160,18 @@ ...@@ -139,16 +160,18 @@
} }
return $usermessages; return $usermessages;
} }
function qa_wall_post_view($message) /**
/* * Returns an element to add to $qa_content['message_list']['messages'] for $message (as obtained via
Returns an element to add to $qa_content['message_list']['messages'] for $message (as obtained via * qa_db_recent_messages_selectspec() and then qa_wall_posts_add_rules()).
qa_db_recent_messages_selectspec() and then qa_wall_posts_add_rules()). * @param $message
*/ * @return array
{ */
require_once QA_INCLUDE_DIR.'app/format.php'; function qa_wall_post_view($message)
{
require_once QA_INCLUDE_DIR . 'app/format.php';
$options = qa_message_html_defaults(); $options = qa_message_html_defaults();
...@@ -160,7 +183,7 @@ ...@@ -160,7 +183,7 @@
'buttons' => array( 'buttons' => array(
'delete' => array( 'delete' => array(
'tags' => 'name="m'.qa_html($message['messageid']).'_dodelete" onclick="return qa_wall_post_click('.qa_js($message['messageid']).', this);"', 'tags' => 'name="m' . qa_html($message['messageid']) . '_dodelete" onclick="return qa_wall_post_click(' . qa_js($message['messageid']) . ', this);"',
'label' => qa_lang_html('question/delete_button'), 'label' => qa_lang_html('question/delete_button'),
'popup' => qa_lang_html('profile/delete_wall_post_popup'), 'popup' => qa_lang_html('profile/delete_wall_post_popup'),
), ),
...@@ -169,35 +192,38 @@ ...@@ -169,35 +192,38 @@
} }
return $htmlfields; 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
Returns an element to add to $qa_content['message_list']['messages'] with a link to view all wall posts * @param $handle
*/ * @param $start
{ * @return array
$url = qa_path_html( 'user/'.$handle.'/wall', array('start' => $start) ); */
function qa_wall_view_more_link($handle, $start)
{
$url = qa_path_html('user/' . $handle . '/wall', array('start' => $start));
return array( return array(
'content' => '<a href="'.$url.'">'.qa_lang_html('profile/wall_view_more').'</a>', 'content' => '<a href="' . $url . '">' . qa_lang_html('profile/wall_view_more') . '</a>',
); );
} }
function qa_pm_delete($userid, $handle, $cookieid, $message, $box) /**
/* * Hides the private message described in $message (as obtained via qa_db_messages_inbox_selectspec() or qa_db_messages_outbox_selectspec()).
Hides the private message described in $message (as obtained via qa_db_messages_inbox_selectspec() or qa_db_messages_outbox_selectspec()). * If both sender and receiver have hidden the message, it gets deleted from the database.
If both sender and receiver have hidden the message, it gets deleted from the database. * Note: currently no event is reported here, so $handle/$cookieid are unused.
Note: currently no event is reported here, so $handle/$cookieid are unused. * @param $userid
*/ * @param $handle
{ * @param $cookieid
require_once QA_INCLUDE_DIR.'db/messages.php'; * @param $message
* @param $box
*/
function qa_pm_delete($userid, $handle, $cookieid, $message, $box)
{
require_once QA_INCLUDE_DIR . 'db/messages.php';
qa_db_message_user_hide($message['messageid'], $box); qa_db_message_user_hide($message['messageid'], $box);
qa_db_message_delete($message['messageid'], false); qa_db_message_delete($message['messageid'], false);
} }
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
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