Commit 58fb33a4 by Scott

Coding style (base)

parent 5bc0177c
...@@ -21,11 +21,11 @@ ...@@ -21,11 +21,11 @@
*/ */
define('QA_VERSION', '1.7.4'); // also used as suffix for .js and .css requests define('QA_VERSION', '1.7.4'); // also used as suffix for .js and .css requests
define('QA_BUILD_DATE', '2016-03-14'); define('QA_BUILD_DATE', '2016-03-14');
/** /**
* Autoloads some Q2A classes so it's possible to use them without adding a require_once first. From version 1.7 onwards. * Autoloads some Q2A classes so it's possible to use them without adding a require_once first. From version 1.7 onwards.
* These loosely follow PHP-FIG's PSR-0 standard where faux namespaces are separated by underscores. This is being done * These loosely follow PHP-FIG's PSR-0 standard where faux namespaces are separated by underscores. This is being done
* slowly and carefully to maintain backwards compatibility, and does not apply to plugins, themes, nor most of the core * slowly and carefully to maintain backwards compatibility, and does not apply to plugins, themes, nor most of the core
...@@ -36,105 +36,92 @@ ...@@ -36,105 +36,92 @@
* Classes are mapped to PHP files with the underscores converted to directory separators. The Q2A_Util_Debug class is in * Classes are mapped to PHP files with the underscores converted to directory separators. The Q2A_Util_Debug class is in
* the file qa-include/Q2A/Util/Debug.php. A class named Q2A_Db_User_Messages would be in a file qa-include/Q2A/Db/User/Messages.php. * the file qa-include/Q2A/Util/Debug.php. A class named Q2A_Db_User_Messages would be in a file qa-include/Q2A/Db/User/Messages.php.
*/ */
function qa_autoload($class) function qa_autoload($class)
{ {
if (strpos($class, 'Q2A_') === 0) if (strpos($class, 'Q2A_') === 0)
require QA_INCLUDE_DIR.strtr($class, '_', '/') . '.php'; require QA_INCLUDE_DIR . strtr($class, '_', '/') . '.php';
} }
spl_autoload_register('qa_autoload'); spl_autoload_register('qa_autoload');
// Execution section of this file - remainder contains function definitions // Execution section of this file - remainder contains function definitions
qa_initialize_php(); qa_initialize_php();
qa_initialize_constants_1(); qa_initialize_constants_1();
if (defined('QA_WORDPRESS_LOAD_FILE')) { if (defined('QA_WORDPRESS_LOAD_FILE')) {
// if relevant, load WordPress integration in global scope // if relevant, load WordPress integration in global scope
require_once QA_WORDPRESS_LOAD_FILE; require_once QA_WORDPRESS_LOAD_FILE;
} } elseif (defined('QA_JOOMLA_LOAD_FILE')) {
elseif (defined('QA_JOOMLA_LOAD_FILE')) {
// if relevant, load Joomla JConfig class into global scope // if relevant, load Joomla JConfig class into global scope
require_once QA_JOOMLA_LOAD_FILE; require_once QA_JOOMLA_LOAD_FILE;
} }
qa_initialize_constants_2();
qa_initialize_modularity();
qa_register_core_modules();
require_once QA_INCLUDE_DIR.'qa-db.php';
qa_initialize_plugins();
function qa_initialize_plugins() { qa_initialize_constants_2();
$pluginManager = new Q2A_Plugin_PluginManager(); qa_initialize_modularity();
$pluginManager->readAllPluginMetadatas(); qa_register_core_modules();
$pluginManager->loadPluginsBeforeDbInit(); require_once QA_INCLUDE_DIR . 'qa-db.php';
qa_load_override_files();
qa_db_allow_connect(); qa_initialize_plugins();
$pluginManager->loadPluginsAfterDbInit();
qa_load_override_files();
}
// Version comparison functions // Version comparison functions
/** /**
* Converts the $version string (e.g. 1.6.2.2) to a floating point that can be used for greater/lesser comparisons * Converts the $version string (e.g. 1.6.2.2) to a floating point that can be used for greater/lesser comparisons
* (PHP's version_compare() function is not quite suitable for our needs) * (PHP's version_compare() function is not quite suitable for our needs)
*/ */
function qa_version_to_float($version) function qa_version_to_float($version)
{ {
$value=0.0; $value = 0.0;
if (preg_match('/[0-9\.]+/', $version, $matches)) { if (preg_match('/[0-9\.]+/', $version, $matches)) {
$parts=explode('.', $matches[0]); $parts = explode('.', $matches[0]);
$units=1.0; $units = 1.0;
foreach ($parts as $part) { foreach ($parts as $part) {
$value+=min($part, 999)*$units; $value += min($part, 999) * $units;
$units/=1000; $units /= 1000;
} }
} }
return $value; return $value;
} }
/** /**
* Returns true if the current Q2A version is lower than $version, if both are valid version strings for qa_version_to_float() * Returns true if the current Q2A version is lower than $version, if both are valid version strings for qa_version_to_float()
*/ */
function qa_qa_version_below($version) function qa_qa_version_below($version)
{ {
$minqa=qa_version_to_float($version); $minqa = qa_version_to_float($version);
$thisqa=qa_version_to_float(QA_VERSION); $thisqa = qa_version_to_float(QA_VERSION);
return $minqa && $thisqa && ($thisqa<$minqa); return $minqa && $thisqa && $thisqa < $minqa;
} }
/** /**
* Returns true if the current PHP version is lower than $version, if both are valid version strings for qa_version_to_float() * Returns true if the current PHP version is lower than $version, if both are valid version strings for qa_version_to_float()
*/ */
function qa_php_version_below($version) function qa_php_version_below($version)
{ {
$minphp=qa_version_to_float($version); $minphp = qa_version_to_float($version);
$thisphp=qa_version_to_float(phpversion()); $thisphp = qa_version_to_float(phpversion());
return $minphp && $thisphp && ($thisphp<$minphp); return $minphp && $thisphp && $thisphp < $minphp;
} }
// Initialization functions called above // Initialization functions called above
/** /**
* Set up and verify the PHP environment for Q2A, including unregistering globals if necessary * Set up and verify the PHP environment for Q2A, including unregistering globals if necessary
*/ */
function qa_initialize_php() function qa_initialize_php()
{ {
if (qa_php_version_below('5.1.6')) if (qa_php_version_below('5.1.6'))
qa_fatal_error('Q2A requires PHP 5.1.6 or later'); qa_fatal_error('Q2A requires PHP 5.1.6 or later');
...@@ -148,90 +135,95 @@ ...@@ -148,90 +135,95 @@
@date_default_timezone_set(@date_default_timezone_get()); // prevent PHP notices where default timezone not set @date_default_timezone_set(@date_default_timezone_get()); // prevent PHP notices where default timezone not set
if (ini_get('register_globals')) { if (ini_get('register_globals')) {
$checkarrays=array('_ENV', '_GET', '_POST', '_COOKIE', '_SERVER', '_FILES', '_REQUEST', '_SESSION'); // unregister globals if they're registered $checkarrays = array('_ENV', '_GET', '_POST', '_COOKIE', '_SERVER', '_FILES', '_REQUEST', '_SESSION'); // unregister globals if they're registered
$keyprotect=array_flip(array_merge($checkarrays, array('GLOBALS'))); $keyprotect = array_flip(array_merge($checkarrays, array('GLOBALS')));
foreach ($checkarrays as $checkarray) foreach ($checkarrays as $checkarray) {
if ( isset(${$checkarray}) && is_array(${$checkarray}) ) if (isset(${$checkarray}) && is_array(${$checkarray})) {
foreach (${$checkarray} as $checkkey => $checkvalue) foreach (${$checkarray} as $checkkey => $checkvalue) {
if (isset($keyprotect[$checkkey])) if (isset($keyprotect[$checkkey])) {
qa_fatal_error('My superglobals are not for overriding'); qa_fatal_error('My superglobals are not for overriding');
else } else {
unset($GLOBALS[$checkkey]); unset($GLOBALS[$checkkey]);
} }
} }
}
}
}
}
/** /**
* First stage of setting up Q2A constants, before (if necessary) loading WordPress or Joomla! integration * First stage of setting up Q2A constants, before (if necessary) loading WordPress or Joomla! integration
*/ */
function qa_initialize_constants_1() function qa_initialize_constants_1()
{ {
global $qa_request_map; global $qa_request_map;
define('QA_CATEGORY_DEPTH', 4); // you can't change this number! define('QA_CATEGORY_DEPTH', 4); // you can't change this number!
if (!defined('QA_BASE_DIR')) if (!defined('QA_BASE_DIR'))
define('QA_BASE_DIR', dirname(dirname(__FILE__)).'/'); // try out best if not set in index.php or qa-index.php - won't work with symbolic links define('QA_BASE_DIR', dirname(dirname(__FILE__)) . '/'); // try out best if not set in index.php or qa-index.php - won't work with symbolic links
define('QA_EXTERNAL_DIR', QA_BASE_DIR.'qa-external/'); define('QA_EXTERNAL_DIR', QA_BASE_DIR . 'qa-external/');
define('QA_INCLUDE_DIR', QA_BASE_DIR.'qa-include/'); define('QA_INCLUDE_DIR', QA_BASE_DIR . 'qa-include/');
define('QA_LANG_DIR', QA_BASE_DIR.'qa-lang/'); define('QA_LANG_DIR', QA_BASE_DIR . 'qa-lang/');
define('QA_THEME_DIR', QA_BASE_DIR.'qa-theme/'); define('QA_THEME_DIR', QA_BASE_DIR . 'qa-theme/');
define('QA_PLUGIN_DIR', QA_BASE_DIR.'qa-plugin/'); define('QA_PLUGIN_DIR', QA_BASE_DIR . 'qa-plugin/');
if (!file_exists(QA_BASE_DIR.'qa-config.php')) if (!file_exists(QA_BASE_DIR . 'qa-config.php'))
qa_fatal_error('The config file could not be found. Please read the instructions in qa-config-example.php.'); qa_fatal_error('The config file could not be found. Please read the instructions in qa-config-example.php.');
require_once QA_BASE_DIR.'qa-config.php'; require_once QA_BASE_DIR . 'qa-config.php';
$qa_request_map = isset($QA_CONST_PATH_MAP) && is_array($QA_CONST_PATH_MAP) ? $QA_CONST_PATH_MAP : array(); $qa_request_map = isset($QA_CONST_PATH_MAP) && is_array($QA_CONST_PATH_MAP) ? $QA_CONST_PATH_MAP : array();
if (defined('QA_WORDPRESS_INTEGRATE_PATH') && strlen(QA_WORDPRESS_INTEGRATE_PATH)) { if (defined('QA_WORDPRESS_INTEGRATE_PATH') && strlen(QA_WORDPRESS_INTEGRATE_PATH)) {
define('QA_FINAL_WORDPRESS_INTEGRATE_PATH', QA_WORDPRESS_INTEGRATE_PATH.((substr(QA_WORDPRESS_INTEGRATE_PATH, -1)=='/') ? '' : '/')); define('QA_FINAL_WORDPRESS_INTEGRATE_PATH', QA_WORDPRESS_INTEGRATE_PATH . ((substr(QA_WORDPRESS_INTEGRATE_PATH, -1) == '/') ? '' : '/'));
define('QA_WORDPRESS_LOAD_FILE', QA_FINAL_WORDPRESS_INTEGRATE_PATH.'wp-load.php'); define('QA_WORDPRESS_LOAD_FILE', QA_FINAL_WORDPRESS_INTEGRATE_PATH . 'wp-load.php');
if (!is_readable(QA_WORDPRESS_LOAD_FILE)) 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'); 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)) { } 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_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'); define('QA_JOOMLA_LOAD_FILE', QA_FINAL_JOOMLA_INTEGRATE_PATH . 'configuration.php');
if (!is_readable(QA_JOOMLA_LOAD_FILE)) 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'); qa_fatal_error('Could not find configuration.php file for Joomla integration - please check QA_JOOMLA_INTEGRATE_PATH in qa-config.php');
} }
}
// Polyfills // Polyfills
// password_hash compatibility for 5.3-5.4 // password_hash compatibility for 5.3-5.4
define('QA_PASSWORD_HASH', !qa_php_version_below('5.3.7')); define('QA_PASSWORD_HASH', !qa_php_version_below('5.3.7'));
if (QA_PASSWORD_HASH) { if (QA_PASSWORD_HASH) {
require_once QA_INCLUDE_DIR.'vendor/password_compat.php'; require_once QA_INCLUDE_DIR . 'vendor/password_compat.php';
} }
// http://php.net/manual/en/function.hash-equals.php#115635 // http://php.net/manual/en/function.hash-equals.php#115635
if(!function_exists('hash_equals')) { if (!function_exists('hash_equals')) {
function hash_equals($str1, $str2) { function hash_equals($str1, $str2)
if(strlen($str1) != strlen($str2)) { {
if (strlen($str1) != strlen($str2)) {
return false; return false;
} else { } else {
$res = $str1 ^ $str2; $res = $str1 ^ $str2;
$ret = 0; $ret = 0;
for($i = strlen($res) - 1; $i >= 0; $i--) $ret |= ord($res[$i]); for ($i = strlen($res) - 1; $i >= 0; $i--) $ret |= ord($res[$i]);
return !$ret; return !$ret;
} }
} }
} }
} }
/** /**
* Second stage of setting up Q2A constants, after (if necessary) loading WordPress or Joomla! integration * Second stage of setting up Q2A constants, after (if necessary) loading WordPress or Joomla! integration
*/ */
function qa_initialize_constants_2() function qa_initialize_constants_2()
{ {
// Default values if not set in qa-config.php // Default values if not set in qa-config.php
@define('QA_COOKIE_DOMAIN', ''); @define('QA_COOKIE_DOMAIN', '');
...@@ -268,20 +260,20 @@ ...@@ -268,20 +260,20 @@
{ {
if (is_array($param)) { // if (is_array($param)) { //
foreach ($param as $key => $value) foreach ($param as $key => $value)
$param[$key]=qa_undo_wordpress_quoting($value, $isget); $param[$key] = qa_undo_wordpress_quoting($value, $isget);
} else { } else {
$param=stripslashes($param); $param = stripslashes($param);
if ($isget) if ($isget)
$param=strtr($param, array('\\\'' => '\'', '\"' => '"')); // also compensate for WordPress's .htaccess file $param = strtr($param, array('\\\'' => '\'', '\"' => '"')); // also compensate for WordPress's .htaccess file
} }
return $param; return $param;
} }
$_GET=qa_undo_wordpress_quoting($_GET, true); $_GET = qa_undo_wordpress_quoting($_GET, true);
$_POST=qa_undo_wordpress_quoting($_POST, false); $_POST = qa_undo_wordpress_quoting($_POST, false);
$_SERVER['PHP_SELF']=stripslashes($_SERVER['PHP_SELF']); $_SERVER['PHP_SELF'] = stripslashes($_SERVER['PHP_SELF']);
} elseif (defined('QA_FINAL_JOOMLA_INTEGRATE_PATH')) { } elseif (defined('QA_FINAL_JOOMLA_INTEGRATE_PATH')) {
// More for Joomla integration // More for Joomla integration
...@@ -312,32 +304,32 @@ ...@@ -312,32 +304,32 @@
define('QA_URL_FORMAT_SAFEST', 5); // http://...../index.php?qa=123&qa_1=why-is-the-sky-blue define('QA_URL_FORMAT_SAFEST', 5); // http://...../index.php?qa=123&qa_1=why-is-the-sky-blue
define('QA_URL_TEST_STRING', '$&-_~#%\\@^*()][`\';=:|".{},!<>?# π§½Жש'); // tests escaping, spaces, quote slashing and unicode - but not + and / define('QA_URL_TEST_STRING', '$&-_~#%\\@^*()][`\';=:|".{},!<>?# π§½Жש'); // tests escaping, spaces, quote slashing and unicode - but not + and /
} }
/** /**
* Gets everything ready to start using modules, layers and overrides * Gets everything ready to start using modules, layers and overrides
*/ */
function qa_initialize_modularity() function qa_initialize_modularity()
{ {
global $qa_modules, $qa_layers, $qa_override_files, $qa_override_files_temp, $qa_overrides, $qa_direct; global $qa_modules, $qa_layers, $qa_override_files, $qa_override_files_temp, $qa_overrides, $qa_direct;
$qa_modules=array(); $qa_modules = array();
$qa_layers=array(); $qa_layers = array();
$qa_override_files=array(); $qa_override_files = array();
$qa_override_files_temp=array(); $qa_override_files_temp = array();
$qa_overrides=array(); $qa_overrides = array();
$qa_direct=array(); $qa_direct = array();
} }
/** /**
* Set up output buffering. Use gzip compression if option set and it's not an admin page (since some of these contain lengthy processes). * Set up output buffering. Use gzip compression if option set and it's not an admin page (since some of these contain lengthy processes).
* @param $request * @param $request
* @return bool whether buffering was used * @return bool whether buffering was used
*/ */
function qa_initialize_buffering($request='') function qa_initialize_buffering($request = '')
{ {
if (headers_sent()) { if (headers_sent()) {
return false; return false;
} }
...@@ -345,14 +337,14 @@ ...@@ -345,14 +337,14 @@
$useGzip = QA_HTML_COMPRESSION && substr($request, 0, 6) !== 'admin/' && extension_loaded('zlib'); $useGzip = QA_HTML_COMPRESSION && substr($request, 0, 6) !== 'admin/' && extension_loaded('zlib');
ob_start($useGzip ? 'ob_gzhandler' : null); ob_start($useGzip ? 'ob_gzhandler' : null);
return true; return true;
} }
/** /**
* Register all modules that come as part of the Q2A core (as opposed to plugins) * Register all modules that come as part of the Q2A core (as opposed to plugins)
*/ */
function qa_register_core_modules() function qa_register_core_modules()
{ {
qa_register_module('filter', 'plugins/qa-filter-basic.php', 'qa_filter_basic', ''); qa_register_module('filter', 'plugins/qa-filter-basic.php', 'qa_filter_basic', '');
qa_register_module('editor', 'plugins/qa-editor-basic.php', 'qa_editor_basic', ''); qa_register_module('editor', 'plugins/qa-editor-basic.php', 'qa_editor_basic', '');
qa_register_module('viewer', 'plugins/qa-viewer-basic.php', 'qa_viewer_basic', ''); qa_register_module('viewer', 'plugins/qa-viewer-basic.php', 'qa_viewer_basic', '');
...@@ -364,18 +356,36 @@ ...@@ -364,18 +356,36 @@
qa_register_module('widget', 'plugins/qa-widget-ask-box.php', 'qa_ask_box', 'Ask Box'); qa_register_module('widget', 'plugins/qa-widget-ask-box.php', 'qa_ask_box', 'Ask Box');
qa_register_module('widget', 'plugins/qa-widget-related-qs.php', 'qa_related_qs', 'Related Questions'); qa_register_module('widget', 'plugins/qa-widget-related-qs.php', 'qa_related_qs', 'Related Questions');
qa_register_module('widget', 'plugins/qa-widget-category-list.php', 'qa_category_list', 'Categories'); qa_register_module('widget', 'plugins/qa-widget-category-list.php', 'qa_category_list', 'Categories');
} }
/** /**
* Load all plugins. They are split into two groups: plugins loaded before database is available, and those loaded afterward.
*/
function qa_initialize_plugins()
{
$pluginManager = new Q2A_Plugin_PluginManager();
$pluginManager->readAllPluginMetadatas();
$pluginManager->loadPluginsBeforeDbInit();
qa_load_override_files();
qa_db_allow_connect();
$pluginManager->loadPluginsAfterDbInit();
qa_load_override_files();
}
/**
* Retrieve metadata information from the $contents of a qa-theme.php or qa-plugin.php file, specified by $type ('Plugin' or 'Theme'). * Retrieve metadata information from the $contents of a qa-theme.php or qa-plugin.php file, specified by $type ('Plugin' or 'Theme').
* If $versiononly is true, only min version metadata is parsed. * If $versiononly is true, only min version metadata is parsed.
* Name, Description, Min Q2A & Min PHP are not currently used by themes. * Name, Description, Min Q2A & Min PHP are not currently used by themes.
* *
* @deprecated Deprecated from 1.7; Q2A_Util_Metadata class and metadata.json files should be used instead * @deprecated Deprecated from 1.7; Q2A_Util_Metadata class and metadata.json files should be used instead
*/ */
function qa_addon_metadata($contents, $type, $versiononly=false) function qa_addon_metadata($contents, $type, $versiononly = false)
{ {
$fields = array( $fields = array(
'min_q2a' => 'Minimum Question2Answer Version', 'min_q2a' => 'Minimum Question2Answer Version',
'min_php' => 'Minimum PHP Version', 'min_php' => 'Minimum PHP Version',
...@@ -398,52 +408,54 @@ ...@@ -398,52 +408,54 @@
foreach ($fields as $key => $field) { foreach ($fields as $key => $field) {
// prepend 'Theme'/'Plugin' and search for key data // prepend 'Theme'/'Plugin' and search for key data
$fieldregex = str_replace(' ', '[ \t]*', preg_quote("$type $field", '/')); $fieldregex = str_replace(' ', '[ \t]*', preg_quote("$type $field", '/'));
if (preg_match('/'.$fieldregex.':[ \t]*([^\n\f]*)[\n\f]/i', $contents, $matches)) if (preg_match('/' . $fieldregex . ':[ \t]*([^\n\f]*)[\n\f]/i', $contents, $matches))
$metadata[$key] = trim($matches[1]); $metadata[$key] = trim($matches[1]);
} }
return $metadata; return $metadata;
} }
/** /**
* Apply all the function overrides in override files that have been registered by plugins * Apply all the function overrides in override files that have been registered by plugins
*/ */
function qa_load_override_files() function qa_load_override_files()
{ {
global $qa_override_files, $qa_override_files_temp, $qa_overrides; global $qa_override_files, $qa_override_files_temp, $qa_overrides;
$functionindex=array(); $functionindex = array();
foreach ($qa_override_files_temp as $override) { foreach ($qa_override_files_temp as $override) {
$qa_override_files[] = $override; $qa_override_files[] = $override;
$filename=$override['directory'].$override['include']; $filename = $override['directory'] . $override['include'];
$functionsphp=file_get_contents($filename); $functionsphp = file_get_contents($filename);
preg_match_all('/\Wfunction\s+(qa_[a-z_]+)\s*\(/im', $functionsphp, $rawmatches, PREG_PATTERN_ORDER|PREG_OFFSET_CAPTURE); preg_match_all('/\Wfunction\s+(qa_[a-z_]+)\s*\(/im', $functionsphp, $rawmatches, PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE);
$reversematches=array_reverse($rawmatches[1], true); // reverse so offsets remain correct as we step through $reversematches = array_reverse($rawmatches[1], true); // reverse so offsets remain correct as we step through
$postreplace=array(); $postreplace = array();
$suffix='_in_'.preg_replace('/[^A-Za-z0-9_]+/', '_', basename($override['include']));
// include file name in defined function names to make debugging easier if there is an error // include file name in defined function names to make debugging easier if there is an error
$suffix = '_in_' . preg_replace('/[^A-Za-z0-9_]+/', '_', basename($override['include']));
foreach ($reversematches as $rawmatch) { foreach ($reversematches as $rawmatch) {
$function=strtolower($rawmatch[0]); $function = strtolower($rawmatch[0]);
$position=$rawmatch[1]; $position = $rawmatch[1];
if (isset($qa_overrides[$function])) if (isset($qa_overrides[$function]))
$postreplace[$function.'_base']=$qa_overrides[$function]; $postreplace[$function . '_base'] = $qa_overrides[$function];
$newname=$function.'_override_'.(@++$functionindex[$function]).$suffix; $newname = $function . '_override_' . (@++$functionindex[$function]) . $suffix;
$functionsphp=substr_replace($functionsphp, $newname, $position, strlen($function)); $functionsphp = substr_replace($functionsphp, $newname, $position, strlen($function));
$qa_overrides[$function]=$newname; $qa_overrides[$function] = $newname;
} }
foreach ($postreplace as $oldname => $newname) foreach ($postreplace as $oldname => $newname) {
if (preg_match_all('/\W('.preg_quote($oldname).')\s*\(/im', $functionsphp, $matches, PREG_PATTERN_ORDER|PREG_OFFSET_CAPTURE)) { if (preg_match_all('/\W(' . preg_quote($oldname) . ')\s*\(/im', $functionsphp, $matches, PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE)) {
$searchmatches=array_reverse($matches[1]); $searchmatches = array_reverse($matches[1]);
foreach ($searchmatches as $searchmatch) foreach ($searchmatches as $searchmatch) {
$functionsphp=substr_replace($functionsphp, $newname, $searchmatch[1], strlen($searchmatch[0])); $functionsphp = substr_replace($functionsphp, $newname, $searchmatch[1], strlen($searchmatch[0]));
}
}
} }
// echo '<pre style="text-align:left;">'.htmlspecialchars($functionsphp).'</pre>'; // to debug munged code // echo '<pre style="text-align:left;">'.htmlspecialchars($functionsphp).'</pre>'; // to debug munged code
...@@ -452,187 +464,200 @@ ...@@ -452,187 +464,200 @@
} }
$qa_override_files_temp = array(); $qa_override_files_temp = array();
} }
// Functions for registering different varieties of Q2A modularity // Functions for registering different varieties of Q2A modularity
/** /**
* Register a module of $type named $name, whose class named $class is defined in file $include (or null if no include necessary) * Register a module of $type named $name, whose class named $class is defined in file $include (or null if no include necessary)
* If this module comes from a plugin, pass in the local plugin $directory and the $urltoroot relative url for that directory * If this module comes from a plugin, pass in the local plugin $directory and the $urltoroot relative url for that directory
*/ */
function qa_register_module($type, $include, $class, $name, $directory=QA_INCLUDE_DIR, $urltoroot=null) function qa_register_module($type, $include, $class, $name, $directory = QA_INCLUDE_DIR, $urltoroot = null)
{ {
global $qa_modules; global $qa_modules;
$previous=@$qa_modules[$type][$name]; $previous = @$qa_modules[$type][$name];
if (isset($previous)) if (isset($previous)) {
qa_fatal_error('A '.$type.' module named '.$name.' already exists. Please check there are no duplicate plugins. '. qa_fatal_error('A ' . $type . ' module named ' . $name . ' already exists. Please check there are no duplicate plugins. ' .
"\n\nModule 1: ".$previous['directory'].$previous['include']."\nModule 2: ".$directory.$include); "\n\nModule 1: " . $previous['directory'] . $previous['include'] . "\nModule 2: " . $directory . $include);
}
$qa_modules[$type][$name]=array( $qa_modules[$type][$name] = array(
'directory' => $directory, 'directory' => $directory,
'urltoroot' => $urltoroot, 'urltoroot' => $urltoroot,
'include' => $include, 'include' => $include,
'class' => $class, 'class' => $class,
); );
} }
/** /**
* Register a layer named $name, defined in file $include. If this layer comes from a plugin (as all currently do), * Register a layer named $name, defined in file $include. If this layer comes from a plugin (as all currently do),
* pass in the local plugin $directory and the $urltoroot relative url for that directory * pass in the local plugin $directory and the $urltoroot relative url for that directory
*/ */
function qa_register_layer($include, $name, $directory=QA_INCLUDE_DIR, $urltoroot=null) function qa_register_layer($include, $name, $directory = QA_INCLUDE_DIR, $urltoroot = null)
{ {
global $qa_layers; global $qa_layers;
$previous=@$qa_layers[$name]; $previous = @$qa_layers[$name];
if (isset($previous)) if (isset($previous)) {
qa_fatal_error('A layer named '.$name.' already exists. Please check there are no duplicate plugins. '. qa_fatal_error('A layer named ' . $name . ' already exists. Please check there are no duplicate plugins. ' .
"\n\nLayer 1: ".$previous['directory'].$previous['include']."\nLayer 2: ".$directory.$include); "\n\nLayer 1: " . $previous['directory'] . $previous['include'] . "\nLayer 2: " . $directory . $include);
}
$qa_layers[$name]=array( $qa_layers[$name] = array(
'directory' => $directory, 'directory' => $directory,
'urltoroot' => $urltoroot, 'urltoroot' => $urltoroot,
'include' => $include, 'include' => $include,
); );
} }
/** /**
* Register a file $include containing override functions. If this file comes from a plugin (as all currently do), * Register a file $include containing override functions. If this file comes from a plugin (as all currently do),
* pass in the local plugin $directory and the $urltoroot relative url for that directory * pass in the local plugin $directory and the $urltoroot relative url for that directory
*/ */
function qa_register_overrides($include, $directory=QA_INCLUDE_DIR, $urltoroot=null) function qa_register_overrides($include, $directory = QA_INCLUDE_DIR, $urltoroot = null)
{ {
global $qa_override_files_temp; global $qa_override_files_temp;
$qa_override_files_temp[] = array( $qa_override_files_temp[] = array(
'directory' => $directory, 'directory' => $directory,
'urltoroot' => $urltoroot, 'urltoroot' => $urltoroot,
'include' => $include 'include' => $include,
); );
} }
/** /**
* Register a set of language phrases, which should be accessed by the prefix $name/ in the qa_lang_*() functions. * Register a set of language phrases, which should be accessed by the prefix $name/ in the qa_lang_*() functions.
* Pass in the $pattern representing the PHP files that define these phrases, where * in the pattern is replaced with * Pass in the $pattern representing the PHP files that define these phrases, where * in the pattern is replaced with
* the language code (e.g. 'fr') and/or 'default'. These files should be formatted like Q2A's qa-lang-*.php files. * the language code (e.g. 'fr') and/or 'default'. These files should be formatted like Q2A's qa-lang-*.php files.
*/ */
function qa_register_phrases($pattern, $name) function qa_register_phrases($pattern, $name)
{ {
global $qa_lang_file_pattern; global $qa_lang_file_pattern;
if (file_exists(QA_INCLUDE_DIR.'lang/qa-lang-'.$name.'.php')) if (file_exists(QA_INCLUDE_DIR . 'lang/qa-lang-' . $name . '.php')) {
qa_fatal_error('The name "'.$name.'" for phrases is reserved and cannot be used by plugins.'."\n\nPhrases: ".$pattern); qa_fatal_error('The name "' . $name . '" for phrases is reserved and cannot be used by plugins.' . "\n\nPhrases: " . $pattern);
}
if (isset($qa_lang_file_pattern[$name]))
qa_fatal_error('A set of phrases named '.$name.' already exists. Please check there are no duplicate plugins. '.
"\n\nPhrases 1: ".$qa_lang_file_pattern[$name]."\nPhrases 2: ".$pattern);
$qa_lang_file_pattern[$name]=$pattern; if (isset($qa_lang_file_pattern[$name])) {
qa_fatal_error('A set of phrases named ' . $name . ' already exists. Please check there are no duplicate plugins. ' .
"\n\nPhrases 1: " . $qa_lang_file_pattern[$name] . "\nPhrases 2: " . $pattern);
} }
$qa_lang_file_pattern[$name] = $pattern;
}
// Function for registering varieties of Q2A modularity, which are (only) called from qa-plugin.php files // Function for registering varieties of Q2A modularity, which are (only) called from qa-plugin.php files
/** /**
* Register a plugin module of $type named $name, whose class named $class is defined in file $include (or null if no include necessary) * Register a plugin module of $type named $name, whose class named $class is defined in file $include (or null if no include necessary)
* This function relies on some global variable values and can only be called from a plugin's qa-plugin.php file * This function relies on some global variable values and can only be called from a plugin's qa-plugin.php file
*/ */
function qa_register_plugin_module($type, $include, $class, $name) function qa_register_plugin_module($type, $include, $class, $name)
{ {
global $qa_plugin_directory, $qa_plugin_urltoroot; global $qa_plugin_directory, $qa_plugin_urltoroot;
if (empty($qa_plugin_directory) || empty($qa_plugin_urltoroot)) if (empty($qa_plugin_directory) || empty($qa_plugin_urltoroot)) {
qa_fatal_error('qa_register_plugin_module() can only be called from a plugin qa-plugin.php file'); qa_fatal_error('qa_register_plugin_module() can only be called from a plugin qa-plugin.php file');
}
qa_register_module($type, $include, $class, $name, $qa_plugin_directory, $qa_plugin_urltoroot); qa_register_module($type, $include, $class, $name, $qa_plugin_directory, $qa_plugin_urltoroot);
} }
/** /**
* Register a plugin layer named $name, defined in file $include. Can only be called from a plugin's qa-plugin.php file * Register a plugin layer named $name, defined in file $include. Can only be called from a plugin's qa-plugin.php file
*/ */
function qa_register_plugin_layer($include, $name) function qa_register_plugin_layer($include, $name)
{ {
global $qa_plugin_directory, $qa_plugin_urltoroot; global $qa_plugin_directory, $qa_plugin_urltoroot;
if (empty($qa_plugin_directory) || empty($qa_plugin_urltoroot)) if (empty($qa_plugin_directory) || empty($qa_plugin_urltoroot)) {
qa_fatal_error('qa_register_plugin_layer() can only be called from a plugin qa-plugin.php file'); qa_fatal_error('qa_register_plugin_layer() can only be called from a plugin qa-plugin.php file');
}
qa_register_layer($include, $name, $qa_plugin_directory, $qa_plugin_urltoroot); qa_register_layer($include, $name, $qa_plugin_directory, $qa_plugin_urltoroot);
} }
/** /**
* Register a plugin file $include containing override functions. Can only be called from a plugin's qa-plugin.php file * Register a plugin file $include containing override functions. Can only be called from a plugin's qa-plugin.php file
*/ */
function qa_register_plugin_overrides($include) function qa_register_plugin_overrides($include)
{ {
global $qa_plugin_directory, $qa_plugin_urltoroot; global $qa_plugin_directory, $qa_plugin_urltoroot;
if (empty($qa_plugin_directory) || empty($qa_plugin_urltoroot)) if (empty($qa_plugin_directory) || empty($qa_plugin_urltoroot)) {
qa_fatal_error('qa_register_plugin_overrides() can only be called from a plugin qa-plugin.php file'); qa_fatal_error('qa_register_plugin_overrides() can only be called from a plugin qa-plugin.php file');
}
qa_register_overrides($include, $qa_plugin_directory, $qa_plugin_urltoroot); qa_register_overrides($include, $qa_plugin_directory, $qa_plugin_urltoroot);
} }
/** /**
* Register a file name $pattern within a plugin directory containing language phrases accessed by the prefix $name * Register a file name $pattern within a plugin directory containing language phrases accessed by the prefix $name
*/ */
function qa_register_plugin_phrases($pattern, $name) function qa_register_plugin_phrases($pattern, $name)
{ {
global $qa_plugin_directory, $qa_plugin_urltoroot; global $qa_plugin_directory, $qa_plugin_urltoroot;
if (empty($qa_plugin_directory) || empty($qa_plugin_urltoroot)) if (empty($qa_plugin_directory) || empty($qa_plugin_urltoroot)) {
qa_fatal_error('qa_register_plugin_phrases() can only be called from a plugin qa-plugin.php file'); qa_fatal_error('qa_register_plugin_phrases() can only be called from a plugin qa-plugin.php file');
qa_register_phrases($qa_plugin_directory.$pattern, $name);
} }
qa_register_phrases($qa_plugin_directory . $pattern, $name);
}
// Low-level functions used throughout Q2A // Low-level functions used throughout Q2A
/** /**
* Calls eval() on the PHP code in $eval which came from the file $filename. It supplements PHP's regular error reporting by * Calls eval() on the PHP code in $eval which came from the file $filename. It supplements PHP's regular error reporting by
* displaying/logging (as appropriate) the original source filename, if an error occurred when evaluating the code. * displaying/logging (as appropriate) the original source filename, if an error occurred when evaluating the code.
*/ */
function qa_eval_from_file($eval, $filename) function qa_eval_from_file($eval, $filename)
{ {
// could also use ini_set('error_append_string') but apparently it doesn't work for errors logged on disk // could also use ini_set('error_append_string') but apparently it doesn't work for errors logged on disk
global $php_errormsg; global $php_errormsg;
$oldtrackerrors=@ini_set('track_errors', 1); $oldtrackerrors = @ini_set('track_errors', 1);
$php_errormsg=null; $php_errormsg = null;
eval('?'.'>'.$eval); eval('?' . '>' . $eval);
if (strlen($php_errormsg)) { if (strlen($php_errormsg)) {
switch (strtolower(@ini_get('display_errors'))) { switch (strtolower(@ini_get('display_errors'))) {
case 'on': case '1': case 'yes': case 'true': case 'stdout': case 'stderr': case 'on':
echo ' of '.qa_html($filename)."\n"; case '1':
case 'yes':
case 'true':
case 'stdout':
case 'stderr':
echo ' of ' . qa_html($filename) . "\n";
break; break;
} }
@error_log('PHP Question2Answer more info: '.$php_errormsg." in eval()'d code from ".qa_html($filename)); @error_log('PHP Question2Answer more info: ' . $php_errormsg . " in eval()'d code from " . qa_html($filename));
} }
@ini_set('track_errors', $oldtrackerrors); @ini_set('track_errors', $oldtrackerrors);
} }
/** /**
* Call $function with the arguments in the $args array (doesn't work with call-by-reference functions) * Call $function with the arguments in the $args array (doesn't work with call-by-reference functions)
*/ */
function qa_call($function, $args) function qa_call($function, $args)
{ {
// call_user_func_array(...) is very slow, so we break out most common cases first // call_user_func_array(...) is very slow, so we break out most common cases first
switch (count($args)) { switch (count($args)) {
case 0: case 0:
...@@ -650,18 +675,18 @@ ...@@ -650,18 +675,18 @@
} }
return call_user_func_array($function, $args); return call_user_func_array($function, $args);
} }
/** /**
* Determines whether a function is to be overridden by a plugin. But if the function is being called with * Determines whether a function is to be overridden by a plugin. But if the function is being called with
* the _base suffix, any override will be bypassed due to $qa_direct. * the _base suffix, any override will be bypassed due to $qa_direct.
* *
* @param string $function The function to override * @param string $function The function to override
* @return string|null The name of the overriding function (of the form `qa_functionname_override_1_in_filename`) * @return string|null The name of the overriding function (of the form `qa_functionname_override_1_in_filename`)
*/ */
function qa_to_override($function) function qa_to_override($function)
{ {
global $qa_overrides, $qa_direct; global $qa_overrides, $qa_direct;
// handle most common case first // handle most common case first
...@@ -669,7 +694,7 @@ ...@@ -669,7 +694,7 @@
return null; return null;
} }
if (strpos($function, '_override_')!==false) { if (strpos($function, '_override_') !== false) {
qa_fatal_error('Override functions should not be calling qa_to_override()!'); qa_fatal_error('Override functions should not be calling qa_to_override()!');
} }
...@@ -679,43 +704,46 @@ ...@@ -679,43 +704,46 @@
} }
return $qa_overrides[$function]; return $qa_overrides[$function];
} }
/** /**
* Call the function which immediately overrides $function with the arguments in the $args array * Call the function which immediately overrides $function with the arguments in the $args array
*/ */
function qa_call_override($function, $args) function qa_call_override($function, $args)
{ {
global $qa_overrides; global $qa_overrides;
if (strpos($function, '_override_')!==false) if (strpos($function, '_override_') !== false) {
qa_fatal_error('Override functions should not be calling qa_call_override()!'); qa_fatal_error('Override functions should not be calling qa_call_override()!');
}
if (!function_exists($function.'_base')) // define the base function the first time that it's needed if (!function_exists($function . '_base')) {
eval('function '.$function.'_base() { global $qa_direct; $qa_direct[\''.$function.'\']=true; $args=func_get_args(); return qa_call(\''.$function.'\', $args); }'); // define the base function the first time that it's needed
eval('function ' . $function . '_base() { global $qa_direct; $qa_direct[\'' . $function . '\']=true; $args=func_get_args(); return qa_call(\'' . $function . '\', $args); }');
}
return qa_call($qa_overrides[$function], $args); return qa_call($qa_overrides[$function], $args);
} }
/** /**
* Exit PHP immediately after reporting a shutdown with $reason to any installed process modules * Exit PHP immediately after reporting a shutdown with $reason to any installed process modules
*/ */
function qa_exit($reason=null) function qa_exit($reason = null)
{ {
qa_report_process_stage('shutdown', $reason); qa_report_process_stage('shutdown', $reason);
$code = $reason === 'error' ? 1 : 0; $code = $reason === 'error' ? 1 : 0;
exit($code); exit($code);
} }
/** /**
* Display $message in the browser, write it to server error log, and then stop abruptly * Display $message in the browser, write it to server error log, and then stop abruptly
*/ */
function qa_fatal_error($message) function qa_fatal_error($message)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
echo 'Question2Answer fatal error:<p style="color: red">' . qa_html($message, true) . '</p>'; echo 'Question2Answer fatal error:<p style="color: red">' . qa_html($message, true) . '</p>';
...@@ -732,51 +760,51 @@ ...@@ -732,51 +760,51 @@
} }
qa_exit('error'); qa_exit('error');
} }
// Functions for listing, loading and getting info on modules // Functions for listing, loading and getting info on modules
/** /**
* Return an array with all registered modules' information * Return an array with all registered modules' information
*/ */
function qa_list_modules_info() function qa_list_modules_info()
{ {
global $qa_modules; global $qa_modules;
return $qa_modules; return $qa_modules;
} }
/** /**
* Return an array of all the module types for which at least one module has been registered * Return an array of all the module types for which at least one module has been registered
*/ */
function qa_list_module_types() function qa_list_module_types()
{ {
return array_keys(qa_list_modules_info()); return array_keys(qa_list_modules_info());
} }
/** /**
* Return a list of names of registered modules of $type * Return a list of names of registered modules of $type
*/ */
function qa_list_modules($type) function qa_list_modules($type)
{ {
$modules = qa_list_modules_info(); $modules = qa_list_modules_info();
return is_array(@$modules[$type]) ? array_keys($modules[$type]) : array(); return is_array(@$modules[$type]) ? array_keys($modules[$type]) : array();
} }
/** /**
* Return an array containing information about the module of $type named $name * Return an array containing information about the module of $type named $name
*/ */
function qa_get_module_info($type, $name) function qa_get_module_info($type, $name)
{ {
$modules = qa_list_modules_info(); $modules = qa_list_modules_info();
return @$modules[$type][$name]; return @$modules[$type][$name];
} }
/** /**
* Return an instantiated class for module of $type named $name, whose functions can be called, or null if it doesn't exist * Return an instantiated class for module of $type named $name, whose functions can be called, or null if it doesn't exist
*/ */
function qa_load_module($type, $name) function qa_load_module($type, $name)
{ {
global $qa_modules; global $qa_modules;
$module = @$qa_modules[$type][$name]; $module = @$qa_modules[$type][$name];
...@@ -786,13 +814,13 @@ ...@@ -786,13 +814,13 @@
return $module['object']; return $module['object'];
if (strlen(@$module['include'])) if (strlen(@$module['include']))
require_once $module['directory'].$module['include']; require_once $module['directory'] . $module['include'];
if (strlen(@$module['class'])) { if (strlen(@$module['class'])) {
$object = new $module['class']; $object = new $module['class'];
if (method_exists($object, 'load_module')) if (method_exists($object, 'load_module'))
$object->load_module($module['directory'], qa_path_to_root().$module['urltoroot'], $type, $name); $object->load_module($module['directory'], qa_path_to_root() . $module['urltoroot'], $type, $name);
$qa_modules[$type][$name]['object'] = $object; $qa_modules[$type][$name]['object'] = $object;
return $object; return $object;
...@@ -800,14 +828,14 @@ ...@@ -800,14 +828,14 @@
} }
return null; return null;
} }
/** /**
* Return an array of instantiated clases for modules which have defined $method * Return an array of instantiated clases for modules which have defined $method
* (all modules are loaded but not included in the returned array) * (all modules are loaded but not included in the returned array)
*/ */
function qa_load_all_modules_with($method) function qa_load_all_modules_with($method)
{ {
$modules = array(); $modules = array();
$regmodules = qa_list_modules_info(); $regmodules = qa_list_modules_info();
...@@ -822,14 +850,14 @@ ...@@ -822,14 +850,14 @@
} }
return $modules; return $modules;
} }
/** /**
* Return an array of instantiated clases for modules of $type which have defined $method * Return an array of instantiated clases for modules of $type which have defined $method
* (other modules of that type are also loaded but not included in the returned array) * (other modules of that type are also loaded but not included in the returned array)
*/ */
function qa_load_modules_with($type, $method) function qa_load_modules_with($type, $method)
{ {
$modules = array(); $modules = array();
$trynames = qa_list_modules($type); $trynames = qa_list_modules($type);
...@@ -842,45 +870,45 @@ ...@@ -842,45 +870,45 @@
} }
return $modules; return $modules;
} }
// HTML and Javascript escaping and sanitization // HTML and Javascript escaping and sanitization
/** /**
* Return HTML representation of $string, work well with blocks of text if $multiline is true * Return HTML representation of $string, work well with blocks of text if $multiline is true
*/ */
function qa_html($string, $multiline=false) function qa_html($string, $multiline = false)
{ {
$html=htmlspecialchars((string)$string); $html = htmlspecialchars((string)$string);
if ($multiline) { if ($multiline) {
$html=preg_replace('/\r\n?/', "\n", $html); $html = preg_replace('/\r\n?/', "\n", $html);
$html=preg_replace('/(?<=\s) /', '&nbsp;', $html); $html = preg_replace('/(?<=\s) /', '&nbsp;', $html);
$html=str_replace("\t", '&nbsp; &nbsp; ', $html); $html = str_replace("\t", '&nbsp; &nbsp; ', $html);
$html=nl2br($html); $html = nl2br($html);
} }
return $html; return $html;
} }
/** /**
* Return $html after ensuring it is safe, i.e. removing Javascripts and the like - uses htmLawed library * Return $html after ensuring it is safe, i.e. removing Javascripts and the like - uses htmLawed library
* Links open in a new window if $linksnewwindow is true. Set $storage to true if sanitization is for * Links open in a new window if $linksnewwindow is true. Set $storage to true if sanitization is for
* storing in the database, rather than immediate display to user - some think this should be less strict. * storing in the database, rather than immediate display to user - some think this should be less strict.
*/ */
function qa_sanitize_html($html, $linksnewwindow=false, $storage=false) function qa_sanitize_html($html, $linksnewwindow=false, $storage=false)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once 'vendor/htmLawed.php'; require_once 'vendor/htmLawed.php';
global $qa_sanitize_html_newwindow; global $qa_sanitize_html_newwindow;
$qa_sanitize_html_newwindow=$linksnewwindow; $qa_sanitize_html_newwindow = $linksnewwindow;
$safe=htmLawed($html, array( $safe = htmLawed($html, array(
'safe' => 1, 'safe' => 1,
'elements' => '*+embed+object-form', 'elements' => '*+embed+object-form',
'schemes' => 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; *:file, http, https; style: !; classid:clsid', 'schemes' => 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; *:file, http, https; style: !; classid:clsid',
...@@ -890,166 +918,166 @@ ...@@ -890,166 +918,166 @@
)); ));
return $safe; return $safe;
} }
/** /**
* htmLawed hook function used to process tags in qa_sanitize_html(...) * htmLawed hook function used to process tags in qa_sanitize_html(...)
*/ */
function qa_sanitize_html_hook_tag($element, $attributes=null) function qa_sanitize_html_hook_tag($element, $attributes = null)
{ {
global $qa_sanitize_html_newwindow; global $qa_sanitize_html_newwindow;
if (!isset($attributes)) // it's a closing tag if (!isset($attributes)) // it's a closing tag
return '</'.$element.'>'; return '</' . $element . '>';
if ( ($element=='param') && (trim(strtolower(@$attributes['name']))=='allowscriptaccess') ) if (($element == 'param') && (trim(strtolower(@$attributes['name'])) == 'allowscriptaccess'))
$attributes['name']='allowscriptaccess_denied'; $attributes['name'] = 'allowscriptaccess_denied';
if ($element=='embed') if ($element == 'embed')
unset($attributes['allowscriptaccess']); unset($attributes['allowscriptaccess']);
if (($element=='a') && isset($attributes['href']) && $qa_sanitize_html_newwindow) if (($element == 'a') && isset($attributes['href']) && $qa_sanitize_html_newwindow)
$attributes['target']='_blank'; $attributes['target'] = '_blank';
$html='<'.$element; $html = '<' . $element;
foreach ($attributes as $key => $value) foreach ($attributes as $key => $value)
$html.=' '.$key.'="'.$value.'"'; $html .= ' ' . $key . '="' . $value . '"';
return $html.'>'; return $html . '>';
} }
/** /**
* Return XML representation of $string, which is similar to HTML but ASCII control characters are also disallowed * Return XML representation of $string, which is similar to HTML but ASCII control characters are also disallowed
*/ */
function qa_xml($string) function qa_xml($string)
{ {
return htmlspecialchars(preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F]/', '', (string)$string)); return htmlspecialchars(preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F]/', '', (string)$string));
} }
/** /**
* Return JavaScript representation of $value, putting in quotes if non-numeric or if $forcequotes is true. In the * Return JavaScript representation of $value, putting in quotes if non-numeric or if $forcequotes is true. In the
* case of boolean values they are returned as the appropriate true or false string * case of boolean values they are returned as the appropriate true or false string
*/ */
function qa_js($value, $forcequotes=false) function qa_js($value, $forcequotes = false)
{ {
$boolean = is_bool($value); $boolean = is_bool($value);
if ($boolean) if ($boolean)
$value = $value ? 'true' : 'false'; $value = $value ? 'true' : 'false';
if ((is_numeric($value) || $boolean) && !$forcequotes) if ((is_numeric($value) || $boolean) && !$forcequotes)
return $value; return $value;
else else
return "'".strtr($value, array( return "'" . strtr($value, array(
"'" => "\\'", "'" => "\\'",
'/' => '\\/', '/' => '\\/',
'\\' => '\\\\', '\\' => '\\\\',
"\n" => "\\n", "\n" => "\\n",
"\r" => "\\n", "\r" => "\\n",
))."'"; )) . "'";
} }
// Finding out more about the current request // Finding out more about the current request
/** /**
* Inform Q2A that the current request is $request (slash-separated, independent of the url scheme chosen), * Inform Q2A that the current request is $request (slash-separated, independent of the url scheme chosen),
* that the relative path to the Q2A root apperas to be $relativeroot, and the url scheme appears to be $usedformat * that the relative path to the Q2A root apperas to be $relativeroot, and the url scheme appears to be $usedformat
*/ */
function qa_set_request($request, $relativeroot, $usedformat=null) function qa_set_request($request, $relativeroot, $usedformat=null)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_request, $qa_root_url_relative, $qa_used_url_format; global $qa_request, $qa_root_url_relative, $qa_used_url_format;
$qa_request=$request; $qa_request = $request;
$qa_root_url_relative=$relativeroot; $qa_root_url_relative = $relativeroot;
$qa_used_url_format=$usedformat; $qa_used_url_format = $usedformat;
} }
/** /**
* Returns the current Q2A request (slash-separated, independent of the url scheme chosen) * Returns the current Q2A request (slash-separated, independent of the url scheme chosen)
*/ */
function qa_request() function qa_request()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_request; global $qa_request;
return $qa_request; return $qa_request;
} }
/** /**
* Returns the indexed $part (as separated by slashes) of the current Q2A request, or null if it doesn't exist * Returns the indexed $part (as separated by slashes) of the current Q2A request, or null if it doesn't exist
*/ */
function qa_request_part($part) function qa_request_part($part)
{ {
$parts=explode('/', qa_request()); $parts = explode('/', qa_request());
return @$parts[$part]; return @$parts[$part];
} }
/** /**
* Returns an array of parts (as separated by slashes) of the current Q2A request, starting at part $start * Returns an array of parts (as separated by slashes) of the current Q2A request, starting at part $start
*/ */
function qa_request_parts($start=0) function qa_request_parts($start = 0)
{ {
return array_slice(explode('/', qa_request()), $start); return array_slice(explode('/', qa_request()), $start);
} }
/** /**
* Return string for incoming GET/POST/COOKIE value, stripping slashes if appropriate * Return string for incoming GET/POST/COOKIE value, stripping slashes if appropriate
*/ */
function qa_gpc_to_string($string) function qa_gpc_to_string($string)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return get_magic_quotes_gpc() ? stripslashes($string) : $string; return get_magic_quotes_gpc() ? stripslashes($string) : $string;
} }
/** /**
* Return string with slashes added, if appropriate for later removal by qa_gpc_to_string() * Return string with slashes added, if appropriate for later removal by qa_gpc_to_string()
*/ */
function qa_string_to_gpc($string) function qa_string_to_gpc($string)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return get_magic_quotes_gpc() ? addslashes($string) : $string; return get_magic_quotes_gpc() ? addslashes($string) : $string;
} }
/** /**
* Return string for incoming GET field, or null if it's not defined * Return string for incoming GET field, or null if it's not defined
*/ */
function qa_get($field) function qa_get($field)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return isset($_GET[$field]) ? qa_gpc_to_string($_GET[$field]) : null; return isset($_GET[$field]) ? qa_gpc_to_string($_GET[$field]) : null;
} }
/** /**
* Return string for incoming POST field, or null if it's not defined. * Return string for incoming POST field, or null if it's not defined.
* While we're at it, trim() surrounding white space and converted to Unix line endings. * While we're at it, trim() surrounding white space and converted to Unix line endings.
*/ */
function qa_post_text($field) function qa_post_text($field)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return isset($_POST[$field]) ? preg_replace('/\r\n?/', "\n", trim(qa_gpc_to_string($_POST[$field]))) : null; return isset($_POST[$field]) ? preg_replace('/\r\n?/', "\n", trim(qa_gpc_to_string($_POST[$field]))) : null;
} }
/** /**
* Return an array for incoming POST field, or null if it's not an array or not defined. * Return an array for incoming POST field, or null if it's not an array or not defined.
* While we're at it, trim() surrounding white space for each value and convert them to Unix line endings. * While we're at it, trim() surrounding white space for each value and convert them to Unix line endings.
*/ */
function qa_post_array($field) function qa_post_array($field)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
if (!isset($_POST[$field]) || !is_array($_POST[$field])) { if (!isset($_POST[$field]) || !is_array($_POST[$field])) {
...@@ -1061,41 +1089,41 @@ ...@@ -1061,41 +1089,41 @@
$result[$key] = preg_replace('/\r\n?/', "\n", trim(qa_gpc_to_string($value))); $result[$key] = preg_replace('/\r\n?/', "\n", trim(qa_gpc_to_string($value)));
return $result; return $result;
} }
/** /**
* Return true if form button $name was clicked (as type=submit/image) to create this page request, or if a * Return true if form button $name was clicked (as type=submit/image) to create this page request, or if a
* simulated click was sent for the button (via 'qa_click' POST field) * simulated click was sent for the button (via 'qa_click' POST field)
*/ */
function qa_clicked($name) function qa_clicked($name)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return isset($_POST[$name]) || isset($_POST[$name.'_x']) || (qa_post_text('qa_click')==$name); return isset($_POST[$name]) || isset($_POST[$name . '_x']) || (qa_post_text('qa_click') == $name);
} }
/** /**
* Determine the remote IP address of the user accessing the site. * Determine the remote IP address of the user accessing the site.
* @return mixed String representing IP if it's available, or null otherwise. * @return mixed String representing IP if it's available, or null otherwise.
*/ */
function qa_remote_ip_address() function qa_remote_ip_address()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null; return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null;
} }
/** /**
* Checks whether an HTTP request has exceeded the post_max_size PHP variable. This happens whenever an HTTP request * Checks whether an HTTP request has exceeded the post_max_size PHP variable. This happens whenever an HTTP request
* is too big to be properly processed by PHP, usually because there is an attachment in the HTTP request. A warning * is too big to be properly processed by PHP, usually because there is an attachment in the HTTP request. A warning
* is added to the server's log displaying the size of the file that triggered this situation. It is important to note * is added to the server's log displaying the size of the file that triggered this situation. It is important to note
* that whenever this happens the $_POST and $_FILES superglobals are empty. * that whenever this happens the $_POST and $_FILES superglobals are empty.
*/ */
function qa_post_limit_exceeded() function qa_post_limit_exceeded()
{ {
if (in_array($_SERVER['REQUEST_METHOD'], array('POST', 'PUT')) && empty($_POST) && empty($_FILES)) { if (in_array($_SERVER['REQUEST_METHOD'], array('POST', 'PUT')) && empty($_POST) && empty($_FILES)) {
$postmaxsize = ini_get('post_max_size'); // Gets the current post_max_size configuration $postmaxsize = ini_get('post_max_size'); // Gets the current post_max_size configuration
$unit = substr($postmaxsize, -1); $unit = substr($postmaxsize, -1);
...@@ -1106,18 +1134,18 @@ ...@@ -1106,18 +1134,18 @@
$postmaxsize = convert_to_bytes($unit, $postmaxsize); $postmaxsize = convert_to_bytes($unit, $postmaxsize);
return $_SERVER['CONTENT_LENGTH'] > $postmaxsize; return $_SERVER['CONTENT_LENGTH'] > $postmaxsize;
} }
} }
/** /**
* Turns a numeric value and a unit (g/m/k) into bytes * Turns a numeric value and a unit (g/m/k) into bytes
* @param string $unit One of 'g', 'm', 'k'. It is case insensitive * @param string $unit One of 'g', 'm', 'k'. It is case insensitive
* @param int $value The value to turn into bytes * @param int $value The value to turn into bytes
* @return int The amount of bytes the unit and the value represent. If the unit is not one of 'g', 'm' or 'k' then * @return int The amount of bytes the unit and the value represent. If the unit is not one of 'g', 'm' or 'k' then
* the original value is returned * the original value is returned
*/ */
function convert_to_bytes($unit, $value) function convert_to_bytes($unit, $value)
{ {
switch (strtolower($unit)) { switch (strtolower($unit)) {
case 'g': case 'g':
return $value * 1073741824; return $value * 1073741824;
...@@ -1128,78 +1156,78 @@ ...@@ -1128,78 +1156,78 @@
default: default:
return $value; return $value;
} }
} }
/** /**
* Whether we are responding to an HTTP GET request * Whether we are responding to an HTTP GET request
* @return bool True if the request is GET * @return bool True if the request is GET
*/ */
function qa_is_http_get() function qa_is_http_get()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return $_SERVER['REQUEST_METHOD'] === 'GET'; return $_SERVER['REQUEST_METHOD'] === 'GET';
} }
/** /**
* Return true if we are responding to an HTTP POST request * Return true if we are responding to an HTTP POST request
*/ */
function qa_is_http_post() function qa_is_http_post()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return $_SERVER['REQUEST_METHOD'] === 'POST' || !empty($_POST); return $_SERVER['REQUEST_METHOD'] === 'POST' || !empty($_POST);
} }
/** /**
* Return true if we appear to be responding to a secure HTTP request (but hard to be sure) * Return true if we appear to be responding to a secure HTTP request (but hard to be sure)
*/ */
function qa_is_https_probably() function qa_is_https_probably()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return (@$_SERVER['HTTPS'] && ($_SERVER['HTTPS']!='off')) || (@$_SERVER['SERVER_PORT']==443); return (@$_SERVER['HTTPS'] && ($_SERVER['HTTPS'] != 'off')) || (@$_SERVER['SERVER_PORT'] == 443);
} }
/** /**
* Return true if it appears the page request is coming from a human using a web browser, rather than a search engine * Return true if it appears the page request is coming from a human using a web browser, rather than a search engine
* or other bot. Based on a whitelist of terms in user agents, this can easily be tricked by a scraper or bad bot. * or other bot. Based on a whitelist of terms in user agents, this can easily be tricked by a scraper or bad bot.
*/ */
function qa_is_human_probably() function qa_is_human_probably()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'util/string.php'; require_once QA_INCLUDE_DIR . 'util/string.php';
$useragent=@$_SERVER['HTTP_USER_AGENT']; $useragent = @$_SERVER['HTTP_USER_AGENT'];
return (strlen($useragent)==0) || qa_string_matches_one($useragent, array( return (strlen($useragent) == 0) || qa_string_matches_one($useragent, array(
'MSIE', 'Firefox', 'Chrome', 'Safari', 'Opera', 'Gecko', 'MIDP', 'PLAYSTATION', 'Teleca', 'MSIE', 'Firefox', 'Chrome', 'Safari', 'Opera', 'Gecko', 'MIDP', 'PLAYSTATION', 'Teleca',
'BlackBerry', 'UP.Browser', 'Polaris', 'MAUI_WAP_Browser', 'iPad', 'iPhone', 'iPod' 'BlackBerry', 'UP.Browser', 'Polaris', 'MAUI_WAP_Browser', 'iPad', 'iPhone', 'iPod',
)); ));
} }
/** /**
* Return true if it appears that the page request is coming from a mobile client rather than a desktop/laptop web browser * Return true if it appears that the page request is coming from a mobile client rather than a desktop/laptop web browser
*/ */
function qa_is_mobile_probably() function qa_is_mobile_probably()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR.'util/string.php'; require_once QA_INCLUDE_DIR . 'util/string.php';
// inspired by: http://dangerousprototypes.com/docs/PhpBB3_MOD:_Replacement_mobile_browser_detection_for_mobile_themes // inspired by: http://dangerousprototypes.com/docs/PhpBB3_MOD:_Replacement_mobile_browser_detection_for_mobile_themes
$loweragent=strtolower(@$_SERVER['HTTP_USER_AGENT']); $loweragent = strtolower(@$_SERVER['HTTP_USER_AGENT']);
if (strpos($loweragent, 'ipad')!==false) // consider iPad as desktop if (strpos($loweragent, 'ipad') !== false) // consider iPad as desktop
return false; return false;
$mobileheaders=array('HTTP_X_OPERAMINI_PHONE', 'HTTP_X_WAP_PROFILE', 'HTTP_PROFILE'); $mobileheaders = array('HTTP_X_OPERAMINI_PHONE', 'HTTP_X_WAP_PROFILE', 'HTTP_PROFILE');
foreach ($mobileheaders as $header) foreach ($mobileheaders as $header)
if (isset($_SERVER[$header])) if (isset($_SERVER[$header]))
...@@ -1208,27 +1236,27 @@ ...@@ -1208,27 +1236,27 @@
if (qa_string_matches_one($loweragent, array( if (qa_string_matches_one($loweragent, array(
'android', 'phone', 'mobile', 'windows ce', 'palm', ' mobi', 'wireless', 'blackberry', 'opera mini', 'symbian', 'android', 'phone', 'mobile', 'windows ce', 'palm', ' mobi', 'wireless', 'blackberry', 'opera mini', 'symbian',
'nokia', 'samsung', 'ericsson,', 'vodafone/', 'kindle', 'ipod', 'wap1.', 'wap2.', 'sony', 'sanyo', 'sharp', 'nokia', 'samsung', 'ericsson,', 'vodafone/', 'kindle', 'ipod', 'wap1.', 'wap2.', 'sony', 'sanyo', 'sharp',
'panasonic', 'philips', 'pocketpc', 'avantgo', 'blazer', 'ipaq', 'up.browser', 'up.link', 'mmp', 'smartphone', 'midp' 'panasonic', 'philips', 'pocketpc', 'avantgo', 'blazer', 'ipaq', 'up.browser', 'up.link', 'mmp', 'smartphone', 'midp',
))) )))
return true; return true;
return qa_string_matches_one(strtolower(@$_SERVER['HTTP_ACCEPT']), array( return qa_string_matches_one(strtolower(@$_SERVER['HTTP_ACCEPT']), array(
'application/vnd.wap.xhtml+xml', 'text/vnd.wap.wml' 'application/vnd.wap.xhtml+xml', 'text/vnd.wap.wml',
)); ));
} }
// Language phrase support // Language phrase support
/** /**
* Return the translated string for $identifier, unless we're using external translation logic. * Return the translated string for $identifier, unless we're using external translation logic.
* This will retrieve the 'site_language' option so make sure you've already loaded/set that if * This will retrieve the 'site_language' option so make sure you've already loaded/set that if
* loading an option now will cause a problem (see issue in qa_default_option()). The part of * loading an option now will cause a problem (see issue in qa_default_option()). The part of
* $identifier before the slash (/) replaces the * in the qa-lang-*.php file references, and the * $identifier before the slash (/) replaces the * in the qa-lang-*.php file references, and the
* part after the / is the key of the array element to be taken from that file's returned result. * part after the / is the key of the array element to be taken from that file's returned result.
*/ */
function qa_lang($identifier) function qa_lang($identifier)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_lang_file_pattern, $qa_phrases_full; global $qa_lang_file_pattern, $qa_phrases_full;
...@@ -1243,7 +1271,7 @@ ...@@ -1243,7 +1271,7 @@
if (isset($qa_lang_file_pattern[$group])) if (isset($qa_lang_file_pattern[$group]))
$include = str_replace('*', 'default', $qa_lang_file_pattern[$group]); $include = str_replace('*', 'default', $qa_lang_file_pattern[$group]);
else else
$include = QA_INCLUDE_DIR.'lang/qa-lang-'.$group.'.php'; $include = QA_INCLUDE_DIR . 'lang/qa-lang-' . $group . '.php';
$qa_phrases_full[$group] = is_file($include) ? (array)(include_once $include) : array(); $qa_phrases_full[$group] = is_file($include) ? (array)(include_once $include) : array();
...@@ -1253,14 +1281,14 @@ ...@@ -1253,14 +1281,14 @@
if (isset($qa_lang_file_pattern[$group])) if (isset($qa_lang_file_pattern[$group]))
$include = str_replace('*', $languagecode, $qa_lang_file_pattern[$group]); $include = str_replace('*', $languagecode, $qa_lang_file_pattern[$group]);
else else
$include = QA_LANG_DIR.$languagecode.'/qa-lang-'.$group.'.php'; $include = QA_LANG_DIR . $languagecode . '/qa-lang-' . $group . '.php';
$phrases = is_file($include) ? (array)(include $include) : array(); $phrases = is_file($include) ? (array)(include $include) : array();
$qa_phrases_full[$group] = array_merge($qa_phrases_full[$group], $phrases); $qa_phrases_full[$group] = array_merge($qa_phrases_full[$group], $phrases);
} }
// add any custom phrases from qa-lang/custom/ // add any custom phrases from qa-lang/custom/
$include = QA_LANG_DIR.'custom/qa-lang-'.$group.'.php'; $include = QA_LANG_DIR . 'custom/qa-lang-' . $group . '.php';
$phrases = is_file($include) ? (array)(include $include) : array(); $phrases = is_file($include) ? (array)(include $include) : array();
$qa_phrases_full[$group] = array_merge($qa_phrases_full[$group], $phrases); $qa_phrases_full[$group] = array_merge($qa_phrases_full[$group], $phrases);
...@@ -1268,180 +1296,182 @@ ...@@ -1268,180 +1296,182 @@
return $qa_phrases_full[$group][$label]; return $qa_phrases_full[$group][$label];
} }
return '['.$identifier.']'; // as a last resort, return the identifier to help in development return '[' . $identifier . ']'; // as a last resort, return the identifier to help in development
} }
/** /**
* Return the translated string for $identifier, with $symbol substituted for $textparam * Return the translated string for $identifier, with $symbol substituted for $textparam
*/ */
function qa_lang_sub($identifier, $textparam, $symbol='^') function qa_lang_sub($identifier, $textparam, $symbol = '^')
{ {
return str_replace($symbol, $textparam, qa_lang($identifier)); return str_replace($symbol, $textparam, qa_lang($identifier));
} }
/** /**
* Return the translated string for $identifier, converted to HTML * Return the translated string for $identifier, converted to HTML
*/ */
function qa_lang_html($identifier) function qa_lang_html($identifier)
{ {
return qa_html(qa_lang($identifier)); return qa_html(qa_lang($identifier));
} }
/** /**
* Return the translated string for $identifier converted to HTML, with $symbol *then* substituted for $htmlparam * Return the translated string for $identifier converted to HTML, with $symbol *then* substituted for $htmlparam
*/ */
function qa_lang_html_sub($identifier, $htmlparam, $symbol='^') function qa_lang_html_sub($identifier, $htmlparam, $symbol = '^')
{ {
return str_replace($symbol, $htmlparam, qa_lang_html($identifier)); return str_replace($symbol, $htmlparam, qa_lang_html($identifier));
} }
/** /**
* Return an array containing the translated string for $identifier converted to HTML, then split into three, * Return an array containing the translated string for $identifier converted to HTML, then split into three,
* with $symbol substituted for $htmlparam in the 'data' element, and obvious 'prefix' and 'suffix' elements * with $symbol substituted for $htmlparam in the 'data' element, and obvious 'prefix' and 'suffix' elements
*/ */
function qa_lang_html_sub_split($identifier, $htmlparam, $symbol='^') function qa_lang_html_sub_split($identifier, $htmlparam, $symbol = '^')
{ {
$html=qa_lang_html($identifier); $html = qa_lang_html($identifier);
$symbolpos=strpos($html, $symbol); $symbolpos = strpos($html, $symbol);
if (!is_numeric($symbolpos)) if (!is_numeric($symbolpos))
qa_fatal_error('Missing '.$symbol.' in language string '.$identifier); qa_fatal_error('Missing ' . $symbol . ' in language string ' . $identifier);
return array( return array(
'prefix' => substr($html, 0, $symbolpos), 'prefix' => substr($html, 0, $symbolpos),
'data' => $htmlparam, 'data' => $htmlparam,
'suffix' => substr($html, $symbolpos+1), 'suffix' => substr($html, $symbolpos + 1),
); );
} }
// Request and path generation // Request and path generation
/** /**
* Return the relative path to the Q2A root (if it was previously set by qa_set_request()) * Return the relative path to the Q2A root (if it was previously set by qa_set_request())
*/ */
function qa_path_to_root() function qa_path_to_root()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_root_url_relative; global $qa_root_url_relative;
return $qa_root_url_relative; return $qa_root_url_relative;
} }
/** /**
* Return an array of mappings of Q2A requests, as defined in the qa-config.php file * Return an array of mappings of Q2A requests, as defined in the qa-config.php file
*/ */
function qa_get_request_map() function qa_get_request_map()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_request_map; global $qa_request_map;
return $qa_request_map; return $qa_request_map;
} }
/** /**
* Return the relative URI path for $request, with optional parameters $params and $anchor. * Return the relative URI path for $request, with optional parameters $params and $anchor.
* Slashes in $request will not be urlencoded, but any other characters will. * Slashes in $request will not be urlencoded, but any other characters will.
* If $neaturls is set, use that, otherwise retrieve the option. If $rooturl is set, take * If $neaturls is set, use that, otherwise retrieve the option. If $rooturl is set, take
* that as the root of the Q2A site, otherwise use path to root which was set elsewhere. * that as the root of the Q2A site, otherwise use path to root which was set elsewhere.
*/ */
function qa_path($request, $params=null, $rooturl=null, $neaturls=null, $anchor=null) function qa_path($request, $params=null, $rooturl=null, $neaturls=null, $anchor=null)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
if (!isset($neaturls)) { if (!isset($neaturls)) {
require_once QA_INCLUDE_DIR.'app/options.php'; require_once QA_INCLUDE_DIR . 'app/options.php';
$neaturls=qa_opt('neat_urls'); $neaturls = qa_opt('neat_urls');
} }
if (!isset($rooturl)) if (!isset($rooturl))
$rooturl=qa_path_to_root(); $rooturl = qa_path_to_root();
$url=$rooturl.( (empty($rooturl) || (substr($rooturl, -1)=='/') ) ? '' : '/'); $url = $rooturl . ((empty($rooturl) || (substr($rooturl, -1) == '/')) ? '' : '/');
$paramsextra=''; $paramsextra = '';
$requestparts=explode('/', $request); $requestparts = explode('/', $request);
$pathmap=qa_get_request_map(); $pathmap = qa_get_request_map();
if (isset($pathmap[$requestparts[0]])) { if (isset($pathmap[$requestparts[0]])) {
$newpart=$pathmap[$requestparts[0]]; $newpart = $pathmap[$requestparts[0]];
if (strlen($newpart)) if (strlen($newpart))
$requestparts[0]=$newpart; $requestparts[0] = $newpart;
elseif (count($requestparts)==1) elseif (count($requestparts) == 1)
array_shift($requestparts); array_shift($requestparts);
} }
foreach ($requestparts as $index => $requestpart) foreach ($requestparts as $index => $requestpart) {
$requestparts[$index]=urlencode($requestpart); $requestparts[$index] = urlencode($requestpart);
$requestpath=implode('/', $requestparts); }
$requestpath = implode('/', $requestparts);
switch ($neaturls) { switch ($neaturls) {
case QA_URL_FORMAT_INDEX: case QA_URL_FORMAT_INDEX:
if (!empty($request)) if (!empty($request))
$url.='index.php/'.$requestpath; $url .= 'index.php/' . $requestpath;
break; break;
case QA_URL_FORMAT_NEAT: case QA_URL_FORMAT_NEAT:
$url.=$requestpath; $url .= $requestpath;
break; break;
case QA_URL_FORMAT_PARAM: case QA_URL_FORMAT_PARAM:
if (!empty($request)) if (!empty($request))
$paramsextra='?qa='.$requestpath; $paramsextra = '?qa=' . $requestpath;
break; break;
default: default:
$url.='index.php'; $url .= 'index.php';
case QA_URL_FORMAT_PARAMS: case QA_URL_FORMAT_PARAMS:
if (!empty($request)) if (!empty($request)) {
foreach ($requestparts as $partindex => $requestpart) foreach ($requestparts as $partindex => $requestpart)
$paramsextra.=(strlen($paramsextra) ? '&' : '?').'qa'.($partindex ? ('_'.$partindex) : '').'='.$requestpart; $paramsextra .= (strlen($paramsextra) ? '&' : '?') . 'qa' . ($partindex ? ('_' . $partindex) : '') . '=' . $requestpart;
}
break; break;
} }
if (isset($params)) if (isset($params))
foreach ($params as $key => $value) foreach ($params as $key => $value)
$paramsextra.=(strlen($paramsextra) ? '&' : '?').urlencode($key).'='.urlencode((string)$value); $paramsextra .= (strlen($paramsextra) ? '&' : '?') . urlencode($key) . '=' . urlencode((string)$value);
return $url.$paramsextra.( empty($anchor) ? '' : '#'.urlencode($anchor) ); return $url . $paramsextra . (empty($anchor) ? '' : '#' . urlencode($anchor));
} }
/** /**
* Return HTML representation of relative URI path for $request - see qa_path() for other parameters * Return HTML representation of relative URI path for $request - see qa_path() for other parameters
*/ */
function qa_path_html($request, $params=null, $rooturl=null, $neaturls=null, $anchor=null) function qa_path_html($request, $params = null, $rooturl = null, $neaturls = null, $anchor = null)
{ {
return qa_html(qa_path($request, $params, $rooturl, $neaturls, $anchor)); return qa_html(qa_path($request, $params, $rooturl, $neaturls, $anchor));
} }
/** /**
* Return the absolute URI for $request - see qa_path() for other parameters * Return the absolute URI for $request - see qa_path() for other parameters
*/ */
function qa_path_absolute($request, $params=null, $anchor=null) function qa_path_absolute($request, $params = null, $anchor = null)
{ {
return qa_path($request, $params, qa_opt('site_url'), null, $anchor); return qa_path($request, $params, qa_opt('site_url'), null, $anchor);
} }
/** /**
* Get Q2A request for a question, and make it search-engine friendly, shortening it if necessary * Get Q2A request for a question, and make it search-engine friendly, shortening it if necessary
* by removing shorter words which are generally less meaningful. * by removing shorter words which are generally less meaningful.
* @param int $questionid The question ID * @param int $questionid The question ID
* @param string $title The question title * @param string $title The question title
* @return string * @return string
*/ */
function qa_q_request($questionid, $title) function qa_q_request($questionid, $title)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
require_once QA_INCLUDE_DIR . 'app/options.php'; require_once QA_INCLUDE_DIR . 'app/options.php';
...@@ -1450,129 +1480,129 @@ ...@@ -1450,129 +1480,129 @@
$title = qa_block_words_replace($title, qa_get_block_words_preg()); $title = qa_block_words_replace($title, qa_get_block_words_preg());
$slug = qa_slugify($title, qa_opt('q_urls_remove_accents'), qa_opt('q_urls_title_length')); $slug = qa_slugify($title, qa_opt('q_urls_remove_accents'), qa_opt('q_urls_title_length'));
return (int) $questionid . '/' . $slug; return (int)$questionid . '/' . $slug;
} }
/** /**
* Return the HTML anchor that should be used for post $postid with $basetype (Q/A/C) * Return the HTML anchor that should be used for post $postid with $basetype (Q/A/C)
*/ */
function qa_anchor($basetype, $postid) function qa_anchor($basetype, $postid)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return strtolower($basetype).$postid; // used to be $postid only but this violated HTML spec return strtolower($basetype) . $postid; // used to be $postid only but this violated HTML spec
} }
/** /**
* Return the URL for question $questionid with $title, possibly using $absolute URLs. * Return the URL for question $questionid with $title, possibly using $absolute URLs.
* To link to a specific answer or comment in a question, set $showtype and $showid accordingly. * To link to a specific answer or comment in a question, set $showtype and $showid accordingly.
*/ */
function qa_q_path($questionid, $title, $absolute=false, $showtype=null, $showid=null) function qa_q_path($questionid, $title, $absolute = false, $showtype = null, $showid = null)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
if ( (($showtype=='Q') || ($showtype=='A') || ($showtype=='C')) && isset($showid)) { if ((($showtype == 'Q') || ($showtype == 'A') || ($showtype == 'C')) && isset($showid)) {
$params=array('show' => $showid); // due to pagination $params = array('show' => $showid); // due to pagination
$anchor=qa_anchor($showtype, $showid); $anchor = qa_anchor($showtype, $showid);
} else { } else {
$params=null; $params = null;
$anchor=null; $anchor = null;
} }
return qa_path(qa_q_request($questionid, $title), $params, $absolute ? qa_opt('site_url') : null, null, $anchor); return qa_path(qa_q_request($questionid, $title), $params, $absolute ? qa_opt('site_url') : null, null, $anchor);
} }
/** /**
* Return the HTML representation of the URL for $questionid - other parameters as for qa_q_path() * Return the HTML representation of the URL for $questionid - other parameters as for qa_q_path()
*/ */
function qa_q_path_html($questionid, $title, $absolute=false, $showtype=null, $showid=null) function qa_q_path_html($questionid, $title, $absolute = false, $showtype = null, $showid = null)
{ {
return qa_html(qa_q_path($questionid, $title, $absolute, $showtype, $showid)); return qa_html(qa_q_path($questionid, $title, $absolute, $showtype, $showid));
} }
/** /**
* Return the request for the specified $feed * Return the request for the specified $feed
*/ */
function qa_feed_request($feed) function qa_feed_request($feed)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return 'feed/'.$feed.'.rss'; return 'feed/' . $feed . '.rss';
} }
/** /**
* Return an HTML-ready relative URL for the current page, preserving GET parameters - this is useful for action="..." in HTML forms * Return an HTML-ready relative URL for the current page, preserving GET parameters - this is useful for action="..." in HTML forms
*/ */
function qa_self_html() function qa_self_html()
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_used_url_format; global $qa_used_url_format;
return qa_path_html(qa_request(), $_GET, null, $qa_used_url_format); return qa_path_html(qa_request(), $_GET, null, $qa_used_url_format);
} }
/** /**
* Return HTML for hidden fields to insert into a <form method="get"...> on the page. * Return HTML for hidden fields to insert into a <form method="get"...> on the page.
* This is needed because any parameters on the URL will be lost when the form is submitted. * This is needed because any parameters on the URL will be lost when the form is submitted.
*/ */
function qa_path_form_html($request, $params=null, $rooturl=null, $neaturls=null, $anchor=null) function qa_path_form_html($request, $params=null, $rooturl=null, $neaturls=null, $anchor=null)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
$path=qa_path($request, $params, $rooturl, $neaturls, $anchor); $path = qa_path($request, $params, $rooturl, $neaturls, $anchor);
$formhtml=''; $formhtml = '';
$questionpos=strpos($path, '?'); $questionpos = strpos($path, '?');
if (is_numeric($questionpos)) { if (is_numeric($questionpos)) {
$params=explode('&', substr($path, $questionpos+1)); $params = explode('&', substr($path, $questionpos + 1));
foreach ($params as $param) foreach ($params as $param)
if (preg_match('/^([^\=]*)(\=(.*))?$/', $param, $matches)) if (preg_match('/^([^\=]*)(\=(.*))?$/', $param, $matches))
$formhtml.='<input type="hidden" name="'.qa_html(urldecode($matches[1])).'" value="'.qa_html(urldecode(@$matches[3])).'"/>'; $formhtml .= '<input type="hidden" name="' . qa_html(urldecode($matches[1])) . '" value="' . qa_html(urldecode(@$matches[3])) . '"/>';
} }
return $formhtml; return $formhtml;
} }
/** /**
* Redirect the user's web browser to $request and then we're done - see qa_path() for other parameters * Redirect the user's web browser to $request and then we're done - see qa_path() for other parameters
*/ */
function qa_redirect($request, $params=null, $rooturl=null, $neaturls=null, $anchor=null) function qa_redirect($request, $params=null, $rooturl=null, $neaturls=null, $anchor=null)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
qa_redirect_raw(qa_path($request, $params, $rooturl, $neaturls, $anchor)); qa_redirect_raw(qa_path($request, $params, $rooturl, $neaturls, $anchor));
} }
/** /**
* Redirect the user's web browser to page $path which is already a URL * Redirect the user's web browser to page $path which is already a URL
*/ */
function qa_redirect_raw($url) function qa_redirect_raw($url)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
header('Location: '.$url); header('Location: ' . $url);
qa_exit('redirect'); qa_exit('redirect');
} }
// General utilities // General utilities
/** /**
* Return the contents of remote $url, using file_get_contents() if possible, otherwise curl functions * Return the contents of remote $url, using file_get_contents() if possible, otherwise curl functions
*/ */
function qa_retrieve_url($url) function qa_retrieve_url($url)
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
// ensure we're fetching a remote URL // ensure we're fetching a remote URL
...@@ -1580,98 +1610,99 @@ ...@@ -1580,98 +1610,99 @@
return ''; return '';
} }
$contents=@file_get_contents($url); $contents = @file_get_contents($url);
if ((!strlen($contents)) && function_exists('curl_exec')) { // try curl as a backup (if allow_url_fopen not set) if ((!strlen($contents)) && function_exists('curl_exec')) { // try curl as a backup (if allow_url_fopen not set)
$curl=curl_init($url); $curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
$contents=@curl_exec($curl); $contents = @curl_exec($curl);
curl_close($curl); curl_close($curl);
} }
return $contents; return $contents;
} }
/** /**
* Shortcut to get or set an option value without specifying database * Shortcut to get or set an option value without specifying database
*/ */
function qa_opt($name, $value=null) function qa_opt($name, $value = null)
{ {
global $qa_options_cache; global $qa_options_cache;
if ((!isset($value)) && isset($qa_options_cache[$name])) if ((!isset($value)) && isset($qa_options_cache[$name]))
return $qa_options_cache[$name]; // quick shortcut to reduce calls to qa_get_options() return $qa_options_cache[$name]; // quick shortcut to reduce calls to qa_get_options()
require_once QA_INCLUDE_DIR.'app/options.php'; require_once QA_INCLUDE_DIR . 'app/options.php';
if (isset($value)) if (isset($value))
qa_set_option($name, $value); qa_set_option($name, $value);
$options=qa_get_options(array($name)); $options = qa_get_options(array($name));
return $options[$name]; return $options[$name];
} }
/** /**
* Simple method to output a preformatted variable * Simple method to output a preformatted variable
*/ */
function qa_debug($var) function qa_debug($var)
{ {
echo "\n" . '<pre style="padding: 10px; background-color: #eee; color: #444; font-size: 11px; text-align: left">'; echo "\n" . '<pre style="padding: 10px; background-color: #eee; color: #444; font-size: 11px; text-align: left">';
echo $var === null ? 'NULL' : print_r($var, true); echo $var === null ? 'NULL' : print_r($var, true);
echo '</pre>' . "\n"; echo '</pre>' . "\n";
} }
// Event and process stage reporting // Event and process stage reporting
/** /**
* Suspend the reporting of events to event modules via qa_report_event(...) if $suspend is * Suspend the reporting of events to event modules via qa_report_event(...) if $suspend is
* true, otherwise reinstate it. A counter is kept to allow multiple calls. * true, otherwise reinstate it. A counter is kept to allow multiple calls.
*/ */
function qa_suspend_event_reports($suspend=true) function qa_suspend_event_reports($suspend = true)
{ {
global $qa_event_reports_suspended; global $qa_event_reports_suspended;
$qa_event_reports_suspended+=($suspend ? 1 : -1); $qa_event_reports_suspended += ($suspend ? 1 : -1);
} }
/** /**
* Send a notification of event $event by $userid, $handle and $cookieid to all event modules, with extra $params * Send a notification of event $event by $userid, $handle and $cookieid to all event modules, with extra $params
*/ */
function qa_report_event($event, $userid, $handle, $cookieid, $params=array()) function qa_report_event($event, $userid, $handle, $cookieid, $params=array())
{ {
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
global $qa_event_reports_suspended; global $qa_event_reports_suspended;
if ($qa_event_reports_suspended>0) if ($qa_event_reports_suspended > 0)
return; return;
$eventmodules=qa_load_modules_with('event', 'process_event'); $eventmodules = qa_load_modules_with('event', 'process_event');
foreach ($eventmodules as $eventmodule) foreach ($eventmodules as $eventmodule)
$eventmodule->process_event($event, $userid, $handle, $cookieid, $params); $eventmodule->process_event($event, $userid, $handle, $cookieid, $params);
} }
function qa_report_process_stage($method) // can have extra params function qa_report_process_stage($method) // can have extra params
{ {
global $qa_process_reports_suspended; global $qa_process_reports_suspended;
if (@$qa_process_reports_suspended) if (@$qa_process_reports_suspended)
return; return;
$qa_process_reports_suspended=true; // prevent loop, e.g. because of an error $qa_process_reports_suspended = true; // prevent loop, e.g. because of an error
$args=func_get_args(); $args = func_get_args();
$args=array_slice($args, 1); $args = array_slice($args, 1);
$processmodules=qa_load_modules_with('process', $method); $processmodules = qa_load_modules_with('process', $method);
foreach ($processmodules as $processmodule) foreach ($processmodules as $processmodule) {
call_user_func_array(array($processmodule, $method), $args); call_user_func_array(array($processmodule, $method), $args);
$qa_process_reports_suspended=null;
} }
$qa_process_reports_suspended = 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