Commit 114f1696 by Scott

Merge branch 'dev' (1.7.2) into 1.8

parents 1775bcdd 281619b4
# Ignore config file in development # Ignore config file in development
qa-config.php qa-config.php
# Other files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
.idea/
sudo: false
language: php language: php
php: php:
- 5.3 - 5.3
...@@ -17,7 +18,7 @@ before_script: ...@@ -17,7 +18,7 @@ before_script:
- curl -o phpcpd.phar https://phar.phpunit.de/phpcpd.phar - curl -o phpcpd.phar https://phar.phpunit.de/phpcpd.phar
# PHP Mess Detector # PHP Mess Detector
- pear config-set preferred_state beta - pear config-set preferred_state beta
- printf "\n" | pecl install imagick - printf "\n\n" | pecl install imagick
- pear channel-discover pear.phpmd.org - pear channel-discover pear.phpmd.org
- pear channel-discover pear.pdepend.org - pear channel-discover pear.pdepend.org
- pear install --alldeps phpmd/PHP_PMD - pear install --alldeps phpmd/PHP_PMD
......
1.7.1 1.7.2
\ No newline at end of file \ No newline at end of file
...@@ -29,6 +29,9 @@ ...@@ -29,6 +29,9 @@
For persistent connections, set the QA_PERSISTENT_CONN_DB at the bottom of this file; do NOT For persistent connections, set the QA_PERSISTENT_CONN_DB at the bottom of this file; do NOT
prepend the hostname with 'p:'. prepend the hostname with 'p:'.
To use a non-default port, add the following line to the list of defines, with the appropriate port number:
define('QA_MYSQL_PORT', '3306');
*/ */
define('QA_MYSQL_HOSTNAME', '127.0.0.1'); define('QA_MYSQL_HOSTNAME', '127.0.0.1');
......
...@@ -144,7 +144,7 @@ ...@@ -144,7 +144,7 @@
if ($question['commentbutton']) { if ($question['commentbutton']) {
if (qa_clicked('q_docomment')) if (qa_clicked('q_docomment'))
qa_page_q_refresh($pagestart, 'comment-'.$questionid); qa_page_q_refresh($pagestart, 'comment-'.$questionid, 'C', $questionid);
if (qa_clicked('c'.$questionid.'_doadd') || ($pagestate==('comment-'.$questionid))) if (qa_clicked('c'.$questionid.'_doadd') || ($pagestate==('comment-'.$questionid)))
qa_page_q_do_comment($question, $question, $commentsfollows, $pagestart, $usecaptcha, $cnewin, $cnewerrors, $formtype, $formpostid, $pageerror); qa_page_q_do_comment($question, $question, $commentsfollows, $pagestart, $usecaptcha, $cnewin, $cnewerrors, $formtype, $formpostid, $pageerror);
...@@ -182,7 +182,7 @@ ...@@ -182,7 +182,7 @@
if ($answer['commentbutton']) { if ($answer['commentbutton']) {
if (qa_clicked($prefix.'docomment')) if (qa_clicked($prefix.'docomment'))
qa_page_q_refresh($pagestart, 'comment-'.$answerid, 'A', $answerid); qa_page_q_refresh($pagestart, 'comment-'.$answerid, 'C', $answerid);
if (qa_clicked('c'.$answerid.'_doadd') || ($pagestate==('comment-'.$answerid))) if (qa_clicked('c'.$answerid.'_doadd') || ($pagestate==('comment-'.$answerid)))
qa_page_q_do_comment($question, $answer, $commentsfollows, $pagestart, $usecaptcha, $cnewin, $cnewerrors, $formtype, $formpostid, $pageerror); qa_page_q_do_comment($question, $answer, $commentsfollows, $pagestart, $usecaptcha, $cnewin, $cnewerrors, $formtype, $formpostid, $pageerror);
...@@ -858,7 +858,7 @@ ...@@ -858,7 +858,7 @@
$commentid=qa_page_q_add_c_submit($question, $parent, $commentsfollows, $usecaptcha, $cnewin[$parentid], $cnewerrors[$parentid]); $commentid=qa_page_q_add_c_submit($question, $parent, $commentsfollows, $usecaptcha, $cnewin[$parentid], $cnewerrors[$parentid]);
if (isset($commentid)) if (isset($commentid))
qa_page_q_refresh($pagestart, null, $parent['basetype'], $parentid); qa_page_q_refresh($pagestart, null, 'C', $commentid);
else { else {
$formtype='c_add'; $formtype='c_add';
......
...@@ -43,6 +43,9 @@ class qa_filter_basic ...@@ -43,6 +43,9 @@ class qa_filter_basic
if (!strlen($handle)) { if (!strlen($handle)) {
return qa_lang('users/handle_empty'); return qa_lang('users/handle_empty');
} }
if (in_array($handle, array('.', '..'))) {
return qa_lang_sub('users/handle_has_bad', '. ..');
}
if (preg_match('/[\\@\\+\\/]/', $handle)) { if (preg_match('/[\\@\\+\\/]/', $handle)) {
return qa_lang_sub('users/handle_has_bad', '@ + /'); return qa_lang_sub('users/handle_has_bad', '@ + /');
} }
......
...@@ -21,8 +21,8 @@ ...@@ -21,8 +21,8 @@
*/ */
define('QA_VERSION', '1.7.1'); // also used as suffix for .js and .css requests define('QA_VERSION', '1.7.2'); // also used as suffix for .js and .css requests
define('QA_BUILD_DATE', '2015-07-27'); define('QA_BUILD_DATE', '2015-11-05');
/** /**
...@@ -262,6 +262,10 @@ ...@@ -262,6 +262,10 @@
define('QA_FINAL_EXTERNAL_USERS', QA_EXTERNAL_USERS); define('QA_FINAL_EXTERNAL_USERS', QA_EXTERNAL_USERS);
} }
if (defined('QA_MYSQL_PORT')) {
define('QA_FINAL_MYSQL_PORT', QA_MYSQL_PORT);
}
// Possible URL schemes for Q2A and the string used for url scheme testing // Possible URL schemes for Q2A and the string used for url scheme testing
define('QA_URL_FORMAT_INDEX', 0); // http://...../index.php/123/why-is-the-sky-blue define('QA_URL_FORMAT_INDEX', 0); // http://...../index.php/123/why-is-the-sky-blue
...@@ -270,7 +274,7 @@ ...@@ -270,7 +274,7 @@
define('QA_URL_FORMAT_PARAMS', 4); // http://...../?qa=123&qa_1=why-is-the-sky-blue define('QA_URL_FORMAT_PARAMS', 4); // http://...../?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_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 /
} }
......
...@@ -21,70 +21,74 @@ ...@@ -21,70 +21,74 @@
*/ */
// Ensure no PHP errors are shown in the blob response // Ensure no PHP errors are shown in the blob response
@ini_set('display_errors', 0); @ini_set('display_errors', 0);
function qa_blob_db_fail_handler()
{
header('HTTP/1.1 500 Internal Server Error');
qa_exit('error');
}
function qa_blob_db_fail_handler()
{
header('HTTP/1.1 500 Internal Server Error');
qa_exit('error');
}
// Load the Q2A base file which sets up a bunch of crucial stuff
require 'qa-base.php'; // Load the Q2A base file which sets up a bunch of crucial stuff
qa_report_process_stage('init_blob'); require 'qa-base.php';
qa_report_process_stage('init_blob');
// Output the blob in question
require_once QA_INCLUDE_DIR.'app/blobs.php'; // Output the blob in question
qa_db_connect('qa_blob_db_fail_handler'); require_once QA_INCLUDE_DIR.'app/blobs.php';
$blob=qa_read_blob(qa_get('qa_blobid')); qa_db_connect('qa_blob_db_fail_handler');
if (isset($blob)) { $blob = qa_read_blob(qa_get('qa_blobid'));
header('Cache-Control: max-age=2592000, public'); // allows browsers and proxies to cache the blob
switch ($blob['format']) { if (isset($blob)) {
case 'jpeg': // allows browsers and proxies to cache the blob (30 days)
case 'jpg': header('Cache-Control: max-age=2592000, public');
header('Content-Type: image/jpeg');
break;
case 'gif': $disposition = 'inline';
header('Content-Type: image/gif');
break;
case 'png': switch ($blob['format']) {
header('Content-Type: image/png'); case 'jpeg':
break; case 'jpg':
header('Content-Type: image/jpeg');
break;
case 'swf': case 'gif':
header('Content-Type: application/x-shockwave-flash'); header('Content-Type: image/gif');
break; break;
default: case 'png':
$filename=preg_replace('/[^A-Za-z0-9 \\._-]/', '-', $blob['filename']); // for compatibility with HTTP headers and all browsers header('Content-Type: image/png');
break;
header('Content-Type: application/octet-stream'); case 'pdf':
header('Content-Disposition: attachment; filename="'.$filename.'"'); header('Content-Type: application/pdf');
break; break;
}
echo $blob['content']; case 'swf':
header('Content-Type: application/x-shockwave-flash');
break;
} else default:
header('HTTP/1.0 404 Not Found'); header('Content-Type: application/octet-stream');
$disposition = 'attachment';
break;
}
// for compatibility with HTTP headers and all browsers
$filename = preg_replace('/[^A-Za-z0-9 \\._-]+/', '', $blob['filename']);
header('Content-Disposition: '.$disposition.'; filename="'.$filename.'"');
qa_db_disconnect(); echo $blob['content'];
} else {
header('HTTP/1.0 404 Not Found');
}
/* qa_db_disconnect();
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
...@@ -60,10 +60,11 @@ ...@@ -60,10 +60,11 @@
return; return;
// in mysqli we connect and select database in constructor // in mysqli we connect and select database in constructor
if (QA_PERSISTENT_CONN_DB) $host = QA_PERSISTENT_CONN_DB ? 'p:'.QA_FINAL_MYSQL_HOSTNAME : QA_FINAL_MYSQL_HOSTNAME;
$db = new mysqli('p:'.QA_FINAL_MYSQL_HOSTNAME, QA_FINAL_MYSQL_USERNAME, QA_FINAL_MYSQL_PASSWORD, QA_FINAL_MYSQL_DATABASE); if (defined('QA_FINAL_MYSQL_PORT'))
$db = new mysqli($host, QA_FINAL_MYSQL_USERNAME, QA_FINAL_MYSQL_PASSWORD, QA_FINAL_MYSQL_DATABASE, QA_FINAL_MYSQL_PORT);
else else
$db = new mysqli(QA_FINAL_MYSQL_HOSTNAME, QA_FINAL_MYSQL_USERNAME, QA_FINAL_MYSQL_PASSWORD, QA_FINAL_MYSQL_DATABASE); $db = new mysqli($host, QA_FINAL_MYSQL_USERNAME, QA_FINAL_MYSQL_PASSWORD, QA_FINAL_MYSQL_DATABASE);
// must use procedural `mysqli_connect_error` here prior to 5.2.9 // must use procedural `mysqli_connect_error` here prior to 5.2.9
$conn_error = mysqli_connect_error(); $conn_error = mysqli_connect_error();
......
<?php <?php
/** /**
* PHPMailer SPL autoloader. * PHPMailer SPL autoloader.
* PHP Version 5.0.0 * PHP Version 5
* @package PHPMailer * @package PHPMailer
* @link https://github.com/PHPMailer/PHPMailer/ * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
* @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk> * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
* @author Jim Jagielski (jimjag) <jimjag@gmail.com> * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
* @author Brent R. Matzelle (original founder) * @author Brent R. Matzelle (original founder)
* @copyright 2013 Marcus Bointon * @copyright 2012 - 2014 Marcus Bointon
* @copyright 2010 - 2012 Jim Jagielski * @copyright 2010 - 2012 Jim Jagielski
* @copyright 2004 - 2009 Andy Prevost * @copyright 2004 - 2009 Andy Prevost
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -133,7 +133,11 @@ class qa_recaptcha_captcha ...@@ -133,7 +133,11 @@ class qa_recaptcha_captcha
{ {
require_once $this->directory.'recaptchalib.php'; require_once $this->directory.'recaptchalib.php';
$recaptcha = new ReCaptcha(qa_opt('recaptcha_private_key')); if (ini_get('allow_url_fopen'))
$recaptcha = new ReCaptcha(qa_opt('recaptcha_private_key'));
else
$recaptcha = new ReCaptcha(qa_opt('recaptcha_private_key') , new ReCaptchaSocketPostRequestMethod());
$remoteIp = qa_remote_ip_address(); $remoteIp = qa_remote_ip_address();
$userResponse = qa_post_text('g-recaptcha-response'); $userResponse = qa_post_text('g-recaptcha-response');
......
...@@ -39,58 +39,269 @@ class ReCaptchaResponse ...@@ -39,58 +39,269 @@ class ReCaptchaResponse
public $errorCodes = array(); public $errorCodes = array();
} }
class ReCaptcha /**
* Stores and formats the parameters for the request to the reCAPTCHA service.
*/
class ReCaptchaRequestParameters
{ {
private static $_signupUrl = 'https://www.google.com/recaptcha/admin'; private $secret;
private static $_siteVerifyUrl = 'https://www.google.com/recaptcha/api/siteverify?'; private $response;
private $_secret; private $remoteIp;
private static $_version = 'php_1.0'; private $version;
/** /**
* Constructor. * Initialise parameters.
* *
* @param string $secret shared secret between site and ReCAPTCHA server. * @param string $secret Site secret.
* @param string $response Value from g-captcha-response form field.
* @param string $remoteIp User's IP address.
* @param string $version Version of this client library.
*/ */
public function __construct($secret) public function __construct($secret, $response, $remoteIp = null, $version = null)
{ {
if ($secret == null || $secret == '') { $this->secret = $secret;
die('To use reCAPTCHA you must get an API key from <a href="' . self::$_signupUrl . '">' . self::$_signupUrl . '</a>'); $this->response = $response;
$this->remoteIp = $remoteIp;
$this->version = $version;
}
/**
* Array representation.
*
* @return array Array formatted parameters.
*/
public function toArray()
{
$params = array('secret' => $this->secret, 'response' => $this->response);
if (!is_null($this->remoteIp)) {
$params['remoteip'] = $this->remoteIp;
} }
$this->_secret = $secret;
if (!is_null($this->version)) {
$params['version'] = $this->version;
}
return $params;
} }
/** /**
* Encodes the given data into a query string format. * Query string representation for HTTP request.
* *
* @param array $data array of string elements to be encoded. * @return string Query string formatted parameters.
*/
public function toQueryString()
{
return http_build_query($this->toArray(), '', '&');
}
}
/**
* Defines certain rules for a RequestMethod
* Interface ReCaptchaRequestMethod
*/
interface ReCaptchaRequestMethod
{
/**
* Submit the request with the specified parameters.
* *
* @return string - encoded request. * @param ReCaptchaRequestParameters $params Request parameters
* @return string Body of the reCAPTCHA response
*/ */
private function _encodeQS($data) public function submit(ReCaptchaRequestParameters $params);
}
/**
* Sends GET requests to the reCAPTCHA service.
*/
class ReCaptchaGetRequestMethod implements ReCaptchaRequestMethod{
const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify?';
/**
* Submit the request with the specified parameters.
*
* @param ReCaptchaRequestParameters $params Request parameters
* @return string Body of the reCAPTCHA response
*/
public function submit(ReCaptchaRequestParameters $params){
return file_get_contents(self::SITE_VERIFY_URL . $params->toQueryString());
}
}
/**
* Convenience wrapper around native socket and file functions to allow for
* mocking.
*/
class ReCaptchaSocket
{
private $handle = null;
/**
* fsockopen
*
* @see http://php.net/fsockopen
* @param string $hostname
* @param int $port
* @param int $errno
* @param string $errstr
* @param float $timeout
* @return resource
*/
public function fsockopen($hostname, $port = -1, &$errno = 0, &$errstr = '', $timeout = null)
{ {
$req = ""; $this->handle = fsockopen($hostname, $port, $errno, $errstr, (is_null($timeout) ? ini_get("default_socket_timeout") : $timeout));
foreach ($data as $key => $value) {
$req .= $key . '=' . urlencode(stripslashes($value)) . '&'; if ($this->handle != false && $errno === 0 && $errstr === '') {
return $this->handle;
} }
// Cut the last '&' return false;
$req = substr($req, 0, strlen($req)-1); }
return $req;
/**
* fwrite
*
* @see http://php.net/fwrite
* @param string $string
* @param int $length
* @return int | bool
*/
public function fwrite($string, $length = null)
{
return fwrite($this->handle, $string, (is_null($length) ? strlen($string) : $length));
} }
/** /**
* Submits an HTTP GET to a reCAPTCHA server. * fgets
* *
* @param string $path url path to recaptcha server. * @see http://php.net/fgets
* @param array $data array of parameters to be sent. * @param int $length
* @return string
*/
public function fgets($length = null)
{
return fgets($this->handle, $length);
}
/**
* feof
* *
* @return array response * @see http://php.net/feof
* @return bool
*/ */
private function _submitHTTPGet($path, $data) public function feof()
{ {
$req = $this->_encodeQS($data); return feof($this->handle);
$response = file_get_contents($path . $req); }
return $response;
/**
* fclose
*
* @see http://php.net/fclose
* @return bool
*/
public function fclose()
{
return fclose($this->handle);
}
}
/**
* Sends a POST request to the reCAPTCHA service, but makes use of fsockopen()
* instead of get_file_contents(). This is to account for people who may be on
* servers where allow_furl_open is disabled.
*/
class ReCaptchaSocketPostRequestMethod implements ReCaptchaRequestMethod
{
const RECAPTCHA_HOST = 'www.google.com';
const SITE_VERIFY_PATH = '/recaptcha/api/siteverify';
const BAD_REQUEST = '{"success": false, "error-codes": ["invalid-request"]}';
const BAD_RESPONSE = '{"success": false, "error-codes": ["invalid-response"]}';
private $socket;
public function __construct(ReCaptchaSocket $socket = null)
{
if (!is_null($socket)) {
$this->socket = $socket;
} else {
$this->socket = new ReCaptchaSocket();
}
}
/**
* Submit the POST request with the specified parameters.
*
* @param ReCaptchaRequestParameters $params Request parameters
* @return string Body of the reCAPTCHA response
*/
public function submit(ReCaptchaRequestParameters $params)
{
$errno = 0;
$errstr = '';
if (false === $this->socket->fsockopen('ssl://' . self::RECAPTCHA_HOST, 443, $errno, $errstr, 30)) {
return self::BAD_REQUEST;
}
$content = $params->toQueryString();
$request = "POST " . self::SITE_VERIFY_PATH . " HTTP/1.1\r\n";
$request .= "Host: " . self::RECAPTCHA_HOST . "\r\n";
$request .= "Content-Type: application/x-www-form-urlencoded\r\n";
$request .= "Content-length: " . strlen($content) . "\r\n";
$request .= "Connection: close\r\n\r\n";
$request .= $content . "\r\n\r\n";
$this->socket->fwrite($request);
$response = '';
while (!$this->socket->feof()) {
$response .= $this->socket->fgets(4096);
}
$this->socket->fclose();
if (0 !== strpos($response, 'HTTP/1.1 200 OK')) {
return self::BAD_RESPONSE;
}
$parts = preg_split("#\n\s*\n#Uis", $response);
return $parts[1];
}
}
class ReCaptcha
{
private static $_signupUrl = 'https://www.google.com/recaptcha/admin';
const VERSION = 'php_1.1.2';
private $secret;
private $requestMethod;
/**
* Constructor.
*
* @param string $secret shared secret between site and ReCAPTCHA server.
*/
public function __construct($secret , ReCaptchaRequestMethod $requestMethod = null)
{
if ($secret == null || $secret == '') {
die('To use reCAPTCHA you must get an API key from <a href="' . self::$_signupUrl . '">' . self::$_signupUrl . '</a>');
}
if (!is_string($secret)) {
die('The provided secret must be a string');
}
$this->secret = $secret;
if (!is_null($requestMethod)) {
$this->requestMethod = $requestMethod;
} else {
$this->requestMethod = new ReCaptchaGetRequestMethod();
}
} }
/** /**
...@@ -112,16 +323,11 @@ class ReCaptcha ...@@ -112,16 +323,11 @@ class ReCaptcha
return $recaptchaResponse; return $recaptchaResponse;
} }
$getResponse = $this->_submitHttpGet( $params = new ReCaptchaRequestParameters($this->secret, $response, $remoteIp, self::VERSION);
self::$_siteVerifyUrl,
array( $rawResponse = $this->requestMethod->submit($params);
'secret' => $this->_secret, $answers = json_decode($rawResponse, true);
'remoteip' => $remoteIp,
'v' => self::$_version,
'response' => $response
)
);
$answers = json_decode($getResponse, true);
$recaptchaResponse = new ReCaptchaResponse(); $recaptchaResponse = new ReCaptchaResponse();
if (trim($answers['success']) == true) { if (trim($answers['success']) == true) {
......
...@@ -308,6 +308,7 @@ h2 {font-size:22px; color:#c659ab; padding-top:12px; clear:both;} ...@@ -308,6 +308,7 @@ h2 {font-size:22px; color:#c659ab; padding-top:12px; clear:both;}
/* Question view */ /* Question view */
.qa-q-view-content, .qa-a-item-content, .qa-c-item-content {word-break: break-word;}
.qa-q-view {padding-left:10px; padding-top:10px;} .qa-q-view {padding-left:10px; padding-top:10px;}
.qa-q-view-main {float:left; width:550px; padding-left:10px;} .qa-q-view-main {float:left; width:550px; padding-left:10px;}
.qa-q-view-content {font-size:16px; margin-bottom:16px;} .qa-q-view-content {font-size:16px; margin-bottom:16px;}
...@@ -445,10 +446,11 @@ h2 {font-size:22px; color:#c659ab; padding-top:12px; clear:both;} ...@@ -445,10 +446,11 @@ h2 {font-size:22px; color:#c659ab; padding-top:12px; clear:both;}
.qa-nav-cat-favorited {margin-left:-20px;} .qa-nav-cat-favorited {margin-left:-20px;}
.qa-tag-favorited, .qa-cat-favorited, .qa-cat-parent-favorited, .qa-user-favorited {background-position: left center; padding-left:19px;} .qa-tag-favorited, .qa-cat-favorited, .qa-cat-parent-favorited, .qa-user-favorited {background-position: left center; padding-left:19px;}
/* Plugins */ /* Miscellaneous */
[class^="qa-part-form-plugin"] {margin-top: 10px;} [class^="qa-part-form-plugin"] {margin-top: 10px;}
.qa-part-form-plugin-0, .qa-part-form-plugin-options {margin-top: 0;} .qa-part-form-plugin-0, .qa-part-form-plugin-options {margin-top: 0;}
.qa-part-form-profile .qa-form-wide-data {word-break: break-word;}
/* IE6 friendly versions of icons with binary alpha channel */ /* IE6 friendly versions of icons with binary alpha channel */
......
...@@ -284,6 +284,7 @@ h2 {font-size:16px; padding-top:12px; clear:both;} ...@@ -284,6 +284,7 @@ h2 {font-size:16px; padding-top:12px; clear:both;}
/* Question view */ /* Question view */
.qa-q-view-content, .qa-a-item-content, .qa-c-item-content {word-break: break-word;}
.qa-q-view-stats {float:left;} .qa-q-view-stats {float:left;}
.qa-q-view-main {float:left; width:600px;} .qa-q-view-main {float:left; width:600px;}
.qa-q-view-content {font-size:14px; margin-bottom:16px;} .qa-q-view-content {font-size:14px; margin-bottom:16px;}
...@@ -316,6 +317,7 @@ h2 {font-size:16px; padding-top:12px; clear:both;} ...@@ -316,6 +317,7 @@ h2 {font-size:16px; padding-top:12px; clear:both;}
/* Answer view */ /* Answer view */
.qa-a-list-item {margin-bottom:40px; zoom:1; padding-bottom:1px;} /* zoom for IE, padding for early FF */ .qa-a-list-item {margin-bottom:40px; zoom:1; padding-bottom:1px;} /* zoom for IE, padding for early FF */
.qa-a-list-item:target{-webkit-animation: highlight 2s ease-in-out; animation: highlight 2s ease-in-out;}
.qa-a-list-item-hidden {} .qa-a-list-item-hidden {}
.qa-a-list-item-hidden .qa-voting {background:#FFF; border:1px solid #ccc; color:#ccc;} .qa-a-list-item-hidden .qa-voting {background:#FFF; border:1px solid #ccc; color:#ccc;}
.qa-a-list-item-hidden .qa-a-item-content {color:#999;} .qa-a-list-item-hidden .qa-a-item-content {color:#999;}
...@@ -354,6 +356,7 @@ h2 {font-size:16px; padding-top:12px; clear:both;} ...@@ -354,6 +356,7 @@ h2 {font-size:16px; padding-top:12px; clear:both;}
/* Comments */ /* Comments */
.qa-c-list-item {border-bottom:1px dotted #666; padding:6px; padding-right:0;} .qa-c-list-item {border-bottom:1px dotted #666; padding:6px; padding-right:0;}
.qa-c-list-item:target {-webkit-animation: highlight 2s ease-in-out; animation: highlight 2s ease-in-out;}
.qa-c-item-hidden {} .qa-c-item-hidden {}
.qa-c-item-hidden .qa-c-item-content {color:#bbb;} .qa-c-item-hidden .qa-c-item-content {color:#bbb;}
.qa-c-item-hidden .qa-c-item-link {color:#aaf;} .qa-c-item-hidden .qa-c-item-link {color:#aaf;}
...@@ -418,8 +421,18 @@ h2 {font-size:16px; padding-top:12px; clear:both;} ...@@ -418,8 +421,18 @@ h2 {font-size:16px; padding-top:12px; clear:both;}
.qa-cat-favorited, .qa-cat-parent-favorited, .qa-user-favorited {background-position: left center; padding-left:17px;} .qa-cat-favorited, .qa-cat-parent-favorited, .qa-user-favorited {background-position: left center; padding-left:17px;}
.qa-nav-cat-favorited {margin-left:-18px;} .qa-nav-cat-favorited {margin-left:-18px;}
/* Plugins */ /* Miscellaneous */
[class^="qa-part-form-plugin"] {margin-top: 10px;} [class^="qa-part-form-plugin"] {margin-top: 10px;}
.qa-part-form-plugin-0 {margin-top: 0;} .qa-part-form-plugin-0 {margin-top: 0;}
.qa-part-form-plugin-options {margin-top: -1px;} .qa-part-form-plugin-options {margin-top: -1px;}
.qa-part-form-profile .qa-form-wide-data {word-break: break-word;}
@-webkit-keyframes highlight {
0% { background-color: #ffffaa; }
100% { background-color: #fff; }
}
@keyframes highlight {
0% { background-color: #ffffaa; }
100% { background-color: #fff; }
}
...@@ -1770,6 +1770,13 @@ a.qa-browse-cat-link:visited { ...@@ -1770,6 +1770,13 @@ a.qa-browse-cat-link:visited {
} }
/* Question view */ /* Question view */
.qa-q-view-content,
.qa-a-item-content,
.qa-c-item-content {
word-break: break-word;
}
.qa-q-view { .qa-q-view {
padding-left: 10px; padding-left: 10px;
padding-top: 10px; padding-top: 10px;
...@@ -1904,6 +1911,10 @@ a.qa-browse-cat-link:visited { ...@@ -1904,6 +1911,10 @@ a.qa-browse-cat-link:visited {
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
min-height: 108px; /* prevent tick icon being hidden */ min-height: 108px; /* prevent tick icon being hidden */
} }
.qa-a-list-item:target {
-webkit-animation: highlight 2s ease-in-out;
animation: highlight 2s ease-in-out;
}
/* zoom for IE, padding for early FF */ /* zoom for IE, padding for early FF */
.qa-a-list-item-selected { .qa-a-list-item-selected {
...@@ -2050,6 +2061,10 @@ a.qa-browse-cat-link:visited { ...@@ -2050,6 +2061,10 @@ a.qa-browse-cat-link:visited {
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
padding: 8px 0 8px 8px; padding: 8px 0 8px 8px;
} }
.qa-c-list-item:target {
-webkit-animation: highlight 2s ease-in-out;
animation: highlight 2s ease-in-out;
}
.qa-c-list-item:nth-child(odd) { .qa-c-list-item:nth-child(odd) {
background: #fafafa; background: #fafafa;
...@@ -2511,6 +2526,9 @@ a.qa-browse-cat-link:visited { ...@@ -2511,6 +2526,9 @@ a.qa-browse-cat-link:visited {
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
} }
.qa-template-user .qa-part-form-profile .qa-form-wide-data {
word-break: break-word;
}
.qa-template-user .qa-part-form-activity td .qa-uf-user-points, .qa-template-user .qa-part-form-activity td .qa-uf-user-points,
.qa-template-user .qa-part-form-activity td .qa-uf-user-q-posts, .qa-template-user .qa-part-form-activity td .qa-uf-user-q-posts,
...@@ -2538,3 +2556,12 @@ a.qa-browse-cat-link:visited { ...@@ -2538,3 +2556,12 @@ a.qa-browse-cat-link:visited {
padding-top: 0; padding-top: 0;
border-top: 0; border-top: 0;
} }
@-webkit-keyframes highlight {
0% { background-color: #ffffaa; }
100% { background-color: #fff; }
}
@keyframes highlight {
0% { background-color: #ffffaa; }
100% { background-color: #fff; }
}
-------------------------------
UBUNTU FONT LICENCE Version 1.0
-------------------------------
PREAMBLE
This licence allows the licensed fonts to be used, studied, modified and
redistributed freely. The fonts, including any derivative works, can be
bundled, embedded, and redistributed provided the terms of this licence
are met. The fonts and derivatives, however, cannot be released under
any other licence. The requirement for fonts to remain under this
licence does not require any document created using the fonts or their
derivatives to be published under this licence, as long as the primary
purpose of the document is not to be a vehicle for the distribution of
the fonts.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this licence and clearly marked as such. This may
include source files, build scripts and documentation.
"Original Version" refers to the collection of Font Software components
as received under this licence.
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to
a new environment.
"Copyright Holder(s)" refers to all individuals and companies who have a
copyright ownership of the Font Software.
"Substantially Changed" refers to Modified Versions which can be easily
identified as dissimilar to the Font Software by users of the Font
Software comparing the Original Version with the Modified Version.
To "Propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification and with or without charging
a redistribution fee), making available to the public, and in some
countries other activities as well.
PERMISSION & CONDITIONS
This licence does not grant any rights under trademark law and all such
rights are reserved.
Permission is hereby granted, free of charge, to any person obtaining a
copy of the Font Software, to propagate the Font Software, subject to
the below conditions:
1) Each copy of the Font Software must contain the above copyright
notice and this licence. These can be included either as stand-alone
text files, human-readable headers or in the appropriate machine-
readable metadata fields within text or binary files as long as those
fields can be easily viewed by the user.
2) The font name complies with the following:
(a) The Original Version must retain its name, unmodified.
(b) Modified Versions which are Substantially Changed must be renamed to
avoid use of the name of the Original Version or similar names entirely.
(c) Modified Versions which are not Substantially Changed must be
renamed to both (i) retain the name of the Original Version and (ii) add
additional naming elements to distinguish the Modified Version from the
Original Version. The name of such Modified Versions must be the name of
the Original Version, with "derivative X" where X represents the name of
the new work, appended to that name.
3) The name(s) of the Copyright Holder(s) and any contributor to the
Font Software shall not be used to promote, endorse or advertise any
Modified Version, except (i) as required by this licence, (ii) to
acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with
their explicit written permission.
4) The Font Software, modified or unmodified, in part or in whole, must
be distributed entirely under this licence, and must not be distributed
under any other licence. The requirement for fonts to remain under this
licence does not affect any document created using the Font Software,
except any version of the Font Software extracted from a document
created using the Font Software may only be distributed under this
licence.
TERMINATION
This licence becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
DEALINGS IN THE FONT SOFTWARE.
@font-face {
font-family: 'Ubuntu'; font-weight: 400; font-style: normal;
src: local('Ubuntu'), local('Ubuntu-regular'),
url('Ubuntu-regular.woff') format('woff');
}
@font-face {
font-family: 'Ubuntu'; font-weight: 700; font-style: normal;
src: local('Ubuntu Bold'), local('Ubuntu-700'),
url('Ubuntu-700.woff') format('woff');
}
@font-face {
font-family: 'Ubuntu'; font-weight: 400; font-style: italic;
src: local('Ubuntu Italic'), local('Ubuntu-italic'),
url('Ubuntu-italic.woff') format('woff');
}
@font-face {
font-family: 'Ubuntu'; font-weight: 700; font-style: italic;
src: local('Ubuntu Bold Italic'), local('Ubuntu-700italic'),
url('Ubuntu-700italic.woff') format('woff');
}
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
.qa-template-admin .qa-message-buttons, .qa-template-admin .qa-message-buttons,
.qa-q-item-avatar-meta, .qa-message-item, .qa-q-item-avatar-meta, .qa-message-item,
.qa-q-view, .qa-part-q-view, .qa-q-view-content, .qa-q-view-buttons, .qa-part-form-q-edit, .qa-q-view, .qa-part-q-view, .qa-q-view-content, .qa-q-view-buttons, .qa-part-form-q-edit,
.qa-a-list-item, .qa-a-item-buttons, .qa-a-list-item, .qa-a-item-buttons, .qa-a-item-content,
.qa-c-item-buttons, .qa-c-item-clear, .qa-c-item-buttons, .qa-c-item-clear,
.qam-footer-row, .qam-qa-list-meta-box, .qam-footer-row, .qam-qa-list-meta-box,
.qa-nav-footer-list, .qa-footer-clear { .qa-nav-footer-list, .qa-footer-clear {
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
.qa-template-admin .qa-message-buttons:after, .qa-template-admin .qa-message-buttons:after,
.qa-q-item-avatar-meta:after, .qa-message-item:after, .qa-q-item-avatar-meta:after, .qa-message-item:after,
.qa-q-view:after, .qa-part-q-view:after, .qa-q-view-content:after, .qa-q-view-buttons:after, .qa-part-form-q-edit:after, .qa-q-view:after, .qa-part-q-view:after, .qa-q-view-content:after, .qa-q-view-buttons:after, .qa-part-form-q-edit:after,
.qa-a-list-item:after, .qa-a-item-buttons:after, .qa-a-list-item:after, .qa-a-item-buttons:after, .qa-a-item-content:after,
.qa-c-item-buttons:after, .qa-c-item-clear:after, .qa-c-item-buttons:after, .qa-c-item-clear:after,
.qam-footer-row:after, .qam-qa-list-meta-box:after, .qam-footer-row:after, .qam-qa-list-meta-box:after,
.qa-nav-footer-list:after, .qa-footer-clear:after { .qa-nav-footer-list:after, .qa-footer-clear:after {
...@@ -37,10 +37,17 @@ ...@@ -37,10 +37,17 @@
clear: both; clear: both;
} }
/*------[ web fonts ]------*/
@font-face { @font-face {
font-family: "fontello"; font-family: "fontello";
src: url("font/fontello.eot?2559038") format("embedded-opentype"), url("font/fontello.eot?2559038#iefix") format("embedded-opentype"), url("font/fontello.woff?2559038") format("woff"), url("font/fontello.ttf?2559038") format("truetype"), url("font/fontello.svg?2559038#fontello") format("svg"); src: url("fonts/fontello.eot?2559038") format("embedded-opentype"),
url("fonts/fontello.eot?2559038#iefix") format("embedded-opentype"),
url("fonts/fontello.woff?2559038") format("woff"),
url("fonts/fontello.ttf?2559038") format("truetype"),
url("fonts/fontello.svg?2559038#fontello") format("svg");
} }
/*------[ base css ]------*/ /*------[ base css ]------*/
html { html {
font-size: 16px; font-size: 16px;
...@@ -704,6 +711,8 @@ blockquote p { ...@@ -704,6 +711,8 @@ blockquote p {
display: block; display: block;
cursor: pointer; cursor: pointer;
min-width: 60px; min-width: 60px;
min-height: 55px;
max-width: 80px;
padding: 2px; padding: 2px;
background-color: #34495e; background-color: #34495e;
text-align: center; text-align: center;
...@@ -735,6 +744,8 @@ blockquote p { ...@@ -735,6 +744,8 @@ blockquote p {
.qam-account-handle { .qam-account-handle {
font-size: 12px; font-size: 12px;
line-height: 1.8; line-height: 1.8;
overflow: hidden;
text-overflow: ellipsis;
} }
.qam-account-items .qa-form-tall-button-login { .qam-account-items .qa-form-tall-button-login {
...@@ -895,9 +906,6 @@ blockquote p { ...@@ -895,9 +906,6 @@ blockquote p {
} }
} }
.qam-title-rss { .qam-title-rss {
float: right; float: right;
color: #ecf0f1; color: #ecf0f1;
...@@ -909,69 +917,44 @@ blockquote p { ...@@ -909,69 +917,44 @@ blockquote p {
width: auto !important; width: auto !important;
} }
.entry-content table, .qa-c-item-content table {
.qa-q-view-content,
.qa-a-item-content,
.qa-c-item-content {
word-break: break-word;
}
.qa-q-view-content table, .qa-a-item-content table, .qa-c-item-content table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
border: 1px solid #ecf0f1; border: 1px solid #ecf0f1;
font-size: 14px; font-size: 14px;
margin-bottom: 20px; margin-bottom: 20px;
} }
.entry-content tr:hover, .qa-c-item-content tr:hover { .qa-q-view-content tr:hover, .qa-a-item-content tr:hover, .qa-c-item-content tr:hover {
background-color: #ecf0f1; background-color: #ecf0f1;
} }
.entry-content th, .entry-content td, .qa-c-item-content th, .qa-c-item-content td { .qa-q-view-content th, .qa-a-item-content th, .qa-c-item-content th,
.qa-q-view-content td, .qa-a-item-content td, .qa-c-item-content td {
padding: 10px; padding: 10px;
border-width: 1px; border-width: 1px;
border-style: solid; border-style: solid;
text-align: left; text-align: left;
} }
.entry-content th, .qa-c-item-content th { .qa-q-view-content th, .qa-a-item-content th, .qa-c-item-content th {
background-color: #bdc3c7; background-color: #bdc3c7;
border-color: #95a5a6; border-color: #95a5a6;
font-weight: 700; font-weight: 700;
} }
.entry-content td, .qa-c-item-content td { .qa-q-view-content td, .qa-a-item-content td, .qa-c-item-content td {
border-color: #ecf0f1; border-color: #ecf0f1;
} }
.entry-content ul, .qa-c-item-content ul { .qa-q-view-content ul, .qa-a-item-content ul, .qa-c-item-content ul,
margin: 20px 0 20px 20px; .qa-q-view-content ol, .qa-a-item-content ol, .qa-c-item-content ol {
padding: 0; margin-left: 20px;
}
.entry-content ul > li, .qa-c-item-content ul > li {
list-style: none;
margin: .4em 0;
position: relative;
}
.entry-content ul > li:before, .qa-c-item-content ul > li:before {
font-family: "fontello";
font-style: normal;
font-weight: normal;
content: '\e82c';
position: absolute;
left: -1.6em;
top: 4px;
display: inline-block;
text-decoration: inherit;
width: 1em;
margin: 0 .2em;
text-align: center;
font-variant: normal;
text-transform: none;
line-height: 1em;
}
.entry-content ul > li > ul, .qa-c-item-content ul > li > ul {
margin: 0 0 0 20px;
}
.entry-content ol, .qa-c-item-content ol {
margin: 20px 0 20px 20px;
padding: 0; padding: 0;
} }
.entry-content ol > li, .qa-c-item-content ol > li {
margin: .4em 0;
}
.entry-content ol > li > ol, .qa-c-item-content ol > li > ol {
margin: 0 0 0 20px;
}
.qa-waiting { .qa-waiting {
background: url('images/spinner-icon-14x14.gif?1410117644') no-repeat center; background: url('images/spinner-icon-14x14.gif?1410117644') no-repeat center;
...@@ -1467,7 +1450,8 @@ blockquote p { ...@@ -1467,7 +1450,8 @@ blockquote p {
} }
.qa-template-admin .qa-q-item-content { .qa-template-admin .qa-q-item-content {
font-size: 0.75em; font-size: 0.75em;
line-height: 2em; max-height: 150px;
overflow-y: auto;
} }
.qa-template-admin .qa-q-item-avatar-meta { .qa-template-admin .qa-q-item-avatar-meta {
border-top: 1px solid #ecf0f1; border-top: 1px solid #ecf0f1;
...@@ -1995,6 +1979,9 @@ input[type="submit"], button { ...@@ -1995,6 +1979,9 @@ input[type="submit"], button {
width: auto; width: auto;
} }
.qa-part-form-profile .qa-form-wide-data {
word-break: break-word;
}
.qa-part-form-profile .qa-form-tall-image { .qa-part-form-profile .qa-form-tall-image {
text-align: center; text-align: center;
} }
...@@ -2485,6 +2472,11 @@ input[type="submit"], button { ...@@ -2485,6 +2472,11 @@ input[type="submit"], button {
position: relative; position: relative;
min-height: 190px; min-height: 190px;
} }
/* highlight selected answer */
.qa-a-list-item:target{
-webkit-animation: answer-highlight 2s ease-in-out;
animation: answer-highlight 2s ease-in-out;
}
.qa-a-item-avatar-meta { .qa-a-item-avatar-meta {
font-size: 12px; font-size: 12px;
...@@ -2617,6 +2609,12 @@ input[type="submit"], button { ...@@ -2617,6 +2609,12 @@ input[type="submit"], button {
background-color: #f4f4f4; background-color: #f4f4f4;
position: relative; position: relative;
} }
/* highlight selected comment */
.qa-c-list-item:target {
-webkit-animation: comment-highlight 2s ease-in-out;
animation: comment-highlight 2s ease-in-out;
}
.qa-c-list-item .qa-form-light-button { .qa-c-list-item .qa-form-light-button {
padding: 10px 15px; padding: 10px 15px;
background: #bdc3c7 none center no-repeat; background: #bdc3c7 none center no-repeat;
...@@ -3614,3 +3612,21 @@ input[type="submit"], button { ...@@ -3614,3 +3612,21 @@ input[type="submit"], button {
.icon-reply:before { .icon-reply:before {
content: '\e82e'; content: '\e82e';
} }
@-webkit-keyframes answer-highlight {
0% { background-color: #ffffaa; }
100% { background-color: #fff; }
}
@keyframes answer-highlight {
0% { background-color: #ffffaa; }
100% { background-color: #fff; }
}
@-webkit-keyframes comment-highlight {
0% { background-color: #ffffaa; }
100% { background-color: #f4f4f4; }
}
@keyframes comment-highlight {
0% { background-color: #ffffaa; }
100% { background-color: #f4f4f4; }
}
...@@ -36,6 +36,9 @@ class qa_html_theme extends qa_html_theme_base ...@@ -36,6 +36,9 @@ class qa_html_theme extends qa_html_theme_base
{ {
protected $theme = 'snowflat'; protected $theme = 'snowflat';
// use local font files instead of Google Fonts
private $localfonts = true;
// theme subdirectories // theme subdirectories
private $js_dir = 'js'; private $js_dir = 'js';
private $icon_url = 'images/icons'; private $icon_url = 'images/icons';
...@@ -44,7 +47,7 @@ class qa_html_theme extends qa_html_theme_base ...@@ -44,7 +47,7 @@ class qa_html_theme extends qa_html_theme_base
private $welcome_widget_class = 'wet-asphalt'; private $welcome_widget_class = 'wet-asphalt';
private $ask_search_box_class = 'turquoise'; private $ask_search_box_class = 'turquoise';
// Size of the user avatar in the navigation bar // Size of the user avatar in the navigation bar
private $nav_bar_avatar_size = 32; private $nav_bar_avatar_size = 52;
/** /**
* Adding aditional meta for responsive design * Adding aditional meta for responsive design
...@@ -68,8 +71,11 @@ class qa_html_theme extends qa_html_theme_base ...@@ -68,8 +71,11 @@ class qa_html_theme extends qa_html_theme_base
if ($this->isRTL) if ($this->isRTL)
$this->content['css_src'][] = $this->rooturl . 'qa-styles-rtl.css?' . QA_VERSION; $this->content['css_src'][] = $this->rooturl . 'qa-styles-rtl.css?' . QA_VERSION;
// add Ubuntu font CSS file // add Ubuntu font CSS file from Google Fonts
$this->content['css_src'][] = 'http://fonts.googleapis.com/css?family=Ubuntu:400,700,400italic,700italic'; if ($this->localfonts)
$this->content['css_src'][] = $this->rooturl . 'fonts/ubuntu.css?' . QA_VERSION;
else
$this->content['css_src'][] = '//fonts.googleapis.com/css?family=Ubuntu:400,700,400italic,700italic';
parent::head_css(); parent::head_css();
...@@ -567,20 +573,23 @@ class qa_html_theme extends qa_html_theme_base ...@@ -567,20 +573,23 @@ class qa_html_theme extends qa_html_theme_base
); );
} }
$auth_icon = strip_tags($tobar_avatar, '<img>'); $avatar = strip_tags($tobar_avatar, '<img>');
if (!empty($avatar))
$handle = '';
} }
else { else {
// display login icon and label // display login icon and label
$handle = $this->content['navigation']['user']['login']['label']; $handle = $this->content['navigation']['user']['login']['label'];
$toggleClass = 'qam-logged-out'; $toggleClass = 'qam-logged-out';
$auth_icon = '<i class="icon-key qam-auth-key"></i>'; $avatar = '<i class="icon-key qam-auth-key"></i>';
} }
// finally output avatar with div tag // finally output avatar with div tag
$handleBlock = empty($handle) ? '' : '<div class="qam-account-handle">' . qa_html($handle) . '</div>';
$this->output( $this->output(
'<div id="qam-account-toggle" class="' . $toggleClass . '">', '<div id="qam-account-toggle" class="' . $toggleClass . '">',
$auth_icon, $avatar,
'<div class="qam-account-handle">' . qa_html($handle) . '</div>', $handleBlock,
'</div>' '</div>'
); );
} }
......
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