Commit 842abb91 by Scott

Merge branch 'bugfix' into dev

parents f6276f4b d757fa58
1.8.1 1.8.2
\ No newline at end of file \ No newline at end of file
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
More about this license: http://www.question2answer.org/license.php More about this license: http://www.question2answer.org/license.php
*/ */
require_once QA_INCLUDE_DIR . 'app/posts.php';
require_once QA_INCLUDE_DIR . 'app/users.php'; require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR . 'app/limits.php'; require_once QA_INCLUDE_DIR . 'app/limits.php';
require_once QA_INCLUDE_DIR . 'db/selects.php'; require_once QA_INCLUDE_DIR . 'db/selects.php';
...@@ -37,7 +38,7 @@ list($question, $childposts) = qa_db_select_with_pending( ...@@ -37,7 +38,7 @@ list($question, $childposts) = qa_db_select_with_pending(
// Check if the question exists, is not closed, and whether the user has permission to do this // Check if the question exists, is not closed, and whether the user has permission to do this
if (@$question['basetype'] == 'Q' && !isset($question['closedbyid']) && !qa_user_post_permit_error('permit_post_a', $question, QA_LIMIT_ANSWERS)) { if (@$question['basetype'] == 'Q' && !qa_post_is_closed($question) && !qa_user_post_permit_error('permit_post_a', $question, QA_LIMIT_ANSWERS)) {
require_once QA_INCLUDE_DIR . 'app/captcha.php'; require_once QA_INCLUDE_DIR . 'app/captcha.php';
require_once QA_INCLUDE_DIR . 'app/format.php'; require_once QA_INCLUDE_DIR . 'app/format.php';
require_once QA_INCLUDE_DIR . 'app/post-create.php'; require_once QA_INCLUDE_DIR . 'app/post-create.php';
......
...@@ -35,8 +35,8 @@ if (qa_get_logged_in_level() < QA_USER_LEVEL_ADMIN) { ...@@ -35,8 +35,8 @@ if (qa_get_logged_in_level() < QA_USER_LEVEL_ADMIN) {
} }
$uri = qa_post_text('uri'); $uri = qa_post_text('uri');
$version = qa_post_text('version'); $currentVersion = qa_post_text('version');
$isCore = qa_post_text('isCore') === 'true'; $isCore = qa_post_text('isCore') === "true";
if ($isCore) { if ($isCore) {
$contents = qa_retrieve_url($uri); $contents = qa_retrieve_url($uri);
...@@ -58,7 +58,7 @@ if ($isCore) { ...@@ -58,7 +58,7 @@ if ($isCore) {
$metadata = $metadataUtil->fetchFromUrl($uri); $metadata = $metadataUtil->fetchFromUrl($uri);
if (strlen(@$metadata['version']) > 0) { if (strlen(@$metadata['version']) > 0) {
if (strcmp($metadata['version'], $version)) { if (version_compare($currentVersion, $metadata['version']) < 0) {
if (qa_qa_version_below(@$metadata['min_q2a'])) { if (qa_qa_version_below(@$metadata['min_q2a'])) {
$versionResponse = strtr(qa_lang_html('admin/version_requires_q2a'), array( $versionResponse = strtr(qa_lang_html('admin/version_requires_q2a'), array(
'^1' => qa_html('v' . $metadata['version']), '^1' => qa_html('v' . $metadata['version']),
......
...@@ -43,7 +43,7 @@ function qa_suspend_notifications($suspend = true) ...@@ -43,7 +43,7 @@ function qa_suspend_notifications($suspend = true)
/** /**
* Send email to person with $userid and/or $email and/or $handle (null/invalid values are ignored or retrieved from * Send email to person with $userid and/or $email and/or $handle (null/invalid values are ignored or retrieved from
* user database as appropriate). Email uses $subject and $body, after substituting each key in $subs with its * user database as appropriate). Email uses $subject and $body, after substituting each key in $subs with its
* corresponding value, plus applying some standard substitutions such as ^site_title, ^handle and ^email. * corresponding value, plus applying some standard substitutions such as ^site_title, ^site_url, ^handle and ^email.
* @param $userid * @param $userid
* @param $email * @param $email
* @param $handle * @param $handle
...@@ -100,6 +100,7 @@ function qa_send_notification($userid, $email, $handle, $subject, $body, $subs, ...@@ -100,6 +100,7 @@ function qa_send_notification($userid, $email, $handle, $subject, $body, $subs,
if (isset($email) && qa_email_validate($email)) { if (isset($email) && qa_email_validate($email)) {
$subs['^site_title'] = qa_opt('site_title'); $subs['^site_title'] = qa_opt('site_title');
$subs['^site_url'] = qa_opt('site_url');
$subs['^handle'] = $handle; $subs['^handle'] = $handle;
$subs['^email'] = $email; $subs['^email'] = $email;
$subs['^open'] = "\n"; $subs['^open'] = "\n";
......
...@@ -293,6 +293,7 @@ function qa_post_html_fields($post, $userid, $cookieid, $usershtml, $dummy, $opt ...@@ -293,6 +293,7 @@ function qa_post_html_fields($post, $userid, $cookieid, $usershtml, $dummy, $opt
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/updates.php'; require_once QA_INCLUDE_DIR . 'app/updates.php';
require_once QA_INCLUDE_DIR . 'app/posts.php';
if (isset($options['blockwordspreg'])) if (isset($options['blockwordspreg']))
require_once QA_INCLUDE_DIR . 'util/string.php'; require_once QA_INCLUDE_DIR . 'util/string.php';
...@@ -320,15 +321,16 @@ function qa_post_html_fields($post, $userid, $cookieid, $usershtml, $dummy, $opt ...@@ -320,15 +321,16 @@ function qa_post_html_fields($post, $userid, $cookieid, $usershtml, $dummy, $opt
$fields['tags'] = 'id="' . qa_html($elementid) . '"'; $fields['tags'] = 'id="' . qa_html($elementid) . '"';
$fields['classes'] = ($isquestion && $favoritedview && @$post['userfavoriteq']) ? 'qa-q-favorited' : ''; $fields['classes'] = ($isquestion && $favoritedview && @$post['userfavoriteq']) ? 'qa-q-favorited' : '';
if ($isquestion && isset($post['closedbyid'])) if ($isquestion && qa_post_is_closed($post)) {
$fields['classes'] = ltrim($fields['classes'] . ' qa-q-closed'); $fields['classes'] = ltrim($fields['classes'] . ' qa-q-closed');
}
if ($microdata) { if ($microdata) {
if ($isanswer) { if ($isanswer) {
$fields['tags'] .= ' itemprop="suggestedAnswer' . ($isselected ? ' acceptedAnswer' : '') . '" itemscope itemtype="http://schema.org/Answer"'; $fields['tags'] .= ' itemprop="suggestedAnswer' . ($isselected ? ' acceptedAnswer' : '') . '" itemscope itemtype="https://schema.org/Answer"';
} }
if ($iscomment) { if ($iscomment) {
$fields['tags'] .= ' itemscope itemtype="http://schema.org/Comment"'; $fields['tags'] .= ' itemscope itemtype="https://schema.org/Comment"';
} }
} }
...@@ -572,8 +574,12 @@ function qa_post_html_fields($post, $userid, $cookieid, $usershtml, $dummy, $opt ...@@ -572,8 +574,12 @@ function qa_post_html_fields($post, $userid, $cookieid, $usershtml, $dummy, $opt
$fields['what'] = qa_lang_html($isquestion ? 'main/asked' : ($isanswer ? 'main/answered' : 'main/commented')); $fields['what'] = qa_lang_html($isquestion ? 'main/asked' : ($isanswer ? 'main/answered' : 'main/commented'));
if (@$options['whatlink'] && strlen(@$options['q_request'])) { if (@$options['whatlink'] && strlen(@$options['q_request'])) {
$fields['what_url'] = ($post['basetype'] == 'Q') ? qa_path_html($options['q_request']) $fields['what_url'] = $post['basetype'] == 'Q'
? qa_path_html($options['q_request'])
: qa_path_html($options['q_request'], array('show' => $postid), null, null, qa_anchor($post['basetype'], $postid)); : qa_path_html($options['q_request'], array('show' => $postid), null, null, qa_anchor($post['basetype'], $postid));
if ($microdata) {
$fields['what_url_tags'] = ' itemprop="url"';
}
} }
} }
...@@ -617,7 +623,7 @@ function qa_post_html_fields($post, $userid, $cookieid, $usershtml, $dummy, $opt ...@@ -617,7 +623,7 @@ function qa_post_html_fields($post, $userid, $cookieid, $usershtml, $dummy, $opt
( // otherwise check if one of these conditions is fulfilled... ( // otherwise check if one of these conditions is fulfilled...
(!isset($post['created'])) || // ... we didn't show the created time (should never happen in practice) (!isset($post['created'])) || // ... we didn't show the created time (should never happen in practice)
($post['hidden'] && ($post['updatetype'] == QA_UPDATE_VISIBLE)) || // ... the post was hidden as the last action ($post['hidden'] && ($post['updatetype'] == QA_UPDATE_VISIBLE)) || // ... the post was hidden as the last action
(isset($post['closedbyid']) && ($post['updatetype'] == QA_UPDATE_CLOSED)) || // ... the post was closed as the last action (qa_post_is_closed($post) && $post['updatetype'] == QA_UPDATE_CLOSED) || // ... the post was closed as the last action
(abs($post['updated'] - $post['created']) > 300) || // ... or over 5 minutes passed between create and update times (abs($post['updated'] - $post['created']) > 300) || // ... or over 5 minutes passed between create and update times
($post['lastuserid'] != $post['userid']) // ... or it was updated by a different user ($post['lastuserid'] != $post['userid']) // ... or it was updated by a different user
) )
...@@ -637,7 +643,7 @@ function qa_post_html_fields($post, $userid, $cookieid, $usershtml, $dummy, $opt ...@@ -637,7 +643,7 @@ function qa_post_html_fields($post, $userid, $cookieid, $usershtml, $dummy, $opt
break; break;
case QA_UPDATE_CLOSED: case QA_UPDATE_CLOSED:
$langstring = isset($post['closedbyid']) ? 'main/closed' : 'main/reopened'; $langstring = qa_post_is_closed($post) ? 'main/closed' : 'main/reopened';
break; break;
case QA_UPDATE_TAGS: case QA_UPDATE_TAGS:
...@@ -737,7 +743,7 @@ function qa_message_html_fields($message, $options = array()) ...@@ -737,7 +743,7 @@ function qa_message_html_fields($message, $options = array())
* @param int $postuserid The post user's ID. * @param int $postuserid The post user's ID.
* @param array $usershtml Array of HTML representing usernames. * @param array $usershtml Array of HTML representing usernames.
* @param string $ip The post user's IP. * @param string $ip The post user's IP.
* @param bool|string $microdata Whether to include microdata (no longer used). * @param bool|string $microdata Whether to include microdata.
* @param string $name The author's username. * @param string $name The author's username.
* @return array The HTML. * @return array The HTML.
*/ */
...@@ -757,7 +763,7 @@ function qa_who_to_html($isbyuser, $postuserid, $usershtml, $ip = null, $microda ...@@ -757,7 +763,7 @@ function qa_who_to_html($isbyuser, $postuserid, $usershtml, $ip = null, $microda
if ($microdata) { if ($microdata) {
// duplicate HTML from qa_get_one_user_html() // duplicate HTML from qa_get_one_user_html()
$whohtml = '<span itemprop="author" itemscope itemtype="http://schema.org/Person"><span itemprop="name">' . $whohtml . '</span></span>'; $whohtml = '<span itemprop="author" itemscope itemtype="https://schema.org/Person"><span itemprop="name">' . $whohtml . '</span></span>';
} }
if (isset($ip)) if (isset($ip))
...@@ -838,10 +844,11 @@ function qa_other_to_q_html_fields($question, $userid, $cookieid, $usershtml, $d ...@@ -838,10 +844,11 @@ function qa_other_to_q_html_fields($question, $userid, $cookieid, $usershtml, $d
break; break;
case 'Q-' . QA_UPDATE_CLOSED: case 'Q-' . QA_UPDATE_CLOSED:
$isClosed = qa_post_is_closed($question);
if (@$question['opersonal']) if (@$question['opersonal'])
$langstring = isset($question['closedbyid']) ? 'misc/your_q_closed' : 'misc/your_q_reopened'; $langstring = $isClosed ? 'misc/your_q_closed' : 'misc/your_q_reopened';
else else
$langstring = isset($question['closedbyid']) ? 'main/closed' : 'main/reopened'; $langstring = $isClosed ? 'main/closed' : 'main/reopened';
break; break;
case 'Q-' . QA_UPDATE_TAGS: case 'Q-' . QA_UPDATE_TAGS:
...@@ -2355,6 +2362,7 @@ function qa_favorite_form($entitytype, $entityid, $favorite, $title) ...@@ -2355,6 +2362,7 @@ function qa_favorite_form($entitytype, $entityid, $favorite, $title)
* Format a number using the decimal point and thousand separator specified in the language files. * Format a number using the decimal point and thousand separator specified in the language files.
* If the number is compacted it is turned into a string such as 1.3k or 2.5m. * If the number is compacted it is turned into a string such as 1.3k or 2.5m.
* *
* @since 1.8.0
* @param integer $number Number to be formatted * @param integer $number Number to be formatted
* @param integer $decimals Amount of decimals to use (ignored if number gets shortened) * @param integer $decimals Amount of decimals to use (ignored if number gets shortened)
* @param bool $compact Whether the number can be shown as compact or not * @param bool $compact Whether the number can be shown as compact or not
......
...@@ -592,7 +592,6 @@ function qa_post_html_defaults($basetype, $full = false) ...@@ -592,7 +592,6 @@ function qa_post_html_defaults($basetype, $full = false)
'blockwordspreg' => qa_get_block_words_preg(), 'blockwordspreg' => qa_get_block_words_preg(),
'showurllinks' => qa_opt('show_url_links'), 'showurllinks' => qa_opt('show_url_links'),
'linksnewwindow' => qa_opt('links_in_new_window'), 'linksnewwindow' => qa_opt('links_in_new_window'),
'microformats' => $full,
'fulldatedays' => qa_opt('show_full_date_days'), 'fulldatedays' => qa_opt('show_full_date_days'),
); );
} }
......
...@@ -155,7 +155,9 @@ function qa_question_set_selchildid($userid, $handle, $cookieid, $oldquestion, $ ...@@ -155,7 +155,9 @@ function qa_question_set_selchildid($userid, $handle, $cookieid, $oldquestion, $
{ {
$oldselchildid = $oldquestion['selchildid']; $oldselchildid = $oldquestion['selchildid'];
qa_db_post_set_selchildid($oldquestion['postid'], isset($selchildid) ? $selchildid : null, $userid, qa_remote_ip_address()); $lastip = qa_remote_ip_address();
qa_db_post_set_selchildid($oldquestion['postid'], isset($selchildid) ? $selchildid : null, $userid, $lastip);
qa_db_points_update_ifuser($oldquestion['userid'], 'aselects'); qa_db_points_update_ifuser($oldquestion['userid'], 'aselects');
qa_db_unselqcount_update(); qa_db_unselqcount_update();
...@@ -168,6 +170,15 @@ function qa_question_set_selchildid($userid, $handle, $cookieid, $oldquestion, $ ...@@ -168,6 +170,15 @@ function qa_question_set_selchildid($userid, $handle, $cookieid, $oldquestion, $
'postid' => $oldselchildid, 'postid' => $oldselchildid,
'answer' => $answers[$oldselchildid], 'answer' => $answers[$oldselchildid],
)); ));
if (!empty($oldquestion['closed']) && empty($oldquestion['closedbyid'])) {
qa_db_post_set_closed($oldquestion['postid'], null, $userid, $lastip);
qa_report_event('q_reopen', $userid, $handle, $cookieid, array(
'postid' => $oldquestion['postid'],
'oldquestion' => $oldquestion,
));
}
} }
if (isset($selchildid)) { if (isset($selchildid)) {
...@@ -179,6 +190,17 @@ function qa_question_set_selchildid($userid, $handle, $cookieid, $oldquestion, $ ...@@ -179,6 +190,17 @@ function qa_question_set_selchildid($userid, $handle, $cookieid, $oldquestion, $
'postid' => $selchildid, 'postid' => $selchildid,
'answer' => $answers[$selchildid], 'answer' => $answers[$selchildid],
)); ));
if (empty($oldquestion['closed'])) {
qa_db_post_set_closed($oldquestion['postid'], null, $userid, $lastip);
qa_report_event('q_close', $userid, $handle, $cookieid, array(
'postid' => $oldquestion['postid'],
'oldquestion' => $oldquestion,
'reason' => 'answer-selected',
'originalid' => $answers[$selchildid],
));
}
} }
} }
......
...@@ -207,8 +207,8 @@ function qa_post_set_selchildid($questionid, $answerid, $byuserid = null) ...@@ -207,8 +207,8 @@ function qa_post_set_selchildid($questionid, $answerid, $byuserid = null)
/** /**
* Closed $questionid if $closed is true, otherwise reopen it. If $closed is true, pass either the $originalpostid of * Close $questionid if $closed is true, otherwise reopen it. If $closed is true, pass either the $originalpostid of
* the question that it is a duplicate of, or a $note to explain why it's closed. Pass the identify of the user in * the question that it is a duplicate of, or a $note to explain why it's closed. Pass the identifier of the user in
* $byuserid (or null for an anonymous change). * $byuserid (or null for an anonymous change).
* @param $questionid * @param $questionid
* @param bool $closed * @param bool $closed
...@@ -234,6 +234,18 @@ function qa_post_set_closed($questionid, $closed = true, $originalpostid = null, ...@@ -234,6 +234,18 @@ function qa_post_set_closed($questionid, $closed = true, $originalpostid = null,
qa_question_close_clear($oldquestion, $oldclosepost, $byuserid, $byhandle, null); qa_question_close_clear($oldquestion, $oldclosepost, $byuserid, $byhandle, null);
} }
/**
* Return whether the given question is closed. This check takes into account the do_close_on_select option which
* considers questions with a selected answer as closed.
* @since 1.8.2
* @param array $question
* @return bool
*/
function qa_post_is_closed(array $question)
{
return isset($question['closedbyid']) || (isset($question['selchildid']) && qa_opt('do_close_on_select'));
}
/** /**
* Hide $postid if $hidden is true, otherwise show the post. Pass the identify of the user making this change in * Hide $postid if $hidden is true, otherwise show the post. Pass the identify of the user making this change in
......
...@@ -62,6 +62,7 @@ function qa_q_list_page_content($questions, $pagesize, $start, $count, $sometitl ...@@ -62,6 +62,7 @@ function qa_q_list_page_content($questions, $pagesize, $start, $count, $sometitl
require_once QA_INCLUDE_DIR . 'app/format.php'; require_once QA_INCLUDE_DIR . 'app/format.php';
require_once QA_INCLUDE_DIR . 'app/updates.php'; require_once QA_INCLUDE_DIR . 'app/updates.php';
require_once QA_INCLUDE_DIR . 'app/posts.php';
$userid = qa_get_logged_in_userid(); $userid = qa_get_logged_in_userid();
...@@ -89,7 +90,7 @@ function qa_q_list_page_content($questions, $pagesize, $start, $count, $sometitl ...@@ -89,7 +90,7 @@ function qa_q_list_page_content($questions, $pagesize, $start, $count, $sometitl
$qa_content['q_list']['qs'] = array(); $qa_content['q_list']['qs'] = array();
if (count($questions)) { if (!empty($questions)) {
$qa_content['title'] = $sometitle; $qa_content['title'] = $sometitle;
$defaults = qa_post_html_defaults('Q'); $defaults = qa_post_html_defaults('Q');
...@@ -100,7 +101,7 @@ function qa_q_list_page_content($questions, $pagesize, $start, $count, $sometitl ...@@ -100,7 +101,7 @@ function qa_q_list_page_content($questions, $pagesize, $start, $count, $sometitl
foreach ($questions as $question) { foreach ($questions as $question) {
$fields = qa_any_to_q_html_fields($question, $userid, qa_cookie_get(), $usershtml, null, qa_post_html_options($question, $defaults)); $fields = qa_any_to_q_html_fields($question, $userid, qa_cookie_get(), $usershtml, null, qa_post_html_options($question, $defaults));
if (!empty($fields['raw']['closedbyid'])) { if (qa_post_is_closed($question)) {
$fields['closed'] = array( $fields['closed'] = array(
'state' => qa_lang_html('main/closed'), 'state' => qa_lang_html('main/closed'),
); );
......
...@@ -520,9 +520,12 @@ function qa_set_user_avatar($userid, $imagedata, $oldblobid = null) ...@@ -520,9 +520,12 @@ function qa_set_user_avatar($userid, $imagedata, $oldblobid = null)
$newblobid = qa_create_blob($imagedata, 'jpeg', null, $userid, null, qa_remote_ip_address()); $newblobid = qa_create_blob($imagedata, 'jpeg', null, $userid, null, qa_remote_ip_address());
if (isset($newblobid)) { if (isset($newblobid)) {
qa_db_user_set($userid, 'avatarblobid', $newblobid); qa_db_user_set($userid, array(
qa_db_user_set($userid, 'avatarwidth', $width); 'avatarblobid' => $newblobid,
qa_db_user_set($userid, 'avatarheight', $height); 'avatarwidth' => $width,
'avatarheight' => $height,
));
qa_db_user_set_flag($userid, QA_USER_FLAGS_SHOW_AVATAR, true); qa_db_user_set_flag($userid, QA_USER_FLAGS_SHOW_AVATAR, true);
qa_db_user_set_flag($userid, QA_USER_FLAGS_SHOW_GRAVATAR, false); qa_db_user_set_flag($userid, QA_USER_FLAGS_SHOW_GRAVATAR, false);
......
...@@ -289,8 +289,10 @@ if (QA_FINAL_EXTERNAL_USERS) { ...@@ -289,8 +289,10 @@ if (QA_FINAL_EXTERNAL_USERS) {
if (empty($userinfo['sessioncode']) || ($source !== $userinfo['sessionsource'])) { if (empty($userinfo['sessioncode']) || ($source !== $userinfo['sessionsource'])) {
$sessioncode = qa_db_user_rand_sessioncode(); $sessioncode = qa_db_user_rand_sessioncode();
qa_db_user_set($userid, 'sessioncode', $sessioncode); qa_db_user_set($userid, array(
qa_db_user_set($userid, 'sessionsource', $source); 'sessioncode' => $sessioncode,
'sessionsource' => $source,
));
} else } else
$sessioncode = $userinfo['sessioncode']; $sessioncode = $userinfo['sessioncode'];
...@@ -518,6 +520,7 @@ if (QA_FINAL_EXTERNAL_USERS) { ...@@ -518,6 +520,7 @@ if (QA_FINAL_EXTERNAL_USERS) {
* Return the URL to the $blobId with a stored size of $width and $height. * Return the URL to the $blobId with a stored size of $width and $height.
* Constrain the image to $size (width AND height) * Constrain the image to $size (width AND height)
* *
* @since 1.8.0
* @param string $blobId The blob ID from the image * @param string $blobId The blob ID from the image
* @param int|null $size The resulting image's size. If omitted the original image size will be used. If the * @param int|null $size The resulting image's size. If omitted the original image size will be used. If the
* size is present it must be greater than 0 * size is present it must be greater than 0
...@@ -569,7 +572,7 @@ if (QA_FINAL_EXTERNAL_USERS) { ...@@ -569,7 +572,7 @@ if (QA_FINAL_EXTERNAL_USERS) {
$userHtml = '<a href="' . $url . '" class="qa-user-link' . $favclass . '"' . $mfAttr . '>' . $userHandle . '</a>'; $userHtml = '<a href="' . $url . '" class="qa-user-link' . $favclass . '"' . $mfAttr . '>' . $userHandle . '</a>';
if ($microdata) { if ($microdata) {
$userHtml = '<span itemprop="author" itemscope itemtype="http://schema.org/Person">' . $userHtml . '</span>'; $userHtml = '<span itemprop="author" itemscope itemtype="https://schema.org/Person">' . $userHtml . '</span>';
} }
return $userHtml; return $userHtml;
...@@ -582,6 +585,7 @@ if (QA_FINAL_EXTERNAL_USERS) { ...@@ -582,6 +585,7 @@ if (QA_FINAL_EXTERNAL_USERS) {
* the user's profile, 'local-default' for an avatar fetched locally from the default avatar blob ID, and NULL * the user's profile, 'local-default' for an avatar fetched locally from the default avatar blob ID, and NULL
* if the avatar could not be fetched from any of these sources * if the avatar could not be fetched from any of these sources
* *
* @since 1.8.0
* @param int $flags The user's flags * @param int $flags The user's flags
* @param string|null $email The user's email * @param string|null $email The user's email
* @param string|null $blobId The blob ID for a locally stored avatar. * @param string|null $blobId The blob ID for a locally stored avatar.
...@@ -1401,6 +1405,7 @@ function qa_check_form_security_code($action, $value) ...@@ -1401,6 +1405,7 @@ function qa_check_form_security_code($action, $value)
/** /**
* Return the URL for the Gravatar corresponding to $email, constrained to $size * Return the URL for the Gravatar corresponding to $email, constrained to $size
* *
* @since 1.8.0
* @param string $email The email of the Gravatar to return * @param string $email The email of the Gravatar to return
* @param int|null $size The size of the Gravatar to return. If omitted the default size will be used * @param int|null $size The size of the Gravatar to return. If omitted the default size will be used
* @return string The URL to the Gravatar of the user * @return string The URL to the Gravatar of the user
......
...@@ -164,17 +164,30 @@ function qa_db_user_get_handle_userids($handles) ...@@ -164,17 +164,30 @@ function qa_db_user_get_handle_userids($handles)
/** /**
* Set $field of $userid to $value in the database users table * Set $field of $userid to $value in the database users table. If the $fields parameter is an array, the $value
* @param $userid * parameter is ignored and each element of the array is treated as a key-value pair of user fields and values.
* @param $field * @param mixed $userid
* @param $value * @param string|array $fields
* @param string|null $value
*/ */
function qa_db_user_set($userid, $field, $value) function qa_db_user_set($userid, $fields, $value = null)
{ {
qa_db_query_sub( if (!is_array($fields)) {
'UPDATE ^users SET ' . qa_db_escape_string($field) . '=$ WHERE userid=$', $fields = array(
$value, $userid $fields => $value,
); );
}
$sql = 'UPDATE ^users SET ';
foreach ($fields as $field => $fieldValue) {
$sql .= qa_db_escape_string($field) . ' = $, ';
}
$sql = substr($sql, 0, -2) . ' WHERE userid = $';
$params = array_values($fields);
$params[] = $userid;
qa_db_query_sub_params($sql, $params);
} }
......
...@@ -184,7 +184,7 @@ function qa_page_q_post_rules($post, $parentpost = null, $siblingposts = null, $ ...@@ -184,7 +184,7 @@ function qa_page_q_post_rules($post, $parentpost = null, $siblingposts = null, $
$rules['closeable'] = qa_opt('allow_close_questions') && $post['type'] == 'Q' && !$rules['closed'] && $permiterror_close_open === false; $rules['closeable'] = qa_opt('allow_close_questions') && $post['type'] == 'Q' && !$rules['closed'] && $permiterror_close_open === false;
// cannot reopen a question if it's been hidden, or if it was closed by someone else and you don't have global closing permissions // cannot reopen a question if it's been hidden, or if it was closed by someone else and you don't have global closing permissions
$rules['reopenable'] = $rules['closed'] && isset($post['closedbyid']) && $permiterror_close_open === false && !$post['hidden'] $rules['reopenable'] = $rules['closed'] && $permiterror_close_open === false && !$post['hidden']
&& ($notclosedbyother || !qa_user_permit_error('permit_close_q', null, $userlevel, true, $userfields)); && ($notclosedbyother || !qa_user_permit_error('permit_close_q', null, $userlevel, true, $userfields));
$rules['moderatable'] = $post['queued'] && !$permiterror_moderate; $rules['moderatable'] = $post['queued'] && !$permiterror_moderate;
...@@ -266,6 +266,8 @@ function qa_page_q_post_rules($post, $parentpost = null, $siblingposts = null, $ ...@@ -266,6 +266,8 @@ function qa_page_q_post_rules($post, $parentpost = null, $siblingposts = null, $
*/ */
function qa_page_q_question_view($question, $parentquestion, $closepost, $usershtml, $formrequested) function qa_page_q_question_view($question, $parentquestion, $closepost, $usershtml, $formrequested)
{ {
require_once QA_INCLUDE_DIR . 'app/posts.php';
$questionid = $question['postid']; $questionid = $question['postid'];
$userid = qa_get_logged_in_userid(); $userid = qa_get_logged_in_userid();
$cookieid = qa_cookie_get(); $cookieid = qa_cookie_get();
...@@ -433,7 +435,7 @@ function qa_page_q_question_view($question, $parentquestion, $closepost, $usersh ...@@ -433,7 +435,7 @@ function qa_page_q_question_view($question, $parentquestion, $closepost, $usersh
// Information about the question that this question is a duplicate of (if appropriate) // Information about the question that this question is a duplicate of (if appropriate)
if (isset($closepost)) { if (isset($closepost) || qa_post_is_closed($question)) {
if ($closepost['basetype'] == 'Q') { if ($closepost['basetype'] == 'Q') {
if ($closepost['hidden']) { if ($closepost['hidden']) {
// don't show link for hidden questions // don't show link for hidden questions
...@@ -461,6 +463,12 @@ function qa_page_q_question_view($question, $parentquestion, $closepost, $usersh ...@@ -461,6 +463,12 @@ function qa_page_q_question_view($question, $parentquestion, $closepost, $usersh
'blockwordspreg' => qa_get_block_words_preg(), 'blockwordspreg' => qa_get_block_words_preg(),
)), )),
); );
} else { // If closed by a selected answer due to the do_close_on_select setting being enabled
$q_view['closed'] = array(
'state' => qa_lang_html('main/closed'),
'label' => qa_lang_html('main/closed'),
'content' => '',
);
} }
} }
...@@ -848,10 +856,10 @@ function qa_page_q_comment_follow_list($question, $parent, $commentsfollows, $al ...@@ -848,10 +856,10 @@ function qa_page_q_comment_follow_list($question, $parent, $commentsfollows, $al
$skipfirst--; $skipfirst--;
} elseif ($commentfollow['basetype'] == 'C') { } elseif ($commentfollow['basetype'] == 'C') {
$commentlist['cs'][$commentfollowid] = qa_page_q_comment_view($question, $parent, $commentfollow, $usershtml, $formrequested); $commentlist['cs'][$commentfollowid] = qa_page_q_comment_view($question, $parent, $commentfollow, $usershtml, $formrequested);
} elseif ($commentfollow['basetype'] == 'Q') { } elseif ($commentfollow['basetype'] == 'Q') {
$htmloptions = qa_post_html_options($commentfollow); $htmloptions = qa_post_html_options($commentfollow);
$htmloptions['avatarsize'] = qa_opt('avatar_q_page_c_size'); $htmloptions['avatarsize'] = qa_opt('avatar_q_page_c_size');
$htmloptions['voteview'] = false;
$commentlist['cs'][$commentfollowid] = qa_post_html_fields($commentfollow, $userid, $cookieid, $usershtml, null, $htmloptions); $commentlist['cs'][$commentfollowid] = qa_post_html_fields($commentfollow, $userid, $cookieid, $usershtml, null, $htmloptions);
} }
......
...@@ -259,8 +259,8 @@ if ($formtype == 'q_edit') { // ...in edit mode ...@@ -259,8 +259,8 @@ if ($formtype == 'q_edit') { // ...in edit mode
$microdata = qa_opt('use_microdata'); $microdata = qa_opt('use_microdata');
if ($microdata) { if ($microdata) {
$qa_content['head_lines'][] = '<meta itemprop="name" content="' . qa_html($qa_content['q_view']['raw']['title']) . '">'; $qa_content['head_lines'][] = '<meta itemprop="name" content="' . qa_html($qa_content['q_view']['raw']['title']) . '">';
$qa_content['html_tags'] .= ' itemscope itemtype="http://schema.org/QAPage"'; $qa_content['html_tags'] .= ' itemscope itemtype="https://schema.org/QAPage"';
$qa_content['main_tags'] = ' itemscope itemtype="http://schema.org/Question"'; $qa_content['wrapper_tags'] = ' itemprop="mainEntity" itemscope itemtype="https://schema.org/Question"';
} }
...@@ -336,7 +336,7 @@ if (qa_opt('sort_answers_by') == 'votes') { ...@@ -336,7 +336,7 @@ if (qa_opt('sort_answers_by') == 'votes') {
// further changes to ordering to deal with queued, hidden and selected answers // further changes to ordering to deal with queued, hidden and selected answers
$countfortitle = $question['acount']; $countfortitle = (int) $question['acount'];
$nextposition = 10000; $nextposition = 10000;
$answerposition = array(); $answerposition = array();
...@@ -424,7 +424,6 @@ foreach ($answerids as $answerid) { ...@@ -424,7 +424,6 @@ foreach ($answerids as $answerid) {
if ($question['basetype'] == 'Q') { if ($question['basetype'] == 'Q') {
$qa_content['a_list']['title_tags'] = 'id="a_list_title"'; $qa_content['a_list']['title_tags'] = 'id="a_list_title"';
if ($countfortitle > 0) {
$split = $countfortitle == 1 $split = $countfortitle == 1
? qa_lang_html_sub_split('question/1_answer_title', '1', '1') ? qa_lang_html_sub_split('question/1_answer_title', '1', '1')
: qa_lang_html_sub_split('question/x_answers_title', $countfortitle); : qa_lang_html_sub_split('question/x_answers_title', $countfortitle);
...@@ -432,9 +431,12 @@ if ($question['basetype'] == 'Q') { ...@@ -432,9 +431,12 @@ if ($question['basetype'] == 'Q') {
if ($microdata) { if ($microdata) {
$split['data'] = '<span itemprop="answerCount">' . $split['data'] . '</span>'; $split['data'] = '<span itemprop="answerCount">' . $split['data'] . '</span>';
} }
$qa_content['a_list']['title'] = $split['prefix'] . $split['data'] . $split['suffix']; $qa_content['a_list']['title'] = $split['prefix'] . $split['data'] . $split['suffix'];
} else
if ($countfortitle == 0) {
$qa_content['a_list']['title_tags'] .= ' style="display:none;" '; $qa_content['a_list']['title_tags'] .= ' style="display:none;" ';
}
} }
if (!$formrequested) { if (!$formrequested) {
......
...@@ -35,24 +35,40 @@ if (QA_FINAL_EXTERNAL_USERS) ...@@ -35,24 +35,40 @@ if (QA_FINAL_EXTERNAL_USERS)
// Check the code and unsubscribe the user if appropriate // Check the code and unsubscribe the user if appropriate
$unsubscribed = false; // check if already unsubscribed
$loginuserid = qa_get_logged_in_userid(); $unsubscribed = (bool) (qa_get_logged_in_flags() & QA_USER_FLAGS_NO_MAILINGS);
$loggedInUserId = qa_get_logged_in_userid();
$isLoggedIn = $loggedInUserId !== null;
if (qa_clicked('dounsubscribe')) {
if (!qa_check_form_security_code('unsubscribe', qa_post_text('formcode'))) {
$pageError = qa_lang_html('misc/form_security_again');
} else {
if ($isLoggedIn) {
// logged in users can unsubscribe right away
qa_db_user_set_flag($loggedInUserId, QA_USER_FLAGS_NO_MAILINGS, true);
$unsubscribed = true;
$incode = trim(qa_get('c')); // trim to prevent passing in blank values to match uninitiated DB rows } else {
$inhandle = qa_get('u'); // logged out users require valid code (from email link)
$incode = trim(qa_post_text('code'));
$inhandle = qa_post_text('handle');
if (!empty($inhandle)) { // match based on code and handle provided on URL if (!empty($inhandle)) {
$userinfo = qa_db_select_with_pending(qa_db_user_account_selectspec($inhandle, false)); $userinfo = qa_db_select_with_pending(qa_db_user_account_selectspec($inhandle, false));
if (strtolower(trim(@$userinfo['emailcode'])) == strtolower($incode)) { if (strtolower(trim(@$userinfo['emailcode'])) == strtolower($incode)) {
qa_db_user_set_flag($userinfo['userid'], QA_USER_FLAGS_NO_MAILINGS, true); qa_db_user_set_flag($userinfo['userid'], QA_USER_FLAGS_NO_MAILINGS, true);
$unsubscribed = true; $unsubscribed = true;
} }
} }
if (!$unsubscribed && isset($loginuserid)) { // as a backup, also unsubscribe logged in user if (!$unsubscribed) {
qa_db_user_set_flag($loginuserid, QA_USER_FLAGS_NO_MAILINGS, true); $pageError = qa_insert_login_links(qa_lang_html('users/unsubscribe_wrong_log_in'), 'unsubscribe');
$unsubscribed = true; }
}
}
} }
...@@ -68,9 +84,60 @@ if ($unsubscribed) { ...@@ -68,9 +84,60 @@ if ($unsubscribed) {
'^1' => '<a href="' . qa_path_html('account') . '">', '^1' => '<a href="' . qa_path_html('account') . '">',
'^2' => '</a>', '^2' => '</a>',
)); ));
} elseif (!empty($pageError)) {
$qa_content['error'] = $pageError;
} else { } else {
$qa_content['error'] = qa_insert_login_links(qa_lang_html('users/unsubscribe_wrong_log_in'), 'unsubscribe'); $contentForm = array(
} 'tags' => 'method="post" action="' . qa_path_html('unsubscribe') . '"',
'style' => 'wide',
'fields' => array(),
'buttons' => array(
'send' => array(
'tags' => 'name="dounsubscribe"',
'label' => qa_lang_html('users/unsubscribe_title'),
),
),
'hidden' => array(
'formcode' => qa_get_form_security_code('unsubscribe'),
),
);
if ($isLoggedIn) {
// user is logged in: show button to confirm unsubscribe
$contentForm['fields']['email'] = array(
'type' => 'static',
'label' => qa_lang_html('users/email_label'),
'value' => qa_html(qa_get_logged_in_email()),
);
} else {
// user is not logged in: show form with email address
$incode = trim(qa_get('c'));
$inhandle = qa_get('u');
if (empty($incode) || empty($inhandle)) {
$qa_content['error'] = qa_insert_login_links(qa_lang_html('users/unsubscribe_wrong_log_in'), 'account');
$contentForm = null;
} else {
$contentForm['fields']['handle'] = array(
'type' => 'static',
'label' => qa_lang_html('users/handle_label'),
'value' => qa_html($inhandle),
);
$contentForm['hidden']['code'] = qa_html($incode);
$contentForm['hidden']['handle'] = qa_html($inhandle);
}
}
if ($contentForm) {
$qa_content['form'] = $contentForm;
}
}
return $qa_content; return $qa_content;
...@@ -162,9 +162,12 @@ if (!QA_FINAL_EXTERNAL_USERS) { ...@@ -162,9 +162,12 @@ if (!QA_FINAL_EXTERNAL_USERS) {
if (isset($useraccount['avatarblobid'])) { if (isset($useraccount['avatarblobid'])) {
require_once QA_INCLUDE_DIR . 'app/blobs.php'; require_once QA_INCLUDE_DIR . 'app/blobs.php';
qa_db_user_set($userid, 'avatarblobid', null); qa_db_user_set($userid, array(
qa_db_user_set($userid, 'avatarwidth', null); 'avatarblobid' => null,
qa_db_user_set($userid, 'avatarheight', null); 'avatarwidth' => null,
'avatarheight' => null,
));
qa_delete_blob($useraccount['avatarblobid']); qa_delete_blob($useraccount['avatarblobid']);
} }
} }
......
...@@ -20,8 +20,8 @@ ...@@ -20,8 +20,8 @@
*/ */
define('QA_VERSION', '1.8.1'); // also used as suffix for .js and .css requests define('QA_VERSION', '1.8.2'); // also used as suffix for .js and .css requests
define('QA_BUILD_DATE', '2018-12-01'); define('QA_BUILD_DATE', '2018-12-20');
/** /**
...@@ -84,6 +84,7 @@ if (!isset($qa_autoconnect) || $qa_autoconnect !== false) { ...@@ -84,6 +84,7 @@ if (!isset($qa_autoconnect) || $qa_autoconnect !== false) {
/** /**
* 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)
* @deprecated 1.8.2 no longer used
* @param $version * @param $version
* @return float * @return float
*/ */
...@@ -106,30 +107,24 @@ function qa_version_to_float($version) ...@@ -106,30 +107,24 @@ function qa_version_to_float($version)
/** /**
* 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
* @param $version * @param $version
* @return bool * @return bool
*/ */
function qa_qa_version_below($version) function qa_qa_version_below($version)
{ {
$minqa = qa_version_to_float($version); return version_compare(QA_VERSION, $version) < 0;
$thisqa = qa_version_to_float(QA_VERSION);
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
* @param $version * @param $version
* @return bool * @return bool
*/ */
function qa_php_version_below($version) function qa_php_version_below($version)
{ {
$minphp = qa_version_to_float($version); return version_compare(phpversion(), $version) < 0;
$thisphp = qa_version_to_float(phpversion());
return $minphp && $thisphp && $thisphp < $minphp;
} }
......
...@@ -372,14 +372,26 @@ function qa_db_apply_sub($query, $arguments) ...@@ -372,14 +372,26 @@ function qa_db_apply_sub($query, $arguments)
/** /**
* Run $query after substituting ^, # and $ symbols, and return the result resource (or call fail handler). * Run $query after substituting ^, # and $ symbols, and return the result resource (or call fail handler).
* @param $query * @param string $query
* @return mixed * @return mixed
*/ */
function qa_db_query_sub($query) // arguments for substitution retrieved using func_get_args() function qa_db_query_sub($query) // arguments for substitution retrieved using func_get_args()
{ {
$funcargs = func_get_args(); $funcargs = func_get_args();
return qa_db_query_raw(qa_db_apply_sub($query, array_slice($funcargs, 1))); return qa_db_query_sub_params($query, array_slice($funcargs, 1));
}
/**
* Run $query after substituting ^, # and $ symbols, and return the result resource (or call fail handler).
* Query parameters are passed as an array.
* @param string $query
* @param array $params
* @return mixed
*/
function qa_db_query_sub_params($query, $params)
{
return qa_db_query_raw(qa_db_apply_sub($query, $params));
} }
......
...@@ -280,6 +280,7 @@ switch ($feedtype) { ...@@ -280,6 +280,7 @@ switch ($feedtype) {
require_once QA_INCLUDE_DIR . 'app/format.php'; require_once QA_INCLUDE_DIR . 'app/format.php';
require_once QA_INCLUDE_DIR . 'app/updates.php'; require_once QA_INCLUDE_DIR . 'app/updates.php';
require_once QA_INCLUDE_DIR . 'app/posts.php';
require_once QA_INCLUDE_DIR . 'util/string.php'; require_once QA_INCLUDE_DIR . 'util/string.php';
if ($feedtype != 'search' && $feedtype != 'hot') // leave search results and hot questions sorted by relevance if ($feedtype != 'search' && $feedtype != 'hot') // leave search results and hot questions sorted by relevance
...@@ -337,7 +338,7 @@ foreach ($questions as $question) { ...@@ -337,7 +338,7 @@ foreach ($questions as $question) {
break; break;
case 'Q-' . QA_UPDATE_CLOSED: case 'Q-' . QA_UPDATE_CLOSED:
$langstring = isset($question['closedbyid']) ? 'misc/feed_closed_prefix' : 'misc/feed_reopened_prefix'; $langstring = qa_post_is_closed($question) ? 'misc/feed_closed_prefix' : 'misc/feed_reopened_prefix';
break; break;
case 'Q-' . QA_UPDATE_TAGS: case 'Q-' . QA_UPDATE_TAGS:
......
...@@ -435,7 +435,8 @@ class qa_html_theme_base ...@@ -435,7 +435,8 @@ class qa_html_theme_base
$this->body_prefix(); $this->body_prefix();
$this->notices(); $this->notices();
$this->output('<div class="qa-body-wrapper">', ''); $extratags = isset($this->content['wrapper_tags']) ? $this->content['wrapper_tags'] : '';
$this->output('<div class="qa-body-wrapper"' . $extratags . '>', '');
$this->widgets('full', 'top'); $this->widgets('full', 'top');
$this->header(); $this->header();
...@@ -713,9 +714,8 @@ class qa_html_theme_base ...@@ -713,9 +714,8 @@ class qa_html_theme_base
{ {
$content = $this->content; $content = $this->content;
$hidden = !empty($content['hidden']) ? ' qa-main-hidden' : ''; $hidden = !empty($content['hidden']) ? ' qa-main-hidden' : '';
$extratags = isset($this->content['main_tags']) ? $this->content['main_tags'] : '';
$this->output('<div class="qa-main' . $hidden . '"' . $extratags . '>'); $this->output('<div class="qa-main' . $hidden . '">');
$this->widgets('main', 'top'); $this->widgets('main', 'top');
...@@ -1964,15 +1964,18 @@ class qa_html_theme_base ...@@ -1964,15 +1964,18 @@ class qa_html_theme_base
{ {
if (isset($post['what'])) { if (isset($post['what'])) {
$classes = $class . '-what'; $classes = $class . '-what';
if (@$post['what_your']) if (isset($post['what_your']) && $post['what_your']) {
$classes .= ' ' . $class . '-what-your'; $classes .= ' ' . $class . '-what-your';
}
if (isset($post['what_url'])) if (isset($post['what_url'])) {
$this->output('<a href="' . $post['what_url'] . '" class="' . $classes . '">' . $post['what'] . '</a>'); $tags = isset($post['what_url_tags']) ? $post['what_url_tags'] : '';
else $this->output('<a href="' . $post['what_url'] . '" class="' . $classes . '"' . $tags . '>' . $post['what'] . '</a>');
} else {
$this->output('<span class="' . $classes . '">' . $post['what'] . '</span>'); $this->output('<span class="' . $classes . '">' . $post['what'] . '</span>');
} }
} }
}
public function post_meta_when($post, $class) public function post_meta_when($post, $class)
{ {
......
...@@ -821,9 +821,9 @@ if (!defined('QA_PREG_BLOCK_WORD_SEPARATOR')) { ...@@ -821,9 +821,9 @@ if (!defined('QA_PREG_BLOCK_WORD_SEPARATOR')) {
define('QA_PREG_BLOCK_WORD_SEPARATOR', '[\n\r\t\ \!\"\\\'\(\)\+\,\.\/\:\;\<\=\>\?\[\\\\\]\^\`\{\|\}\~\$\&\-\_\#\%\@]'); define('QA_PREG_BLOCK_WORD_SEPARATOR', '[\n\r\t\ \!\"\\\'\(\)\+\,\.\/\:\;\<\=\>\?\[\\\\\]\^\`\{\|\}\~\$\&\-\_\#\%\@]');
} }
// Pattern to match Chinese/Japanese/Korean ideographic symbols in UTF-8 encoding // Pattern to match Thai/Chinese/Japanese/Korean ideographic symbols in UTF-8 encoding
if (!defined('QA_PREG_CJK_IDEOGRAPHS_UTF8')) { if (!defined('QA_PREG_CJK_IDEOGRAPHS_UTF8')) {
define('QA_PREG_CJK_IDEOGRAPHS_UTF8', '\xE2[\xBA-\xBF][\x80-\xBF]|\xE3[\x80\x88-\xBF][\x80-\xBF]|[\xE4-\xE9][\x80-\xBF][\x80-\xBF]|\xEF[\xA4-\xAB][\x80-\xBF]|\xF0[\xA0-\xAF][\x80-\xBF][\x80-\xBF]'); define('QA_PREG_CJK_IDEOGRAPHS_UTF8', '\xE0[\xB8-\xB9][\x80-\xBF]|\xE2[\xBA-\xBF][\x80-\xBF]|\xE3[\x80\x88-\xBF][\x80-\xBF]|[\xE4-\xE9][\x80-\xBF][\x80-\xBF]|\xEF[\xA4-\xAB][\x80-\xBF]|\xF0[\xA0-\xAF][\x80-\xBF][\x80-\xBF]');
} }
qa_string_initialize(); qa_string_initialize();
...@@ -145,10 +145,12 @@ class qa_event_logger ...@@ -145,10 +145,12 @@ class qa_event_logger
public function value_to_text($value) public function value_to_text($value)
{ {
require_once QA_INCLUDE_DIR . 'util/string.php';
if (is_array($value)) if (is_array($value))
$text = 'array(' . count($value) . ')'; $text = 'array(' . count($value) . ')';
elseif (strlen($value) > 40) elseif (qa_strlen($value) > 40)
$text = substr($value, 0, 38) . '...'; $text = qa_substr($value, 0, 38) . '...';
else else
$text = $value; $text = $value;
......
...@@ -25,7 +25,7 @@ class qa_wysiwyg_upload ...@@ -25,7 +25,7 @@ class qa_wysiwyg_upload
{ {
public function match_request($request) public function match_request($request)
{ {
return ($request == 'wysiwyg-editor-upload'); return $request === 'wysiwyg-editor-upload';
} }
public function process_request($request) public function process_request($request)
...@@ -34,24 +34,33 @@ class qa_wysiwyg_upload ...@@ -34,24 +34,33 @@ class qa_wysiwyg_upload
$url = ''; $url = '';
if (is_array($_FILES) && count($_FILES)) { if (is_array($_FILES) && count($_FILES)) {
if (!qa_opt('wysiwyg_editor_upload_images')) if (qa_opt('wysiwyg_editor_upload_images')) {
$message = qa_lang_html('users/no_permission'); require_once QA_INCLUDE_DIR . 'app/upload.php';
require_once QA_INCLUDE_DIR.'app/upload.php';
$onlyImage = qa_get('qa_only_image');
$upload = qa_upload_file_one( $upload = qa_upload_file_one(
qa_opt('wysiwyg_editor_upload_max_size'), qa_opt('wysiwyg_editor_upload_max_size'),
qa_get('qa_only_image') || !qa_opt('wysiwyg_editor_upload_all'), $onlyImage || !qa_opt('wysiwyg_editor_upload_all'),
qa_get('qa_only_image') ? 600 : null, // max width if it's an image upload $onlyImage ? 600 : null, // max width if it's an image upload
null // no max height null // no max height
); );
$message = @$upload['error']; if (isset($upload['error'])) {
$url = @$upload['bloburl']; $message = $upload['error'];
} else {
$url = $upload['bloburl'];
}
} else {
$message = qa_lang_html('users/no_permission');
}
} }
echo "<script>window.parent.CKEDITOR.tools.callFunction(".qa_js(qa_get('CKEditorFuncNum')). echo sprintf(
", ".qa_js($url).", ".qa_js($message).");</script>"; '<script>window.parent.CKEDITOR.tools.callFunction(%s, %s, %s);</script>',
qa_js(qa_get('CKEditorFuncNum')),
qa_js($url),
qa_js($message)
);
return null; return null;
} }
......
...@@ -2,6 +2,29 @@ ...@@ -2,6 +2,29 @@
class BaseTest extends PHPUnit_Framework_TestCase class BaseTest extends PHPUnit_Framework_TestCase
{ {
public function test__qa_version_to_float()
{
$this->assertSame(1.006, qa_version_to_float('1.6'));
$this->assertSame(1.007004, qa_version_to_float('1.7.4'));
$this->assertSame(1.008, qa_version_to_float('1.8.0-beta1'));
}
public function test__qa_qa_version_below()
{
// as we cannot change the QA_VERSION constant, we test an appended version against the set constant
$buildVersion = QA_VERSION . '.1234';
$betaVersion = QA_VERSION . '-beta1';
$this->assertSame(true, qa_qa_version_below($buildVersion));
$this->assertSame(false, qa_qa_version_below($betaVersion));
}
public function test__qa_php_version_below()
{
// as we cannot change the PHP version, we test against an unsupported PHP version and a far-future version
$this->assertSame(false, qa_php_version_below('5.1.4'));
$this->assertSame(true, qa_php_version_below('11.1.0'));
}
public function test__qa_js() public function test__qa_js()
{ {
$this->assertSame("'test'", qa_js('test')); $this->assertSame("'test'", qa_js('test'));
......
...@@ -452,8 +452,8 @@ h2 {font-size:16px; padding-top:12px; clear:both;} ...@@ -452,8 +452,8 @@ h2 {font-size:16px; padding-top:12px; clear:both;}
/* Favorited items */ /* Favorited items */
.qa-q-favorited .qa-q-item-title a, .qa-tag-favorited, .qa-cat-favorited, .qa-user-favorited, .qa-nav-cat-favorited, .qa-browse-cat-favorited {background: url(favorite-icon-14x14.gif) no-repeat;} .qa-q-favorited .qa-q-item-title a, .qa-tag-favorited, .qa-cat-favorited, .qa-user-favorited, .qa-nav-cat-favorited, .qa-browse-cat-favorited {background-image: url(favorite-icon-14x14.gif); background-repeat: no-repeat;}
.qa-cat-parent-favorited {background: url(favorite-light-icon-14x14.gif) no-repeat;} .qa-cat-parent-favorited {background-image: url(favorite-light-icon-14x14.gif); background-repeat: no-repeat;}
.qa-q-favorited .qa-q-item-title a, .qa-nav-cat-favorited, .qa-browse-cat-favorited {background-position: left center; padding-left:18px;} .qa-q-favorited .qa-q-item-title a, .qa-nav-cat-favorited, .qa-browse-cat-favorited {background-position: left center; padding-left:18px;}
.qa-tag-favorited {background-position: 3px center; padding-left:20px;} .qa-tag-favorited {background-position: 3px center; padding-left:20px;}
.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;}
......
...@@ -66,24 +66,28 @@ class qa_html_theme extends qa_html_theme_base ...@@ -66,24 +66,28 @@ class qa_html_theme extends qa_html_theme_base
if ($this->localfonts) { if ($this->localfonts) {
// add Ubuntu font locally (inlined for speed) // add Ubuntu font locally (inlined for speed)
$this->output_array(array( $this->output_array(array(
'<style>', "<style>",
'@font-face {', "@font-face {",
' font-family: "Ubuntu"; font-style: normal; font-weight: 400;', " font-family: 'Ubuntu'; font-weight: normal; font-style: normal;",
' src: local("Ubuntu"), url("' . $this->rooturl . 'fonts/Ubuntu-regular.woff") format("woff");', " src: local('Ubuntu'),",
'}', " url('{$this->rooturl}fonts/ubuntu-regular.woff2') format('woff2'), url('{$this->rooturl}fonts/ubuntu-regular.woff') format('woff');",
'@font-face {', "}",
' font-family: "Ubuntu"; font-style: normal; font-weight: 700;', "@font-face {",
' src: local("Ubuntu Bold"), local("Ubuntu-Bold"), url("' . $this->rooturl . 'fonts/Ubuntu-700.woff") format("woff");', " font-family: 'Ubuntu'; font-weight: bold; font-style: normal;",
'}', " src: local('Ubuntu Bold'), local('Ubuntu-Bold'),",
'@font-face {', " url('{$this->rooturl}fonts/ubuntu-bold.woff2') format('woff2'), url('{$this->rooturl}fonts/ubuntu-bold.woff') format('woff');",
' font-family: "Ubuntu"; font-style: italic; font-weight: 400;', "}",
' src: local("Ubuntu Italic"), local("Ubuntu-Italic"), url("' . $this->rooturl . 'fonts/Ubuntu-italic.woff") format("woff");', "@font-face {",
'}', " font-family: 'Ubuntu'; font-weight: normal; font-style: italic;",
'@font-face {', " src: local('Ubuntu Italic'), local('Ubuntu-Italic'),",
' font-family: "Ubuntu"; font-style: italic; font-weight: 700;', " url('{$this->rooturl}fonts/ubuntu-italic.woff2') format('woff2'), url('{$this->rooturl}fonts/ubuntu-italic.woff') format('woff');",
' src: local("Ubuntu Bold Italic"), local("Ubuntu-BoldItalic"), url("' . $this->rooturl . 'fonts/Ubuntu-700italic.woff") format("woff");', "}",
'}', "@font-face {",
'</style>', " font-family: 'Ubuntu'; font-weight: bold; font-style: italic;",
" src: local('Ubuntu Bold Italic'), local('Ubuntu-BoldItalic'),",
" url('{$this->rooturl}fonts/ubuntu-bold-italic.woff2') format('woff2'), url('{$this->rooturl}fonts/ubuntu-bold-italic.woff') format('woff');",
"}",
"</style>",
)); ));
} }
else { else {
...@@ -229,7 +233,8 @@ class qa_html_theme extends qa_html_theme_base ...@@ -229,7 +233,8 @@ class qa_html_theme extends qa_html_theme_base
$this->widgets('full', 'top'); $this->widgets('full', 'top');
$this->header(); $this->header();
$this->output('<div class="qa-body-wrapper">', ''); $extratags = isset($this->content['wrapper_tags']) ? $this->content['wrapper_tags'] : '';
$this->output('<div class="qa-body-wrapper"' . $extratags . '>', '');
$this->widgets('full', 'high'); $this->widgets('full', 'high');
$this->output('<div class="qa-main-wrapper">', ''); $this->output('<div class="qa-main-wrapper">', '');
......
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