r58628 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r58627‎ | r58628 | r58629 >
Date:09:41, 6 November 2009
Author:werdna
Status:deferred
Tags:
Comment:
LiquidThreads: implement protection of threads and discussion pages from new posts and new replies
Modified paths:
  • /trunk/extensions/LiquidThreads/LiquidThreads.php (modified) (history)
  • /trunk/extensions/LiquidThreads/api/ApiThreadAction.php (modified) (history)
  • /trunk/extensions/LiquidThreads/classes/Hooks.php (modified) (history)
  • /trunk/extensions/LiquidThreads/classes/Thread.php (modified) (history)
  • /trunk/extensions/LiquidThreads/classes/Threads.php (modified) (history)
  • /trunk/extensions/LiquidThreads/classes/View.php (modified) (history)
  • /trunk/extensions/LiquidThreads/i18n/Lqt.i18n.php (modified) (history)
  • /trunk/extensions/LiquidThreads/pages/TalkpageView.php (modified) (history)

Diff [purge]

Index: trunk/extensions/LiquidThreads/i18n/Lqt.i18n.php
@@ -274,6 +274,16 @@
275275 'lqt-talkpage-history-title' => 'Discussion page history',
276276 'lqt-talkpage-history-subtitle' => 'For $1',
277277 'lqt-talkpage-history-tab' => 'Header',
 278+
 279+ // Protection
 280+ 'restriction-reply' => 'Post replies',
 281+ 'restriction-newthread' => 'Post new threads',
 282+ 'lqt-protected-reply-thread' => 'You cannot post in this thread because it has been '.
 283+ 'protected from new posts.',
 284+ 'lqt-protected-reply-talkpage' => 'You cannot post in this thread because this '.
 285+ 'discussion page has been protected from replies to its threads.',
 286+ 'lqt-protected-newthread' => 'You cannot post new threads to this discussion '.
 287+ 'page because it has been protected from new threads.',
278288 );
279289
280290 /** Message documentation (Message documentation)
Index: trunk/extensions/LiquidThreads/LiquidThreads.php
@@ -24,10 +24,10 @@
2525 define( 'LQT_OLDEST_THREADS', 'ot' );
2626
2727 // FIXME: would be neat if it was possible to somehow localise this.
28 -$wgCanonicalNamespaceNames[NS_LQT_THREAD] = 'Thread';
29 -$wgCanonicalNamespaceNames[NS_LQT_THREAD_TALK] = 'Thread_talk';
30 -$wgCanonicalNamespaceNames[NS_LQT_SUMMARY] = 'Summary';
31 -$wgCanonicalNamespaceNames[NS_LQT_SUMMARY_TALK] = 'Summary_talk';
 28+$wgCanonicalNamespaceNames[NS_LQT_THREAD] = 'Thread';
 29+$wgCanonicalNamespaceNames[NS_LQT_THREAD_TALK] = 'Thread_talk';
 30+$wgCanonicalNamespaceNames[NS_LQT_SUMMARY] = 'Summary';
 31+$wgCanonicalNamespaceNames[NS_LQT_SUMMARY_TALK] = 'Summary_talk';
3232
3333 // FIXME: would be neat if it was possible to somehow localise this.
3434 $wgExtraNamespaces[NS_LQT_THREAD] = 'Thread';
@@ -94,6 +94,9 @@
9595 // Blocking
9696 $wgHooks['UserIsBlockedFrom'][] = 'LqtHooks::userIsBlockedFrom';
9797
 98+// Protection
 99+$wgHooks['ProtectionFormGetApplicableTypes'][] = 'LqtHooks::getProtectionTypes';
 100+
98101 // Special pages
99102 $wgSpecialPages['MoveThread'] = 'SpecialMoveThread';
100103 $wgSpecialPages['NewMessages'] = 'SpecialNewMessages';
Index: trunk/extensions/LiquidThreads/classes/Threads.php
@@ -74,22 +74,31 @@
7575 }
7676 }
7777
78 - static function loadFromResult( $res, $db ) {
 78+ static function loadFromResult( $res, $db, $bulkLoad ) {
7979 $rows = array();
 80+ $threads = array();
8081
8182 while ( $row = $db->fetchObject( $res ) ) {
8283 $rows[] = $row;
 84+
 85+ if (!$bulkLoad) {
 86+ $threads[$row->thread_id] = Thread::newFromRow( $row );
 87+ }
8388 }
8489
 90+ if (!$bulkLoad) {
 91+ return $threads;
 92+ }
 93+
8594 return Thread::bulkLoad( $rows );
8695 }
8796
88 - static function where( $where, $options = array() ) {
 97+ static function where( $where, $options = array(), $bulkLoad = true ) {
8998 global $wgDBprefix;
9099 $dbr = wfGetDB( DB_SLAVE );
91100
92101 $res = $dbr->select( 'thread', '*', $where, __METHOD__, $options );
93 - $threads = Threads::loadFromResult( $res, $dbr );
 102+ $threads = Threads::loadFromResult( $res, $dbr, $bulkLoad );
94103
95104 foreach ( $threads as $thread ) {
96105 if ( $thread->root() ) {
@@ -115,7 +124,7 @@
116125 }
117126 }
118127
119 - static function withRoot( $post ) {
 128+ static function withRoot( $post, $bulkLoad = true ) {
120129 if ( $post->getTitle()->getNamespace() != NS_LQT_THREAD ) {
121130 // No articles outside the thread namespace have threads associated with them;
122131 return null;
@@ -125,23 +134,25 @@
126135 return self::$cache_by_root[$post->getID()];
127136 }
128137
129 - $ts = Threads::where( array( 'thread_root' => $post->getID() ) );
 138+ $ts = Threads::where( array( 'thread_root' => $post->getID() ), array(),
 139+ $bulkLoad );
130140
131141 return self::assertSingularity( $ts, 'thread_root', $post->getID() );
132142 }
133143
134 - static function withId( $id ) {
 144+ static function withId( $id, $bulkLoad = true ) {
135145 if ( array_key_exists( $id, self::$cache_by_id ) ) {
136146 return self::$cache_by_id[$id];
137147 }
138148
139 - $ts = Threads::where( array( 'thread_id' => $id ) );
 149+ $ts = Threads::where( array( 'thread_id' => $id ), array(), $bulkLoad );
140150
141151 return self::assertSingularity( $ts, 'thread_id', $id );
142152 }
143153
144 - static function withSummary( $article ) {
145 - $ts = Threads::where( array( 'thread_summary_page' => $article->getId() ) );
 154+ static function withSummary( $article, $bulkLoad = true ) {
 155+ $ts = Threads::where( array( 'thread_summary_page' => $article->getId() ),
 156+ array(), $bulkLoad);
146157 return self::assertSingularity( $ts, 'thread_summary_page', $article->getId() );
147158 }
148159
Index: trunk/extensions/LiquidThreads/classes/View.php
@@ -334,6 +334,21 @@
335335 can temporarily use a random scratch title. It's fine if the title changes
336336 throughout the edit cycle, since the article doesn't exist yet anyways.
337337 */
 338+
 339+ // Check permissions
 340+ if ( $edit_type == 'new' ) {
 341+ if ( Thread::canUserPost( $this->user, $this->article ) !== true ) {
 342+ $this->output->addWikiMsg( 'lqt-protected-newthread' );
 343+ return;
 344+ }
 345+ } elseif ( $edit_type == 'reply' ) {
 346+ $perm_result = $edit_applies_to->canUserReply( $this->user );
 347+ if ( $perm_result !== true ) {
 348+ $msg = "lqt-protected-reply-$perm_result";
 349+ $this->output->addWikiMsg( $msg );
 350+ return;
 351+ }
 352+ }
338353
339354 // Check if we actually want a subject, pull the submitted subject, and validate it.
340355 $subject_expected = ( $edit_type == 'new' || $thread && $thread->isTopmostThread() );
@@ -678,15 +693,17 @@
679694 'enabled' => true, 'tooltip' => $label );
680695 }
681696
682 - $commands['reply'] = array(
683 - 'label' => wfMsgExt( 'lqt_reply', 'parseinline' ),
684 - 'href' => $this->talkpageUrl( $this->title, 'reply', $thread,
685 - true /* include fragment */, $this->request ),
686 - 'enabled' => true,
687 - 'icon' => 'reply.png',
688 - 'showlabel' => 1,
689 - 'tooltip' => wfMsg( 'lqt_reply' )
690 - );
 697+ if ( $thread->canUserReply( $this->user ) === true ) {
 698+ $commands['reply'] = array(
 699+ 'label' => wfMsgExt( 'lqt_reply', 'parseinline' ),
 700+ 'href' => $this->talkpageUrl( $this->title, 'reply', $thread,
 701+ true /* include fragment */, $this->request ),
 702+ 'enabled' => true,
 703+ 'icon' => 'reply.png',
 704+ 'showlabel' => 1,
 705+ 'tooltip' => wfMsg( 'lqt_reply' )
 706+ );
 707+ }
691708
692709 $commands['link'] = array(
693710 'label' => wfMsgExt( 'lqt_permalink', 'parseinline' ),
@@ -725,6 +742,22 @@
726743 'href' => $move_href,
727744 'enabled' => true );
728745 }
 746+
 747+ if ( $this->user->isAllowed( 'protect' ) ) {
 748+ $protect_href = $thread->title()->getFullURL( 'action=protect' );
 749+
 750+ // Check if it's already protected
 751+ if ( !$thread->title()->isProtected() ) {
 752+ $label = wfMsg( 'protect' );
 753+ } else {
 754+ $label = wfMsg( 'unprotect' );
 755+ }
 756+
 757+ $commands['protect'] = array( 'label' => $label,
 758+ 'href' => $protect_href,
 759+ 'enabled' => true, );
 760+ }
 761+
729762 if ( !$this->user->isAnon() && !$thread->title()->userIsWatching() ) {
730763 $commands['watch'] = array( 'label' => wfMsg( 'watch' ),
731764 'href' => self::permalinkUrlWithQuery( $thread, 'action=watch' ),
Index: trunk/extensions/LiquidThreads/classes/Hooks.php
@@ -471,4 +471,29 @@
472472
473473 return true;
474474 }
 475+
 476+ static function getProtectionTypes( $title, &$types ) {
 477+ $isLqtPage = LqtDispatch::isLqtPage( $title );
 478+ $isThread = $title->getNamespace() == NS_LQT_THREAD;
 479+
 480+ // Bulk load is a no-no, causes infinite recursion.
 481+ $thread = Threads::withRoot( new Article( $title ), false );
 482+
 483+ $isThread = $isThread && $thread && $thread->isTopmostThread();
 484+
 485+ if ( !$isLqtPage && !$isThread ) {
 486+ return true;
 487+ }
 488+
 489+ if ( $isLqtPage ) {
 490+ $types[] = 'newthread';
 491+ $types[] = 'reply';
 492+ }
 493+
 494+ if ( $isThread ) {
 495+ $types[] = 'reply';
 496+ }
 497+
 498+ return true;
 499+ }
475500 }
Index: trunk/extensions/LiquidThreads/classes/Thread.php
@@ -1385,4 +1385,36 @@
13861386
13871387 return $ok;
13881388 }
 1389+
 1390+ /* N.B. Returns true, or a string with either thread or talkpage, noting which is
 1391+ protected */
 1392+ public function canUserReply( $user ) {
 1393+ $threadRestrictions = $this->topmostThread()->title()->getRestrictions('reply');
 1394+ $talkpageRestrictions = $this->article()->getTitle()->getRestrictions('reply');
 1395+
 1396+ $threadRestrictions = array_fill_keys( $threadRestrictions, 'thread' );
 1397+ $talkpageRestrictions = array_fill_keys( $talkpageRestrictions, 'talkpage' );
 1398+
 1399+ $restrictions = array_merge( $threadRestrictions, $talkpageRestrictions );
 1400+
 1401+ foreach( $restrictions as $right => $source ) {
 1402+ if ( !$user->isAllowed( $right ) ) {
 1403+ return $source;
 1404+ }
 1405+ }
 1406+
 1407+ return true;
 1408+ }
 1409+
 1410+ public static function canUserPost( $user, $talkpage ) {
 1411+ $restrictions = $talkpage->getTitle()->getRestrictions('newthread');
 1412+
 1413+ foreach( $restrictions as $right ) {
 1414+ if ( !$user->isAllowed( $right ) ) {
 1415+ return false;
 1416+ }
 1417+ }
 1418+
 1419+ return true;
 1420+ }
