mirror of
https://github.com/godotengine/godot-question2answer.git
synced 2026-01-01 01:48:37 +03:00
This project lived only on the server without version control. This is now the starting point for the repository.
2029 lines
77 KiB
PHP
2029 lines
77 KiB
PHP
<?php
|
|
/*
|
|
Question2Answer by Gideon Greenspan and contributors
|
|
http://www.question2answer.org/
|
|
|
|
Description: Builders of selectspec arrays (see qa-db.php) used to specify database SELECTs
|
|
|
|
|
|
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.'db/maxima.php';
|
|
|
|
|
|
/**
|
|
* Return the results of all the SELECT operations specified by the supplied selectspec parameters, while also
|
|
* performing all pending selects that have not yet been executed. If only one parameter is supplied, return its
|
|
* result, otherwise return an array of results indexed as per the parameters.
|
|
*/
|
|
function qa_db_select_with_pending() // any number of parameters read via func_get_args()
|
|
{
|
|
require_once QA_INCLUDE_DIR . 'app/options.php';
|
|
|
|
global $qa_db_pending_selectspecs, $qa_db_pending_results;
|
|
|
|
$selectspecs = func_get_args();
|
|
$singleresult = (count($selectspecs) == 1);
|
|
$outresults = array();
|
|
|
|
foreach ($selectspecs as $key => $selectspec) { // can pass null parameters
|
|
if (empty($selectspec)) {
|
|
unset($selectspecs[$key]);
|
|
$outresults[$key] = null;
|
|
}
|
|
}
|
|
|
|
if (is_array($qa_db_pending_selectspecs)) {
|
|
foreach ($qa_db_pending_selectspecs as $pendingid => $selectspec) {
|
|
if (!isset($qa_db_pending_results[$pendingid])) {
|
|
$selectspecs['pending_' . $pendingid] = $selectspec;
|
|
}
|
|
}
|
|
}
|
|
|
|
$outresults = $outresults + qa_db_multi_select($selectspecs);
|
|
|
|
if (is_array($qa_db_pending_selectspecs)) {
|
|
foreach ($qa_db_pending_selectspecs as $pendingid => $selectspec) {
|
|
if (!isset($qa_db_pending_results[$pendingid])) {
|
|
$qa_db_pending_results[$pendingid] = $outresults['pending_' . $pendingid];
|
|
unset($outresults['pending_' . $pendingid]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $singleresult ? $outresults[0] : $outresults;
|
|
}
|
|
|
|
|
|
/**
|
|
* Queue a $selectspec for running later, with $pendingid (used for retrieval)
|
|
* @param $pendingid
|
|
* @param $selectspec
|
|
*/
|
|
function qa_db_queue_pending_select($pendingid, $selectspec)
|
|
{
|
|
global $qa_db_pending_selectspecs;
|
|
|
|
$qa_db_pending_selectspecs[$pendingid] = $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the result of the queued SELECT query identified by $pendingid. Run the query if it hasn't run already. If
|
|
* $selectspec is supplied, it doesn't matter if this hasn't been queued before - it will be queued and run now.
|
|
* @param $pendingid
|
|
* @param $selectspec
|
|
* @return
|
|
*/
|
|
function qa_db_get_pending_result($pendingid, $selectspec = null)
|
|
{
|
|
global $qa_db_pending_selectspecs, $qa_db_pending_results;
|
|
|
|
if (isset($selectspec)) {
|
|
qa_db_queue_pending_select($pendingid, $selectspec);
|
|
} elseif (!isset($qa_db_pending_selectspecs[$pendingid])) {
|
|
qa_fatal_error('Pending query was never set up: ' . $pendingid);
|
|
}
|
|
|
|
if (!isset($qa_db_pending_results[$pendingid])) {
|
|
qa_db_select_with_pending();
|
|
}
|
|
|
|
return $qa_db_pending_results[$pendingid];
|
|
}
|
|
|
|
|
|
/**
|
|
* Remove the results of queued SELECT query identified by $pendingid if it has already been run. This means it will
|
|
* run again if its results are requested via qa_db_get_pending_result()
|
|
* @param $pendingid
|
|
*/
|
|
function qa_db_flush_pending_result($pendingid)
|
|
{
|
|
global $qa_db_pending_results;
|
|
unset($qa_db_pending_results[$pendingid]);
|
|
}
|
|
|
|
|
|
/**
|
|
* Modify a selectspec to count the number of items. This assumes the original selectspec does not have a LIMIT clause.
|
|
* Currently works with message inbox/outbox functions and user-flags function.
|
|
* @param $selectSpec
|
|
* @return mixed
|
|
*/
|
|
function qa_db_selectspec_count($selectSpec)
|
|
{
|
|
$selectSpec['columns'] = array('count' => 'COUNT(*)');
|
|
$selectSpec['single'] = true;
|
|
unset($selectSpec['arraykey']);
|
|
|
|
return $selectSpec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the common selectspec used to build any selectspecs which retrieve posts from the database.
|
|
* If $voteuserid is set, retrieve the vote made by a particular that user on each post.
|
|
* If $full is true, get full information on the posts, instead of just information for listing pages.
|
|
* If $user is true, get information about the user who wrote the post (or cookie if anonymous).
|
|
* @param $voteuserid
|
|
* @param bool $full
|
|
* @param bool $user
|
|
* @return array
|
|
*/
|
|
function qa_db_posts_basic_selectspec($voteuserid = null, $full = false, $user = true)
|
|
{
|
|
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
|
|
|
|
$selectspec = array(
|
|
'columns' => array(
|
|
'^posts.postid', '^posts.categoryid', '^posts.type', 'basetype' => 'LEFT(^posts.type, 1)',
|
|
'hidden' => "INSTR(^posts.type, '_HIDDEN')>0", 'queued' => "INSTR(^posts.type, '_QUEUED')>0",
|
|
'^posts.acount', '^posts.selchildid', '^posts.closedbyid', '^posts.upvotes', '^posts.downvotes', '^posts.netvotes', '^posts.views', '^posts.hotness',
|
|
'^posts.flagcount', '^posts.title', '^posts.tags', 'created' => 'UNIX_TIMESTAMP(^posts.created)', '^posts.name',
|
|
'categoryname' => '^categories.title', 'categorybackpath' => "^categories.backpath",
|
|
'categoryids' => "CONCAT_WS(',', ^posts.catidpath1, ^posts.catidpath2, ^posts.catidpath3, ^posts.categoryid)",
|
|
),
|
|
|
|
'arraykey' => 'postid',
|
|
'source' => '^posts LEFT JOIN ^categories ON ^categories.categoryid=^posts.categoryid',
|
|
'arguments' => array(),
|
|
);
|
|
|
|
if (isset($voteuserid)) {
|
|
require_once QA_INCLUDE_DIR . 'app/updates.php';
|
|
|
|
$selectspec['columns']['uservote'] = '^uservotes.vote';
|
|
$selectspec['columns']['userflag'] = '^uservotes.flag';
|
|
$selectspec['columns']['userfavoriteq'] = '^userfavorites.entityid<=>^posts.postid';
|
|
$selectspec['source'] .= ' LEFT JOIN ^uservotes ON ^posts.postid=^uservotes.postid AND ^uservotes.userid=$';
|
|
$selectspec['source'] .= ' LEFT JOIN ^userfavorites ON ^posts.postid=^userfavorites.entityid AND ^userfavorites.userid=$ AND ^userfavorites.entitytype=$';
|
|
array_push($selectspec['arguments'], $voteuserid, $voteuserid, QA_ENTITY_QUESTION);
|
|
}
|
|
|
|
if ($full) {
|
|
$selectspec['columns']['content'] = '^posts.content';
|
|
$selectspec['columns']['notify'] = '^posts.notify';
|
|
$selectspec['columns']['updated'] = 'UNIX_TIMESTAMP(^posts.updated)';
|
|
$selectspec['columns']['updatetype'] = '^posts.updatetype';
|
|
$selectspec['columns'][] = '^posts.format';
|
|
$selectspec['columns'][] = '^posts.lastuserid';
|
|
$selectspec['columns']['lastip'] = '^posts.lastip';
|
|
$selectspec['columns'][] = '^posts.parentid';
|
|
$selectspec['columns']['lastviewip'] = '^posts.lastviewip';
|
|
}
|
|
|
|
if ($user) {
|
|
$selectspec['columns'][] = '^posts.userid';
|
|
$selectspec['columns'][] = '^posts.cookieid';
|
|
$selectspec['columns']['createip'] = '^posts.createip';
|
|
$selectspec['columns'][] = '^userpoints.points';
|
|
|
|
if (!QA_FINAL_EXTERNAL_USERS) {
|
|
$selectspec['columns'][] = '^users.flags';
|
|
$selectspec['columns'][] = '^users.level';
|
|
$selectspec['columns']['email'] = '^users.email';
|
|
$selectspec['columns']['handle'] = '^users.handle';
|
|
$selectspec['columns']['avatarblobid'] = 'BINARY ^users.avatarblobid';
|
|
$selectspec['columns'][] = '^users.avatarwidth';
|
|
$selectspec['columns'][] = '^users.avatarheight';
|
|
$selectspec['source'] .= ' LEFT JOIN ^users ON ^posts.userid=^users.userid';
|
|
|
|
if ($full) {
|
|
$selectspec['columns']['lasthandle'] = 'lastusers.handle';
|
|
$selectspec['source'] .= ' LEFT JOIN ^users AS lastusers ON ^posts.lastuserid=lastusers.userid';
|
|
}
|
|
}
|
|
|
|
$selectspec['source'] .= ' LEFT JOIN ^userpoints ON ^posts.userid=^userpoints.userid';
|
|
}
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Supplement a selectspec returned by qa_db_posts_basic_selectspec() to get information about another post (answer or
|
|
* comment) which is related to the main post (question) retrieved. Pass the name of table which will contain the other
|
|
* post in $poststable. Set $fromupdated to true to get information about when this other post was edited, rather than
|
|
* created. If $full is true, get full information on this other post.
|
|
* @param $selectspec
|
|
* @param $poststable
|
|
* @param bool $fromupdated
|
|
* @param bool $full
|
|
*/
|
|
function qa_db_add_selectspec_opost(&$selectspec, $poststable, $fromupdated = false, $full = false)
|
|
{
|
|
$selectspec['arraykey'] = 'opostid';
|
|
|
|
$selectspec['columns']['obasetype'] = 'LEFT(' . $poststable . '.type, 1)';
|
|
$selectspec['columns']['ohidden'] = "INSTR(" . $poststable . ".type, '_HIDDEN')>0";
|
|
$selectspec['columns']['opostid'] = $poststable . '.postid';
|
|
$selectspec['columns']['ouserid'] = $poststable . ($fromupdated ? '.lastuserid' : '.userid');
|
|
$selectspec['columns']['ocookieid'] = $poststable . '.cookieid';
|
|
$selectspec['columns']['oname'] = $poststable . '.name';
|
|
$selectspec['columns']['oip'] = $poststable . ($fromupdated ? '.lastip' : '.createip');
|
|
$selectspec['columns']['otime'] = 'UNIX_TIMESTAMP(' . $poststable . ($fromupdated ? '.updated' : '.created') . ')';
|
|
$selectspec['columns']['oflagcount'] = $poststable . '.flagcount';
|
|
|
|
if ($fromupdated) {
|
|
$selectspec['columns']['oupdatetype'] = $poststable . '.updatetype';
|
|
}
|
|
|
|
if ($full) {
|
|
$selectspec['columns']['ocontent'] = $poststable . '.content';
|
|
$selectspec['columns']['oformat'] = $poststable . '.format';
|
|
}
|
|
|
|
if ($fromupdated || $full) {
|
|
$selectspec['columns']['oupdated'] = 'UNIX_TIMESTAMP(' . $poststable . '.updated)';
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Supplement a selectspec returned by qa_db_posts_basic_selectspec() to get information about the author of another
|
|
* post (answer or comment) which is related to the main post (question) retrieved. Pass the name of table which will
|
|
* contain the other user's details in $userstable and the name of the table which will contain the other user's points
|
|
* in $pointstable.
|
|
* @param $selectspec
|
|
* @param $userstable
|
|
* @param $pointstable
|
|
*/
|
|
function qa_db_add_selectspec_ousers(&$selectspec, $userstable, $pointstable)
|
|
{
|
|
if (!QA_FINAL_EXTERNAL_USERS) {
|
|
$selectspec['columns']['oflags'] = $userstable . '.flags';
|
|
$selectspec['columns']['olevel'] = $userstable . '.level';
|
|
$selectspec['columns']['oemail'] = $userstable . '.email';
|
|
$selectspec['columns']['ohandle'] = $userstable . '.handle';
|
|
$selectspec['columns']['oavatarblobid'] = 'BINARY ' . $userstable . '.avatarblobid'; // cast to BINARY due to MySQL bug which renders it signed in a union
|
|
$selectspec['columns']['oavatarwidth'] = $userstable . '.avatarwidth';
|
|
$selectspec['columns']['oavatarheight'] = $userstable . '.avatarheight';
|
|
}
|
|
|
|
$selectspec['columns']['opoints'] = $pointstable . '.points';
|
|
}
|
|
|
|
|
|
/**
|
|
* Given $categoryslugs in order of the hierarchiy, return the equivalent value for the backpath column in the categories table
|
|
* @param $categoryslugs
|
|
* @return string
|
|
*/
|
|
function qa_db_slugs_to_backpath($categoryslugs)
|
|
{
|
|
if (!is_array($categoryslugs)) {
|
|
// accept old-style string arguments for one category deep
|
|
$categoryslugs = array($categoryslugs);
|
|
}
|
|
|
|
return implode('/', array_reverse($categoryslugs));
|
|
}
|
|
|
|
|
|
/**
|
|
* Return SQL code that represents the constraint of a post being in the category with $categoryslugs, or any of its subcategories
|
|
* @param $categoryslugs
|
|
* @param $arguments
|
|
* @return string
|
|
*/
|
|
function qa_db_categoryslugs_sql_args($categoryslugs, &$arguments)
|
|
{
|
|
if (!is_array($categoryslugs)) {
|
|
// accept old-style string arguments for one category deep
|
|
$categoryslugs = strlen($categoryslugs) ? array($categoryslugs) : array();
|
|
}
|
|
|
|
$levels = count($categoryslugs);
|
|
|
|
if ($levels > 0 && $levels <= QA_CATEGORY_DEPTH) {
|
|
$arguments[] = qa_db_slugs_to_backpath($categoryslugs);
|
|
return (($levels == QA_CATEGORY_DEPTH) ? 'categoryid' : ('catidpath' . $levels)) . '=(SELECT categoryid FROM ^categories WHERE backpath=$ LIMIT 1) AND ';
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve questions (of type $specialtype if provided, or 'Q' by default) sorted by $sort,
|
|
* restricted to $createip (if not null) and the category for $categoryslugs (if not null), with the corresponding vote
|
|
* made by $voteuserid (if not null) and including $full content or not. Return $count (if null, a default is used)
|
|
* questions starting from offset $start.
|
|
* @param $voteuserid
|
|
* @param $sort
|
|
* @param $start
|
|
* @param $categoryslugs
|
|
* @param $createip
|
|
* @param bool $specialtype
|
|
* @param bool $full
|
|
* @param $count
|
|
* @return array
|
|
*/
|
|
function qa_db_qs_selectspec($voteuserid, $sort, $start, $categoryslugs = null, $createip = null, $specialtype = false, $full = false, $count = null)
|
|
{
|
|
if ($specialtype == 'Q' || $specialtype == 'Q_QUEUED') {
|
|
$type = $specialtype;
|
|
} else {
|
|
$type = $specialtype ? 'Q_HIDDEN' : 'Q'; // for backwards compatibility
|
|
}
|
|
|
|
$count = isset($count) ? min($count, QA_DB_RETRIEVE_QS_AS) : QA_DB_RETRIEVE_QS_AS;
|
|
|
|
switch ($sort) {
|
|
case 'acount':
|
|
case 'flagcount':
|
|
case 'netvotes':
|
|
case 'views':
|
|
$sortsql = 'ORDER BY ^posts.' . $sort . ' DESC, ^posts.created DESC';
|
|
break;
|
|
|
|
case 'created':
|
|
case 'hotness':
|
|
$sortsql = 'ORDER BY ^posts.' . $sort . ' DESC';
|
|
break;
|
|
|
|
default:
|
|
qa_fatal_error('qa_db_qs_selectspec() called with illegal sort value');
|
|
break;
|
|
}
|
|
|
|
$selectspec = qa_db_posts_basic_selectspec($voteuserid, $full);
|
|
|
|
$selectspec['source'] .=
|
|
" JOIN (SELECT postid FROM ^posts WHERE " .
|
|
qa_db_categoryslugs_sql_args($categoryslugs, $selectspec['arguments']) .
|
|
(isset($createip) ? "createip=UNHEX($) AND " : "") .
|
|
"type=$ " . $sortsql . " LIMIT #,#) y ON ^posts.postid=y.postid";
|
|
|
|
if (isset($createip)) {
|
|
$selectspec['arguments'][] = bin2hex(@inet_pton($createip));
|
|
}
|
|
|
|
array_push($selectspec['arguments'], $type, $start, $count);
|
|
|
|
$selectspec['sortdesc'] = $sort;
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve recent questions (of type $specialtype if provided, or 'Q' by default) which,
|
|
* depending on $by, either (a) have no answers, (b) have on selected answers, or (c) have no upvoted answers. The
|
|
* questions are restricted to the category for $categoryslugs (if not null), and will have the corresponding vote made
|
|
* by $voteuserid (if not null) and will include $full content or not. Return $count (if null, a default is used)
|
|
* questions starting from offset $start.
|
|
* @param $voteuserid
|
|
* @param $by
|
|
* @param $start
|
|
* @param $categoryslugs
|
|
* @param bool $specialtype
|
|
* @param bool $full
|
|
* @param $count
|
|
* @return array
|
|
*/
|
|
function qa_db_unanswered_qs_selectspec($voteuserid, $by, $start, $categoryslugs = null, $specialtype = false, $full = false, $count = null)
|
|
{
|
|
if ($specialtype == 'Q' || $specialtype == 'Q_QUEUED') {
|
|
$type = $specialtype;
|
|
} else {
|
|
$type = $specialtype ? 'Q_HIDDEN' : 'Q'; // for backwards compatibility
|
|
}
|
|
|
|
$count = isset($count) ? min($count, QA_DB_RETRIEVE_QS_AS) : QA_DB_RETRIEVE_QS_AS;
|
|
|
|
switch ($by) {
|
|
case 'selchildid':
|
|
$bysql = 'selchildid IS NULL';
|
|
break;
|
|
|
|
case 'amaxvote':
|
|
$bysql = 'amaxvote=0';
|
|
break;
|
|
|
|
default:
|
|
$bysql = 'acount=0';
|
|
break;
|
|
}
|
|
|
|
$selectspec = qa_db_posts_basic_selectspec($voteuserid, $full);
|
|
|
|
$selectspec['source'] .= " JOIN (SELECT postid FROM ^posts WHERE " . qa_db_categoryslugs_sql_args($categoryslugs, $selectspec['arguments']) . "type=$ AND " . $bysql . " AND closedbyid IS NULL ORDER BY ^posts.created DESC LIMIT #,#) y ON ^posts.postid=y.postid";
|
|
|
|
array_push($selectspec['arguments'], $type, $start, $count);
|
|
|
|
$selectspec['sortdesc'] = 'created';
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the antecedent questions for recent answers (of type $specialtype if provided, or
|
|
* 'A' by default), restricted to $createip (if not null) and the category for $categoryslugs (if not null), with the
|
|
* corresponding vote on those questions made by $voteuserid (if not null). Return $count (if null, a default is used)
|
|
* questions starting from offset $start. The selectspec will also retrieve some information about the answers
|
|
* themselves (including the content if $fullanswers is true), in columns named with the prefix 'o'.
|
|
* @param $voteuserid
|
|
* @param $start
|
|
* @param $categoryslugs
|
|
* @param $createip
|
|
* @param bool $specialtype
|
|
* @param bool $fullanswers
|
|
* @param $count
|
|
* @return array
|
|
*/
|
|
function qa_db_recent_a_qs_selectspec($voteuserid, $start, $categoryslugs = null, $createip = null, $specialtype = false, $fullanswers = false, $count = null)
|
|
{
|
|
if ($specialtype == 'A' || $specialtype == 'A_QUEUED') {
|
|
$type = $specialtype;
|
|
} else {
|
|
$type = $specialtype ? 'A_HIDDEN' : 'A'; // for backwards compatibility
|
|
}
|
|
|
|
$count = isset($count) ? min($count, QA_DB_RETRIEVE_QS_AS) : QA_DB_RETRIEVE_QS_AS;
|
|
|
|
$selectspec = qa_db_posts_basic_selectspec($voteuserid);
|
|
|
|
qa_db_add_selectspec_opost($selectspec, 'aposts', false, $fullanswers);
|
|
qa_db_add_selectspec_ousers($selectspec, 'ausers', 'auserpoints');
|
|
|
|
$selectspec['source'] .=
|
|
" JOIN ^posts AS aposts ON ^posts.postid=aposts.parentid" .
|
|
(QA_FINAL_EXTERNAL_USERS ? "" : " LEFT JOIN ^users AS ausers ON aposts.userid=ausers.userid") .
|
|
" LEFT JOIN ^userpoints AS auserpoints ON aposts.userid=auserpoints.userid" .
|
|
" JOIN (SELECT postid FROM ^posts WHERE " .
|
|
qa_db_categoryslugs_sql_args($categoryslugs, $selectspec['arguments']) .
|
|
(isset($createip) ? "createip=UNHEX($) AND " : "") .
|
|
"type=$ ORDER BY ^posts.created DESC LIMIT #,#) y ON aposts.postid=y.postid" .
|
|
($specialtype ? '' : " WHERE ^posts.type='Q'");
|
|
|
|
if (isset($createip)) {
|
|
$selectspec['arguments'][] = bin2hex(@inet_pton($createip));
|
|
}
|
|
|
|
array_push($selectspec['arguments'], $type, $start, $count);
|
|
|
|
$selectspec['sortdesc'] = 'otime';
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the antecedent questions for recent comments (of type $specialtype if provided, or
|
|
* 'C' by default), restricted to $createip (if not null) and the category for $categoryslugs (if not null), with the
|
|
* corresponding vote on those questions made by $voteuserid (if not null). Return $count (if null, a default is used)
|
|
* questions starting from offset $start. The selectspec will also retrieve some information about the comments
|
|
* themselves (including the content if $fullcomments is true), in columns named with the prefix 'o'.
|
|
* @param $voteuserid
|
|
* @param $start
|
|
* @param $categoryslugs
|
|
* @param $createip
|
|
* @param bool $specialtype
|
|
* @param bool $fullcomments
|
|
* @param $count
|
|
* @return array
|
|
*/
|
|
function qa_db_recent_c_qs_selectspec($voteuserid, $start, $categoryslugs = null, $createip = null, $specialtype = false, $fullcomments = false, $count = null)
|
|
{
|
|
if ($specialtype == 'C' || $specialtype == 'C_QUEUED') {
|
|
$type = $specialtype;
|
|
} else {
|
|
$type = $specialtype ? 'C_HIDDEN' : 'C'; // for backwards compatibility
|
|
}
|
|
|
|
$count = isset($count) ? min($count, QA_DB_RETRIEVE_QS_AS) : QA_DB_RETRIEVE_QS_AS;
|
|
|
|
$selectspec = qa_db_posts_basic_selectspec($voteuserid);
|
|
|
|
qa_db_add_selectspec_opost($selectspec, 'cposts', false, $fullcomments);
|
|
qa_db_add_selectspec_ousers($selectspec, 'cusers', 'cuserpoints');
|
|
|
|
$selectspec['source'] .=
|
|
" JOIN ^posts AS parentposts ON" .
|
|
" ^posts.postid=(CASE LEFT(parentposts.type, 1) WHEN 'A' THEN parentposts.parentid ELSE parentposts.postid END)" .
|
|
" JOIN ^posts AS cposts ON parentposts.postid=cposts.parentid" .
|
|
(QA_FINAL_EXTERNAL_USERS ? "" : " LEFT JOIN ^users AS cusers ON cposts.userid=cusers.userid") .
|
|
" LEFT JOIN ^userpoints AS cuserpoints ON cposts.userid=cuserpoints.userid" .
|
|
" JOIN (SELECT postid FROM ^posts WHERE " .
|
|
qa_db_categoryslugs_sql_args($categoryslugs, $selectspec['arguments']) .
|
|
(isset($createip) ? "createip=UNHEX($) AND " : "") .
|
|
"type=$ ORDER BY ^posts.created DESC LIMIT #,#) y ON cposts.postid=y.postid" .
|
|
($specialtype ? '' : " WHERE ^posts.type='Q' AND ((parentposts.type='Q') OR (parentposts.type='A'))");
|
|
|
|
if (isset($createip)) {
|
|
$selectspec['arguments'][] = bin2hex(@inet_pton($createip));
|
|
}
|
|
|
|
array_push($selectspec['arguments'], $type, $start, $count);
|
|
|
|
$selectspec['sortdesc'] = 'otime';
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the antecedent questions for recently edited posts, restricted to edits by $lastip
|
|
* (if not null), the category for $categoryslugs (if not null) and only visible posts (if $onlyvisible), with the
|
|
* corresponding vote on those questions made by $voteuserid (if not null). Return $count (if null, a default is used)
|
|
* questions starting from offset $start. The selectspec will also retrieve some information about the edited posts
|
|
* themselves (including the content if $fulledited is true), in columns named with the prefix 'o'.
|
|
* @param $voteuserid
|
|
* @param $start
|
|
* @param $categoryslugs
|
|
* @param $lastip
|
|
* @param bool $onlyvisible
|
|
* @param bool $fulledited
|
|
* @param $count
|
|
* @return array
|
|
*/
|
|
function qa_db_recent_edit_qs_selectspec($voteuserid, $start, $categoryslugs = null, $lastip = null, $onlyvisible = true, $fulledited = false, $count = null)
|
|
{
|
|
$count = isset($count) ? min($count, QA_DB_RETRIEVE_QS_AS) : QA_DB_RETRIEVE_QS_AS;
|
|
|
|
$selectspec = qa_db_posts_basic_selectspec($voteuserid);
|
|
|
|
qa_db_add_selectspec_opost($selectspec, 'editposts', true, $fulledited);
|
|
qa_db_add_selectspec_ousers($selectspec, 'editusers', 'edituserpoints');
|
|
|
|
$selectspec['source'] .=
|
|
" JOIN ^posts AS parentposts ON" .
|
|
" ^posts.postid=IF(LEFT(parentposts.type, 1)='Q', parentposts.postid, parentposts.parentid)" .
|
|
" JOIN ^posts AS editposts ON parentposts.postid=IF(LEFT(editposts.type, 1)='Q', editposts.postid, editposts.parentid)" .
|
|
(QA_FINAL_EXTERNAL_USERS ? "" : " LEFT JOIN ^users AS editusers ON editposts.lastuserid=editusers.userid") .
|
|
" LEFT JOIN ^userpoints AS edituserpoints ON editposts.lastuserid=edituserpoints.userid" .
|
|
" JOIN (SELECT postid FROM ^posts WHERE " .
|
|
qa_db_categoryslugs_sql_args($categoryslugs, $selectspec['arguments']) .
|
|
(isset($lastip) ? "lastip=UNHEX($) AND " : "") .
|
|
($onlyvisible ? "type IN ('Q', 'A', 'C')" : "1") .
|
|
" ORDER BY ^posts.updated DESC LIMIT #,#) y ON editposts.postid=y.postid" .
|
|
($onlyvisible ? " WHERE parentposts.type IN ('Q', 'A', 'C') AND ^posts.type IN ('Q', 'A', 'C')" : "");
|
|
|
|
if (isset($lastip)) {
|
|
$selectspec['arguments'][] = bin2hex(@inet_pton($lastip));
|
|
}
|
|
|
|
array_push($selectspec['arguments'], $start, $count);
|
|
|
|
$selectspec['sortdesc'] = 'otime';
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the antecedent questions for the most flagged posts, with the corresponding vote
|
|
* on those questions made by $voteuserid (if not null). Return $count (if null, a default is used) questions starting
|
|
* from offset $start. The selectspec will also retrieve some information about the flagged posts themselves (including
|
|
* the content if $fullflagged is true).
|
|
* @param $voteuserid
|
|
* @param $start
|
|
* @param bool $fullflagged
|
|
* @param $count
|
|
* @return array
|
|
*/
|
|
function qa_db_flagged_post_qs_selectspec($voteuserid, $start, $fullflagged = false, $count = null)
|
|
{
|
|
$count = isset($count) ? min($count, QA_DB_RETRIEVE_QS_AS) : QA_DB_RETRIEVE_QS_AS;
|
|
|
|
$selectspec = qa_db_posts_basic_selectspec($voteuserid);
|
|
|
|
qa_db_add_selectspec_opost($selectspec, 'flagposts', false, $fullflagged);
|
|
qa_db_add_selectspec_ousers($selectspec, 'flagusers', 'flaguserpoints');
|
|
|
|
$selectspec['source'] .=
|
|
" JOIN ^posts AS parentposts ON" .
|
|
" ^posts.postid=IF(LEFT(parentposts.type, 1)='Q', parentposts.postid, parentposts.parentid)" .
|
|
" JOIN ^posts AS flagposts ON parentposts.postid=IF(LEFT(flagposts.type, 1)='Q', flagposts.postid, flagposts.parentid)" .
|
|
(QA_FINAL_EXTERNAL_USERS ? "" : " LEFT JOIN ^users AS flagusers ON flagposts.userid=flagusers.userid") .
|
|
" LEFT JOIN ^userpoints AS flaguserpoints ON flagposts.userid=flaguserpoints.userid" .
|
|
" JOIN (SELECT postid FROM ^posts WHERE flagcount>0 AND type IN ('Q', 'A', 'C') ORDER BY ^posts.flagcount DESC, ^posts.created DESC LIMIT #,#) y ON flagposts.postid=y.postid";
|
|
|
|
array_push($selectspec['arguments'], $start, $count);
|
|
|
|
$selectspec['sortdesc'] = 'oflagcount';
|
|
$selectspec['sortdesc_2'] = 'otime';
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the posts in $postids, with the corresponding vote on those posts made by
|
|
* $voteuserid (if not null). Returns full information if $full is true.
|
|
* @param $voteuserid
|
|
* @param $postids
|
|
* @param bool $full
|
|
* @return array
|
|
*/
|
|
function qa_db_posts_selectspec($voteuserid, $postids, $full = false)
|
|
{
|
|
$selectspec = qa_db_posts_basic_selectspec($voteuserid, $full);
|
|
|
|
$selectspec['source'] .= " WHERE ^posts.postid IN (#)";
|
|
$selectspec['arguments'][] = $postids;
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the basetype for the posts in $postids, as an array mapping postid => basetype
|
|
* @param $postids
|
|
* @return array
|
|
*/
|
|
function qa_db_posts_basetype_selectspec($postids)
|
|
{
|
|
return array(
|
|
'columns' => array('postid', 'basetype' => 'LEFT(type, 1)'),
|
|
'source' => "^posts WHERE postid IN (#)",
|
|
'arguments' => array($postids),
|
|
'arraykey' => 'postid',
|
|
'arrayvalue' => 'basetype',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the basetype for the posts in $postids, as an array mapping postid => basetype
|
|
* @param $voteuserid
|
|
* @param $postids
|
|
* @param bool $full
|
|
* @return array
|
|
*/
|
|
function qa_db_posts_to_qs_selectspec($voteuserid, $postids, $full = false)
|
|
{
|
|
$selectspec = qa_db_posts_basic_selectspec($voteuserid, $full);
|
|
|
|
$selectspec['columns']['obasetype'] = 'LEFT(childposts.type, 1)';
|
|
$selectspec['columns']['opostid'] = 'childposts.postid';
|
|
|
|
$selectspec['source'] .=
|
|
" JOIN ^posts AS parentposts ON" .
|
|
" ^posts.postid=IF(LEFT(parentposts.type, 1)='Q', parentposts.postid, parentposts.parentid)" .
|
|
" JOIN ^posts AS childposts ON parentposts.postid=IF(LEFT(childposts.type, 1)='Q', childposts.postid, childposts.parentid)" .
|
|
" WHERE childposts.postid IN (#)";
|
|
|
|
$selectspec['arraykey'] = 'opostid';
|
|
$selectspec['arguments'][] = $postids;
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the full information for $postid, with the corresponding vote made by $voteuserid (if not null)
|
|
* @param $voteuserid
|
|
* @param $postid
|
|
* @return array
|
|
*/
|
|
function qa_db_full_post_selectspec($voteuserid, $postid)
|
|
{
|
|
$selectspec = qa_db_posts_basic_selectspec($voteuserid, true);
|
|
|
|
$selectspec['source'] .= " WHERE ^posts.postid=#";
|
|
$selectspec['arguments'][] = $postid;
|
|
$selectspec['single'] = true;
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the full information for all posts whose parent is $parentid, with the
|
|
* corresponding vote made by $voteuserid (if not null)
|
|
* @param $voteuserid
|
|
* @param $parentid
|
|
* @return array
|
|
*/
|
|
function qa_db_full_child_posts_selectspec($voteuserid, $parentid)
|
|
{
|
|
$selectspec = qa_db_posts_basic_selectspec($voteuserid, true);
|
|
|
|
$selectspec['source'] .= " WHERE ^posts.parentid=#";
|
|
$selectspec['arguments'][] = $parentid;
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the full information for all posts whose parent is an answer which
|
|
* has $questionid as its parent, with the corresponding vote made by $voteuserid (if not null)
|
|
* @param $voteuserid
|
|
* @param $questionid
|
|
* @return array
|
|
*/
|
|
function qa_db_full_a_child_posts_selectspec($voteuserid, $questionid)
|
|
{
|
|
$selectspec = qa_db_posts_basic_selectspec($voteuserid, true);
|
|
|
|
$selectspec['source'] .= " JOIN ^posts AS parents ON ^posts.parentid=parents.postid WHERE parents.parentid=# AND LEFT(parents.type, 1)='A'";
|
|
$selectspec['arguments'][] = $questionid;
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the question for the parent of $postid (where $postid is of a follow-on question or comment),
|
|
* i.e. the parent of $questionid's parent if $questionid's parent is an answer, otherwise $questionid's parent itself.
|
|
* @param $postid
|
|
* @return array
|
|
*/
|
|
function qa_db_post_parent_q_selectspec($postid)
|
|
{
|
|
$selectspec = qa_db_posts_basic_selectspec();
|
|
|
|
$selectspec['source'] .= " WHERE ^posts.postid=(SELECT IF(LEFT(parent.type, 1)='A', parent.parentid, parent.postid) FROM ^posts AS child LEFT JOIN ^posts AS parent ON parent.postid=child.parentid WHERE child.postid=# AND parent.type IN('Q','A'))";
|
|
$selectspec['arguments'] = array($postid);
|
|
$selectspec['single'] = true;
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the post (either duplicate question or explanatory note) which has closed $questionid, if any
|
|
* @param $questionid
|
|
* @return array
|
|
*/
|
|
function qa_db_post_close_post_selectspec($questionid)
|
|
{
|
|
$selectspec = qa_db_posts_basic_selectspec(null, true);
|
|
|
|
$selectspec['source'] .= " WHERE ^posts.postid=(SELECT closedbyid FROM ^posts WHERE postid=#)";
|
|
$selectspec['arguments'] = array($questionid);
|
|
$selectspec['single'] = true;
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the posts that have been closed as a duplicate of this question, if any
|
|
* @param $questionid int The canonical question.
|
|
* @return array
|
|
*/
|
|
function qa_db_post_duplicates_selectspec($questionid)
|
|
{
|
|
$selectspec = qa_db_posts_basic_selectspec(null, true);
|
|
|
|
$selectspec['source'] .= " WHERE ^posts.closedbyid=#";
|
|
$selectspec['arguments'] = array($questionid);
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the metadata value for $postid with key $title
|
|
* @param $postid
|
|
* @param $title
|
|
* @return array
|
|
*/
|
|
function qa_db_post_meta_selectspec($postid, $title)
|
|
{
|
|
$selectspec = array(
|
|
'columns' => array('title', 'content'),
|
|
'source' => "^postmetas WHERE postid=# AND " . (is_array($title) ? "title IN ($)" : "title=$"),
|
|
'arguments' => array($postid, $title),
|
|
'arrayvalue' => 'content',
|
|
);
|
|
|
|
if (is_array($title)) {
|
|
$selectspec['arraykey'] = 'title';
|
|
} else {
|
|
$selectspec['single'] = true;
|
|
}
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the most closely related questions to $questionid, with the corresponding vote
|
|
* made by $voteuserid (if not null). Return $count (if null, a default is used) questions. This works by looking for
|
|
* other questions which have title words, tag words or an (exact) category in common.
|
|
* @param $voteuserid
|
|
* @param $questionid
|
|
* @param $count
|
|
* @return array
|
|
*/
|
|
function qa_db_related_qs_selectspec($voteuserid, $questionid, $count = null)
|
|
{
|
|
$count = isset($count) ? min($count, QA_DB_RETRIEVE_QS_AS) : QA_DB_RETRIEVE_QS_AS;
|
|
|
|
$selectspec = qa_db_posts_basic_selectspec($voteuserid);
|
|
|
|
$selectspec['columns'][] = 'score';
|
|
|
|
// added LOG(postid)/1000000 here to ensure ordering is deterministic even if several posts have same score
|
|
|
|
$selectspec['source'] .= " JOIN (SELECT postid, SUM(score)+LOG(postid)/1000000 AS score FROM ((SELECT ^titlewords.postid, LOG(#/titlecount) AS score FROM ^titlewords JOIN ^words ON ^titlewords.wordid=^words.wordid JOIN ^titlewords AS source ON ^titlewords.wordid=source.wordid WHERE source.postid=# AND titlecount<#) UNION ALL (SELECT ^posttags.postid, 2*LOG(#/tagcount) AS score FROM ^posttags JOIN ^words ON ^posttags.wordid=^words.wordid JOIN ^posttags AS source ON ^posttags.wordid=source.wordid WHERE source.postid=# AND tagcount<#) UNION ALL (SELECT ^posts.postid, LOG(#/^categories.qcount) FROM ^posts JOIN ^categories ON ^posts.categoryid=^categories.categoryid AND ^posts.type='Q' WHERE ^categories.categoryid=(SELECT categoryid FROM ^posts WHERE postid=#) AND ^categories.qcount<#)) x WHERE postid!=# GROUP BY postid ORDER BY score DESC LIMIT #) y ON ^posts.postid=y.postid";
|
|
|
|
array_push($selectspec['arguments'], QA_IGNORED_WORDS_FREQ, $questionid, QA_IGNORED_WORDS_FREQ, QA_IGNORED_WORDS_FREQ,
|
|
$questionid, QA_IGNORED_WORDS_FREQ, QA_IGNORED_WORDS_FREQ, $questionid, QA_IGNORED_WORDS_FREQ, $questionid, $count);
|
|
|
|
$selectspec['sortdesc'] = 'score';
|
|
|
|
if (!isset($voteuserid)) {
|
|
$selectspec['caching'] = array(
|
|
'key' => __FUNCTION__ . ":$questionid:$count",
|
|
'ttl' => qa_opt('caching_q_time'),
|
|
);
|
|
}
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the top question matches for a search, with the corresponding vote made by
|
|
* $voteuserid (if not null) and including $full content or not. Return $count (if null, a default is used) questions
|
|
* starting from offset $start. The search is performed for any of $titlewords in the title, $contentwords in the
|
|
* content (of the question or an answer or comment for whom that is the antecedent question), $tagwords in tags, for
|
|
* question author usernames which match a word in $handlewords or which match $handle as a whole. The results also
|
|
* include a 'score' column based on the matching strength and post hotness, and a 'matchparts' column that tells us
|
|
* where the score came from (since a question could get weight from a match in the question itself, and/or weight from
|
|
* a match in its answers, comments, or comments on answers). The 'matchparts' is a comma-separated list of tuples
|
|
* matchtype:matchpostid:matchscore to be used with qa_search_set_max_match().
|
|
* @param $voteuserid
|
|
* @param $titlewords
|
|
* @param $contentwords
|
|
* @param $tagwords
|
|
* @param $handlewords
|
|
* @param $handle
|
|
* @param $start
|
|
* @param bool $full
|
|
* @param $count
|
|
* @return array
|
|
*/
|
|
function qa_db_search_posts_selectspec($voteuserid, $titlewords, $contentwords, $tagwords, $handlewords, $handle, $start, $full = false, $count = null)
|
|
{
|
|
$count = isset($count) ? min($count, QA_DB_RETRIEVE_QS_AS) : QA_DB_RETRIEVE_QS_AS;
|
|
|
|
// add LOG(postid)/1000000 here to ensure ordering is deterministic even if several posts have same score
|
|
// The score also gives a bonus for hot questions, where the bonus scales linearly with hotness. The hottest
|
|
// question gets a bonus equivalent to a matching unique tag, and the least hot question gets zero bonus.
|
|
|
|
$selectspec = qa_db_posts_basic_selectspec($voteuserid, $full);
|
|
|
|
$selectspec['columns'][] = 'score';
|
|
$selectspec['columns'][] = 'matchparts';
|
|
$selectspec['source'] .= " JOIN (SELECT questionid, SUM(score)+2*(LOG(#)*(MAX(^posts.hotness)-(SELECT MIN(hotness) FROM ^posts WHERE type='Q'))/((SELECT MAX(hotness) FROM ^posts WHERE type='Q')-(SELECT MIN(hotness) FROM ^posts WHERE type='Q')))+LOG(questionid)/1000000 AS score, GROUP_CONCAT(CONCAT_WS(':', matchposttype, matchpostid, ROUND(score,3))) AS matchparts FROM (";
|
|
$selectspec['sortdesc'] = 'score';
|
|
array_push($selectspec['arguments'], QA_IGNORED_WORDS_FREQ);
|
|
|
|
$selectparts = 0;
|
|
|
|
if (!empty($titlewords)) {
|
|
// At the indexing stage, duplicate words in title are ignored, so this doesn't count multiple appearances.
|
|
|
|
$selectspec['source'] .= ($selectparts++ ? " UNION ALL " : "") .
|
|
"(SELECT postid AS questionid, LOG(#/titlecount) AS score, 'Q' AS matchposttype, postid AS matchpostid FROM ^titlewords JOIN ^words ON ^titlewords.wordid=^words.wordid WHERE word IN ($) AND titlecount<#)";
|
|
|
|
array_push($selectspec['arguments'], QA_IGNORED_WORDS_FREQ, $titlewords, QA_IGNORED_WORDS_FREQ);
|
|
}
|
|
|
|
if (!empty($contentwords)) {
|
|
// (1-1/(1+count)) weights words in content based on their frequency: If a word appears once in content
|
|
// it's equivalent to 1/2 an appearance in the title (ignoring the contentcount/titlecount factor).
|
|
// If it appears an infinite number of times, it's equivalent to one appearance in the title.
|
|
// This will discourage keyword stuffing while still giving some weight to multiple appearances.
|
|
// On top of that, answer matches are worth half a question match, and comment/note matches half again.
|
|
|
|
$selectspec['source'] .= ($selectparts++ ? " UNION ALL " : "") .
|
|
"(SELECT questionid, (1-1/(1+count))*LOG(#/contentcount)*(CASE ^contentwords.type WHEN 'Q' THEN 1.0 WHEN 'A' THEN 0.5 ELSE 0.25 END) AS score, ^contentwords.type AS matchposttype, ^contentwords.postid AS matchpostid FROM ^contentwords JOIN ^words ON ^contentwords.wordid=^words.wordid WHERE word IN ($) AND contentcount<#)";
|
|
|
|
array_push($selectspec['arguments'], QA_IGNORED_WORDS_FREQ, $contentwords, QA_IGNORED_WORDS_FREQ);
|
|
}
|
|
|
|
if (!empty($tagwords)) {
|
|
// Appearances in the tag words count like 2 appearances in the title (ignoring the tagcount/titlecount factor).
|
|
// This is because tags express explicit semantic intent, whereas titles do not necessarily.
|
|
|
|
$selectspec['source'] .= ($selectparts++ ? " UNION ALL " : "") .
|
|
"(SELECT postid AS questionid, 2*LOG(#/tagwordcount) AS score, 'Q' AS matchposttype, postid AS matchpostid FROM ^tagwords JOIN ^words ON ^tagwords.wordid=^words.wordid WHERE word IN ($) AND tagwordcount<#)";
|
|
|
|
array_push($selectspec['arguments'], QA_IGNORED_WORDS_FREQ, $tagwords, QA_IGNORED_WORDS_FREQ);
|
|
}
|
|
|
|
if (!empty($handlewords)) {
|
|
if (QA_FINAL_EXTERNAL_USERS) {
|
|
require_once QA_INCLUDE_DIR . 'app/users.php';
|
|
|
|
$userids = qa_get_userids_from_public($handlewords);
|
|
|
|
if (count($userids)) {
|
|
$selectspec['source'] .= ($selectparts++ ? " UNION ALL " : "") .
|
|
"(SELECT postid AS questionid, LOG(#/qposts) AS score, 'Q' AS matchposttype, postid AS matchpostid FROM ^posts JOIN ^userpoints ON ^posts.userid=^userpoints.userid WHERE ^posts.userid IN ($) AND type='Q')";
|
|
|
|
array_push($selectspec['arguments'], QA_IGNORED_WORDS_FREQ, $userids);
|
|
}
|
|
|
|
} else {
|
|
$selectspec['source'] .= ($selectparts++ ? " UNION ALL " : "") .
|
|
"(SELECT postid AS questionid, LOG(#/qposts) AS score, 'Q' AS matchposttype, postid AS matchpostid FROM ^posts JOIN ^users ON ^posts.userid=^users.userid JOIN ^userpoints ON ^userpoints.userid=^users.userid WHERE handle IN ($) AND type='Q')";
|
|
|
|
array_push($selectspec['arguments'], QA_IGNORED_WORDS_FREQ, $handlewords);
|
|
}
|
|
}
|
|
|
|
if (strlen($handle)) { // to allow searching for multi-word usernames (only works if search query contains full username and nothing else)
|
|
if (QA_FINAL_EXTERNAL_USERS) {
|
|
$userids = qa_get_userids_from_public(array($handle));
|
|
|
|
if (count($userids)) {
|
|
$selectspec['source'] .= ($selectparts++ ? " UNION ALL " : "") .
|
|
"(SELECT postid AS questionid, LOG(#/qposts) AS score, 'Q' AS matchposttype, postid AS matchpostid FROM ^posts JOIN ^userpoints ON ^posts.userid=^userpoints.userid WHERE ^posts.userid=$ AND type='Q')";
|
|
|
|
array_push($selectspec['arguments'], QA_IGNORED_WORDS_FREQ, reset($userids));
|
|
}
|
|
|
|
} else {
|
|
$selectspec['source'] .= ($selectparts++ ? " UNION ALL " : "") .
|
|
"(SELECT postid AS questionid, LOG(#/qposts) AS score, 'Q' AS matchposttype, postid AS matchpostid FROM ^posts JOIN ^users ON ^posts.userid=^users.userid JOIN ^userpoints ON ^userpoints.userid=^users.userid WHERE handle=$ AND type='Q')";
|
|
|
|
array_push($selectspec['arguments'], QA_IGNORED_WORDS_FREQ, $handle);
|
|
}
|
|
}
|
|
|
|
if ($selectparts == 0) {
|
|
$selectspec['source'] .= '(SELECT NULL as questionid, 0 AS score, NULL AS matchposttype, NULL AS matchpostid FROM ^posts WHERE postid IS NULL)';
|
|
}
|
|
|
|
$selectspec['source'] .= ") x LEFT JOIN ^posts ON ^posts.postid=questionid GROUP BY questionid ORDER BY score DESC LIMIT #,#) y ON ^posts.postid=y.questionid";
|
|
|
|
array_push($selectspec['arguments'], $start, $count);
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Processes the matchparts column in $question which was returned from a search performed via qa_db_search_posts_selectspec()
|
|
* Returns the id of the strongest matching answer or comment, or null if the question itself was the strongest match
|
|
* @param $question
|
|
* @param $type
|
|
* @param $postid
|
|
* @return null
|
|
*/
|
|
function qa_search_set_max_match($question, &$type, &$postid)
|
|
{
|
|
$type = 'Q';
|
|
$postid = $question['postid'];
|
|
$bestscore = null;
|
|
|
|
$matchparts = explode(',', $question['matchparts']);
|
|
foreach ($matchparts as $matchpart) {
|
|
if (sscanf($matchpart, '%1s:%f:%f', $matchposttype, $matchpostid, $matchscore) == 3) {
|
|
if (!isset($bestscore) || $matchscore > $bestscore) {
|
|
$bestscore = $matchscore;
|
|
$type = $matchposttype;
|
|
$postid = $matchpostid;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return a selectspec to retrieve the full information on the category whose id is $slugsorid (if $isid is true),
|
|
* otherwise whose backpath matches $slugsorid
|
|
* @param $slugsorid
|
|
* @param $isid
|
|
* @return array
|
|
*/
|
|
function qa_db_full_category_selectspec($slugsorid, $isid)
|
|
{
|
|
if ($isid) {
|
|
$identifiersql = 'categoryid=#';
|
|
} else {
|
|
$identifiersql = 'backpath=$';
|
|
$slugsorid = qa_db_slugs_to_backpath($slugsorid);
|
|
}
|
|
|
|
return array(
|
|
'columns' => array('categoryid', 'parentid', 'title', 'tags', 'qcount', 'content', 'backpath'),
|
|
'source' => '^categories WHERE ' . $identifiersql,
|
|
'arguments' => array($slugsorid),
|
|
'single' => 'true',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve ($full or not) info on the categories which "surround" the central category specified
|
|
* by $slugsorid, $isid and $ispostid. The "surrounding" categories include all categories (even unrelated) at the
|
|
* top level, any ancestors (at any level) of the category, the category's siblings and sub-categories (to one level).
|
|
* The central category is specified as follows. If $isid AND $ispostid then $slugsorid is the ID of a post with the category.
|
|
* Otherwise if $isid then $slugsorid is the category's own id. Otherwise $slugsorid is the full backpath of the category.
|
|
* @param $slugsorid
|
|
* @param $isid
|
|
* @param bool $ispostid
|
|
* @param bool $full
|
|
* @return array
|
|
*/
|
|
function qa_db_category_nav_selectspec($slugsorid, $isid, $ispostid = false, $full = false)
|
|
{
|
|
if ($isid) {
|
|
if ($ispostid) {
|
|
$identifiersql = 'categoryid=(SELECT categoryid FROM ^posts WHERE postid=#)';
|
|
} else {
|
|
$identifiersql = 'categoryid=#';
|
|
}
|
|
} else {
|
|
$identifiersql = 'backpath=$';
|
|
$slugsorid = qa_db_slugs_to_backpath($slugsorid);
|
|
}
|
|
|
|
$parentselects = array( // requires QA_CATEGORY_DEPTH=4
|
|
'SELECT NULL AS parentkey', // top level
|
|
'SELECT grandparent.parentid FROM ^categories JOIN ^categories AS parent ON ^categories.parentid=parent.categoryid JOIN ^categories AS grandparent ON parent.parentid=grandparent.categoryid WHERE ^categories.' . $identifiersql, // 2 gens up
|
|
'SELECT parent.parentid FROM ^categories JOIN ^categories AS parent ON ^categories.parentid=parent.categoryid WHERE ^categories.' . $identifiersql,
|
|
// 1 gen up
|
|
'SELECT parentid FROM ^categories WHERE ' . $identifiersql, // same gen
|
|
'SELECT categoryid FROM ^categories WHERE ' . $identifiersql, // gen below
|
|
);
|
|
|
|
$columns = array(
|
|
'parentid' => '^categories.parentid',
|
|
'title' => '^categories.title',
|
|
'tags' => '^categories.tags',
|
|
'qcount' => '^categories.qcount',
|
|
'position' => '^categories.position',
|
|
);
|
|
|
|
if ($full) {
|
|
foreach ($columns as $alias => $column) {
|
|
$columns[$alias] = 'MAX(' . $column . ')';
|
|
}
|
|
|
|
$columns['childcount'] = 'COUNT(child.categoryid)';
|
|
$columns['content'] = 'MAX(^categories.content)';
|
|
$columns['backpath'] = 'MAX(^categories.backpath)';
|
|
}
|
|
|
|
array_unshift($columns, '^categories.categoryid');
|
|
|
|
$selectspec = array(
|
|
'columns' => $columns,
|
|
'source' => '^categories JOIN (' . implode(' UNION ', $parentselects) . ') y ON ^categories.parentid<=>parentkey' .
|
|
($full ? ' LEFT JOIN ^categories AS child ON child.parentid=^categories.categoryid GROUP BY ^categories.categoryid' : '') .
|
|
' ORDER BY ^categories.position',
|
|
'arguments' => array($slugsorid, $slugsorid, $slugsorid, $slugsorid),
|
|
'arraykey' => 'categoryid',
|
|
'sortasc' => 'position',
|
|
);
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve information on all subcategories of $categoryid (used for Ajax navigation of hierarchy)
|
|
* @param $categoryid
|
|
* @return array
|
|
*/
|
|
function qa_db_category_sub_selectspec($categoryid)
|
|
{
|
|
return array(
|
|
'columns' => array('categoryid', 'title', 'tags', 'qcount', 'position'),
|
|
'source' => '^categories WHERE parentid<=># ORDER BY position',
|
|
'arguments' => array($categoryid),
|
|
'arraykey' => 'categoryid',
|
|
'sortasc' => 'position',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve a single category as specified by its $slugs (in order of hierarchy)
|
|
* @param $slugs
|
|
* @return array
|
|
*/
|
|
function qa_db_slugs_to_category_id_selectspec($slugs)
|
|
{
|
|
return array(
|
|
'columns' => array('categoryid'),
|
|
'source' => '^categories WHERE backpath=$',
|
|
'arguments' => array(qa_db_slugs_to_backpath($slugs)),
|
|
'arrayvalue' => 'categoryid',
|
|
'single' => true,
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the list of custom pages or links, ordered for display
|
|
* @param $onlynavin
|
|
* @param $onlypageids
|
|
* @return array
|
|
*/
|
|
function qa_db_pages_selectspec($onlynavin = null, $onlypageids = null)
|
|
{
|
|
$selectspec = array(
|
|
// +0 required to work around MySQL bug where by permit value is mis-read as signed, e.g. -106 instead of 150
|
|
'columns' => array('pageid', 'title', 'flags', 'permit' => 'permit+0', 'nav', 'tags', 'position', 'heading'),
|
|
'arraykey' => 'pageid',
|
|
'sortasc' => 'position',
|
|
);
|
|
|
|
if (isset($onlypageids)) {
|
|
$selectspec['source'] = '^pages WHERE pageid IN (#)';
|
|
$selectspec['arguments'] = array($onlypageids);
|
|
} elseif (isset($onlynavin)) {
|
|
$selectspec['source'] = '^pages WHERE nav IN ($) ORDER BY position';
|
|
$selectspec['arguments'] = array($onlynavin);
|
|
} else {
|
|
$selectspec['source'] = '^pages ORDER BY position';
|
|
}
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the list of widgets, ordered for display
|
|
*/
|
|
function qa_db_widgets_selectspec()
|
|
{
|
|
return array(
|
|
'columns' => array('widgetid', 'place', 'position', 'tags', 'title'),
|
|
'source' => '^widgets ORDER BY position',
|
|
'sortasc' => 'position',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the full information about a custom page
|
|
* @param $slugorpageid
|
|
* @param $ispageid
|
|
* @return array
|
|
*/
|
|
function qa_db_page_full_selectspec($slugorpageid, $ispageid)
|
|
{
|
|
return array(
|
|
'columns' => array('pageid', 'title', 'flags', 'permit', 'nav', 'tags', 'position', 'heading', 'content'),
|
|
'source' => '^pages WHERE ' . ($ispageid ? 'pageid' : 'tags') . '=$',
|
|
'arguments' => array($slugorpageid),
|
|
'single' => true,
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the most recent questions with $tag, with the corresponding vote on those
|
|
* questions made by $voteuserid (if not null) and including $full content or not. Return $count (if null, a default is
|
|
* used) questions starting from $start.
|
|
* @param $voteuserid
|
|
* @param $tag
|
|
* @param $start
|
|
* @param bool $full
|
|
* @param $count
|
|
* @return array
|
|
*/
|
|
function qa_db_tag_recent_qs_selectspec($voteuserid, $tag, $start, $full = false, $count = null)
|
|
{
|
|
$count = isset($count) ? min($count, QA_DB_RETRIEVE_QS_AS) : QA_DB_RETRIEVE_QS_AS;
|
|
|
|
require_once QA_INCLUDE_DIR . 'util/string.php';
|
|
|
|
$selectspec = qa_db_posts_basic_selectspec($voteuserid, $full);
|
|
|
|
// use two tests here - one which can use the index, and the other which narrows it down exactly - then limit to 1 just in case
|
|
$selectspec['source'] .= " JOIN (SELECT postid FROM ^posttags WHERE wordid=(SELECT wordid FROM ^words WHERE word=$ AND word=$ COLLATE utf8_bin LIMIT 1) ORDER BY postcreated DESC LIMIT #,#) y ON ^posts.postid=y.postid";
|
|
array_push($selectspec['arguments'], $tag, qa_strtolower($tag), $start, $count);
|
|
$selectspec['sortdesc'] = 'created';
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the number of questions tagged with $tag (single value)
|
|
* @param $tag
|
|
* @return array
|
|
*/
|
|
function qa_db_tag_word_selectspec($tag)
|
|
{
|
|
return array(
|
|
'columns' => array('wordid', 'word', 'tagcount'),
|
|
'source' => '^words WHERE word=$ AND word=$ COLLATE utf8_bin',
|
|
'arguments' => array($tag, qa_strtolower($tag)),
|
|
'single' => true,
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve recent questions by the user identified by $identifier, where $identifier is a
|
|
* handle if we're using internal user management, or a userid if we're using external users. Also include the
|
|
* corresponding vote on those questions made by $voteuserid (if not null). Return $count (if null, a default is used)
|
|
* questions.
|
|
* @param $voteuserid
|
|
* @param $identifier
|
|
* @param $count
|
|
* @param int $start
|
|
* @return array
|
|
*/
|
|
function qa_db_user_recent_qs_selectspec($voteuserid, $identifier, $count = null, $start = 0)
|
|
{
|
|
$count = isset($count) ? min($count, QA_DB_RETRIEVE_QS_AS) : QA_DB_RETRIEVE_QS_AS;
|
|
|
|
$selectspec = qa_db_posts_basic_selectspec($voteuserid);
|
|
|
|
$selectspec['source'] .= " WHERE ^posts.userid=" . (QA_FINAL_EXTERNAL_USERS ? "$" : "(SELECT userid FROM ^users WHERE handle=$ LIMIT 1)") . " AND type='Q' ORDER BY ^posts.created DESC LIMIT #,#";
|
|
array_push($selectspec['arguments'], $identifier, $start, $count);
|
|
$selectspec['sortdesc'] = 'created';
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the antecedent questions for recent answers by the user identified by $identifier
|
|
* (see qa_db_user_recent_qs_selectspec() comment), with the corresponding vote on those questions made by $voteuserid
|
|
* (if not null). Return $count (if null, a default is used) questions. The selectspec will also retrieve some
|
|
* information about the answers themselves, in columns named with the prefix 'o'.
|
|
* @param $voteuserid
|
|
* @param $identifier
|
|
* @param $count
|
|
* @param int $start
|
|
* @return array
|
|
*/
|
|
function qa_db_user_recent_a_qs_selectspec($voteuserid, $identifier, $count = null, $start = 0)
|
|
{
|
|
$count = isset($count) ? min($count, QA_DB_RETRIEVE_QS_AS) : QA_DB_RETRIEVE_QS_AS;
|
|
|
|
$selectspec = qa_db_posts_basic_selectspec($voteuserid);
|
|
|
|
qa_db_add_selectspec_opost($selectspec, 'aposts');
|
|
|
|
$selectspec['columns']['oupvotes'] = 'aposts.upvotes';
|
|
$selectspec['columns']['odownvotes'] = 'aposts.downvotes';
|
|
$selectspec['columns']['onetvotes'] = 'aposts.netvotes';
|
|
|
|
$selectspec['source'] .=
|
|
" JOIN ^posts AS aposts ON ^posts.postid=aposts.parentid" .
|
|
" JOIN (SELECT postid FROM ^posts WHERE " .
|
|
" userid=" . (QA_FINAL_EXTERNAL_USERS ? "$" : "(SELECT userid FROM ^users WHERE handle=$ LIMIT 1)") .
|
|
" AND type='A' ORDER BY created DESC LIMIT #,#) y ON aposts.postid=y.postid WHERE ^posts.type='Q'";
|
|
|
|
array_push($selectspec['arguments'], $identifier, $start, $count);
|
|
$selectspec['sortdesc'] = 'otime';
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the antecedent questions for recent comments by the user identified by $identifier
|
|
* (see qa_db_user_recent_qs_selectspec() comment), with the corresponding vote on those questions made by $voteuserid
|
|
* (if not null). Return $count (if null, a default is used) questions. The selectspec will also retrieve some
|
|
* information about the comments themselves, in columns named with the prefix 'o'.
|
|
* @param $voteuserid
|
|
* @param $identifier
|
|
* @param $count
|
|
* @return array
|
|
*/
|
|
function qa_db_user_recent_c_qs_selectspec($voteuserid, $identifier, $count = null)
|
|
{
|
|
$count = isset($count) ? min($count, QA_DB_RETRIEVE_QS_AS) : QA_DB_RETRIEVE_QS_AS;
|
|
|
|
$selectspec = qa_db_posts_basic_selectspec($voteuserid);
|
|
|
|
qa_db_add_selectspec_opost($selectspec, 'cposts');
|
|
|
|
$selectspec['source'] .=
|
|
" JOIN ^posts AS parentposts ON" .
|
|
" ^posts.postid=(CASE parentposts.type WHEN 'A' THEN parentposts.parentid ELSE parentposts.postid END)" .
|
|
" JOIN ^posts AS cposts ON parentposts.postid=cposts.parentid" .
|
|
" JOIN (SELECT postid FROM ^posts WHERE " .
|
|
" userid=" . (QA_FINAL_EXTERNAL_USERS ? "$" : "(SELECT userid FROM ^users WHERE handle=$ LIMIT 1)") .
|
|
" AND type='C' ORDER BY created DESC LIMIT #) y ON cposts.postid=y.postid WHERE ^posts.type='Q' AND parentposts.type IN ('Q', 'A')";
|
|
|
|
array_push($selectspec['arguments'], $identifier, $count);
|
|
$selectspec['sortdesc'] = 'otime';
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the antecedent questions for recently edited posts by the user identified by
|
|
* $identifier (see qa_db_user_recent_qs_selectspec() comment), with the corresponding vote on those questions made by
|
|
* $voteuserid (if not null). Return $count (if null, a default is used) questions. The selectspec will also retrieve
|
|
* some information about the edited posts themselves, in columns named with the prefix 'o'.
|
|
* @param $voteuserid
|
|
* @param $identifier
|
|
* @param $count
|
|
* @return array
|
|
*/
|
|
function qa_db_user_recent_edit_qs_selectspec($voteuserid, $identifier, $count = null)
|
|
{
|
|
$count = isset($count) ? min($count, QA_DB_RETRIEVE_QS_AS) : QA_DB_RETRIEVE_QS_AS;
|
|
|
|
$selectspec = qa_db_posts_basic_selectspec($voteuserid);
|
|
|
|
qa_db_add_selectspec_opost($selectspec, 'editposts', true);
|
|
|
|
$selectspec['source'] .=
|
|
" JOIN ^posts AS parentposts ON" .
|
|
" ^posts.postid=IF(LEFT(parentposts.type, 1)='Q', parentposts.postid, parentposts.parentid)" .
|
|
" JOIN ^posts AS editposts ON parentposts.postid=IF(LEFT(editposts.type, 1)='Q', editposts.postid, editposts.parentid)" .
|
|
" JOIN (SELECT postid FROM ^posts WHERE " .
|
|
" lastuserid=" . (QA_FINAL_EXTERNAL_USERS ? "$" : "(SELECT userid FROM ^users WHERE handle=$ LIMIT 1)") .
|
|
" AND type IN ('Q', 'A', 'C') ORDER BY updated DESC LIMIT #) y ON editposts.postid=y.postid " .
|
|
" WHERE parentposts.type IN ('Q', 'A', 'C') AND ^posts.type IN ('Q', 'A', 'C')";
|
|
|
|
array_push($selectspec['arguments'], $identifier, $count);
|
|
$selectspec['sortdesc'] = 'otime';
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the most popular tags. Return $count (if null, a default is used) tags, starting
|
|
* from offset $start. The selectspec will produce a sorted array with tags in the key, and counts in the values.
|
|
* @param $start
|
|
* @param $count
|
|
* @return array
|
|
*/
|
|
function qa_db_popular_tags_selectspec($start, $count = null)
|
|
{
|
|
$count = isset($count) ? $count : QA_DB_RETRIEVE_TAGS;
|
|
|
|
return array(
|
|
'columns' => array('word', 'tagcount'),
|
|
'source' => '^words JOIN (SELECT wordid FROM ^words WHERE tagcount>0 ORDER BY tagcount DESC LIMIT #,#) y ON ^words.wordid=y.wordid',
|
|
'arguments' => array($start, $count),
|
|
'arraykey' => 'word',
|
|
'arrayvalue' => 'tagcount',
|
|
'sortdesc' => 'tagcount',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the list of user profile fields, ordered for display
|
|
*/
|
|
function qa_db_userfields_selectspec()
|
|
{
|
|
return array(
|
|
'columns' => array('fieldid', 'title', 'content', 'flags', 'permit', 'position'),
|
|
'source' => '^userfields',
|
|
'arraykey' => 'title',
|
|
'sortasc' => 'position',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selecspec to retrieve a single array with details of the account of the user identified by
|
|
* $useridhandle, which should be a userid if $isuserid is true, otherwise $useridhandle should be a handle.
|
|
* @param $useridhandle
|
|
* @param $isuserid
|
|
* @return array
|
|
*/
|
|
function qa_db_user_account_selectspec($useridhandle, $isuserid)
|
|
{
|
|
return array(
|
|
'columns' => array(
|
|
'^users.userid', 'passsalt', 'passcheck' => 'HEX(passcheck)', 'passhash', 'email', 'level', 'emailcode', 'handle',
|
|
'created' => 'UNIX_TIMESTAMP(created)', 'sessioncode', 'sessionsource', 'flags', 'loggedin' => 'UNIX_TIMESTAMP(loggedin)',
|
|
'loginip', 'written' => 'UNIX_TIMESTAMP(written)', 'writeip',
|
|
'avatarblobid' => 'BINARY avatarblobid', // cast to BINARY due to MySQL bug which renders it signed in a union
|
|
'avatarwidth', 'avatarheight', 'points', 'wallposts',
|
|
),
|
|
|
|
'source' => '^users LEFT JOIN ^userpoints ON ^userpoints.userid=^users.userid WHERE ^users.' . ($isuserid ? 'userid' : 'handle') . '=$',
|
|
'arguments' => array($useridhandle),
|
|
'single' => true,
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve all user profile information of the user identified by
|
|
* $useridhandle (see qa_db_user_account_selectspec() comment), as an array of [field] => [value]
|
|
* @param $useridhandle
|
|
* @param $isuserid
|
|
* @return array
|
|
*/
|
|
function qa_db_user_profile_selectspec($useridhandle, $isuserid)
|
|
{
|
|
return array(
|
|
'columns' => array('title', 'content'),
|
|
'source' => '^userprofile WHERE userid=' . ($isuserid ? '$' : '(SELECT userid FROM ^users WHERE handle=$ LIMIT 1)'),
|
|
'arguments' => array($useridhandle),
|
|
'arraykey' => 'title',
|
|
'arrayvalue' => 'content',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve all notices for the user $userid
|
|
* @param $userid
|
|
* @return array
|
|
*/
|
|
function qa_db_user_notices_selectspec($userid)
|
|
{
|
|
return array(
|
|
'columns' => array('noticeid', 'content', 'format', 'tags', 'created' => 'UNIX_TIMESTAMP(created)'),
|
|
'source' => '^usernotices WHERE userid=$ ORDER BY created',
|
|
'arguments' => array($userid),
|
|
'sortasc' => 'created',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve all columns from the userpoints table for the user identified by $identifier
|
|
* (see qa_db_user_recent_qs_selectspec() comment), as a single array
|
|
* @param $identifier
|
|
* @param bool $isuserid
|
|
* @return array
|
|
*/
|
|
function qa_db_user_points_selectspec($identifier, $isuserid = QA_FINAL_EXTERNAL_USERS)
|
|
{
|
|
return array(
|
|
'columns' => array('points', 'qposts', 'aposts', 'cposts', 'aselects', 'aselecteds', 'qupvotes', 'qdownvotes', 'aupvotes', 'adownvotes', 'qvoteds', 'avoteds', 'upvoteds', 'downvoteds', 'bonus'),
|
|
'source' => '^userpoints WHERE userid=' . ($isuserid ? '$' : '(SELECT userid FROM ^users WHERE handle=$ LIMIT 1)'),
|
|
'arguments' => array($identifier),
|
|
'single' => true,
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to calculate the rank in points of the user identified by $identifier
|
|
* (see qa_db_user_recent_qs_selectspec() comment), as a single value
|
|
* @param $identifier
|
|
* @param bool $isuserid
|
|
* @return array
|
|
*/
|
|
function qa_db_user_rank_selectspec($identifier, $isuserid = QA_FINAL_EXTERNAL_USERS)
|
|
{
|
|
return array(
|
|
'columns' => array('rank' => '1+COUNT(*)'),
|
|
'source' => '^userpoints WHERE points>COALESCE((SELECT points FROM ^userpoints WHERE userid=' . ($isuserid ? '$' : '(SELECT userid FROM ^users WHERE handle=$ LIMIT 1)') . '), 0)',
|
|
'arguments' => array($identifier),
|
|
'arrayvalue' => 'rank',
|
|
'single' => true,
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to get the top scoring users, with handles if we're using internal user management. Return
|
|
* $count (if null, a default is used) users starting from the offset $start.
|
|
* @param $start
|
|
* @param $count
|
|
* @return array
|
|
*/
|
|
function qa_db_top_users_selectspec($start, $count = null)
|
|
{
|
|
$count = isset($count) ? min($count, QA_DB_RETRIEVE_USERS) : QA_DB_RETRIEVE_USERS;
|
|
|
|
if (QA_FINAL_EXTERNAL_USERS) {
|
|
return array(
|
|
'columns' => array('userid', 'points'),
|
|
'source' => '^userpoints ORDER BY points DESC LIMIT #,#',
|
|
'arguments' => array($start, $count),
|
|
'arraykey' => 'userid',
|
|
'sortdesc' => 'points',
|
|
);
|
|
}
|
|
|
|
// If the site is configured to share the ^users table then there might not be a record in the ^userpoints table
|
|
if (defined('QA_MYSQL_USERS_PREFIX')) {
|
|
$basePoints = (int)qa_opt('points_base');
|
|
$source = '^users JOIN (SELECT ^users.userid, COALESCE(points,' . $basePoints . ') AS points FROM ^users LEFT JOIN ^userpoints ON ^users.userid=^userpoints.userid ORDER BY points DESC LIMIT #,#) y ON ^users.userid=y.userid';
|
|
} else {
|
|
$source = '^users JOIN (SELECT userid FROM ^userpoints ORDER BY points DESC LIMIT #,#) y ON ^users.userid=y.userid JOIN ^userpoints ON ^users.userid=^userpoints.userid';;
|
|
}
|
|
|
|
return array(
|
|
'columns' => array('^users.userid', 'handle', 'points', 'flags', '^users.email', 'avatarblobid' => 'BINARY avatarblobid', 'avatarwidth', 'avatarheight'),
|
|
'source' => $source,
|
|
'arguments' => array($start, $count),
|
|
'arraykey' => 'userid',
|
|
'sortdesc' => 'points',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to get the newest users. Return $count (if null, a default is used) users starting from the
|
|
* offset $start. This query must not be run when using external users
|
|
* @param $start
|
|
* @param $count
|
|
* @return array
|
|
*/
|
|
function qa_db_newest_users_selectspec($start, $count = null)
|
|
{
|
|
$count = isset($count) ? min($count, QA_DB_RETRIEVE_USERS) : QA_DB_RETRIEVE_USERS;
|
|
|
|
return array(
|
|
'columns' => array('userid', 'handle', 'flags', 'email', 'created' => 'UNIX_TIMESTAMP(created)', 'avatarblobid' => 'BINARY avatarblobid', 'avatarwidth', 'avatarheight'),
|
|
'source' => '^users ORDER BY created DESC, userid DESC LIMIT #,#',
|
|
'arguments' => array($start, $count),
|
|
'sortdesc' => 'created',
|
|
'sortdesc_2' => 'userid',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to get information about users at a certain privilege level or higher
|
|
* @param $level
|
|
* @return array
|
|
*/
|
|
function qa_db_users_from_level_selectspec($level)
|
|
{
|
|
return array(
|
|
'columns' => array('^users.userid', 'handle', 'level'),
|
|
'source' => '^users WHERE level>=# ORDER BY level DESC',
|
|
'arguments' => array($level),
|
|
'sortdesc' => 'level',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to get information about users with the $flag bit set (unindexed query)
|
|
* @param $flag
|
|
* @param int $start
|
|
* @param $limit
|
|
* @return array
|
|
*/
|
|
function qa_db_users_with_flag_selectspec($flag, $start = 0, $limit = null)
|
|
{
|
|
$source = '^users WHERE (flags & #)';
|
|
$arguments = array($flag);
|
|
|
|
if (isset($limit)) {
|
|
$limit = min($limit, QA_DB_RETRIEVE_USERS);
|
|
$source .= ' LIMIT #,#';
|
|
array_push($arguments, $start, $limit);
|
|
}
|
|
|
|
return array(
|
|
'columns' => array('^users.userid', 'handle', 'flags', 'level'),
|
|
'source' => $source,
|
|
'arguments' => $arguments,
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return columns for standard messages selectspec
|
|
*/
|
|
function qa_db_messages_columns()
|
|
{
|
|
return array(
|
|
'messageid', 'fromuserid', 'touserid', 'content', 'format',
|
|
'created' => 'UNIX_TIMESTAMP(^messages.created)',
|
|
|
|
'fromflags' => 'ufrom.flags', 'fromlevel' => 'ufrom.level',
|
|
'fromemail' => 'ufrom.email', 'fromhandle' => 'ufrom.handle',
|
|
'fromavatarblobid' => 'BINARY ufrom.avatarblobid', // cast to BINARY due to MySQL bug which renders it signed in a union
|
|
'fromavatarwidth' => 'ufrom.avatarwidth', 'fromavatarheight' => 'ufrom.avatarheight',
|
|
|
|
'toflags' => 'uto.flags', 'tolevel' => 'uto.level',
|
|
'toemail' => 'uto.email', 'tohandle' => 'uto.handle',
|
|
'toavatarblobid' => 'BINARY uto.avatarblobid', // cast to BINARY due to MySQL bug which renders it signed in a union
|
|
'toavatarwidth' => 'uto.avatarwidth', 'toavatarheight' => 'uto.avatarheight',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* If $fromidentifier is not null, return the selectspec to get recent private messages which have been sent from
|
|
* the user identified by $fromidentifier+$fromisuserid to the user identified by $toidentifier+$toisuserid (see
|
|
* qa_db_user_recent_qs_selectspec() comment). If $fromidentifier is null, then get recent wall posts
|
|
* for the user identified by $toidentifier+$toisuserid. Return $count (if null, a default is used) messages.
|
|
* @param $fromidentifier
|
|
* @param $fromisuserid
|
|
* @param $toidentifier
|
|
* @param $toisuserid
|
|
* @param $count
|
|
* @param int $start
|
|
* @return array
|
|
*/
|
|
function qa_db_recent_messages_selectspec($fromidentifier, $fromisuserid, $toidentifier, $toisuserid, $count = null, $start = 0)
|
|
{
|
|
$count = isset($count) ? min($count, QA_DB_RETRIEVE_MESSAGES) : QA_DB_RETRIEVE_MESSAGES;
|
|
|
|
if (isset($fromidentifier)) {
|
|
$fromsub = $fromisuserid ? '$' : '(SELECT userid FROM ^users WHERE handle=$ LIMIT 1)';
|
|
$where = 'fromuserid=' . $fromsub . " AND type='PRIVATE'";
|
|
} else {
|
|
$where = "type='PUBLIC'";
|
|
}
|
|
$tosub = $toisuserid ? '$' : '(SELECT userid FROM ^users WHERE handle=$ LIMIT 1)';
|
|
|
|
$source = '^messages LEFT JOIN ^users ufrom ON fromuserid=ufrom.userid LEFT JOIN ^users uto ON touserid=uto.userid WHERE ' . $where . ' AND touserid=' . $tosub . ' ORDER BY ^messages.created DESC LIMIT #,#';
|
|
|
|
$arguments = isset($fromidentifier) ? array($fromidentifier, $toidentifier, $start, $count) : array($toidentifier, $start, $count);
|
|
|
|
return array(
|
|
'columns' => qa_db_messages_columns(),
|
|
'source' => $source,
|
|
'arguments' => $arguments,
|
|
'arraykey' => 'messageid',
|
|
'sortdesc' => 'created',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get selectspec for messages *to* specified user. $type is either 'public' or 'private'.
|
|
* $toidentifier is a handle or userid depending on the value of $toisuserid.
|
|
* Returns $limit messages, or all of them if $limit is null (used in qa_db_selectspec_count).
|
|
* @param $type
|
|
* @param $toidentifier
|
|
* @param $toisuserid
|
|
* @param int $start
|
|
* @param $limit
|
|
* @return array
|
|
*/
|
|
function qa_db_messages_inbox_selectspec($type, $toidentifier, $toisuserid, $start = 0, $limit = null)
|
|
{
|
|
$type = strtoupper($type);
|
|
|
|
$where = 'touserid=' . ($toisuserid ? '$' : '(SELECT userid FROM ^users WHERE handle=$ LIMIT 1)') . ' AND type=$ AND tohidden=0';
|
|
$source = '^messages LEFT JOIN ^users ufrom ON fromuserid=ufrom.userid LEFT JOIN ^users uto ON touserid=uto.userid WHERE ' . $where . ' ORDER BY ^messages.created DESC';
|
|
$arguments = array($toidentifier, $type);
|
|
|
|
if (isset($limit)) {
|
|
$limit = min($limit, QA_DB_RETRIEVE_MESSAGES);
|
|
$source .= ' LIMIT #,#';
|
|
$arguments[] = $start;
|
|
$arguments[] = $limit;
|
|
}
|
|
|
|
return array(
|
|
'columns' => qa_db_messages_columns(),
|
|
'source' => $source,
|
|
'arguments' => $arguments,
|
|
'arraykey' => 'messageid',
|
|
'sortdesc' => 'created',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get selectspec for messages *from* specified user. $type is either 'public' or 'private'.
|
|
* $fromidentifier is a handle or userid depending on the value of $fromisuserid.
|
|
* Returns $limit messages, or all of them if $limit is null (used in qa_db_selectspec_count).
|
|
* @param $type
|
|
* @param $fromidentifier
|
|
* @param $fromisuserid
|
|
* @param int $start
|
|
* @param $limit
|
|
* @return array
|
|
*/
|
|
function qa_db_messages_outbox_selectspec($type, $fromidentifier, $fromisuserid, $start = 0, $limit = null)
|
|
{
|
|
$type = strtoupper($type);
|
|
|
|
$where = 'fromuserid=' . ($fromisuserid ? '$' : '(SELECT userid FROM ^users WHERE handle=$ LIMIT 1)') . ' AND type=$ AND fromhidden=0';
|
|
$source = '^messages LEFT JOIN ^users ufrom ON fromuserid=ufrom.userid LEFT JOIN ^users uto ON touserid=uto.userid WHERE ' . $where . ' ORDER BY ^messages.created DESC';
|
|
$arguments = array($fromidentifier, $type);
|
|
|
|
if (isset($limit)) {
|
|
$limit = min($limit, QA_DB_RETRIEVE_MESSAGES);
|
|
$source .= ' LIMIT #,#';
|
|
$arguments[] = $start;
|
|
$arguments[] = $limit;
|
|
}
|
|
|
|
return array(
|
|
'columns' => qa_db_messages_columns(),
|
|
'source' => $source,
|
|
'arguments' => $arguments,
|
|
'arraykey' => 'messageid',
|
|
'sortdesc' => 'created',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve whether or not $userid has favorited entity $entitytype identifier by $identifier.
|
|
* The $identifier should be a handle, word, backpath or postid for users, tags, categories and questions respectively.
|
|
* @param $userid
|
|
* @param $entitytype
|
|
* @param $identifier
|
|
* @return array
|
|
*/
|
|
function qa_db_is_favorite_selectspec($userid, $entitytype, $identifier)
|
|
{
|
|
require_once QA_INCLUDE_DIR . 'app/updates.php';
|
|
|
|
$selectspec = array(
|
|
'columns' => array('flags' => 'COUNT(*)'),
|
|
'source' => '^userfavorites WHERE userid=$ AND entitytype=$',
|
|
'arrayvalue' => 'flags',
|
|
'single' => true,
|
|
);
|
|
|
|
switch ($entitytype) {
|
|
case QA_ENTITY_USER:
|
|
$selectspec['source'] .= ' AND entityid=(SELECT userid FROM ^users WHERE handle=$ LIMIT 1)';
|
|
break;
|
|
|
|
case QA_ENTITY_TAG:
|
|
$selectspec['source'] .= ' AND entityid=(SELECT wordid FROM ^words WHERE word=$ LIMIT 1)';
|
|
break;
|
|
|
|
case QA_ENTITY_CATEGORY:
|
|
$selectspec['source'] .= ' AND entityid=(SELECT categoryid FROM ^categories WHERE backpath=$ LIMIT 1)';
|
|
$identifier = qa_db_slugs_to_backpath($identifier);
|
|
break;
|
|
|
|
default:
|
|
$selectspec['source'] .= ' AND entityid=$';
|
|
break;
|
|
}
|
|
|
|
$selectspec['arguments'] = array($userid, $entitytype, $identifier);
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve an array of $userid's favorited questions, with the usual information.
|
|
* Returns $limit questions, or all of them if $limit is null (used in qa_db_selectspec_count).
|
|
* @param $userid
|
|
* @param $limit
|
|
* @param int $start
|
|
* @return array
|
|
*/
|
|
function qa_db_user_favorite_qs_selectspec($userid, $limit = null, $start = 0)
|
|
{
|
|
require_once QA_INCLUDE_DIR . 'app/updates.php';
|
|
|
|
$selectspec = qa_db_posts_basic_selectspec($userid);
|
|
|
|
$selectspec['source'] .= ' JOIN ^userfavorites AS selectfave ON ^posts.postid=selectfave.entityid WHERE selectfave.userid=$ AND selectfave.entitytype=$ AND ^posts.type="Q" ORDER BY ^posts.created DESC';
|
|
$selectspec['arguments'][] = $userid;
|
|
$selectspec['arguments'][] = QA_ENTITY_QUESTION;
|
|
|
|
if (isset($limit)) {
|
|
$limit = min($limit, QA_DB_RETRIEVE_QS_AS);
|
|
$selectspec['source'] .= ' LIMIT #,#';
|
|
$selectspec['arguments'][] = $start;
|
|
$selectspec['arguments'][] = $limit;
|
|
}
|
|
|
|
$selectspec['sortdesc'] = 'created';
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve an array of $userid's favorited users, with information about those users' accounts.
|
|
* Returns $limit users, or all of them if $limit is null (used in qa_db_selectspec_count).
|
|
* @param $userid
|
|
* @param $limit
|
|
* @param int $start
|
|
* @return array
|
|
*/
|
|
function qa_db_user_favorite_users_selectspec($userid, $limit = null, $start = 0)
|
|
{
|
|
require_once QA_INCLUDE_DIR . 'app/updates.php';
|
|
|
|
$source = '^users JOIN ^userpoints ON ^users.userid=^userpoints.userid JOIN ^userfavorites ON ^users.userid=^userfavorites.entityid WHERE ^userfavorites.userid=$ AND ^userfavorites.entitytype=$ ORDER BY ^users.handle';
|
|
$arguments = array($userid, QA_ENTITY_USER);
|
|
|
|
if (isset($limit)) {
|
|
$limit = min($limit, QA_DB_RETRIEVE_USERS);
|
|
$source .= ' LIMIT #,#';
|
|
$arguments[] = $start;
|
|
$arguments[] = $limit;
|
|
}
|
|
|
|
return array(
|
|
'columns' => array('^users.userid', 'handle', 'points', 'flags', '^users.email', 'avatarblobid' => 'BINARY avatarblobid', 'avatarwidth', 'avatarheight'),
|
|
'source' => $source,
|
|
'arguments' => $arguments,
|
|
'sortasc' => 'handle',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve an array of $userid's favorited tags, with information about those tags.
|
|
* Returns $limit tags, or all of them if $limit is null (used in qa_db_selectspec_count).
|
|
* @param $userid
|
|
* @param $limit
|
|
* @param int $start
|
|
* @return array
|
|
*/
|
|
function qa_db_user_favorite_tags_selectspec($userid, $limit = null, $start = 0)
|
|
{
|
|
require_once QA_INCLUDE_DIR . 'app/updates.php';
|
|
|
|
$source = '^words JOIN ^userfavorites ON ^words.wordid=^userfavorites.entityid WHERE ^userfavorites.userid=$ AND ^userfavorites.entitytype=$ ORDER BY ^words.tagcount DESC';
|
|
$arguments = array($userid, QA_ENTITY_TAG);
|
|
|
|
if (isset($limit)) {
|
|
$limit = min($limit, QA_DB_RETRIEVE_TAGS);
|
|
$source .= ' LIMIT #,#';
|
|
$arguments[] = $start;
|
|
$arguments[] = $limit;
|
|
}
|
|
|
|
return array(
|
|
'columns' => array('word', 'tagcount'),
|
|
'source' => $source,
|
|
'arguments' => $arguments,
|
|
'sortdesc' => 'tagcount',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve an array of $userid's favorited categories, with information about those categories.
|
|
* @param $userid
|
|
* @return array
|
|
*/
|
|
function qa_db_user_favorite_categories_selectspec($userid)
|
|
{
|
|
require_once QA_INCLUDE_DIR . 'app/updates.php';
|
|
|
|
return array(
|
|
'columns' => array('categoryid', 'title', 'tags', 'qcount', 'backpath', 'content'),
|
|
'source' => "^categories JOIN ^userfavorites ON ^categories.categoryid=^userfavorites.entityid WHERE ^userfavorites.userid=$ AND ^userfavorites.entitytype=$",
|
|
'arguments' => array($userid, QA_ENTITY_CATEGORY),
|
|
'sortasc' => 'title',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve information about all a user's favorited items except the questions. Depending on
|
|
* the type of item, the array for each item will contain a userid, category backpath or tag word.
|
|
* @param $userid
|
|
* @return array
|
|
*/
|
|
function qa_db_user_favorite_non_qs_selectspec($userid)
|
|
{
|
|
require_once QA_INCLUDE_DIR . 'app/updates.php';
|
|
|
|
return array(
|
|
'columns' => array('type' => 'entitytype', 'userid' => 'IF (entitytype=$, entityid, NULL)', 'categorybackpath' => '^categories.backpath', 'tags' => '^words.word'),
|
|
'source' => '^userfavorites LEFT JOIN ^words ON entitytype=$ AND wordid=entityid LEFT JOIN ^categories ON entitytype=$ AND categoryid=entityid WHERE userid=$ AND entitytype!=$',
|
|
'arguments' => array(QA_ENTITY_USER, QA_ENTITY_TAG, QA_ENTITY_CATEGORY, $userid, QA_ENTITY_QUESTION),
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve the list of recent updates for $userid. Set $forfavorites to whether this should
|
|
* include updates on the user's favorites and $forcontent to whether it should include responses to user's content.
|
|
* This combines events from both the user's stream and the the shared stream for any entities which the user has
|
|
* favorited and which no longer post to user streams (see long comment in /qa-include/db/favorites.php).
|
|
* @param $userid
|
|
* @param bool $forfavorites
|
|
* @param bool $forcontent
|
|
* @return array
|
|
*/
|
|
function qa_db_user_updates_selectspec($userid, $forfavorites = true, $forcontent = true)
|
|
{
|
|
require_once QA_INCLUDE_DIR . 'app/updates.php';
|
|
|
|
$selectspec = qa_db_posts_basic_selectspec($userid);
|
|
|
|
$nonesql = qa_db_argument_to_mysql(QA_ENTITY_NONE, true);
|
|
|
|
$selectspec['columns']['obasetype'] = 'LEFT(updateposts.type, 1)';
|
|
$selectspec['columns']['oupdatetype'] = 'fullevents.updatetype';
|
|
$selectspec['columns']['ohidden'] = "INSTR(updateposts.type, '_HIDDEN')>0";
|
|
$selectspec['columns']['opostid'] = 'fullevents.lastpostid';
|
|
$selectspec['columns']['ouserid'] = 'fullevents.lastuserid';
|
|
$selectspec['columns']['otime'] = 'UNIX_TIMESTAMP(fullevents.updated)';
|
|
$selectspec['columns']['opersonal'] = 'fullevents.entitytype=' . $nonesql;
|
|
$selectspec['columns']['oparentid'] = 'updateposts.parentid';
|
|
|
|
qa_db_add_selectspec_ousers($selectspec, 'eventusers', 'eventuserpoints');
|
|
|
|
if ($forfavorites) { // life is hard
|
|
$selectspec['source'] .= ' JOIN ' .
|
|
"(SELECT entitytype, questionid, lastpostid, updatetype, lastuserid, updated FROM ^userevents WHERE userid=$" .
|
|
($forcontent ? '' : " AND entitytype!=" . $nonesql) .
|
|
" UNION SELECT ^sharedevents.entitytype, questionid, lastpostid, updatetype, lastuserid, updated FROM ^sharedevents JOIN ^userfavorites ON ^sharedevents.entitytype=^userfavorites.entitytype AND ^sharedevents.entityid=^userfavorites.entityid AND ^userfavorites.nouserevents=1 WHERE userid=$) fullevents ON ^posts.postid=fullevents.questionid";
|
|
|
|
array_push($selectspec['arguments'], $userid, $userid);
|
|
|
|
} else { // life is easy
|
|
$selectspec['source'] .= " JOIN ^userevents AS fullevents ON ^posts.postid=fullevents.questionid AND fullevents.userid=$ AND fullevents.entitytype=" . $nonesql;
|
|
$selectspec['arguments'][] = $userid;
|
|
}
|
|
|
|
$selectspec['source'] .=
|
|
" JOIN ^posts AS updateposts ON updateposts.postid=fullevents.lastpostid" .
|
|
" AND (updateposts.type IN ('Q', 'A', 'C') OR fullevents.entitytype=" . $nonesql . ")" .
|
|
" AND (^posts.selchildid=fullevents.lastpostid OR NOT fullevents.updatetype<=>$) AND ^posts.type IN ('Q', 'Q_HIDDEN')" .
|
|
(QA_FINAL_EXTERNAL_USERS ? '' : ' LEFT JOIN ^users AS eventusers ON fullevents.lastuserid=eventusers.userid') .
|
|
' LEFT JOIN ^userpoints AS eventuserpoints ON fullevents.lastuserid=eventuserpoints.userid';
|
|
$selectspec['arguments'][] = QA_UPDATE_SELECTED;
|
|
|
|
unset($selectspec['arraykey']); // allow same question to be retrieved multiple times
|
|
|
|
$selectspec['sortdesc'] = 'otime';
|
|
|
|
return $selectspec;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve all of the per-hour activity limits for user $userid
|
|
* @param $userid
|
|
* @return array
|
|
*/
|
|
function qa_db_user_limits_selectspec($userid)
|
|
{
|
|
return array(
|
|
'columns' => array('action', 'period', 'count'),
|
|
'source' => '^userlimits WHERE userid=$',
|
|
'arguments' => array($userid),
|
|
'arraykey' => 'action',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve all of the per-hour activity limits for ip address $ip
|
|
* @param $ip
|
|
* @return array
|
|
*/
|
|
function qa_db_ip_limits_selectspec($ip)
|
|
{
|
|
return array(
|
|
'columns' => array('action', 'period', 'count'),
|
|
'source' => '^iplimits WHERE ip=UNHEX($)',
|
|
'arguments' => array(bin2hex(@inet_pton($ip))),
|
|
'arraykey' => 'action',
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the selectspec to retrieve all of the context specific (currently per-categpry) levels for the user identified by
|
|
* $identifier, which is treated as a userid if $isuserid is true, otherwise as a handle. Set $full to true to obtain extra
|
|
* information about these contexts (currently, categories).
|
|
* @param $identifier
|
|
* @param bool $isuserid
|
|
* @param bool $full
|
|
* @return array
|
|
*/
|
|
function qa_db_user_levels_selectspec($identifier, $isuserid = QA_FINAL_EXTERNAL_USERS, $full = false)
|
|
{
|
|
require_once QA_INCLUDE_DIR . 'app/updates.php';
|
|
|
|
$selectspec = array(
|
|
'columns' => array('entityid', 'entitytype', 'level'),
|
|
'source' => '^userlevels' . ($full ? ' LEFT JOIN ^categories ON ^userlevels.entitytype=$ AND ^userlevels.entityid=^categories.categoryid' : '') . ' WHERE userid=' . ($isuserid ? '$' : '(SELECT userid FROM ^users WHERE handle=$ LIMIT 1)'),
|
|
'arguments' => array($identifier),
|
|
);
|
|
|
|
if ($full) {
|
|
array_push($selectspec['columns'], 'title', 'backpath');
|
|
array_unshift($selectspec['arguments'], QA_ENTITY_CATEGORY);
|
|
}
|
|
|
|
return $selectspec;
|
|
}
|