diff --git a/.gitignore b/.gitignore index ecc5ae578..58bf25168 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ ehthumbs.db Thumbs.db .idea/ + +*.iml diff --git a/VERSION.txt b/VERSION.txt index fe4e75fb7..7b378be30 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -1.8.3 \ No newline at end of file +1.8.4 \ No newline at end of file diff --git a/qa-content/qa-global.js b/qa-content/qa-global.js index 6d3f3a0fc..3f304a438 100644 --- a/qa-content/qa-global.js +++ b/qa-content/qa-global.js @@ -95,9 +95,12 @@ function qa_vote_click(elem) mess = document.createElement('div'); mess.id = 'errorbox'; mess.className = 'qa-error'; - mess.innerHTML = lines[1]; mess.style.display = 'none'; + mess.role = 'alert'; + mess.innerHTML = lines[1]; } + mess.setAttribute('role', 'alert'); + elem.setAttribute('aria-describedby', mess.id); var postelem = document.getElementById(anchor); var e = postelem.parentNode.insertBefore(mess, postelem); @@ -194,19 +197,19 @@ function qa_display_rule_show(target, show, first) var qa_element_revealed = null; -function qa_toggle_element(elem) +function qa_toggle_element(elem, dontClose) { var e = elem ? document.getElementById(elem) : null; if (e && e.qa_disabled) e = null; - if (e && (qa_element_revealed == e)) { + if (e && (qa_element_revealed === e) && !dontClose) { qa_conceal(qa_element_revealed, 'form'); qa_element_revealed = null; } else { - if (qa_element_revealed) + if (qa_element_revealed !== e) qa_conceal(qa_element_revealed, 'form'); if (e) { @@ -431,7 +434,7 @@ function qa_form_params(formname) var e = es[i]; var t = (e.type || '').toLowerCase(); - if (((t != 'checkbox') && (t != 'radio')) || e.checked) + if (((t != 'checkbox') && (t != 'radio')) || (e && e.checked)) params[e.name] = e.value; } diff --git a/qa-include/app/format.php b/qa-include/app/format.php index b2f8acb9b..1d4af5ec3 100644 --- a/qa-include/app/format.php +++ b/qa-include/app/format.php @@ -426,7 +426,7 @@ function qa_post_html_fields($post, $userid, $cookieid, $usershtml, $dummy, $opt // this is for backwards compatibility with any existing links using the old style of anchor // that contained the post id only (changed to be valid under W3C specifications) - $fields['content'] = '' . $fields['content']; + $fields['content'] = '' . $fields['content']; } // Voting stuff @@ -495,7 +495,7 @@ function qa_post_html_fields($post, $userid, $cookieid, $usershtml, $dummy, $opt // schema.org microdata - vote display might be formatted (e.g. '2k') so we use meta tag for true count if ($microdata) { - $fields['netvotes_view']['suffix'] .= ' '; + $fields['netvotes_view']['suffix'] .= ' '; $fields['upvotes_view']['suffix'] .= ' '; } @@ -542,7 +542,8 @@ function qa_post_html_fields($post, $userid, $cookieid, $usershtml, $dummy, $opt $fields['vote_down_tags'] = 'title="' . qa_lang_html('main/voted_down_popup') . '" name="' . qa_html('vote_' . $postid . '_0_' . $elementid) . '" ' . $onclick; } else { - $fields['vote_up_tags'] = 'title="' . qa_lang_html('main/vote_up_popup') . '" name="' . qa_html('vote_' . $postid . '_1_' . $elementid) . '" ' . $onclick; + $title = $fields['raw']['type'] == "Q" ? qa_lang_html('main/vote_up_popup_question') : qa_lang_html('main/vote_up_popup_answer'); + $fields['vote_up_tags'] = 'title="' . $title . '" name="' . qa_html('vote_' . $postid . '_1_' . $elementid) . '" ' . $onclick; if (strpos($voteview, '-uponly-level')) { $fields['vote_state'] = 'up_only'; @@ -553,8 +554,9 @@ function qa_post_html_fields($post, $userid, $cookieid, $usershtml, $dummy, $opt $fields['vote_down_tags'] = 'title="' . qa_lang_html('main/vote_disabled_down_approve') . '"'; } else { - $fields['vote_state'] = 'enabled'; - $fields['vote_down_tags'] = 'title="' . qa_lang_html('main/vote_down_popup') . '" name="' . qa_html('vote_' . $postid . '_-1_' . $elementid) . '" ' . $onclick; + $title = $fields['raw']['type'] == "Q" ? qa_lang_html('main/vote_down_popup_question') : qa_lang_html('main/vote_down_popup_answer'); + $fields['vote_state'] = 'enabled'; + $fields['vote_down_tags'] = 'title="' . $title . '" name="' . qa_html('vote_' . $postid . '_-1_' . $elementid) . '" ' . $onclick; } } } @@ -1867,8 +1869,9 @@ function qa_set_up_name_field(&$qa_content, &$fields, $inname, $fieldprefix = '' { $fields['name'] = array( 'label' => qa_lang_html('question/anon_name_label'), - 'tags' => 'name="' . $fieldprefix . 'name"', + 'tags' => 'name="' . $fieldprefix . 'name" id="' .$fieldprefix . 'inputNameAnon"', 'value' => qa_html($inname), + 'id' => $fieldprefix . 'inputNameAnon' ); } @@ -1889,10 +1892,12 @@ function qa_set_up_name_field(&$qa_content, &$fields, $inname, $fieldprefix = '' */ function qa_set_up_notify_fields(&$qa_content, &$fields, $basetype, $login_email, $innotify, $inemail, $errors_email, $fieldprefix = '') { - $fields['notify'] = array( - 'tags' => 'name="' . $fieldprefix . 'notify"', + + $fields['notify'] = array( + 'tags' => 'name="' . $fieldprefix . 'notify" id="' . $fieldprefix .'notify"', 'type' => 'checkbox', 'value' => qa_html($innotify), + 'id' => $fieldprefix.'notify' ); switch ($basetype) { @@ -1920,19 +1925,20 @@ function qa_set_up_notify_fields(&$qa_content, &$fields, $basetype, $login_email '' . $labelaskemail . '' . ''; - $fields['notify']['tags'] .= ' id="' . $fieldprefix . 'notify" onclick="if (document.getElementById(\'' . $fieldprefix . 'notify\').checked) document.getElementById(\'' . $fieldprefix . 'email\').focus();"'; + $fields['notify']['tags'] .= ' id="' . $fieldprefix . 'notify" onclick="if (document.getElementById(\'' . $fieldprefix . 'notify\').checked) document.getElementById(\'' . $fieldprefix . 'email_display\').focus();"'; $fields['notify']['tight'] = true; $fields['email'] = array( - 'id' => $fieldprefix . 'email_display', - 'tags' => 'name="' . $fieldprefix . 'email" id="' . $fieldprefix . 'email"', + 'label' => qa_lang_html('main/email_optional'), + 'id' => $fieldprefix . 'email_display', + 'tags' => 'name="' . $fieldprefix . 'email" id="' . $fieldprefix . 'email_display"', 'value' => qa_html($inemail), 'note' => qa_lang_html('question/notify_email_note'), 'error' => qa_html($errors_email), ); qa_set_display_rules($qa_content, array( - $fieldprefix . 'email_display' => $fieldprefix . 'notify', + $fieldprefix . 'email_display-tbody' => $fieldprefix . 'notify', $fieldprefix . 'email_shown' => $fieldprefix . 'notify', $fieldprefix . 'email_hidden' => '!' . $fieldprefix . 'notify', )); diff --git a/qa-include/app/page.php b/qa-include/app/page.php index ce6bc767e..80db415f4 100644 --- a/qa-include/app/page.php +++ b/qa-include/app/page.php @@ -517,6 +517,7 @@ function qa_content_prepare($voting = false, $categoryids = array()) 'feedback' => array( 'url' => qa_path_html('feedback'), 'label' => qa_lang_html('main/nav_feedback'), + 'role' => 'link' ), ), diff --git a/qa-include/app/votes.php b/qa-include/app/votes.php index 420a3ffad..14f41c385 100644 --- a/qa-include/app/votes.php +++ b/qa-include/app/votes.php @@ -126,6 +126,10 @@ function qa_vote_set($post, $userid, $handle, $cookieid, $vote) $vote = (int)min(1, max(-1, $vote)); $oldvote = (int)qa_db_uservote_get($post['postid'], $userid); + if ($oldvote === $vote) { + return; + } + qa_db_uservote_set($post['postid'], $userid, $vote); qa_db_post_recount_votes($post['postid']); diff --git a/qa-include/db/cache.php b/qa-include/db/cache.php index 0ee1abd95..4030571a9 100644 --- a/qa-include/db/cache.php +++ b/qa-include/db/cache.php @@ -44,7 +44,7 @@ function qa_db_cache_set($type, $cacheid, $content) ); qa_db_query_sub( - 'INSERT INTO ^cache (type, cacheid, content, created, lastread) VALUES ($, #, $, NOW(), NOW()) ' . + 'INSERT INTO ^cache (type, cacheid, content, created, lastread) VALUES ($, $, $, NOW(), NOW()) ' . 'ON DUPLICATE KEY UPDATE content = VALUES(content), created = VALUES(created), lastread = VALUES(lastread)', $type, $cacheid, $content ); diff --git a/qa-include/lang/qa-lang-main.php b/qa-include/lang/qa-lang-main.php index 94679b9b4..330b576de 100644 --- a/qa-include/lang/qa-lang-main.php +++ b/qa-include/lang/qa-lang-main.php @@ -20,213 +20,226 @@ */ return array( - '_decimal_point' => '.', - '_thousands_separator' => ',', - '_thousands_suffix' => 'k', - '_millions_suffix' => 'm', - '1_answer' => '1 answer', - '1_comment' => '1 comment', - '1_day' => '1 day', - '1_disliked' => '1 dislike', - '1_flag' => '1 flag', - '1_hour' => '1 hour', - '1_liked' => '1 like', - '1_minute' => '1 minute', - '1_month' => '1 month', - '1_point' => '1 point', - '1_question' => '1 question', - '1_second' => '1 second', - '1_tag' => '1 tag', - '1_user' => '1 user', - '1_view' => '1 view', - '1_vote' => '1 vote', - '1_week' => '1 week', - '1_year' => '1 year', - 'add_category_x_favorites' => 'Add category ^ to my favorites', - 'add_favorites' => 'Add to my favorites', - 'add_tag_x_favorites' => 'Add tag ^ to my favorites', - 'all_categories' => 'All categories', - 'anonymous' => 'anonymous', - 'answer_edited' => 'answer edited', - 'answer_reshown' => 'answer reshown', - 'answer_selected' => 'answer selected', - 'answered' => 'answered', - 'answered_qs_in_x' => 'Most answered questions in ^', - 'answered_qs_title' => 'Most answered questions', - 'asked' => 'asked', - 'asked_related_q' => 'asked related question', - 'by_x' => 'by ^', - 'cancel_button' => 'Cancel', - 'closed' => 'closed', - 'comment_edited' => 'comment edited', - 'comment_moved' => 'comment moved', - 'comment_reshown' => 'comment reshown', - 'commented' => 'commented', - 'date_day_min_digits' => 1, // 1 or 2 - 'date_format_other_years' => '^month ^day, ^year', - 'date_format_this_year' => '^month ^day', - 'date_month_1' => 'Jan', - 'date_month_2' => 'Feb', - 'date_month_3' => 'Mar', - 'date_month_4' => 'Apr', - 'date_month_5' => 'May', - 'date_month_6' => 'Jun', - 'date_month_7' => 'Jul', - 'date_month_8' => 'Aug', - 'date_month_9' => 'Sep', - 'date_month_10' => 'Oct', - 'date_month_11' => 'Nov', - 'date_month_12' => 'Dec', - 'date_year_digits' => 4, // 2 or 4 - 'edited' => 'edited', - 'email_error' => 'An error occurred trying to send the email.', - 'field_required' => 'Please enter something in this field', - 'file_upload_limit_exceeded' => 'The size of the file exceeds the server\'s limits', - 'general_error' => 'A server error occurred - please try again.', - 'hidden' => 'hidden', - 'highest_users' => 'Top scoring users', - 'hot_qs_in_x' => 'Hot questions in ^', - 'hot_qs_title' => 'Hot questions', - 'image_not_read' => 'The image could not be read. Please upload one of: ^', - 'image_too_big_x_pc' => 'This image is too big. Please scale to ^% then try again.', - 'in_category_x' => 'in ^', - 'ip_address_x' => 'IP address ^', - 'logged_in_x' => 'Hello ^', - 'max_length_x' => 'Maximum length is ^ characters', - 'max_upload_size_x' => 'Maximum upload size is ^', - 'me' => 'me', - 'meta_order' => '^what^when^where^who', // you can reorder but DO NOT translate! e.g. <15 hours ago> - 'min_length_x' => 'Please provide more information - at least ^ characters', - 'moved' => 'moved', - 'nav_activity' => 'All Activity', - 'nav_admin' => 'Admin', - 'nav_ask' => 'Ask a Question', - 'nav_categories' => 'Categories', - 'nav_feedback' => 'Send feedback', - 'nav_home' => 'Home', - 'nav_hot' => 'Hot!', - 'nav_login' => 'Login', - 'nav_logout' => 'Logout', - 'nav_most_answers' => 'Most answers', - 'nav_most_recent' => 'Recent', - 'nav_most_views' => 'Most views', - 'nav_most_votes' => 'Most votes', - 'nav_no_answer' => 'No answer', - 'nav_no_selected_answer' => 'No selected answer', - 'nav_no_upvoted_answer' => 'No upvoted answer', - 'nav_qa' => 'Q&A', - 'nav_qs' => 'Questions', - 'nav_register' => 'Register', - 'nav_tags' => 'Tags', - 'nav_unanswered' => 'Unanswered', - 'nav_updates' => 'My Updates', - 'nav_users' => 'Users', - 'newest_users' => 'Newest users', - 'no_active_users' => 'No active users found', - 'no_answers_found' => 'No answers found', - 'no_answers_in_x' => 'No answers in ^', - 'no_categories_found' => 'No categories found', - 'no_category' => 'No category', - 'no_comments_found' => 'No comments found', - 'no_comments_in_x' => 'No comments in ^', - 'no_questions_found' => 'No questions found', - 'no_questions_in_x' => 'No questions in ^', - 'no_related_qs_title' => 'No related questions found', - 'no_results_for_x' => 'No results found for ^', - 'no_tags_found' => 'No tags found', - 'no_una_questions_found' => 'No unanswered questions found', - 'no_una_questions_in_x' => 'No unanswered questions in ^', - 'no_unselected_qs_found' => 'No questions found without a selected answer', - 'no_unupvoteda_qs_found' => 'No questions found without an upvoted answer', - 'page_label' => 'Page:', - 'page_next' => 'next', - 'page_not_found' => 'Page not found', - 'page_prev' => 'prev', - 'popular_tags' => 'Most popular tags', - 'questions_tagged_x' => 'Recent questions tagged ^', - 'recategorized' => 'recategorized', - 'recent_activity_in_x' => 'Recent activity in ^', - 'recent_activity_title' => 'Recent activity', - 'recent_as_in_x' => 'Recently answered questions in ^', - 'recent_as_title' => 'Recently answered questions', - 'recent_cs_in_x' => 'Recently commented questions in ^', - 'recent_cs_title' => 'Recently commented questions', - 'recent_qs_as_in_x' => 'Recent questions and answers in ^', - 'recent_qs_as_title' => 'Recent questions and answers', - 'recent_qs_in_x' => 'Recent questions in ^', - 'recent_qs_title' => 'Recent questions', - 'related_qs_title' => 'Related questions', - 'remove_favorites' => 'Remove from my favorites', - 'remove_x_favorites' => 'Remove ^ from my favorites', - 'reopened' => 'reopened', - 'reshown' => 'reshown', - 'results_for_x' => 'Search results for ^', - 'retagged' => 'retagged', - 'save_button' => 'Save Changes', - 'search_button' => 'Search', - 'search_explanation' => 'Please enter some text into the search box and try again.', - 'search_title' => 'Search results', - 'selected' => 'selected', - 'send_button' => 'Send', - 'since_x' => 'since ^', - 'suggest_ask' => 'Help get things started by ^1asking a question^2.', - 'suggest_category_qs' => 'To see more, click for all the ^1questions in this category^2.', - 'suggest_qs' => 'To see more, click for the ^1full list of questions^2.', - 'suggest_qs_tags' => 'To see more, click for the ^1full list of questions^2 or ^3popular tags^4.', - 'to_x' => 'to ^', - 'unanswered_qs_in_x' => 'Questions without answers in ^', - 'unanswered_qs_title' => 'Recent questions without answers', - 'unselected_qs_in_x' => 'Questions without a selected answer in ^', - 'unselected_qs_title' => 'Recent questions without a selected answer', - 'unupvoteda_qs_in_x' => 'Questions without an upvoted answer in ^', - 'unupvoteda_qs_title' => 'Recent questions without an upvoted answer', - 'upload_limit' => 'Too many uploads - please try again in an hour', - 'view_q_must_be_approved' => 'Your account must be approved to view question pages. Please wait or ^1add more information^2.', - 'view_q_must_confirm' => 'Please ^5confirm your email address^6 to view question pages.', - 'view_q_must_login' => 'Please ^1log in^2 or ^3register^4 to view question pages.', - 'viewed_qs_in_x' => 'Most viewed questions in ^', - 'viewed_qs_title' => 'Most viewed questions', - 'vote_disabled_approve' => 'Your account must be approved before you can vote', - 'vote_disabled_down' => 'Voting down is only available to some users', - 'vote_disabled_down_approve' => 'Your account must be approved before you can vote down', - 'vote_disabled_hidden_post' => 'You cannot vote on hidden posts', - 'vote_disabled_hidden_a' => 'You cannot vote on hidden answers', // @deprecated - 'vote_disabled_hidden_q' => 'You cannot vote on hidden questions', // @deprecated - 'vote_disabled_level' => 'Voting is only available to some users', - 'vote_disabled_my_post' => 'You cannot vote on your own posts', - 'vote_disabled_my_a' => 'You cannot vote on your own answers', // @deprecated - 'vote_disabled_my_q' => 'You cannot vote on your own questions', // @deprecated - 'vote_disabled_q_page_only' => 'Please view this question to vote', - 'vote_disabled_queued' => 'You can only vote on approved posts', - 'vote_down_must_confirm' => 'Please ^5confirm your email address^6 to vote down.', - 'vote_down_popup' => 'Click to vote down', - 'vote_limit' => 'Too many votes received - please try again in an hour', - 'vote_must_confirm' => 'Please ^5confirm your email address^6 to vote.', - 'vote_must_login' => 'Please ^1log in^2 or ^3register^4 to vote.', - 'vote_not_allowed' => 'Voting on this is not allowed', - 'vote_up_popup' => 'Click to vote up', - 'voted_down_popup' => 'You have voted this down - click to remove vote', - 'voted_qs_in_x' => 'Highest voted questions in ^', - 'voted_qs_title' => 'Highest voted questions', - 'voted_up_popup' => 'You have voted this up - click to remove vote', - 'written' => '', // blank in English - placeholder for other languages - 'x_ago' => '^ ago', - 'x_answers' => '^ answers', - 'x_comments' => '^ comments', - 'x_days' => '^ days', - 'x_disliked' => '^ dislike', - 'x_flags' => '^ flags', - 'x_hours' => '^ hours', - 'x_liked' => '^ like', - 'x_minutes' => '^ minutes', - 'x_months' => '^ months', - 'x_points' => '^ points', - 'x_questions' => '^ questions', - 'x_seconds' => '^ seconds', - 'x_tags' => '^ tags', - 'x_users' => '^ users', - 'x_views' => '^ views', - 'x_votes' => '^ votes', - 'x_weeks' => '^ weeks', - 'x_years' => '^ years', + '_decimal_point' => '.', + '_thousands_separator' => ',', + '_thousands_suffix' => 'k', + '_millions_suffix' => 'm', + '1_answer' => '1 answer', + '1_comment' => '1 comment', + '1_day' => '1 day', + '1_disliked' => '1 dislike', + '1_flag' => '1 flag', + '1_hour' => '1 hour', + '1_liked' => '1 like', + '1_minute' => '1 minute', + '1_month' => '1 month', + '1_point' => '1 point', + '1_question' => '1 question', + '1_second' => '1 second', + '1_tag' => '1 tag', + '1_user' => '1 user', + '1_view' => '1 view', + '1_vote' => '1 vote', + '1_week' => '1 week', + '1_year' => '1 year', + 'add_category_x_favorites' => 'Add category ^ to my favorites', + 'add_favorites' => 'Add to my favorites', + 'add_tag_x_favorites' => 'Add tag ^ to my favorites', + 'all_categories' => 'All categories', + 'anonymous' => 'anonymous', + 'answer_edited' => 'answer edited', + 'answer_reshown' => 'answer reshown', + 'answer_selected' => 'answer selected', + 'answered' => 'answered', + 'answered_qs_in_x' => 'Most answered questions in ^', + 'answered_qs_title' => 'Most answered questions', + 'asked' => 'asked', + 'asked_related_q' => 'asked related question', + 'by_x' => 'by ^', + 'cancel_button' => 'Cancel', + 'closed' => 'closed', + 'close_msg' => 'close message', + 'comment_edited' => 'comment edited', + 'comment_moved' => 'comment moved', + 'comment_reshown' => 'comment reshown', + 'commented' => 'commented', + 'date_day_min_digits' => 1, // 1 or 2 + 'date_format_other_years' => '^month ^day, ^year', + 'date_format_this_year' => '^month ^day', + 'date_month_1' => 'Jan', + 'date_month_2' => 'Feb', + 'date_month_3' => 'Mar', + 'date_month_4' => 'Apr', + 'date_month_5' => 'May', + 'date_month_6' => 'Jun', + 'date_month_7' => 'Jul', + 'date_month_8' => 'Aug', + 'date_month_9' => 'Sep', + 'date_month_10' => 'Oct', + 'date_month_11' => 'Nov', + 'date_month_12' => 'Dec', + 'date_year_digits' => 4, // 2 or 4 + 'edited' => 'edited', + 'email_error' => 'An error occurred trying to send the email.', + 'email_optional' => 'Email address (optional)', + 'field_required' => 'Please enter something in this field', + 'file_upload_limit_exceeded' => 'The size of the file exceeds the server\'s limits', + 'general_error' => 'A server error occurred - please try again.', + 'hidden' => 'hidden', + 'highest_users' => 'Top scoring users', + 'hot_qs_in_x' => 'Hot questions in ^', + 'hot_qs_title' => 'Hot questions', + 'image_not_read' => 'The image could not be read. Please upload one of: ^', + 'image_too_big_x_pc' => 'This image is too big. Please scale to ^% then try again.', + 'in_category_x' => 'in ^', + 'ip_address_x' => 'IP address ^', + 'logged_in_x' => 'Hello ^', + 'max_length_x' => 'Maximum length is ^ characters', + 'max_upload_size_x' => 'Maximum upload size is ^', + 'me' => 'me', + 'meta_order' => '^what^when^where^who', // you can reorder but DO NOT translate! e.g. <15 hours ago> + 'min_length_x' => 'Please provide more information - at least ^ characters', + 'moved' => 'moved', + 'nav_activity' => 'All Activity', + 'nav_admin' => 'Admin', + 'nav_ask' => 'Ask a Question', + 'nav_categories' => 'Categories', + 'nav_feedback' => 'Send feedback', + 'nav_home' => 'Home', + 'nav_hot' => 'Hot!', + 'nav_login' => 'Login', + 'nav_login_link_title' => "Show menu for logged in user "^"", + 'signed_in_as' => "Signed in as "^"", + 'admin_section' => "Admin section", + 'profile_section' => "Profile section", + 'nav_logout' => 'Logout', + 'nav_most_answers' => 'Most answers', + 'nav_most_recent' => 'Recent', + 'nav_most_views' => 'Most views', + 'nav_most_votes' => 'Most votes', + 'nav_no_answer' => 'No answer', + 'nav_no_selected_answer' => 'No selected answer', + 'nav_no_upvoted_answer' => 'No upvoted answer', + 'nav_qa' => 'Q&A', + 'nav_qs' => 'Questions', + 'nav_register' => 'Register', + 'nav_tags' => 'Tags', + 'nav_unanswered' => 'Unanswered', + 'nav_updates' => 'My Updates', + 'nav_users' => 'Users', + 'newest_users' => 'Newest users', + 'no_active_users' => 'No active users found', + 'no_answers_found' => 'No answers found', + 'no_answers_in_x' => 'No answers in ^', + 'no_categories_found' => 'No categories found', + 'no_category' => 'No category', + 'no_comments_found' => 'No comments found', + 'no_comments_in_x' => 'No comments in ^', + 'no_questions_found' => 'No questions found', + 'no_questions_in_x' => 'No questions in ^', + 'no_related_qs_title' => 'No related questions found', + 'no_results_for_x' => 'No results found for ^', + 'no_tags_found' => 'No tags found', + 'no_una_questions_found' => 'No unanswered questions found', + 'no_una_questions_in_x' => 'No unanswered questions in ^', + 'no_unselected_qs_found' => 'No questions found without a selected answer', + 'no_unupvoteda_qs_found' => 'No questions found without an upvoted answer', + 'page_label' => 'Page:', + 'page_next' => 'next', + 'page_not_found' => 'Page not found', + 'page_prev' => 'prev', + 'popular_tags' => 'Most popular tags', + 'questions_tagged_x' => 'Recent questions tagged ^', + 'list_of' => 'list of', + 'user_account' => 'Account', + 'recategorized' => 'recategorized', + 'recent_activity_in_x' => 'Recent activity in ^', + 'recent_activity_title' => 'Recent activity', + 'recent_as_in_x' => 'Recently answered questions in ^', + 'recent_as_title' => 'Recently answered questions', + 'recent_cs_in_x' => 'Recently commented questions in ^', + 'recent_cs_title' => 'Recently commented questions', + 'recent_qs_as_in_x' => 'Recent questions and answers in ^', + 'recent_qs_as_title' => 'Recent questions and answers', + 'recent_qs_in_x' => 'Recent questions in ^', + 'recent_qs_title' => 'Recent questions', + 'related_qs_title' => 'Related questions', + 'remove_favorites' => 'Remove from my favorites', + 'remove_x_favorites' => 'Remove ^ from my favorites', + 'reopened' => 'reopened', + 'reshown' => 'reshown', + 'results_for_x' => 'Search results for ^', + 'retagged' => 'retagged', + 'save_button' => 'Save Changes', + 'search_button' => 'Search', + 'search_explanation' => 'Please enter some text into the search box and try again.', + 'search_title' => 'Search results', + 'selected' => 'selected', + 'send_button' => 'Send', + 'since_x' => 'since ^', + 'suggest_ask' => 'Help get things started by ^1asking a question^2.', + 'suggest_category_qs' => 'To see more, click for all the ^1questions in this category^2.', + 'suggest_qs' => 'To see more, click for the ^1full list of questions^2.', + 'suggest_qs_tags' => 'To see more, click for the ^1full list of questions^2 or ^3popular tags^4.', + 'to_x' => 'to ^', + 'unanswered_qs_in_x' => 'Questions without answers in ^', + 'unanswered_qs_title' => 'Recent questions without answers', + 'unselected_qs_in_x' => 'Questions without a selected answer in ^', + 'unselected_qs_title' => 'Recent questions without a selected answer', + 'unupvoteda_qs_in_x' => 'Questions without an upvoted answer in ^', + 'unupvoteda_qs_title' => 'Recent questions without an upvoted answer', + 'upload_limit' => 'Too many uploads - please try again in an hour', + 'view_q_must_be_approved' => 'Your account must be approved to view question pages. Please wait or ^1add more information^2.', + 'view_q_must_confirm' => 'Please ^5confirm your email address^6 to view question pages.', + 'view_q_must_login' => 'Please ^1log in^2 or ^3register^4 to view question pages.', + 'viewed_qs_in_x' => 'Most viewed questions in ^', + 'viewed_qs_title' => 'Most viewed questions', + 'vote_disabled_approve' => 'Your account must be approved before you can vote', + 'vote_disabled_down' => 'Voting down is only available to some users', + 'vote_disabled_down_approve' => 'Your account must be approved before you can vote down', + 'vote_disabled_hidden_post' => 'You cannot vote on hidden posts', + 'vote_disabled_hidden_a' => 'You cannot vote on hidden answers', // @deprecated + 'vote_disabled_hidden_q' => 'You cannot vote on hidden questions', // @deprecated + 'vote_disabled_level' => 'Voting is only available to some users', + 'vote_disabled_my_post' => 'You cannot vote on your own posts', + 'vote_disabled_my_a' => 'You cannot vote on your own answers', // @deprecated + 'vote_disabled_my_q' => 'You cannot vote on your own questions', // @deprecated + 'vote_disabled_q_page_only' => 'Please view this question to vote', + 'vote_disabled_queued' => 'You can only vote on approved posts', + 'vote_down_must_confirm' => 'Please ^5confirm your email address^6 to vote down.', + 'vote_down_popup' => 'Click to vote down', + 'vote_down_popup_answer' => 'Vote down answer', + 'vote_down_popup_question' => 'Vote down question', + 'vote_limit' => 'Too many votes received - please try again in an hour', + 'vote_must_confirm' => 'Please ^5confirm your email address^6 to vote.', + 'vote_must_login' => 'Please ^1log in^2 or ^3register^4 to vote.', + 'vote_not_allowed' => 'Voting on this is not allowed', + 'vote_up_popup_answer' => 'Vote up answer', + 'vote_up_popup_question' => 'Vote up question', + 'vote_up_popup' => 'Click to vote up', + 'voted_down_popup' => 'You have voted this down - click to remove vote', + 'voted_qs_in_x' => 'Highest voted questions in ^', + 'voted_qs_title' => 'Highest voted questions', + 'voted_up_popup' => 'You have voted this up - click to remove vote', + 'netvote_of_question' => 'Votes for this question:', + 'written' => '', // blank in English - placeholder for other languages + 'x_ago' => '^ ago', + 'x_answers' => '^ answers', + 'x_comments' => '^ comments', + 'x_days' => '^ days', + 'x_disliked' => '^ dislike', + 'x_flags' => '^ flags', + 'x_hours' => '^ hours', + 'x_liked' => '^ like', + 'x_minutes' => '^ minutes', + 'x_months' => '^ months', + 'x_points' => '^ points', + 'x_questions' => '^ questions', + 'x_seconds' => '^ seconds', + 'x_tags' => '^ tags', + 'x_users' => '^ users', + 'x_views' => '^ views', + 'x_votes' => '^ votes', + 'x_weeks' => '^ weeks', + 'x_years' => '^ years', ); diff --git a/qa-include/pages/ask.php b/qa-include/pages/ask.php index 4f11c2438..374d2b2ff 100644 --- a/qa-include/pages/ask.php +++ b/qa-include/pages/ask.php @@ -189,6 +189,7 @@ $field = qa_editor_load_field($editor, $qa_content, @$in['content'], @$in['format'], 'content', 12, false); $field['label'] = qa_lang_html('question/q_content_label'); $field['error'] = qa_html(@$errors['content']); +$field['id'] = 'content'; $custom = qa_opt('show_custom_ask') ? trim(qa_opt('custom_ask')) : ''; @@ -208,6 +209,7 @@ 'tags' => 'name="title" id="title" autocomplete="off"', 'value' => qa_html(@$in['title']), 'error' => qa_html(@$errors['title']), + 'id' => 'title' ), 'similar' => array( diff --git a/qa-include/pages/feedback.php b/qa-include/pages/feedback.php index 477b237d7..2916f8e5b 100644 --- a/qa-include/pages/feedback.php +++ b/qa-include/pages/feedback.php @@ -138,21 +138,24 @@ 'value' => qa_html(@$inmessage), 'rows' => 8, 'error' => qa_html(@$errors['message']), + 'id' => 'message' ), 'name' => array( 'type' => $feedbacksent ? 'static' : '', 'label' => qa_lang_html('misc/feedback_name'), - 'tags' => 'name="name"', + 'tags' => 'name="name" id="nameInput"', 'value' => qa_html(isset($inname) ? $inname : @$userprofile['name']), + 'id' => 'nameInput' ), 'email' => array( 'type' => $feedbacksent ? 'static' : '', 'label' => qa_lang_html('misc/feedback_email'), - 'tags' => 'name="email"', + 'tags' => 'name="email" id="emailInput"', 'value' => qa_html(isset($inemail) ? $inemail : qa_get_logged_in_email()), 'note' => $feedbacksent ? null : qa_opt('email_privacy'), + 'id' => 'emailInput' ), ), diff --git a/qa-include/pages/forgot.php b/qa-include/pages/forgot.php index 9b1ce5b23..c606dbfe8 100644 --- a/qa-include/pages/forgot.php +++ b/qa-include/pages/forgot.php @@ -94,6 +94,7 @@ 'value' => qa_html(@$inemailhandle), 'error' => qa_html(@$errors['emailhandle']), 'note' => qa_lang_html('users/send_reset_note'), + 'id' => 'emailhandle' ), ), diff --git a/qa-include/pages/login.php b/qa-include/pages/login.php index 0a8582656..0ad6532e6 100644 --- a/qa-include/pages/login.php +++ b/qa-include/pages/login.php @@ -151,25 +151,28 @@ 'fields' => array( 'email_handle' => array( 'label' => qa_opt('allow_login_email_only') ? qa_lang_html('users/email_label') : qa_lang_html('users/email_handle_label'), - 'tags' => 'name="emailhandle" id="emailhandle" dir="auto"', + 'tags' => 'name="emailhandle" id="emailhandle" dir="auto" autocomplete="username"', 'value' => qa_html(@$inemailhandle), 'error' => qa_html(@$errors['emailhandle']), + 'id' => 'emailhandle' ), 'password' => array( 'type' => 'password', 'label' => qa_lang_html('users/password_label'), - 'tags' => 'name="password" id="password" dir="auto"', + 'tags' => 'name="password" id="password" dir="auto" autocomplete="current-password"', 'value' => qa_html(@$inpassword), 'error' => empty($errors['password']) ? '' : (qa_html(@$errors['password']) . ' - ' . $forgothtml), 'note' => $passwordsent ? qa_lang_html('users/password_sent') : $forgothtml, + 'id' => 'password' ), 'remember' => array( 'type' => 'checkbox', 'label' => qa_lang_html('users/remember_label'), - 'tags' => 'name="remember"', + 'tags' => 'name="remember" id="rememberInput"', 'value' => !empty($inremember), + 'id' => 'rememberInput' ), ), diff --git a/qa-include/pages/question-post.php b/qa-include/pages/question-post.php index 801be44c9..6e708ab1d 100644 --- a/qa-include/pages/question-post.php +++ b/qa-include/pages/question-post.php @@ -309,9 +309,10 @@ function qa_page_q_edit_q_form(&$qa_content, $question, $in, $errors, $completet 'title' => array( 'type' => $question['editable'] ? 'text' : 'static', 'label' => qa_lang_html('question/q_title_label'), - 'tags' => 'name="q_title"', + 'tags' => 'name="q_title" id="q_title"', 'value' => qa_html(($question['editable'] && isset($in['title'])) ? $in['title'] : $question['title']), 'error' => qa_html(@$errors['title']), + 'id' => 'q_title' ), 'category' => array( @@ -322,6 +323,8 @@ function qa_page_q_edit_q_form(&$qa_content, $question, $in, $errors, $completet 'content' => array( 'label' => qa_lang_html('question/q_content_label'), 'error' => qa_html(@$errors['content']), + 'tags' => 'name="q_content" id="q_content"', + 'id' => 'q_content' ), 'extra' => array( @@ -404,8 +407,9 @@ function qa_page_q_edit_q_form(&$qa_content, $question, $in, $errors, $completet $form['fields']['silent'] = array( 'type' => 'checkbox', 'label' => qa_lang_html('question/save_silent_label'), - 'tags' => 'name="q_silent"', + 'tags' => 'name="q_silent" id="q_silent"', 'value' => qa_html(@$in['silent']), + 'id' => 'q_silent' ); } @@ -711,10 +715,11 @@ function qa_page_q_edit_a_form(&$qa_content, $id, $answer, $question, $answers, '', 'type' => 'checkbox', 'tight' => true, + 'id' => $prefix . 'dotoc' ); $form['fields']['commenton'] = array( - 'tags' => 'name="' . $prefix . 'commenton"', + 'tags' => 'name="' . $prefix . 'commenton" id="' . $prefix . 'commenton"', 'id' => $prefix . 'commenton', 'type' => 'select', 'note' => qa_lang_html($hascomments ? 'question/a_convert_warn_cs' : 'question/a_convert_warn'), @@ -744,8 +749,9 @@ function qa_page_q_edit_a_form(&$qa_content, $id, $answer, $question, $answers, $form['fields']['silent'] = array( 'type' => 'checkbox', 'label' => qa_lang_html('question/save_silent_label'), - 'tags' => 'name="' . $prefix . 'silent"', + 'tags' => 'name="' . $prefix . 'silent" id="' . $prefix . '_silent"', 'value' => qa_html(@$in['silent']), + 'id' => $prefix . '_silent' ); } @@ -951,8 +957,9 @@ function qa_page_q_edit_c_form(&$qa_content, $id, $comment, $in, $errors) $form['fields']['silent'] = array( 'type' => 'checkbox', 'label' => qa_lang_html('question/save_silent_label'), - 'tags' => 'name="' . $prefix . 'silent"', + 'tags' => 'name="' . $prefix . 'silent" id="' . $prefix . '_silent"', 'value' => qa_html(@$in['silent']), + 'id' => $prefix . '_silent' ); } diff --git a/qa-include/pages/question-view.php b/qa-include/pages/question-view.php index 0f5940933..904d976a0 100644 --- a/qa-include/pages/question-view.php +++ b/qa-include/pages/question-view.php @@ -395,7 +395,7 @@ function qa_page_q_question_view($question, $parentquestion, $closepost, $usersh if ($question['answerbutton']) { // don't show if shown by default $buttons['answer'] = array( - 'tags' => 'name="q_doanswer" id="q_doanswer" onclick="return qa_toggle_element(\'anew\')"', + 'tags' => 'name="q_doanswer" id="q_doanswer" onclick="return qa_toggle_element(\'anew\', true)"', 'label' => qa_lang_html('question/answer_button'), 'popup' => qa_lang_html('question/answer_q_popup'), ); @@ -403,7 +403,7 @@ function qa_page_q_question_view($question, $parentquestion, $closepost, $usersh if ($question['commentbutton']) { $buttons['comment'] = array( - 'tags' => 'name="q_docomment" onclick="return qa_toggle_element(\'c' . $questionid . '\')"', + 'tags' => 'name="q_docomment" onclick="return qa_toggle_element(\'c' . $questionid . '\', true)"', 'label' => qa_lang_html('question/comment_button'), 'popup' => qa_lang_html('question/comment_q_popup'), ); @@ -617,7 +617,7 @@ function qa_page_q_answer_view($question, $answer, $isselected, $usershtml, $for if ($answer['commentbutton']) { $buttons['comment'] = array( - 'tags' => 'name="' . $prefix . 'docomment" onclick="return qa_toggle_element(\'c' . $answerid . '\')"', + 'tags' => 'name="' . $prefix . 'docomment" onclick="return qa_toggle_element(\'c' . $answerid . '\', true)"', 'label' => qa_lang_html('question/comment_button'), 'popup' => qa_lang_html('question/comment_a_popup'), ); @@ -755,7 +755,7 @@ function qa_page_q_comment_view($question, $parent, $comment, $usershtml, $formr if ($parent['commentbutton'] && qa_opt('show_c_reply_buttons') && $comment['type'] == 'C') { $buttons['comment'] = array( 'tags' => 'name="' . (($parent['basetype'] == 'Q') ? 'q' : ('a' . qa_html($parent['postid']))) . - '_docomment" onclick="return qa_toggle_element(\'c' . qa_html($parent['postid']) . '\')"', + '_docomment" onclick="return qa_toggle_element(\'c' . qa_html($parent['postid']) . '\', true)"', 'label' => qa_lang_html('question/reply_button'), 'popup' => qa_lang_html('question/reply_c_popup'), ); @@ -935,8 +935,6 @@ function qa_page_q_add_a_form(&$qa_content, $formid, $captchareason, $question, $form = array( 'tags' => 'method="post" action="' . qa_self_html() . '" name="a_form"', - 'title' => qa_lang_html('question/your_answer_title'), - 'fields' => array( 'custom' => array( 'type' => 'custom', @@ -947,7 +945,11 @@ function qa_page_q_add_a_form(&$qa_content, $formid, $captchareason, $question, qa_editor_load_field($editor, $qa_content, @$in['content'], @$in['format'], 'a_content', 12, $formrequested, $loadnow), array( 'error' => qa_html(@$errors['content']), - ) + ), + [ + 'label' => qa_lang_html('question/your_answer_title'), + 'id' => 'a_content' + ] ), ), @@ -1083,10 +1085,11 @@ function qa_page_q_add_c_form(&$qa_content, $question, $parent, $formid, $captch $custom = qa_opt('show_custom_comment') ? trim(qa_opt('custom_comment')) : ''; + $label = qa_lang_html(($question['postid'] == $parent['postid']) ? 'question/your_comment_q' : 'question/your_comment_a'); $form = array( 'tags' => 'method="post" action="' . qa_self_html() . '" name="c_form_' . qa_html($parent['postid']) . '"', - 'title' => qa_lang_html(($question['postid'] == $parent['postid']) ? 'question/your_comment_q' : 'question/your_comment_a'), + 'fields' => array( 'custom' => array( @@ -1098,7 +1101,11 @@ function qa_page_q_add_c_form(&$qa_content, $question, $parent, $formid, $captch qa_editor_load_field($editor, $qa_content, @$in['content'], @$in['format'], $prefix . 'content', 4, $loadfocusnow, $loadfocusnow), array( 'error' => qa_html(@$errors['content']), - ) + ), + [ + 'label' => $label, + 'id' => $prefix . 'content' + ] ), ), diff --git a/qa-include/pages/register.php b/qa-include/pages/register.php index dacde6c39..e0ba230fe 100644 --- a/qa-include/pages/register.php +++ b/qa-include/pages/register.php @@ -158,6 +158,7 @@ 'tags' => 'name="handle" id="handle" dir="auto"', 'value' => qa_html(@$inhandle), 'error' => qa_html(@$errors['handle']), + 'id' => 'handle' ), 'password' => array( @@ -166,6 +167,7 @@ 'tags' => 'name="password" id="password" dir="auto"', 'value' => qa_html(@$inpassword), 'error' => qa_html(@$errors['password']), + 'id' => 'password' ), 'email' => array( @@ -174,6 +176,7 @@ 'value' => qa_html(@$inemail), 'note' => qa_opt('email_privacy'), 'error' => qa_html(@$errors['email']), + 'id' => 'email' ), ), diff --git a/qa-include/pages/user-profile.php b/qa-include/pages/user-profile.php index 73a926467..d51af83e2 100644 --- a/qa-include/pages/user-profile.php +++ b/qa-include/pages/user-profile.php @@ -285,6 +285,11 @@ if (qa_clicked('dodelete') && ($loginlevel >= QA_USER_LEVEL_ADMIN)) { require_once QA_INCLUDE_DIR . 'app/users-edit.php'; + qa_report_event('u_delete_before', $loginuserid, qa_get_logged_in_handle(), qa_cookie_get(), array( + 'userid' => $userid, + 'handle' => $useraccount['handle'], + )); + qa_delete_user($userid); qa_report_event('u_delete', $loginuserid, qa_get_logged_in_handle(), qa_cookie_get(), array( diff --git a/qa-include/qa-base.php b/qa-include/qa-base.php index 4a9f95125..a06bbaeb0 100644 --- a/qa-include/qa-base.php +++ b/qa-include/qa-base.php @@ -20,8 +20,8 @@ */ -define('QA_VERSION', '1.8.3'); // also used as suffix for .js and .css requests -define('QA_BUILD_DATE', '2019-01-12'); +define('QA_VERSION', '1.8.4'); // also used as suffix for .js and .css requests +define('QA_BUILD_DATE', '2020-05-07'); /** @@ -1024,7 +1024,7 @@ function qa_sanitize_html($html, $linksnewwindow = false, $storage = false) $safe = htmLawed($html, array( 'safe' => 1, - 'elements' => '*+embed+object-form', + 'elements' => '*-form', 'schemes' => 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; *:file, http, https; style: !; classid:clsid', 'keep_bad' => 0, 'anti_link_spam' => array('/.*/', ''), @@ -1167,7 +1167,11 @@ function qa_gpc_to_string($string) { if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } - return get_magic_quotes_gpc() ? stripslashes($string) : $string; + // get_magic_quotes_gpc always returns false from PHP 5.4; this avoids deprecation notice on PHP 7.4+ + if (qa_php_version_below('5.4.0')) + return get_magic_quotes_gpc() ? stripslashes($string) : $string; + else + return $string; } @@ -1180,7 +1184,11 @@ function qa_string_to_gpc($string) { if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); } - return get_magic_quotes_gpc() ? addslashes($string) : $string; + // get_magic_quotes_gpc always returns false from PHP 5.4; this avoids deprecation notice on PHP 7.4+ + if (qa_php_version_below('5.4.0')) + return get_magic_quotes_gpc() ? addslashes($string) : $string; + else + return $string; } diff --git a/qa-include/qa-theme-base.php b/qa-include/qa-theme-base.php index 9069b7406..6312c4421 100644 --- a/qa-include/qa-theme-base.php +++ b/qa-include/qa-theme-base.php @@ -156,7 +156,7 @@ public function output_split($parts, $class, $outertag = 'span', $innertag = 'sp return; $this->output( - '<' . $outertag . ' class="' . $class . (isset($extraclass) ? (' ' . $extraclass) : '') . '">', + '<' . $outertag . ' class="' . $class . (isset($extraclass) ? (' ' . $extraclass) : '') . '"' . (isset($parts['aria-hidden']) ? 'aria-hidden="true"' : '') . '>', (strlen(@$parts['prefix']) ? ('<' . $innertag . ' class="' . $class . '-pad">' . $parts['prefix'] . '') : '') . (strlen(@$parts['data']) ? ('<' . $innertag . ' class="' . $class . '-data">' . $parts['data'] . '') : '') . (strlen(@$parts['suffix']) ? ('<' . $innertag . ' class="' . $class . '-pad">' . $parts['suffix'] . '') : ''), @@ -566,12 +566,12 @@ public function search_button($search) $this->output(''); } - public function nav($navtype, $level = null) + public function nav($navtype, $level = null, $ariaLabel = "") { $navigation = @$this->content['navigation'][$navtype]; if ($navtype == 'user' || isset($navigation)) { - $this->output('
'); + $this->output('
'); + $this->output(''); } } public function nav_list($navigation, $class, $level = null) { - $this->output('
    '); + $role = 'role="tablist"'; + foreach ($navigation as $key => $navlink) { + $role = isset($navlink['role']) ? 'role="presentation"' : $role; + } + $this->output('
      '); $index = 0; @@ -626,8 +630,9 @@ public function nav_item($key, $navlink, $class, $level = null) '/' => '-', )); - $this->output('
    • '); + $role = isset($navlink['role']) ? $navlink['role'] : 'tab'; + $this->output('
    • '); $this->nav_link($navlink, $class); $subnav = isset($navlink['subnav']) ? $navlink['subnav'] : array(); @@ -715,7 +720,7 @@ public function main() $content = $this->content; $hidden = !empty($content['hidden']) ? ' qa-main-hidden' : ''; - $this->output('
      '); + $this->output('
      '); $this->widgets('main', 'top'); @@ -732,7 +737,7 @@ public function main() $this->widgets('main', 'bottom'); - $this->output('
      ', ''); + $this->output(' ', ''); } public function page_title_error() @@ -911,7 +916,7 @@ public function attribution() // Hi there. I'd really appreciate you displaying this link on your Q2A site. Thank you - Gideon $this->output( - '
      ', + '
      ', 'Powered by Question2Answer', '
      ' ); @@ -986,7 +991,7 @@ public function form_body($form) $columns = $this->form_columns($form); if ($columns) - $this->output(''); + $this->output('
      '); $this->form_ok($form, $columns); $this->form_fields($form, $columns); @@ -1065,9 +1070,9 @@ public function form_field_rows($form, $columns, $field) if (isset($field['id'])) { if ($columns == 1) - $this->output('', ''); + $this->output('', ''); else - $this->output(''); + $this->output(''); } else $this->output(''); @@ -1102,15 +1107,13 @@ public function form_label($field, $style, $columns, $prefixed, $suffixed, $cols $this->output('