13891421 }
Index: trunk/extensions/LiquidThreads/pages/TalkpageView.php
@@ -228,14 +228,18 @@
229229
230230 $talkpageHeader = '';
231231
232 - $newThreadText = wfMsgExt( 'lqt_new_thread', 'parseinline' );
233 - $newThreadLink = $sk->link( $this->title, $newThreadText,
234 - array( ),
235 - array( 'lqt_method' => 'talkpage_new_thread' ),
236 - array( 'known' ) );
237 -
238 - $talkpageHeader .= Xml::tags( 'strong', array( 'class' => 'lqt_start_discussion' ),
239 - $newThreadLink );
 232+ if ( Thread::canUserPost( $this->user, $this->article ) ) {
 233+ $newThreadText = wfMsgExt( 'lqt_new_thread', 'parseinline' );
 234+ $newThreadLink = $sk->link( $this->title, $newThreadText,
 235+ array( ),
 236+ array( 'lqt_method' => 'talkpage_new_thread' ),
 237+ array( 'known' ) );
 238+
 239+ $talkpageHeader .= Xml::tags( 'strong',
 240+ array( 'class' => 'lqt_start_discussion' ),
 241+ $newThreadLink );
 242+ }
 243+
240244 $talkpageHeader .= $this->getSearchBox();
241245 $talkpageHeader .= $this->showTalkpageViewOptions( $article );
242246 $talkpageHeader = Xml::tags( 'div', array( 'class' => 'lqt-talkpage-header' ),
Index: trunk/extensions/LiquidThreads/api/ApiThreadAction.php
@@ -277,6 +277,12 @@
278278 }
279279 $talkpage = new Article( $talkpageTitle );
280280
 281+ // Check if we can post.
 282+ if ( Thread::canUserPost( $wgUser, $talkpage ) !== true ) {
 283+ $this->dieUsage( 'You cannot post to the specified talkpage, '.
 284+ 'because it is protected from new posts', 'talkpage-protected' );
 285+ return;
 286+ }
281287
282288 // Validate subject, generate a title
283289 if ( empty( $params['subject'] ) ) {
@@ -384,6 +390,15 @@
385391 }
386392 $replyTo = array_pop( $threads );
387393
 394+ // Check if we can reply to that thread.
 395+ $perm_result = $replyTo->canUserReply( $wgUser );
 396+ if ( $perm_result !== true ) {
 397+ $this->dieUsage( "You cannot reply to this thread, because the ".
 398+ $perm_result." is protected from replies.",
 399+ $perm_result.'-protected' );
 400+ return;
 401+ }
 402+
388403 // Validate text parameter
389404 if ( empty( $params['text'] ) ) {
390405 $this->dieUsage( 'You must include text in your post', 'no-text' );

Follow-up revisions

RevisionCommit summaryAuthorDate
r58816Follow-up to r58633 and r58628 (lqt): Rename Title::getProtectionTypes to Tit...btongminh12:05, 9 November 2009

Status & tagging log