function qa_send_email($params) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } // @error_log(print_r($params, true)); require_once QA_INCLUDE_DIR . 'vendor/PHPMailer/PHPMailerAutoload.php'; $mailer = new PHPMailer(); $mailer->CharSet = 'utf-8'; $mailer->From = $params['fromemail']; $mailer->Sender = $params['fromemail']; $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(); }
function qa_db_blob_exists($blobid) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } return qa_db_read_one_value(qa_db_query_sub('SELECT COUNT(*) FROM ^blobs WHERE blobid=#', $blobid)) > 0; }
function qa_db_table_definitions() { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } require_once QA_INCLUDE_DIR . 'db/maxima.php'; require_once QA_INCLUDE_DIR . 'app/users.php'; /* Important note on character encoding in database and PHP connection to MySQL [this note is no longer relevant since we *do* explicitly set the connection character set since Q2A 1.5 - see qa-db.php */ /* Other notes on the definitions below * In MySQL versions prior to 5.0.3, VARCHAR(x) columns will be silently converted to TEXT where x>255 * See box at top of qa-app-recalc.php for a list of redundant (non-normal) information in the database * Starting in version 1.2, we explicitly name keys and foreign key constraints, instead of allowing MySQL to name these by default. Our chosen names match the default names that MySQL would have assigned, and indeed *did* assign for people who installed an earlier version of Q2A. By naming them explicitly, we're on more solid ground for possible future changes to indexes and foreign keys in the schema. * There are other foreign key constraints that it would be valid to add, but that would not serve much purpose in terms of preventing inconsistent data being retrieved, and would just slow down some queries. * We name some columns here in a not entirely intuitive way. The reason is to match the names of columns in other tables which are of a similar nature. This will save time and space when combining several SELECT queries together via a UNION in qa_db_multi_select() - see comments in qa-db.php for more information. */ $useridcoltype = qa_db_user_column_type_verify(); $tables = array('users' => array('userid' => $useridcoltype . ' NOT NULL AUTO_INCREMENT', 'created' => 'DATETIME NOT NULL', 'createip' => 'INT UNSIGNED NOT NULL', 'email' => 'VARCHAR(' . QA_DB_MAX_EMAIL_LENGTH . ') NOT NULL', 'handle' => 'VARCHAR(' . QA_DB_MAX_HANDLE_LENGTH . ') NOT NULL', 'avatarblobid' => 'BIGINT UNSIGNED', 'avatarwidth' => 'SMALLINT UNSIGNED', 'avatarheight' => 'SMALLINT UNSIGNED', 'passsalt' => 'BINARY(16)', 'passcheck' => 'BINARY(20)', 'level' => 'TINYINT UNSIGNED NOT NULL', 'loggedin' => 'DATETIME NOT NULL', 'loginip' => 'INT UNSIGNED NOT NULL', 'written' => 'DATETIME', 'writeip' => 'INT UNSIGNED', 'emailcode' => 'CHAR(8) CHARACTER SET ascii NOT NULL DEFAULT \'\'', 'sessioncode' => 'CHAR(8) CHARACTER SET ascii NOT NULL DEFAULT \'\'', 'sessionsource' => 'VARCHAR (16) CHARACTER SET ascii DEFAULT \'\'', 'flags' => 'SMALLINT UNSIGNED NOT NULL DEFAULT 0', 'wallposts' => 'MEDIUMINT NOT NULL DEFAULT 0', 'PRIMARY KEY (userid)', 'KEY email (email)', 'KEY handle (handle)', 'KEY level (level)', 'kEY created (created, level, flags)'), 'userlogins' => array('userid' => $useridcoltype . ' NOT NULL', 'source' => 'VARCHAR (16) CHARACTER SET ascii NOT NULL', 'identifier' => 'VARBINARY (1024) NOT NULL', 'identifiermd5' => 'BINARY (16) NOT NULL', 'KEY source (source, identifiermd5)', 'KEY userid (userid)'), 'userlevels' => array('userid' => $useridcoltype . ' NOT NULL', 'entitytype' => "CHAR(1) CHARACTER SET ascii NOT NULL", 'entityid' => 'INT UNSIGNED NOT NULL', 'level' => 'TINYINT UNSIGNED', 'UNIQUE userid (userid, entitytype, entityid)', 'KEY entitytype (entitytype, entityid)'), 'userprofile' => array('userid' => $useridcoltype . ' NOT NULL', 'title' => 'VARCHAR(' . QA_DB_MAX_PROFILE_TITLE_LENGTH . ') NOT NULL', 'content' => 'VARCHAR(' . QA_DB_MAX_PROFILE_CONTENT_LENGTH . ') NOT NULL', 'UNIQUE userid (userid,title)'), 'userfields' => array('fieldid' => 'SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT', 'title' => 'VARCHAR(' . QA_DB_MAX_PROFILE_TITLE_LENGTH . ') NOT NULL', 'content' => 'VARCHAR(' . QA_DB_MAX_PROFILE_TITLE_LENGTH . ')', 'position' => 'SMALLINT UNSIGNED NOT NULL', 'flags' => 'TINYINT UNSIGNED NOT NULL', 'permit' => 'TINYINT UNSIGNED', 'PRIMARY KEY (fieldid)'), 'messages' => array('messageid' => 'INT UNSIGNED NOT NULL AUTO_INCREMENT', 'type' => "ENUM('PUBLIC', 'PRIVATE') NOT NULL DEFAULT 'PRIVATE'", 'fromuserid' => $useridcoltype . ' NOT NULL', 'touserid' => $useridcoltype . ' NOT NULL', 'fromhidden' => 'TINYINT(1) UNSIGNED NOT NULL DEFAULT 0', 'tohidden' => 'TINYINT(1) UNSIGNED NOT NULL DEFAULT 0', 'content' => 'VARCHAR(' . QA_DB_MAX_CONTENT_LENGTH . ') NOT NULL', 'format' => 'VARCHAR(' . QA_DB_MAX_FORMAT_LENGTH . ') CHARACTER SET ascii NOT NULL', 'created' => 'DATETIME NOT NULL', 'PRIMARY KEY (messageid)', 'KEY type (type, fromuserid, touserid, created)', 'KEY touserid (touserid, type, created)', 'KEY fromhidden (fromhidden)', 'KEY tohidden (tohidden)'), 'userfavorites' => array('userid' => $useridcoltype . ' NOT NULL', 'entitytype' => "CHAR(1) CHARACTER SET ascii NOT NULL", 'entityid' => 'INT UNSIGNED NOT NULL', 'nouserevents' => 'TINYINT UNSIGNED NOT NULL', 'PRIMARY KEY (userid, entitytype, entityid)', 'KEY userid (userid, nouserevents)', 'KEY entitytype (entitytype, entityid, nouserevents)'), 'usernotices' => array('noticeid' => 'INT UNSIGNED NOT NULL AUTO_INCREMENT', 'userid' => $useridcoltype . ' NOT NULL', 'content' => 'VARCHAR(' . QA_DB_MAX_CONTENT_LENGTH . ') NOT NULL', 'format' => 'VARCHAR(' . QA_DB_MAX_FORMAT_LENGTH . ') CHARACTER SET ascii NOT NULL', 'tags' => 'VARCHAR(' . QA_DB_MAX_CAT_PAGE_TAGS_LENGTH . ')', 'created' => 'DATETIME NOT NULL', 'PRIMARY KEY (noticeid)', 'KEY userid (userid, created)'), 'userevents' => array('userid' => $useridcoltype . ' NOT NULL', 'entitytype' => "CHAR(1) CHARACTER SET ascii NOT NULL", 'entityid' => 'INT UNSIGNED NOT NULL', 'questionid' => 'INT UNSIGNED NOT NULL', 'lastpostid' => 'INT UNSIGNED NOT NULL', 'updatetype' => 'CHAR(1) CHARACTER SET ascii', 'lastuserid' => $useridcoltype, 'updated' => 'DATETIME NOT NULL', 'KEY userid (userid, updated)', 'KEY questionid (questionid, userid)'), 'sharedevents' => array('entitytype' => "CHAR(1) CHARACTER SET ascii NOT NULL", 'entityid' => 'INT UNSIGNED NOT NULL', 'questionid' => 'INT UNSIGNED NOT NULL', 'lastpostid' => 'INT UNSIGNED NOT NULL', 'updatetype' => 'CHAR(1) CHARACTER SET ascii', 'lastuserid' => $useridcoltype, 'updated' => 'DATETIME NOT NULL', 'KEY entitytype (entitytype, entityid, updated)', 'KEY questionid (questionid, entitytype, entityid)'), 'cookies' => array('cookieid' => 'BIGINT UNSIGNED NOT NULL', 'created' => 'DATETIME NOT NULL', 'createip' => 'INT UNSIGNED NOT NULL', 'written' => 'DATETIME', 'writeip' => 'INT UNSIGNED', 'PRIMARY KEY (cookieid)'), 'categories' => array('categoryid' => 'INT UNSIGNED NOT NULL AUTO_INCREMENT', 'parentid' => 'INT UNSIGNED', 'title' => 'VARCHAR(' . QA_DB_MAX_CAT_PAGE_TITLE_LENGTH . ') NOT NULL', 'tags' => 'VARCHAR(' . QA_DB_MAX_CAT_PAGE_TAGS_LENGTH . ') NOT NULL', 'content' => 'VARCHAR(' . QA_DB_MAX_CAT_CONTENT_LENGTH . ') NOT NULL DEFAULT \'\'', 'qcount' => 'INT UNSIGNED NOT NULL DEFAULT 0', 'position' => 'SMALLINT UNSIGNED NOT NULL', 'backpath' => 'VARCHAR(' . QA_CATEGORY_DEPTH * (QA_DB_MAX_CAT_PAGE_TAGS_LENGTH + 1) . ') NOT NULL DEFAULT \'\'', 'PRIMARY KEY (categoryid)', 'UNIQUE parentid (parentid, tags)', 'UNIQUE parentid_2 (parentid, position)', 'KEY backpath (backpath(' . QA_DB_MAX_CAT_PAGE_TAGS_LENGTH . '))'), 'pages' => array('pageid' => 'SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT', 'title' => 'VARCHAR(' . QA_DB_MAX_CAT_PAGE_TITLE_LENGTH . ') NOT NULL', 'nav' => 'CHAR(1) CHARACTER SET ascii NOT NULL', 'position' => 'SMALLINT UNSIGNED NOT NULL', 'flags' => 'TINYINT UNSIGNED NOT NULL', 'permit' => 'TINYINT UNSIGNED', 'tags' => 'VARCHAR(' . QA_DB_MAX_CAT_PAGE_TAGS_LENGTH . ') NOT NULL', 'heading' => 'VARCHAR(' . QA_DB_MAX_TITLE_LENGTH . ')', 'content' => 'MEDIUMTEXT', 'PRIMARY KEY (pageid)', 'KEY tags (tags)', 'UNIQUE position (position)'), 'widgets' => array('widgetid' => 'SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT', 'place' => 'CHAR(2) CHARACTER SET ascii NOT NULL', 'position' => 'SMALLINT UNSIGNED NOT NULL', 'tags' => 'VARCHAR(' . QA_DB_MAX_WIDGET_TAGS_LENGTH . ') CHARACTER SET ascii NOT NULL', 'title' => 'VARCHAR(' . QA_DB_MAX_WIDGET_TITLE_LENGTH . ') NOT NULL', 'PRIMARY KEY (widgetid)', 'UNIQUE position (position)'), 'posts' => array('postid' => 'INT UNSIGNED NOT NULL AUTO_INCREMENT', 'type' => "ENUM('Q', 'A', 'C', 'Q_HIDDEN', 'A_HIDDEN', 'C_HIDDEN', 'Q_QUEUED', 'A_QUEUED', 'C_QUEUED', 'NOTE') NOT NULL", 'parentid' => 'INT UNSIGNED', 'categoryid' => 'INT UNSIGNED', 'catidpath1' => 'INT UNSIGNED', 'catidpath2' => 'INT UNSIGNED', 'catidpath3' => 'INT UNSIGNED', 'acount' => 'SMALLINT UNSIGNED NOT NULL DEFAULT 0', 'amaxvote' => 'SMALLINT UNSIGNED NOT NULL DEFAULT 0', 'selchildid' => 'INT UNSIGNED', 'closedbyid' => 'INT UNSIGNED', 'userid' => $useridcoltype, 'cookieid' => 'BIGINT UNSIGNED', 'createip' => 'INT UNSIGNED', 'lastuserid' => $useridcoltype, 'lastip' => 'INT UNSIGNED', 'upvotes' => 'SMALLINT UNSIGNED NOT NULL DEFAULT 0', 'downvotes' => 'SMALLINT UNSIGNED NOT NULL DEFAULT 0', 'netvotes' => 'SMALLINT NOT NULL DEFAULT 0', 'lastviewip' => 'INT UNSIGNED', 'views' => 'INT UNSIGNED NOT NULL DEFAULT 0', 'hotness' => 'FLOAT', 'flagcount' => 'TINYINT UNSIGNED NOT NULL DEFAULT 0', 'format' => 'VARCHAR(' . QA_DB_MAX_FORMAT_LENGTH . ') CHARACTER SET ascii NOT NULL DEFAULT \'\'', 'created' => 'DATETIME NOT NULL', 'updated' => 'DATETIME', 'updatetype' => 'CHAR(1) CHARACTER SET ascii', 'title' => 'VARCHAR(' . QA_DB_MAX_TITLE_LENGTH . ')', 'content' => 'VARCHAR(' . QA_DB_MAX_CONTENT_LENGTH . ')', 'tags' => 'VARCHAR(' . QA_DB_MAX_TAGS_LENGTH . ')', 'name' => 'VARCHAR(' . QA_DB_MAX_NAME_LENGTH . ')', 'notify' => 'VARCHAR(' . QA_DB_MAX_EMAIL_LENGTH . ')', 'PRIMARY KEY (postid)', 'KEY type (type, created)', 'KEY type_2 (type, acount, created)', 'KEY type_4 (type, netvotes, created)', 'KEY type_5 (type, views, created)', 'KEY type_6 (type, hotness)', 'KEY type_7 (type, amaxvote, created)', 'KEY parentid (parentid, type)', 'KEY userid (userid, type, created)', 'KEY selchildid (selchildid, type, created)', 'KEY closedbyid (closedbyid)', 'KEY catidpath1 (catidpath1, type, created)', 'KEY catidpath2 (catidpath2, type, created)', 'KEY catidpath3 (catidpath3, type, created)', 'KEY categoryid (categoryid, type, created)', 'KEY createip (createip, created)', 'KEY updated (updated, type)', 'KEY flagcount (flagcount, created, type)', 'KEY catidpath1_2 (catidpath1, updated, type)', 'KEY catidpath2_2 (catidpath2, updated, type)', 'KEY catidpath3_2 (catidpath3, updated, type)', 'KEY categoryid_2 (categoryid, updated, type)', 'KEY lastuserid (lastuserid, updated, type)', 'KEY lastip (lastip, updated, type)', 'CONSTRAINT ^posts_ibfk_2 FOREIGN KEY (parentid) REFERENCES ^posts(postid)', 'CONSTRAINT ^posts_ibfk_3 FOREIGN KEY (categoryid) REFERENCES ^categories(categoryid) ON DELETE SET NULL', 'CONSTRAINT ^posts_ibfk_4 FOREIGN KEY (closedbyid) REFERENCES ^posts(postid)'), 'blobs' => array('blobid' => 'BIGINT UNSIGNED NOT NULL', 'format' => 'VARCHAR(' . QA_DB_MAX_FORMAT_LENGTH . ') CHARACTER SET ascii NOT NULL', 'content' => 'MEDIUMBLOB', 'filename' => 'VARCHAR(' . QA_DB_MAX_BLOB_FILE_NAME_LENGTH . ')', 'userid' => $useridcoltype, 'cookieid' => 'BIGINT UNSIGNED', 'createip' => 'INT UNSIGNED', 'created' => 'DATETIME', 'PRIMARY KEY (blobid)'), 'words' => array('wordid' => 'INT UNSIGNED NOT NULL AUTO_INCREMENT', 'word' => 'VARCHAR(' . QA_DB_MAX_WORD_LENGTH . ') NOT NULL', 'titlecount' => 'INT UNSIGNED NOT NULL DEFAULT 0', 'contentcount' => 'INT UNSIGNED NOT NULL DEFAULT 0', 'tagwordcount' => 'INT UNSIGNED NOT NULL DEFAULT 0', 'tagcount' => 'INT UNSIGNED NOT NULL DEFAULT 0', 'PRIMARY KEY (wordid)', 'KEY word (word)', 'KEY tagcount (tagcount)'), 'titlewords' => array('postid' => 'INT UNSIGNED NOT NULL', 'wordid' => 'INT UNSIGNED NOT NULL', 'KEY postid (postid)', 'KEY wordid (wordid)', 'CONSTRAINT ^titlewords_ibfk_1 FOREIGN KEY (postid) REFERENCES ^posts(postid) ON DELETE CASCADE', 'CONSTRAINT ^titlewords_ibfk_2 FOREIGN KEY (wordid) REFERENCES ^words(wordid)'), 'contentwords' => array('postid' => 'INT UNSIGNED NOT NULL', 'wordid' => 'INT UNSIGNED NOT NULL', 'count' => 'TINYINT UNSIGNED NOT NULL', 'type' => "ENUM('Q', 'A', 'C', 'NOTE') NOT NULL", 'questionid' => 'INT UNSIGNED NOT NULL', 'KEY postid (postid)', 'KEY wordid (wordid)', 'CONSTRAINT ^contentwords_ibfk_1 FOREIGN KEY (postid) REFERENCES ^posts(postid) ON DELETE CASCADE', 'CONSTRAINT ^contentwords_ibfk_2 FOREIGN KEY (wordid) REFERENCES ^words(wordid)'), 'tagwords' => array('postid' => 'INT UNSIGNED NOT NULL', 'wordid' => 'INT UNSIGNED NOT NULL', 'KEY postid (postid)', 'KEY wordid (wordid)', 'CONSTRAINT ^tagwords_ibfk_1 FOREIGN KEY (postid) REFERENCES ^posts(postid) ON DELETE CASCADE', 'CONSTRAINT ^tagwords_ibfk_2 FOREIGN KEY (wordid) REFERENCES ^words(wordid)'), 'posttags' => array('postid' => 'INT UNSIGNED NOT NULL', 'wordid' => 'INT UNSIGNED NOT NULL', 'postcreated' => 'DATETIME NOT NULL', 'KEY postid (postid)', 'KEY wordid (wordid,postcreated)', 'CONSTRAINT ^posttags_ibfk_1 FOREIGN KEY (postid) REFERENCES ^posts(postid) ON DELETE CASCADE', 'CONSTRAINT ^posttags_ibfk_2 FOREIGN KEY (wordid) REFERENCES ^words(wordid)'), 'uservotes' => array('postid' => 'INT UNSIGNED NOT NULL', 'userid' => $useridcoltype . ' NOT NULL', 'vote' => 'TINYINT NOT NULL', 'flag' => 'TINYINT NOT NULL', 'UNIQUE userid (userid, postid)', 'KEY postid (postid)', 'CONSTRAINT ^uservotes_ibfk_1 FOREIGN KEY (postid) REFERENCES ^posts(postid) ON DELETE CASCADE'), 'userpoints' => array('userid' => $useridcoltype . ' NOT NULL', 'points' => 'INT NOT NULL DEFAULT 0', 'qposts' => 'MEDIUMINT NOT NULL DEFAULT 0', 'aposts' => 'MEDIUMINT NOT NULL DEFAULT 0', 'cposts' => 'MEDIUMINT NOT NULL DEFAULT 0', 'aselects' => 'MEDIUMINT NOT NULL DEFAULT 0', 'aselecteds' => 'MEDIUMINT NOT NULL DEFAULT 0', 'qupvotes' => 'MEDIUMINT NOT NULL DEFAULT 0', 'qdownvotes' => 'MEDIUMINT NOT NULL DEFAULT 0', 'aupvotes' => 'MEDIUMINT NOT NULL DEFAULT 0', 'adownvotes' => 'MEDIUMINT NOT NULL DEFAULT 0', 'qvoteds' => 'INT NOT NULL DEFAULT 0', 'avoteds' => 'INT NOT NULL DEFAULT 0', 'upvoteds' => 'INT NOT NULL DEFAULT 0', 'downvoteds' => 'INT NOT NULL DEFAULT 0', 'bonus' => 'INT NOT NULL DEFAULT 0', 'PRIMARY KEY (userid)', 'KEY points (points)'), 'userlimits' => array('userid' => $useridcoltype . ' NOT NULL', 'action' => 'CHAR(1) CHARACTER SET ascii NOT NULL', 'period' => 'INT UNSIGNED NOT NULL', 'count' => 'SMALLINT UNSIGNED NOT NULL', 'UNIQUE userid (userid, action)'), 'iplimits' => array('ip' => 'INT UNSIGNED NOT NULL', 'action' => 'CHAR(1) CHARACTER SET ascii NOT NULL', 'period' => 'INT UNSIGNED NOT NULL', 'count' => 'SMALLINT UNSIGNED NOT NULL', 'UNIQUE ip (ip, action)'), 'options' => array('title' => 'VARCHAR(' . QA_DB_MAX_OPTION_TITLE_LENGTH . ') NOT NULL', 'content' => 'VARCHAR(' . QA_DB_MAX_CONTENT_LENGTH . ') NOT NULL', 'PRIMARY KEY (title)'), 'cache' => array('type' => 'CHAR(8) CHARACTER SET ascii NOT NULL', 'cacheid' => 'BIGINT UNSIGNED DEFAULT 0', 'content' => 'MEDIUMBLOB NOT NULL', 'created' => 'DATETIME NOT NULL', 'lastread' => 'DATETIME NOT NULL', 'PRIMARY KEY (type,cacheid)', 'KEY (lastread)'), 'usermetas' => array('userid' => $useridcoltype . ' NOT NULL', 'title' => 'VARCHAR(' . QA_DB_MAX_META_TITLE_LENGTH . ') NOT NULL', 'content' => 'VARCHAR(' . QA_DB_MAX_META_CONTENT_LENGTH . ') NOT NULL', 'PRIMARY KEY (userid, title)'), 'postmetas' => array('postid' => 'INT UNSIGNED NOT NULL', 'title' => 'VARCHAR(' . QA_DB_MAX_META_TITLE_LENGTH . ') NOT NULL', 'content' => 'VARCHAR(' . QA_DB_MAX_META_CONTENT_LENGTH . ') NOT NULL', 'PRIMARY KEY (postid, title)', 'CONSTRAINT ^postmetas_ibfk_1 FOREIGN KEY (postid) REFERENCES ^posts(postid) ON DELETE CASCADE'), 'categorymetas' => array('categoryid' => 'INT UNSIGNED NOT NULL', 'title' => 'VARCHAR(' . QA_DB_MAX_META_TITLE_LENGTH . ') NOT NULL', 'content' => 'VARCHAR(' . QA_DB_MAX_META_CONTENT_LENGTH . ') NOT NULL', 'PRIMARY KEY (categoryid, title)', 'CONSTRAINT ^categorymetas_ibfk_1 FOREIGN KEY (categoryid) REFERENCES ^categories(categoryid) ON DELETE CASCADE'), 'tagmetas' => array('tag' => 'VARCHAR(' . QA_DB_MAX_WORD_LENGTH . ') NOT NULL', 'title' => 'VARCHAR(' . QA_DB_MAX_META_TITLE_LENGTH . ') NOT NULL', 'content' => 'VARCHAR(' . QA_DB_MAX_META_CONTENT_LENGTH . ') NOT NULL', 'PRIMARY KEY (tag, title)')); if (QA_FINAL_EXTERNAL_USERS) { unset($tables['users']); unset($tables['userlogins']); unset($tables['userprofile']); unset($tables['userfields']); unset($tables['messages']); } else { $userforeignkey = 'FOREIGN KEY (userid) REFERENCES ^users(userid)'; $tables['userlogins'][] = 'CONSTRAINT ^userlogins_ibfk_1 ' . $userforeignkey . ' ON DELETE CASCADE'; $tables['userprofile'][] = 'CONSTRAINT ^userprofile_ibfk_1 ' . $userforeignkey . ' ON DELETE CASCADE'; $tables['posts'][] = 'CONSTRAINT ^posts_ibfk_1 ' . $userforeignkey . ' ON DELETE SET NULL'; $tables['uservotes'][] = 'CONSTRAINT ^uservotes_ibfk_2 ' . $userforeignkey . ' ON DELETE CASCADE'; $tables['userlimits'][] = 'CONSTRAINT ^userlimits_ibfk_1 ' . $userforeignkey . ' ON DELETE CASCADE'; $tables['userfavorites'][] = 'CONSTRAINT ^userfavorites_ibfk_1 ' . $userforeignkey . ' ON DELETE CASCADE'; $tables['usernotices'][] = 'CONSTRAINT ^usernotices_ibfk_1 ' . $userforeignkey . ' ON DELETE CASCADE'; $tables['userevents'][] = 'CONSTRAINT ^userevents_ibfk_1 ' . $userforeignkey . ' ON DELETE CASCADE'; $tables['userlevels'][] = 'CONSTRAINT ^userlevels_ibfk_1 ' . $userforeignkey . ' ON DELETE CASCADE'; $tables['usermetas'][] = 'CONSTRAINT ^usermetas_ibfk_1 ' . $userforeignkey . ' ON DELETE CASCADE'; } return $tables; }
function qa_db_cache_get($type, $cacheid) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } $content = qa_db_read_one_value(qa_db_query_sub('SELECT content FROM ^cache WHERE type=$ AND cacheid=#', $type, $cacheid), true); if (isset($content)) { qa_db_query_sub('UPDATE ^cache SET lastread=NOW() WHERE type=$ AND cacheid=#', $type, $cacheid); } return $content; }
function qa_q_list_page_content($questions, $pagesize, $start, $count, $sometitle, $nonetitle, $navcategories, $categoryid, $categoryqcount, $categorypathprefix, $feedpathprefix, $suggest, $pagelinkparams = null, $categoryparams = null, $dummy = null) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } require_once QA_INCLUDE_DIR . 'app/format.php'; require_once QA_INCLUDE_DIR . 'app/updates.php'; $userid = qa_get_logged_in_userid(); // Chop down to size, get user information for display if (isset($pagesize)) { $questions = array_slice($questions, 0, $pagesize); } $usershtml = qa_userids_handles_html(qa_any_get_userids_handles($questions)); // Prepare content for theme $qa_content = qa_content_prepare(true, array_keys(qa_category_path($navcategories, $categoryid))); $qa_content['q_list']['form'] = array('tags' => 'method="post" action="' . qa_self_html() . '"', 'hidden' => array('code' => qa_get_form_security_code('vote'))); $qa_content['q_list']['qs'] = array(); if (count($questions)) { $qa_content['title'] = $sometitle; $defaults = qa_post_html_defaults('Q'); if (isset($categorypathprefix)) { $defaults['categorypathprefix'] = $categorypathprefix; } foreach ($questions as $question) { $fields = qa_any_to_q_html_fields($question, $userid, qa_cookie_get(), $usershtml, null, qa_post_html_options($question, $defaults)); if (!empty($fields['raw']['closedbyid'])) { $fields['closed'] = array('state' => qa_lang_html('main/closed')); } $qa_content['q_list']['qs'][] = $fields; } } else { $qa_content['title'] = $nonetitle; } if (isset($userid) && isset($categoryid)) { $favoritemap = qa_get_favorite_non_qs_map(); $categoryisfavorite = @$favoritemap['category'][$navcategories[$categoryid]['backpath']]; $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_cookie_get_create() { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } require_once QA_INCLUDE_DIR . 'qa-db-cookies.php'; $cookieid = qa_cookie_get(); if (isset($cookieid) && qa_db_cookie_exists($cookieid)) { } 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_wall_posts_add_rules($usermessages, $start, $userid) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } $deleteable = $start == 0 && isset($userid); // can delete all of the most recent messages... foreach ($usermessages as $key => $message) { if ($message['touserid'] != $userid && $message['fromuserid'] != $userid) { $deleteable = false; } // ... until we come across one that doesn't involve me $usermessages[$key]['deleteable'] = $deleteable; } return $usermessages; }
function qa_db_hotness_update($firstpostid, $lastpostid = null, $viewincrement = false) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_should_update_counts()) { if (!isset($lastpostid)) { $lastpostid = $firstpostid; } $query = "UPDATE ^posts AS x, (SELECT parents.postid, parents.created AS qcreated, COALESCE(MAX(children.created), parents.created) as acreated, COUNT(children.postid) AS acount, parents.netvotes, parents.views FROM ^posts AS parents LEFT JOIN ^posts AS children ON parents.postid=children.parentid AND children.type='A' WHERE parents.postid>=# AND parents.postid<=# AND LEFT(parents.type, 1)='Q' GROUP BY postid) AS a SET x.hotness=(" . '((TO_DAYS(a.qcreated)-734138)*86400.0+TIME_TO_SEC(a.qcreated))*# + ' . '((TO_DAYS(a.acreated)-734138)*86400.0+TIME_TO_SEC(a.acreated))*# + ' . '(a.acount+0.0)*# + ' . '(a.netvotes+0.0)*# + ' . '(a.views+0.0+#)*#' . ')' . ($viewincrement ? ', x.views=x.views+1, x.lastviewip=INET_ATON($)' : '') . ' WHERE x.postid=a.postid'; // Additional multiples based on empirical analysis of activity on Q2A meta site to give approx equal influence for all factors $arguments = array($firstpostid, $lastpostid, qa_opt('hot_weight_q_age'), qa_opt('hot_weight_a_age'), qa_opt('hot_weight_answers') * 160000, qa_opt('hot_weight_votes') * 160000, $viewincrement ? 1 : 0, qa_opt('hot_weight_views') * 4000); if ($viewincrement) { $arguments[] = qa_remote_ip_address(); } qa_db_query_raw(qa_db_apply_sub($query, $arguments)); } }
function qa_get_max_upload_size() { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } $mindb = 16777215; // from MEDIUMBLOB column type $minphp = trim(ini_get('upload_max_filesize')); switch (strtolower(substr($minphp, -1))) { case 'g': $minphp *= 1024; case 'm': $minphp *= 1024; case 'k': $minphp *= 1024; } return min($mindb, $minphp); }
function qa_wall_posts_add_rules($usermessages, $start) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } $userid = qa_get_logged_in_userid(); $userdeleteall = !(qa_user_permit_error('permit_hide_show') || qa_user_permit_error('permit_delete_hidden')); // reuse "Hiding or showing any post" and "Deleting hidden posts" permissions $userrecent = $start == 0 && isset($userid); // User can delete all of the recent messages they wrote on someone's wall... foreach ($usermessages as $key => $message) { if ($message['fromuserid'] != $userid) { $userrecent = false; } // ... until we come across one that they didn't write (which could be a reply) $usermessages[$key]['deleteable'] = $message['touserid'] == $userid || $userrecent && $message['fromuserid'] == $userid || $userdeleteall; // if the user has enough permissions to delete from any wall } return $usermessages; }
function qw_request_text($field) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } return isset($_REQUEST[$field]) ? preg_replace('/\\r\\n?/', "\n", trim(qa_gpc_to_string($_REQUEST[$field]))) : null; }
function qa_limits_increment($userid, $action) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } require_once QA_INCLUDE_DIR . 'qa-db-limits.php'; $period = (int) (qa_opt('db_time') / 3600); if (isset($userid)) { qa_db_limits_user_add($userid, $action, $period, 1); } qa_db_limits_ip_add(qa_remote_ip_address(), $action, $period, 1); }
function qa_report_event($event, $userid, $handle, $cookieid, $params = array()) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } global $qa_event_reports_suspended; if ($qa_event_reports_suspended > 0) { return; } $eventmodules = qa_load_modules_with('event', 'process_event'); foreach ($eventmodules as $eventmodule) { $eventmodule->process_event($event, $userid, $handle, $cookieid, $params); } }
function qa_upload_file($localfilename, $sourcefilename, $maxfilesize = null, $onlyimage = false, $imagemaxwidth = null, $imagemaxheight = null) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } $result = array(); // Check per-user upload limits require_once QA_INCLUDE_DIR . 'qa-app-users.php'; require_once QA_INCLUDE_DIR . 'qa-app-limits.php'; switch (qa_user_permit_error(null, QA_LIMIT_UPLOADS)) { case 'limit': $result['error'] = qa_lang('main/upload_limit'); return $result; case false: qa_limits_increment(qa_get_logged_in_userid(), QA_LIMIT_UPLOADS); break; default: $result['error'] = qa_lang('users/no_permission'); return $result; } // Check the uploaded file is not too large $filesize = filesize($localfilename); if (isset($maxfilesize)) { $maxfilesize = min($maxfilesize, qa_get_max_upload_size()); } else { $maxfilesize = qa_get_max_upload_size(); } if ($filesize <= 0 || $filesize > $maxfilesize) { // if file was too big for PHP, $filesize will be zero $result['error'] = qa_lang_sub('main/max_upload_size_x', number_format($maxfilesize / 1048576, 1) . 'MB'); return $result; } // Find out what type of source file was uploaded and if appropriate, check it's an image and get preliminary size measure $pathinfo = pathinfo($sourcefilename); $format = strtolower(@$pathinfo['extension']); $isimage = $format == 'png' || $format == 'gif' || $format == 'jpeg' || $format == 'jpg'; // allowed image extensions if ($isimage) { $imagesize = @getimagesize($localfilename); if (is_array($imagesize)) { $result['width'] = $imagesize[0]; $result['height'] = $imagesize[1]; switch ($imagesize['2']) { // reassign format based on actual content, if we can case IMAGETYPE_GIF: $format = 'gif'; break; case IMAGETYPE_JPEG: $format = 'jpeg'; break; case IMAGETYPE_PNG: $format = 'png'; break; } } } $result['format'] = $format; if ($onlyimage) { if (!$isimage || !is_array($imagesize)) { $result['error'] = qa_lang_sub('main/image_not_read', 'GIF, JPG, PNG'); return $result; } } // Read in the raw file contents $content = file_get_contents($localfilename); // If appropriate, get more accurate image size and apply constraints to it require_once QA_INCLUDE_DIR . 'qa-util-image.php'; if ($isimage && qa_has_gd_image()) { $image = @imagecreatefromstring($content); if (is_resource($image)) { $result['width'] = $width = imagesx($image); $result['height'] = $height = imagesy($image); if (isset($imagemaxwidth) || isset($imagemaxheight)) { if (qa_image_constrain($width, $height, isset($imagemaxwidth) ? $imagemaxwidth : $width, isset($imagemaxheight) ? $imagemaxheight : $height)) { qa_gd_image_resize($image, $width, $height); if (is_resource($image)) { $content = qa_gd_image_jpeg($image); $result['format'] = $format = 'jpeg'; $result['width'] = $width; $result['height'] = $height; } } } if (is_resource($image)) { // might have been lost imagedestroy($image); } } } // Create the blob and return require_once QA_INCLUDE_DIR . 'qa-app-blobs.php'; $userid = qa_get_logged_in_userid(); $cookieid = isset($userid) ? qa_cookie_get() : qa_cookie_get_create(); $result['blobid'] = qa_create_blob($content, $format, $sourcefilename, $userid, $cookieid, qa_remote_ip_address()); if (!isset($result['blobid'])) { $result['error'] = qa_lang('main/general_error'); return $result; } $result['bloburl'] = qa_get_blob_url($result['blobid'], true); return $result; }
function qa_set_user_avatar($userid, $imagedata, $oldblobid = null) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } require_once QA_INCLUDE_DIR . 'qa-util-image.php'; $imagedata = qa_image_constrain_data($imagedata, $width, $height, qa_opt('avatar_store_size')); if (isset($imagedata)) { require_once QA_INCLUDE_DIR . 'qa-db-blobs.php'; $newblobid = qa_db_blob_create($imagedata, 'jpeg', null, $userid, null, qa_remote_ip_address()); if (isset($newblobid)) { qa_db_user_set($userid, 'avatarblobid', $newblobid); qa_db_user_set($userid, 'avatarwidth', $width); qa_db_user_set($userid, 'avatarheight', $height); qa_db_user_set_flag($userid, QA_USER_FLAGS_SHOW_AVATAR, true); qa_db_user_set_flag($userid, QA_USER_FLAGS_SHOW_GRAVATAR, false); if (isset($oldblobid)) { qa_db_blob_delete($oldblobid); } return true; } } return false; }
function qa_admin_sub_navigation() { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } $navigation = array(); if (qa_get_logged_in_level() >= QA_USER_LEVEL_ADMIN) { $navigation['admin/general'] = array('label' => qa_lang_html('admin/general_title'), 'url' => qa_path_html('admin/general')); $navigation['admin/emails'] = array('label' => qa_lang_html('admin/emails_title'), 'url' => qa_path_html('admin/emails')); $navigation['admin/user'] = array('label' => qa_lang_html('admin/users_title'), 'url' => qa_path_html('admin/users')); $navigation['admin/layout'] = array('label' => qa_lang_html('admin/layout_title'), 'url' => qa_path_html('admin/layout')); $navigation['admin/posting'] = array('label' => qa_lang_html('admin/posting_title'), 'url' => qa_path_html('admin/posting')); $navigation['admin/viewing'] = array('label' => qa_lang_html('admin/viewing_title'), 'url' => qa_path_html('admin/viewing')); $navigation['admin/lists'] = array('label' => qa_lang_html('admin/lists_title'), 'url' => qa_path_html('admin/lists')); if (qa_using_categories()) { $navigation['admin/categories'] = array('label' => qa_lang_html('admin/categories_title'), 'url' => qa_path_html('admin/categories')); } $navigation['admin/permissions'] = array('label' => qa_lang_html('admin/permissions_title'), 'url' => qa_path_html('admin/permissions')); $navigation['admin/pages'] = array('label' => qa_lang_html('admin/pages_title'), 'url' => qa_path_html('admin/pages')); $navigation['admin/feeds'] = array('label' => qa_lang_html('admin/feeds_title'), 'url' => qa_path_html('admin/feeds')); $navigation['admin/points'] = array('label' => qa_lang_html('admin/points_title'), 'url' => qa_path_html('admin/points')); $navigation['admin/spam'] = array('label' => qa_lang_html('admin/spam_title'), 'url' => qa_path_html('admin/spam')); $navigation['admin/stats'] = array('label' => qa_lang_html('admin/stats_title'), 'url' => qa_path_html('admin/stats')); if (!QA_FINAL_EXTERNAL_USERS) { $navigation['admin/mailing'] = array('label' => qa_lang_html('admin/mailing_title'), 'url' => qa_path_html('admin/mailing')); } $navigation['admin/plugins'] = array('label' => qa_lang_html('admin/plugins_title'), 'url' => qa_path_html('admin/plugins')); } if (!qa_user_permit_error('permit_moderate')) { $navigation['admin/moderate'] = array('label' => qa_lang_html('admin/moderate_title'), 'url' => qa_path_html('admin/moderate')); } if (qa_opt('flagging_of_posts') && !qa_user_permit_error('permit_hide_show')) { $navigation['admin/flagged'] = array('label' => qa_lang_html('admin/flagged_title'), 'url' => qa_path_html('admin/flagged')); } if (!qa_user_permit_error('permit_hide_show') || !qa_user_permit_error('permit_delete_hidden')) { $navigation['admin/hidden'] = array('label' => qa_lang_html('admin/hidden_title'), 'url' => qa_path_html('admin/hidden')); } return $navigation; }
function qa_check_form_security_code($action, $value) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } $reportproblems = array(); $silentproblems = array(); if (!isset($value)) { $silentproblems[] = 'code missing'; } elseif (!strlen($value)) { $silentproblems[] = 'code empty'; } else { $parts = explode('-', $value); if (count($parts) == 3) { $loggedin = $parts[0]; $timestamp = $parts[1]; $hash = $parts[2]; $timenow = qa_opt('db_time'); if ($timestamp > $timenow) { $reportproblems[] = 'time ' . ($timestamp - $timenow) . 's in future'; } elseif ($timestamp < $timenow - QA_FORM_EXPIRY_SECS) { $silentproblems[] = 'timeout after ' . ($timenow - $timestamp) . 's'; } if (qa_is_logged_in()) { if (!$loggedin) { $silentproblems[] = 'now logged in'; } } else { if ($loggedin) { $silentproblems[] = 'now logged out'; } else { $key = @$_COOKIE['qa_key']; if (!isset($key)) { $silentproblems[] = 'key cookie missing'; } elseif (!strlen($key)) { $silentproblems[] = 'key cookie empty'; } elseif (strlen($key) != QA_FORM_KEY_LENGTH) { $reportproblems[] = 'key cookie ' . $key . ' invalid'; } } } if (empty($silentproblems) && empty($reportproblems)) { if (strtolower(qa_calc_form_security_hash($action, $timestamp)) != strtolower($hash)) { $reportproblems[] = 'code mismatch'; } } } else { $reportproblems[] = 'code ' . $value . ' malformed'; } } if (count($reportproblems)) { @error_log('PHP Question2Answer form security violation for ' . $action . ' by ' . (qa_is_logged_in() ? 'userid ' . qa_get_logged_in_userid() : 'anonymous') . ' (' . implode(', ', array_merge($reportproblems, $silentproblems)) . ')' . ' on ' . @$_SERVER['REQUEST_URI'] . ' via ' . @$_SERVER['HTTP_REFERER']); } return empty($silentproblems) && empty($reportproblems); }
function qa_get_gravatar_html($email, $size) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } if ($size > 0) { return '<img src="' . (qa_is_https_probably() ? 'https' : 'http') . '://www.gravatar.com/avatar/' . md5(strtolower(trim($email))) . '?s=' . (int) $size . '" width="' . (int) $size . '" height="' . (int) $size . '" class="qa-avatar-image" alt=""/>'; } else { return null; } }
function qa_index_set_request() { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } $relativedepth = 0; if (isset($_GET['qa-rewrite'])) { // URLs rewritten by .htaccess $urlformat = QA_URL_FORMAT_NEAT; $requestparts = explode('/', qa_gpc_to_string($_GET['qa-rewrite'])); unset($_GET['qa-rewrite']); if (!empty($_SERVER['REQUEST_URI'])) { // workaround for the fact that Apache unescapes characters while rewriting $origpath = $_SERVER['REQUEST_URI']; $_GET = array(); $questionpos = strpos($origpath, '?'); if (is_numeric($questionpos)) { $params = explode('&', substr($origpath, $questionpos + 1)); foreach ($params as $param) { if (preg_match('/^([^\\=]*)(\\=(.*))?$/', $param, $matches)) { $argument = strtr(urldecode($matches[1]), '.', '_'); // simulate PHP's $_GET behavior $_GET[$argument] = qa_string_to_gpc(urldecode(@$matches[3])); } } $origpath = substr($origpath, 0, $questionpos); } // Generally we assume that $_GET['qa-rewrite'] has the right path depth, but this won't be the case if there's // a & or # somewhere in the middle of the path, due to apache unescaping. So we make a special case for that. $keepparts = count($requestparts); $requestparts = explode('/', urldecode($origpath)); // new request calculated from $_SERVER['REQUEST_URI'] for ($part = count($requestparts) - 1; $part >= 0; $part--) { if (is_numeric(strpos($requestparts[$part], '&')) || is_numeric(strpos($requestparts[$part], '#'))) { $keepparts += count($requestparts) - $part - 1; // this is how many parts we lost break; } } $requestparts = array_slice($requestparts, -$keepparts); // remove any irrelevant parts from the beginning } $relativedepth = count($requestparts); } elseif (isset($_GET['qa'])) { if (strpos($_GET['qa'], '/') === false) { $urlformat = empty($_SERVER['REQUEST_URI']) || strpos($_SERVER['REQUEST_URI'], '/index.php') !== false ? QA_URL_FORMAT_SAFEST : QA_URL_FORMAT_PARAMS; $requestparts = array(qa_gpc_to_string($_GET['qa'])); for ($part = 1; $part < 10; $part++) { if (isset($_GET['qa_' . $part])) { $requestparts[] = qa_gpc_to_string($_GET['qa_' . $part]); unset($_GET['qa_' . $part]); } } } else { $urlformat = QA_URL_FORMAT_PARAM; $requestparts = explode('/', qa_gpc_to_string($_GET['qa'])); } unset($_GET['qa']); } else { $phpselfunescaped = strtr($_SERVER['PHP_SELF'], '+', ' '); // seems necessary, and plus does not work with this scheme $indexpath = '/index.php/'; $indexpos = strpos($phpselfunescaped, $indexpath); if (is_numeric($indexpos)) { $urlformat = QA_URL_FORMAT_INDEX; $requestparts = explode('/', substr($phpselfunescaped, $indexpos + strlen($indexpath))); $relativedepth = 1 + count($requestparts); } else { $urlformat = null; // at home page so can't identify path type $requestparts = array(); } } foreach ($requestparts as $part => $requestpart) { // remove any blank parts if (!strlen($requestpart)) { unset($requestparts[$part]); } } reset($requestparts); $key = key($requestparts); $replacement = array_search(@$requestparts[$key], qa_get_request_map()); if ($replacement !== false) { $requestparts[$key] = $replacement; } qa_set_request(implode('/', $requestparts), $relativedepth > 1 ? str_repeat('../', $relativedepth - 1) : './', $urlformat); }
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", '^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 . 'qa-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'] = 'INET_NTOA(^posts.lastip)'; $selectspec['columns'][] = '^posts.parentid'; $selectspec['columns']['lastviewip'] = 'INET_NTOA(^posts.lastviewip)'; } if ($user) { $selectspec['columns'][] = '^posts.userid'; $selectspec['columns'][] = '^posts.cookieid'; $selectspec['columns']['createip'] = 'INET_NTOA(^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; }
function qa_index_set_request() { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } $relativedepth = 0; if (isset($_GET['qa-rewrite'])) { // URLs rewritten by .htaccess $urlformat = QA_URL_FORMAT_NEAT; $requestparts = explode('/', qa_gpc_to_string($_GET['qa-rewrite'])); unset($_GET['qa-rewrite']); $relativedepth = count($requestparts); // Workaround for fact that Apache unescapes characters while rewriting, based on assumption that $_GET['qa-rewrite'] has // right path depth, which is true do long as there are only escaped characters in the last part of the path if (!empty($_SERVER['REQUEST_URI'])) { $origpath = $_SERVER['REQUEST_URI']; $_GET = array(); $questionpos = strpos($origpath, '?'); if (is_numeric($questionpos)) { $params = explode('&', substr($origpath, $questionpos + 1)); foreach ($params as $param) { if (preg_match('/^([^\\=]*)(\\=(.*))?$/', $param, $matches)) { $_GET[urldecode($matches[1])] = qa_string_to_gpc(urldecode(@$matches[3])); } } $origpath = substr($origpath, 0, $questionpos); } $requestparts = array_slice(explode('/', urldecode($origpath)), -count($requestparts)); } } elseif (isset($_GET['qa'])) { if (strpos($_GET['qa'], '/') === false) { $urlformat = empty($_SERVER['REQUEST_URI']) || strpos($_SERVER['REQUEST_URI'], '/index.php') !== false ? QA_URL_FORMAT_SAFEST : QA_URL_FORMAT_PARAMS; $requestparts = array(qa_gpc_to_string($_GET['qa'])); for ($part = 1; $part < 10; $part++) { if (isset($_GET['qa_' . $part])) { $requestparts[] = qa_gpc_to_string($_GET['qa_' . $part]); unset($_GET['qa_' . $part]); } } } else { $urlformat = QA_URL_FORMAT_PARAM; $requestparts = explode('/', qa_gpc_to_string($_GET['qa'])); } unset($_GET['qa']); } else { $phpselfunescaped = strtr($_SERVER['PHP_SELF'], '+', ' '); // seems necessary, and plus does not work with this scheme $indexpath = '/index.php/'; $indexpos = strpos($phpselfunescaped, $indexpath); if (is_numeric($indexpos)) { $urlformat = QA_URL_FORMAT_INDEX; $requestparts = explode('/', substr($phpselfunescaped, $indexpos + strlen($indexpath))); $relativedepth = 1 + count($requestparts); } else { $urlformat = null; // at home page so can't identify path type $requestparts = array(); } } foreach ($requestparts as $part => $requestpart) { // remove any blank parts if (!strlen($requestpart)) { unset($requestparts[$part]); } } reset($requestparts); $key = key($requestparts); $replacement = array_search(@$requestparts[$key], qa_get_request_map()); if ($replacement !== false) { $requestparts[$key] = $replacement; } qa_set_request(implode('/', $requestparts), $relativedepth > 1 ? str_repeat('../', $relativedepth - 1) : './', $urlformat); }
function qa_substr($string, $start, $length = 2147483647) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } return function_exists('mb_substr') ? mb_substr($string, $start, $length, 'UTF-8') : substr($string, $start, $length); }
function qa_admin_sub_navigation() { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } $navigation = array(); $level = qa_get_logged_in_level(); if ($level >= QA_USER_LEVEL_ADMIN) { $navigation['admin/general'] = array('label' => qa_lang_html('admin/general_title'), 'url' => qa_path_html('admin/general')); $navigation['admin/emails'] = array('label' => qa_lang_html('admin/emails_title'), 'url' => qa_path_html('admin/emails')); $navigation['admin/users'] = array('label' => qa_lang_html('admin/users_title'), 'url' => qa_path_html('admin/users'), 'selected_on' => array('admin/users$', 'admin/userfields$', 'admin/usertitles$')); if ($level >= QA_USER_LEVEL_SUPER) { $navigation['admin/layout'] = array('label' => qa_lang_html('admin/layout_title'), 'url' => qa_path_html('admin/layout')); } $navigation['admin/posting'] = array('label' => qa_lang_html('admin/posting_title'), 'url' => qa_path_html('admin/posting')); $navigation['admin/viewing'] = array('label' => qa_lang_html('admin/viewing_title'), 'url' => qa_path_html('admin/viewing')); $navigation['admin/lists'] = array('label' => qa_lang_html('admin/lists_title'), 'url' => qa_path_html('admin/lists')); if (qa_using_categories()) { $navigation['admin/categories'] = array('label' => qa_lang_html('admin/categories_title'), 'url' => qa_path_html('admin/categories')); } $navigation['admin/permissions'] = array('label' => qa_lang_html('admin/permissions_title'), 'url' => qa_path_html('admin/permissions')); if ($level >= QA_USER_LEVEL_SUPER) { $navigation['admin/pages'] = array('label' => qa_lang_html('admin/pages_title'), 'url' => qa_path_html('admin/pages')); } $navigation['admin/feeds'] = array('label' => qa_lang_html('admin/feeds_title'), 'url' => qa_path_html('admin/feeds')); $navigation['admin/points'] = array('label' => qa_lang_html('admin/points_title'), 'url' => qa_path_html('admin/points')); $navigation['admin/spam'] = array('label' => qa_lang_html('admin/spam_title'), 'url' => qa_path_html('admin/spam')); if ($level >= QA_USER_LEVEL_SUPER) { $navigation['admin/stats'] = array('label' => qa_lang_html('admin/stats_title'), 'url' => qa_path_html('admin/stats')); } if (!QA_FINAL_EXTERNAL_USERS) { $navigation['admin/mailing'] = array('label' => qa_lang_html('admin/mailing_title'), 'url' => qa_path_html('admin/mailing')); } if ($level >= QA_USER_LEVEL_SUPER) { $navigation['admin/plugins'] = array('label' => qa_lang_html('admin/plugins_title'), 'url' => qa_path_html('admin/plugins')); } } if (!qa_user_maximum_permit_error('permit_moderate')) { $count = qa_user_permit_error('permit_moderate') ? null : qa_opt('cache_queuedcount'); // if only in some categories don't show cached count $navigation['admin/moderate'] = array('label' => qa_lang_html('admin/moderate_title') . ($count ? ' (' . $count . ')' : ''), 'url' => qa_path_html('admin/moderate')); } if (qa_opt('flagging_of_posts') && !qa_user_maximum_permit_error('permit_hide_show')) { $count = qa_user_permit_error('permit_hide_show') ? null : qa_opt('cache_flaggedcount'); // if only in some categories don't show cached count $navigation['admin/flagged'] = array('label' => qa_lang_html('admin/flagged_title') . ($count ? ' (' . $count . ')' : ''), 'url' => qa_path_html('admin/flagged')); } if (!qa_user_maximum_permit_error('permit_hide_show') || !qa_user_maximum_permit_error('permit_delete_hidden')) { $navigation['admin/hidden'] = array('label' => qa_lang_html('admin/hidden_title'), 'url' => qa_path_html('admin/hidden')); } if (!QA_FINAL_EXTERNAL_USERS && qa_opt('moderate_users') && $level >= QA_USER_LEVEL_MODERATOR) { $count = qa_opt('cache_uapprovecount'); $navigation['admin/approve'] = array('label' => qa_lang_html('admin/approve_users_title') . ($count ? ' (' . $count . ')' : ''), 'url' => qa_path_html('admin/approve')); } return $navigation; }
function qa_db_user_rand_sessioncode() { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } require_once QA_INCLUDE_DIR . 'qa-util-string.php'; return qa_random_alphanum(8); }
function qa_page_q_post_rules($post, $parentpost = null, $siblingposts = null, $childposts = null) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } $userid = qa_get_logged_in_userid(); $cookieid = qa_cookie_get(); $rules['isbyuser'] = qa_post_is_by_user($post, $userid, $cookieid); $rules['queued'] = substr($post['type'], 1) == '_QUEUED'; $rules['closed'] = $post['basetype'] == 'Q' && (isset($post['closedbyid']) || isset($post['selchildid']) && qa_opt('do_close_on_select')); // Cache some responses to the user permission checks $permiterror_post_q = qa_user_permit_error('permit_post_q'); $permiterror_post_a = qa_user_permit_error('permit_post_a'); $permiterror_post_c = qa_user_permit_error('permit_post_c'); $permiterror_edit = qa_user_permit_error($post['basetype'] == 'Q' ? 'permit_edit_q' : ($post['basetype'] == 'A' ? 'permit_edit_a' : 'permit_edit_c')); $permiterror_retagcat = qa_user_permit_error('permit_retag_cat'); $permiterror_hide_show = qa_user_permit_error($rules['isbyuser'] ? null : 'permit_hide_show'); $permiterror_close_open = qa_user_permit_error($rules['isbyuser'] ? null : 'permit_close_q'); $permiterror_moderate = qa_user_permit_error('permit_moderate'); // General permissions $rules['authorlast'] = !isset($post['lastuserid']) || $post['lastuserid'] === $post['userid']; $rules['viewable'] = $post['hidden'] ? !$permiterror_hide_show : ($rules['queued'] ? $rules['isbyuser'] || !$permiterror_moderate : true); // Answer, comment and edit might show the button even if the user still needs to do something (e.g. log in) $rules['answerbutton'] = $post['type'] == 'Q' && $permiterror_post_a != 'level' && !$rules['closed'] && (qa_opt('allow_self_answer') || !$rules['isbyuser']); $rules['commentbutton'] = ($post['type'] == 'Q' || $post['type'] == 'A') && $permiterror_post_c != 'level' && qa_opt($post['type'] == 'Q' ? 'comment_on_qs' : 'comment_on_as'); $rules['commentable'] = $rules['commentbutton'] && !$permiterror_post_c; $rules['editbutton'] = !$post['hidden'] && ($rules['isbyuser'] || $permiterror_edit != 'level' && !$rules['queued']) && !$rules['closed']; $rules['editable'] = $rules['editbutton'] && ($rules['isbyuser'] || !$permiterror_edit); $rules['retagcatbutton'] = $post['basetype'] == 'Q' && (qa_using_tags() || qa_using_categories()) && !$post['hidden'] && ($rules['isbyuser'] || $permiterror_retagcat != 'level'); $rules['retagcatable'] = $rules['retagcatbutton'] && ($rules['isbyuser'] || !$permiterror_retagcat); if ($rules['editbutton'] && $rules['retagcatbutton']) { // only show one button since they lead to the same form if ($rules['retagcatable'] && !$rules['editable']) { $rules['editbutton'] = false; } else { $rules['retagcatbutton'] = false; } } $rules['aselectable'] = $post['type'] == 'Q' && !qa_user_permit_error($rules['isbyuser'] ? null : 'permit_select_a'); $rules['flagbutton'] = qa_opt('flagging_of_posts') && !$rules['isbyuser'] && !$post['hidden'] && !$rules['queued'] && !@$post['userflag'] && qa_user_permit_error('permit_flag') != 'level'; $rules['flagtohide'] = $rules['flagbutton'] && !qa_user_permit_error('permit_flag') && $post['flagcount'] + 1 >= qa_opt('flagging_hide_after'); $rules['unflaggable'] = @$post['userflag'] && !$post['hidden']; $rules['clearflaggable'] = $post['flagcount'] >= (@$post['userflag'] ? 2 : 1) && !qa_user_permit_error('permit_hide_show'); // Other actions only show the button if it's immediately possible $notclosedbyother = !($rules['closed'] && isset($post['closedbyid']) && !$rules['authorlast']); $nothiddenbyother = !($post['hidden'] && !$rules['authorlast']); $rules['closeable'] = qa_opt('allow_close_questions') && $post['type'] == 'Q' && !$rules['closed'] && !$permiterror_close_open; $rules['reopenable'] = $rules['closed'] && isset($post['closedbyid']) && !$permiterror_close_open && !$post['hidden'] && ($notclosedbyother || !qa_user_permit_error('permit_close_q')); // cannot reopen a question if it's been hidden, or if it was closed by someone else and you don't have global closing permissions $rules['moderatable'] = $rules['queued'] && !$permiterror_moderate; $rules['hideable'] = !$post['hidden'] && ($rules['isbyuser'] || !$rules['queued']) && !$permiterror_hide_show && ($notclosedbyother || !qa_user_permit_error('permit_hide_show')); // cannot hide a question if it was closed by someone else and you don't have global hiding permissions $rules['reshowable'] = $post['hidden'] && !$permiterror_hide_show && !qa_user_moderation_reason() && ($nothiddenbyother && !$post['flagcount'] || !qa_user_permit_error('permit_hide_show')); // cannot reshow a question if it was hidden by someone else, or if it has flags - unless you have global hiding permissions $rules['deleteable'] = $post['hidden'] && !qa_user_permit_error('permit_delete_hidden'); $rules['claimable'] = !isset($post['userid']) && isset($userid) && strlen(@$post['cookieid']) && strcmp(@$post['cookieid'], $cookieid) == 0 && !($post['basetype'] == 'Q' ? $permiterror_post_q : ($post['basetype'] == 'A' ? $permiterror_post_a : $permiterror_post_c)); $rules['followable'] = $post['type'] == 'A' ? qa_opt('follow_on_as') : false; // Check for claims that could break rules about self answering and mulltiple answers if ($rules['claimable'] && $post['basetype'] == 'A') { if (!qa_opt('allow_self_answer') && isset($parentpost) && qa_post_is_by_user($parentpost, $userid, $cookieid)) { $rules['claimable'] = false; } if (isset($siblingposts) && !qa_opt('allow_multi_answers')) { foreach ($siblingposts as $siblingpost) { if ($siblingpost['parentid'] == $post['parentid'] && $siblingpost['basetype'] == 'A' && qa_post_is_by_user($siblingpost, $userid, $cookieid)) { $rules['claimable'] = false; } } } } // Now make any changes based on the child posts if (isset($childposts)) { foreach ($childposts as $childpost) { if ($childpost['parentid'] == $post['postid'] && ($childpost['basetype'] == 'A' || $childpost['basetype'] == 'C')) { $rules['deleteable'] = false; if ($childpost['basetype'] == 'A' && qa_post_is_by_user($childpost, $userid, $cookieid)) { if (!qa_opt('allow_multi_answers')) { $rules['answerbutton'] = false; } if (!qa_opt('allow_self_answer')) { $rules['claimable'] = false; } } } } } // Return the resulting rules return $rules; }
function qa_user_moderation_reason() { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } $reason = false; if (qa_get_logged_in_level() < QA_USER_LEVEL_EXPERT && qa_user_permit_error('permit_moderate')) { $userid = qa_get_logged_in_userid(); if (isset($userid)) { if (qa_opt('confirm_user_emails') && qa_opt('moderate_unconfirmed') && !(qa_get_logged_in_flags() & QA_USER_FLAGS_EMAIL_CONFIRMED)) { $reason = 'confirm'; } elseif (qa_opt('moderate_by_points') && qa_get_logged_in_points() < qa_opt('moderate_points_limit')) { $reason = 'points'; } } elseif (qa_opt('moderate_anon_post')) { $reason = 'login'; } } return $reason; }
function qa_flags_clear_all($oldpost, $userid, $handle, $cookieid) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } require_once QA_INCLUDE_DIR . 'db/votes.php'; require_once QA_INCLUDE_DIR . 'app/limits.php'; require_once QA_INCLUDE_DIR . 'db/post-update.php'; qa_db_userflags_clear_all($oldpost['postid']); qa_db_post_recount_flags($oldpost['postid']); qa_db_flaggedcount_update(); 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)); }
function qa_get_permit_options() { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } $permits = array('permit_view_q_page', 'permit_post_q', 'permit_post_a'); if (qa_opt('comment_on_qs') || qa_opt('comment_on_as')) { $permits[] = 'permit_post_c'; } if (qa_opt('voting_on_qs')) { $permits[] = 'permit_vote_q'; } if (qa_opt('voting_on_as')) { $permits[] = 'permit_vote_a'; } if (qa_opt('voting_on_qs') || qa_opt('voting_on_as')) { $permits[] = 'permit_vote_down'; } if (qa_using_tags() || qa_using_categories()) { $permits[] = 'permit_retag_cat'; } array_push($permits, 'permit_edit_q', 'permit_edit_a'); if (qa_opt('comment_on_qs') || qa_opt('comment_on_as')) { $permits[] = 'permit_edit_c'; } if (qa_opt('allow_close_questions')) { $permits[] = 'permit_close_q'; } array_push($permits, 'permit_select_a', 'permit_anon_view_ips'); if (qa_opt('flagging_of_posts')) { $permits[] = 'permit_flag'; } $permits[] = 'permit_moderate'; array_push($permits, 'permit_hide_show', 'permit_delete_hidden'); return $permits; }
/** * Return the full name (with prefix) of database table $rawname, usually if it used after a ^ symbol. */ function qa_db_add_table_prefix($rawname) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } $prefix = QA_MYSQL_TABLE_PREFIX; if (defined('QA_MYSQL_USERS_PREFIX')) { switch (strtolower($rawname)) { case 'users': case 'userlogins': case 'userprofile': case 'userfields': case 'messages': case 'cookies': case 'blobs': case 'cache': case 'userlogins_ibfk_1': // also special cases for constraint names // also special cases for constraint names case 'userprofile_ibfk_1': $prefix = QA_MYSQL_USERS_PREFIX; break; } } return $prefix . $rawname; }
function qa_db_points_update_ifuser($userid, $columns) { if (qa_to_override(__FUNCTION__)) { $args = func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_should_update_counts() && isset($userid)) { require_once QA_INCLUDE_DIR . 'app/options.php'; require_once QA_INCLUDE_DIR . 'app/cookies.php'; $calculations = qa_db_points_calculations(); if ($columns === true) { $keycolumns = $calculations; } elseif (empty($columns)) { $keycolumns = array(); } elseif (is_array($columns)) { $keycolumns = array_flip($columns); } else { $keycolumns = array($columns => true); } $insertfields = 'userid, '; $insertvalues = '$, '; $insertpoints = (int) qa_opt('points_base'); $updates = ''; $updatepoints = $insertpoints; foreach ($calculations as $field => $calculation) { $multiple = (int) $calculation['multiple']; if (isset($keycolumns[$field])) { $insertfields .= $field . ', '; $insertvalues .= '@_' . $field . ':=(SELECT ' . $calculation['formula'] . '), '; $updates .= $field . '=@_' . $field . ', '; $insertpoints .= '+(' . (int) $multiple . '*@_' . $field . ')'; } $updatepoints .= '+(' . $multiple . '*' . (isset($keycolumns[$field]) ? '@_' : '') . $field . ')'; } $query = 'INSERT INTO ^userpoints (' . $insertfields . 'points) VALUES (' . $insertvalues . $insertpoints . ') ' . 'ON DUPLICATE KEY UPDATE ' . $updates . 'points=' . $updatepoints . '+bonus'; qa_db_query_raw(str_replace('~', "='" . qa_db_escape_string($userid) . "'", qa_db_apply_sub($query, array($userid)))); // build like this so that a #, $ or ^ character in the $userid (if external integration) isn't substituted if (qa_db_insert_on_duplicate_inserted()) { qa_db_userpointscount_update(); } } }