Commit c3e5f3ae by Scott

Merge branch 'controllers' into dev

parents 990a0f4a a0c6a196
......@@ -19,6 +19,8 @@
More about this license: http://www.question2answer.org/license.php
*/
use Q2A\Exceptions\ExceptionHandler;
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../../');
exit;
......@@ -178,7 +180,20 @@ function qa_get_request_content()
$firstlower = strtolower($requestparts[0]);
$routing = qa_page_routing();
if (isset($routing[$requestlower])) {
qa_routing_config();
$route = qa_service('router')->match($requestlower);
if ($route !== null) {
// use new Controller system
qa_set_template($route->getId());
$controllerClass = $route->getController();
$ctrl = new $controllerClass();
try {
$qa_content = $ctrl->executeAction($route->getAction(), $route->getParameters());
} catch (Exception $e) {
$qa_content = (new ExceptionHandler())->handle($e);
}
} elseif (isset($routing[$requestlower])) {
qa_set_template($firstlower);
$qa_content = require QA_INCLUDE_DIR . $routing[$requestlower];
......@@ -437,16 +452,32 @@ function qa_page_routing()
'unanswered/' => 'pages/unanswered.php',
'unsubscribe' => 'pages/unsubscribe.php',
'updates' => 'pages/updates.php',
'user/' => 'pages/user.php',
'users' => 'pages/users.php',
'users/blocked' => 'pages/users-blocked.php',
'users/new' => 'pages/users-newest.php',
'users/special' => 'pages/users-special.php',
);
}
/**
* Set up routing.
*/
function qa_routing_config()
{
$router = qa_service('router');
$router->addRoute('user', 'get', 'user/{str}', '\Q2A\Controllers\User\UserProfile', 'profile');
$router->addRoute('user', 'post', 'user/{str}', '\Q2A\Controllers\User\UserProfile', 'profile');
$router->addRoute('user-self', 'get', 'user', '\Q2A\Controllers\User\UserProfile', 'index');
$router->addRoute('user-wall', 'get', 'user/{str}/wall', '\Q2A\Controllers\User\UserMessages', 'wall');
$router->addRoute('user-activity', 'get', 'user/{str}/activity', '\Q2A\Controllers\User\UserPosts', 'activity');
$router->addRoute('user-questions', 'get', 'user/{str}/questions', '\Q2A\Controllers\User\UserPosts', 'questions');
$router->addRoute('user-answers', 'get', 'user/{str}/answers', '\Q2A\Controllers\User\UserPosts', 'answers');
$router->addRoute('user-top', 'get', 'users', '\Q2A\Controllers\User\UsersList', 'top');
$router->addRoute('user-blocked', 'get', 'users/blocked', '\Q2A\Controllers\User\UsersList', 'blocked');
$router->addRoute('user-new', 'get', 'users/new', '\Q2A\Controllers\User\UsersList', 'newest');
$router->addRoute('user-special', 'get', 'users/special', '\Q2A\Controllers\User\UsersList', 'special');
}
/**
* Sets the template which should be passed to the theme class, telling it which type of page it's displaying
* @param $template
*/
......
......@@ -1570,7 +1570,7 @@ function qa_db_newest_users_selectspec($start, $count = null)
function qa_db_users_from_level_selectspec($level)
{
return array(
'columns' => array('^users.userid', 'handle', 'level'),
'columns' => array('^users.userid', 'handle', 'flags', 'level', 'email', 'avatarblobid' => 'BINARY avatarblobid', 'avatarwidth', 'avatarheight'),
'source' => '^users WHERE level>=# ORDER BY level DESC',
'arguments' => array($level),
'sortdesc' => 'level',
......@@ -1597,7 +1597,7 @@ function qa_db_users_with_flag_selectspec($flag, $start = 0, $limit = null)
}
return array(
'columns' => array('^users.userid', 'handle', 'flags', 'level'),
'columns' => array('^users.userid', 'handle', 'flags', 'level', 'email', 'avatarblobid' => 'BINARY avatarblobid', 'avatarwidth', 'avatarheight'),
'source' => $source,
'arguments' => $arguments,
);
......
......@@ -41,9 +41,14 @@ function qa_autoload($class)
{
if (strpos($class, 'Q2A_') === 0)
require QA_INCLUDE_DIR . strtr($class, '_', '/') . '.php';
if (strpos($class, 'Q2A\\') === 0) {
require QA_BASE_DIR . 'qa-src/' . strtr(substr($class, 4), '\\', '/') . '.php';
}
}
spl_autoload_register('qa_autoload');
use Q2A\App\Application;
// Execution section of this file - remainder contains function definitions
......@@ -58,7 +63,6 @@ if (defined('QA_WORDPRESS_LOAD_FILE')) {
require_once QA_JOOMLA_LOAD_FILE;
}
qa_initialize_constants_2();
qa_initialize_modularity();
qa_register_core_modules();
......@@ -1839,6 +1843,36 @@ function qa_retrieve_url($url)
/**
* Helper function to access the Application object.
* @return Application
*/
function qa_app()
{
return Application::getInstance();
}
/**
* Helper function to access services in the Container.
* If the $key parameter is set and the $object parameter is null the container is called to resolve the $key.
* If the $key and the $object parameters are null the container is called to bind the $object to the $key.
* @param mixed $key Identifier for the object to get/set.
* @param mixed $object Object to set in the $key (if null, a stored object is returned)
* @return mixed
*/
function qa_service($key, $object = null)
{
$app = Application::getInstance();
if ($object === null) {
return $app->getContainer()->get($key);
}
$app->getContainer()->set($key, $object);
}
/**
* Shortcut to get or set an option value without specifying database
* @param $name
* @param mixed $value
......
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\App;
use Q2A\Http\Router;
class Application
{
/** @var Container */
private $container;
/** @var static */
protected static $instance;
protected function __construct()
{
$this->container = new Container();
$this->registerCoreServices();
}
/**
* Instantiate and fetch the application as a singleton.
* @return static
*/
public static function getInstance()
{
if (static::$instance === null) {
static::$instance = new static();
}
return static::$instance;
}
/**
* Register the services used by the core.
*/
private function registerCoreServices()
{
$this->container->set('router', new Router());
}
/**
* Return the container instance.
* @return Container
*/
public function getContainer()
{
return $this->container;
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\App;
use Q2A\Exceptions\FatalErrorException;
class Container
{
/** @var array */
protected $services = array();
/**
* Bind an object to a key.
* @param string $key The key to bind the object to
* @param mixed $object The object to bind to the key
*/
public function set($key, $object)
{
$this->services[$key] = $object;
}
/**
* Return an object assigned to the given key. If the key is not found an exception is thrown.
* @param string $key The key to look for
* @return mixed
*/
public function get($key)
{
if (isset($this->services[$key])) {
return $this->services[$key];
}
throw new FatalErrorException(sprintf('Key "%s" not found in container', $key));
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Auth;
use Q2A\Exceptions\FatalErrorException;
class InternalUsersOnlyException extends FatalErrorException
{
/**
* InternalUsersOnlyException constructor.
*
* @param string $message
*/
public function __construct($message = 'Feature only supported by internal users.')
{
parent::__construct($message);
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Auth;
use Q2A\Exceptions\ErrorMessageException;
class NoPermissionException extends ErrorMessageException
{
/**
* NoPermissionException constructor.
*
* @param string $message
*/
public function __construct($message = null)
{
if ($message === null) {
$message = qa_lang_html('users/no_permission');
}
parent::__construct($message);
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Controllers;
use Q2A\Middleware\BaseMiddleware;
abstract class BaseController
{
/** @var BaseMiddleware[string] */
private $middleware;
public function __construct()
{
// TODO: constructor taking Database class parameter
$this->middleware = array();
}
/**
* Attach a middleware to one action or an array of actions. Use '*' to match all actions.
*
* @param BaseMiddleware $middleware
* @param array|string $actions
*/
public function addMiddleware(BaseMiddleware $middleware, $actions = '*')
{
if (is_array($actions)) {
foreach ($actions as $action) {
$this->addMiddlewareToAction($middleware, $action);
}
} else { // If it is a string
$this->addMiddlewareToAction($middleware, $actions);
}
}
/**
* @param BaseMiddleware $middleware
* @param string $action
*/
private function addMiddlewareToAction(BaseMiddleware $middleware, $action)
{
if (!isset($this->middleware[$action])) {
$this->middleware[$action] = array();
}
$this->middleware[$action][] = $middleware;
}
/**
* Execute the given action with the given parameters on this controller. after running all
* middleware for the action. This method is expected to return a qa_content array or throw an
* exception.
*
* @param string $action Action to execute
* @param array $parameters Parameters to send to the action
*
* @return mixed
*/
public function executeAction($action, $parameters)
{
$this->executeMiddlewareForAction('*');
$this->executeMiddlewareForAction($action);
return call_user_func_array(array($this, $action), $parameters);
}
/**
* @param string $action
*/
private function executeMiddlewareForAction($action)
{
if (!isset($this->middleware[$action])) {
return;
}
foreach ($this->middleware[$action] as $middleware) {
$middleware->handle();
}
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Controllers\User;
use Q2A\Http\Exceptions\PageNotFoundException;
use Q2A\Middleware\Auth\InternalUsersOnly;
require_once QA_INCLUDE_DIR . 'db/selects.php';
require_once QA_INCLUDE_DIR . 'app/messages.php';
class UserMessages extends \Q2A\Controllers\BaseController
{
public function __construct()
{
parent::__construct();
$this->addMiddleware(new InternalUsersOnly());
}
/**
* @param string $handle
*
* @return array
* @throws PageNotFoundException
*/
public function wall($handle)
{
$userhtml = qa_html($handle);
$start = qa_get_start();
// Find the questions for this user
list($useraccount, $usermessages) = qa_db_select_with_pending(
qa_db_user_account_selectspec($handle, false),
qa_db_recent_messages_selectspec(null, null, $handle, false, qa_opt_if_loaded('page_size_wall'), $start)
);
if (!is_array($useraccount)) { // check the user exists
throw new PageNotFoundException();
}
// Perform pagination
$pagesize = qa_opt('page_size_wall');
$count = $useraccount['wallposts'];
$loginuserid = qa_get_logged_in_userid();
$usermessages = array_slice($usermessages, 0, $pagesize);
$usermessages = qa_wall_posts_add_rules($usermessages, $start);
// Process deleting or adding a wall post (similar but not identical code to qq-page-user-profile.php)
$errors = array();
$wallposterrorhtml = qa_wall_error_html($loginuserid, $useraccount['userid'], $useraccount['flags']);
foreach ($usermessages as $message) {
if ($message['deleteable'] && qa_clicked('m' . $message['messageid'] . '_dodelete')) {
if (!qa_check_form_security_code('wall-' . $useraccount['handle'], qa_post_text('code'))) {
$errors['page'] = qa_lang_html('misc/form_security_again');
} else {
qa_wall_delete_post($loginuserid, qa_get_logged_in_handle(), qa_cookie_get(), $message);
qa_redirect(qa_request(), $_GET);
}
}
}
if (qa_clicked('dowallpost')) {
$inmessage = qa_post_text('message');
if (!strlen($inmessage)) {
$errors['message'] = qa_lang('profile/post_wall_empty');
} elseif (!qa_check_form_security_code('wall-' . $useraccount['handle'], qa_post_text('code'))) {
$errors['message'] = qa_lang_html('misc/form_security_again');
} elseif (!$wallposterrorhtml) {
qa_wall_add_post($loginuserid, qa_get_logged_in_handle(), qa_cookie_get(), $useraccount['userid'], $useraccount['handle'], $inmessage, '');
qa_redirect(qa_request());
}
}
// Prepare content for theme
$qa_content = qa_content_prepare();
$qa_content['title'] = qa_lang_html_sub('profile/wall_for_x', $userhtml);
$qa_content['error'] = @$errors['page'];
$qa_content['message_list'] = array(
'tags' => 'id="wallmessages"',
'form' => array(
'tags' => 'name="wallpost" method="post" action="' . qa_self_html() . '"',
'style' => 'tall',
'hidden' => array(
'qa_click' => '', // for simulating clicks in Javascript
'handle' => qa_html($useraccount['handle']),
'start' => qa_html($start),
'code' => qa_get_form_security_code('wall-' . $useraccount['handle']),
),
),
'messages' => array(),
);
if ($start == 0) { // only allow posting on first page
if ($wallposterrorhtml) {
$qa_content['message_list']['error'] = $wallposterrorhtml; // an error that means we are not allowed to post
} else {
$qa_content['message_list']['form']['fields'] = array(
'message' => array(
'tags' => 'name="message" id="message"',
'value' => qa_html(@$inmessage, false),
'rows' => 2,
'error' => qa_html(@$errors['message']),
),
);
$qa_content['message_list']['form']['buttons'] = array(
'post' => array(
'tags' => 'name="dowallpost" onclick="return qa_submit_wall_post(this, false);"',
'label' => qa_lang_html('profile/post_wall_button'),
),
);
}
}
foreach ($usermessages as $message) {
$qa_content['message_list']['messages'][] = qa_wall_post_view($message);
}
$qa_content['page_links'] = qa_html_page_links(qa_request(), $start, $pagesize, $count, qa_opt('pages_prev_next'));
// Sub menu for navigation in user pages
$ismyuser = isset($loginuserid) && $loginuserid == $useraccount['userid'];
$qa_content['navigation']['sub'] = qa_user_sub_navigation($handle, 'wall', $ismyuser);
return $qa_content;
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Controllers\User;
use Q2A\Http\Exceptions\PageNotFoundException;
require_once QA_INCLUDE_DIR . 'db/users.php';
require_once QA_INCLUDE_DIR . 'db/selects.php';
require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR . 'app/format.php';
class UserPosts extends \Q2A\Controllers\BaseController
{
protected $userid;
protected $userhtml;
/**
* @param string $handle
*
* @return array
* @throws PageNotFoundException
*/
public function activity($handle)
{
$this->userHtml($handle);
// Find the recent activity for this user
$loginuserid = qa_get_logged_in_userid();
$identifier = QA_FINAL_EXTERNAL_USERS ? $this->userid : $handle;
list($useraccount, $questions, $answerqs, $commentqs, $editqs) = qa_db_select_with_pending(
QA_FINAL_EXTERNAL_USERS ? null : qa_db_user_account_selectspec($handle, false),
qa_db_user_recent_qs_selectspec($loginuserid, $identifier, qa_opt_if_loaded('page_size_activity')),
qa_db_user_recent_a_qs_selectspec($loginuserid, $identifier),
qa_db_user_recent_c_qs_selectspec($loginuserid, $identifier),
qa_db_user_recent_edit_qs_selectspec($loginuserid, $identifier)
);
if (!QA_FINAL_EXTERNAL_USERS && !is_array($useraccount)) { // check the user exists
throw new PageNotFoundException();
}
// Get information on user references
$questions = qa_any_sort_and_dedupe(array_merge($questions, $answerqs, $commentqs, $editqs));
$questions = array_slice($questions, 0, qa_opt('page_size_activity'));
$usershtml = qa_userids_handles_html(qa_any_get_userids_handles($questions), false);
// Prepare content for theme
$qa_content = qa_content_prepare(true);
if (count($questions))
$qa_content['title'] = qa_lang_html_sub('profile/recent_activity_by_x', $this->userhtml);
else
$qa_content['title'] = qa_lang_html_sub('profile/no_posts_by_x', $this->userhtml);
// Recent activity by this user
$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();
$htmldefaults = qa_post_html_defaults('Q');
$htmldefaults['whoview'] = false;
$htmldefaults['voteview'] = false;
$htmldefaults['avatarsize'] = 0;
foreach ($questions as $question) {
$qa_content['q_list']['qs'][] = qa_any_to_q_html_fields($question, $loginuserid, qa_cookie_get(),
$usershtml, null, array('voteview' => false) + qa_post_html_options($question, $htmldefaults));
}
// Sub menu for navigation in user pages
$ismyuser = isset($loginuserid) && $loginuserid == (QA_FINAL_EXTERNAL_USERS ? $this->userid : $useraccount['userid']);
$qa_content['navigation']['sub'] = qa_user_sub_navigation($handle, 'activity', $ismyuser);
return $qa_content;
}
/**
* @param string $handle
*
* @return array
* @throws PageNotFoundException
*/
public function questions($handle)
{
$this->userHtml($handle);
$start = qa_get_start();
// Find the questions for this user
$loginuserid = qa_get_logged_in_userid();
$identifier = QA_FINAL_EXTERNAL_USERS ? $this->userid : $handle;
list($useraccount, $userpoints, $questions) = qa_db_select_with_pending(
QA_FINAL_EXTERNAL_USERS ? null : qa_db_user_account_selectspec($handle, false),
qa_db_user_points_selectspec($identifier),
qa_db_user_recent_qs_selectspec($loginuserid, $identifier, qa_opt_if_loaded('page_size_qs'), $start)
);
if (!QA_FINAL_EXTERNAL_USERS && !is_array($useraccount)) { // check the user exists
throw new PageNotFoundException();
}
// Get information on user questions
$pagesize = qa_opt('page_size_qs');
$count = (int)@$userpoints['qposts'];
$questions = array_slice($questions, 0, $pagesize);
$usershtml = qa_userids_handles_html($questions, false);
// Prepare content for theme
$qa_content = qa_content_prepare(true);
if (count($questions))
$qa_content['title'] = qa_lang_html_sub('profile/questions_by_x', $this->userhtml);
else
$qa_content['title'] = qa_lang_html_sub('profile/no_questions_by_x', $this->userhtml);
// Recent questions by this user
$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();
$htmldefaults = qa_post_html_defaults('Q');
$htmldefaults['whoview'] = false;
$htmldefaults['avatarsize'] = 0;
foreach ($questions as $question) {
$qa_content['q_list']['qs'][] = qa_post_html_fields($question, $loginuserid, qa_cookie_get(),
$usershtml, null, qa_post_html_options($question, $htmldefaults));
}
$qa_content['page_links'] = qa_html_page_links(qa_request(), $start, $pagesize, $count, qa_opt('pages_prev_next'));
// Sub menu for navigation in user pages
$ismyuser = isset($loginuserid) && $loginuserid == (QA_FINAL_EXTERNAL_USERS ? $this->userid : $useraccount['userid']);
$qa_content['navigation']['sub'] = qa_user_sub_navigation($handle, 'questions', $ismyuser);
return $qa_content;
}
/**
* @param string $handle
*
* @return array
* @throws PageNotFoundException
*/
public function answers($handle)
{
$this->userHtml($handle);
$start = qa_get_start();
// Find the questions for this user
$loginuserid = qa_get_logged_in_userid();
$identifier = QA_FINAL_EXTERNAL_USERS ? $this->userid : $handle;
list($useraccount, $userpoints, $questions) = qa_db_select_with_pending(
QA_FINAL_EXTERNAL_USERS ? null : qa_db_user_account_selectspec($handle, false),
qa_db_user_points_selectspec($identifier),
qa_db_user_recent_a_qs_selectspec($loginuserid, $identifier, qa_opt_if_loaded('page_size_activity'), $start)
);
if (!QA_FINAL_EXTERNAL_USERS && !is_array($useraccount)) { // check the user exists
throw new PageNotFoundException();
}
// Get information on user questions
$pagesize = qa_opt('page_size_activity');
$count = (int)@$userpoints['aposts'];
$questions = array_slice($questions, 0, $pagesize);
$usershtml = qa_userids_handles_html($questions, false);
// Prepare content for theme
$qa_content = qa_content_prepare(true);
if (count($questions))
$qa_content['title'] = qa_lang_html_sub('profile/answers_by_x', $this->userhtml);
else
$qa_content['title'] = qa_lang_html_sub('profile/no_answers_by_x', $this->userhtml);
// Recent questions by this user
$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();
$htmldefaults = qa_post_html_defaults('Q');
$htmldefaults['whoview'] = false;
$htmldefaults['avatarsize'] = 0;
$htmldefaults['ovoteview'] = true;
$htmldefaults['answersview'] = false;
foreach ($questions as $question) {
$options = qa_post_html_options($question, $htmldefaults);
$options['voteview'] = qa_get_vote_view('A', false, false);
$qa_content['q_list']['qs'][] = qa_other_to_q_html_fields($question, $loginuserid, qa_cookie_get(),
$usershtml, null, $options);
}
$qa_content['page_links'] = qa_html_page_links(qa_request(), $start, $pagesize, $count, qa_opt('pages_prev_next'));
// Sub menu for navigation in user pages
$ismyuser = isset($loginuserid) && $loginuserid == (QA_FINAL_EXTERNAL_USERS ? $this->userid : $useraccount['userid']);
$qa_content['navigation']['sub'] = qa_user_sub_navigation($handle, 'answers', $ismyuser);
return $qa_content;
}
/**
* Return the HTML to display for the handle, and if we're using external users, determine the userid.
*
* @param string $handle
* @throws PageNotFoundException
*/
private function userHtml($handle)
{
if (QA_FINAL_EXTERNAL_USERS) {
$this->userid = qa_handle_to_userid($handle);
if (!isset($this->userid)) { // check the user exists
throw new PageNotFoundException();
}
$usershtml = qa_get_users_html(array($this->userid), false, qa_path_to_root(), true);
$this->userhtml = @$usershtml[$this->userid];
} else
$this->userhtml = qa_html($handle);
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Controllers\User;
use Q2A\Http\Exceptions\PageNotFoundException;
require_once QA_INCLUDE_DIR . 'db/users.php';
require_once QA_INCLUDE_DIR . 'db/selects.php';
require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR . 'app/format.php';
class UserProfile extends \Q2A\Controllers\BaseController
{
private $userid;
private $userhtml;
public function index()
{
$handle = qa_get_logged_in_handle();
qa_redirect(!empty($handle) ? 'user/' . $handle : 'users');
}
public function profile($handle)
{
// Get the HTML to display for the handle, and if we're using external users, determine the userid
if (QA_FINAL_EXTERNAL_USERS) {
$userid = qa_handle_to_userid($handle);
if (!isset($userid)) { // check the user exists
throw new PageNotFoundException();
}
$usershtml = qa_get_users_html(array($userid), false, qa_path_to_root(), true);
$userhtml = @$usershtml[$userid];
} else
$userhtml = qa_html($handle);
// Redirect to 'My Account' page if button clicked
if (qa_clicked('doaccount'))
qa_redirect('account');
// Find the user profile and questions and answers for this handle
$loginuserid = qa_get_logged_in_userid();
$identifier = QA_FINAL_EXTERNAL_USERS ? $userid : $handle;
list($useraccount, $userprofile, $userfields, $usermessages, $userpoints, $userlevels, $navcategories, $userrank) =
qa_db_select_with_pending(
QA_FINAL_EXTERNAL_USERS ? null : qa_db_user_account_selectspec($handle, false),
QA_FINAL_EXTERNAL_USERS ? null : qa_db_user_profile_selectspec($handle, false),
QA_FINAL_EXTERNAL_USERS ? null : qa_db_userfields_selectspec(),
QA_FINAL_EXTERNAL_USERS ? null : qa_db_recent_messages_selectspec(null, null, $handle, false, qa_opt_if_loaded('page_size_wall')),
qa_db_user_points_selectspec($identifier),
qa_db_user_levels_selectspec($identifier, QA_FINAL_EXTERNAL_USERS, true),
qa_db_category_nav_selectspec(null, true),
qa_db_user_rank_selectspec($identifier)
);
if (!QA_FINAL_EXTERNAL_USERS && $handle !== qa_get_logged_in_handle()) {
foreach ($userfields as $index => $userfield) {
if (isset($userfield['permit']) && qa_permit_value_error($userfield['permit'], $loginuserid, qa_get_logged_in_level(), qa_get_logged_in_flags()))
unset($userfields[$index]); // don't pay attention to user fields we're not allowed to view
}
}
// Check the user exists and work out what can and can't be set (if not using single sign-on)
$errors = array();
$loginlevel = qa_get_logged_in_level();
if (!QA_FINAL_EXTERNAL_USERS) { // if we're using integrated user management, we can know and show more
require_once QA_INCLUDE_DIR . 'app/messages.php';
if (!is_array($userpoints) && !is_array($useraccount)) { // check the user exists
throw new PageNotFoundException();
}
$userid = $useraccount['userid'];
$fieldseditable = false;
$maxlevelassign = null;
$maxuserlevel = $useraccount['level'];
foreach ($userlevels as $userlevel)
$maxuserlevel = max($maxuserlevel, $userlevel['level']);
if (isset($loginuserid) && $loginuserid != $userid &&
($loginlevel >= QA_USER_LEVEL_SUPER || $loginlevel > $maxuserlevel) &&
!qa_user_permit_error()
) { // can't change self - or someone on your level (or higher, obviously) unless you're a super admin
if ($loginlevel >= QA_USER_LEVEL_SUPER)
$maxlevelassign = QA_USER_LEVEL_SUPER;
elseif ($loginlevel >= QA_USER_LEVEL_ADMIN)
$maxlevelassign = QA_USER_LEVEL_MODERATOR;
elseif ($loginlevel >= QA_USER_LEVEL_MODERATOR)
$maxlevelassign = QA_USER_LEVEL_EXPERT;
if ($loginlevel >= QA_USER_LEVEL_ADMIN)
$fieldseditable = true;
if (isset($maxlevelassign) && ($useraccount['flags'] & QA_USER_FLAGS_USER_BLOCKED))
$maxlevelassign = min($maxlevelassign, QA_USER_LEVEL_EDITOR); // if blocked, can't promote too high
}
$approvebutton = isset($maxlevelassign)
&& $useraccount['level'] < QA_USER_LEVEL_APPROVED
&& $maxlevelassign >= QA_USER_LEVEL_APPROVED
&& !($useraccount['flags'] & QA_USER_FLAGS_USER_BLOCKED)
&& qa_opt('moderate_users');
$usereditbutton = $fieldseditable || isset($maxlevelassign);
$userediting = $usereditbutton && (qa_get_state() == 'edit');
$wallposterrorhtml = qa_wall_error_html($loginuserid, $useraccount['userid'], $useraccount['flags']);
// This code is similar but not identical to that in to qq-page-user-wall.php
$usermessages = array_slice($usermessages, 0, qa_opt('page_size_wall'));
$usermessages = qa_wall_posts_add_rules($usermessages, 0);
foreach ($usermessages as $message) {
if ($message['deleteable'] && qa_clicked('m' . $message['messageid'] . '_dodelete')) {
if (!qa_check_form_security_code('wall-' . $useraccount['handle'], qa_post_text('code')))
$errors['page'] = qa_lang_html('misc/form_security_again');
else {
qa_wall_delete_post($loginuserid, qa_get_logged_in_handle(), qa_cookie_get(), $message);
qa_redirect(qa_request(), null, null, null, 'wall');
}
}
}
}
// Process edit or save button for user, and other actions
if (!QA_FINAL_EXTERNAL_USERS) {
$reloaduser = false;
if ($usereditbutton) {
if (qa_clicked('docancel')) {
qa_redirect(qa_request());
} elseif (qa_clicked('doedit')) {
qa_redirect(qa_request(), array('state' => 'edit'));
} elseif (qa_clicked('dosave')) {
require_once QA_INCLUDE_DIR . 'app/users-edit.php';
require_once QA_INCLUDE_DIR . 'db/users.php';
$inemail = qa_post_text('email');
$inprofile = array();
foreach ($userfields as $userfield)
$inprofile[$userfield['fieldid']] = qa_post_text('field_' . $userfield['fieldid']);
if (!qa_check_form_security_code('user-edit-' . $handle, qa_post_text('code'))) {
$errors['page'] = qa_lang_html('misc/form_security_again');
$userediting = true;
} else {
if (qa_post_text('removeavatar')) {
qa_db_user_set_flag($userid, QA_USER_FLAGS_SHOW_AVATAR, false);
qa_db_user_set_flag($userid, QA_USER_FLAGS_SHOW_GRAVATAR, false);
if (isset($useraccount['avatarblobid'])) {
require_once QA_INCLUDE_DIR . 'app/blobs.php';
qa_db_user_set($userid, 'avatarblobid', null);
qa_db_user_set($userid, 'avatarwidth', null);
qa_db_user_set($userid, 'avatarheight', null);
qa_delete_blob($useraccount['avatarblobid']);
}
}
if ($fieldseditable) {
$filterhandle = $handle; // we're not filtering the handle...
$errors = qa_handle_email_filter($filterhandle, $inemail, $useraccount);
unset($errors['handle']); // ...and we don't care about any errors in it
if (!isset($errors['email'])) {
if ($inemail != $useraccount['email']) {
qa_db_user_set($userid, 'email', $inemail);
qa_db_user_set_flag($userid, QA_USER_FLAGS_EMAIL_CONFIRMED, false);
}
}
if (count($inprofile)) {
$filtermodules = qa_load_modules_with('filter', 'filter_profile');
foreach ($filtermodules as $filtermodule)
$filtermodule->filter_profile($inprofile, $errors, $useraccount, $userprofile);
}
foreach ($userfields as $userfield) {
if (!isset($errors[$userfield['fieldid']]))
qa_db_user_profile_set($userid, $userfield['title'], $inprofile[$userfield['fieldid']]);
}
if (count($errors))
$userediting = true;
qa_report_event('u_edit', $loginuserid, qa_get_logged_in_handle(), qa_cookie_get(), array(
'userid' => $userid,
'handle' => $useraccount['handle'],
));
}
if (isset($maxlevelassign)) {
$inlevel = min($maxlevelassign, (int)qa_post_text('level')); // constrain based on maximum permitted to prevent simple browser-based attack
if ($inlevel != $useraccount['level'])
qa_set_user_level($userid, $useraccount['handle'], $inlevel, $useraccount['level']);
if (qa_using_categories()) {
$inuserlevels = array();
for ($index = 1; $index <= 999; $index++) {
$inlevel = qa_post_text('uc_' . $index . '_level');
if (!isset($inlevel))
break;
$categoryid = qa_get_category_field_value('uc_' . $index . '_cat');
if (strlen($categoryid) && strlen($inlevel)) {
$inuserlevels[] = array(
'entitytype' => QA_ENTITY_CATEGORY,
'entityid' => $categoryid,
'level' => min($maxlevelassign, (int)$inlevel),
);
}
}
qa_db_user_levels_set($userid, $inuserlevels);
}
}
if (empty($errors))
qa_redirect(qa_request());
list($useraccount, $userprofile, $userlevels) = qa_db_select_with_pending(
qa_db_user_account_selectspec($userid, true),
qa_db_user_profile_selectspec($userid, true),
qa_db_user_levels_selectspec($userid, true, true)
);
}
}
}
if (qa_clicked('doapprove') || qa_clicked('doblock') || qa_clicked('dounblock') || qa_clicked('dohideall') || qa_clicked('dodelete')) {
if (!qa_check_form_security_code('user-' . $handle, qa_post_text('code')))
$errors['page'] = qa_lang_html('misc/form_security_again');
else {
if ($approvebutton && qa_clicked('doapprove')) {
require_once QA_INCLUDE_DIR . 'app/users-edit.php';
qa_set_user_level($userid, $useraccount['handle'], QA_USER_LEVEL_APPROVED, $useraccount['level']);
qa_redirect(qa_request());
}
if (isset($maxlevelassign) && ($maxuserlevel < QA_USER_LEVEL_MODERATOR)) {
if (qa_clicked('doblock')) {
require_once QA_INCLUDE_DIR . 'app/users-edit.php';
qa_set_user_blocked($userid, $useraccount['handle'], true);
qa_redirect(qa_request());
}
if (qa_clicked('dounblock')) {
require_once QA_INCLUDE_DIR . 'app/users-edit.php';
qa_set_user_blocked($userid, $useraccount['handle'], false);
qa_redirect(qa_request());
}
if (qa_clicked('dohideall') && !qa_user_permit_error('permit_hide_show')) {
require_once QA_INCLUDE_DIR . 'db/admin.php';
require_once QA_INCLUDE_DIR . 'app/posts.php';
$postids = qa_db_get_user_visible_postids($userid);
foreach ($postids as $postid)
qa_post_set_status($postid, QA_POST_STATUS_HIDDEN, $loginuserid);
qa_redirect(qa_request());
}
if (qa_clicked('dodelete') && ($loginlevel >= QA_USER_LEVEL_ADMIN)) {
require_once QA_INCLUDE_DIR . 'app/users-edit.php';
qa_delete_user($userid);
qa_report_event('u_delete', $loginuserid, qa_get_logged_in_handle(), qa_cookie_get(), array(
'userid' => $userid,
'handle' => $useraccount['handle'],
));
qa_redirect('users');
}
}
}
}
if (qa_clicked('dowallpost')) {
$inmessage = qa_post_text('message');
if (!strlen($inmessage)) {
$errors['message'] = qa_lang('profile/post_wall_empty');
} elseif (!qa_check_form_security_code('wall-' . $useraccount['handle'], qa_post_text('code'))) {
$errors['message'] = qa_lang_html('misc/form_security_again');
} elseif (!$wallposterrorhtml) {
qa_wall_add_post($loginuserid, qa_get_logged_in_handle(), qa_cookie_get(), $userid, $useraccount['handle'], $inmessage, '');
qa_redirect(qa_request(), null, null, null, 'wall');
}
}
}
// Process bonus setting button
if ($loginlevel >= QA_USER_LEVEL_ADMIN && qa_clicked('dosetbonus')) {
require_once QA_INCLUDE_DIR . 'db/points.php';
$inbonus = (int)qa_post_text('bonus');
if (!qa_check_form_security_code('user-activity-' . $handle, qa_post_text('code'))) {
$errors['page'] = qa_lang_html('misc/form_security_again');
} else {
qa_db_points_set_bonus($userid, $inbonus);
qa_db_points_update_ifuser($userid, null);
qa_redirect(qa_request(), null, null, null, 'activity');
}
}
// Prepare content for theme
$qa_content = qa_content_prepare();
$qa_content['title'] = qa_lang_html_sub('profile/user_x', $userhtml);
$qa_content['error'] = @$errors['page'];
if (isset($loginuserid) && $loginuserid != $useraccount['userid'] && !QA_FINAL_EXTERNAL_USERS) {
$favoritemap = qa_get_favorite_non_qs_map();
$favorite = @$favoritemap['user'][$useraccount['userid']];
$qa_content['favorite'] = qa_favorite_form(QA_ENTITY_USER, $useraccount['userid'], $favorite,
qa_lang_sub($favorite ? 'main/remove_x_favorites' : 'users/add_user_x_favorites', $handle));
}
// General information about the user, only available if we're using internal user management
if (!QA_FINAL_EXTERNAL_USERS) {
$membertime = qa_time_to_string(qa_opt('db_time') - $useraccount['created']);
$joindate = qa_when_to_html($useraccount['created'], 0);
$qa_content['form_profile'] = array(
'tags' => 'method="post" action="' . qa_self_html() . '"',
'style' => 'wide',
'fields' => array(
'avatar' => array(
'type' => 'image',
'style' => 'tall',
'label' => '',
'html' => qa_get_user_avatar_html($useraccount['flags'], $useraccount['email'], $useraccount['handle'],
$useraccount['avatarblobid'], $useraccount['avatarwidth'], $useraccount['avatarheight'], qa_opt('avatar_profile_size')),
'id' => 'avatar',
),
'removeavatar' => null,
'duration' => array(
'type' => 'static',
'label' => qa_lang_html('users/member_for'),
'value' => qa_html($membertime . ' (' . qa_lang_sub('main/since_x', $joindate['data']) . ')'),
'id' => 'duration',
),
'level' => array(
'type' => 'static',
'label' => qa_lang_html('users/member_type'),
'tags' => 'name="level"',
'value' => qa_html(qa_user_level_string($useraccount['level'])),
'note' => (($useraccount['flags'] & QA_USER_FLAGS_USER_BLOCKED) && isset($maxlevelassign)) ? qa_lang_html('users/user_blocked') : '',
'id' => 'level',
),
),
);
if (empty($qa_content['form_profile']['fields']['avatar']['html']))
unset($qa_content['form_profile']['fields']['avatar']);
// Private message link
if (qa_opt('allow_private_messages') && isset($loginuserid) && $loginuserid != $userid && !($useraccount['flags'] & QA_USER_FLAGS_NO_MESSAGES) && !$userediting) {
$qa_content['form_profile']['fields']['level']['value'] .= strtr(qa_lang_html('profile/send_private_message'), array(
'^1' => '<a href="' . qa_path_html('message/' . $handle) . '">',
'^2' => '</a>',
));
}
// Levels editing or viewing (add category-specific levels)
if ($userediting) {
if (isset($maxlevelassign)) {
$qa_content['form_profile']['fields']['level']['type'] = 'select';
$showlevels = array(QA_USER_LEVEL_BASIC);
if (qa_opt('moderate_users'))
$showlevels[] = QA_USER_LEVEL_APPROVED;
array_push($showlevels, QA_USER_LEVEL_EXPERT, QA_USER_LEVEL_EDITOR, QA_USER_LEVEL_MODERATOR, QA_USER_LEVEL_ADMIN, QA_USER_LEVEL_SUPER);
$leveloptions = array();
$catleveloptions = array('' => qa_lang_html('users/category_level_none'));
foreach ($showlevels as $showlevel) {
if ($showlevel <= $maxlevelassign) {
$leveloptions[$showlevel] = qa_html(qa_user_level_string($showlevel));
if ($showlevel > QA_USER_LEVEL_BASIC)
$catleveloptions[$showlevel] = $leveloptions[$showlevel];
}
}
$qa_content['form_profile']['fields']['level']['options'] = $leveloptions;
// Category-specific levels
if (qa_using_categories()) {
$catleveladd = strlen(qa_get('catleveladd')) > 0;
if (!$catleveladd && !count($userlevels)) {
$qa_content['form_profile']['fields']['level']['suffix'] = strtr(qa_lang_html('users/category_level_add'), array(
'^1' => '<a href="' . qa_path_html(qa_request(), array('state' => 'edit', 'catleveladd' => 1)) . '">',
'^2' => '</a>',
));
} else {
$qa_content['form_profile']['fields']['level']['suffix'] = qa_lang_html('users/level_in_general');
}
if ($catleveladd || count($userlevels))
$userlevels[] = array('entitytype' => QA_ENTITY_CATEGORY);
$index = 0;
foreach ($userlevels as $userlevel) {
if ($userlevel['entitytype'] == QA_ENTITY_CATEGORY) {
$index++;
$id = 'ls_' . +$index;
$qa_content['form_profile']['fields']['uc_' . $index . '_level'] = array(
'label' => qa_lang_html('users/category_level_label'),
'type' => 'select',
'tags' => 'name="uc_' . $index . '_level" id="' . qa_html($id) . '" onchange="this.qa_prev=this.options[this.selectedIndex].value;"',
'options' => $catleveloptions,
'value' => isset($userlevel['level']) ? qa_html(qa_user_level_string($userlevel['level'])) : '',
'suffix' => qa_lang_html('users/category_level_in'),
);
$qa_content['form_profile']['fields']['uc_' . $index . '_cat'] = array();
if (isset($userlevel['entityid']))
$fieldnavcategories = qa_db_select_with_pending(qa_db_category_nav_selectspec($userlevel['entityid'], true));
else
$fieldnavcategories = $navcategories;
qa_set_up_category_field($qa_content, $qa_content['form_profile']['fields']['uc_' . $index . '_cat'],
'uc_' . $index . '_cat', $fieldnavcategories, @$userlevel['entityid'], true, true);
unset($qa_content['form_profile']['fields']['uc_' . $index . '_cat']['note']);
}
}
$qa_content['script_lines'][] = array(
"function qa_update_category_levels()",
"{",
"\tglob=document.getElementById('level_select');",
"\tif (!glob)",
"\t\treturn;",
"\tvar opts=glob.options;",
"\tvar lev=parseInt(opts[glob.selectedIndex].value);",
"\tfor (var i=1; i<9999; i++) {",
"\t\tvar sel=document.getElementById('ls_'+i);",
"\t\tif (!sel)",
"\t\t\tbreak;",
"\t\tsel.qa_prev=sel.qa_prev || sel.options[sel.selectedIndex].value;",
"\t\tsel.options.length=1;", // just leaves "no upgrade" element
"\t\tfor (var j=0; j<opts.length; j++)",
"\t\t\tif (parseInt(opts[j].value)>lev)",
"\t\t\t\tsel.options[sel.options.length]=new Option(opts[j].text, opts[j].value, false, (opts[j].value==sel.qa_prev));",
"\t}",
"}",
);
$qa_content['script_onloads'][] = array(
"qa_update_category_levels();",
);
$qa_content['form_profile']['fields']['level']['tags'] .= ' id="level_select" onchange="qa_update_category_levels();"';
}
}
} else {
foreach ($userlevels as $userlevel) {
if ($userlevel['entitytype'] == QA_ENTITY_CATEGORY && $userlevel['level'] > $useraccount['level']) {
$qa_content['form_profile']['fields']['level']['value'] .= '<br/>' .
strtr(qa_lang_html('users/level_for_category'), array(
'^1' => qa_html(qa_user_level_string($userlevel['level'])),
'^2' => '<a href="' . qa_path_html(implode('/', array_reverse(explode('/', $userlevel['backpath'])))) . '">' . qa_html($userlevel['title']) . '</a>',
));
}
}
}
// Show any extra privileges due to user's level or their points
$showpermits = array();
$permitoptions = qa_get_permit_options();
foreach ($permitoptions as $permitoption) {
// if not available to approved and email confirmed users with no points, but yes available to the user, it's something special
if (qa_permit_error($permitoption, $userid, QA_USER_LEVEL_APPROVED, QA_USER_FLAGS_EMAIL_CONFIRMED, 0) &&
!qa_permit_error($permitoption, $userid, $useraccount['level'], $useraccount['flags'], $userpoints['points'])
) {
if ($permitoption == 'permit_retag_cat')
$showpermits[] = qa_lang(qa_using_categories() ? 'profile/permit_recat' : 'profile/permit_retag');
else
$showpermits[] = qa_lang('profile/' . $permitoption); // then show it as an extra priviliege
}
}
if (count($showpermits)) {
$qa_content['form_profile']['fields']['permits'] = array(
'type' => 'static',
'label' => qa_lang_html('profile/extra_privileges'),
'value' => qa_html(implode("\n", $showpermits), true),
'rows' => count($showpermits),
'id' => 'permits',
);
}
// Show email address only if we're an administrator
if ($loginlevel >= QA_USER_LEVEL_ADMIN && !qa_user_permit_error()) {
$doconfirms = qa_opt('confirm_user_emails') && $useraccount['level'] < QA_USER_LEVEL_EXPERT;
$isconfirmed = ($useraccount['flags'] & QA_USER_FLAGS_EMAIL_CONFIRMED) > 0;
$htmlemail = qa_html(isset($inemail) ? $inemail : $useraccount['email']);
$qa_content['form_profile']['fields']['email'] = array(
'type' => $userediting ? 'text' : 'static',
'label' => qa_lang_html('users/email_label'),
'tags' => 'name="email"',
'value' => $userediting ? $htmlemail : ('<a href="mailto:' . $htmlemail . '">' . $htmlemail . '</a>'),
'error' => qa_html(@$errors['email']),
'note' => ($doconfirms ? (qa_lang_html($isconfirmed ? 'users/email_confirmed' : 'users/email_not_confirmed') . ' ') : '') .
($userediting ? '' : qa_lang_html('users/only_shown_admins')),
'id' => 'email',
);
}
// Show IP addresses and times for last login or write - only if we're a moderator or higher
if ($loginlevel >= QA_USER_LEVEL_MODERATOR && !qa_user_permit_error()) {
$qa_content['form_profile']['fields']['lastlogin'] = array(
'type' => 'static',
'label' => qa_lang_html('users/last_login_label'),
'value' =>
strtr(qa_lang_html('users/x_ago_from_y'), array(
'^1' => qa_time_to_string(qa_opt('db_time') - $useraccount['loggedin']),
'^2' => qa_ip_anchor_html(@inet_ntop($useraccount['loginip'])),
)),
'note' => $userediting ? null : qa_lang_html('users/only_shown_moderators'),
'id' => 'lastlogin',
);
if (isset($useraccount['written'])) {
$qa_content['form_profile']['fields']['lastwrite'] = array(
'type' => 'static',
'label' => qa_lang_html('users/last_write_label'),
'value' =>
strtr(qa_lang_html('users/x_ago_from_y'), array(
'^1' => qa_time_to_string(qa_opt('db_time') - $useraccount['written']),
'^2' => qa_ip_anchor_html(@inet_ntop($useraccount['writeip'])),
)),
'note' => $userediting ? null : qa_lang_html('users/only_shown_moderators'),
'id' => 'lastwrite',
);
} else {
unset($qa_content['form_profile']['fields']['lastwrite']);
}
}
// Show other profile fields
$fieldsediting = $fieldseditable && $userediting;
foreach ($userfields as $userfield) {
if (($userfield['flags'] & QA_FIELD_FLAGS_LINK_URL) && !$fieldsediting) {
$valuehtml = qa_url_to_html_link(@$userprofile[$userfield['title']], qa_opt('links_in_new_window'));
} else {
$value = @$inprofile[$userfield['fieldid']];
if (!isset($value))
$value = @$userprofile[$userfield['title']];
$valuehtml = qa_html($value, (($userfield['flags'] & QA_FIELD_FLAGS_MULTI_LINE) && !$fieldsediting));
}
$label = trim(qa_user_userfield_label($userfield), ':');
if (strlen($label))
$label .= ':';
$notehtml = null;
if (isset($userfield['permit']) && !$userediting) {
if ($userfield['permit'] <= QA_PERMIT_ADMINS)
$notehtml = qa_lang_html('users/only_shown_admins');
elseif ($userfield['permit'] <= QA_PERMIT_MODERATORS)
$notehtml = qa_lang_html('users/only_shown_moderators');
elseif ($userfield['permit'] <= QA_PERMIT_EDITORS)
$notehtml = qa_lang_html('users/only_shown_editors');
elseif ($userfield['permit'] <= QA_PERMIT_EXPERTS)
$notehtml = qa_lang_html('users/only_shown_experts');
}
$qa_content['form_profile']['fields'][$userfield['title']] = array(
'type' => $fieldsediting ? 'text' : 'static',
'label' => qa_html($label),
'tags' => 'name="field_' . $userfield['fieldid'] . '"',
'value' => $valuehtml,
'error' => qa_html(@$errors[$userfield['fieldid']]),
'note' => $notehtml,
'rows' => ($userfield['flags'] & QA_FIELD_FLAGS_MULTI_LINE) ? 8 : null,
'id' => 'userfield-' . $userfield['fieldid'],
);
}
// Edit form or button, if appropriate
if ($userediting) {
if ((qa_opt('avatar_allow_gravatar') && ($useraccount['flags'] & QA_USER_FLAGS_SHOW_GRAVATAR)) ||
(qa_opt('avatar_allow_upload') && ($useraccount['flags'] & QA_USER_FLAGS_SHOW_AVATAR) && isset($useraccount['avatarblobid']))
) {
$qa_content['form_profile']['fields']['removeavatar'] = array(
'type' => 'checkbox',
'label' => qa_lang_html('users/remove_avatar'),
'tags' => 'name="removeavatar"',
);
}
$qa_content['form_profile']['buttons'] = array(
'save' => array(
'tags' => 'onclick="qa_show_waiting_after(this, false);"',
'label' => qa_lang_html('users/save_user'),
),
'cancel' => array(
'tags' => 'name="docancel"',
'label' => qa_lang_html('main/cancel_button'),
),
);
$qa_content['form_profile']['hidden'] = array(
'dosave' => '1',
'code' => qa_get_form_security_code('user-edit-' . $handle),
);
} elseif ($usereditbutton) {
$qa_content['form_profile']['buttons'] = array();
if ($approvebutton) {
$qa_content['form_profile']['buttons']['approve'] = array(
'tags' => 'name="doapprove"',
'label' => qa_lang_html('users/approve_user_button'),
);
}
$qa_content['form_profile']['buttons']['edit'] = array(
'tags' => 'name="doedit"',
'label' => qa_lang_html('users/edit_user_button'),
);
if (isset($maxlevelassign) && $useraccount['level'] < QA_USER_LEVEL_MODERATOR) {
if ($useraccount['flags'] & QA_USER_FLAGS_USER_BLOCKED) {
$qa_content['form_profile']['buttons']['unblock'] = array(
'tags' => 'name="dounblock"',
'label' => qa_lang_html('users/unblock_user_button'),
);
if (!qa_user_permit_error('permit_hide_show')) {
$qa_content['form_profile']['buttons']['hideall'] = array(
'tags' => 'name="dohideall" onclick="qa_show_waiting_after(this, false);"',
'label' => qa_lang_html('users/hide_all_user_button'),
);
}
if ($loginlevel >= QA_USER_LEVEL_ADMIN) {
$qa_content['form_profile']['buttons']['delete'] = array(
'tags' => 'name="dodelete" onclick="qa_show_waiting_after(this, false);"',
'label' => qa_lang_html('users/delete_user_button'),
);
}
} else {
$qa_content['form_profile']['buttons']['block'] = array(
'tags' => 'name="doblock"',
'label' => qa_lang_html('users/block_user_button'),
);
}
$qa_content['form_profile']['hidden'] = array(
'code' => qa_get_form_security_code('user-' . $handle),
);
}
} elseif (isset($loginuserid) && ($loginuserid == $userid)) {
$qa_content['form_profile']['buttons'] = array(
'account' => array(
'tags' => 'name="doaccount"',
'label' => qa_lang_html('users/edit_profile'),
),
);
}
if (!is_array($qa_content['form_profile']['fields']['removeavatar']))
unset($qa_content['form_profile']['fields']['removeavatar']);
$qa_content['raw']['account'] = $useraccount; // for plugin layers to access
$qa_content['raw']['profile'] = $userprofile;
}
// Information about user activity, available also with single sign-on integration
$qa_content['form_activity'] = array(
'title' => '<span id="activity">' . qa_lang_html_sub('profile/activity_by_x', $userhtml) . '</span>',
'style' => 'wide',
'fields' => array(
'bonus' => array(
'label' => qa_lang_html('profile/bonus_points'),
'tags' => 'name="bonus"',
'value' => qa_html(isset($inbonus) ? $inbonus : $userpoints['bonus']),
'type' => 'number',
'note' => qa_lang_html('users/only_shown_admins'),
'id' => 'bonus',
),
'points' => array(
'type' => 'static',
'label' => qa_lang_html('profile/score'),
'value' => (@$userpoints['points'] == 1)
? qa_lang_html_sub('main/1_point', '<span class="qa-uf-user-points">1</span>', '1')
: qa_lang_html_sub('main/x_points', '<span class="qa-uf-user-points">' . qa_html(qa_format_number(@$userpoints['points'])) . '</span>'),
'id' => 'points',
),
'title' => array(
'type' => 'static',
'label' => qa_lang_html('profile/title'),
'value' => qa_get_points_title_html(@$userpoints['points'], qa_get_points_to_titles()),
'id' => 'title',
),
'questions' => array(
'type' => 'static',
'label' => qa_lang_html('profile/questions'),
'value' => '<span class="qa-uf-user-q-posts">' . qa_html(qa_format_number(@$userpoints['qposts'])) . '</span>',
'id' => 'questions',
),
'answers' => array(
'type' => 'static',
'label' => qa_lang_html('profile/answers'),
'value' => '<span class="qa-uf-user-a-posts">' . qa_html(qa_format_number(@$userpoints['aposts'])) . '</span>',
'id' => 'answers',
),
),
);
if ($loginlevel >= QA_USER_LEVEL_ADMIN) {
$qa_content['form_activity']['tags'] = 'method="post" action="' . qa_self_html() . '"';
$qa_content['form_activity']['buttons'] = array(
'setbonus' => array(
'tags' => 'name="dosetbonus"',
'label' => qa_lang_html('profile/set_bonus_button'),
),
);
$qa_content['form_activity']['hidden'] = array(
'code' => qa_get_form_security_code('user-activity-' . $handle),
);
} else {
unset($qa_content['form_activity']['fields']['bonus']);
}
if (!isset($qa_content['form_activity']['fields']['title']['value']))
unset($qa_content['form_activity']['fields']['title']);
if (qa_opt('comment_on_qs') || qa_opt('comment_on_as')) { // only show comment count if comments are enabled
$qa_content['form_activity']['fields']['comments'] = array(
'type' => 'static',
'label' => qa_lang_html('profile/comments'),
'value' => '<span class="qa-uf-user-c-posts">' . qa_html(qa_format_number(@$userpoints['cposts'])) . '</span>',
'id' => 'comments',
);
}
if (qa_opt('voting_on_qs') || qa_opt('voting_on_as')) { // only show vote record if voting is enabled
$votedonvalue = '';
if (qa_opt('voting_on_qs')) {
$qvotes = @$userpoints['qupvotes'] + @$userpoints['qdownvotes'];
$innervalue = '<span class="qa-uf-user-q-votes">' . qa_format_number($qvotes) . '</span>';
$votedonvalue .= ($qvotes == 1) ? qa_lang_html_sub('main/1_question', $innervalue, '1')
: qa_lang_html_sub('main/x_questions', $innervalue);
if (qa_opt('voting_on_as'))
$votedonvalue .= ', ';
}
if (qa_opt('voting_on_as')) {
$avotes = @$userpoints['aupvotes'] + @$userpoints['adownvotes'];
$innervalue = '<span class="qa-uf-user-a-votes">' . qa_format_number($avotes) . '</span>';
$votedonvalue .= ($avotes == 1) ? qa_lang_html_sub('main/1_answer', $innervalue, '1')
: qa_lang_html_sub('main/x_answers', $innervalue);
}
$qa_content['form_activity']['fields']['votedon'] = array(
'type' => 'static',
'label' => qa_lang_html('profile/voted_on'),
'value' => $votedonvalue,
'id' => 'votedon',
);
$upvotes = @$userpoints['qupvotes'] + @$userpoints['aupvotes'];
$innervalue = '<span class="qa-uf-user-upvotes">' . qa_format_number($upvotes) . '</span>';
$votegavevalue = (($upvotes == 1) ? qa_lang_html_sub('profile/1_up_vote', $innervalue, '1') : qa_lang_html_sub('profile/x_up_votes', $innervalue)) . ', ';
$downvotes = @$userpoints['qdownvotes'] + @$userpoints['adownvotes'];
$innervalue = '<span class="qa-uf-user-downvotes">' . qa_format_number($downvotes) . '</span>';
$votegavevalue .= ($downvotes == 1) ? qa_lang_html_sub('profile/1_down_vote', $innervalue, '1') : qa_lang_html_sub('profile/x_down_votes', $innervalue);
$qa_content['form_activity']['fields']['votegave'] = array(
'type' => 'static',
'label' => qa_lang_html('profile/gave_out'),
'value' => $votegavevalue,
'id' => 'votegave',
);
$innervalue = '<span class="qa-uf-user-upvoteds">' . qa_format_number(@$userpoints['upvoteds']) . '</span>';
$votegotvalue = ((@$userpoints['upvoteds'] == 1) ? qa_lang_html_sub('profile/1_up_vote', $innervalue, '1')
: qa_lang_html_sub('profile/x_up_votes', $innervalue)) . ', ';
$innervalue = '<span class="qa-uf-user-downvoteds">' . qa_format_number(@$userpoints['downvoteds']) . '</span>';
$votegotvalue .= (@$userpoints['downvoteds'] == 1) ? qa_lang_html_sub('profile/1_down_vote', $innervalue, '1')
: qa_lang_html_sub('profile/x_down_votes', $innervalue);
$qa_content['form_activity']['fields']['votegot'] = array(
'type' => 'static',
'label' => qa_lang_html('profile/received'),
'value' => $votegotvalue,
'id' => 'votegot',
);
}
if (@$userpoints['points']) {
$qa_content['form_activity']['fields']['points']['value'] .=
qa_lang_html_sub('profile/ranked_x', '<span class="qa-uf-user-rank">' . qa_format_number($userrank) . '</span>');
}
if (@$userpoints['aselects']) {
$qa_content['form_activity']['fields']['questions']['value'] .= ($userpoints['aselects'] == 1)
? qa_lang_html_sub('profile/1_with_best_chosen', '<span class="qa-uf-user-q-selects">1</span>', '1')
: qa_lang_html_sub('profile/x_with_best_chosen', '<span class="qa-uf-user-q-selects">' . qa_format_number($userpoints['aselects']) . '</span>');
}
if (@$userpoints['aselecteds']) {
$qa_content['form_activity']['fields']['answers']['value'] .= ($userpoints['aselecteds'] == 1)
? qa_lang_html_sub('profile/1_chosen_as_best', '<span class="qa-uf-user-a-selecteds">1</span>', '1')
: qa_lang_html_sub('profile/x_chosen_as_best', '<span class="qa-uf-user-a-selecteds">' . qa_format_number($userpoints['aselecteds']) . '</span>');
}
// For plugin layers to access
$qa_content['raw']['userid'] = $userid;
$qa_content['raw']['points'] = $userpoints;
$qa_content['raw']['rank'] = $userrank;
// Wall posts
if (!QA_FINAL_EXTERNAL_USERS && qa_opt('allow_user_walls')) {
$qa_content['message_list'] = array(
'title' => '<span id="wall">' . qa_lang_html_sub('profile/wall_for_x', $userhtml) . '</span>',
'tags' => 'id="wallmessages"',
'form' => array(
'tags' => 'name="wallpost" method="post" action="' . qa_self_html() . '#wall"',
'style' => 'tall',
'hidden' => array(
'qa_click' => '', // for simulating clicks in Javascript
'handle' => qa_html($useraccount['handle']),
'start' => 0,
'code' => qa_get_form_security_code('wall-' . $useraccount['handle']),
),
),
'messages' => array(),
);
if ($wallposterrorhtml) {
$qa_content['message_list']['error'] = $wallposterrorhtml; // an error that means we are not allowed to post
} else {
$qa_content['message_list']['form']['fields'] = array(
'message' => array(
'tags' => 'name="message" id="message"',
'value' => qa_html(@$inmessage, false),
'rows' => 2,
'error' => qa_html(@$errors['message']),
),
);
$qa_content['message_list']['form']['buttons'] = array(
'post' => array(
'tags' => 'name="dowallpost" onclick="return qa_submit_wall_post(this, true);"',
'label' => qa_lang_html('profile/post_wall_button'),
),
);
}
foreach ($usermessages as $message)
$qa_content['message_list']['messages'][] = qa_wall_post_view($message);
if ($useraccount['wallposts'] > count($usermessages))
$qa_content['message_list']['messages'][] = qa_wall_view_more_link($handle, count($usermessages));
}
// Sub menu for navigation in user pages
$ismyuser = isset($loginuserid) && $loginuserid == (QA_FINAL_EXTERNAL_USERS ? $userid : $useraccount['userid']);
$qa_content['navigation']['sub'] = qa_user_sub_navigation($handle, 'profile', $ismyuser);
return $qa_content;
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Controllers\User;
use Q2A\Auth\NoPermissionException;
use Q2A\Middleware\Auth\InternalUsersOnly;
use Q2A\Middleware\Auth\MinimumUserLevel;
require_once QA_INCLUDE_DIR . 'db/users.php';
require_once QA_INCLUDE_DIR . 'db/selects.php';
require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR . 'app/format.php';
class UsersList extends \Q2A\Controllers\BaseController
{
public function __construct()
{
parent::__construct();
$this->addMiddleware(new InternalUsersOnly(), array('newest', 'special', 'blocked'));
$this->addMiddleware(new MinimumUserLevel(QA_USER_LEVEL_MODERATOR), array('blocked'));
}
/**
* Display top users page (ordered by points)
* @return array $qa_content
*/
public function top()
{
// callables to fetch user data
$fetchUsers = function($start, $pageSize) {
return array(
qa_opt('cache_userpointscount'),
qa_db_select_with_pending(qa_db_top_users_selectspec($start, $pageSize))
);
};
$userScore = function($user) {
return qa_html(qa_format_number($user['points'], 0, true));
};
$qa_content = $this->rankedUsersContent($fetchUsers, $userScore);
$qa_content['title'] = empty($qa_content['ranking']['items'])
? qa_lang_html('main/no_active_users')
: qa_lang_html('main/highest_users');
$qa_content['ranking']['sort'] = 'points';
return $qa_content;
}
/**
* Display newest users page
*
* @return array $qa_content
* @throws NoPermissionException
*/
public function newest()
{
// check we have permission to view this page (moderator or above)
if (qa_user_permit_error('permit_view_new_users_page')) {
throw new NoPermissionException();
}
// callables to fetch user data
$fetchUsers = function($start, $pageSize) {
return array(
qa_opt('cache_userpointscount'),
qa_db_select_with_pending(qa_db_newest_users_selectspec($start, $pageSize))
);
};
$userDate = function($user) {
$when = qa_when_to_html($user['created'], 7);
return $when['data'];
};
$qa_content = $this->rankedUsersContent($fetchUsers, $userDate);
$qa_content['title'] = empty($qa_content['ranking']['items'])
? qa_lang_html('main/no_active_users')
: qa_lang_html('main/newest_users');
$qa_content['ranking']['sort'] = 'date';
return $qa_content;
}
/**
* Display special users page (admins, moderators, etc)
*
* @return array $qa_content
* @throws NoPermissionException
*/
public function special()
{
// check we have permission to view this page (moderator or above)
if (qa_user_permit_error('permit_view_special_users_page')) {
throw new NoPermissionException();
}
// callables to fetch user data
$fetchUsers = function($start, $pageSize) {
// here we fetch *all* users to get the total instead of a separate query; there are unlikely to be many special users
$users = qa_db_select_with_pending(qa_db_users_from_level_selectspec(QA_USER_LEVEL_EXPERT));
return array(count($users), $users);
};
$userLevel = function($user) {
return qa_html(qa_user_level_string($user['level']));
};
$qa_content = $this->rankedUsersContent($fetchUsers, $userLevel);
$qa_content['title'] = qa_lang_html('users/special_users');
$qa_content['ranking']['sort'] = 'level';
return $qa_content;
}
/**
* Display blocked users page
* @return array $qa_content
*/
public function blocked()
{
// callables to fetch user data
$fetchUsers = function($start, $pageSize) {
list($totalUsers, $users) = qa_db_select_with_pending(
qa_db_selectspec_count(qa_db_users_with_flag_selectspec(QA_USER_FLAGS_USER_BLOCKED)),
qa_db_users_with_flag_selectspec(QA_USER_FLAGS_USER_BLOCKED, $start, $pageSize)
);
return array($totalUsers['count'], $users);
};
$userLevel = function($user) {
return qa_html(qa_user_level_string($user['level']));
};
$qa_content = $this->rankedUsersContent($fetchUsers, $userLevel);
$qa_content['title'] = empty($qa_content['ranking']['items'])
? qa_lang_html('users/no_blocked_users')
: qa_lang_html('users/blocked_users');
$qa_content['ranking']['sort'] = 'level';
return $qa_content;
}
/**
* Fetch $qa_content array for a set of ranked users.
* @param callable $fnUsersAndCount Function that returns the list of users for a page and the user total.
* @param callable $fnUserScore Function that returns the "score" (points, date, etc) that will be displayed.
* @return array $qa_content
*/
private function rankedUsersContent($fnUsersAndCount, $fnUserScore)
{
// get the users to display on this page
$request = qa_request();
$start = qa_get_start();
$pageSize = qa_opt('page_size_users');
list($totalUsers, $users) = $fnUsersAndCount($start, $pageSize);
// get userids and handles of retrieved users
$usersHtml = qa_userids_handles_html($users);
// prepare content for theme
$content = qa_content_prepare();
$content['ranking'] = array(
'items' => array(),
'rows' => ceil($pageSize / qa_opt('columns_users')),
'type' => 'users',
// 'sort' => '',
);
foreach ($users as $user) {
if (QA_FINAL_EXTERNAL_USERS) {
$avatarHtml = qa_get_external_avatar_html($user['userid'], qa_opt('avatar_users_size'), true);
} else {
$avatarHtml = qa_get_user_avatar_html($user['flags'], $user['email'], $user['handle'],
$user['avatarblobid'], $user['avatarwidth'], $user['avatarheight'], qa_opt('avatar_users_size'), true);
}
$content['ranking']['items'][] = array(
'avatar' => $avatarHtml,
'label' => $usersHtml[$user['userid']],
'score' => $fnUserScore($user),
'raw' => $user,
);
}
$content['page_links'] = qa_html_page_links($request, $start, $pageSize, $totalUsers, qa_opt('pages_prev_next'));
// set the canonical url based on possible pagination
$params = $start > 0 ? array('start' => $start) : null;
$content['canonical'] = qa_path_html($request, $params, qa_opt('site_url'));
$content['navigation']['sub'] = qa_users_sub_navigation();
return $content;
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Database;
use PDO;
use PDOException;
use PDOStatement;
use Q2A_Storage_CacheFactory;
class DbConnection
{
/** @var PDO */
protected $pdo;
/** @var array */
protected $config;
/** @var bool */
protected $allowConnect = false;
/** @var string */
protected $failHandler;
/** @var int */
protected $updateCountsSuspended = 0;
public function __construct()
{
$this->config = array(
'driver' => 'mysql',
'host' => QA_FINAL_MYSQL_HOSTNAME,
'username' => QA_FINAL_MYSQL_USERNAME,
'password' => QA_FINAL_MYSQL_PASSWORD,
'database' => QA_FINAL_MYSQL_DATABASE,
);
if (defined('QA_FINAL_WORDPRESS_INTEGRATE_PATH')) {
// Wordpress allows setting port inside DB_HOST constant, like 127.0.0.1:3306
$hostAndPort = explode(':', $this->config['host']);
if (count($hostAndPort) >= 2) {
$this->config['host'] = $hostAndPort[0];
$this->config['port'] = $hostAndPort[1];
}
} elseif (defined('QA_FINAL_MYSQL_PORT')) {
$this->config['port'] = QA_FINAL_MYSQL_PORT;
}
}
/**
* Obtain the raw PDO object.
* @return PDO
*/
public function getPDO()
{
return $this->pdo;
}
/**
* Indicates to the Q2A database layer that database connections are permitted from this point forwards (before
* this point, some plugins may not have had a chance to override some database access functions).
*/
public function allowConnect()
{
$this->allowConnect = true;
}
/**
* Connect to the Q2A database, optionally install the $failHandler (and call it if necessary). Uses PDO as of Q2A
* 1.9.
* @param string $failHandler
* @return mixed|void
*/
public function connect($failHandler = null)
{
if (!$this->allowConnect) {
qa_fatal_error('It appears that a plugin is trying to access the database, but this is not allowed until Q2A initialization is complete.');
return;
}
if ($failHandler !== null) {
// set this even if connection already opened
$this->failHandler = $failHandler;
}
if ($this->pdo) {
return;
}
$dsn = sprintf('%s:host=%s;dbname=%s;charset=utf8', $this->config['driver'], $this->config['host'], $this->config['database']);
if (isset($this->config['port'])) {
$dsn .= ';port=' . $this->config['port'];
}
$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
if (QA_PERSISTENT_CONN_DB) {
$options[PDO::ATTR_PERSISTENT] = true;
}
try {
$this->pdo = new PDO($dsn, $this->config['username'], $this->config['password'], $options);
} catch (PDOException $ex) {
$this->failError('connect', $ex->getCode(), $ex->getMessage());
}
qa_report_process_stage('db_connected');
}
/**
* Disconnect from the Q2A database.
* @return void
*/
public function disconnect()
{
$this->pdo = null;
}
/**
* If a DB error occurs, call the installed fail handler (if any) otherwise report error and exit immediately.
* @param string $type
* @param int $errno
* @param string $error
* @param string $query
* @return mixed
*/
public function failError($type, $errno = null, $error = null, $query = null)
{
@error_log('PHP Question2Answer MySQL ' . $type . ' error ' . $errno . ': ' . $error . (isset($query) ? (' - Query: ' . $query) : ''));
if (function_exists($this->failHandler)) {
$this->failHandler($type, $errno, $error, $query);
} else {
echo sprintf(
'<hr><div style="color: red">Database %s<p>%s</p><code>%s</code></div>',
htmlspecialchars($type . ' error ' . $errno), nl2br(htmlspecialchars($error)), nl2br(htmlspecialchars($query))
);
qa_exit('error');
}
}
/**
* Prepare and execute a SQL query, handling any failures. In debugging mode, track the queries and resources used.
* @param string $query
* @param array $params
* @return DbResult
*/
public function query($query, $params = array())
{
$query = $this->applyTableSub($query);
try {
if (QA_DEBUG_PERFORMANCE) {
global $qa_usage;
// time the query
$oldtime = array_sum(explode(' ', microtime()));
$stmt = $this->execute($query, $params);
$usedtime = array_sum(explode(' ', microtime())) - $oldtime;
$qa_usage->logDatabaseQuery($query, $usedtime, $stmt->rowCount(), $stmt->columnCount());
} else {
$stmt = $this->execute($query, $params);
}
return new DbResult($stmt);
} catch (PDOException $ex) {
$this->failError('query', $ex->getCode(), $ex->getMessage(), $query);
}
}
/**
* Lower-level function to prepare and execute a SQL query. Automatically retries if there is a MySQL deadlock
* error.
* @param string $query
* @param array $params
* @return PDOStatement
*/
protected function execute($query, $params = array())
{
$stmt = $this->pdo->prepare($query);
for ($attempt = 0; $attempt < 100; $attempt++) {
$success = $stmt->execute($params);
if ($success === true || $stmt->errorCode() !== '1213') {
break;
}
// deal with InnoDB deadlock errors by waiting 0.01s then retrying
usleep(10000);
}
return $stmt;
}
/*
The selectspec array can contain the elements below. See db/selects.php for lots of examples.
By default, singleSelect() and multiSelect() return the data for each selectspec as a numbered
array of arrays, one per row. The array for each row has column names in the keys, and data in the values.
But this can be changed using the 'arraykey', 'arrayvalue' and 'single' in the selectspec.
Note that even if you specify ORDER BY in 'source', the final results may not be ordered. This is because
the SELECT could be done within a UNION that (annoyingly) doesn't maintain order. Use 'sortasc' or 'sortdesc'
to fix this. You can however rely on the combination of ORDER BY and LIMIT retrieving the appropriate records.
'columns' => Array of names of columns to be retrieved (required)
If a value in the columns array has an integer key, it is retrieved AS itself (in a SQL sense).
If a value in the columns array has a non-integer key, it is retrieved AS that key.
Values in the columns array can include table specifiers before the period.
'source' => Any SQL after FROM, including table names, JOINs, GROUP BY, ORDER BY, WHERE, etc... (required)
'arguments' => Substitutions in order for $s and #s in the query, applied in qa_db_apply_sub() above (required)
'arraykey' => Name of column to use for keys of the outer-level returned array, instead of numbers by default
'arrayvalue' => Name of column to use for values of outer-level returned array, instead of arrays by default
'single' => If true, return the array for a single row and don't embed it within an outer-level array
'sortasc' => Sort the output ascending by this column
'sortdesc' => Sort the output descending by this column
Why does multiSelect() combine usually unrelated SELECT statements into a single query?
Because if the database and web servers are on different computers, there will be latency.
This way we ensure that every read pageview on the site requires as few DB queries as possible, so
that we pay for this latency only one time.
For writes we worry less, since the user is more likely to be expecting a delay.
If QA_OPTIMIZE_DISTANT_DB is set to false in qa-config.php, we assume zero latency and go back to
simple queries, since this will allow both MySQL and PHP to provide quicker results.
*/
/**
* Return the data specified by a single $selectspec - see long comment above.
* @param $selectspec
* @return array|mixed
*/
public function singleSelect($selectspec)
{
// check for cached results
if (isset($selectspec['caching'])) {
$cacheDriver = Q2A_Storage_CacheFactory::getCacheDriver();
$cacheKey = 'q2a.query:' . $selectspec['caching']['key'];
if ($cacheDriver->isEnabled()) {
$queryData = $cacheDriver->get($cacheKey);
if ($queryData !== null)
return $queryData;
}
}
$query = 'SELECT ';
foreach ($selectspec['columns'] as $columnas => $columnfrom) {
$query .= $columnfrom . (is_int($columnas) ? '' : (' AS ' . $columnas)) . ', ';
}
$query = substr($query, 0, -2);
if (isset($selectspec['source']) && strlen($selectspec['source']) > 0) {
$query .= ' FROM ' . $selectspec['source'];
}
$params = isset($selectspec['arguments']) ? $selectspec['arguments'] : array();
$arraykey = isset($selectspec['arraykey']) ? $selectspec['arraykey'] : null;
$result = $this->query($query, $params);
$data = $result->fetchAllAssoc($arraykey); // arrayvalue is applied in qa_db_post_select()
$this->formatSelect($data, $selectspec); // post-processing
// save cached results
if (isset($selectspec['caching'])) {
if ($cacheDriver->isEnabled()) {
$cacheDriver->set($cacheKey, $data, $selectspec['caching']['ttl']);
}
}
return $data;
}
/**
* Return the data specified by each element of $selectspecs, where the keys of the
* returned array match the keys of the supplied $selectspecs array. See long comment above.
* @param array $selectspecs
* @return array
*/
public function multiSelect($selectspecs)
{
// Perform simple queries if the database is local or there are only 0-1 selectspecs
if (!QA_OPTIMIZE_DISTANT_DB || count($selectspecs) <= 1) {
$outresults = array();
foreach ($selectspecs as $selectkey => $selectspec)
$outresults[$selectkey] = $this->singleSelect($selectspec);
return $outresults;
}
// Otherwise, parse columns for each spec to deal with columns without an 'AS' specification
foreach ($selectspecs as $selectkey => $selectspec) {
$selectspecs[$selectkey]['outcolumns'] = array();
$selectspecs[$selectkey]['autocolumn'] = array();
foreach ($selectspec['columns'] as $columnas => $columnfrom) {
if (is_int($columnas)) {
// fetch column name if not already provided in the array key
$periodpos = strpos($columnfrom, '.');
$columnas = is_numeric($periodpos) ? substr($columnfrom, $periodpos + 1) : $columnfrom;
$selectspecs[$selectkey]['autocolumn'][$columnas] = true;
}
if (isset($selectspecs[$selectkey]['outcolumns'][$columnas])) {
qa_fatal_error('Duplicate column name in DbConnection::multiSelect()');
}
$selectspecs[$selectkey]['outcolumns'][$columnas] = $columnfrom;
}
if (isset($selectspec['arraykey']) && !isset($selectspecs[$selectkey]['outcolumns'][$selectspec['arraykey']])) {
qa_fatal_error('Used arraykey not in columns in DbConnection::multiSelect()');
}
if (isset($selectspec['arrayvalue']) && !isset($selectspecs[$selectkey]['outcolumns'][$selectspec['arrayvalue']])) {
qa_fatal_error('Used arrayvalue not in columns in DbConnection::multiSelect()');
}
}
// Work out the full list of columns used
$outcolumns = array();
foreach ($selectspecs as $selectspec) {
$outcolumns = array_unique(array_merge($outcolumns, array_keys($selectspec['outcolumns'])));
}
// Build the query based on this full list
$query = '';
$queryParams = array();
foreach ($selectspecs as $selectkey => $selectspec) {
$subquery = "(SELECT " . $this->pdo->quote($selectkey) . ' AS selectkey';
foreach ($outcolumns as $columnas) {
$subquery .= ', ' . (isset($selectspec['outcolumns'][$columnas]) ? $selectspec['outcolumns'][$columnas] : 'NULL');
if (empty($query) && !isset($selectspec['autocolumn'][$columnas])) {
$subquery .= ' AS ' . $columnas;
}
}
if (isset($selectspec['source']) && strlen($selectspec['source']) > 0) {
$subquery .= ' FROM ' . $selectspec['source'];
}
$subquery .= ')';
if (strlen($query)) {
$query .= ' UNION ALL ';
}
$query .= $subquery;
if (isset($selectspec['arguments'])) {
$queryParams = array_merge($queryParams, array_values($selectspec['arguments']));
}
}
// Perform query and extract results
$stmt = $this->query($query, $queryParams);
$rawresults = $stmt->fetchAllAssoc();
$outresults = array();
foreach ($selectspecs as $selectkey => $selectspec)
$outresults[$selectkey] = array();
foreach ($rawresults as $rawresult) {
$selectkey = $rawresult['selectkey'];
$selectspec = $selectspecs[$selectkey];
$keepresult = array();
foreach ($selectspec['outcolumns'] as $columnas => $columnfrom)
$keepresult[$columnas] = $rawresult[$columnas];
if (isset($selectspec['arraykey']))
$outresults[$selectkey][$keepresult[$selectspec['arraykey']]] = $keepresult;
else
$outresults[$selectkey][] = $keepresult;
}
// Post-processing to apply various stuff include sorting request, since we can't rely on ORDER BY due to UNION
foreach ($selectspecs as $selectkey => $selectspec) {
$this->formatSelect($outresults[$selectkey], $selectspec);
}
return $outresults;
}
/**
* Post-process $outresult according to $selectspec, applying 'sortasc', 'sortdesc', 'arrayvalue' and 'single'.
* @param array $outresult
* @param array $selectspec
*/
private function formatSelect(&$outresult, $selectspec)
{
// PHP's sorting algorithm is not 'stable', so we use '_order_' element to keep stability.
// By contrast, MySQL's ORDER BY does seem to give the results in a reliable order.
if (isset($selectspec['sortasc'])) {
require_once QA_INCLUDE_DIR . 'util/sort.php';
$index = 0;
foreach ($outresult as $key => $value) {
$outresult[$key]['_order_'] = $index++;
}
qa_sort_by($outresult, $selectspec['sortasc'], '_order_');
} elseif (isset($selectspec['sortdesc'])) {
require_once QA_INCLUDE_DIR . 'util/sort.php';
if (isset($selectspec['sortdesc_2'])) {
qa_sort_by($outresult, $selectspec['sortdesc'], $selectspec['sortdesc_2']);
} else {
$index = count($outresult);
foreach ($outresult as $key => $value)
$outresult[$key]['_order_'] = $index--;
qa_sort_by($outresult, $selectspec['sortdesc'], '_order_');
}
$outresult = array_reverse($outresult, true);
}
if (isset($selectspec['arrayvalue'])) {
foreach ($outresult as $key => $value) {
$outresult[$key] = $value[$selectspec['arrayvalue']];
}
}
if (@$selectspec['single']) {
$outresult = count($outresult) ? reset($outresult) : null;
}
}
/**
* Substitute ^ in a SQL query with the configured table prefix.
* @param string $query
* @return string
*/
public function applyTableSub($query)
{
return preg_replace_callback('/\^([A-Za-z_0-9]+)/', function($matches) {
return $this->addTablePrefix($matches[1]);
}, $query);
}
/**
* Return the full name (with prefix) of a database table identifier.
* @param string $rawName
* @return string
*/
public function addTablePrefix($rawName)
{
$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
case 'userprofile_ibfk_1':
$prefix = QA_MYSQL_USERS_PREFIX;
break;
}
}
return $prefix . $rawName;
}
/**
* Return the value of the auto-increment column for the last inserted row.
* @return string
*/
public function lastInsertId()
{
return $this->pdo->lastInsertId();
}
/**
* Suspend or reinstate the updating of counts (of many different types) in the database, to save time when making
* a lot of changes. A counter is kept to allow multiple calls.
* @param bool $suspend
*/
public function suspendUpdateCounts($suspend = true)
{
$this->updateCountsSuspended += ($suspend ? 1 : -1);
}
/**
* Returns whether counts should currently be updated (i.e. if count updating has not been suspended).
* @return bool
*/
public function shouldUpdateCounts()
{
return $this->updateCountsSuspended <= 0;
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Database;
use PDO;
use PDOStatement;
use Q2A\Database\Exceptions\ReadingFromEmptyResultException;
class DbResult
{
private $stmt;
public function __construct(PDOStatement $stmt)
{
$this->stmt = $stmt;
}
/**
* Return the first row from the results as an array of [column name] => [column value].
* @return array|null
*/
public function fetchNextAssoc()
{
$data = $this->stmt->fetch();
return $data === false ? null : $data;
}
/**
* Return the first row from the results as an array of [column name] => [column value]. If no value is fetched
* from the database then an exception is thrown.
* @return array|null
* @throws ReadingFromEmptyResultException
*/
public function fetchNextAssocOrFail()
{
return $this->getResultOrFail($this->stmt->fetch(PDO::FETCH_ASSOC));
}
/**
* Return the data as an associative array.
* @param string $key Unique field to use as the array key for each row.
* @param string $value Field to use as the value (for key=>value format instead of the default key=>row)
* @return array
*/
public function fetchAllAssoc($key = null, $value = null)
{
$data = array();
while (($row = $this->stmt->fetch(PDO::FETCH_ASSOC)) !== false) {
if (isset($key)) {
$data[$row[$key]] = isset($value) ? $row[$value] : $row;
} else {
$data[] = isset($value) ? $row[$value] : $row;
}
}
return $data;
}
/**
* Return a specific cell from the results. Typically used with (single-row, single-column) aggregate queries. If
* no value is fetched form the database return null.
* @param $col 0-indexed column to select from (defaults to first column).
* @return string
*/
public function fetchOneValue($col = 0)
{
$data = $this->stmt->fetchColumn($col);
return $data === false ? null : $data;
}
/**
* Return a specific cell from the results. Typically used with (single-row, single-column) aggregate queries. If
* no value is fetched from the database then an exception is thrown.
* @param $col 0-indexed column to select from (defaults to first column).
* @return string
* @throws ReadingFromEmptyResultException
*/
public function fetchOneValueOrFail($col = 0)
{
return $this->getResultOrFail($this->stmt->fetchColumn($col));
}
/**
* Return a numbered array containing the specified column.
* @param $col 0-indexed column to select from (defaults to first column).
* @return array
*/
public function fetchAllValues($col = 0)
{
return $this->stmt->fetchAll(PDO::FETCH_COLUMN, $col);
}
/**
* Return data or optionally throw an exception, if data is false (empty).
* @param mixed $data
* @return mixed
* @throws ReadingFromEmptyResultException
*/
private function getResultOrFail($data)
{
if ($data === false) {
throw new ReadingFromEmptyResultException('Reading one value from empty results');
}
return $data;
}
/**
* Number of rows found (SELECT queries) or rows affected (UPDATE/INSERT/DELETE queries).
* @return int
*/
public function rowCount()
{
return $this->stmt->rowCount();
}
/**
* Return true if the number of rows found (SELECT queries) or rows affected (UPDATE/INSERT/DELETE queries) is 0
* and false otherwise.
* @return int
*/
public function isEmpty()
{
return $this->stmt->rowCount() === 0;
}
/**
* Obtain the raw PDOStatement object.
* @return PDOStatement
*/
public function getPDOStatement()
{
return $this->stmt;
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Database\Exceptions;
use Q2A\Exceptions\FatalErrorException;
class ReadingFromEmptyResultException extends FatalErrorException
{
/**
* ReadingFromEmptyResultException constructor.
*
* @param string $message
*/
public function __construct($message = 'There has been an attempt to read from an empty database result')
{
parent::__construct($message);
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Exceptions;
use Exception;
class ErrorMessageException extends Exception
{
/**
* ErrorMessageException constructor.
*
* @param string $message
*/
public function __construct($message = 'Error message exception.')
{
parent::__construct($message);
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Exceptions;
use Exception;
use Q2A\Http\Exceptions\PageNotFoundException;
require_once QA_INCLUDE_DIR . 'app/format.php';
class ExceptionHandler
{
public function __construct()
{
}
public function handle(Exception $exception)
{
if ($exception instanceof FatalErrorException) {
$this->handleFatalErrorException($exception);
} elseif ($exception instanceof PageNotFoundException) {
return $this->handlePageNotFoundException($exception);
} elseif ($exception instanceof ErrorMessageException) {
return $this->handleErrorMessageException($exception);
} else {
return $this->handleUnknownException($exception);
}
}
private function handleFatalErrorException(FatalErrorException $exception)
{
qa_fatal_error($exception->getMessage());
}
private function handlePageNotFoundException(PageNotFoundException $exception)
{
header('HTTP/1.0 404 Not Found');
$qa_content = $this->handleErrorMessageException($exception);
$qa_content['suggest_next'] = qa_html_suggest_qs_tags(qa_using_tags());
return $qa_content;
}
private function handleErrorMessageException(ErrorMessageException $exception)
{
$qa_content = qa_content_prepare();
$qa_content['error'] = $exception->getMessage();
return $qa_content;
}
private function handleUnknownException(Exception $exception)
{
return $this->handleErrorMessageException(new ErrorMessageException($exception->getMessage()));
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Exceptions;
use Exception;
class FatalErrorException extends Exception
{
/**
* FatalErrorException constructor.
*
* @param string $message
*/
public function __construct($message = 'Fatal error exception.')
{
parent::__construct($message);
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Http\Exceptions;
use Q2A\Exceptions\ErrorMessageException;
class PageNotFoundException extends ErrorMessageException
{
/**
* PageNotFoundException constructor.
*
* @param string $message
*/
public function __construct($message = null)
{
if ($message === null) {
$message = qa_lang_html('main/page_not_found');
}
parent::__construct($message);
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Http;
class Route
{
/** @var string */
private $id;
/** @var string */
private $httpMethod;
/** @var string */
private $routePath;
/** @var string */
private $controller;
/** @var string */
private $action;
/** @var array */
private $parameters;
public function __construct($id = null, $httpMethod = null, $routePath = null, $controller = null, $action = null)
{
$this->id = $id;
$this->httpMethod = strtoupper($httpMethod);
$this->routePath = $routePath;
$this->controller = $controller;
$this->action = $action;
$this->parameters = array();
}
/**
* @return string
*/
public function getId()
{
return $this->id;
}
/**
* @return string
*/
public function getHttpMethod()
{
return $this->httpMethod;
}
/**
* @return string
*/
public function getRoutePath()
{
return $this->routePath;
}
/**
* @return string
*/
public function getController()
{
return $this->controller;
}
/**
* @return string
*/
public function getAction()
{
return $this->action;
}
/**
* @return array
*/
public function getParameters()
{
return $this->parameters;
}
/**
* Bind actual request parameters to the route, replacing any existing ones.
*
* @param array $parameters
*/
public function setParameters($parameters)
{
$this->parameters = $parameters;
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Http;
class Router
{
/** @var Route[] */
protected $routes = array();
/** @var array */
private $paramsConverter;
/** @var string */
private $httpMethod;
public function __construct()
{
$this->paramsConverter = array(
'{str}' => '([^/]+)',
'{int}' => '([0-9]+)',
);
$this->httpMethod = strtoupper($_SERVER['REQUEST_METHOD']);
}
public function addRoute($id, $httpMethod, $routePath, $class, $func)
{
$this->routes[] = new Route($id, $httpMethod, $routePath, $class, $func);
}
/**
* Return the route definition that matches the given request. If none is found then null is
* returned.
*
* @param string $request Request that will be looked for a match
*
* @return Route|null
*/
public function match($request)
{
foreach ($this->routes as $route) {
if ($route->getHttpMethod() !== $this->httpMethod) {
continue;
}
$pathRegex = $this->buildPathRegex($route->getRoutePath());
if (preg_match($pathRegex, $request, $matches)) {
$route->setParameters(array_slice($matches, 1));
return $route;
}
}
return null;
}
/**
* Build the final regular expression to match the request. This method replaces all the
* parameters' placeholders with the given regular expression.
*
* @param string $routePath Route that might have placeholders
*
* @return string
*/
private function buildPathRegex($routePath)
{
return '#^' . strtr($routePath, $this->paramsConverter) . '$#';
}
/**
* Return the HTTP method of the current request.
*
* @return string
*/
public function getHttpMethod()
{
return $this->httpMethod;
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Middleware\Auth;
use Q2A\Auth\InternalUsersOnlyException;
use Q2A\Middleware\BaseMiddleware;
class InternalUsersOnly extends BaseMiddleware
{
/**
* Throw an exception if the current configuration is set to external users.
*
* @throws InternalUsersOnlyException
*/
public function handle()
{
if (QA_FINAL_EXTERNAL_USERS) {
throw new InternalUsersOnlyException('User accounts are handled by external code');
}
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Middleware\Auth;
use Q2A\Auth\NoPermissionException;
use Q2A\Middleware\BaseMiddleware;
class MinimumUserLevel extends BaseMiddleware
{
private $minimumUserLevel;
/**
* MinimumUserLevel constructor.
*
* @param int $minimumUserLevel Minimum user level allowed to perform the action
*/
public function __construct($minimumUserLevel)
{
$this->minimumUserLevel = $minimumUserLevel;
}
/**
* Throw an exception if the current configuration is set to external users.
*
* @throws NoPermissionException
*/
public function handle()
{
if (qa_get_logged_in_level() < $this->minimumUserLevel) {
throw new NoPermissionException();
}
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
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
*/
namespace Q2A\Middleware;
abstract class BaseMiddleware
{
abstract public function handle();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment