Index: trunk/extensions/LiquidThreads/LqtFunctions.php |
— | — | @@ -102,3 +102,15 @@ |
103 | 103 | return wfMsgExt( 'lqt-log-action-move', 'parseinline', |
104 | 104 | array( $title->getPrefixedText(), $parameters[0], $parameters[1] ) ); |
105 | 105 | } |
| 106 | + |
| 107 | +function lqtGetPreferences( $user, &$preferences ) { |
| 108 | + wfLoadExtensionMessages( 'LiquidThreads' ); |
| 109 | + $preferences['lqtnotifytalk'] = |
| 110 | + array( |
| 111 | + 'type' => 'toggle', |
| 112 | + 'label-message' => 'lqt-preference-notify-talk', |
| 113 | + 'section' => 'personal/email' |
| 114 | + ); |
| 115 | + |
| 116 | + return true; |
| 117 | +} |
Index: trunk/extensions/LiquidThreads/LiquidThreads.php |
— | — | @@ -49,6 +49,7 @@ |
50 | 50 | $wgHooks['OldChangesListRecentChangesLine'][] = 'LqtDispatch::customizeOldChangesList'; |
51 | 51 | $wgHooks['SkinTemplateOutputPageBeforeExec'][] = 'LqtDispatch::setNewtalkHTML'; |
52 | 52 | $wgHooks['TitleGetRestrictions'][] = 'Thread::getRestrictionsForTitle'; |
| 53 | +$wgHooks['GetPreferences'][] = 'lqtGetPreferences'; |
53 | 54 | |
54 | 55 | // Special pages |
55 | 56 | $wgSpecialPages['DeleteThread'] = 'SpecialDeleteThread'; |
— | — | @@ -91,6 +92,9 @@ |
92 | 93 | $wgLogHeaders['liquidthreads'] = 'lqt-log-header'; |
93 | 94 | $wgLogActionsHandlers['liquidthreads/move'] = 'lqtFormatMoveLogEntry'; |
94 | 95 | |
| 96 | +// Preferences |
| 97 | +$wgDefaultUserOptions['lqtnotifytalk'] = true; |
| 98 | + |
95 | 99 | /** CONFIGURATION SECTION */ |
96 | 100 | |
97 | 101 | /* Number of days a thread needs to have existed to be considered for summarizing and archival */ |
— | — | @@ -105,3 +109,6 @@ |
106 | 110 | /* Allows switching LiquidThreads off for regular talk pages |
107 | 111 | (intended for testing and transition) */ |
108 | 112 | $wgLqtTalkPages = true; |
| 113 | + |
| 114 | +/* Whether or not to activate LiquidThreads email notifications */ |
| 115 | +$wgLqtEnotif = true; |
Index: trunk/extensions/LiquidThreads/classes/LqtThreads.php |
— | — | @@ -105,7 +105,7 @@ |
106 | 106 | |
107 | 107 | self::createTalkpageIfNeeded( $article ); |
108 | 108 | |
109 | | - NewMessages::writeMessageStateForUpdatedThread( $newthread ); |
| 109 | + NewMessages::writeMessageStateForUpdatedThread( $newthread, $change_type, $wgUser ); |
110 | 110 | |
111 | 111 | return $newthread; |
112 | 112 | } |
Index: trunk/extensions/LiquidThreads/classes/LqtView.php |
— | — | @@ -176,6 +176,18 @@ |
177 | 177 | if ( is_array( $query ) ) $query = self::queryStringFromArray( $query ); |
178 | 178 | return $thread->root()->getTitle()->getFullUrl( $query ); |
179 | 179 | } |
| 180 | + |
| 181 | + static function permalink( $thread, $text = null, $method = null, $operand = null, |
| 182 | + $sk = null, $attribs = array() ) { |
| 183 | + if ( is_null($sk) ) { |
| 184 | + global $wgUser; |
| 185 | + $sk = $wgUser->getSkin(); |
| 186 | + } |
| 187 | + |
| 188 | + list( $title, $query ) = self::permalinkData( $thread, $method, $operand ); |
| 189 | + |
| 190 | + return $sk->link( $title, $text, $attribs, $query ); |
| 191 | + } |
180 | 192 | |
181 | 193 | static function permalinkUrlWithDiff( $thread ) { |
182 | 194 | $changed_thread = $thread->changeObject(); |
Index: trunk/extensions/LiquidThreads/classes/LqtNewMessages.php |
— | — | @@ -40,40 +40,167 @@ |
41 | 41 | * Write a user_message_state for each user who is watching the thread. |
42 | 42 | * If the thread is on a user's talkpage, set that user's newtalk. |
43 | 43 | */ |
44 | | - static function writeMessageStateForUpdatedThread( $t ) { |
| 44 | + static function writeMessageStateForUpdatedThread( $t, $type, $changeUser ) { |
45 | 45 | global $wgDBprefix, $wgUser; |
| 46 | + |
| 47 | + wfDebugLog( 'LiquidThreads', 'Doing notifications' ); |
46 | 48 | |
47 | 49 | if ( $t->article()->getTitle()->getNamespace() == NS_USER ) { |
48 | 50 | $name = $t->article()->getTitle()->getDBkey(); |
49 | 51 | list( $name ) = split( '/', $name ); // subpages |
50 | 52 | $user = User::newFromName( $name ); |
51 | | - if ( $user && $user->getID() != $wgUser->getID() ) { |
| 53 | + if ( $user && $user->getID() != $changeUser->getID() ) { |
52 | 54 | $user->setNewtalk( true ); |
53 | 55 | } |
54 | 56 | } |
55 | 57 | |
56 | 58 | $dbw =& wfGetDB( DB_MASTER ); |
57 | 59 | |
58 | | - $talkpage_t = $t->article()->getTitle(); |
| 60 | + $talkpage_t = $t->article()->getTitle()->getSubjectPage(); |
59 | 61 | $root_t = $t->root()->getTitle(); |
60 | 62 | |
61 | 63 | $q_talkpage_t = $dbw->addQuotes( $talkpage_t->getDBkey() ); |
62 | 64 | $q_root_t = $dbw->addQuotes( $root_t->getDBkey() ); |
63 | 65 | |
64 | 66 | // Select any applicable watchlist entries for the thread. |
65 | | - $where_clause = <<<SQL |
66 | | -( |
67 | | - (wl_namespace = {$talkpage_t->getNamespace()} and wl_title = $q_talkpage_t ) |
68 | | -or (wl_namespace = {$root_t->getNamespace()} and wl_title = $q_root_t ) |
69 | | -) |
70 | | -SQL; |
| 67 | + $talkpageWhere = array( 'wl_namespace' => $talkpage_t->getNamespace(), |
| 68 | + 'wl_title' => $talkpage_t->getDBkey() ); |
| 69 | + $rootWhere = array( 'wl_namespace' => $root_t->getNamespace(), |
| 70 | + 'wl_title' => $root_t->getDBkey() ); |
| 71 | + |
| 72 | + $talkpageWhere = $dbw->makeList( $talkpageWhere, LIST_AND ); |
| 73 | + $rootWhere = $dbw->makeList( $rootWhere, LIST_AND ); |
| 74 | + |
| 75 | + $where_clause = $dbw->makeList( array( $talkpageWhere, $rootWhere ), LIST_OR ); |
71 | 76 | |
72 | 77 | // it sucks to not have 'on duplicate key update'. first update users who already have a ums for this thread |
73 | 78 | // and who have already read it, by setting their state to unread. |
74 | | - $dbw->query( "update {$wgDBprefix}user_message_state, {$wgDBprefix}watchlist set ums_read_timestamp = null where ums_user = wl_user and ums_thread = {$t->id()} and $where_clause" ); |
75 | | - |
76 | | - $dbw->query( "insert ignore into {$wgDBprefix}user_message_state (ums_user, ums_thread) select user_id, {$t->id()} from {$wgDBprefix}user, {$wgDBprefix}watchlist where user_id = wl_user and $where_clause;" ); |
| 79 | + |
| 80 | + // Pull users to update the message state for, including whether or not a |
| 81 | + // user_message_state row exists for them, and whether or not to send an email |
| 82 | + // notification. |
| 83 | + $dbr = wfGetDB( DB_SLAVE ); |
| 84 | + $res = $dbr->select( array( 'watchlist', 'user_message_state', 'user_properties' ), |
| 85 | + array( 'wl_user', 'ums_user', 'ums_read_timestamp', 'up_value' ), |
| 86 | + $where_clause, __METHOD__, array(), |
| 87 | + array( 'user_message_state' => |
| 88 | + array( 'left join', array( 'ums_user=wl_user', |
| 89 | + 'ums_thread' => $t->id() ) ), |
| 90 | + 'user_properties' => array( |
| 91 | + 'left join', |
| 92 | + array( 'up_user=wl_user', |
| 93 | + 'up_property' => 'lqtnotifytalk', |
| 94 | + ) |
| 95 | + ), |
| 96 | + ) |
| 97 | + ); |
| 98 | + |
| 99 | + $insert_rows = array(); |
| 100 | + $update_tuples = array(); |
| 101 | + $notify_users = array(); |
| 102 | + while( $row = $dbr->fetchObject( $res ) ) { |
| 103 | + // Don't notify yourself |
| 104 | + if ( $changeUser->getId() == $row->wl_user ) |
| 105 | + continue; |
| 106 | + |
| 107 | + if ( $row->ums_read_timestamp ) { |
| 108 | + $conds = array( 'ums_user' => $row->ums_user, |
| 109 | + 'ums_thread' => $t->id() ); |
| 110 | + $update_tuples[$row->ums_user.$t->id()] = $dbw->makeList( $conds, LIST_AND ); |
| 111 | + } elseif ( $row->ums_user ) { |
| 112 | + // It's already positive. |
| 113 | + } else { |
| 114 | + $insert_rows[] = |
| 115 | + array( |
| 116 | + 'ums_user' => $row->wl_user, |
| 117 | + 'ums_thread' => $t->id(), |
| 118 | + ); |
| 119 | + } |
| 120 | + |
| 121 | + if ( ( is_null($row->up_value) && User::getDefaultOption( 'lqtnotifytalk' ) ) |
| 122 | + || $row->up_value ) { |
| 123 | + $notify_users[] = $row->wl_user; |
| 124 | + } |
| 125 | + } |
| 126 | + |
| 127 | + // Avoids duplicates |
| 128 | + $update_tuples = array_values( $update_tuples ); |
| 129 | + |
| 130 | + // Do the actual updates |
| 131 | + if ( count($insert_rows) ) { |
| 132 | + $dbw->insert( 'user_message_state', $insert_rows, __METHOD__, array( 'IGNORE' ) ); |
| 133 | + } |
| 134 | + if ( count($update_tuples) ) { |
| 135 | + $where = $dbw->makeList( $update_tuples, LIST_OR ); |
| 136 | + |
| 137 | + $dbw->update( 'user_message_state', array( 'ums_read_timestamp' => null ), |
| 138 | + array($where), __METHOD__ ); |
| 139 | + } |
| 140 | + |
| 141 | + if ( count($notify_users) ) { |
| 142 | + self::notifyUsersByMail( $t, $notify_users, wfTimestampNow(), $type ); |
| 143 | + } |
77 | 144 | } |
| 145 | + |
| 146 | + static function notifyUsersByMail( $t, $watching_users, $timestamp, $type ) { |
| 147 | + wfLoadExtensionMessages( 'LiquidThreads' ); |
| 148 | + $messages = array( |
| 149 | + Threads::CHANGE_REPLY_CREATED => 'lqt-enotif-reply', |
| 150 | + Threads::CHANGE_NEW_THREAD => 'lqt-enotif-newthread', |
| 151 | + ); |
| 152 | + $subjects = array( |
| 153 | + Threads::CHANGE_REPLY_CREATED => 'lqt-enotif-subject-reply', |
| 154 | + Threads::CHANGE_NEW_THREAD => 'lqt-enotif-subject-newthread', |
| 155 | + ); |
| 156 | + |
| 157 | + if ( !isset($messages[$type]) || !isset($subjects[$type]) ) { |
| 158 | + wfDebugLog( 'LiquidThreads', "Email notification failed: type $type unrecognised" ); |
| 159 | + return; |
| 160 | + } else { |
| 161 | + $msgName = $messages[$type]; |
| 162 | + $subjectMsg = $subjects[$type]; |
| 163 | + } |
| 164 | + |
| 165 | + // Send email notification, fetching all the data in one go |
| 166 | + $dbr = wfGetDB( DB_SLAVE ); |
| 167 | + $res = $dbr->select( array( 'user', 'user_properties' ), '*', |
| 168 | + array( 'user_id' => $watching_users ), __METHOD__, array(), |
| 169 | + array( 'user_properties' => |
| 170 | + array( 'left join', |
| 171 | + array( |
| 172 | + 'up_user=user_id', |
| 173 | + 'up_property' => 'timecorrection' |
| 174 | + ) |
| 175 | + ) |
| 176 | + ) |
| 177 | + ); |
| 178 | + |
| 179 | + while( $row = $dbr->fetchObject( $res ) ) { |
| 180 | + $u = User::newFromRow( $row ); |
| 181 | + |
| 182 | + global $wgLang; |
| 183 | + |
| 184 | + $permalink = LqtView::permalinkUrl( $t ); |
| 185 | + |
| 186 | + // Adjust with time correction |
| 187 | + $adjustedTimestamp = $wgLang->userAdjust( $timestamp, $row->up_value ); |
| 188 | + |
| 189 | + $date = $wgLang->date( $adjustedTimestamp ); |
| 190 | + $time = $wgLang->time( $adjustedTimestamp ); |
| 191 | + |
| 192 | + $talkPage = $t->article()->getTitle()->getPrefixedText(); |
| 193 | + $msg = wfMsg( $msgName, $u->getName(), $t->subjectWithoutIncrement(), |
| 194 | + $date, $time, $talkPage, $permalink ); |
| 195 | + |
| 196 | + global $wgPasswordSender; |
| 197 | + |
| 198 | + $from = new MailAddress( $wgPasswordSender, 'WikiAdmin' ); |
| 199 | + $to = new MailAddress( $u ); |
| 200 | + $subject = wfMsg( $subjectMsg, $t->subjectWithoutIncrement() ); |
| 201 | + |
| 202 | + UserMailer::send( $to, $from, $subject, $msg ); |
| 203 | + } |
| 204 | + } |
78 | 205 | |
79 | 206 | static function newUserMessages( $user ) { |
80 | 207 | global $wgDBprefix; |
Index: trunk/extensions/LiquidThreads/classes/LqtThread.php |
— | — | @@ -160,7 +160,7 @@ |
161 | 161 | __METHOD__ ); |
162 | 162 | |
163 | 163 | if ( $change_type == Threads::CHANGE_EDITED_ROOT ) { |
164 | | - NewMessages::writeMessageStateForUpdatedThread( $this ); |
| 164 | + NewMessages::writeMessageStateForUpdatedThread( $this, $change_type, $wgUser ); |
165 | 165 | } |
166 | 166 | } |
167 | 167 | |
Index: trunk/extensions/LiquidThreads/Lqt.i18n.php |
— | — | @@ -151,6 +151,23 @@ |
152 | 152 | 'lqt-log-name' => 'Threaded discussion log', |
153 | 153 | 'lqt-log-header' => 'This log details actions taken on discussion threads.', |
154 | 154 | 'lqt-log-action-move' => 'moved [[$1]] from [[$2]] to [[$3]].', |
| 155 | + |
| 156 | + // Preferences |
| 157 | + 'lqt-preference-notify-talk' => 'Email me when somebody replies to a thread I am watching', |
| 158 | + |
| 159 | + // Email notification |
| 160 | + 'lqt-enotif-subject-reply' => '{{SITENAME}} discussion - Reply: $1', |
| 161 | + 'lqt-enotif-subject-newthread' => '{{SITENAME}} discussion - New thread: $1', |
| 162 | + 'lqt-enotif-newthread' => "Hi $1, |
| 163 | +This is a notification from {{SITENAME}} that a new thread on $5, '$2', |
| 164 | +was created on $3 at $4. |
| 165 | + |
| 166 | +You can see it at <$6>", |
| 167 | + 'lqt-enotif-reply' => "Hi $1, |
| 168 | +This is a notification from {{SITENAME}} that a new reply to '$2' on $5, |
| 169 | +was created on $3 at $4. |
| 170 | + |
| 171 | +You can see it at <$6>", |
155 | 172 | ); |
156 | 173 | |
157 | 174 | /** Message documentation (Message documentation) |