Commit cb812352 by Scott

Merge branch 'dev' (1.7.3) into 1.8

parents 074e21a7 768ed4d1
Options -Indexes
DirectoryIndex index.php
<IfModule mod_rewrite.c>
RewriteEngine On
......
sudo: false
language: php
php:
- 5.3
- 5.4
- 5.5
- '5.3'
- '5.4'
- '5.5'
- '5.6'
- '7.0'
notifications:
email:
......@@ -12,32 +16,16 @@ notifications:
before_script:
# PHP_CodeSniffer
- pear install pear/PHP_CodeSniffer
- phpenv rehash
- curl -o phpcs.phar https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar
# PHP Copy/Paste Detector
- curl -o phpcpd.phar https://phar.phpunit.de/phpcpd.phar
# PHP Mess Detector
- pear config-set preferred_state beta
- printf "\n\n" | pecl install imagick
- pear channel-discover pear.phpmd.org
- pear channel-discover pear.pdepend.org
- pear install --alldeps phpmd/PHP_PMD
- phpenv rehash
# PHPLOC
- curl -o phploc.phar https://phar.phpunit.de/phploc-2.0.6.phar
# Basic config required for PHPUnit
- cp qa-tests/phpunit-qa-config.php qa-config.php
script:
# Most of this has been turned off until the code is actually close to passing
# PHP_CodeSniffer
#- phpcs --report=emacs --extensions=php --standard=qa-tests/phpcs/ruleset.xml .
# PHP Mess Detector
#- phpmd --exclude qa-include/vendor/ . text codesize
#- phpmd --exclude qa-include/vendor/ . text unusedcode
# PHP_CodeSniffer (turned off for now)
#- php phpcs.phar --report=emacs --extensions=php --standard=qa-tests/phpcs/ruleset.xml .
# PHP Copy/Paste Detector
- php phpcpd.phar --exclude vendor .
# PHPLOC
- php phploc.phar .
# PHPUnit
- phpunit --bootstrap qa-tests/autoload.php qa-tests
1.7.2
\ No newline at end of file
1.7.3
\ No newline at end of file
......@@ -70,7 +70,7 @@ function qa_recalc_update(elem, state, noteid)
qa_recalc_update(elem, lines[1], noteid);
} else if (lines[0]=='0') {
document.getElementById(noteid).innerHTML=lines[2];
document.getElementById(noteid).innerHTML=lines[1];
qa_recalc_cleanup(elem);
} else {
......
......@@ -83,18 +83,18 @@
$question=$question+qa_page_q_post_rules($question, null, null, $qchildposts); // array union
$answer=$answer+qa_page_q_post_rules($answer, $question, $qchildposts, $achildposts);
foreach ($achildposts as $key => $achildpost)
$achildposts[$key]=$achildpost+qa_page_q_post_rules($achildpost, $answer, $achildposts, null);
$commentsfollows=qa_page_q_load_c_follows($question, $qchildposts, $achildposts);
$achildposts = qa_page_q_load_c_follows($question, array(), $achildposts);
foreach ($commentsfollows as $key => $commentfollow)
$commentsfollows[$key]=$commentfollow+qa_page_q_post_rules($commentfollow, $answer, $commentsfollows, null);
$usershtml=qa_userids_handles_html(array_merge(array($answer), $achildposts), true);
qa_sort_by($achildposts, 'created');
$usershtml=qa_userids_handles_html(array_merge(array($answer), $commentsfollows), true);
qa_sort_by($commentsfollows, 'created');
$a_view=qa_page_q_answer_view($question, $answer, ($answer['postid']==$question['selchildid']) && ($answer['type']=='A'),
$usershtml, false);
$a_view['c_list']=qa_page_q_comment_follow_list($question, $answer, $achildposts, false, $usershtml, false, null);
$a_view['c_list']=qa_page_q_comment_follow_list($question, $answer, $commentsfollows, false, $usershtml, false, null);
$themeclass=qa_load_theme_class(qa_get_site_theme(), 'ajax-answer', null, null);
$themeclass->initialize();
......
......@@ -119,7 +119,7 @@
$blob=qa_db_blob_read($blobid);
if (defined('QA_BLOBS_DIRECTORY') && !isset($blob['content']))
if (isset($blob) && defined('QA_BLOBS_DIRECTORY') && !isset($blob['content']))
$blob['content']=qa_read_blob_file($blobid, $blob['format']);
return $blob;
......@@ -133,7 +133,11 @@
{
if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
return file_get_contents(qa_get_blob_filename($blobid, $format));
$filename = qa_get_blob_filename($blobid, $format);
if (is_readable($filename))
return file_get_contents($filename);
else
return null;
}
......
......@@ -118,12 +118,12 @@
foreach ($useridhandles as $useridhandle) {
// only add each user to the array once
$uid = isset($useridhandle['userid']) ? $useridhandle['userid'] : null;
if ($uid && $useridhandle['handle'] && !isset($usershtml[$uid])) {
if ($uid && !isset($usershtml[$uid])) {
$usershtml[$uid] = qa_get_one_user_html($useridhandle['handle'], $microdata, @$favoritemap['user'][$uid]);
}
$luid = isset($useridhandle['lastuserid']) ? $useridhandle['lastuserid'] : null;
if ($luid && $useridhandle['lasthandle'] && !isset($usershtml[$luid])) {
if ($luid && !isset($usershtml[$luid])) {
$usershtml[$luid] = qa_get_one_user_html($useridhandle['lasthandle'], $microdata, @$favoritemap['user'][$luid]);
}
}
......@@ -383,7 +383,7 @@
// Post content
if (@$options['contentview'] && !empty($post['content'])) {
if (@$options['contentview'] && isset($post['content'])) {
$viewer=qa_load_viewer($post['content'], $post['format']);
$fields['content']=$viewer->get_html($post['content'], $post['format'], array(
......@@ -1381,7 +1381,7 @@
if (!$ismyuser)
unset($navigation['favorites']);
if (!$ismyuser || !qa_opt('allow_private_messages') || !qa_opt('show_message_history'))
if (QA_FINAL_EXTERNAL_USERS || !$ismyuser || !qa_opt('allow_private_messages') || !qa_opt('show_message_history'))
unset($navigation['messages']);
return $navigation;
......@@ -1573,7 +1573,7 @@
{
require_once QA_INCLUDE_DIR.'util/string.php';
$text=qa_post_text($fieldname);
$text=qa_remove_utf8mb4(qa_post_text($fieldname));
if (qa_opt('tag_separator_comma'))
return array_unique(preg_split('/\s*,\s*/', trim(qa_strtolower(strtr($text, '/', ' '))), -1, PREG_SPLIT_NO_EMPTY));
......@@ -1970,6 +1970,15 @@
return $viewer->get_html($content, $format, $options);
}
/**
* Retrieve title from HTTP POST, appropriately sanitised.
*/
function qa_get_post_title($fieldname)
{
require_once QA_INCLUDE_DIR.'util/string.php';
return qa_remove_utf8mb4(qa_post_text($fieldname));
}
function qa_get_post_content($editorfield, $contentfield, &$ineditor, &$incontent, &$informat, &$intext)
/*
......@@ -1977,13 +1986,16 @@
Assigns the module's output to $incontent and $informat, editor's name in $ineditor, text rendering of content in $intext
*/
{
$ineditor=qa_post_text($editorfield);
require_once QA_INCLUDE_DIR.'util/string.php';
$ineditor=qa_post_text($editorfield);
$editor=qa_load_module('editor', $ineditor);
$readdata=$editor->read_post($contentfield);
$incontent=$readdata['content'];
// sanitise 4-byte Unicode
$incontent=qa_remove_utf8mb4($readdata['content']);
$informat=$readdata['format'];
$intext=qa_viewer_text($incontent, $informat);
$intext=qa_remove_utf8mb4(qa_viewer_text($incontent, $informat));
}
......
......@@ -437,6 +437,11 @@
if ($oldquestion['type']!='Q_HIDDEN')
qa_fatal_error('Tried to delete a non-hidden question');
$params = array(
'postid' => $oldquestion['postid'],
'oldquestion' => $oldquestion,
);
qa_report_event('q_delete_before', $userid, $handle, $cookieid, $params);
if (isset($oldclosepost) && ($oldclosepost['parentid']==$oldquestion['postid'])) {
......@@ -448,11 +453,6 @@
$useridvotes=qa_db_uservote_post_get($oldquestion['postid']);
$oldpath=qa_db_post_get_category_path($oldquestion['postid']);
$params = array(
'postid' => $oldquestion['postid'],
'oldquestion' => $oldquestion,
);
qa_post_unindex($oldquestion['postid']);
qa_db_post_delete($oldquestion['postid']); // also deletes any related voteds due to foreign key cascading
qa_update_counts_for_q(null);
......
......@@ -36,9 +36,13 @@
*/
{
require_once QA_INCLUDE_DIR.'db/users.php';
require_once QA_INCLUDE_DIR.'util/string.php';
$errors=array();
// sanitise 4-byte Unicode
$handle = qa_remove_utf8mb4($handle);
$filtermodules=qa_load_modules_with('filter', 'filter_handle');
foreach ($filtermodules as $filtermodule) {
......
......@@ -392,7 +392,7 @@
*/
{
return qa_db_read_one_assoc(qa_db_query_sub(
'SELECT blobid, content, format FROM ^blobs WHERE blobid>=# AND content IS NOT NULL',
'SELECT blobid, content, format FROM ^blobs WHERE blobid>=# AND content IS NOT NULL LIMIT 1',
$startblobid
), true);
}
......@@ -413,7 +413,7 @@
*/
{
return qa_db_read_one_assoc(qa_db_query_sub(
'SELECT blobid, format FROM ^blobs WHERE blobid>=# AND content IS NULL',
'SELECT blobid, format FROM ^blobs WHERE blobid>=# AND content IS NULL LIMIT 1',
$startblobid
), true);
}
......@@ -421,4 +421,4 @@
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
*/
......@@ -639,7 +639,7 @@
{
$selectspec=qa_db_posts_basic_selectspec();
$selectspec['source'].=" WHERE ^posts.postid=(SELECT IF(LEFT(parent.type, 1)='A', parent.parentid, parent.postid) FROM ^posts AS child LEFT JOIN ^posts AS parent ON parent.postid=child.parentid WHERE child.postid=#)";
$selectspec['source'].=" WHERE ^posts.postid=(SELECT IF(LEFT(parent.type, 1)='A', parent.parentid, parent.postid) FROM ^posts AS child LEFT JOIN ^posts AS parent ON parent.postid=child.parentid WHERE child.postid=# AND parent.type IN('Q','A'))";
$selectspec['arguments']=array($postid);
$selectspec['single']=true;
......
......@@ -253,6 +253,9 @@
'flagging_notify_first' => 1,
'max_num_q_tags' => 2,
'max_rate_ip_logins' => 1,
'min_len_a_content' => 1,
'min_len_c_content' => 1,
'min_len_q_title' => 1,
'page_size_activity' => 1,
'page_size_ask_check_qs' => 3,
'page_size_ask_tags' => 3,
......
......@@ -95,7 +95,7 @@ if ($permiterror) {
$captchareason = qa_user_captcha_reason();
$in['title'] = qa_post_text('title'); // allow title and tags to be posted by an external form
$in['title'] = qa_get_post_title('title'); // allow title and tags to be posted by an external form
$in['extra'] = qa_opt('extra_field_active') ? qa_post_text('extra') : null;
if (qa_using_tags()) {
......
......@@ -418,7 +418,7 @@
$in=array();
if ($question['editable']) {
$in['title']=qa_post_text('q_title');
$in['title']=qa_get_post_title('q_title');
qa_get_post_content('q_editor', 'q_content', $in['editor'], $in['content'], $in['format'], $in['text']);
$in['extra']=qa_opt('extra_field_active') ? qa_post_text('q_extra') : null;
}
......
......@@ -399,12 +399,22 @@
if (isset($closepost)) {
if ($closepost['basetype']=='Q') {
$q_view['closed']=array(
'state' => qa_lang_html('main/closed'),
'label' => qa_lang_html('question/closed_as_duplicate'),
'content' => qa_html(qa_block_words_replace($closepost['title'], qa_get_block_words_preg())),
'url' => qa_q_path_html($closepost['postid'], $closepost['title']),
);
if ($closepost['hidden']) {
// don't show link for hidden questions
$q_view['closed']=array(
'state' => qa_lang_html('main/closed'),
'label' => qa_lang_html('main/closed'),
'content' => '',
);
}
else {
$q_view['closed']=array(
'state' => qa_lang_html('main/closed'),
'label' => qa_lang_html('question/closed_as_duplicate'),
'content' => qa_html(qa_block_words_replace($closepost['title'], qa_get_block_words_preg())),
'url' => qa_q_path_html($closepost['postid'], $closepost['title']),
);
}
} elseif ($closepost['type']=='NOTE') {
$viewer=qa_load_viewer($closepost['content'], $closepost['format']);
......
......@@ -32,7 +32,7 @@
if (!strlen($handle)) {
$handle = qa_get_logged_in_handle();
qa_redirect(isset($handle) ? 'user/'.$handle : 'users');
qa_redirect(!empty($handle) ? 'user/'.$handle : 'users');
}
......
......@@ -70,6 +70,10 @@ class qa_filter_basic
$this->validate_field_length($errors, $question, 'content', 0, QA_DB_MAX_CONTENT_LENGTH); // for storage
$this->validate_field_length($errors, $question, 'text', qa_opt('min_len_q_content'), null); // for display
// ensure content error is shown
if (isset($errors['text'])) {
$errors['content'] = $errors['text'];
}
if (isset($question['tags'])) {
$counttags = count($question['tags']);
......@@ -149,7 +153,7 @@ class qa_filter_basic
$errorKey = $key;
}
// skip the field is key not set (for example, 'title' when recategorizing questions)
// skip the field if key not set (for example, 'title' when recategorizing questions)
if (array_key_exists($key, $post)) {
$length = qa_strlen($post[$key]);
......
......@@ -21,8 +21,8 @@
*/
define('QA_VERSION', '1.7.2'); // also used as suffix for .js and .css requests
define('QA_BUILD_DATE', '2015-11-05');
define('QA_VERSION', '1.7.3'); // also used as suffix for .js and .css requests
define('QA_BUILD_DATE', '2016-01-29');
/**
......
......@@ -47,7 +47,7 @@ qa_db_connect('qa_blob_db_fail_handler');
$blob = qa_read_blob(qa_get('qa_blobid'));
if (isset($blob)) {
if (isset($blob) && isset($blob['content'])) {
// allows browsers and proxies to cache the blob (30 days)
header('Cache-Control: max-age=2592000, public');
......
......@@ -36,7 +36,7 @@
{
return array(
'login' => wp_login_url(qa_opt('site_url').$redirect_back_to_url),
'register' => site_url('wp-login.php?action=register'),
'register' => function_exists('wp_registration_url') ? wp_registration_url() : site_url('wp-login.php?action=register'),
'logout' => strtr(wp_logout_url(), array('&amp;' => '&')),
);
}
......@@ -163,4 +163,4 @@
/*
Omit PHP closing tag to help avoid accidental output
*/
\ No newline at end of file
*/
......@@ -534,6 +534,20 @@
return $string;
}
/**
* Removes 4-byte Unicode characters (e.g. emoji) from a string due to missing support in MySQL < 5.5.3.
* @param string $string
* @return string
*/
function qa_remove_utf8mb4($string)
{
return preg_replace('%(?:
\xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)%xs', '', $string);
}
function qa_block_words_explode($wordstring)
/*
......
......@@ -28,25 +28,25 @@ class SMTP
{
/**
* The PHPMailer SMTP version number.
* @type string
* @var string
*/
const VERSION = '5.2.13';
const VERSION = '5.2.14';
/**
* SMTP line break constant.
* @type string
* @var string
*/
const CRLF = "\r\n";
/**
* The SMTP port to use if one is not specified.
* @type integer
* @var integer
*/
const DEFAULT_SMTP_PORT = 25;
/**
* The maximum line length allowed by RFC 2822 section 2.1.1
* @type integer
* @var integer
*/
const MAX_LINE_LENGTH = 998;
......@@ -77,15 +77,15 @@ class SMTP
/**
* The PHPMailer SMTP Version number.
* @type string
* @var string
* @deprecated Use the `VERSION` constant instead
* @see SMTP::VERSION
*/
public $Version = '5.2.13';
public $Version = '5.2.14';
/**
* SMTP server port number.
* @type integer
* @var integer
* @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead
* @see SMTP::DEFAULT_SMTP_PORT
*/
......@@ -93,7 +93,7 @@ class SMTP
/**
* SMTP reply line ending.
* @type string
* @var string
* @deprecated Use the `CRLF` constant instead
* @see SMTP::CRLF
*/
......@@ -107,7 +107,7 @@ class SMTP
* * self::DEBUG_SERVER (`2`) Client commands and server responses
* * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status
* * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages
* @type integer
* @var integer
*/
public $do_debug = self::DEBUG_OFF;
......@@ -122,7 +122,7 @@ class SMTP
* <code>
* $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
* </code>
* @type string|callable
* @var string|callable
*/
public $Debugoutput = 'echo';
......@@ -130,7 +130,7 @@ class SMTP
* Whether to use VERP.
* @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
* @link http://www.postfix.org/VERP_README.html Info on VERP
* @type boolean
* @var boolean
*/
public $do_verp = false;
......@@ -139,26 +139,26 @@ class SMTP
* Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
* This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
* @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2
* @type integer
* @var integer
*/
public $Timeout = 300;
/**
* How long to wait for commands to complete, in seconds.
* Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
* @type integer
* @var integer
*/
public $Timelimit = 300;
/**
* The socket for the server connection.
* @type resource
* @var resource
*/
protected $smtp_conn;
/**
* Error information, if any, for the last SMTP command.
* @type array
* @var array
*/
protected $error = array(
'error' => '',
......@@ -170,7 +170,7 @@ class SMTP
/**
* The reply the server sent to us for HELO.
* If null, no HELO string has yet been received.
* @type string|null
* @var string|null
*/
protected $helo_rply = null;
......@@ -181,13 +181,13 @@ class SMTP
* represents the server name. In case of HELO it is the only element of the array.
* Other values can be boolean TRUE or an array containing extension options.
* If null, no HELO/EHLO string has yet been received.
* @type array|null
* @var array|null
*/
protected $server_caps = null;
/**
* The most recent reply received from the server.
* @type string
* @var string
*/
protected $last_reply = '';
......@@ -356,7 +356,7 @@ class SMTP
* @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5, XOAUTH2)
* @param string $realm The auth realm for NTLM
* @param string $workstation The auth workstation for NTLM
* @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth)
* @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth)
* @return bool True if successfully authenticated.* @access public
*/
public function authenticate(
......@@ -814,15 +814,15 @@ class SMTP
* Sets the TO argument to $toaddr.
* Returns true if the recipient was accepted false if it was rejected.
* Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
* @param string $toaddr The address the message is being sent to
* @param string $address The address the message is being sent to
* @access public
* @return boolean
*/
public function recipient($toaddr)
public function recipient($address)
{
return $this->sendCommand(
'RCPT TO',
'RCPT TO:<' . $toaddr . '>',
'RCPT TO:<' . $address . '>',
array(250, 251)
);
}
......@@ -841,9 +841,9 @@ class SMTP
/**
* Send a command to an SMTP server and check its return code.
* @param string $command The command name - not sent to the server
* @param string $command The command name - not sent to the server
* @param string $commandstring The actual command to send
* @param integer|array $expect One or more expected integer success codes
* @param integer|array $expect One or more expected integer success codes
* @access protected
* @return boolean True on success.
*/
......@@ -853,6 +853,11 @@ class SMTP
$this->setError("Called $command without being connected");
return false;
}
//Reject line breaks in all commands
if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) {
$this->setError("Command '$command' contained line breaks");
return false;
}
$this->client_send($commandstring . self::CRLF);
$this->last_reply = $this->get_lines();
......
......@@ -56,7 +56,7 @@ class qa_facebook_login_page
if ($fb_userid) {
try {
$user=$facebook->api('/me?fields=email,name,verified,location,website,about,picture');
$user=$facebook->api('/me?fields=email,name,verified,location,website,about,picture.width(250)');
if (is_array($user))
qa_log_in_external_user('facebook', $fb_userid, array(
......
No preview for this file type
No preview for this file type
No preview for this file type
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