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
...@@ -20,13 +20,8 @@ ...@@ -20,13 +20,8 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
// 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');
......
...@@ -48,7 +48,7 @@ if (!defined('QA_VERSION')) { // don't allow this page to be requested directly ...@@ -48,7 +48,7 @@ if (!defined('QA_VERSION')) { // don't allow this page to be requested directly
*/ */
function qa_get_mysql_user_column_type() function qa_get_mysql_user_column_type()
{ {
// Set this before anything else // Set this before anything else
return null; return null;
...@@ -101,7 +101,7 @@ function qa_get_mysql_user_column_type() ...@@ -101,7 +101,7 @@ function qa_get_mysql_user_column_type()
*/ */
function qa_get_login_links($relative_url_prefix, $redirect_back_to_url) function qa_get_login_links($relative_url_prefix, $redirect_back_to_url)
{ {
// Until you edit this function, don't show login, register or logout links // Until you edit this function, don't show login, register or logout links
return array( return array(
'login' => null, 'login' => null,
...@@ -187,7 +187,7 @@ function qa_get_login_links($relative_url_prefix, $redirect_back_to_url) ...@@ -187,7 +187,7 @@ function qa_get_login_links($relative_url_prefix, $redirect_back_to_url)
*/ */
function qa_get_logged_in_user() function qa_get_logged_in_user()
{ {
// Until you edit this function, nobody is ever logged in // Until you edit this function, nobody is ever logged in
return null; return null;
...@@ -267,7 +267,7 @@ function qa_get_logged_in_user() ...@@ -267,7 +267,7 @@ function qa_get_logged_in_user()
*/ */
function qa_get_user_email($userid) function qa_get_user_email($userid)
{ {
// Until you edit this function, always return null // Until you edit this function, always return null
return null; return null;
...@@ -308,7 +308,7 @@ function qa_get_user_email($userid) ...@@ -308,7 +308,7 @@ function qa_get_user_email($userid)
*/ */
function qa_get_userids_from_public($publicusernames) function qa_get_userids_from_public($publicusernames)
{ {
// Until you edit this function, always return null // Until you edit this function, always return null
return null; return null;
...@@ -371,7 +371,7 @@ function qa_get_userids_from_public($publicusernames) ...@@ -371,7 +371,7 @@ function qa_get_userids_from_public($publicusernames)
*/ */
function qa_get_public_from_userids($userids) function qa_get_public_from_userids($userids)
{ {
// Until you edit this function, always return null // Until you edit this function, always return null
return null; return null;
...@@ -440,7 +440,7 @@ function qa_get_public_from_userids($userids) ...@@ -440,7 +440,7 @@ function qa_get_public_from_userids($userids)
*/ */
function qa_get_logged_in_user_html($logged_in_user, $relative_url_prefix) function qa_get_logged_in_user_html($logged_in_user, $relative_url_prefix)
{ {
// By default, show the public username linked to the Q2A profile page for the user // By default, show the public username linked to the Q2A profile page for the user
$publicusername = $logged_in_user['publicusername']; $publicusername = $logged_in_user['publicusername'];
...@@ -502,7 +502,7 @@ function qa_get_logged_in_user_html($logged_in_user, $relative_url_prefix) ...@@ -502,7 +502,7 @@ function qa_get_logged_in_user_html($logged_in_user, $relative_url_prefix)
*/ */
function qa_get_users_html($userids, $should_include_link, $relative_url_prefix) function qa_get_users_html($userids, $should_include_link, $relative_url_prefix)
{ {
// By default, show the public username linked to the Q2A profile page for each user // By default, show the public username linked to the Q2A profile page for each user
$useridtopublic = qa_get_public_from_userids($userids); $useridtopublic = qa_get_public_from_userids($userids);
......
<?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,31 +155,23 @@ class Q2A_Util_Usage ...@@ -148,31 +155,23 @@ class Q2A_Util_Usage
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach ($stages as $stage => $stagedelta) : ?>
<?php <tr>
$stages = $this->stages; <td class="row-heading"><?php echo ucfirst($stage); ?></td>
$stages['total'] = $totaldelta; <td><?php echo sprintf('%.1f', $stagedelta['clock'] * 1000); ?></td>
<td><?php echo sprintf('%d%%', $stagedelta['clock'] * 100 / $totaldelta['clock']); ?></td>
foreach ($stages as $stage => $stagedelta): <td><?php echo sprintf('%.1f', $stagedelta['cpu'] * 1000); ?></td>
?> <td><?php echo sprintf('%d%%', $stagedelta['cpu'] * 100 / $totaldelta['clock']); ?></td>
<tr> <td><?php echo $stagedelta['files']; ?></td>
<td class="row-heading"><?php echo ucfirst($stage); ?></td> <td><?php echo sprintf('%.1f', $stagedelta['mysql'] * 1000); ?></td>
<td><?php echo sprintf('%.1f', $stagedelta['clock'] * 1000); ?></td> <td><?php echo sprintf('%d%%', $stagedelta['mysql'] * 100 / $totaldelta['clock']); ?></td>
<td><?php echo sprintf('%d%%', $stagedelta['clock'] * 100 / $totaldelta['clock']); ?></td> <td><?php echo $stagedelta['queries']; ?></td>
<td><?php echo sprintf('%.1f', $stagedelta['cpu'] * 1000); ?></td> <td><?php echo sprintf('%.1f', $stagedelta['other'] * 1000); ?></td>
<td><?php echo sprintf('%d%%', $stagedelta['cpu'] * 100 / $totaldelta['clock']); ?></td> <td><?php echo sprintf('%d%%', $stagedelta['other'] * 100 / $totaldelta['clock']); ?></td>
<td><?php echo $stagedelta['files']; ?></td> <td><?php echo sprintf('%dk', $stagedelta['ram'] / 1024); ?></td>
<td><?php echo sprintf('%.1f', $stagedelta['mysql'] * 1000); ?></td> <td><?php echo sprintf('%d%%', $stagedelta['ram'] ? ($stagedelta['ram'] * 100 / $totaldelta['ram']) : 0); ?></td>
<td><?php echo sprintf('%d%%', $stagedelta['mysql'] * 100 / $totaldelta['clock']); ?></td> </tr>
<td><?php echo $stagedelta['queries']; ?></td> <?php endforeach; ?>
<td><?php echo sprintf('%.1f', $stagedelta['other'] * 1000); ?></td>
<td><?php echo sprintf('%d%%', $stagedelta['other'] * 100 / $totaldelta['clock']); ?></td>
<td><?php echo sprintf('%dk', $stagedelta['ram'] / 1024); ?></td>
<td><?php echo sprintf('%d%%', $stagedelta['ram'] ? ($stagedelta['ram'] * 100 / $totaldelta['ram']) : 0); ?></td>
</tr>
<?php
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,84 +19,80 @@ ...@@ -20,84 +19,80 @@
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";
// 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
$themeclass->a_list_item($a_view); $themeclass->a_list_item($a_view);
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,86 +19,82 @@ ...@@ -20,86 +19,82 @@
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);
$exampletags = array(); $exampletags = array();
$minweight = exp(qa_match_to_min_score(qa_opt('match_example_tags'))); $minweight = exp(qa_match_to_min_score(qa_opt('match_example_tags')));
$maxcount = qa_opt('page_size_ask_tags'); $maxcount = qa_opt('page_size_ask_tags');
foreach ($tagweight as $tag => $weight) { foreach ($tagweight as $tag => $weight) {
if ($weight < $minweight) if ($weight < $minweight)
break; break;
$exampletags[] = $tag; $exampletags[] = $tag;
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');
$relatedquestions = array_slice($relatedquestions, 0, $maxcount); $relatedquestions = array_slice($relatedquestions, 0, $maxcount);
$limitedquestions = array(); $limitedquestions = array();
foreach ($relatedquestions as $question) { foreach ($relatedquestions as $question) {
if ($question['score'] < $minscore) if ($question['score'] < $minscore)
break; break;
$limitedquestions[] = $question; $limitedquestions[] = $question;
}
$themeclass = qa_load_theme_class(qa_get_site_theme(), 'ajax-asktitle', null, null);
$themeclass->initialize();
$themeclass->q_ask_similar($limitedquestions, qa_lang_html('question/ask_same_q'));
} }
$themeclass = qa_load_theme_class(qa_get_site_theme(), 'ajax-asktitle', null, null);
/* $themeclass->initialize();
Omit PHP closing tag to help avoid accidental output $themeclass->q_ask_similar($limitedquestions, qa_lang_html('question/ask_same_q'));
*/ }
\ 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,99 +19,94 @@ ...@@ -20,99 +19,94 @@
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)
); );
// If so, page content to be updated via Ajax // If so, page content to be updated via Ajax
echo "QA_AJAX_RESPONSE\n1\n"; echo "QA_AJAX_RESPONSE\n1\n";
// 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);
// 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);
$a_view=qa_page_q_answer_view($question, $answer, ($answer['postid']==$question['selchildid']) && ($answer['type']=='A'), $usershtml = qa_userids_handles_html(array_merge(array($answer), $commentsfollows), true);
$usershtml, false); qa_sort_by($commentsfollows, 'created');
$a_view['c_list']=qa_page_q_comment_follow_list($question, $answer, $commentsfollows, false, $usershtml, false, null); $a_view = qa_page_q_answer_view($question, $answer, ($answer['postid'] == $question['selchildid'] && $answer['type'] == 'A'),
$usershtml, false);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-answer', null, null); $a_view['c_list'] = qa_page_q_comment_follow_list($question, $answer, $commentsfollows, false, $usershtml, false, null);
$themeclass->initialize();
$themeclass = qa_load_theme_class(qa_get_site_theme(), 'ajax-answer', null, null);
$themeclass->initialize();
// ... send back the HTML for it
echo "\n"; // ... send back the HTML for it
$themeclass->a_list_item($a_view); echo "\n";
}
return; $themeclass->a_list_item($a_view);
} }
}
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,78 +19,69 @@ ...@@ -20,78 +19,69 @@
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 (isset($comment)) {
$parent = $parent + qa_page_q_post_rules($parent, ($questionid == $parentid) ? null : $question, null, $children);
// in theory we should retrieve the parent's siblings for the above, but they're not going to be relevant
$comment = $comment + qa_page_q_post_rules($comment, $parent, $children, null);
// If the comment was not deleted... $usershtml = qa_userids_handles_html(array($comment), true);
if (isset($comment)) { $c_view = qa_page_q_comment_view($question, $parent, $comment, $usershtml, false);
$parent=$parent+qa_page_q_post_rules($parent, ($questionid==$parentid) ? null : $question, null, $children);
// in theory we should retrieve the parent's siblings for the above, but they're not going to be relevant
$comment=$comment+qa_page_q_post_rules($comment, $parent, $children, null);
$usershtml=qa_userids_handles_html(array($comment), true); $themeclass = qa_load_theme_class(qa_get_site_theme(), 'ajax-comment', null, null);
$themeclass->initialize();
$c_view=qa_page_q_comment_view($question, $parent, $comment, $usershtml, false);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-comment', null, null); // ... send back the HTML for it
$themeclass->initialize();
echo "\n";
// ... send back the HTML for it $themeclass->c_list_item($c_view);
echo "\n";
$themeclass->c_list_item($c_view);
}
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);
qa_sort_by($children, 'created');
$c_list=qa_page_q_comment_follow_list($question, $parent, $children, true, $usershtml, false, null); $usershtml = qa_userids_handles_html($children, true);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-comments', null, null); qa_sort_by($children, 'created');
$themeclass->initialize();
echo "QA_AJAX_RESPONSE\n1\n"; $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->initialize();
// Send back the ID of the new comment echo "QA_AJAX_RESPONSE\n1\n";
echo qa_anchor('C', $commentid)."\n";
// send back the ID of the new comment
echo qa_anchor('C', $commentid) . "\n";
// Send back the HTML
$themeclass->c_list_items($c_list['cs']); // send back the HTML
$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)) {
$cookieid = qa_cookie_get();
elseif (isset($userid)) { qa_user_favorite_set($userid, qa_get_logged_in_handle(), $cookieid, $entitytype, $entityid, $setfavorite);
$cookieid=qa_cookie_get();
qa_user_favorite_set($userid, qa_get_logged_in_handle(), $cookieid, $entitytype, $entityid, $setfavorite); $favoriteform = qa_favorite_form($entitytype, $entityid, $setfavorite, qa_lang($setfavorite ? 'main/remove_favorites' : 'main/add_favorites'));
$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->initialize();
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-favorite', null, null); echo "QA_AJAX_RESPONSE\n1\n";
$themeclass->initialize();
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,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.'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 {
$userid=qa_get_logged_in_userid();
if ($noticeid=='welcome')
qa_db_user_set_flag($userid, QA_USER_FLAGS_WELCOME_NOTICE, false);
else
qa_db_usernotice_delete($userid, $noticeid);
}
else {
$userid = qa_get_logged_in_userid();
echo "QA_AJAX_RESPONSE\n1"; if ($noticeid == 'welcome')
qa_db_user_set_flag($userid, QA_USER_FLAGS_WELCOME_NOTICE, false);
else
qa_db_usernotice_delete($userid, $noticeid);
} }
/* echo "QA_AJAX_RESPONSE\n1";
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'))) {
$state = '';
$message = qa_lang('misc/form_security_reload');
if (!qa_check_form_security_code('admin/recalc', qa_post_text('code'))) { } else {
$state=''; $state = qa_post_text('state');
$message=qa_lang('misc/form_security_reload'); $stoptime = time() + 3;
} else {
$state=qa_post_text('state');
$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 {
echo "QA_AJAX_RESPONSE\n1\n".$state."\n".qa_html($message); $state = '';
$message = qa_lang('admin/no_privileges');
}
/* 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,56 +19,57 @@ ...@@ -20,56 +19,57 @@
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);
}
$usershtml=qa_userids_handles_html($children, true);
qa_sort_by($children, 'created'); $commentsfollows = $questionid == $parentid
? qa_page_q_load_c_follows($question, $children, array(), $duplicateposts)
: qa_page_q_load_c_follows($question, array(), $children);
$c_list=qa_page_q_comment_follow_list($question, $parent, $children, true, $usershtml, false, null); $usershtml = qa_userids_handles_html($commentsfollows, true);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-comments', null, null); qa_sort_by($commentsfollows, 'created');
$themeclass->initialize();
echo "QA_AJAX_RESPONSE\n1\n"; $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->initialize();
// Send back the HTML echo "QA_AJAX_RESPONSE\n1\n";
$themeclass->c_list_items($c_list['cs']);
return; // Send back the HTML
}
$themeclass->c_list_items($c_list['cs']);
echo "QA_AJAX_RESPONSE\n0\n"; return;
}
/* 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 {
$response = qa_lang_html('admin/version_latest_unknown');
} }
} else {
$uri = qa_post_text('uri');
$version = qa_post_text('version');
$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)); qa_vote_set($post, $userid, qa_get_logged_in_handle(), $cookieid, $vote);
$voteerror=qa_vote_error_html($post, $vote, $userid, qa_request());
}
if ($voteerror===false) { $post = qa_db_select_with_pending(qa_db_full_post_selectspec($userid, $postid));
qa_vote_set($post, $userid, qa_get_logged_in_handle(), $cookieid, $vote);
$post=qa_db_select_with_pending(qa_db_full_post_selectspec($userid, $postid)); $fields = qa_post_html_fields($post, $userid, $cookieid, array(), null, array(
'voteview' => qa_get_vote_view($post, true), // behave as if on question page since the vote succeeded
));
$fields=qa_post_html_fields($post, $userid, $cookieid, array(), null, array( $themeclass = qa_load_theme_class(qa_get_site_theme(), 'voting', null, null);
'voteview' => qa_get_vote_view($post, true), // behave as if on question page since the vote succeeded $themeclass->initialize();
));
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'voting', null, null); echo "QA_AJAX_RESPONSE\n1\n";
$themeclass->initialize(); $themeclass->voting_inner_html($fields);
echo "QA_AJAX_RESPONSE\n1\n"; return;
$themeclass->voting_inner_html($fields);
} else }
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 {
$messageid = qa_wall_add_post($loginuserid, qa_get_logged_in_handle(), qa_cookie_get(),
$touseraccount['userid'], $touseraccount['handle'], $message, '');
$touseraccount['wallposts']++; // won't have been updated
else { $usermessages = qa_db_select_with_pending(qa_db_recent_messages_selectspec(null, null, $touseraccount['userid'], true, qa_opt('page_size_wall')));
$messageid=qa_wall_add_post($loginuserid, qa_get_logged_in_handle(), qa_cookie_get(), $usermessages = qa_wall_posts_add_rules($usermessages, 0);
$touseraccount['userid'], $touseraccount['handle'], $message, '');
$touseraccount['wallposts']++; // won't have been updated
$usermessages=qa_db_select_with_pending(qa_db_recent_messages_selectspec(null, null, $touseraccount['userid'], true, qa_opt('page_size_wall'))); $themeclass = qa_load_theme_class(qa_get_site_theme(), 'wall', null, null);
$usermessages=qa_wall_posts_add_rules($usermessages, 0); $themeclass->initialize();
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'wall', null, null); echo "QA_AJAX_RESPONSE\n1\n";
$themeclass->initialize();
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) {
$themeclass->message_item(qa_wall_post_view($message));
foreach ($usermessages as $message)
$themeclass->message_item(qa_wall_post_view($message));
if ($morelink && ($touseraccount['wallposts']>count($usermessages)))
$themeclass->message_item(qa_wall_view_more_link($tohandle, count($usermessages)));
} }
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-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
*/
{
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 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); }
function qa_cookie_get_create() return isset($_COOKIE['qa_id']) ? qa_gpc_to_string($_COOKIE['qa_id']) : null;
/* }
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)
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'db/cookies.php';
$cookieid=qa_cookie_get(); /**
* 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)
*/
function qa_cookie_get_create()
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
if (isset($cookieid) && qa_db_cookie_exists($cookieid)) require_once QA_INCLUDE_DIR . 'db/cookies.php';
; // cookie is valid
else
$cookieid=qa_db_cookie_create(qa_remote_ip_address());
setcookie('qa_id', $cookieid, time()+86400*365, '/', QA_COOKIE_DOMAIN); $cookieid = qa_cookie_get();
$_COOKIE['qa_id']=$cookieid;
return $cookieid; if (!isset($cookieid) || !qa_db_cookie_exists($cookieid)) {
// cookie is invalid
$cookieid = qa_db_cookie_create(qa_remote_ip_address());
} }
setcookie('qa_id', $cookieid, time() + 86400 * 365, '/', QA_COOKIE_DOMAIN, (bool)ini_get('session.cookie_secure'), true);
$_COOKIE['qa_id'] = $cookieid;
function qa_cookie_report_action($cookieid, $action) return $cookieid;
/* }
Called after a database write $action performed by a user identified by $cookieid,
relating to $questionid, $answerid and/or $commentid
*/
{
require_once QA_INCLUDE_DIR.'db/cookies.php';
qa_db_cookie_written($cookieid, qa_remote_ip_address());
}
/**
* Called after a database write $action performed by a user identified by $cookieid,
* relating to $questionid, $answerid and/or $commentid
* @param $cookieid
* @param $action
*/
function qa_cookie_report_action($cookieid, $action)
{
require_once QA_INCLUDE_DIR . 'db/cookies.php';
/* 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,159 +19,164 @@ ...@@ -20,159 +19,164 @@
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';
/**
* 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.
* @param bool $suspend
*/
function qa_suspend_notifications($suspend = true)
{
global $qa_notifications_suspended;
$qa_notifications_suspended += ($suspend ? 1 : -1);
}
/**
* 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
* 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); }
global $qa_notifications_suspended;
if ($qa_notifications_suspended > 0)
return false;
require_once QA_INCLUDE_DIR . 'db/selects.php';
require_once QA_INCLUDE_DIR . 'util/string.php';
if (isset($userid)) {
$needemail = !qa_email_validate(@$email); // take from user if invalid, e.g. @ used in practice
$needhandle = empty($handle);
if ($needemail || $needhandle) {
if (QA_FINAL_EXTERNAL_USERS) {
if ($needhandle) {
$handles = qa_get_public_from_userids(array($userid));
$handle = @$handles[$userid];
}
function qa_suspend_notifications($suspend=true) if ($needemail)
/* $email = qa_get_user_email($userid);
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.
*/
{
global $qa_notifications_suspended;
$qa_notifications_suspended+=($suspend ? 1 : -1); } else {
} $useraccount = qa_db_select_with_pending(
array(
'columns' => array('email', 'handle'),
'source' => '^users WHERE userid = #',
'arguments' => array($userid),
'single' => true,
)
);
if ($needhandle)
$handle = @$useraccount['handle'];
function qa_send_notification($userid, $email, $handle, $subject, $body, $subs, $html = false) if ($needemail)
/* $email = @$useraccount['email'];
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
corresponding value, plus applying some standard substitutions such as ^site_title, ^handle and ^email.
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_notifications_suspended;
if ($qa_notifications_suspended>0)
return false;
require_once QA_INCLUDE_DIR.'db/selects.php';
require_once QA_INCLUDE_DIR.'util/string.php';
if (isset($userid)) {
$needemail=!qa_email_validate(@$email); // take from user if invalid, e.g. @ used in practice
$needhandle=empty($handle);
if ($needemail || $needhandle) {
if (QA_FINAL_EXTERNAL_USERS) {
if ($needhandle) {
$handles=qa_get_public_from_userids(array($userid));
$handle=@$handles[$userid];
}
if ($needemail)
$email=qa_get_user_email($userid);
} else {
$useraccount=qa_db_select_with_pending(
array(
'columns' => array('email', 'handle'),
'source' => '^users WHERE userid = #',
'arguments' => array($userid),
'single' => true,
)
);
if ($needhandle)
$handle=@$useraccount['handle'];
if ($needemail)
$email=@$useraccount['email'];
}
} }
} }
if (isset($email) && qa_email_validate($email)) {
$subs['^site_title']=qa_opt('site_title');
$subs['^handle']=$handle;
$subs['^email']=$email;
$subs['^open']="\n";
$subs['^close']="\n";
return qa_send_email(array(
'fromemail' => qa_opt('from_email'),
'fromname' => qa_opt('site_title'),
'toemail' => $email,
'toname' => $handle,
'subject' => strtr($subject, $subs),
'body' => (empty($handle) ? '' : qa_lang_sub('emails/to_handle_prefix', $handle)).strtr($body, $subs),
'html' => $html,
));
} else
return false;
} }
if (isset($email) && qa_email_validate($email)) {
$subs['^site_title'] = qa_opt('site_title');
$subs['^handle'] = $handle;
$subs['^email'] = $email;
$subs['^open'] = "\n";
$subs['^close'] = "\n";
return qa_send_email(array(
'fromemail' => qa_opt('from_email'),
'fromname' => qa_opt('site_title'),
'toemail' => $email,
'toname' => $handle,
'subject' => strtr($subject, $subs),
'body' => (empty($handle) ? '' : qa_lang_sub('emails/to_handle_prefix', $handle)) . strtr($body, $subs),
'html' => $html,
));
}
function qa_send_email($params) return false;
/* }
Send the email based on the $params array - the following keys are required (some can be empty): fromemail,
fromname, toemail, toname, subject, body, html
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
// @error_log(print_r($params, true));
require_once QA_INCLUDE_DIR.'vendor/PHPMailer/PHPMailerAutoload.php';
$mailer=new PHPMailer();
$mailer->CharSet='utf-8';
$mailer->From=$params['fromemail']; /**
$mailer->Sender=$params['fromemail']; * Send the email based on the $params array - the following keys are required (some can be empty): fromemail,
$mailer->FromName=$params['fromname']; * fromname, toemail, toname, subject, body, html
$mailer->addAddress($params['toemail'], $params['toname']); * @param $params
if(!empty($params['replytoemail'])){ * @return bool
$mailer->addReplyTo($params['replytoemail'], $params['replytoname']); */
} function qa_send_email($params)
$mailer->Subject=$params['subject']; {
$mailer->Body=$params['body']; if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
if ($params['html']) // @error_log(print_r($params, true));
$mailer->isHTML(true);
if (qa_opt('smtp_active')) { require_once QA_INCLUDE_DIR . 'vendor/PHPMailer/PHPMailerAutoload.php';
$mailer->isSMTP();
$mailer->Host=qa_opt('smtp_address');
$mailer->Port=qa_opt('smtp_port');
if (qa_opt('smtp_secure')){ $mailer = new PHPMailer();
$mailer->SMTPSecure=qa_opt('smtp_secure'); $mailer->CharSet = 'utf-8';
}
else {
$mailer->SMTPOptions=array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
)
);
}
if (qa_opt('smtp_authenticate')) { $mailer->From = $params['fromemail'];
$mailer->SMTPAuth=true; $mailer->Sender = $params['fromemail'];
$mailer->Username=qa_opt('smtp_username'); $mailer->FromName = $params['fromname'];
$mailer->Password=qa_opt('smtp_password'); $mailer->addAddress($params['toemail'], $params['toname']);
} if (!empty($params['replytoemail'])) {
$mailer->addReplyTo($params['replytoemail'], $params['replytoname']);
}
$mailer->Subject = $params['subject'];
$mailer->Body = $params['body'];
if ($params['html'])
$mailer->isHTML(true);
if (qa_opt('smtp_active')) {
$mailer->isSMTP();
$mailer->Host = qa_opt('smtp_address');
$mailer->Port = qa_opt('smtp_port');
if (qa_opt('smtp_secure')) {
$mailer->SMTPSecure = qa_opt('smtp_secure');
} else {
$mailer->SMTPOptions = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true,
),
);
} }
$send_status = $mailer->send(); if (qa_opt('smtp_authenticate')) {
if(!$send_status){ $mailer->SMTPAuth = true;
@error_log('PHP Question2Answer email send error: '.$mailer->ErrorInfo); $mailer->Username = qa_opt('smtp_username');
$mailer->Password = qa_opt('smtp_password');
} }
return $send_status;
} }
$send_status = $mailer->send();
/* if (!$send_status) {
Omit PHP closing tag to help avoid accidental output @error_log('PHP Question2Answer email send error: ' . $mailer->ErrorInfo);
*/ }
return $send_status;
}
...@@ -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 . 'app/updates.php';
/**
* 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
* 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.
* @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
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
if (isset($otheruserid) && ($otheruserid != $lastuserid))
qa_db_event_create_not_entity($otheruserid, $questionid, $lastpostid, $updatetype, $lastuserid, $timestamp); // possible other user to be informed
}
/**
* 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
* to the question $questionid, and was caused by $lastuserid. Pass a unix $timestamp for the event time or leave as
* null to use now.
* @param $tagstring
* @param $questionid
* @param $updatetype
* @param $lastuserid
* @param $timestamp
*/
function qa_create_event_for_tags($tagstring, $questionid, $updatetype, $lastuserid, $timestamp = null)
{
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);
} }
}
require_once QA_INCLUDE_DIR.'db/events.php';
require_once QA_INCLUDE_DIR.'app/updates.php';
/**
* Add appropriate events to the database for an action performed on $categoryid (namely, a question being created in
function qa_create_event_for_q_user($questionid, $lastpostid, $updatetype, $lastuserid, $otheruserid=null, $timestamp=null) * 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
Add appropriate events to the database for an action performed on a question. The event of type $updatetype relates * time or leave as null to use now.
to $lastpostid whose antecedent question is $questionid, and was caused by $lastuserid. Pass a unix $timestamp for * @param $categoryid
the event time or leave as null to use now. This will add an event to $questionid's and $lastuserid's streams. If * @param $questionid
$otheruserid is set, it will also add an notification-style event for that user, unless they are the one who did it. * @param $updatetype
*/ * @param $lastuserid
{ * @param $timestamp
qa_db_event_create_for_entity(QA_ENTITY_QUESTION, $questionid, $questionid, $lastpostid, $updatetype, $lastuserid, $timestamp); // anyone who favorited the question */
function qa_create_event_for_category($categoryid, $questionid, $updatetype, $lastuserid, $timestamp = null)
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 if (isset($categoryid)) {
require_once QA_INCLUDE_DIR . 'db/selects.php';
if (isset($otheruserid) && ($otheruserid!=$lastuserid)) require_once QA_INCLUDE_DIR . 'app/format.php';
qa_db_event_create_not_entity($otheruserid, $questionid, $lastpostid, $updatetype, $lastuserid, $timestamp); // possible other user to be informed
} $categories = qa_category_path(qa_db_single_select(qa_db_category_nav_selectspec($categoryid, true)), $categoryid);
foreach ($categories as $category) {
qa_db_event_create_for_entity(QA_ENTITY_CATEGORY, $category['categoryid'], $questionid, $questionid, $updatetype, $lastuserid, $timestamp);
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
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
null to use now.
*/
{
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);
}
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
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
time or leave as null to use now.
*/
{
if (isset($categoryid)) {
require_once QA_INCLUDE_DIR.'db/selects.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);
foreach ($categories as $category)
qa_db_event_create_for_entity(QA_ENTITY_CATEGORY, $category['categoryid'], $questionid, $questionid, $updatetype, $lastuserid, $timestamp);
} }
} }
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
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,133 +19,138 @@ ...@@ -20,133 +19,138 @@
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) {
qa_opt('mailing_last_timestamp', time());
qa_opt('mailing_last_userid', '0');
qa_opt('mailing_total_users', qa_db_count_users());
qa_opt('mailing_done_users', 0);
}
}
function qa_mailing_stop() if (strlen(qa_opt('mailing_last_userid')) == 0) {
/* qa_opt('mailing_last_timestamp', time());
Stop a mailing to all users qa_opt('mailing_last_userid', '0');
*/ qa_opt('mailing_total_users', qa_db_count_users());
{ qa_opt('mailing_done_users', 0);
qa_opt('mailing_last_timestamp', '');
qa_opt('mailing_last_userid', '');
qa_opt('mailing_done_users', '');
qa_opt('mailing_total_users', '');
} }
}
function qa_mailing_perform_step() /**
/* * Stop a mailing to all users
Allow the mailing to proceed forwards, for the appropriate amount of time and users, based on the options */
*/ function qa_mailing_stop()
{ {
require_once QA_INCLUDE_DIR.'db/users.php'; qa_opt('mailing_last_timestamp', '');
qa_opt('mailing_last_userid', '');
qa_opt('mailing_done_users', '');
qa_opt('mailing_total_users', '');
}
$lastuserid=qa_opt('mailing_last_userid');
if (strlen($lastuserid)) { /**
$thistime=time(); * Allow the mailing to proceed forwards, for the appropriate amount of time and users, based on the options
$lasttime=qa_opt('mailing_last_timestamp'); */
$perminute=qa_opt('mailing_per_minute'); function qa_mailing_perform_step()
{
require_once QA_INCLUDE_DIR . 'db/users.php';
if (($lasttime-$thistime)>60) // if it's been a while, we assume there hasn't been continuous mailing... $lastuserid = qa_opt('mailing_last_userid');
$lasttime=$thistime-1; // ... so only do 1 second's worth
else // otherwise...
$lasttime=max($lasttime, $thistime-6); // ... don't do more than 6 seconds' worth
$count=min(floor(($thistime-$lasttime)*$perminute/60), 100); // don't do more than 100 messages at a time if (strlen($lastuserid)) {
$thistime = time();
$lasttime = qa_opt('mailing_last_timestamp');
$perminute = qa_opt('mailing_per_minute');
if ($count>0) { if (($lasttime - $thistime) > 60) // if it's been a while, we assume there hasn't been continuous mailing...
qa_opt('mailing_last_timestamp', $thistime+30); $lasttime = $thistime - 1; // ... so only do 1 second's worth
// prevents a parallel call to qa_mailing_perform_step() from sending messages, unless we're very unlucky with timing (poor man's mutex) else // otherwise...
$lasttime = max($lasttime, $thistime - 6); // ... don't do more than 6 seconds' worth
$sentusers=0; $count = min(floor(($thistime - $lasttime) * $perminute / 60), 100); // don't do more than 100 messages at a time
$users=qa_db_users_get_mailing_next($lastuserid, $count);
if (count($users)) { if ($count > 0) {
foreach ($users as $user) qa_opt('mailing_last_timestamp', $thistime + 30);
$lastuserid=max($lastuserid, $user['userid']); // prevents a parallel call to qa_mailing_perform_step() from sending messages, unless we're very unlucky with timing (poor man's mutex)
qa_opt('mailing_last_userid', $lastuserid); $sentusers = 0;
qa_opt('mailing_done_users', qa_opt('mailing_done_users')+count($users)); $users = qa_db_users_get_mailing_next($lastuserid, $count);
foreach ($users as $user) if (count($users)) {
if (!($user['flags'] & QA_USER_FLAGS_NO_MAILINGS)) { foreach ($users as $user) {
qa_mailing_send_one($user['userid'], $user['handle'], $user['email'], $user['emailcode']); $lastuserid = max($lastuserid, $user['userid']);
$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_userid', $lastuserid);
qa_opt('mailing_done_users', qa_opt('mailing_done_users') + count($users));
} else foreach ($users as $user) {
qa_mailing_stop(); if (!($user['flags'] & QA_USER_FLAGS_NO_MAILINGS)) {
} qa_mailing_send_one($user['userid'], $user['handle'], $user['email'], $user['emailcode']);
} $sentusers++;
} }
}
qa_opt('mailing_last_timestamp', $lasttime + $sentusers * 60 / $perminute); // can be floating point result, based on number of mails actually sent
function qa_mailing_send_one($userid, $handle, $email, $emailcode) } else
/* qa_mailing_stop();
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
*/
{
require_once QA_INCLUDE_DIR.'app/emails.php';
require_once QA_INCLUDE_DIR.'db/users.php';
if (!strlen(trim($emailcode))) {
$emailcode=qa_db_user_rand_emailcode();
qa_db_user_set($userid, 'emailcode', $emailcode);
} }
$unsubscribeurl=qa_path_absolute('unsubscribe', array('c' => $emailcode, 'u' => $handle));
return qa_send_email(array(
'fromemail' => qa_opt('mailing_from_email'),
'fromname' => qa_opt('mailing_from_name'),
'toemail' => $email,
'toname' => $handle,
'subject' => qa_opt('mailing_subject'),
'body' => trim(qa_opt('mailing_body'))."\n\n\n".qa_lang('users/unsubscribe').' '.$unsubscribeurl,
'html' => false,
));
} }
}
function qa_mailing_progress_message()
/* /**
Return a message describing current progress in the mailing * 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
{ * @param $userid
if (strlen(qa_opt('mailing_last_userid'))) * @param $handle
return strtr(qa_lang('admin/mailing_progress'), array( * @param $email
'^1' => number_format(qa_opt('mailing_done_users')), * @param $emailcode
'^2' => number_format(qa_opt('mailing_total_users')), * @return bool
)); */
else function qa_mailing_send_one($userid, $handle, $email, $emailcode)
return null; {
require_once QA_INCLUDE_DIR . 'app/emails.php';
require_once QA_INCLUDE_DIR . 'db/users.php';
if (!strlen(trim($emailcode))) {
$emailcode = qa_db_user_rand_emailcode();
qa_db_user_set($userid, 'emailcode', $emailcode);
} }
$unsubscribeurl = qa_path_absolute('unsubscribe', array('c' => $emailcode, 'u' => $handle));
return qa_send_email(array(
'fromemail' => qa_opt('mailing_from_email'),
'fromname' => qa_opt('mailing_from_name'),
'toemail' => $email,
'toname' => $handle,
'subject' => qa_opt('mailing_subject'),
'body' => trim(qa_opt('mailing_body')) . "\n\n\n" . qa_lang('users/unsubscribe') . ' ' . $unsubscribeurl,
'html' => false,
));
}
/**
* Return a message describing current progress in the mailing
*/
function qa_mailing_progress_message()
{
require_once QA_INCLUDE_DIR . 'app/format.php';
if (strlen(qa_opt('mailing_last_userid'))) {
return strtr(qa_lang('admin/mailing_progress'), array(
'^1' => qa_format_number(qa_opt('mailing_done_users')),
'^2' => qa_format_number(qa_opt('mailing_total_users')),
));
}
/* 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-search.php
Description: Wrapper functions and utilities for search modules Description: Wrapper functions and utilities for search modules
...@@ -20,121 +19,122 @@ ...@@ -20,121 +19,122 @@
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;
}
/**
* Returns $count search results for $query performed by $userid, starting at offset $start. Set $absoluteurls to true
* to get absolute URLs for the results and $fullcontent if the results should include full post content. This calls
* through to the chosen search module, and performs all the necessary post-processing to supplement the results for
* display online or in an RSS feed.
* @param $query
* @param $start
* @param $count
* @param $userid
* @param $absoluteurls
* @param $fullcontent
* @return
*/
function qa_get_search_results($query, $start, $count, $userid, $absoluteurls, $fullcontent)
{
// Identify which search module should be used
$searchmodules = qa_load_modules_with('search', 'process_search');
if (!count($searchmodules))
qa_fatal_error('No search engine is available');
$module = reset($searchmodules); // use first one by default
if (count($searchmodules) > 1) {
$tryname = qa_opt('search_module'); // use chosen one if it's available
if (isset($searchmodules[$tryname]))
$module = $searchmodules[$tryname];
} }
// Get the results
function qa_get_search_results($query, $start, $count, $userid, $absoluteurls, $fullcontent) $results = $module->process_search($query, $start, $count, $userid, $absoluteurls, $fullcontent);
/*
Returns $count search results for $query performed by $userid, starting at offset $start. Set $absoluteurls to true
to get absolute URLs for the results and $fullcontent if the results should include full post content. This calls
through to the chosen search module, and performs all the necessary post-processing to supplement the results for
display online or in an RSS feed.
*/
{
// Identify which search module should be used // Work out what additional information (if any) we need to retrieve for the results
$searchmodules=qa_load_modules_with('search', 'process_search'); $keypostidgetfull = array();
$keypostidgettype = array();
$keypostidgetquestion = array();
$keypageidgetpage = array();
if (!count($searchmodules)) foreach ($results as $result) {
qa_fatal_error('No search engine is available'); if (isset($result['question_postid']) && !isset($result['question']))
$keypostidgetfull[$result['question_postid']] = true;
$module=reset($searchmodules); // use first one by default if (isset($result['match_postid'])) {
if (!((isset($result['question_postid'])) || (isset($result['question']))))
$keypostidgetquestion[$result['match_postid']] = true; // we can also get $result['match_type'] from this
if (count($searchmodules)>1) { elseif (!isset($result['match_type']))
$tryname=qa_opt('search_module'); // use chosen one if it's available $keypostidgettype[$result['match_postid']] = true;
if (isset($searchmodules[$tryname]))
$module=$searchmodules[$tryname];
} }
// Get the results if (isset($result['page_pageid']) && !isset($result['page']))
$keypageidgetpage[$result['page_pageid']] = true;
}
$results=$module->process_search($query, $start, $count, $userid, $absoluteurls, $fullcontent); // Perform the appropriate database queries
// Work out what additional information (if any) we need to retrieve for the results list($postidfull, $postidtype, $postidquestion, $pageidpage) = qa_db_select_with_pending(
count($keypostidgetfull) ? qa_db_posts_selectspec($userid, array_keys($keypostidgetfull), $fullcontent) : null,
count($keypostidgettype) ? qa_db_posts_basetype_selectspec(array_keys($keypostidgettype)) : null,
count($keypostidgetquestion) ? qa_db_posts_to_qs_selectspec($userid, array_keys($keypostidgetquestion), $fullcontent) : null,
count($keypageidgetpage) ? qa_db_pages_selectspec(null, array_keys($keypageidgetpage)) : null
);
$keypostidgetfull=array(); // Supplement the results as appropriate
$keypostidgettype=array();
$keypostidgetquestion=array();
$keypageidgetpage=array();
foreach ($results as $result) { foreach ($results as $key => $result) {
if (isset($result['question_postid']) && !isset($result['question'])) if (isset($result['question_postid']) && !isset($result['question']))
$keypostidgetfull[$result['question_postid']]=true; if (@$postidfull[$result['question_postid']]['basetype'] == 'Q')
$result['question'] = @$postidfull[$result['question_postid']];
if (isset($result['match_postid'])) { if (isset($result['match_postid'])) {
if (!( (isset($result['question_postid'])) || (isset($result['question'])) )) if (!(isset($result['question_postid']) || isset($result['question']))) {
$keypostidgetquestion[$result['match_postid']]=true; // we can also get $result['match_type'] from this $result['question'] = @$postidquestion[$result['match_postid']];
elseif (!isset($result['match_type'])) if (!isset($result['match_type']))
$keypostidgettype[$result['match_postid']]=true; $result['match_type'] = @$result['question']['obasetype'];
}
if (isset($result['page_pageid']) && !isset($result['page'])) } elseif (!isset($result['match_type']))
$keypageidgetpage[$result['page_pageid']]=true; $result['match_type'] = @$postidtype[$result['match_postid']];
} }
// Perform the appropriate database queries if (isset($result['question']) && !isset($result['question_postid']))
$result['question_postid'] = $result['question']['postid'];
list($postidfull, $postidtype, $postidquestion, $pageidpage)=qa_db_select_with_pending(
count($keypostidgetfull) ? qa_db_posts_selectspec($userid, array_keys($keypostidgetfull), $fullcontent) : null,
count($keypostidgettype) ? qa_db_posts_basetype_selectspec(array_keys($keypostidgettype)) : null,
count($keypostidgetquestion) ? qa_db_posts_to_qs_selectspec($userid, array_keys($keypostidgetquestion), $fullcontent) : null,
count($keypageidgetpage) ? qa_db_pages_selectspec(null, array_keys($keypageidgetpage)) : null
);
// Supplement the results as appropriate
foreach ($results as $key => $result) {
if (isset($result['question_postid']) && !isset($result['question']))
if (@$postidfull[$result['question_postid']]['basetype']=='Q')
$result['question']=@$postidfull[$result['question_postid']];
if (isset($result['match_postid'])) {
if (!( (isset($result['question_postid'])) || (isset($result['question'])) )) {
$result['question']=@$postidquestion[$result['match_postid']];
if (!isset($result['match_type'])) if (isset($result['page_pageid']) && !isset($result['page']))
$result['match_type']=@$result['question']['obasetype']; $result['page'] = @$pageidpage[$result['page_pageid']];
} elseif (!isset($result['match_type'])) if (!isset($result['title'])) {
$result['match_type']=@$postidtype[$result['match_postid']]; if (isset($result['question']))
} $result['title'] = $result['question']['title'];
elseif (isset($result['page']))
if (isset($result['question']) && !isset($result['question_postid'])) $result['title'] = $result['page']['heading'];
$result['question_postid']=$result['question']['postid'];
if (isset($result['page_pageid']) && !isset($result['page']))
$result['page']=@$pageidpage[$result['page_pageid']];
if (!isset($result['title'])) {
if (isset($result['question']))
$result['title']=$result['question']['title'];
elseif (isset($result['page']))
$result['title']=$result['page']['heading'];
}
if (!isset($result['url'])) {
if (isset($result['question']))
$result['url']=qa_q_path($result['question']['postid'], $result['question']['title'],
$absoluteurls, @$result['match_type'], @$result['match_postid']);
elseif (isset($result['page']))
$result['url']=qa_path($result['page']['tags'], null, qa_opt('site_url'));
}
$results[$key]=$result;
} }
// Return the results if (!isset($result['url'])) {
if (isset($result['question']))
$result['url'] = qa_q_path($result['question']['postid'], $result['question']['title'],
$absoluteurls, @$result['match_type'], @$result['match_postid']);
elseif (isset($result['page']))
$result['url'] = qa_path($result['page']['tags'], null, qa_opt('site_url'));
}
return $results; $results[$key] = $result;
} }
// Return the results
/* return $results;
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-updates.php
Description: Definitions relating to favorites and updates in the database tables Description: Definitions relating to favorites and updates in the database tables
...@@ -20,39 +19,35 @@ ...@@ -20,39 +19,35 @@
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;
} }
// Character codes for the different types of entity that can be followed (entitytype columns) // Character codes for the different types of entity that can be followed (entitytype columns)
define('QA_ENTITY_QUESTION', 'Q'); define('QA_ENTITY_QUESTION', 'Q');
define('QA_ENTITY_USER', 'U'); define('QA_ENTITY_USER', 'U');
define('QA_ENTITY_TAG', 'T'); define('QA_ENTITY_TAG', 'T');
define('QA_ENTITY_CATEGORY', 'C'); define('QA_ENTITY_CATEGORY', 'C');
define('QA_ENTITY_NONE', '-'); define('QA_ENTITY_NONE', '-');
// Character codes for the different types of updates on a post (updatetype columns) // Character codes for the different types of updates on a post (updatetype columns)
define('QA_UPDATE_CATEGORY', 'A'); // questions only, category changed define('QA_UPDATE_CATEGORY', 'A'); // questions only, category changed
define('QA_UPDATE_CLOSED', 'C'); // questions only, closed or reopened define('QA_UPDATE_CLOSED', 'C'); // questions only, closed or reopened
define('QA_UPDATE_CONTENT', 'E'); // title or content edited define('QA_UPDATE_CONTENT', 'E'); // title or content edited
define('QA_UPDATE_PARENT', 'M'); // e.g. comment moved when converting its parent answer to a comment define('QA_UPDATE_PARENT', 'M'); // e.g. comment moved when converting its parent answer to a comment
define('QA_UPDATE_SELECTED', 'S'); // answers only, removed if unselected define('QA_UPDATE_SELECTED', 'S'); // answers only, removed if unselected
define('QA_UPDATE_TAGS', 'T'); // questions only define('QA_UPDATE_TAGS', 'T'); // questions only
define('QA_UPDATE_TYPE', 'Y'); // e.g. answer to comment define('QA_UPDATE_TYPE', 'Y'); // e.g. answer to comment
define('QA_UPDATE_VISIBLE', 'H'); // hidden or reshown define('QA_UPDATE_VISIBLE', 'H'); // hidden or reshown
// Character codes for types of update that only appear in the streams tables, not on the posts themselves // Character codes for types of update that only appear in the streams tables, not on the posts themselves
define('QA_UPDATE_FOLLOWS', 'F'); // if a new question was asked related to one of its answers, or for a comment that follows another define('QA_UPDATE_FOLLOWS', 'F'); // if a new question was asked related to one of its answers, or for a comment that follows another
define('QA_UPDATE_C_FOR_Q', 'U'); // if comment created was on a question of the user whose stream this appears in define('QA_UPDATE_C_FOR_Q', 'U'); // if comment created was on a question of the user whose stream this appears in
define('QA_UPDATE_C_FOR_A', 'N'); // if comment created was on an answer of the user whose stream this appears in define('QA_UPDATE_C_FOR_A', 'N'); // if comment created was on an answer of the user whose stream this appears in
/*
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-db-blobs.php
Description: Database-level access to blobs table for large chunks of data (e.g. images) Description: Database-level access to blobs table for large chunks of data (e.g. images)
...@@ -20,91 +19,103 @@ ...@@ -20,91 +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_db_blob_create($content, $format, $sourcefilename=null, $userid=null, $cookieid=null, $ip=null)
/*
Create a new blob in the database with $content and $format, other fields as provided
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
for ($attempt=0; $attempt<10; $attempt++) {
$blobid=qa_db_random_bigint();
if (qa_db_blob_exists($blobid))
continue;
qa_db_query_sub(
'INSERT INTO ^blobs (blobid, format, content, filename, userid, cookieid, createip, created) VALUES (#, $, $, $, $, #, INET_ATON($), NOW())',
$blobid, $format, $content, $sourcefilename, $userid, $cookieid, $ip
);
return $blobid;
}
return null; /**
} * Create a new blob in the database with $content and $format, other fields as provided
* @param $content
* @param $format
function qa_db_blob_read($blobid) * @param $sourcefilename
/* * @param $userid
Get the information about blob $blobid from the database * @param $cookieid
*/ * @param $ip
{ * @return mixed|null|string
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } */
function qa_db_blob_create($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); }
return qa_db_read_one_assoc(qa_db_query_sub( for ($attempt = 0; $attempt < 10; $attempt++) {
'SELECT content, format, filename FROM ^blobs WHERE blobid=#', $blobid = qa_db_random_bigint();
$blobid
), true);
}
if (qa_db_blob_exists($blobid))
continue;
function qa_db_blob_set_content($blobid, $content)
/*
Change the content of blob $blobid in the database to $content (can also be null)
*/
{
qa_db_query_sub( qa_db_query_sub(
'UPDATE ^blobs SET content=$ WHERE blobid=#', 'INSERT INTO ^blobs (blobid, format, content, filename, userid, cookieid, createip, created) VALUES (#, $, $, $, $, #, UNHEX($), NOW())',
$content, $blobid $blobid, $format, $content, $sourcefilename, $userid, $cookieid, bin2hex(@inet_pton($ip))
); );
}
function qa_db_blob_delete($blobid)
/*
Delete blob $blobid in the database
*/
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
qa_db_query_sub( return $blobid;
'DELETE FROM ^blobs WHERE blobid=#',
$blobid
);
} }
return null;
function qa_db_blob_exists($blobid) }
/*
Check if blob $blobid exists in the database
*/ /**
{ * Get the information about blob $blobid from the database
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } * @param $blobid
* @return array|mixed|null
return qa_db_read_one_value(qa_db_query_sub( */
'SELECT COUNT(*) FROM ^blobs WHERE blobid=#', function qa_db_blob_read($blobid)
$blobid {
)) > 0; if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
}
return qa_db_read_one_assoc(qa_db_query_sub(
'SELECT content, format, filename FROM ^blobs WHERE blobid=#',
/* $blobid
Omit PHP closing tag to help avoid accidental output ), true);
*/ }
\ No newline at end of file
/**
* Change the content of blob $blobid in the database to $content (can also be null)
* @param $blobid
* @param $content
*/
function qa_db_blob_set_content($blobid, $content)
{
qa_db_query_sub(
'UPDATE ^blobs SET content=$ WHERE blobid=#',
$content, $blobid
);
}
/**
* Delete blob $blobid in the database
* @param $blobid
* @return mixed
*/
function qa_db_blob_delete($blobid)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
qa_db_query_sub(
'DELETE FROM ^blobs WHERE blobid=#',
$blobid
);
}
/**
* Check if blob $blobid exists in the database
* @param $blobid
* @return bool|mixed
*/
function qa_db_blob_exists($blobid)
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$blob = qa_db_read_one_value(qa_db_query_sub(
'SELECT COUNT(*) FROM ^blobs WHERE blobid=#',
$blobid
));
return $blob > 0;
}
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.
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.
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.
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