Commit 438f4872 by Gideon Greenspan

1.5-beta-1

parents
DirectoryIndex index.php
RewriteEngine On
#RewriteBase /your-sub-directory
RewriteCond %{REQUEST_URI} ^(.*)//(.*)$
RewriteRule . %1/%2 [R=301,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ index.php?qa-rewrite=$0&%{QUERY_STRING} [L]
<HTML>
<HEAD>
<META HTTP-EQUIV="Refresh" CONTENT="2;URL=http://www.question2answer.org/versions.php">
</HEAD>
<BODY>
Redirecting... if nothing happens, <A HREF="http://www.question2answer.org/versions.php">click here</A>.
</BODY>
</HTML>
\ No newline at end of file
<HTML>
<HEAD>
<META HTTP-EQUIV="Refresh" CONTENT="2;URL=http://www.question2answer.org/license.php">
</HEAD>
<BODY>
Redirecting... if nothing happens, <A HREF="http://www.question2answer.org/license.php">click here</A>.
</BODY>
</HTML>
\ No newline at end of file
<HTML>
<HEAD>
<META HTTP-EQUIV="Refresh" CONTENT="2;URL=http://www.question2answer.org/install.php">
</HEAD>
<BODY>
Redirecting... if nothing happens, <A HREF="http://www.question2answer.org/install.php">click here</A>.
</BODY>
</HTML>
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: index.php
Version: See define()s at top of qa-include/qa-base.php
Description: A stub that only sets up the Q2A root and includes qa-index.php
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
*/
// 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']).'/');
require 'qa-include/qa-index.php';
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-config-example.php
Version: See define()s at top of qa-include/qa-base.php
Description: After renaming, use this to set up database details and other stuff
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
*/
/*
======================================================================
THE 4 DEFINITIONS BELOW ARE REQUIRED AND MUST BE SET BEFORE USING!
======================================================================
*/
define('QA_MYSQL_HOSTNAME', '127.0.0.1'); // try '127.0.0.1' or 'localhost' if MySQL on same server
define('QA_MYSQL_USERNAME', 'your-mysql-username');
define('QA_MYSQL_PASSWORD', 'your-mysql-password');
define('QA_MYSQL_DATABASE', 'your-mysql-db-name');
/*
Ultra-concise installation instructions:
1. Create a MySQL database.
2. Create a MySQL user with full permissions for that database.
3. Rename this file to qa-config.php.
4. Set the above four definitions and save.
5. Place all the Question2Answer files on your server.
6. Open the appropriate URL, and follow the instructions.
More detailed installation instructions here: http://www.question2answer.org/
*/
/*
======================================================================
OPTIONAL CONSTANT DEFINITIONS, INCLUDING SUPPORT FOR SINGLE SIGN-ON
======================================================================
QA_MYSQL_TABLE_PREFIX will be added to table names, to allow multiple datasets in a single
MySQL database, or to include the Question2Answer tables in an existing MySQL database.
*/
define('QA_MYSQL_TABLE_PREFIX', 'qa_');
/*
If you wish, you can define QA_MYSQL_USERS_PREFIX separately from QA_MYSQL_TABLE_PREFIX.
If so, it is used instead of QA_MYSQL_TABLE_PREFIX as the prefix for tables containing
information about user accounts (not including users' activity and points). This allows
multiple Q2A sites to have shared logins and users, but separate posts and activity.
define('QA_MYSQL_USERS_PREFIX', 'qa_users_');
*/
/*
If you wish, you can define QA_COOKIE_DOMAIN so that any cookies created by Q2A are assigned
to a specific domain name, instead of the full domain name of the request by default. This is
useful if you're running multiple Q2A sites on subdomains with a shared user base.
define('QA_COOKIE_DOMAIN', '.example.com'); // be sure to keep the leading period
*/
/*
If you wish, you can define an array $QA_CONST_PATH_MAP to modify the URLs used in your Q2A site.
The key of each array element should be the standard part of the path, e.g. 'questions',
and the value should be the replacement for that standard part, e.g. 'topics'. If you edit this
file in UTF-8 encoding you can also use non-ASCII characters in these URLs.
$QA_CONST_PATH_MAP=array(
'questions' => 'topics',
'categories' => 'sections',
'users' => 'contributors',
'user' => 'contributor',
);
*/
/*
Set QA_EXTERNAL_USERS to true to use your user identification code in qa-external/qa-external-users.php
This allows you to integrate with your existing user database and management system. For more details,
consult the online documentation on installing Question2Answer with single sign-on.
The constants QA_EXTERNAL_LANG and QA_EXTERNAL_EMAILER are deprecated from Q2A 1.5 since the same
effect can now be achieved in plugins by using function overrides.
*/
define('QA_EXTERNAL_USERS', false);
/*
Out-of-the-box WordPress 3.x integration - to integrate with your WordPress site and user
database, define QA_WORDPRESS_INTEGRATE_PATH as the full path to the WordPress directory
containing wp-load.php. You do not need to set the QA_MYSQL_* constants above since these
will be taken from WordPress automatically. See online documentation for more details.
define('QA_WORDPRESS_INTEGRATE_PATH', '/PATH/TO/WORDPRESS');
*/
/*
Some settings to help optimize your Question2Answer site's performance.
If QA_HTML_COMPRESSION is true, HTML web pages will be output using Gzip compression, if
the user's browser indicates this is supported. This will increase the performance of your
site, but may make debugging harder if PHP does not complete execution.
QA_MAX_LIMIT_START is the maximum start parameter that can be requested, for paging through
long lists of questions, etc... As the start parameter gets higher, queries tend to get
slower, since MySQL must examine more information. Very high start numbers are usually only
requested by search engine robots anyway.
If a word is used QA_IGNORED_WORDS_FREQ times or more in a particular way, it is ignored
when searching or finding related questions. This saves time by ignoring words which are so
common that they are probably not worth matching on.
Set QA_ALLOW_UNINDEXED_QUERIES to true if you don't mind running some database queries which
are not indexed efficiently. For example, this will enable browsing unanswered questions per
category. If your database becomes large, these queries could become costly.
Set QA_OPTIMIZE_LOCAL_DB to true if your web server and MySQL are running on the same box.
When viewing a page on your site, this will use many simple MySQL queries instead of fewer
complex ones, which makes sense since there is no latency for localhost access.
Set QA_OPTIMIZE_DISTANT_DB to true if your web server and MySQL are far enough apart to
create significant latency. This will minimize the number of database queries as much as
is possible, even at the cost of significant additional processing at each end.
Set QA_PERSISTENT_CONN_DB to true to use persistent database connections. Only use this if
you are absolutely sure it is a good idea under your setup - generally it is not.
For more information: http://www.php.net/manual/en/features.persistent-connections.php
Set QA_DEBUG_PERFORMANCE to true to show detailed performance profiling information at the
bottom of every Question2Answer page.
*/
define('QA_HTML_COMPRESSION', true);
define('QA_MAX_LIMIT_START', 19999);
define('QA_IGNORED_WORDS_FREQ', 10000);
define('QA_ALLOW_UNINDEXED_QUERIES', false);
define('QA_OPTIMIZE_LOCAL_DB', false);
define('QA_OPTIMIZE_DISTANT_DB', false);
define('QA_PERSISTENT_CONN_DB', false);
define('QA_DEBUG_PERFORMANCE', false);
/*
And lastly... if you want to, you can predefine any constant from qa-db-maxima.php in this
file to override the default setting. Just make sure you know what you're doing!
*/
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
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
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
*/
var qa_recalc_running=0;
window.onbeforeunload=function(event)
{
if (qa_recalc_running>0) {
event=event||window.event;
var message=qa_warning_recalc;
event.returnValue=message;
return message;
}
}
function qa_recalc_click(state, elem, value, noteid)
{
if (elem.qa_recalc_running) {
elem.qa_recalc_stopped=true;
} else {
elem.qa_recalc_running=true;
elem.qa_recalc_stopped=false;
qa_recalc_running++;
document.getElementById(noteid).innerHTML='';
elem.qa_original_value=elem.value;
if (value)
elem.value=value;
qa_recalc_update(elem, state, noteid);
}
return false;
}
function qa_recalc_update(elem, state, noteid)
{
if (state)
qa_ajax_post('recalc', {state:state},
function(lines) {
if (lines[0]=='1') {
if (lines[2])
document.getElementById(noteid).innerHTML=lines[2];
if (elem.qa_recalc_stopped)
qa_recalc_cleanup(elem);
else
qa_recalc_update(elem, lines[1], noteid);
} else if (lines[0]=='0') {
document.getElementById(noteid).innerHTML=lines[2];
qa_recalc_cleanup(elem);
} else {
qa_ajax_error();
qa_recalc_cleanup(elem);
}
}
);
else
qa_recalc_cleanup(elem);
}
function qa_recalc_cleanup(elem)
{
elem.value=elem.qa_original_value;
elem.qa_recalc_running=null;
qa_recalc_running--;
}
function qa_mailing_start(noteid, pauseid)
{
qa_ajax_post('mailing', {},
function (lines) {
if (lines[0]=='1') {
document.getElementById(noteid).innerHTML=lines[1];
window.setTimeout(function() { qa_mailing_start(noteid, pauseid); }, 1); // don't recurse
} else if (lines[0]=='0') {
document.getElementById(noteid).innerHTML=lines[1];
document.getElementById(pauseid).style.display='none';
} else {
qa_ajax_error();
}
}
);
}
function qa_admin_click(target)
{
var p=target.name.split('_');
var params={postid:p[1], action:p[2]};
qa_ajax_post('click_admin', params,
function (lines) {
if (lines[0]=='1') {
qa_conceal(document.getElementById('p'+p[1]), 'q_item');
} else {
qa_ajax_error();
}
}
);
return false;
}
function qa_version_check(uri, versionkey, version, urikey, elem)
{
var params={uri:uri, versionkey:versionkey, version:version, urikey:urikey};
qa_ajax_post('version', params,
function (lines) {
if (lines[0]=='1')
document.getElementById(elem).innerHTML=lines[1];
}
);
}
\ No newline at end of file
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-content/qa-ask.js
Version: See define()s at top of qa-include/qa-base.php
Description: Javascript for ask page and question editing, including tag auto-completion
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
*/
function qa_title_change(value)
{
qa_ajax_post('asktitle', {title:value}, function(lines) {
if (lines[0]=='1') {
if (lines[1].length) {
qa_tags_examples=lines[1];
qa_tag_hints();
}
if (lines.length>2) {
var simelem=document.getElementById('similar');
if (simelem)
simelem.innerHTML=lines.slice(2).join('\n');
}
} else if (lines[0]=='0')
alert(lines[1]);
else
qa_ajax_error();
});
}
function qa_tag_click(link)
{
var elem=document.getElementById('tags');
var parts=qa_tag_typed_parts(elem);
// removes any HTML tags and ampersand
var tag=link.innerHTML.replace(/<[^>]*>/g, '').replace('&amp;', '&');
var separator=qa_tag_onlycomma ? ', ' : ' ';
// replace if matches typed, otherwise append
var newvalue=(parts.typed && (tag.toLowerCase().indexOf(parts.typed.toLowerCase())>=0))
? (parts.before+separator+tag+separator+parts.after+separator) : (elem.value+separator+tag+separator);
// sanitize and set value
if (qa_tag_onlycomma)
elem.value=newvalue.replace(/[\s,]*,[\s,]*/g, ', ').replace(/^[\s,]+/g, '');
else
elem.value=newvalue.replace(/[\s,]+/g, ' ').replace(/^[\s,]+/g, '');
elem.focus();
qa_tag_hints();
return false;
}
function qa_tag_hints(skipcomplete)
{
var elem=document.getElementById('tags');
var parts=qa_tag_typed_parts(elem);
var html='';
var completed=false;
// first try to auto-complete
if (parts.typed && qa_tags_complete) {
html=qa_tags_to_html((qa_tags_examples+','+qa_tags_complete).split(','), parts.typed.toLowerCase().replace('&', '&amp;'));
completed=html ? true : false;
}
// otherwise show examples
if (qa_tags_examples && !completed)
html=qa_tags_to_html(qa_tags_examples.split(','), null);
// set title visiblity and hint list
document.getElementById('tag_examples_title').style.display=(html && !completed) ? '' : 'none';
document.getElementById('tag_complete_title').style.display=(html && completed) ? '' : 'none';
document.getElementById('tag_hints').innerHTML=html;
}
function qa_tags_to_html(tags, matchlc)
{
var html='';
var added=0;
var tagseen={};
for (var i=0; i<tags.length; i++) {
var tag=tags[i];
var taglc=tag.toLowerCase();
if (!tagseen[taglc]) {
tagseen[taglc]=true;
if ( (!matchlc) || (taglc.indexOf(matchlc)>=0) ) { // match if necessary
if (matchlc) { // if matching, show appropriate part in bold
var matchstart=taglc.indexOf(matchlc);
var matchend=matchstart+matchlc.length;
inner='<SPAN STYLE="font-weight:normal;">'+tag.substring(0, matchstart)+'<B>'+
tag.substring(matchstart, matchend)+'</B>'+tag.substring(matchend)+'</SPAN>';
} else // otherwise show as-is
inner=tag;
html+=qa_tag_template.replace(/\^/g, inner.replace('$', '$$$$'))+' '; // replace ^ in template, escape $s
if (++added>=qa_tags_max)
break;
}
}
}
return html;
}
function qa_caret_from_end(elem)
{
if (document.selection) { // for IE
elem.focus();
var sel=document.selection.createRange();
sel.moveStart('character', -elem.value.length);
return elem.value.length-sel.text.length;
} else if (typeof(elem.selectionEnd)!='undefined') // other browsers
return elem.value.length-elem.selectionEnd;
else // by default return safest value
return 0;
}
function qa_tag_typed_parts(elem)
{
var caret=elem.value.length-qa_caret_from_end(elem);
var active=elem.value.substring(0, caret);
var passive=elem.value.substring(active.length);
// if the caret is in the middle of a word, move the end of word from passive to active
if (
active.match(qa_tag_onlycomma ? /[^\s,][^,]*$/ : /[^\s,]$/) &&
(adjoinmatch=passive.match(qa_tag_onlycomma ? /^[^,]*[^\s,][^,]*/ : /^[^\s,]+/))
) {
active+=adjoinmatch[0];
passive=elem.value.substring(active.length);
}
// find what has been typed so far
var typedmatch=active.match(qa_tag_onlycomma ? /[^\s,]+[^,]*$/ : /[^\s,]+$/) || [''];
return {before:active.substring(0, active.length-typedmatch[0].length), after:passive, typed:typedmatch[0]};
}
function qa_category_select(idprefix, startpath)
{
var startval=startpath ? startpath.split("/") : [];
var setdescnow=true;
for (var l=0; l<=qa_cat_maxdepth; l++) {
var elem=document.getElementById(idprefix+'_'+l);
if (elem) {
if (l) {
if (l<startval.length && startval[l].length) {
var val=startval[l];
for (var j=0; j<elem.options.length; j++)
if (elem.options[j].value==val)
elem.selectedIndex=j;
} else
var val=elem.options[elem.selectedIndex].value;
} else
val='';
if (elem.qa_last_sel!==val) {
elem.qa_last_sel=val;
var subelem=document.getElementById(idprefix+'_'+l+'_sub');
if (subelem)
subelem.parentNode.removeChild(subelem);
if (val.length || (l==0)) {
subelem=elem.parentNode.insertBefore(document.createElement('span'), elem.nextSibling);
subelem.id=idprefix+'_'+l+'_sub';
subelem.innerHTML=' ...';
qa_ajax_post('category', {categoryid:val},
(function(elem, l) {
return function(lines) {
var subelem=document.getElementById(idprefix+'_'+l+'_sub');
if (subelem)
subelem.parentNode.removeChild(subelem);
if (lines[0]=='1') {
elem.qa_cat_desc=lines[1];
var addedoption=false;
if (lines.length>2) {
var subelem=elem.parentNode.insertBefore(document.createElement('span'), elem.nextSibling);
subelem.id=idprefix+'_'+l+'_sub';
subelem.innerHTML=' ';
var newelem=elem.cloneNode(false);
newelem.name=newelem.id=idprefix+'_'+(l+1);
newelem.options.length=0;
if (l ? qa_cat_allownosub : qa_cat_allownone)
newelem.options[0]=new Option(l ? '' : elem.options[0].text, '', true, true);
for (var i=2; i<lines.length; i++) {
var parts=lines[i].split('/');
if (String(qa_cat_exclude).length && (String(qa_cat_exclude)==parts[0]))
continue;
newelem.options[newelem.options.length]=new Option(parts[1], parts[0]);
addedoption=true;
}
if (addedoption) {
subelem.appendChild(newelem);
qa_category_select(idprefix, startpath);
}
if (l==0)
elem.style.display='none';
}
if (!addedoption)
set_category_description(idprefix);
} else if (lines[0]=='0')
alert(lines[1]);
else
qa_ajax_error();
}
})(elem, l)
);
setdescnow=false;
}
break;
}
}
}
if (setdescnow)
set_category_description(idprefix);
}
function set_category_description(idprefix)
{
var n=document.getElementById(idprefix+'_note');
if (n) {
desc='';
for (var l=1; l<=qa_cat_maxdepth; l++) {
var elem=document.getElementById(idprefix+'_'+l);
if (elem && elem.options[elem.selectedIndex].value.length)
desc=elem.qa_cat_desc;
}
n.innerHTML=desc;
}
}
\ No newline at end of file
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-content/qa-page.js
Version: See define()s at top of qa-include/qa-base.php
Description: Common Javascript including voting, notices and favorites
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
*/
function qa_reveal(elem, type, callback)
{
if (elem)
$(elem).slideDown(400, callback);
}
function qa_conceal(elem, type, callback)
{
if (elem)
$(elem).slideUp(400);
}
function qa_set_inner_html(elem, type, html)
{
elem.innerHTML=html;
}
function qa_set_outer_html(elem, type, html)
{
if (elem) {
var e=document.createElement('DIV');
e.innerHTML=html;
elem.parentNode.replaceChild(e.firstChild, elem);
}
}
function qa_vote_click(elem)
{
var ens=elem.name.split('_');
var postid=ens[1];
var vote=parseInt(ens[2]);
var anchor=ens[3];
qa_ajax_post('vote', {postid:postid, vote:vote},
function(lines) {
if (lines[0]=='1') {
qa_set_inner_html(document.getElementById('voting_'+postid), 'voting', lines.slice(1).join("\n"));
} else if (lines[0]=='0') {
var mess=document.getElementById('errorbox');
if (!mess) {
var mess=document.createElement('div');
mess.id='errorbox';
mess.className='qa-error';
mess.innerHTML=lines[1];
mess.style.display='none';
}
var postelem=document.getElementById(anchor);
var e=postelem.parentNode.insertBefore(mess, postelem);
qa_reveal(e);
} else
qa_ajax_error();
}
);
return false;
}
function qa_notice_click(elem)
{
var ens=elem.name.split('_');
qa_ajax_post('notice', {noticeid:ens[1]},
function(lines) {
if (lines[0]=='1')
qa_conceal(document.getElementById('notice_'+ens[1]), 'notice');
else
qa_ajax_error();
}
);
return false;
}
function qa_favorite_click(elem)
{
var ens=elem.name.split('_');
qa_ajax_post('favorite', {entitytype:ens[1], entityid:ens[2], favorite:parseInt(ens[3])},
function (lines) {
if (lines[0]=='1')
qa_set_inner_html(document.getElementById('favoriting'), 'favoriting', lines.slice(1).join("\n"));
else
qa_ajax_error();
}
);
return false;
}
function qa_ajax_post(operation, params, callback)
{
jQuery.extend(params, {qa:'ajax', qa_operation:operation, qa_root:qa_root, qa_request:qa_request});
jQuery.post(qa_root, params, function(response) {
var header='QA_AJAX_RESPONSE';
var headerpos=response.indexOf(header);
if (headerpos>=0)
callback(response.substr(headerpos+header.length).replace(/^\s+/, '').split("\n"));
else
callback([]);
}, 'text').error(function(jqXHR) { if (jqXHR.readyState>0) callback([]) });
}
function qa_ajax_error()
{
alert('Unexpected response from server - please try again or switch off Javascript.');
}
\ No newline at end of file
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-content/qa-question.js
Version: See define()s at top of qa-include/qa-base.php
Description: Javascript to handle question page actions
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
*/
var qa_element_revealed=null;
function qa_toggle_element(elem)
{
var e=elem ? document.getElementById(elem) : null;
if (e && e.qa_disabled)
e=null;
if (e && (qa_element_revealed==e)) {
qa_conceal(qa_element_revealed, 'form');
qa_element_revealed=null;
} else {
if (qa_element_revealed)
qa_conceal(qa_element_revealed, 'form');
if (e) {
if (e.qa_load && !e.qa_loaded) {
e.qa_load();
e.qa_loaded=true;
}
if (e.qa_show)
e.qa_show();
qa_reveal(e, 'form', function() {
var t=$(e).offset().top;
var h=$(e).height()+16;
var wt=$(window).scrollTop();
var wh=$(window).height();
if ( (t<wt) || (t>(wt+wh)) )
qa_scroll_page_to(t);
else if ((t+h)>(wt+wh))
qa_scroll_page_to(t+h-wh);
if (e.qa_focus)
e.qa_focus();
});
}
qa_element_revealed=e;
}
return !(e||!elem); // failed to find item
}
function qa_submit_answer(questionid)
{
var params=qa_form_params('a_form');
params.a_questionid=questionid;
qa_ajax_post('answer', params,
function(lines) {
if (lines[0]=='1') {
if (lines[1]<1) {
var b=document.getElementById('q_doanswer');
if (b)
b.style.display='none';
}
qa_set_inner_html(document.getElementById('a_list_title'), 'a_list_title', lines[2]);
var e=document.createElement('DIV');
e.innerHTML=lines.slice(3).join("\n");
var c=e.firstChild;
c.style.display='none';
var l=document.getElementById('a_list');
l.insertBefore(c, l.firstChild);
var a=document.getElementById('anew');
a.qa_disabled=true;
qa_reveal(c, 'answer');
qa_conceal(a, 'form');
} else if (lines[0]=='0') {
// document.forms['q_page_form'].elements['a_doadd2'].value=1;
document.forms['a_form'].submit();
} else {
qa_ajax_error();
}
}
);
return false;
}
function qa_submit_comment(questionid, parentid)
{
var params=qa_form_params('c_form_'+parentid);
params.c_questionid=questionid;
params.c_parentid=parentid;
qa_ajax_post('comment', params,
function (lines) {
if (lines[0]=='1') {
var l=document.getElementById('c'+parentid+'_list');
l.innerHTML=lines.slice(2).join("\n");
l.style.display='';
var a=document.getElementById('c'+parentid);
a.qa_disabled=true;
var c=document.getElementById(lines[1]); // id of comment
if (c) {
c.style.display='none';
qa_reveal(c, 'comment');
}
qa_conceal(a, 'form');
} else if (lines[0]=='0') {
// document.forms['q_page_form'].elements['c'+parentid+'_doadd2'].value=1;
document.forms['c_form_'+parentid].submit();
} else {
qa_ajax_error();
}
}
);
return false;
}
function qa_answer_click(answerid, questionid, target)
{
var params={};
params.answerid=answerid;
params.questionid=questionid;
params[target.name]=target.value;
qa_ajax_post('click_a', params,
function (lines) {
if (lines[0]=='1') {
qa_set_inner_html(document.getElementById('a_list_title'), 'a_list_title', lines[1]);
var l=document.getElementById('a'+answerid);
var h=lines.slice(2).join("\n");
if (h.length)
qa_set_outer_html(l, 'answer', h);
else
qa_conceal(l, 'answer');
} else {
document.forms['q_page_form'].elements['qa_click'].value=target.name;
document.forms['q_page_form'].submit();
}
}
);
return false;
}
function qa_comment_click(commentid, questionid, parentid, target)
{
var params={};
params.commentid=commentid;
params.questionid=questionid;
params.parentid=parentid;
params[target.name]=target.value;
qa_ajax_post('click_c', params,
function (lines) {
if (lines[0]=='1') {
var l=document.getElementById('c'+commentid);
var h=lines.slice(1).join("\n");
if (h.length)
qa_set_outer_html(l, 'comment', h)
else
qa_conceal(l, 'comment');
} else {
document.forms['q_page_form'].elements['qa_click'].value=target.name;
document.forms['q_page_form'].submit();
}
}
);
return false;
}
function qa_show_comments(parentid)
{
var params={};
params.c_parentid=parentid;
qa_ajax_post('show_cs', params,
function (lines) {
if (lines[0]=='1') {
var l=document.getElementById('c'+parentid+'_list');
l.innerHTML=lines.slice(1).join("\n");
l.style.display='none';
qa_reveal(l, 'comments');
} else {
qa_ajax_error();
}
}
);
return false;
}
function qa_form_params(formname)
{
var es=document.forms[formname].elements;
var params={};
for (var i=0; i<es.length; i++) {
var e=es[i];
params[e.name]=e.value;
}
return params;
}
function qa_scroll_page_to(scroll)
{
$('html,body').animate({scrollTop: scroll}, 400);
}
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-ajax-answer.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax create answer requests
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
*/
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-app-limits.php';
// First check whether the person has permission to do this
if (!qa_user_permit_error('permit_post_a', QA_LIMIT_ANSWERS)) {
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
require_once QA_INCLUDE_DIR.'qa-app-captcha.php';
require_once QA_INCLUDE_DIR.'qa-app-format.php';
require_once QA_INCLUDE_DIR.'qa-app-post-create.php';
require_once QA_INCLUDE_DIR.'qa-app-cookies.php';
require_once QA_INCLUDE_DIR.'qa-page-question-view.php';
require_once QA_INCLUDE_DIR.'qa-page-question-submit.php';
// Load relevant information about this question and check it exists
$usecaptcha=qa_user_use_captcha('captcha_on_anon_post');
$questionid=qa_post_text('a_questionid');
$userid=qa_get_logged_in_userid();
list($question, $childposts)=qa_db_select_with_pending(
qa_db_full_post_selectspec($userid, $questionid),
qa_db_full_child_posts_selectspec($userid, $questionid)
);
if ((@$question['basetype']=='Q') && !isset($question['closedbyid'])) {
$answers=qa_page_q_load_as($question, $childposts);
// Try to create the new answer
$answerid=qa_page_q_add_a_submit($question, $answers, $usecaptcha, $in, $errors);
$countanswers=$question['acount']+1;
if (isset($answerid)) {
// If successful, page content will be updated via Ajax
$answer=qa_db_select_with_pending(qa_db_full_post_selectspec($userid, $answerid));
$question=$question+qa_page_q_post_rules($question, null, null, $childposts); // array union
$answer=$answer+qa_page_q_post_rules($answer, $question, $answers, null);
$usershtml=qa_userids_handles_html(array($answer), true);
$a_view=qa_page_q_answer_view($question, $answer, false, $usershtml, false);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-answer', null, null);
echo "QA_AJAX_RESPONSE\n1\n";
// Send back whether the 'answer' button should still be visible
echo (int)qa_opt('allow_multi_answers')."\n";
// Send back the count of answers
if ($countanswers==1)
echo qa_lang_html('question/1_answer_title')."\n";
else
echo qa_lang_html_sub('question/x_answers_title', $countanswers)."\n";
// Send back the HTML
$themeclass->a_list_item($a_view);
return;
}
}
}
echo "QA_AJAX_RESPONSE\n0\n"; // fall back to non-Ajax submission if there were any problems
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-ajax-asktitle.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax request based on ask a question title
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
*/
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
require_once QA_INCLUDE_DIR.'qa-util-string.php';
// Collect the information we need from the database
$intitle=qa_post_text('title');
$doaskcheck=qa_opt('do_ask_check_qs');
$doexampletags=qa_using_tags() && qa_opt('do_example_tags');
if ($doaskcheck || $doexampletags) {
$countqs=max($doexampletags ? QA_DB_RETRIEVE_ASK_TAG_QS : 0, $doaskcheck ? qa_opt('page_size_ask_check_qs') : 0);
$relatedquestions=qa_db_select_with_pending(
qa_db_search_posts_selectspec(null, qa_string_to_words($intitle), null, null, null, null, 0, false, $countqs)
);
}
// Collect example tags if appropriate
if ($doexampletags) {
require_once QA_INCLUDE_DIR.'qa-app-format.php';
$tagweight=array();
foreach ($relatedquestions as $question) {
$tags=qa_tagstring_to_tags($question['tags']);
foreach ($tags as $tag)
@$tagweight[$tag]+=exp($question['score']);
}
arsort($tagweight, SORT_NUMERIC);
$exampletags=array();
$minweight=exp(qa_match_to_min_score(qa_opt('match_example_tags')));
$maxcount=qa_opt('page_size_ask_tags');
foreach ($tagweight as $tag => $weight) {
if ($weight<$minweight)
break;
$exampletags[]=$tag;
if (count($exampletags)>=$maxcount)
break;
}
} else
$exampletags=array();
// Output the response header and example tags
echo "QA_AJAX_RESPONSE\n1\n";
echo strtr(implode(',', $exampletags), "\r\n", ' ')."\n";
// Collect and output the list of related questions
if ($doaskcheck) {
require_once QA_INCLUDE_DIR.'qa-app-format.php';
$count=0;
$minscore=qa_match_to_min_score(qa_opt('match_ask_check_qs'));
$maxcount=qa_opt('page_size_ask_check_qs');
foreach ($relatedquestions as $question) {
if ($question['score']<$minscore)
break;
if (!$count)
echo qa_lang_html('question/ask_same_q').'<BR/>';
echo strtr(
'<A HREF="'.qa_q_path_html($question['postid'], $question['title']).'" TARGET="_blank">'.qa_html($question['title']).'</A><BR/>',
"\r\n", ' '
)."\n";
if ((++$count)>=$maxcount)
break;
}
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-ajax-category.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax category information requests
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
*/
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
$categoryid=qa_post_text('categoryid');
if (!strlen($categoryid))
$categoryid=null;
list($fullcategory, $categories)=qa_db_select_with_pending(
qa_db_full_category_selectspec($categoryid, true),
qa_db_category_sub_selectspec($categoryid)
);
echo "QA_AJAX_RESPONSE\n1\n";
echo qa_html(strtr(@$fullcategory['content'], "\r\n", ' ')); // category description
foreach ($categories as $category)
echo "\n".$category['categoryid'].'/'.$category['title']; // subcategory information
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-ajax-click-admin.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax single clicks on posts in admin section
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
*/
require_once QA_INCLUDE_DIR.'qa-app-admin.php';
require_once QA_INCLUDE_DIR.'qa-app-users.php';
$postid=qa_post_text('postid');
$action=qa_post_text('action');
if (qa_admin_single_click($postid, $action)) // permission check happens in here
echo "QA_AJAX_RESPONSE\n1\n";
else
echo "QA_AJAX_RESPONSE\n0\n";
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-ajax-click-answer.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax single clicks on answer
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
*/
require_once QA_INCLUDE_DIR.'qa-app-cookies.php';
require_once QA_INCLUDE_DIR.'qa-app-format.php';
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
require_once QA_INCLUDE_DIR.'qa-page-question-view.php';
require_once QA_INCLUDE_DIR.'qa-page-question-submit.php';
// Load relevant information about this answer
$answerid=qa_post_text('answerid');
$questionid=qa_post_text('questionid');
$userid=qa_get_logged_in_userid();
@list($answer, $question, $qchildposts, $achildposts)=qa_db_select_with_pending(
qa_db_full_post_selectspec($userid, $answerid),
qa_db_full_post_selectspec($userid, $questionid),
qa_db_full_child_posts_selectspec($userid, $questionid),
qa_db_full_child_posts_selectspec($userid, $answerid)
);
// Check if there was an operation that succeeded
if (
(@$answer['basetype']=='A') &&
(@$question['basetype']=='Q')
) {
$answers=qa_page_q_load_as($question, $qchildposts);
$question=$question+qa_page_q_post_rules($question, null, null, $qchildposts); // array union
$answer=$answer+qa_page_q_post_rules($answer, $question, $qchildposts, $achildposts);
if (qa_page_q_single_click_a($answer, $question, $answers, $achildposts, false, $error)) {
list($answer, $question)=qa_db_select_with_pending(
qa_db_full_post_selectspec($userid, $answerid),
qa_db_full_post_selectspec($userid, $questionid)
);
// If so, page content to be updated via Ajax
echo "QA_AJAX_RESPONSE\n1\n";
// Send back new count of answers
$countanswers=$question['acount'];
if ($countanswers==1)
echo qa_lang_html('question/1_answer_title');
else
echo qa_lang_html_sub('question/x_answers_title', $countanswers);
// If the answer was not deleted....
if (isset($answer)) {
$question=$question+qa_page_q_post_rules($question, null, null, $qchildposts); // array union
$answer=$answer+qa_page_q_post_rules($answer, $question, $qchildposts, $achildposts);
foreach ($achildposts as $key => $achildpost)
$achildposts[$key]=$achildpost+qa_page_q_post_rules($achildpost, $answer, $achildposts, null);
$usershtml=qa_userids_handles_html(array_merge(array($answer), $achildposts), true);
$a_view=qa_page_q_answer_view($question, $answer, ($answer['postid']==$question['selchildid']) && ($answer['type']=='A'),
$usershtml, false);
$a_view['c_list']=qa_page_q_comment_follow_list($answer, $achildposts, false, $usershtml, false, null);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-answer', null, null);
// ... send back the HTML for it
echo "\n";
$themeclass->a_list_item($a_view);
}
return;
}
}
echo "QA_AJAX_RESPONSE\n0\n"; // fall back to non-Ajax submission if something failed
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-ajax-click-comment.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax single clicks on comments
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
*/
require_once QA_INCLUDE_DIR.'qa-app-cookies.php';
require_once QA_INCLUDE_DIR.'qa-app-format.php';
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
require_once QA_INCLUDE_DIR.'qa-page-question-view.php';
require_once QA_INCLUDE_DIR.'qa-page-question-submit.php';
// Load relevant information about this comment
$commentid=qa_post_text('commentid');
$questionid=qa_post_text('questionid');
$parentid=qa_post_text('parentid');
$userid=qa_get_logged_in_userid();
@list($comment, $question, $parent, $children)=qa_db_select_with_pending(
qa_db_full_post_selectspec($userid, $commentid),
qa_db_full_post_selectspec($userid, $questionid),
qa_db_full_post_selectspec($userid, $parentid),
qa_db_full_child_posts_selectspec($userid, $parentid)
);
// Check if there was an operation that succeeded
if (
(@$comment['basetype']=='C') &&
(@$question['basetype']=='Q') &&
((@$parent['basetype']=='Q') || (@$parent['basetype']=='A'))
) {
$comment=$comment+qa_page_q_post_rules($comment, $parent, $children, null); // array union
if (qa_page_q_single_click_c($comment, $question, $parent, $error)) {
$comment=qa_db_select_with_pending(qa_db_full_post_selectspec($userid, $commentid));
// If so, page content to be updated via Ajax
echo "QA_AJAX_RESPONSE\n1";
// If the comment was not deleted...
if (isset($comment)) {
$parent=$parent+qa_page_q_post_rules($parent, ($questionid==$parentid) ? null : $question, null, $children);
// in theory we should retrieve the parent's siblings for the above, but they're not going to be relevant
$comment=$comment+qa_page_q_post_rules($comment, $parent, $children, null);
$usershtml=qa_userids_handles_html(array($comment), true);
$c_view=qa_page_q_comment_view($parent, $comment, $usershtml, false);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-comment', null, null);
// ... send back the HTML for it
echo "\n";
$themeclass->c_list_item($c_view);
}
return;
}
}
echo "QA_AJAX_RESPONSE\n0\n"; // fall back to non-Ajax submission if something failed
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-ajax-comment.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax create comment requests
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
*/
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-app-limits.php';
// First check whether the person has permission to do this
if (!qa_user_permit_error('permit_post_c', QA_LIMIT_COMMENTS)) {
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
require_once QA_INCLUDE_DIR.'qa-app-captcha.php';
require_once QA_INCLUDE_DIR.'qa-app-format.php';
require_once QA_INCLUDE_DIR.'qa-app-post-create.php';
require_once QA_INCLUDE_DIR.'qa-app-cookies.php';
require_once QA_INCLUDE_DIR.'qa-page-question-view.php';
require_once QA_INCLUDE_DIR.'qa-page-question-submit.php';
require_once QA_INCLUDE_DIR.'qa-util-sort.php';
$usecaptcha=qa_user_use_captcha('captcha_on_anon_post');
// Load relevant information about this question and check it exists
$questionid=qa_post_text('c_questionid');
$parentid=qa_post_text('c_parentid');
$userid=qa_get_logged_in_userid();
@list($question, $parent, $children)=qa_db_select_with_pending(
qa_db_full_post_selectspec($userid, $questionid),
qa_db_full_post_selectspec($userid, $parentid),
qa_db_full_child_posts_selectspec($userid, $parentid)
);
if (
(@$question['basetype']=='Q') &&
((@$parent['basetype']=='Q') || (@$parent['basetype']=='A'))
) {
// Try to create the new comment
$commentid=qa_page_q_add_c_submit($question, $parent, $children, $usecaptcha, $in, $errors);
if (isset($commentid)) {
// If successful, page content will be updated via Ajax
$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);
// in theory we should retrieve the parent's siblings for the above, but they're not going to be relevant
foreach ($children as $key => $child)
$children[$key]=$child+qa_page_q_post_rules($child, $parent, $children, null);
$usershtml=qa_userids_handles_html($children, true);
qa_sort_by($children, 'created');
$c_list=qa_page_q_comment_follow_list($parent, $children, true, $usershtml, false, null);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-comments', null, null);
echo "QA_AJAX_RESPONSE\n1\n";
// Send back the ID of the new comment
echo qa_anchor('C', $commentid)."\n";
// Send back the HTML
foreach ($c_list['cs'] as $c_item)
$themeclass->c_list_item($c_item);
return;
}
}
}
echo "QA_AJAX_RESPONSE\n0\n"; // fall back to non-Ajax submission if there were any problems
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-ajax-favorite.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax favorite requests
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
*/
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-app-cookies.php';
require_once QA_INCLUDE_DIR.'qa-app-favorites.php';
require_once QA_INCLUDE_DIR.'qa-app-format.php';
$entitytype=qa_post_text('entitytype');
$entityid=qa_post_text('entityid');
$setfavorite=qa_post_text('favorite');
$userid=qa_get_logged_in_userid();
if (isset($userid)) {
$cookieid=qa_cookie_get();
qa_user_favorite_set($userid, qa_get_logged_in_handle(), $cookieid, $entitytype, $entityid, $setfavorite);
$favoriteform=qa_favorite_form($entitytype, $entityid, $setfavorite, qa_lang($setfavorite ? 'main/remove_favorites' : 'main/add_favorites'));
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-favorite', null, null);
echo "QA_AJAX_RESPONSE\n1\n";
$themeclass->favorite_inner_html($favoriteform);
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-ajax-mailing.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax mailing loop requests
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
*/
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-app-mailing.php';
$continue=false;
if (qa_get_logged_in_level()>=QA_USER_LEVEL_ADMIN) {
$starttime=time();
qa_mailing_perform_step();
if ($starttime==time())
sleep(1); // make sure at least one second has passed
$message=qa_mailing_progress_message();
if (isset($message))
$continue=true;
else
$message=qa_lang('admin/mailing_complete');
} else
$message=qa_lang('admin/no_privileges');
echo "QA_AJAX_RESPONSE\n".(int)$continue."\n".qa_html($message);
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-ajax-notice.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax requests to close a notice
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
*/
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-db-notices.php';
require_once QA_INCLUDE_DIR.'qa-db-users.php';
$noticeid=qa_post_text('noticeid');
if ($noticeid=='visitor')
setcookie('qa_noticed', 1, time()+86400*3650, '/', QA_COOKIE_DOMAIN);
else {
$userid=qa_get_logged_in_userid();
if ($noticeid=='welcome')
qa_db_user_set_flag($userid, QA_USER_FLAGS_WELCOME_NOTICE, false);
else
qa_db_usernotice_delete($userid, $noticeid);
}
echo "QA_AJAX_RESPONSE\n1";
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-ajax-recalc.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax admin recalculation requests
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
*/
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-app-recalc.php';
if (qa_get_logged_in_level()>=QA_USER_LEVEL_ADMIN) {
$state=qa_post_text('state');
$stoptime=time()+3;
while ( qa_recalc_perform_step($state) && (time()<$stoptime) )
;
$message=qa_recalc_get_message($state);
} else {
$state='';
$message=qa_lang('admin/no_privileges');
}
echo "QA_AJAX_RESPONSE\n1\n".$state."\n".qa_html($message);
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-ajax-show-comments.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax request to view full comment list
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
*/
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-app-cookies.php';
require_once QA_INCLUDE_DIR.'qa-app-format.php';
require_once QA_INCLUDE_DIR.'qa-page-question-view.php';
require_once QA_INCLUDE_DIR.'qa-util-sort.php';
// Load relevant information about this question and check it exists
$parentid=qa_post_text('c_parentid');
$userid=qa_get_logged_in_userid();
list($parent, $children)=qa_db_select_with_pending(
qa_db_full_post_selectspec($userid, $parentid),
qa_db_full_child_posts_selectspec($userid, $parentid)
);
if (isset($parent)) {
$parent=$parent+qa_page_q_post_rules($parent, null, null, $children);
// in theory we should retrieve the parent's parent and siblings for the above, but they're not going to be relevant
foreach ($children as $key => $child)
$children[$key]=$child+qa_page_q_post_rules($child, $parent, $children, null);
$usershtml=qa_userids_handles_html($children, true);
qa_sort_by($children, 'created');
$c_list=qa_page_q_comment_follow_list($parent, $children, true, $usershtml, false, null);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-comments', null, null);
echo "QA_AJAX_RESPONSE\n1\n";
// Send back the HTML
foreach ($c_list['cs'] as $c_item)
$themeclass->c_list_item($c_item);
return;
}
echo "QA_AJAX_RESPONSE\n0\n";
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-ajax-version.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax version check requests
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
*/
require_once QA_INCLUDE_DIR.'qa-app-admin.php';
$uri=qa_post_text('uri');
$versionkey=qa_post_text('versionkey');
$urikey=qa_post_text('urikey');
$version=qa_post_text('version');
$metadata=qa_admin_addon_metadata(qa_retrieve_url($uri), array(
'version' => $versionkey,
'uri' => $urikey,
));
if (strlen(@$metadata['version'])) {
if (strcmp($metadata['version'], $version)) {
$response=qa_lang_html_sub('admin/version_get_x', qa_html('v'.$metadata['version']));
if (strlen(@$metadata['uri']))
$response='<A HREF="'.qa_html($metadata['uri']).'" STYLE="color:#d00;">'.$response.'</A>';
} else
$response=qa_lang_html('admin/version_latest');
} else
$response=qa_lang_html('admin/version_latest_unknown');
echo "QA_AJAX_RESPONSE\n1\n".$response;
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-ajax-vote.php
Version: See define()s at top of qa-include/qa-base.php
Description: Server-side response to Ajax voting requests
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
*/
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-app-cookies.php';
require_once QA_INCLUDE_DIR.'qa-app-votes.php';
require_once QA_INCLUDE_DIR.'qa-app-format.php';
require_once QA_INCLUDE_DIR.'qa-app-options.php';
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
$postid=qa_post_text('postid');
$vote=qa_post_text('vote');
$userid=qa_get_logged_in_userid();
$cookieid=qa_cookie_get();
$post=qa_db_select_with_pending(qa_db_full_post_selectspec($userid, $postid));
$voteerror=qa_vote_error_html($post, $vote, $userid, qa_request());
if ($voteerror===false) {
qa_vote_set($post, $userid, qa_get_logged_in_handle(), $cookieid, $vote);
$post=qa_db_select_with_pending(qa_db_full_post_selectspec($userid, $postid));
$fields=qa_post_html_fields($post, $userid, $cookieid, array(), null, array(
'voteview' => qa_get_vote_view($post['basetype'], true), // behave as if on question page since the vote succeeded
));
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'voting', null, null);
echo "QA_AJAX_RESPONSE\n1\n";
$themeclass->voting_inner_html($fields);
} else
echo "QA_AJAX_RESPONSE\n0\n".$voteerror;
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-ajax.php
Version: See define()s at top of qa-include/qa-base.php
Description: Front line of response to Ajax requests, routing as appropriate
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
*/
// Output this header as early as possible
header('Content-Type: text/plain; charset=utf-8');
// Ensure no PHP errors are shown in the Ajax response
@ini_set('display_errors', 0);
// Load the Q2A base file which sets up a bunch of crucial functions
require 'qa-base.php';
qa_report_process_stage('init_ajax');
// Get general Ajax parameters from the POST payload, and clear $_GET
qa_set_request(qa_post_text('qa_request'), qa_post_text('qa_root'));
$_GET=array(); // for qa_self_html()
// Database failure handler
function qa_ajax_db_fail_handler()
{
echo "QA_AJAX_RESPONSE\n0\nA database error occurred.";
qa_exit('error');
}
// Perform the appropriate Ajax operation
$routing=array(
'notice' => 'qa-ajax-notice.php',
'favorite' => 'qa-ajax-favorite.php',
'vote' => 'qa-ajax-vote.php',
'recalc' => 'qa-ajax-recalc.php',
'mailing' => 'qa-ajax-mailing.php',
'version' => 'qa-ajax-version.php',
'category' => 'qa-ajax-category.php',
'asktitle' => 'qa-ajax-asktitle.php',
'answer' => 'qa-ajax-answer.php',
'comment' => 'qa-ajax-comment.php',
'click_a' => 'qa-ajax-click-answer.php',
'click_c' => 'qa-ajax-click-comment.php',
'click_admin' => 'qa-ajax-click-admin.php',
'show_cs' => 'qa-ajax-show-comments.php',
);
$operation=qa_post_text('qa_operation');
if (isset($routing[$operation])) {
qa_db_connect('qa_ajax_db_fail_handler');
require QA_INCLUDE_DIR.$routing[$operation];
qa_db_disconnect();
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-app-blobs.php
Version: See define()s at top of qa-include/qa-base.php
Description: Application-level blob-management functions
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
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
function qa_get_blob_url($blobid, $absolute=false)
/*
Return the URL which will output $blobid from the database when requested, $absolute or relative
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
return qa_path('blob', array('qa_blobid' => $blobid), $absolute ? qa_opt('site_url') : null, QA_URL_FORMAT_PARAMS);
}
function qa_get_max_upload_size()
/*
Return the maximum size of file that can be uploaded, based on database and PHP limits
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
$mindb=16777215; // from MEDIUMBLOB column type
$minphp=trim(ini_get('upload_max_filesize'));
switch (strtolower(substr($minphp, -1))) {
case 'g':
$minphp*=1024;
case 'm':
$minphp*=1024;
case 'k':
$minphp*=1024;
}
return min($mindb, $minphp);
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-app-captcha.php
Version: See define()s at top of qa-include/qa-base.php
Description: Wrapper functions and utilities for captcha modules
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
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
function qa_captcha_available()
/*
Return whether a captcha module has been selected and it indicates that it is fully set up to go
*/
{
$module=qa_load_module('captcha', qa_opt('captcha_module'));
return isset($module) && ( (!method_exists($module, 'allow_captcha')) || $module->allow_captcha());
}
function qa_set_up_captcha_field(&$qa_content, &$fields, $errors, $note=null)
/*
Prepare $qa_content for showing a captcha, adding the element to $fields, given previous $errors, and a $note to display
*/
{
if (qa_captcha_available()) {
$captcha=qa_load_module('captcha', qa_opt('captcha_module'));
$count=@++$qa_content['qa_captcha_count']; // work around fact that reCAPTCHA can only display per page
if ($count>1)
$html='[captcha placeholder]'; // single captcha will be moved about the page, to replace this
else {
$qa_content['script_var']['qa_captcha_in']='qa_captcha_div_1';
$html=$captcha->form_html($qa_content, @$errors['captcha']);
}
$fields['captcha']=array(
'type' => 'custom',
'label' => qa_lang_html('misc/captcha_label'),
'html' => '<DIV ID="qa_captcha_div_'.$count.'">'.$html.'</DIV>',
'error' => @array_key_exists('captcha', $errors) ? qa_lang_html('misc/captcha_error') : null,
'note' => $note,
);
return "if (qa_captcha_in!='qa_captcha_div_".$count."') { document.getElementById('qa_captcha_div_".$count."').innerHTML=document.getElementById(qa_captcha_in).innerHTML; document.getElementById(qa_captcha_in).innerHTML=''; qa_captcha_in='qa_captcha_div_".$count."'; }";
}
return '';
}
function qa_captcha_validate_post(&$errors)
/*
Check if captcha is submitted correctly, and if not, set $errors['captcha'] to a descriptive string
*/
{
if (qa_captcha_available()) {
$captcha=qa_load_module('captcha', qa_opt('captcha_module'));
if (!$captcha->validate_post($error)) {
$errors['captcha']=$error;
return false;
}
}
return true;
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-app-cookies.php
Version: See define()s at top of qa-include/qa-base.php
Description: User cookie management (application level) for tracking anonymous posts
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
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
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__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
return isset($_COOKIE['qa_id']) ? qa_gpc_to_string($_COOKIE['qa_id']) : null;
}
function qa_cookie_get_create()
/*
Return user identification cookie sent by browser if valid, or create a new one if not.
Either way, extend for another year (this is used when an anonymous post is created)
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
require_once QA_INCLUDE_DIR.'qa-db-cookies.php';
$cookieid=qa_cookie_get();
if (isset($cookieid) && qa_db_cookie_exists($cookieid))
; // cookie is valid
else
$cookieid=qa_db_cookie_create(qa_remote_ip_address());
setcookie('qa_id', $cookieid, time()+86400*365, '/', QA_COOKIE_DOMAIN);
$_COOKIE['qa_id']=$cookieid;
return $cookieid;
}
function qa_cookie_report_action($cookieid, $action)
/*
Called after a database write $action performed by a user identified by $cookieid,
relating to $questionid, $answerid and/or $commentid
*/
{
require_once QA_INCLUDE_DIR.'qa-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
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-app-emails.php
Version: See define()s at top of qa-include/qa-base.php
Description: Wrapper functions for sending email notifications to users
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
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
require_once QA_INCLUDE_DIR.'qa-app-options.php';
function qa_suspend_notifications($suspend=true)
/*
Suspend the sending of all email notifications via qa_send_notification(...) if $suspend is true, otherwise
reinstate it. A counter is kept to allow multiple calls.
*/
{
global $qa_notifications_suspended;
$qa_notifications_suspended+=($suspend ? 1 : -1);
}
function qa_send_notification($userid, $email, $handle, $subject, $body, $subs)
/*
Send email to person with $userid and/or $email and/or $handle (null/invalid values are ignored or retrieved from
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__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
global $qa_notifications_suspended;
if ($qa_notifications_suspended>0)
return false;
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
require_once QA_INCLUDE_DIR.'qa-util-string.php';
if (isset($userid)) {
$needemail=!qa_email_validate(@$email); // take from user if invalid, e.g. @ used in practice
$needhandle=empty($handle);
if ($needemail || $needhandle) {
if (QA_FINAL_EXTERNAL_USERS) {
if ($needhandle) {
$handles=qa_get_public_from_userids(array($userid));
$handle=@$handles[$userid];
}
if ($needemail)
$email=qa_get_user_email($userid);
} else {
$useraccount=qa_db_select_with_pending(
qa_db_user_account_selectspec($userid, true)
);
if ($needhandle)
$handle=@$useraccount['handle'];
if ($needemail)
$email=@$useraccount['email'];
}
}
}
if (isset($email) && qa_email_validate($email)) {
$subs['^site_title']=qa_opt('site_title');
$subs['^handle']=$handle;
$subs['^email']=$email;
$subs['^open']="\n";
$subs['^close']="\n";
return qa_send_email(array(
'fromemail' => qa_opt('from_email'),
'fromname' => qa_opt('site_title'),
'toemail' => $email,
'toname' => $handle,
'subject' => strtr($subject, $subs),
'body' => (empty($handle) ? '' : qa_lang_sub('emails/to_handle_prefix', $handle)).strtr($body, $subs),
'html' => false,
));
} else
return false;
}
function qa_send_email($params)
/*
Send the email based on the $params array - the following keys are required (some can be empty): fromemail,
fromname, toemail, toname, subject, body, html
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
require_once QA_INCLUDE_DIR.'qa-class.phpmailer.php';
$mailer=new PHPMailer();
$mailer->CharSet='utf-8';
$mailer->From=$params['fromemail'];
$mailer->Sender=$params['fromemail'];
$mailer->FromName=$params['fromname'];
$mailer->AddAddress($params['toemail'], $params['toname']);
$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');
if (qa_opt('smtp_authenticate')) {
$mailer->SMTPAuth=true;
$mailer->Username=qa_opt('smtp_username');
$mailer->Password=qa_opt('smtp_password');
}
}
return $mailer->Send();
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-app-events.php
Version: See define()s at top of qa-include/qa-base.php
Description: Handles the submission of events to the database (application level)
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
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
require_once QA_INCLUDE_DIR.'qa-db-events.php';
require_once QA_INCLUDE_DIR.'qa-app-updates.php';
function qa_create_event_for_q_user($questionid, $lastpostid, $updatetype, $lastuserid, $otheruserid=null, $timestamp=null)
/*
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.
*/
{
qa_db_event_create_for_entity(QA_ENTITY_QUESTION, $questionid, $questionid, $lastpostid, $updatetype, $lastuserid, $timestamp); // anyone who favorited the question
if (isset($lastuserid))
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
}
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.'qa-util-string.php';
require_once QA_INCLUDE_DIR.'qa-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.'qa-db-selects.php';
require_once QA_INCLUDE_DIR.'qa-app-format.php';
$categories=qa_category_path(qa_db_single_select(qa_db_category_nav_selectspec($categoryid, true)), $categoryid);
foreach ($categories as $category)
qa_db_event_create_for_entity(QA_ENTITY_CATEGORY, $category['categoryid'], $questionid, $questionid, $updatetype, $lastuserid, $timestamp);
}
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-app-favorites.php
Version: See define()s at top of qa-include/qa-base.php
Description: Handles favoriting and unfavoriting (application level)
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
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
function qa_user_favorite_set($userid, $handle, $cookieid, $entitytype, $entityid, $favorite)
/*
If $favorite is true, set $entitytype and $entityid to be favorites of $userid with $handle and $cookieid, otherwise
remove them from its favorites list. Handles event reporting.
*/
{
require_once QA_INCLUDE_DIR.'qa-db-favorites.php';
require_once QA_INCLUDE_DIR.'qa-app-limits.php';
require_once QA_INCLUDE_DIR.'qa-app-updates.php';
if ($favorite)
qa_db_favorite_create($userid, $entitytype, $entityid);
else
qa_db_favorite_delete($userid, $entitytype, $entityid);
switch ($entitytype) {
case QA_ENTITY_QUESTION:
$action=$favorite ? 'q_favorite' : 'q_unfavorite';
$params=array('postid' => $entityid);
break;
case QA_ENTITY_USER:
$action=$favorite ? 'u_favorite' : 'u_unfavorite';
$params=array('userid' => $entityid);
break;
case QA_ENTITY_TAG:
$action=$favorite ? 'tag_favorite' : 'tag_unfavorite';
$params=array('wordid' => $entityid);
break;
case QA_ENTITY_CATEGORY:
$action=$favorite ? 'cat_favorite' : 'cat_unfavorite';
$params=array('categoryid' => $entityid);
break;
default:
qa_fatal_error('Favorite type not recognized');
break;
}
qa_report_event($action, $userid, $handle, $cookieid, $params);
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-app-limits.php
Version: See define()s at top of qa-include/qa-base.php
Description: Monitoring and rate-limiting user actions (application level)
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
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
define('QA_LIMIT_QUESTIONS', 'Q');
define('QA_LIMIT_ANSWERS', 'A');
define('QA_LIMIT_COMMENTS', 'C');
define('QA_LIMIT_VOTES', 'V');
define('QA_LIMIT_REGISTRATIONS', 'R');
define('QA_LIMIT_LOGINS', 'L');
define('QA_LIMIT_UPLOADS', 'U');
define('QA_LIMIT_FLAGS', 'F');
define('QA_LIMIT_MESSAGES', 'M');
function qa_limits_remaining($userid, $action)
/*
Return how many more times user $userid and/or the requesting IP can perform $action this hour,
where $action is one of the QA_LIMIT_* constants defined above.
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
require_once QA_INCLUDE_DIR.'qa-app-options.php';
require_once QA_INCLUDE_DIR.'qa-db-limits.php';
$period=(int)(qa_opt('db_time')/3600);
$dblimits=qa_db_limits_get($userid, qa_remote_ip_address(), $action);
switch ($action) {
case QA_LIMIT_QUESTIONS:
$userlimit=qa_opt('max_rate_user_qs');
$iplimit=qa_opt('max_rate_ip_qs');
break;
case QA_LIMIT_ANSWERS:
$userlimit=qa_opt('max_rate_user_as');
$iplimit=qa_opt('max_rate_ip_as');
break;
case QA_LIMIT_COMMENTS:
$userlimit=qa_opt('max_rate_user_cs');
$iplimit=qa_opt('max_rate_ip_cs');
break;
case QA_LIMIT_VOTES:
$userlimit=qa_opt('max_rate_user_votes');
$iplimit=qa_opt('max_rate_ip_votes');
break;
case QA_LIMIT_REGISTRATIONS:
$userlimit=1; // not really relevant
$iplimit=qa_opt('max_rate_ip_registers');
break;
case QA_LIMIT_LOGINS:
$userlimit=1; // not really relevant
$iplimit=qa_opt('max_rate_ip_logins');
break;
case QA_LIMIT_UPLOADS:
$userlimit=qa_opt('max_rate_user_uploads');
$iplimit=qa_opt('max_rate_ip_uploads');
break;
case QA_LIMIT_FLAGS:
$userlimit=qa_opt('max_rate_user_flags');
$iplimit=qa_opt('max_rate_ip_flags');
break;
case QA_LIMIT_MESSAGES:
$userlimit=qa_opt('max_rate_user_messages');
$iplimit=qa_opt('max_rate_ip_messages');
break;
default:
qa_fatal_error('Unknown limit code in qa_limits_remaining: '.$action);
break;
}
return max(0, min(
$userlimit-((@$dblimits['user']['period']==$period) ? $dblimits['user']['count'] : 0),
$iplimit-((@$dblimits['ip']['period']==$period) ? $dblimits['ip']['count'] : 0)
));
}
function qa_is_ip_blocked()
/*
Return whether the requesting IP address has been blocked from write operations
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
$blockipclauses=qa_block_ips_explode(qa_opt('block_ips_write'));
foreach ($blockipclauses as $blockipclause)
if (qa_block_ip_match(qa_remote_ip_address(), $blockipclause))
return true;
return false;
}
function qa_block_ips_explode($blockipstring)
/*
Return an array of the clauses within $blockipstring, each of which can contain hyphens or asterisks
*/
{
$blockipstring=preg_replace('/\s*\-\s*/', '-', $blockipstring); // special case for 'x.x.x.x - x.x.x.x'
return preg_split('/[^0-9\.\-\*]/', $blockipstring, -1, PREG_SPLIT_NO_EMPTY);
}
function qa_block_ip_match($ip, $blockipclause)
/*
Returns whether the ip address $ip is matched by the clause $blockipclause, which can contain a hyphen or asterisk
*/
{
if (long2ip(ip2long($ip))==$ip) {
if (preg_match('/^(.*)\-(.*)$/', $blockipclause, $matches)) {
if ( (long2ip(ip2long($matches[1]))==$matches[1]) && (long2ip(ip2long($matches[2]))==$matches[2]) ) {
$iplong=sprintf('%u', ip2long($ip));
$end1long=sprintf('%u', ip2long($matches[1]));
$end2long=sprintf('%u', ip2long($matches[2]));
return (($iplong>=$end1long) && ($iplong<=$end2long)) || (($iplong>=$end2long) && ($iplong<=$end1long));
}
} elseif (strlen($blockipclause))
return preg_match('/^'.str_replace('\\*', '[0-9]+', preg_quote($blockipclause, '/')).'$/', $ip) > 0;
// preg_quote misses hyphens but that is OK here
}
return false;
}
function qa_report_write_action($userid, $cookieid, $action, $questionid, $answerid, $commentid)
/*
Called after a database write $action performed by a user identified by $userid and/or $cookieid.
*/
{}
function qa_limits_increment($userid, $action)
/*
Take note for rate limits that user $userid and/or the requesting IP just performed $action,
where $action is one of the QA_LIMIT_* constants defined above.
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
require_once QA_INCLUDE_DIR.'qa-db-limits.php';
$period=(int)(qa_opt('db_time')/3600);
if (isset($userid))
qa_db_limits_user_add($userid, $action, $period, 1);
qa_db_limits_ip_add(qa_remote_ip_address(), $action, $period, 1);
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-app-mailing.php
Version: See define()s at top of qa-include/qa-base.php
Description: Functions for sending a mailing to all users
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
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
function qa_mailing_start()
/*
Start a mailing to all users, unless one has already been started
*/
{
require_once QA_INCLUDE_DIR.'qa-db-admin.php';
if (strlen(qa_opt('mailing_last_userid'))==0) {
qa_opt('mailing_last_timestamp', time());
qa_opt('mailing_last_userid', '0');
qa_opt('mailing_total_users', qa_db_count_users());
qa_opt('mailing_done_users', 0);
}
}
function qa_mailing_stop()
/*
Stop a mailing to all users
*/
{
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()
/*
Allow the mailing to proceed forwards, for the appropriate amount of time and users, based on the options
*/
{
require_once QA_INCLUDE_DIR.'qa-db-users.php';
$lastuserid=qa_opt('mailing_last_userid');
if (strlen($lastuserid)) {
$thistime=time();
$lasttime=qa_opt('mailing_last_timestamp');
$perminute=qa_opt('mailing_per_minute');
if (($lasttime-$thistime)>60) // if it's been a while, we assume there hasn't been continuous mailing...
$lasttime=$thistime-1; // ... so only do 1 second's worth
else // otherwise...
$lasttime=max($lasttime, $thistime-6); // ... don't do more than 6 seconds' worth
$count=min(floor(($thistime-$lasttime)*$perminute/60), 100); // don't do more than 100 messages at a time
if ($count>0) {
qa_opt('mailing_last_timestamp', $thistime+30);
// prevents a parallel call to qa_mailing_perform_step() from sending messages, unless we're very unlucky with timing (poor man's mutex)
$sentusers=0;
$users=qa_db_users_get_mailing_next($lastuserid, $count);
if (count($users)) {
foreach ($users as $user)
$lastuserid=max($lastuserid, $user['userid']);
qa_opt('mailing_last_userid', $lastuserid);
qa_opt('mailing_done_users', qa_opt('mailing_done_users')+count($users));
foreach ($users as $user)
if (!($user['flags'] & QA_USER_FLAGS_NO_MAILINGS)) {
qa_mailing_send_one($user['userid'], $user['handle'], $user['email'], $user['emailcode']);
$sentusers++;
}
qa_opt('mailing_last_timestamp', $lasttime+$sentusers*60/$perminute); // can be floating point result, based on number of mails actually sent
} else
qa_mailing_stop();
}
}
}
function qa_mailing_send_one($userid, $handle, $email, $emailcode)
/*
Send a single message from the mailing, to $userid with $handle and $email.
Pass the user's existing $emailcode if there is one, otherwise a new one will be set up
*/
{
require_once QA_INCLUDE_DIR.'qa-app-emails.php';
require_once QA_INCLUDE_DIR.'qa-db-users.php';
if (!strlen(trim($emailcode))) {
$emailcode=qa_db_user_rand_emailcode();
qa_db_user_set($userid, 'emailcode', $emailcode);
}
$unsubscribeurl=qa_path('unsubscribe', array('c' => $emailcode, 'u' => $handle), qa_opt('site_url'));
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
*/
{
if (strlen(qa_opt('mailing_last_userid')))
return strtr(qa_lang('admin/mailing_progress'), array(
'^1' => number_format(qa_opt('mailing_done_users')),
'^2' => number_format(qa_opt('mailing_total_users')),
));
else
return null;
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-app-post-create.php
Version: See define()s at top of qa-include/qa-base.php
Description: Creating questions, answers and comments (application level)
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
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
require_once QA_INCLUDE_DIR.'qa-db-maxima.php';
require_once QA_INCLUDE_DIR.'qa-db-post-create.php';
require_once QA_INCLUDE_DIR.'qa-db-points.php';
require_once QA_INCLUDE_DIR.'qa-db-hotness.php';
require_once QA_INCLUDE_DIR.'qa-util-string.php';
function qa_combine_notify_email($userid, $notify, $email)
/*
Return value to store in database combining $notify and $email values entered by user $userid (or null for anonymous)
*/
{
return $notify ? (empty($email) ? (isset($userid) ? '@' : null) : $email) : null;
}
function qa_question_create($followanswer, $userid, $handle, $cookieid, $title, $content, $format, $text, $tagstring, $notify, $email,
$categoryid=null, $extravalue=null, $queued=false)
/*
Add a question (application level) - create record, update appropriate counts, index it, send notifications.
If question is follow-on from an answer, $followanswer should contain answer database record, otherwise null.
See qa-app-posts.php for a higher-level function which is easier to use.
*/
{
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
$postid=qa_db_post_create($queued ? 'Q_QUEUED' : 'Q', @$followanswer['postid'], $userid, isset($userid) ? null : $cookieid,
qa_remote_ip_address(), $title, $content, $format, $tagstring, qa_combine_notify_email($userid, $notify, $email), $categoryid);
if (isset($extravalue)) {
require_once QA_INCLUDE_DIR.'qa-db-metas.php';
qa_db_postmeta_set($postid, 'qa_q_extra', $extravalue);
}
qa_db_posts_calc_category_path($postid);
qa_db_hotness_update($postid);
if (!$queued) {
qa_db_category_path_qcount_update(qa_db_post_get_category_path($postid));
qa_post_index($postid, 'Q', $postid, @$followanswer['postid'], $title, $content, $format, $text, $tagstring);
qa_db_points_update_ifuser($userid, 'qposts');
qa_db_qcount_update();
qa_db_unaqcount_update();
qa_db_unselqcount_update();
qa_db_unupaqcount_update();
}
qa_report_event($queued ? 'q_queue' : 'q_post', $userid, $handle, $cookieid, array(
'postid' => $postid,
'parentid' => @$followanswer['postid'],
'parent' => $followanswer,
'title' => $title,
'content' => $content,
'format' => $format,
'text' => $text,
'tags' => $tagstring,
'categoryid' => $categoryid,
'extra' => $extravalue,
'notify' => $notify,
'email' => $email,
));
return $postid;
}
function qa_array_filter_by_keys($inarray, $keys)
/*
Return an array containing the elements of $inarray whose key is in $keys
*/
{
$outarray=array();
foreach ($keys as $key)
if (isset($inarray[$key]))
$outarray[$key]=$inarray[$key];
return $outarray;
}
function qa_suspend_post_indexing($suspend=true)
/*
Suspend the indexing (and unindexing) of posts via qa_post_index(...) and qa_post_unindex(...)
if $suspend is true, otherwise reinstate it. A counter is kept to allow multiple calls.
*/
{
global $qa_post_indexing_suspended;
$qa_post_indexing_suspended+=($suspend ? 1 : -1);
}
function qa_post_index($postid, $type, $questionid, $parentid, $title, $content, $format, $text, $tagstring)
/*
Add post $postid (which comes under $questionid) of $type (Q/A/C) to the database index, with $title, $text
and $tagstring. Calls through to all installed search modules.
*/
{
global $qa_post_indexing_suspended;
if ($qa_post_indexing_suspended>0)
return;
// Send through to any search modules for indexing
$searches=qa_load_modules_with('search', 'index_post');
foreach ($searches as $search)
$search->index_post($postid, $type, $questionid, $parentid, $title, $content, $format, $text, $tagstring);
}
function qa_answer_create($userid, $handle, $cookieid, $content, $format, $text, $notify, $email, $question, $queued=false)
/*
Add an answer (application level) - create record, update appropriate counts, index it, send notifications.
$question should contain database record for the question this is an answer to.
See qa-app-posts.php for a higher-level function which is easier to use.
*/
{
$postid=qa_db_post_create($queued ? 'A_QUEUED' : 'A', $question['postid'], $userid, isset($userid) ? null : $cookieid,
qa_remote_ip_address(), null, $content, $format, null, qa_combine_notify_email($userid, $notify, $email), $question['categoryid']);
qa_db_posts_calc_category_path($postid);
if (!$queued) {
if ($question['type']=='Q') // don't index answer if parent question is hidden or queued
qa_post_index($postid, 'A', $question['postid'], $question['postid'], null, $content, $format, $text, null);
qa_db_post_acount_update($question['postid']);
qa_db_hotness_update($question['postid']);
qa_db_points_update_ifuser($userid, 'aposts');
qa_db_acount_update();
qa_db_unaqcount_update();
}
qa_report_event($queued ? 'a_queue' : 'a_post', $userid, $handle, $cookieid, array(
'postid' => $postid,
'parentid' => $question['postid'],
'parent' => $question,
'content' => $content,
'format' => $format,
'text' => $text,
'categoryid' => $question['categoryid'],
'notify' => $notify,
'email' => $email,
));
return $postid;
}
function qa_comment_create($userid, $handle, $cookieid, $content, $format, $text, $notify, $email, $question, $parent, $commentsfollows, $queued=false)
/*
Add a comment (application level) - create record, update appropriate counts, index it, send notifications.
$question should contain database record for the question this is part of (as direct or comment on Q's answer).
If this is a comment on an answer, $answer should contain database record for the answer, otherwise null.
$commentsfollows should contain database records for all previous comments on the same question or answer,
but it can also contain other records that are ignored.
See qa-app-posts.php for a higher-level function which is easier to use.
*/
{
require_once QA_INCLUDE_DIR.'qa-app-emails.php';
require_once QA_INCLUDE_DIR.'qa-app-options.php';
require_once QA_INCLUDE_DIR.'qa-app-format.php';
require_once QA_INCLUDE_DIR.'qa-util-string.php';
if (!isset($parent))
$parent=$question; // for backwards compatibility with old answer parameter
$postid=qa_db_post_create($queued ? 'C_QUEUED' : 'C', $parent['postid'], $userid, isset($userid) ? null : $cookieid,
qa_remote_ip_address(), null, $content, $format, null, qa_combine_notify_email($userid, $notify, $email), $question['categoryid']);
qa_db_posts_calc_category_path($postid);
if (!$queued) {
if ( ($question['type']=='Q') && (($parent['type']=='Q') || ($parent['type']=='A')) ) // only index if antecedents fully visible
qa_post_index($postid, 'C', $question['postid'], $parent['postid'], null, $content, $format, $text, null);
qa_db_points_update_ifuser($userid, 'cposts');
qa_db_ccount_update();
}
$thread=array();
foreach ($commentsfollows as $comment)
if (($comment['type']=='C') && ($comment['parentid']==$parent['postid'])) // find just those for this parent, fully visible
$thread[]=$comment;
qa_report_event($queued ? 'c_queue' : 'c_post', $userid, $handle, $cookieid, array(
'postid' => $postid,
'parentid' => $parent['postid'],
'parenttype' => $parent['basetype'],
'parent' => $parent,
'questionid' => $question['postid'],
'question' => $question,
'thread' => $thread,
'content' => $content,
'format' => $format,
'text' => $text,
'categoryid' => $question['categoryid'],
'notify' => $notify,
'email' => $email,
));
return $postid;
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-app-q-list.php
Version: See define()s at top of qa-include/qa-base.php
Description: Controller for most question listing pages, plus custom pages and plugin pages
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
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
function qa_q_list_page_content($questions, $pagesize, $start, $count, $sometitle, $nonetitle,
$navcategories, $categoryid, $categoryqcount, $categorypathprefix, $feedpathprefix, $suggest,
$pagelinkparams=null, $categoryparams=null, $categoryisfavorite=null)
/*
Returns the $qa_content structure for a question list page showing $questions retrieved from the database.
If $pagesize is not null, it sets the max number of questions to display.
If $count is not null, pagination is determined by $start and $count.
The page title is $sometitle unless there are no questions shown, in which case it's $nonetitle.
$navcategories should contain the categories retrived from the database using qa_db_category_nav_selectspec(...) for
$categoryid, which is the current category shown (set $categoryisfavorite to whether the user has favorited it).
If $categorypathprefix is set, category navigation will be shown, with per-category question counts if
$categoryqcount is true. The nav links will have the prefix $categorypathprefix and possible extra $categoryparams.
If $feedpathprefix is set, the page has an RSS feed whose URL uses that prefix.
If there are no links to other pages, $suggest is used to suggest what the user should do.
The $pagelinkparams are passed through to qa_html_page_links(...) which creates links for page 2, 3, etc..
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
require_once QA_INCLUDE_DIR.'qa-app-format.php';
require_once QA_INCLUDE_DIR.'qa-app-updates.php';
$userid=qa_get_logged_in_userid();
// Chop down to size, get user information for display
if (isset($pagesize))
$questions=array_slice($questions, 0, $pagesize);
$usershtml=qa_userids_handles_html(qa_any_get_userids_handles($questions));
// Prepare content for theme
$qa_content=qa_content_prepare(true, array_keys(qa_category_path($navcategories, $categoryid)));
$qa_content['q_list']['form']=array(
'tags' => 'METHOD="POST" ACTION="'.qa_self_html().'"',
);
$qa_content['q_list']['qs']=array();
if (count($questions)) {
$qa_content['title']=$sometitle;
$options=qa_post_html_defaults('Q');
if (isset($categorypathprefix))
$options['categorypathprefix']=$categorypathprefix;
foreach ($questions as $question)
$qa_content['q_list']['qs'][]=qa_any_to_q_html_fields($question, $userid, qa_cookie_get(), $usershtml, null, $options);
} else
$qa_content['title']=$nonetitle;
if (isset($userid) && isset($categoryid) && isset($categoryisfavorite))
$qa_content['favorite']=qa_favorite_form(QA_ENTITY_CATEGORY, $categoryid, $categoryisfavorite,
qa_lang_sub($categoryisfavorite ? 'main/remove_x_favorites' : 'main/add_category_x_favorites', $navcategories[$categoryid]['title']));
if (isset($count) && isset($pagesize))
$qa_content['page_links']=qa_html_page_links(qa_request(), $start, $pagesize, $count, qa_opt('pages_prev_next'), $pagelinkparams);
if (empty($qa_content['page_links']))
$qa_content['suggest_next']=$suggest;
if (qa_using_categories() && count($navcategories) && isset($categorypathprefix))
$qa_content['navigation']['cat']=qa_category_navigation($navcategories, $categoryid, $categorypathprefix, $categoryqcount, $categoryparams);
if (isset($feedpathprefix) && (qa_opt('feed_per_category') || !isset($categoryid)) )
$qa_content['feed']=array(
'url' => qa_path_html(qa_feed_request($feedpathprefix.(isset($categoryid) ? ('/'.qa_category_path_request($navcategories, $categoryid)) : ''))),
'label' => strip_tags($sometitle),
);
return $qa_content;
}
function qa_qs_sub_navigation($sort, $categoryslugs)
/*
Return the sub navigation structure common to question listing pages
*/
{
$request='questions';
if (isset($categoryslugs))
foreach ($categoryslugs as $slug)
$request.='/'.$slug;
$navigation=array(
'recent' => array(
'label' => qa_lang('main/nav_most_recent'),
'url' => qa_path_html($request),
),
'hot' => array(
'label' => qa_lang('main/nav_hot'),
'url' => qa_path_html($request, array('sort' => 'hot')),
),
'votes' => array(
'label' => qa_lang('main/nav_most_votes'),
'url' => qa_path_html($request, array('sort' => 'votes')),
),
'answers' => array(
'label' => qa_lang('main/nav_most_answers'),
'url' => qa_path_html($request, array('sort' => 'answers')),
),
'views' => array(
'label' => qa_lang('main/nav_most_views'),
'url' => qa_path_html($request, array('sort' => 'views')),
),
);
if (isset($navigation[$sort]))
$navigation[$sort]['selected']=true;
else
$navigation['recent']['selected']=true;
if (!qa_opt('do_count_q_views'))
unset($navigation['views']);
return $navigation;
}
function qa_unanswered_sub_navigation($by, $categoryslugs)
/*
Return the sub navigation structure common to unanswered pages
*/
{
$request='unanswered';
if (isset($categoryslugs))
foreach ($categoryslugs as $slug)
$request.='/'.$slug;
$navigation=array(
'answers' => array(
'label' => qa_lang('main/nav_no_answer'),
'url' => qa_path_html($request),
),
'selected' => array(
'label' => qa_lang('main/nav_no_selected_answer'),
'url' => qa_path_html($request, array('by' => 'selected')),
),
'upvotes' => array(
'label' => qa_lang('main/nav_no_upvoted_answer'),
'url' => qa_path_html($request, array('by' => 'upvotes')),
),
);
if (isset($navigation[$by]))
$navigation[$by]['selected']=true;
else
$navigation['answers']['selected']=true;
if (!qa_opt('voting_on_as'))
unset($navigation['upvotes']);
return $navigation;
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-app-search.php
Version: See define()s at top of qa-include/qa-base.php
Description: Wrapper functions and utilities for search modules
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
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
function qa_get_search_results($query, $start, $count, $userid, $absoluteurls, $fullcontent)
/*
Returns $count search results for $query performed by $userid, starting at offset $start. Set $absoluteurls to true
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
$searchmodules=qa_load_modules_with('search', 'process_search');
if (!count($searchmodules))
qa_fatal_error('No search engine is available');
$module=reset($searchmodules); // use first one by default
if (count($searchmodules)>1) {
$tryname=qa_opt('search_module'); // use chosen one if it's available
if (isset($searchmodules[$tryname]))
$module=$searchmodules[$tryname];
}
// Get the results
$results=$module->process_search($query, $start, $count, $userid, $absoluteurls, $fullcontent);
// Work out what additional information (if any) we need to retrieve for the results
$keypostidgetfull=array();
$keypostidgettype=array();
$keypostidgetquestion=array();
$keypageidgetpage=array();
foreach ($results as $result) {
if (isset($result['question_postid']) && !isset($result['question']))
$keypostidgetfull[$result['question_postid']]=true;
if (isset($result['match_postid'])) {
if (!( (isset($result['question_postid'])) || (isset($result['question'])) ))
$keypostidgetquestion[$result['match_postid']]=true; // we can also get $result['match_type'] from this
elseif (!isset($result['match_type']))
$keypostidgettype[$result['match_postid']]=true;
}
if (isset($result['page_pageid']) && !isset($result['page']))
$keypageidgetpage[$result['page_pageid']]=true;
}
// Perform the appropriate database queries
@list($postidfull, $postidtype, $postidquestion, $pageidpage)=qa_db_select_with_pending(
count($keypostidgetfull) ? qa_db_posts_selectspec($userid, array_keys($keypostidgetfull), $fullcontent) : null,
count($keypostidgettype) ? qa_db_posts_basetype_selectspec(array_keys($keypostidgettype)) : null,
count($keypostidgetquestion) ? qa_db_posts_to_qs_selectspec($userid, array_keys($keypostidgetquestion), $fullcontent) : null,
count($keypageidgetpage) ? qa_db_pages_selectspec(null, array_keys($keypageidgetpage)) : null
);
// Supplement the results as appropriate
foreach ($results as $key => $result) {
if (isset($result['question_postid']) && !isset($result['question']))
if (@$postidfull[$result['question_postid']]['basetype']=='Q')
$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']))
$result['match_type']=@$result['question']['obasetype'];
} elseif (!isset($result['match_type']))
$result['match_type']=@$postidtype[$result['match_postid']];
}
if (isset($result['question']) && !isset($result['question_postid']))
$result['question_postid']=$result['question']['postid'];
if (isset($result['page_pageid']) && !isset($result['page']))
$result['page']=@$pageidpage[$result['page_pageid']];
if (!isset($result['title'])) {
if (isset($result['question']))
$result['title']=$result['question']['title'];
elseif (isset($result['page']))
$result['title']=$result['page']['heading'];
}
if (!isset($result['url'])) {
if (isset($result['question']))
$result['url']=qa_q_path($result['question']['postid'], $result['question']['title'],
$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
return $results;
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-app-updates.php
Version: See define()s at top of qa-include/qa-base.php
Description: Definitions relating to favorites and updates in the database tables
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
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
// Character codes for the different types of entity that can be followed (entitytype columns)
define('QA_ENTITY_QUESTION', 'Q');
define('QA_ENTITY_USER', 'U');
define('QA_ENTITY_TAG', 'T');
define('QA_ENTITY_CATEGORY', 'C');
define('QA_ENTITY_NONE', '-');
// Character codes for the different types of updates on a post (updatetype columns)
define('QA_UPDATE_CATEGORY', 'A'); // questions only, category changed
define('QA_UPDATE_CLOSED', 'C'); // questions only, closed or reopened
define('QA_UPDATE_CONTENT', 'E'); // title or content edited
define('QA_UPDATE_FOLLOWS', 'F'); // questions only (this is only set in streams tables, not on post)
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_TAGS', 'T'); // questions only
define('QA_UPDATE_TYPE', 'Y'); // e.g. answer to comment
define('QA_UPDATE_VISIBLE', 'H'); // hidden or reshown
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-app-votes.php
Version: See define()s at top of qa-include/qa-base.php
Description: Handling incoming votes (application level)
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
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
function qa_vote_error_html($post, $vote, $userid, $topage)
/*
Check if $userid can vote on $post, on the page $topage.
Return an HTML error to display if there was a problem, or false if it's OK.
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-app-limits.php';
if (
is_array($post) &&
( ($post['basetype']=='Q') || ($post['basetype']=='A') ) &&
qa_opt(($post['basetype']=='Q') ? 'voting_on_qs' : 'voting_on_as') &&
( (!isset($post['userid'])) || (!isset($userid)) || ($post['userid']!=$userid) )
) {
$permiterror=qa_user_permit_error(($post['basetype']=='Q') ? 'permit_vote_q' : 'permit_vote_a', QA_LIMIT_VOTES);
$errordownonly=(!$permiterror) && ($vote<0);
if ($errordownonly)
$permiterror=qa_user_permit_error('permit_vote_down');
switch ($permiterror) {
case 'login':
return qa_insert_login_links(qa_lang_html('main/vote_must_login'), $topage);
break;
case 'confirm':
return qa_insert_login_links(qa_lang_html($errordownonly ? 'main/vote_down_must_confirm' : 'main/vote_must_confirm'), $topage);
break;
case 'limit':
return qa_lang_html('main/vote_limit');
break;
default:
return qa_lang_html('users/no_permission');
break;
case false:
return false;
}
} else
return qa_lang_html('main/vote_not_allowed'); // voting option should not have been presented (but could happen due to options change)
}
function qa_vote_set($post, $userid, $handle, $cookieid, $vote)
/*
Actually set (application level) the $vote (-1/0/1) by $userid (with $handle and $cookieid) on $postid.
Handles user points, recounting and event reports as appropriate.
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
require_once QA_INCLUDE_DIR.'qa-db-points.php';
require_once QA_INCLUDE_DIR.'qa-db-hotness.php';
require_once QA_INCLUDE_DIR.'qa-db-votes.php';
require_once QA_INCLUDE_DIR.'qa-db-post-create.php';
require_once QA_INCLUDE_DIR.'qa-app-limits.php';
$vote=(int)min(1, max(-1, $vote));
$oldvote=(int)qa_db_uservote_get($post['postid'], $userid);
qa_db_uservote_set($post['postid'], $userid, $vote);
qa_db_post_recount_votes($post['postid']);
$postisanswer=($post['basetype']=='A');
if ($postisanswer) {
qa_db_post_acount_update($post['parentid']);
qa_db_unupaqcount_update();
}
$columns=array();
if ( ($vote>0) || ($oldvote>0) )
$columns[]=$postisanswer ? 'aupvotes' : 'qupvotes';
if ( ($vote<0) || ($oldvote<0) )
$columns[]=$postisanswer ? 'adownvotes' : 'qdownvotes';
qa_db_points_update_ifuser($userid, $columns);
qa_db_points_update_ifuser($post['userid'], array($postisanswer ? 'avoteds' : 'qvoteds', 'upvoteds', 'downvoteds'));
if ($post['basetype']=='Q')
qa_db_hotness_update($post['postid']);
if ($vote<0)
$event=$postisanswer ? 'a_vote_down' : 'q_vote_down';
elseif ($vote>0)
$event=$postisanswer ? 'a_vote_up' : 'q_vote_up';
else
$event=$postisanswer ? 'a_vote_nil' : 'q_vote_nil';
qa_report_event($event, $userid, $handle, $cookieid, array(
'postid' => $post['postid'],
'vote' => $vote,
'oldvote' => $oldvote,
));
}
function qa_flag_error_html($post, $userid, $topage)
/*
Check if $userid can flag $post, on the page $topage.
Return an HTML error to display if there was a problem, or false if it's OK.
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
require_once QA_INCLUDE_DIR.'qa-app-options.php';
require_once QA_INCLUDE_DIR.'qa-app-users.php';
require_once QA_INCLUDE_DIR.'qa-app-limits.php';
if (
is_array($post) &&
qa_opt('flagging_of_posts') &&
( (!isset($post['userid'])) || (!isset($userid)) || ($post['userid']!=$userid) )
) {
switch (qa_user_permit_error('permit_flag', QA_LIMIT_FLAGS)) {
case 'login':
return qa_insert_login_links(qa_lang_html('question/flag_must_login'), $topage);
break;
case 'confirm':
return qa_insert_login_links(qa_lang_html('question/flag_must_confirm'), $topage);
break;
case 'limit':
return qa_lang_html('question/flag_limit');
break;
default:
return qa_lang_html('users/no_permission');
break;
case false:
return false;
}
} else
return qa_lang_html('question/flag_not_allowed'); // flagging option should not have been presented
}
function qa_flag_set_tohide($oldpost, $userid, $handle, $cookieid, $question)
/*
Set (application level) a flag by $userid (with $handle and $cookieid) on $oldpost which belongs to $question.
Handles recounting, admin notifications and event reports as appropriate.
Returns true if the post should now be hidden because it has accumulated enough flags.
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
require_once QA_INCLUDE_DIR.'qa-db-votes.php';
require_once QA_INCLUDE_DIR.'qa-app-limits.php';
qa_db_userflag_set($oldpost['postid'], $userid, true);
qa_db_post_recount_flags($oldpost['postid']);
switch ($oldpost['basetype']) {
case 'Q':
$event='q_flag';
break;
case 'A':
$event='a_flag';
break;
case 'C':
$event='c_flag';
break;
}
$post=qa_db_select_with_pending(qa_db_full_post_selectspec(null, $oldpost['postid']));
qa_report_event($event, $userid, $handle, $cookieid, array(
'postid' => $oldpost['postid'],
'oldpost' => $oldpost,
'flagcount' => $post['flagcount'],
'questionid' => $question['postid'],
'question' => $question,
));
return ($post['flagcount']>=qa_opt('flagging_hide_after')) && !$post['hidden'];
}
function qa_flag_clear($oldpost, $userid, $handle, $cookieid)
/*
Clear (application level) a flag on $oldpost by $userid (with $handle and $cookieid).
Handles recounting and event reports as appropriate.
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
require_once QA_INCLUDE_DIR.'qa-db-votes.php';
require_once QA_INCLUDE_DIR.'qa-app-limits.php';
qa_db_userflag_set($oldpost['postid'], $userid, false);
qa_db_post_recount_flags($oldpost['postid']);
switch ($oldpost['basetype']) {
case 'Q':
$event='q_unflag';
break;
case 'A':
$event='a_unflag';
break;
case 'C':
$event='c_unflag';
break;
}
qa_report_event($event, $userid, $handle, $cookieid, array(
'postid' => $oldpost['postid'],
'oldpost' => $oldpost,
));
}
function qa_flags_clear_all($oldpost, $userid, $handle, $cookieid)
/*
Clear (application level) all flags on $oldpost by $userid (with $handle and $cookieid).
Handles recounting and event reports as appropriate.
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
require_once QA_INCLUDE_DIR.'qa-db-votes.php';
require_once QA_INCLUDE_DIR.'qa-app-limits.php';
qa_db_userflags_clear_all($oldpost['postid']);
qa_db_post_recount_flags($oldpost['postid']);
switch ($oldpost['basetype']) {
case 'Q':
$event='q_clearflags';
break;
case 'A':
$event='a_clearflags';
break;
case 'C':
$event='c_clearflags';
break;
}
qa_report_event($event, $userid, $handle, $cookieid, array(
'postid' => $oldpost['postid'],
'oldpost' => $oldpost,
));
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-blob.php
Version: See define()s at top of qa-include/qa-base.php
Description: Response to blob requests, outputting blob from the database
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
*/
// Ensure no PHP errors are shown in the blob response
@ini_set('display_errors', 0);
function qa_blob_db_fail_handler()
{
header('HTTP/1.1 500 Internal Server Error');
qa_exit('error');
}
// Load the Q2A base file which sets up a bunch of crucial stuff
require 'qa-base.php';
qa_report_process_stage('init_blob');
// Output the blob in question
require_once QA_INCLUDE_DIR.'qa-db-blobs.php';
qa_db_connect('qa_blob_db_fail_handler');
$blob=qa_db_blob_read(qa_get('qa_blobid'));
if (isset($blob)) {
header('Cache-Control: max-age=2592000, public'); // allows browsers and proxies to cache the blob
switch ($blob['format']) {
case 'jpeg':
case 'jpg':
header('Content-Type: image/jpeg');
break;
case 'gif':
header('Content-Type: image/gif');
break;
case 'png':
header('Content-Type: image/png');
break;
case 'swf':
header('Content-Type: application/x-shockwave-flash');
break;
default:
$filename=preg_replace('/[^A-Za-z0-9 \\._-]/', '-', $blob['filename']); // for compatibility with HTTP headers and all browsers
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.$filename.'"');
break;
}
echo $blob['content'];
} else
header('HTTP/1.0 404 Not Found');
qa_db_disconnect();
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-check-lang.php
Version: See define()s at top of qa-include/qa-base.php
Description: Development tool to see which language phrases are missing or unused
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
*/
define('QA_BASE_DIR', dirname(dirname(empty($_SERVER['SCRIPT_FILENAME']) ? __FILE__ : $_SERVER['SCRIPT_FILENAME'])).'/');
require 'qa-base.php';
header('Content-type: text/html; charset=utf-8');
?>
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=utf-8"/>
<TITLE>Question2Answer Language Check</TITLE>
<STYLE>
code {font-size:125%;}
</STYLE>
</HEAD>
<BODY STYLE="font-family:arial; font-size:12px;">
<?php
function get_phrase_substitutions($phrase)
{
$substitutions=array();
if (preg_match_all('/\^(([0-9]+)|([a-z_]+)|)/', $phrase, $matches))
foreach ($matches[0] as $match)
@$substitutions[$match]++;
return $substitutions;
}
echo '<font color="#c00"><code>Dark red = important to review.</code></font><br>';
echo '<font color="#c99"><code>Light red = probably safe to ignore.</code></font>';
echo '<H1>Checking US English files in <code>qa-include</code>...</H1>';
$includefiles=glob(QA_INCLUDE_DIR.'qa-*.php');
$definite=array();
$probable=array();
$possible=array();
$defined=array();
$english=array();
$backmap=array();
$substitutions=array();
output_start_includes();
foreach ($includefiles as $includefile) {
$contents=file_get_contents($includefile);
preg_match_all('/qa_lang[a-z_]*\s*\(\s*[\'\"]([a-z]+)\/([0-9a-z_]+)[\'\"]/', $contents, $matches, PREG_SET_ORDER);
foreach ($matches as $matchparts)
if ($matchparts[2]=='date_month_') { // special case for month names
for ($month=1; $month<=12; $month++)
@$definite[$matchparts[1]][$matchparts[2].$month]++;
} else
@$definite[$matchparts[1]][$matchparts[2]]++;
preg_match_all('/[\'\"]([a-z]+)\/([0-9a-z_]+)[\'\"]/', $contents, $matches, PREG_SET_ORDER);
foreach ($matches as $matchparts)
@$probable[$matchparts[1]][$matchparts[2]]++;
if (preg_match('|/qa-include/qa-lang-([a-z]+)\.php$|', $includefile, $matches)) { // it's a lang file
$prefix=$matches[1];
output_reading_include($includefile);
$phrases=@include $includefile;
foreach ($phrases as $key => $value) {
@$defined[$prefix][$key]++;
$english[$prefix][$key]=$value;
$backmap[$value][]=array('prefix' => $prefix, 'key' => $key);
$substitutions[$prefix][$key]=get_phrase_substitutions($value);
}
} else { // it's a different file
preg_match_all('/[\'\"\/]([0-9a-z_]+)[\'\"]/', $contents, $matches, PREG_SET_ORDER);
foreach ($matches as $matchparts)
@$possible[$matchparts[1]]++;
}
}
output_finish_includes();
foreach ($definite as $key => $valuecount)
foreach ($valuecount as $value => $count)
if (!@$defined[$key][$value])
output_lang_issue($key, $value, 'used by '.$count.' file/s but not defined');
foreach ($defined as $key => $valuecount)
foreach ($valuecount as $value => $count)
if ( (!@$definite[$key][$value]) && (!@$probable[$key][$value]) && (!@$possible[$value]) )
output_lang_issue($key, $value, 'defined but apparently not used');
foreach ($backmap as $phrase => $where)
if (count($where)>1)
foreach ($where as $onewhere)
output_lang_issue($onewhere['prefix'], $onewhere['key'], 'contains the shared phrase "'.$phrase.'"', false);
require_once QA_INCLUDE_DIR.'qa-app-admin.php';
$languages=qa_admin_language_options();
unset($languages['']);
foreach ($languages as $code => $language) {
echo '<H1>Checking '.$language.' files in <code>qa-lang/'.$code.'</code>...</H1>';
$langdefined=array();
$langdifferent=array();
$langsubstitutions=array();
$langincludefiles=glob(QA_LANG_DIR.$code.'/qa-*.php');
$langnewphrases=array();
output_start_includes();
foreach ($langincludefiles as $langincludefile)
if (preg_match('/qa-lang-([a-z]+)\.php$/', $langincludefile, $matches)) { // it's a lang file
$prefix=$matches[1];
output_reading_include($langincludefile);
$phrases=@include $langincludefile;
foreach ($phrases as $key => $value) {
@$langdefined[$prefix][$key]++;
$langdifferent[$prefix][$key]=($value!=@$english[$prefix][$key]);
$langsubstitutions[$prefix][$key]=get_phrase_substitutions($value);
}
}
output_finish_includes();
foreach ($langdefined as $key => $valuecount)
foreach ($valuecount as $value => $count) {
if (!@$defined[$key][$value])
output_lang_issue($key, $value, 'defined but not in US English files');
elseif (!$langdifferent[$key][$value])
output_lang_issue($key, $value, 'identical to US English files', false);
else
foreach ($substitutions[$key][$value] as $substitution => $subcount)
if (!@$langsubstitutions[$key][$value][$substitution])
output_lang_issue($key, $value, 'omitted the substitution '.$substitution);
elseif ($subcount > @$langsubstitutions[$key][$value][$substitution])
output_lang_issue($key, $value, 'has fewer of the substitution '.$substitution);
}
foreach ($defined as $key => $valuecount) {
$showaserror=!(($key=='admin') || ($key=='options') || ($code=='en-GB'));
if (@$langdefined[$key]) {
if (count($langdefined[$key]) < (count($valuecount)/2)) { // only a few phrases defined
output_lang_issue($key, null, 'few translations provided so will use US English defaults', $showaserror);
} else
foreach ($valuecount as $value => $count)
if (!@$langdefined[$key][$value]) {
output_lang_issue($key, $value, 'undefined so will use US English defaults', $showaserror);
$langnewphrases[$key][$value]=$english[$key][$value];
}
} else
output_lang_issue($key, null, 'no translations provided so will use US English defaults', $showaserror);
}
foreach ($langnewphrases as $prefix => $phrases) {
echo '<H2>'.$language.' phrases to add to <code>qa-lang/'.$code.'/qa-lang-'.$prefix.'.php</code>:</H2>';
echo 'Copy and paste this into the middle of <code>qa-lang/'.$code.'/qa-lang-'.$prefix.'.php</code> then translate the right-hand sides after the <code>=></code> symbol.';
echo '<PRE>';
foreach ($phrases as $key => $value)
echo '<SPAN STYLE="font-size:25%;">'."\t\t</SPAN>'".$key."' => \"".strtr($value, array('\\' => '\\\\', '"' => '\"', '$' => '\$', "\n" => '\n', "\t" => '\t'))."\",\n";
echo '</PRE>';
}
}
function output_lang_issue($prefix, $key, $issue, $error=true)
{
echo '<font color="'.($error ? '#c00' : '#c99').'"><code>';
echo 'qa-lang-<B>'.qa_html($prefix).'</B>.php:';
if (strlen($key))
echo "'<B>".qa_html($key)."</B>'";
echo '</code></font> &nbsp; '.qa_html($issue).'<BR>';
}
function output_start_includes()
{
global $oneread;
$oneread=false;
echo '<P STYLE="font-size:80%; color:#999;">Reading: ';
}
function output_reading_include($file)
{
global $oneread;
echo ($oneread ? ', ' : '').htmlspecialchars(basename($file));
flush();
$oneread=true;
}
function output_finish_includes()
{
echo '</P>';
}
echo '<H1>Finished scanning for problems!</H1>';
?>
</BODY>
</HTML>
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-db-blobs.php
Version: See define()s at top of qa-include/qa-base.php
Description: Database-level access to blobs table for large chunks of data (e.g. images)
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
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
function qa_db_blob_create($content, $format, $filename=null, $userid=null, $cookieid=null, $ip=null)
/*
Create a new blob in the database with $content and $format, returning its blobid
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_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, $filename, $userid, $cookieid, $ip
);
return $blobid;
}
return null;
}
function qa_db_blob_read($blobid)
/*
Get the content of blob $blobid from the database
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
return qa_db_read_one_assoc(qa_db_query_sub(
'SELECT content, format, filename FROM ^blobs WHERE blobid=#',
$blobid
), true);
}
function qa_db_blob_delete($blobid)
/*
Delete blob $blobid in the database
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
qa_db_query_sub(
'DELETE FROM ^blobs WHERE blobid=#',
$blobid
);
}
function qa_db_blob_exists($blobid)
/*
Check if blob $blobid exists in the database
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
return qa_db_read_one_value(qa_db_query_sub(
'SELECT COUNT(*) FROM ^blobs WHERE blobid=#',
$blobid
)) > 0;
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-db-cache.php
Version: See define()s at top of qa-include/qa-base.php
Description: Database-level access to cache table
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
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
require_once QA_INCLUDE_DIR.'qa-db-maxima.php';
function qa_db_cache_set($type, $cacheid, $content)
/*
Create (or replace) the item ($type, $cacheid) in the database cache table with $content
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
qa_db_query_sub(
'DELETE FROM ^cache WHERE lastread<NOW()-INTERVAL # SECOND',
QA_DB_MAX_CACHE_AGE
);
qa_db_query_sub(
'REPLACE ^cache (type, cacheid, content, created, lastread) VALUES ($, #, $, NOW(), NOW())',
$type, $cacheid, $content
);
}
function qa_db_cache_get($type, $cacheid)
/*
Retrieve the item ($type, $cacheid) from the database cache table
*/
{
if (qa_to_override(__FUNCTION__)) return qa_call_override(__FUNCTION__, $args=func_get_args());
$content=qa_db_read_one_value(qa_db_query_sub(
'SELECT content FROM ^cache WHERE type=$ AND cacheid=#',
$type, $cacheid
), true);
if (isset($content))
qa_db_query_sub(
'UPDATE ^cache SET lastread=NOW() WHERE type=$ AND cacheid=#',
$type, $cacheid
);
return $content;
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-db-cookies.php
Version: See define()s at top of qa-include/qa-base.php
Description: Database access functions for user cookies
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
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
function qa_db_cookie_create($ipaddress)
/*
Create a new random cookie for $ipaddress and insert into database, returning it
*/
{
for ($attempt=0; $attempt<10; $attempt++) {
$cookieid=qa_db_random_bigint();
if (qa_db_cookie_exists($cookieid))
continue;
qa_db_query_sub(
'INSERT INTO ^cookies (cookieid, created, createip) '.
'VALUES (#, NOW(), COALESCE(INET_ATON($), 0))',
$cookieid, $ipaddress
);
return $cookieid;
}
return null;
}
function qa_db_cookie_written($cookieid, $ipaddress)
/*
Note in database that a write operation has been done by user identified by $cookieid and from $ipaddress
*/
{
qa_db_query_sub(
'UPDATE ^cookies SET written=NOW(), writeip=COALESCE(INET_ATON($), 0) WHERE cookieid=#',
$ipaddress, $cookieid
);
}
function qa_db_cookie_exists($cookieid)
/*
Return whether $cookieid exists in database
*/
{
return qa_db_read_one_value(qa_db_query_sub(
'SELECT COUNT(*) FROM ^cookies WHERE cookieid=#',
$cookieid
)) > 0;
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-db-events.php
Version: See define()s at top of qa-include/qa-base.php
Description: Database-level access to userevents and sharedevents tables
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
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
function qa_db_event_create_for_entity($entitytype, $entityid, $questionid, $lastpostid, $updatetype, $lastuserid, $timestamp=null)
/*
Add an event to the event streams for entity $entitytype with $entityid. 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 the event both to the entity's shared stream, and the
individual user streams for any users following the entity not via its shared stream (See long comment in
qa-db-favorites.php). Also handles truncation.
*/
{
require_once QA_INCLUDE_DIR.'qa-db-maxima.php';
require_once QA_INCLUDE_DIR.'qa-app-updates.php';
$updatedsql=isset($timestamp) ? ('FROM_UNIXTIME('.qa_db_argument_to_mysql($timestamp, false).')') : 'NOW()';
// Enter it into the appropriate shared event stream for that entity
qa_db_query_sub(
'INSERT INTO ^sharedevents (entitytype, entityid, questionid, lastpostid, updatetype, lastuserid, updated) '.
'VALUES ($, #, #, #, $, $, '.$updatedsql.')',
$entitytype, $entityid, $questionid, $lastpostid, $updatetype, $lastuserid
);
// If this is for a question entity, check the shared event stream doesn't have too many entries for that question
$questiontruncated=false;
if ($entitytype==QA_ENTITY_QUESTION) {
$truncate=qa_db_read_one_value(qa_db_query_sub(
'SELECT updated FROM ^sharedevents WHERE entitytype=$ AND entityid=# AND questionid=# ORDER BY updated DESC LIMIT #,1',
$entitytype, $entityid, $questionid, QA_DB_MAX_EVENTS_PER_Q
), true);
if (isset($truncate)) {
qa_db_query_sub(
'DELETE FROM ^sharedevents WHERE entitytype=$ AND entityid=# AND questionid=# AND updated<=$',
$entitytype, $entityid, $questionid, $truncate
);
$questiontruncated=true;
}
}
// If we didn't truncate due to a specific question, truncate the shared event stream for its overall length
if (!$questiontruncated) {
$truncate=qa_db_read_one_value(qa_db_query_sub(
'SELECT updated FROM ^sharedevents WHERE entitytype=$ AND entityid=$ ORDER BY updated DESC LIMIT #,1',
$entitytype, $entityid, (int)qa_opt('max_store_user_updates')
), true);
if (isset($truncate))
qa_db_query_sub(
'DELETE FROM ^sharedevents WHERE entitytype=$ AND entityid=$ AND updated<=$',
$entitytype, $entityid, $truncate
);
}
// See if we can identify a user who has favorited this entity, but is not using its shared event stream
$randomuserid=qa_db_read_one_value(qa_db_query_sub(
'SELECT userid FROM ^userfavorites WHERE entitytype=$ AND entityid=# AND nouserevents=0 ORDER BY RAND() LIMIT 1',
$entitytype, $entityid
), true);
if (isset($randomuserid)) {
// If one was found, this means we have one or more individual event streams, so update them all
qa_db_query_sub(
'INSERT INTO ^userevents (userid, entitytype, entityid, questionid, lastpostid, updatetype, lastuserid, updated) '.
'SELECT userid, $, #, #, #, $, $, '.$updatedsql.' FROM ^userfavorites WHERE entitytype=$ AND entityid=# AND nouserevents=0',
$entitytype, $entityid, $questionid, $lastpostid, $updatetype, $lastuserid, $entitytype, $entityid
);
// Now truncate the random individual event stream that was found earlier
// (in theory we should truncate them all, but truncation is just a 'housekeeping' activity, so it's not necessary)
qa_db_user_events_truncate($randomuserid, $questionid);
}
}
function qa_db_event_create_not_entity($userid, $questionid, $lastpostid, $updatetype, $lastuserid, $timestamp=null)
/*
Add an event to the event stream for $userid which is not related to an entity they are following (but rather a
notification which is relevant for them, e.g. if someone answers their 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. Also handles truncation.
*/
{
require_once QA_INCLUDE_DIR.'qa-app-updates.php';
$updatedsql=isset($timestamp) ? ('FROM_UNIXTIME('.qa_db_argument_to_mysql($timestamp, false).')') : 'NOW()';
qa_db_query_sub(
"INSERT INTO ^userevents (userid, entitytype, entityid, questionid, lastpostid, updatetype, lastuserid, updated) ".
"VALUES ($, $, 0, #, #, $, $, ".$updatedsql.")",
$userid, QA_ENTITY_NONE, $questionid, $lastpostid, $updatetype, $lastuserid
);
qa_db_user_events_truncate($userid, $questionid);
}
function qa_db_user_events_truncate($userid, $questionid=null)
/*
Trim the number of events in the event stream for $userid. If an event was just added for a particular question,
pass the question's id in $questionid (to help focus the truncation).
*/
{
// First try truncating based on there being too many events for this question
$questiontruncated=false;
if (isset($questionid)) {
$truncate=qa_db_read_one_value(qa_db_query_sub(
'SELECT updated FROM ^userevents WHERE userid=$ AND questionid=# ORDER BY updated DESC LIMIT #,1',
$userid, $questionid, QA_DB_MAX_EVENTS_PER_Q
), true);
if (isset($truncate)) {
qa_db_query_sub(
'DELETE FROM ^userevents WHERE userid=$ AND questionid=# AND updated<=$',
$userid, $questionid, $truncate
);
$questiontruncated=true;
}
}
// If that didn't happen, try truncating the stream in general based on its total length
if (!$questiontruncated) {
$truncate=qa_db_read_one_value(qa_db_query_sub(
'SELECT updated FROM ^userevents WHERE userid=$ ORDER BY updated DESC LIMIT #,1',
$userid, (int)qa_opt('max_store_user_updates')
), true);
if (isset($truncate))
qa_db_query_sub(
'DELETE FROM ^userevents WHERE userid=$ AND updated<=$',
$userid, $truncate
);
}
}
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment