Commit 1d7b49b8 by Scott

Merge Joomla external users integration into 1.8

parents dc5e46d0 71c64c0b
......@@ -22,6 +22,7 @@ Q2A is highly customisable with many awesome features:
- Private messages and public wall posts.
- Log in via Facebook or others (using plugins).
- Out-of-the-box WordPress 3+ integration.
- Out-of-the-box Joomla! 3.0+ integration (in conjunction with a Joomla! extension).
- Custom single sign-on support for other sites.
- PHP/MySQL scalable to millions of users and posts.
- Safe from XSS, CSRF and SQL injection attacks.
......
......@@ -137,6 +137,17 @@
*/
/*
Out-of-the-box Joomla! 3.x integration - to integrate with your Joomla! site, define
QA_JOOMLA_INTEGRATE_PATH. as the full path to the Joomla! directory. If your Q2A
site is a subdirectory of your main Joomla site (recommended), you can specify
dirname(__DIR__) rather than the full path.
With this set, you do not need to set the QA_MYSQL_* constants above since these
will be taken from Joomla automatically. See online documentation for more details.
define('QA_JOOMLA_INTEGRATE_PATH', dirname(__DIR__));
*/
/*
Some settings to help optimize your Question2Answer site's performance.
If QA_HTML_COMPRESSION is true, HTML web pages will be output using Gzip compression, if
......
......@@ -56,11 +56,15 @@
// If we're using single sign-on integration (WordPress or otherwise), load PHP file for that
if (defined('QA_FINAL_WORDPRESS_INTEGRATE_PATH'))
if (defined('QA_FINAL_WORDPRESS_INTEGRATE_PATH')) {
require_once QA_INCLUDE_DIR.'util/external-users-wp.php';
else
}
elseif (defined('QA_FINAL_JOOMLA_INTEGRATE_PATH')) {
require_once QA_INCLUDE_DIR.'util/external-users-joomla.php';
}
else {
require_once QA_EXTERNAL_DIR.'qa-external-users.php';
}
// Access functions for user information
......
......@@ -49,8 +49,14 @@
qa_initialize_php();
qa_initialize_constants_1();
if (defined('QA_WORDPRESS_LOAD_FILE')) // if relevant, load WordPress integration in global scope
if (defined('QA_WORDPRESS_LOAD_FILE')) {
// if relevant, load WordPress integration in global scope
require_once QA_WORDPRESS_LOAD_FILE;
}
elseif (defined('QA_JOOMLA_LOAD_FILE')) {
// if relevant, load Joomla JConfig class into global scope
require_once QA_JOOMLA_LOAD_FILE;
}
qa_initialize_constants_2();
qa_initialize_modularity();
......@@ -147,7 +153,7 @@
function qa_initialize_constants_1()
/*
First stage of setting up Q2A constants, before (if necessary) loading WordPress integration
First stage of setting up Q2A constants, before (if necessary) loading WordPress or Joomla! integration
*/
{
global $qa_request_map;
......@@ -177,6 +183,13 @@
if (!is_readable(QA_WORDPRESS_LOAD_FILE))
qa_fatal_error('Could not find wp-load.php file for WordPress integration - please check QA_WORDPRESS_INTEGRATE_PATH in qa-config.php');
}
elseif (defined('QA_JOOMLA_INTEGRATE_PATH') && strlen(QA_JOOMLA_INTEGRATE_PATH)) {
define('QA_FINAL_JOOMLA_INTEGRATE_PATH', QA_JOOMLA_INTEGRATE_PATH.((substr(QA_JOOMLA_INTEGRATE_PATH, -1)=='/') ? '' : '/'));
define('QA_JOOMLA_LOAD_FILE', QA_FINAL_JOOMLA_INTEGRATE_PATH.'configuration.php');
if (!is_readable(QA_JOOMLA_LOAD_FILE))
qa_fatal_error('Could not find configuration.php file for Joomla integration - please check QA_JOOMLA_INTEGRATE_PATH in qa-config.php');
}
// Polyfills
......@@ -221,7 +234,7 @@
function qa_initialize_constants_2()
/*
Second stage of setting up Q2A constants, after (if necessary) loading WordPress integration
Second stage of setting up Q2A constants, after (if necessary) loading WordPress or Joomla! integration
*/
{
......@@ -274,6 +287,14 @@
$_POST=qa_undo_wordpress_quoting($_POST, false);
$_SERVER['PHP_SELF']=stripslashes($_SERVER['PHP_SELF']);
} elseif (defined('QA_FINAL_JOOMLA_INTEGRATE_PATH')) {
// More for Joomla integration
$jconfig = new JConfig();
define('QA_FINAL_MYSQL_HOSTNAME', $jconfig->host);
define('QA_FINAL_MYSQL_USERNAME', $jconfig->user);
define('QA_FINAL_MYSQL_PASSWORD', $jconfig->password);
define('QA_FINAL_MYSQL_DATABASE', $jconfig->db);
define('QA_FINAL_EXTERNAL_USERS', true);
} else {
define('QA_FINAL_MYSQL_HOSTNAME', QA_MYSQL_HOSTNAME);
define('QA_FINAL_MYSQL_USERNAME', QA_MYSQL_USERNAME);
......
......@@ -150,6 +150,15 @@ else {
$success .= 'Your Question2Answer database has been created and integrated with your WordPress site.';
}
elseif (defined('QA_FINAL_JOOMLA_INTEGRATE_PATH')) {
require_once QA_INCLUDE_DIR.'db/admin.php';
require_once QA_INCLUDE_DIR.'app/format.php';
$jconfig = new JConfig();
// create link back to Joomla! home page (Joomla doesn't have a 'home' config setting we can use like WP does, so we'll just assume that the Joomla home is the parent of the Q2A site. If it isn't, the user can fix the link for themselves later)
qa_db_page_move(qa_db_page_create($jconfig->sitename, QA_PAGE_FLAGS_EXTERNAL, '../', null, null, null), 'O', 1);
$success .= 'Your Question2Answer database has been created and integrated with your Joomla! site.';
}
else {
$success .= 'Your Question2Answer database has been created for external user identity management. Please read the online documentation to complete integration.';
}
......@@ -209,6 +218,10 @@ if (qa_db_connection(false) !== null && !@$pass_failure_from_install) {
if (defined('QA_FINAL_WORDPRESS_INTEGRATE_PATH')) {
$errorhtml .= "\n\nWhen you click below, your Question2Answer site will be set up to integrate with the users of your WordPress site <a href=\"".qa_html(get_option('home'))."\" target=\"_blank\">".qa_html(get_option('blogname'))."</a>. Please consult the online documentation for more information.";
}
elseif (defined('QA_FINAL_JOOMLA_INTEGRATE_PATH')) {
$jconfig = new JConfig();
$errorhtml .= "\n\nWhen you click below, your Question2Answer site will be set up to integrate with the users of your Joomla! site <a href=\"../\" target=\"_blank\">".$jconfig->sitename."</a>. It's also recommended to install the Joomla QAIntegration plugin for additional user-access control. Please consult the online documentation for more information.";
}
else {
$errorhtml .= "\n\nWhen you click below, your Question2Answer site will be set up to integrate with your existing user database and management. Users will be referenced with database column type ".qa_html(qa_get_mysql_user_column_type()).". Please consult the online documentation for more information.";
}
......
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
File: qa-include/qa-external-users-joomla.php
Description: External user functions for basic Joomla integration
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
More about this license: http://www.question2answer.org/license.php
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
function qa_get_mysql_user_column_type()
{
return "INT";
}
function qa_get_login_links($relative_url_prefix, $redirect_back_to_url)
{
$jhelper = new qa_joomla_helper();
$config_urls = $jhelper->trigger_get_urls_event();
return array(
'login' => $config_urls['login'],
'register' => $config_urls['reg'],
'logout' => $config_urls['logout']
);
}
function qa_get_logged_in_user()
{
$jhelper = new qa_joomla_helper();
$user = $jhelper->get_user();
$config_urls = $jhelper->trigger_get_urls_event();
if ($user && !$user->guest) {
$access = $jhelper->trigger_access_event($user);
$level = QA_USER_LEVEL_BASIC;
if ($access['post']) {
$level = QA_USER_LEVEL_APPROVED;
}
if ($access['edit']) {
$level = QA_USER_LEVEL_EDITOR;
}
if ($access['mod']) {
$level = QA_USER_LEVEL_MODERATOR;
}
if ($access['admin']) {
$level = QA_USER_LEVEL_ADMIN;
}
if ($access['super'] || $user->get('isRoot')) {
$level = QA_USER_LEVEL_SUPER;
}
$teamGroup = $jhelper->trigger_team_group_event($user);
$qa_user = array(
'userid' => $user->id,
'publicusername' => $user->username,
'email' => $user->email,
'level' => $level,
);
if ($user->block) {
$qa_user['blocked'] = true;
}
return $qa_user;
}
return null;
}
function qa_get_user_email($userid)
{
$jhelper = new qa_joomla_helper();
$user = $jhelper->get_user($userid);
if ($user) {
return $user->email;
}
return null;
}
function qa_get_userids_from_public($publicusernames)
{
$output = array();
if (count($publicusernames)) {
$jhelper = new qa_joomla_helper();
foreach ($publicusernames as $username) {
$output[$username] = $jhelper->get_userid($username);
}
}
return $output;
}
function qa_get_public_from_userids($userids)
{
$output = array();
if (count($userids)) {
$jhelper = new qa_joomla_helper();
foreach ($userids as $userID) {
$user = $jhelper->get_user($userID);
$output[$userID] = $user->username;
}
}
return $output;
}
function qa_get_logged_in_user_html($logged_in_user, $relative_url_prefix)
{
$publicusername = $logged_in_user['publicusername'];
return '<a href="' . qa_path_html('user/' . $publicusername) . '" class="qa-user-link">' . htmlspecialchars($publicusername) . '</a>';
}
function qa_get_users_html($userids, $should_include_link, $relative_url_prefix)
{
$useridtopublic = qa_get_public_from_userids($userids);
$usershtml = array();
foreach ($userids as $userid) {
$publicusername = $useridtopublic[$userid];
$usershtml[$userid] = htmlspecialchars($publicusername);
if ($should_include_link) {
$usershtml[$userid] = '<a href="' . qa_path_html('user/' . $publicusername) . '" class="qa-user-link">' . $usershtml[$userid] . '</a>';
}
}
return $usershtml;
}
function qa_avatar_html_from_userid($userid, $size, $padding)
{
$jhelper = new qa_joomla_helper();
$avatarURL = $jhelper->trigger_get_avatar_event($userid, $size);
$avatarHTML = $avatarURL ? "<img src='{$avatarURL}' class='qa-avatar-image' alt=''/>" : '';
if ($padding) {
// If $padding is true, the HTML you return should render to a square of $size x $size pixels, even if the avatar is not square.
$avatarHTML = "<span style='display:inline-block; width:{$size}px; height:{$size}px; overflow:hidden;'>{$avatarHTML}</span>";
}
return $avatarHTML;
}
function qa_user_report_action($userid, $action)
{
$jhelper = new qa_joomla_helper();
$jhelper->trigger_log_event($userid, $action);
}
/**
* Link to Joomla app.
*/
class qa_joomla_helper
{
private $app;
public function __construct()
{
$this->find_joomla_path();
$this->load_joomla_app();
}
private function find_joomla_path()
{
// JPATH_BASE must be defined for Joomla to work
if (!defined('JPATH_BASE')) {
define('JPATH_BASE', QA_FINAL_JOOMLA_INTEGRATE_PATH);
}
}
private function load_joomla_app()
{
// This will define the _JEXEC constant that will allow us to access the rest of the Joomla framework
if (!defined('_JEXEC')) {
define('_JEXEC', 1);
}
require_once(JPATH_BASE . '/includes/defines.php');
require_once(JPATH_BASE . '/includes/framework.php');
// Instantiate the application.
$this->app = JFactory::getApplication('site');
// Initialise the application.
$this->app->initialise();
}
public function get_app()
{
return $this->app;
}
public function get_user($userid = null)
{
return JFactory::getUser($userid);
}
public function get_userid($username)
{
return JUserHelper::getUserId($username);
}
public function trigger_access_event($user)
{
return $this->trigger_joomla_event('onQnaAccess', array($user));
}
public function trigger_team_group_event($user)
{
return $this->trigger_joomla_event('onTeamGroup', array($user));
}
public function trigger_get_urls_event()
{
return $this->trigger_joomla_event('onGetURLs', array());
}
public function trigger_get_avatar_event($userid, $size)
{
return $this->trigger_joomla_event('onGetAvatar', array($userid, $size));
}
public function trigger_log_event($userid, $action)
{
return $this->trigger_joomla_event('onWriteLog', array($userid, $action), false);
}
private function trigger_joomla_event($event, $args = array(), $expectResponse = true)
{
JPluginHelper::importPlugin('q2a');
$dispatcher = JEventDispatcher::getInstance();
$results = $dispatcher->trigger($event, $args);
if ($expectResponse && (!is_array($results) || count($results) < 1)) {
// no Q2A plugins installed in Joomla, so we'll have to resort to defaults
$results = $this->default_response($event, $args);
}
return array_pop($results);
}
private function default_response($event, $args)
{
return array(qa_joomla_default_integration::$event($args));
}
}
/**
* Implements the same methods as a Joomla plugin would implement, but called locally within Q2A.
* This is intended as a set of default actions in case no Joomla plugin has been installed. It's
* recommended to install the Joomla QAIntegration plugin for additional user-access control.
*/
class qa_joomla_default_integration
{
/**
* If you're relying on the defaults, you must make sure that your Joomla instance has the following pages configured.
*/
public static function onGetURLs()
{
$login = 'index.php?option=com_users&view=login';
$logout = 'index.php?option=com_users&task=user.logout&' . JSession::getFormToken() . '=1&return=' . urlencode(base64_encode('index.php'));
$reg = 'index.php?option=com_users&view=registration';
return array(
// undo Joomla's escaping of characters since Q2A also escapes
'login' => htmlspecialchars_decode(JRoute::_($login)),
'logout' => htmlspecialchars_decode(JRoute::_($logout)),
'reg' => htmlspecialchars_decode(JRoute::_($reg)),
'denied' => htmlspecialchars_decode(JRoute::_('index.php')),
);
}
/**
* Return the access levels available to the user. A proper Joomla plugin would allow you to fine tune this in as much
* detail as you needed, but this default method can only look at the core Joomla system permissions and try to map
* those to the Q2A perms. Not ideal; enough to get started, but recommend switching to the Joomla plugin if possible.
*/
public static function onQnaAccess(array $args)
{
list($user) = $args;
return array(
'view' => true,
'post' => !($user->guest || $user->block),
'edit' => $user->authorise('core.edit'),
'mod' => $user->authorise('core.edit.state'),
'admin' => $user->authorise('core.manage'),
'super' => $user->authorise('core.admin') || $user->get('isRoot'),
);
}
/**
* Return the group name (if any) that was responsible for granting the user access to the given view level.
* For this default method, we just won't return anything.
*/
public static function onTeamGroup($args)
{
list($user) = $args;
return null;
}
/**
* This method would be used to ask Joomla to supply an avatar for a user.
* For this default method, we just won't do anything.
*/
public static function onGetAvatar($args)
{
list($userid, $size) = $args;
return null;
}
/**
* This method would be used to notify Joomla of a Q2A action, eg so it could write a log entry.
* For this default method, we just won't do anything.
*/
public static function onWriteLog($args)
{
list($userid, $action) = $args;
return null;
}
}
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