diff --git a/forum/qa-include/lang/qa-lang-question.php b/forum/qa-include/lang/qa-lang-question.php index 163a533cd..250255a09 100644 --- a/forum/qa-include/lang/qa-lang-question.php +++ b/forum/qa-include/lang/qa-lang-question.php @@ -108,6 +108,12 @@ 'flag_button' => 'flag', 'flag_c_popup' => 'Flag this comment as spam or inappropriate', 'flag_hide_button' => 'flag and hide', + + 'mute_button' => 'Mute thread', + 'mute_popup' => 'Mute this thread to not receive any further notifications', + 'unmute_button' => 'Unmute thread', + 'unmute_popup' => 'Unmute this thread to receive notifications again', + 'flag_limit' => 'Too many posts flagged - please try again in an hour', 'flag_must_confirm' => 'Please ^5confirm your email address^6 to flag posts.', 'flag_must_login' => 'Please ^1log in^2 or ^3register^4 to flag posts.', diff --git a/forum/qa-include/pages/question-submit.php b/forum/qa-include/pages/question-submit.php index a3024d9c9..713c8e008 100644 --- a/forum/qa-include/pages/question-submit.php +++ b/forum/qa-include/pages/question-submit.php @@ -39,6 +39,7 @@ function qa_page_q_single_click_q($question, $answers, $commentsfollows, $closep { require_once QA_INCLUDE_DIR.'app/post-update.php'; require_once QA_INCLUDE_DIR.'app/limits.php'; + require_once QA_PLUGIN_DIR.'/q2apro-on-site-notifications/utils.php'; $userid=qa_get_logged_in_userid(); $handle=qa_get_logged_in_handle(); @@ -49,6 +50,16 @@ function qa_page_q_single_click_q($question, $answers, $commentsfollows, $closep return true; } + if (qa_clicked('q_domute') && qa_page_q_click_check_form_code($question, $error)) { + mute_thread($userid, $question['postid']); + return true; + } + + if (qa_clicked('q_dounmute') && qa_page_q_click_check_form_code($question, $error)) { + unmute_thread($userid, $question['postid']); + return true; + } + if ( (qa_clicked('q_dohide') && $question['hideable']) || (qa_clicked('q_doreject') && $question['moderatable']) ) if (qa_page_q_click_check_form_code($question, $error)) { qa_question_set_hidden($question, true, $userid, $handle, $cookieid, $answers, $commentsfollows, $closepost); @@ -123,6 +134,8 @@ function qa_page_q_single_click_a($answer, $question, $answers, $commentsfollows If there is an error to display, it will be passed out in $error. */ { + require_once QA_PLUGIN_DIR.'/q2apro-on-site-notifications/utils.php'; + $userid=qa_get_logged_in_userid(); $handle=qa_get_logged_in_handle(); $cookieid=qa_cookie_get(); @@ -141,6 +154,16 @@ function qa_page_q_single_click_a($answer, $question, $answers, $commentsfollows return true; } + if (qa_clicked($prefix . 'domute') && qa_page_q_click_check_form_code($answer, $error)) { + mute_thread($userid, $answer['postid']); + return true; + } + + if (qa_clicked($prefix . 'dounmute') && qa_page_q_click_check_form_code($answer, $error)) { + unmute_thread($userid, $answer['postid']); + return true; + } + if ( (qa_clicked($prefix.'dohide') && $answer['hideable']) || (qa_clicked($prefix.'doreject') && $answer['moderatable']) ) if (qa_page_q_click_check_form_code($answer, $error)) { qa_answer_set_hidden($answer, true, $userid, $handle, $cookieid, $question, $commentsfollows); diff --git a/forum/qa-lang/pl/qa-lang-question.php b/forum/qa-lang/pl/qa-lang-question.php index b372c599e..291dc4ae2 100644 --- a/forum/qa-lang/pl/qa-lang-question.php +++ b/forum/qa-lang/pl/qa-lang-question.php @@ -96,6 +96,12 @@ 'flag_button' => 'zgłoś', 'flag_c_popup' => 'Zgłoś ten komentarz jako spam lub niezgodny z regulaminem', 'flag_hide_button' => 'zgłoś i ukryj', + + 'mute_button' => 'Wycisz wątek', + 'mute_popup' => 'Wycisz ten wątek, aby nie otrzymywać dalszych powiadomień', + 'unmute_button' => 'Subskrybuj wątek', + 'unmute_popup' => 'Subskrybuj wątek, aby ponownie otrzymywać powiadomienia', + 'flag_limit' => 'Zbyt wiele zgłoszeń. Spróbuj ponownie za godzinę', 'flag_must_confirm' => '^5Potwierdź swój adres email^6, aby móc zgłaszać treść.', 'flag_must_login' => '^1Zaloguj^2 lub ^3zarejestruj się^4, aby móc zgłaszać treść.', diff --git a/forum/qa-plugin/q2apro-on-site-notifications/q2apro-history-check.php b/forum/qa-plugin/q2apro-on-site-notifications/q2apro-history-check.php index 9dcbedb69..5145ca2f2 100644 --- a/forum/qa-plugin/q2apro-on-site-notifications/q2apro-history-check.php +++ b/forum/qa-plugin/q2apro-on-site-notifications/q2apro-history-check.php @@ -29,8 +29,26 @@ * Link to plugin file: https://github.com/NoahY/q2a-history/blob/master/qa-history-check.php */ +require_once __DIR__ . '/utils.php'; + class q2apro_history_check { + public function init_queries($tableslc) + { + $queries = []; + + if (!in_array(qa_db_add_table_prefix('muted_threads'), $tableslc)) { + $queries[] = 'CREATE TABLE IF NOT EXISTS ^muted_threads ( + postid bigint(20) unsigned NOT NULL, + userid bigint(20) unsigned NOT NULL, + KEY postid (postid), + KEY userid (userid) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8'; + } + + return $queries; + } + function process_event($event, $userid, $handle, $cookieid, $params) { if (!qa_opt('event_logger_to_database')) { @@ -139,11 +157,12 @@ private function handle_comments_thread($cookieId, $params) $commentsQuery = qa_db_query_sub('SELECT DISTINCT userid FROM `^posts` WHERE `parentid` = # AND `type` = "C" AND `userid` IS NOT NULL', $params['parentid']); + $muteChecker = new ThreadMuteChecker($params['parentid']); while (($comment = qa_db_read_one_assoc($commentsQuery, true)) !== null) { $commentUserId = $comment['userid']; // don't inform user that comments, and don't inform user that comments on his own question/answer - if ($commentUserId != $postUserId && $commentUserId != $parentUserId) { + if ($commentUserId != $postUserId && $commentUserId != $parentUserId && !$muteChecker->hasUserMutedThread($commentUserId)) { $commentUserHandle = qa_userid_to_handle($commentUserId); qa_db_query_sub( diff --git a/forum/qa-plugin/q2apro-on-site-notifications/q2apro-onsitenotifications-layer.php b/forum/qa-plugin/q2apro-on-site-notifications/q2apro-onsitenotifications-layer.php index b731ccc46..8bde830d9 100644 --- a/forum/qa-plugin/q2apro-on-site-notifications/q2apro-onsitenotifications-layer.php +++ b/forum/qa-plugin/q2apro-on-site-notifications/q2apro-onsitenotifications-layer.php @@ -157,6 +157,60 @@ function doctype() { qa_html_theme_base::doctype(); } + public function a_item_buttons($a_item) + { + if (!empty($a_item['form'])) { + $answerId = $a_item['raw']['postid']; + $questionId = $a_item['raw']['parentid']; + + $this->addCustomButtons( + $a_item, + 'a' . $a_item['raw']['postid'], + ' onclick="return qa_answer_click('.qa_js($answerId).', '.qa_js($questionId).', this);"' + ); + } + + return parent::a_item_buttons($a_item); + } + + public function q_view_buttons($q_view) + { + if (!empty($q_view['form'])) { + $this->addCustomButtons( + $q_view, + 'q', + ' onclick="qa_show_waiting_after(this, false);"' + ); + } + + return parent::q_view_buttons($q_view); + } + + protected function addCustomButtons(&$view, $prefix, $clicksuffix) + { + require_once __DIR__ . '/../qa-plugin/q2apro-on-site-notifications/utils.php'; + + $current_user_id = qa_get_logged_in_userid(); + if ($current_user_id === null) { + return; + } + + $postId = $view['raw']['postid']; + $participantsObtainer = new ThreadParticipantsObtainer($postId); + if (!$participantsObtainer->isParticipant($current_user_id)) { + return; + } + + $muteChecker = new ThreadMuteChecker($postId); + $muted = $muteChecker->hasUserMutedThread($current_user_id); + $action = $muted ? 'unmute' : 'mute'; + $view['form']['buttons'][$action]=array( + 'tags' => "name=\"{$prefix}_do{$action}\"" . $clicksuffix, + 'label' => qa_lang_html("question/{$action}_button"), + 'popup' => qa_lang_html("question/{$action}_popup"), + ); + } + } // end qa_html_theme_layer /* diff --git a/forum/qa-plugin/q2apro-on-site-notifications/utils.php b/forum/qa-plugin/q2apro-on-site-notifications/utils.php new file mode 100644 index 000000000..5fac495e2 --- /dev/null +++ b/forum/qa-plugin/q2apro-on-site-notifications/utils.php @@ -0,0 +1,75 @@ +muted_users = $this->getUsersWhoMutedThread($postid); + } + + public function hasUserMutedThread(int $userid) + { + if (!isset($this->cache[$userid])) { + $this->cache[$userid] = in_array($userid, $this->muted_users); + } + + return $this->cache[$userid]; + } + + protected function getUsersWhoMutedThread(int $postid) + { + return qa_db_read_all_values( + qa_db_query_sub('SELECT userid FROM ^muted_threads + WHERE postid = # + ', $postid), + ); + } +} + +class ThreadParticipantsObtainer +{ + public $participants; + + public function __construct(int $postid) + { + $this->participants = $this->getThreadParticipants($postid); + } + + public function isParticipant(int $userid) + { + return in_array("$userid", $this->participants); + } + + protected function getThreadParticipants(int $postid) + { + $query = implode(' UNION ', [ + 'SELECT userid FROM ^posts WHERE postid = #', + 'SELECT DISTINCT userid FROM ^posts WHERE parentid = #', + ]); + + return qa_db_read_all_values( + qa_db_query_sub($query, $postid, $postid), + ); + } +} + +function mute_thread(int $userid, int $postid) +{ + qa_db_query_sub( + "INSERT INTO ^muted_threads (userid, postid) VALUES (#, #)", + $userid, + $postid + ); +} + +function unmute_thread(int $userid, int $postid) +{ + qa_db_query_sub( + "DELETE FROM ^muted_threads WHERE userid = # AND postid = #", + $userid, + $postid + ); +} diff --git a/forum/qa-theme/SnowFlat/images/icons/mute-white.png b/forum/qa-theme/SnowFlat/images/icons/mute-white.png new file mode 100644 index 000000000..6ca31749e Binary files /dev/null and b/forum/qa-theme/SnowFlat/images/icons/mute-white.png differ diff --git a/forum/qa-theme/SnowFlat/images/icons/mute.png b/forum/qa-theme/SnowFlat/images/icons/mute.png new file mode 100644 index 000000000..b4f216ee4 Binary files /dev/null and b/forum/qa-theme/SnowFlat/images/icons/mute.png differ diff --git a/forum/qa-theme/SnowFlat/images/icons/unmute-white.png b/forum/qa-theme/SnowFlat/images/icons/unmute-white.png new file mode 100644 index 000000000..e21b841ea Binary files /dev/null and b/forum/qa-theme/SnowFlat/images/icons/unmute-white.png differ diff --git a/forum/qa-theme/SnowFlat/images/icons/unmute.png b/forum/qa-theme/SnowFlat/images/icons/unmute.png new file mode 100644 index 000000000..f82526020 Binary files /dev/null and b/forum/qa-theme/SnowFlat/images/icons/unmute.png differ diff --git a/forum/qa-theme/SnowFlat/qa-styles.css b/forum/qa-theme/SnowFlat/qa-styles.css index 9051bbaef..1ae99c558 100644 --- a/forum/qa-theme/SnowFlat/qa-styles.css +++ b/forum/qa-theme/SnowFlat/qa-styles.css @@ -2338,6 +2338,12 @@ input[type="submit"], button { .qa-form-light-button-unflag, .qa-form-light-button-clearflags { background-image: url('images/icons/un-flag-white.png'); } +.qa-form-light-button-mute { + background-image: url('images/icons/mute-white.png'); +} +.qa-form-light-button-unmute { + background-image: url('images/icons/unmute-white.png'); +} .qa-form-light-button-hide { background-image: url('images/icons/hide-white.png'); } @@ -2856,6 +2862,12 @@ input[type="submit"], button { .qa-c-list-item .qa-form-light-button-unflag, .qa-c-list-item .qa-form-light-button-clearflags { background-image: url('images/icons/un-flag.png'); } +.qa-c-list-item .qa-form-light-button-mute { + background-image: url('images/icons/mute.png'); +} +.qa-c-list-item .qa-form-light-button-unmute { + background-image: url('images/icons/unmute.png'); +} .qa-c-list-item .qa-form-light-button-hide { background-image: url('images/icons/hide.png'); }