EBernhardson has uploaded a new change for review.
https://gerrit.wikimedia.org/r/91135
Change subject: [WIP] Moderate a post or topic
......................................................................
[WIP] Moderate a post or topic
Adds actions to moderate topics and attempts to fix prior issues
with application of delete and censor. Still needs some work in
regards to historical posts.
Change-Id: I10cae63c2491c9f467968d6f8d604ea7f743ea96
---
M Flow.i18n.php
M includes/Block/Topic.php
M includes/Model/AbstractRevision.php
M includes/Model/PostRevision.php
M includes/Templating.php
M includes/View/PostActionMenu.php
M modules/discussion/styles/topic.less
A templates/bak
M templates/topic.html.php
9 files changed, 556 insertions(+), 124 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Flow
refs/changes/35/91135/1
diff --git a/Flow.i18n.php b/Flow.i18n.php
index c68f915..6059643 100644
--- a/Flow.i18n.php
+++ b/Flow.i18n.php
@@ -27,6 +27,9 @@
'flow-post-censored-by' => '{{GENDER:$1|Censored}} by $1 $2',
'flow-post-actions' => 'actions',
'flow-topic-actions' => 'actions',
+ 'flow-topic-hidden-by' => 'This topic was hidden by $1',
+ 'flow-topic-deleted-by' => 'This topic was deleted by $1',
+ 'flow-topic-censored-by' => 'This topic was suppressed by $1',
'flow-cancel' => 'Cancel',
'flow-newtopic-title-placeholder' => 'Message subject',
@@ -59,6 +62,10 @@
'flow-topic-action-watchlist' => 'Watchlist',
'flow-topic-action-edit-title' => 'Edit title',
'flow-topic-action-history' => 'Topic history',
+ 'flow-topic-action-hide-topic' => 'Hide topic',
+ 'flow-topic-action-delete-topic' => 'Delete topic',
+ 'flow-topic-action-censor-topic' => 'Suppress topic',
+ 'flow-topic-action-restore-topic' => 'Restore topic',
'flow-error-http' => 'An error occurred while contacting the server.
Your post was not saved.', // Needs real copy
'flow-error-other' => 'An unexpected error occurred. Your post was not
saved.',
@@ -197,6 +204,12 @@
{{Identical|Action}}',
'flow-topic-actions' => 'Used as link text.
{{Identical|Action}}',
+ 'flow-topic-hidden-by' => 'Used as replacement topic title when hidden.
Parameters:
+* $1 - username of the user that hid the topic',
+ 'flow-topic-deleted-by' => 'Used as replacement topic title when
deleted. Parameters:
+* $1 - username of the user that deleted the topic',
+ 'flow-topic-censored-by' => 'Used as replacement topic title when
suppressed. Parameters:
+* $1 - username of the user that suppressed the topic',
'flow-cancel' => 'Used as action link text.
{{Identical|Cancel}}',
'flow-newtopic-title-placeholder' => 'Used as placeholder for the
"Subject/Title for topic" textarea.',
@@ -252,6 +265,10 @@
'flow-topic-action-edit-title' => 'Used as title for the link which is
used to edit the title.',
'flow-topic-action-history' => 'Used as text for the link which is used
to view topic-history.
{{Identical|Topic history}}',
+ 'flow-topic-action-hide-topic' => 'Used as a link in a dropdown menu to
hide a topic.',
+ 'flow-topic-action-delete-topic' => 'Used as a link in a dropdown menu
to delete a topic.',
+ 'flow-topic-action-censor-topic' => 'Used as a link in a dropdown menu
to suppress a topic.',
+ 'flow-topic-action-restore-topic' => 'Used as a link in a dropdown menu
to clear existing moderation.',
'flow-error-http' => 'Used as error message on HTTP error.',
'flow-error-other' => 'Used as generic error message.',
'flow-error-external' => 'Uses as error message. Parameters:
diff --git a/includes/Block/Topic.php b/includes/Block/Topic.php
index 6cec6dc..516b8dc 100644
--- a/includes/Block/Topic.php
+++ b/includes/Block/Topic.php
@@ -20,6 +20,7 @@
protected $topicTitle;
protected $rootLoader;
protected $newRevision;
+ protected $relatedRevisions = array();
protected $notification;
protected $requestedPost;
@@ -28,10 +29,12 @@
protected $supportedActions = array(
// Standard editing
'edit-post', 'reply',
- // Moderation
+ // Topic Moderation
+ 'hide-topic', 'delete-topic', 'censor-topic', 'restore-topic',
+ // Post Moderation
'hide-post', 'delete-post', 'censor-post', 'restore-post',
// Other stuff
- 'hide-topic', 'edit-title',
+ 'edit-title',
);
public function __construct( Workflow $workflow, ManagerGroup $storage,
NotificationController $notificationController, $root ) {
@@ -58,8 +61,19 @@
break;
case 'hide-topic':
- // this should be a workflow level action, not
implemented per-block
- $this->validateHideTopic();
+ $this->validateModerateTopic(
AbstractRevision::MODERATED_HIDDEN );
+ break;
+
+ case 'delete-topic':
+ $this->validateModerateTopic(
AbstractRevision::MODERATED_DELETED );
+ break;
+
+ case 'censor-topic':
+ $this->validateModerateTopic(
AbstractRevision::MODERATED_CENSORED );
+ break;
+
+ case 'restore-topic':
+ $this->validateRestorePost();
break;
case 'hide-post':
@@ -139,9 +153,27 @@
}
}
- protected function validateHideTopic() {
- if ( !$this->workflow->lock( $this->user ) ) {
- $this->errors['hide-topic'] = wfMessage(
'flow-error-hide-failure' );
+ protected function validateModerateTopic( $moderationState ) {
+ $topic = $this->loadTopicTitle();
+ $this->newRevision = $topic->moderate( $this->user,
$moderationState );
+ // The application of moderation to historical revisions is
pushed out of the
+ // model because the moderation is in AbstractRevision, but not
all revisions
+ // (ex: header) will want moderation to apply to historical
revisions.
+ if ( $this->newRevision &&
$this->newRevision->needsModerateHistorical() ) {
+ $this->relatedRevisions = $this->loadHistorical( $topic
);
+ foreach ( $this->relatedRevisions as $revision ) {
+ $revision->moderate( $this->user,
$moderationState, null, /* $createRevision = */ false );
+ }
+ } elseif ( !$this->newRevision ) {
+ $this->errors['moderate'] = wfMessage(
'flow-error-not-allowed' );
+ }
+ }
+
+ protected function validateRestoreTopic() {
+ $topic = $this->loadTopicTitle();
+ $this->newRevision = $topic->restore( $this->user );
+ if ( !$this->newRevision ) {
+ $this->errors['restore-topic'] = wfMessage(
'flow-error-not-allowed' );
}
}
@@ -213,6 +245,10 @@
switch( $this->action ) {
case 'reply':
+ case 'hide-topic':
+ case 'delete-topic':
+ case 'censor-topic':
+ case 'restore-topic':
case 'hide-post':
case 'delete-post':
case 'censor-post':
@@ -224,6 +260,10 @@
}
$this->storage->put( $this->newRevision );
$this->storage->put( $this->workflow );
+ // These are moderated historical revisions of
$this->newRevision
+ foreach ( $this->relatedRevisions as $revision ) {
+ $this->storage->put( $revision );
+ }
$self = $this;
$newRevision = $this->newRevision;
$rootPost = $this->loadRootPost();
@@ -557,6 +597,33 @@
return $this->requestedPost[$postId] ?: null;
}
+ protected function loadHistorical( PostRevision $post ) {
+ if ( $post->isFirstRevision() ) {
+ throw new \Exception( 'first?' );
+ return array();
+ }
+
+ $found = $this->storage->find(
+ 'PostRevision',
+ array( 'tree_rev_descendant_id' => $post->getPostId() ),
+ // TODO: should we really be reverting moderation state
for more than
+ // 50 revisions?
+ array( 'limit' => 50 )
+ );
+ if ( !$found ) {
+ throw new \Exception( 'should have found revisions' );
+ }
+ // We need to filter out $post
+ $revId = $post->getRevisionId();
+ foreach ( $found as $idx => $revision ) {
+ if ( $revId->equals( $revision->getRevisionId() ) ) {
+ unset( $found[$idx] );
+ break;
+ }
+ }
+ return $found;
+ }
+
// Somehow the template has to know which post the errors go with
public function getRepliedTo() {
return isset( $this->submitted['replyTo'] ) ?
$this->submitted['replyTo'] : null;
diff --git a/includes/Model/AbstractRevision.php
b/includes/Model/AbstractRevision.php
index 89776a5..c7ef83f 100644
--- a/includes/Model/AbstractRevision.php
+++ b/includes/Model/AbstractRevision.php
@@ -22,6 +22,8 @@
'perm' => null,
// i18n key to replace content with when state is
active(unused with perm === null )
'content' => null,
+ // This is the bit of text rendered instead of the
content when isTopicTitle returns true
+ 'topic' => null,
// This is the bit of text rendered instead of the post
creator
'usertext' => null,
// Whether or not to create a new revision when setting
this state
@@ -35,6 +37,8 @@
// i18n key to replace content with when state is active
// NOTE: special case self::getHiddenContent still
retrieves content in this case only
'content' => 'flow-post-hidden-by',
+ // This is the bit of text rendered instead of the
content when isTopicTitle returns true
+ 'topic' => 'flow-topic-hidden-by',
// This is the bit of text rendered instead of the post
creator
'usertext' => 'flow-rev-message-hid-post',
// Whether or not to create a new revision when setting
this state
@@ -47,6 +51,8 @@
'perm' => 'flow-delete',
// i18n key to replace content with when state is active
'content' => 'flow-post-deleted-by',
+ // This is the bit of text rendered instead of the
content when isTopicTitle returns true
+ 'topic' => 'flow-topic-deleted-by',
// This is the bit of text rendered instead of the post
creator
'usertext' => 'flow-rev-message-deleted-post',
// Whether or not to create a new revision when setting
this state
@@ -59,6 +65,8 @@
'perm' => 'flow-censor',
// i18n key to replace content with when state is active
'content' => 'flow-post-censored-by',
+ // This is the bit of text rendered instead of the
content when isTopicTitle returns true
+ 'topic' => 'flow-topic-censored-by',
// This is the bit of text rendered instead of the post
creator
'usertext' => 'flow-rev-message-censored-post',
// Whether or not to create a new revision when setting
this state
@@ -198,7 +206,11 @@
return $keys[max( $aPos, $bPos )];
}
- public function moderate( User $user, $state, $changeType = null ) {
+ /**
+ * $createRevision = false should only be used to apply a moderation
action
+ * to historical revisions, not general moderation.
+ */
+ public function moderate( User $user, $state, $changeType = null,
$createRevision = true ) {
if ( !isset( self::$perms[$state] ) ) {
wfDebugLog( __CLASS__, __FUNCTION__ . ': Provided
moderation state does not exist : ' . $state );
return null;
@@ -208,11 +220,14 @@
if ( !$this->isAllowed( $user, $mostRestrictive ) ) {
return null;
}
- // Censoring is special, other moderation types just create
- // a new revision but censoring adjusts the existing revision.
- // Yes this mucks with the history just being a revision list.
- if ( self::$perms[$state]['new-revision'] ) {
+
+ if ( $createRevision ) {
$obj = $this->newNullRevision( $user );
+ if ( $changeType === null && isset(
self::$perms[$state]['change-type'] ) ) {
+ $obj->changeType =
self::$perms[$state]['change-type'];
+ } else {
+ $obj->changeType = $changeType;
+ }
} else {
$obj = $this;
}
@@ -227,12 +242,17 @@
$obj->moderatedByUserText = $user->getName();
$obj->moderationTimestamp = wfTimestampNow();
}
- if ( $changeType === null && isset(
self::$perms[$state]['change-type'] ) ) {
- $obj->changeType = self::$perms[$state]['change-type'];
- } else {
- $obj->changeType = $changeType;
- }
+
return $obj;
+ }
+
+ public function needsModerateHistorical() {
+ $state = $this->moderationState;
+ if ( !isset( self::$perms[$state]['new-revision'] ) ) {
+ wfWarn( __CLASS__, __FUNCTION__ . ": Moderation state
does not exist : $state" );
+ return false;
+ }
+ return self::$perms[$state]['new-revision'];
}
public function restore( User $user ) {
@@ -277,17 +297,21 @@
if ( $this->isAllowed( $user ) ) {
return $this->getConvertedContent( $format );
} else {
- $moderatedAt = new MWTimestamp(
$this->moderationTimestamp );
-
- // Messages: flow-post-hidden-by, flow-post-deleted-by,
flow-post-censored-by
- return wfMessage(
- self::$perms[$this->moderationState]['content'],
- $this->moderatedByUserText,
- $moderatedAt->getHumanTimestamp()
- );
+ return $this->getModeratedContent();
}
}
+ protected function getModeratedContent() {
+ $moderatedAt = new MWTimestamp( $this->moderationTimestamp );
+
+ // Messages: flow-post-hidden-by, flow-post-deleted-by,
flow-post-censored-by
+ return wfMessage(
+ self::$perms[$this->moderationState]['content'],
+ $this->moderatedByUserText,
+ $moderatedAt->getHumanTimestamp()
+ );
+ }
+
public function getContentRaw() {
if ( $this->decompressedContent === null ) {
$this->decompressedContent =
\Revision::decompressRevisionText( $this->content, $this->flags );
diff --git a/includes/Model/PostRevision.php b/includes/Model/PostRevision.php
index 9f878c5..0ceec7a 100644
--- a/includes/Model/PostRevision.php
+++ b/includes/Model/PostRevision.php
@@ -88,7 +88,7 @@
/**
* Get the user ID of the user who created this post.
- * Checks permissions and returns false
+ * Checks permissions and returns false
*
* @param $user User The user to check permissions for.
* @return int|bool The user ID, or false
@@ -389,4 +389,15 @@
}
return $user->getId() == $this->getCreatorId() ||
$user->isAllowed( 'flow-edit-post' );
}
+
+ protected function getModeratedContent() {
+ if ( $this->isTopicTitle() ) {
+ return wfMessage(
+ self::$perms[$this->moderationState]['topic'],
+ $this->moderatedByUserText
+ );
+ } else {
+ return parent::getModeratedContent();
+ }
+ }
}
diff --git a/includes/Templating.php b/includes/Templating.php
index 250404c..76c1b5e 100644
--- a/includes/Templating.php
+++ b/includes/Templating.php
@@ -94,15 +94,7 @@
array(
'block' => $block,
'post' => $post,
- // An ideal world may pull this from the
container, but for now this is fine. This templating
- // class has too many responsibilities to keep
receiving all required objects in the constructor.
- 'postActionMenu' => new PostActionMenu(
- $this->urlGenerator,
- $wgUser,
- $block,
- $post,
- $wgUser->getEditToken( $wgFlowTokenSalt
)
- ),
+ 'postActionMenu' => $this->createActionMenu(
$post, $block ),
),
$return
);
@@ -113,7 +105,22 @@
'block' => $block,
'topic' => $block->getWorkflow(),
'root' => $root,
+ 'postActionMenu' => $this->createActionMenu( $root,
$block ),
), $return );
+ }
+
+ // An ideal world may pull this from the container, but for now this is
fine. This templating
+ // class has too many responsibilities to keep receiving all required
objects in the constructor.
+ protected function createActionMenu( PostRevision $post, Block $block )
{
+ global $wgUser, $wgFlowTokenSalt;
+
+ return new PostActionMenu(
+ $this->urlGenerator,
+ $wgUser,
+ $block,
+ $post,
+ $wgUser->getEditToken( $wgFlowTokenSalt )
+ );
}
public function getPagingLink( $block, $direction, $offset, $limit ) {
@@ -220,7 +227,7 @@
* Gets a Flow-formatted plaintext human-readable identifier for a user.
* Usually the user's name, but it can also return "an anonymous user",
* or information about an item's moderation state.
- *
+ *
* @param User $user The User object to get a
description for.
* @param AbstractRevision $rev An AbstractRevision object to
retrieve moderation state from.
* @param bool $showIPs Whether or not to show IP
addresses for anonymous users
diff --git a/includes/View/PostActionMenu.php b/includes/View/PostActionMenu.php
index 43d1ea9..9386f65 100644
--- a/includes/View/PostActionMenu.php
+++ b/includes/View/PostActionMenu.php
@@ -32,6 +32,37 @@
*/
protected function getActionDetails( $action ) {
$actions = array(
+ // Not sure about mixing topic's and post's, although
they are handled
+ // the same currently.
+ 'hide-topic' => array(
+ 'method' => 'POST',
+ 'permissions' => array(
+ PostRevision::MODERATED_NONE =>
'flow-hide',
+ PostRevision::MODERATED_HIDDEN =>
'flow-hide',
+ ),
+ ),
+ 'delete-topic' => array(
+ 'method' => 'POST',
+ 'permissions' => array(
+ PostRevision::MODERATED_NONE =>
'flow-delete',
+ PostRevision::MODERATED_HIDDEN =>
'flow-delete',
+ ),
+ ),
+ 'censor-topic' => array(
+ 'method' => 'POST',
+ 'permissions' => array(
+ PostRevision::MODERATED_NONE =>
'flow-censor',
+ PostRevision::MODERATED_HIDDEN =>
'flow-censor',
+ ),
+ ),
+ 'restore-topic' => array(
+ 'method' => 'POST',
+ 'permissions' => array(
+ PostRevision::MODERATED_HIDDEN =>
'flow-hide',
+ PostRevision::MODERATED_DELETED =>
array( 'flow-delete', 'flow-censor' ),
+ PostRevision::MODERATED_CENSORED =>
'flow-censor',
+ ),
+ ),
'hide-post' => array(
'method' => 'POST',
'permissions' => array(
diff --git a/modules/discussion/styles/topic.less
b/modules/discussion/styles/topic.less
index f307318..6fe85a5 100644
--- a/modules/discussion/styles/topic.less
+++ b/modules/discussion/styles/topic.less
@@ -4,6 +4,38 @@
.flow-topic-container {
padding-bottom: 54px;
+ &.flow-topic-moderated {
+ > .flow-post-container {
+ display: none
+ }
+ .flow-topic-posts-meta {
+ display: none
+ }
+ }
+
+ .flow-topic-moderated-hide,
+ .flow-topic-moderated-deleted,
+ .flow-topic-moderated-suppressed {
+ padding-left: 22px !important;
+
+ background-position: left;
+ background-size: 14px auto;
+ background-repeat: no-repeat;
+ }
+
+ .flow-topic-moderated-hide {
+
.background-image-svg('../../base/images/moderate_menu_hidden_normal.svg',
'../../base/images/moderate_menu_hidden_normal.png');
+ }
+
+ .flow-topic-moderated-deleted {
+ .background-image-svg('../../base/images/moderated_normal.svg',
'../../base/images/moderated_normal.png');
+ }
+
+ .flow-topic-moderated-censored {
+
.background-image-svg('../../base/images/suppressed_normal.svg',
'../../base/images/suppressed_normal.png');
+ }
+
+
.flow-titlebar {
position: relative;
@@ -22,6 +54,16 @@
&:hover,
&.mw-ui-hover {
background: @topic-titlebar-background-color;
+
+ .flow-topic-moderated-censored {
+
.background-image-svg('../../base/images/suppressed_hover.svg',
'../../base/images/suppressed_hover.png');
+ }
+ .flow-topic-moderated-deleted {
+
.background-image-svg('../../base/images/moderated_hover.svg',
'../../base/images/moderated_hover.png');
+ }
+ .flow-topic-moderated-hide {
+
.background-image-svg('../../base/images/moderate_menu_hidden_hover.svg',
'../../base/images/moderate_menu_hidden_hover.png');
+ }
}
.flow-topic-title {
diff --git a/templates/bak b/templates/bak
new file mode 100644
index 0000000..8a8089a
--- /dev/null
+++ b/templates/bak
@@ -0,0 +1,198 @@
+<?php
+
+// treat title like unparsed (wiki)text
+$title = $root->getContent( $user, 'wikitext' );
+
+// pre-register recursive callbacks; will then be fetched all at once when the
+// first one's result is requested
+$indexDescendantCount = $root->registerDescendantCount();
+$indexParticipants = $root->registerParticipants();
+
+echo Html::openElement( 'div', array(
+ 'class' => 'flow-topic-container flow-topic-full' .
$root->isModerated() ? ' flow-topic-moderated' : '',
+ 'id' => 'flow-topic-' . $topic->getId()->getHex(),
+ 'data-topic-id' => $topic->getId()->getHex(),
+ 'data-title' => $root->isModerated() ? '' : $title,
+) );
+?>
+<div class="flow-element-container">
+ <div class="flow-titlebar mw-ui-button">
+ <?php
+ echo Html::rawElement(
+ 'a',
+ array(
+ 'href' => $this->generateUrl(
$root->getPostId(), 'edit-title' ),
+ 'class' => 'flow-edit-topic-link flow-icon
flow-icon-top-aligned',
+ ),
+ wfMessage( 'flow-topic-action-edit-title' )
+ );
+ ?>
+
+ <div class="flow-topic-title">
+ <?php if ( $root->isModerated() ): ?>
+ <h2 class='flow-topic-moderated
flow-topic-moderated-<?php echo $root->getModerationState() ?>'>
+ <?php // passing null as user
(unprivileged) gets the hidden/deleted/suppressed text
+ echo htmlspecialchars(
$root->getContent( null ) ) ?>
+ </h2>
+ <?php else: ?>
+ <h2 class="flow-realtitle">
+ <?php echo htmlspecialchars( $title );
?>
+ </h2>
+ <?php endif ?>
+ </div>
+
+ <div class="flow-actions">
+ <a class="flow-actions-link" href="#"><?php echo
wfMessage( 'flow-topic-actions' )->escaped(); ?></a>
+ <div class="flow-actions-flyout">
+ <ul>
+ <?php if ( $postActionMenu->isAllowed(
'hide-topic' ) ) {
+ echo '<li
class="flow-action-hide">', $postActionMenu->getButton(
+ 'hide-topic',
+ wfMessage(
'flow-topic-action-hide-topic' )->plain(),
+ 'mw-ui-button'
+ ), '</li>';
+ } ?>
+ <?php if ( $postActionMenu->isAllowed(
'delete-topic' ) ) {
+ echo '<li
class="flow-action-delete">', $postActionMenu->getButton(
+ 'delete-topic',
+ wfMessage(
'flow-topic-action-delete-topic' )->plain(),
+ 'mw-ui-button'
+ ), '</li>';
+ } ?>
+ <?php if ( $postActionMenu->isAllowed(
'censor-topic' ) ) {
+ echo '<li
class="flow-action-censor">', $postActionMenu->getButton(
+ 'censor-topic',
+ wfMessage(
'flow-topic-action-censor-topic' )->plain(),
+ 'mw-ui-button'
+ ), '</li>';
+ } ?>
+ <?php if ( $postActionMenu->isAllowed(
'restore-topic' ) ) {
+ echo '<li
class="flow-action-restore">', $postActionMenu->getButton(
+ 'restore-topic',
+ wfMessage(
'flow-topic-action-restore-topic' )->plain(),
+ 'mw-ui-button'
+ ), '</li>';
+ } ?>
+ <li class="flow-action-close">
+ <a href="#"
class="mw-ui-button">@todo: Close topic</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+
+ <p class="flow-datestamp">
+ <?php
+ // timestamp html
+ $content = '
+ <span class="flow-agotime"
style="display: inline">'. $topic->getLastModifiedObj()->getHumanTimestamp()
.'</span>
+ <span class="flow-utctime"
style="display: none">'. $topic->getLastModifiedObj()->getTimestamp( TS_RFC2822
) .'</span>';
+
+ // build history button with timestamp html as
content
+ echo Html::rawElement( 'a',
+ array(
+ 'class' =>
'flow-action-history-link',
+ 'href' => $this->generateUrl(
$root->getPostId(), 'topic-history' ),
+ ),
+ $content
+ );
+ ?>
+ </p>
+
+ <?php if ( $root->isAllowed( $user ) ):
+ echo Html::element(
+ 'a',
+ array(
+ 'class' => 'flow-icon-permalink
flow-icon flow-icon-top-aligned',
+ 'title' => wfMessage(
'flow-topic-action-view' )->text(),
+ 'href' => $this->generateUrl( $topic ),
+ ),
+ wfMessage( 'flow-topic-action-view' )->text()
+ );
+ ?>
+
+ <ul class="flow-topic-posts-meta">
+ <li class="flow-topic-participants">
+ <?php echo $this->printParticipants(
$root, $indexParticipants ); ?>
+ </li>
+ <li class="flow-topic-comments">
+ <a href="#" class="flow-reply-link"
data-topic-id="<?php echo $topic->getId()->getHex() ?>">
+ <?php
+ // get total number of
posts in topic
+ $comments =
$root->getRecursiveResult( $indexDescendantCount );
+ echo wfMessage(
'flow-topic-comments', $comments )->text();
+ ?>
+ </a>
+ </li>
+ </ul>
+
+ <?php
+ // @todo: afaik, there's no watchlist
functionality yet; this blurb is just to position it correctly already
+
+ $watchlistActive = false; // @todo: true if
already watchlisted, false if not
+ echo Html::element(
+ 'a',
+ array(
+ 'class' => 'flow-icon-watchlist
flow-icon flow-icon-bottom-aligned'
+ . ( $watchlistActive ?
' flow-icon-watchlist-active' : '' ),
+ 'title' => wfMessage(
'flow-topic-action-watchlist' )->text(),
+ 'href' => '#',
+ 'onclick' => "alert( '@todo:
Not yet implemented!' ); return false;"
+ ),
+ wfMessage(
'flow-topic-action-watchlist' )->text()
+ );
+ ?>
+ <?php endif; /* !$root->isModerated() */ ?>
+ </div>
+</div>
+<?php if ( $root->isAllowed( $user ) ):
+ foreach( $root->getChildren() as $child ) {
+ echo $this->renderPost( $child, $block, $root );
+ }
+
+ // Topic reply box
+ echo Html::openElement( 'div', array(
+ 'class' => 'flow-topic-reply-container flow-post-container
flow-element-container',
+ 'data-post-id' => $root->getRevisionId()->getHex(),
+ 'id' => 'flow-topic-reply-' . $topic->getId()->getHex()
+ ) );
+ ?>
+ <span class="flow-creator">
+ <span class="flow-creator-simple" style="display:
inline">
+ <?php echo $this->getUserText( $user ); ?>
+ </span>
+ <span class="flow-creator-full" style="display: none">
+ <?php echo $this->userToolLinks(
$user->getId(), $user->getName() ); ?>
+ </span>
+ </span>
+ <?php
+ echo Html::openElement( 'form', array(
+ 'method' => 'POST',
+ 'action' => $this->generateUrl( $block->getWorkflow(),
'reply' ),
+ 'class' => 'flow-topic-reply-form',
+ ) ),
+ Html::element( 'input', array(
+ 'type' => 'hidden',
+ 'name' => $block->getName() . '[replyTo]',
+ 'value' => $topic->getId()->getHex(),
+ ) ),
+ Html::element( 'input', array(
+ 'type' => 'hidden',
+ 'name' => 'wpEditToken',
+ 'value' => $editToken,
+ ) ),
+ Html::textarea( $block->getName() . '[topic-reply-content]',
'', array(
+ 'placeholder' => wfMessage(
'flow-reply-topic-placeholder', $user->getName(), $title )->text(),
+ 'class' => 'flow-input mw-ui-input
flow-topic-reply-content',
+ ) ),
+ Html::openElement( 'div', array( 'class' =>
'flow-post-form-controls' ) ),
+ Html::element( 'input', array(
+ 'type' => 'submit',
+ 'value' => wfMessage( 'flow-reply-submit',
$this->getUserText( $root->getCreator( $user ), $root ) )->text(),
+ 'class' => 'mw-ui-button mw-ui-constructive
flow-topic-reply-submit',
+ ) ),
+ Html::closeElement( 'div' ),
+ Html::closeElement( 'form' ),
+ Html::closeElement( 'div' );
+ ?>
+ </div>
+<?php endif /* !$root->isModerated() */ ?>
diff --git a/templates/topic.html.php b/templates/topic.html.php
index 0adade3..67bbc84 100644
--- a/templates/topic.html.php
+++ b/templates/topic.html.php
@@ -9,10 +9,10 @@
$indexParticipants = $root->registerParticipants();
echo Html::openElement( 'div', array(
- 'class' => 'flow-topic-container flow-topic-full',
+ 'class' => 'flow-topic-container flow-topic-full' . (
$root->isModerated() ? ' flow-topic-moderated' : '' ),
'id' => 'flow-topic-' . $topic->getId()->getHex(),
'data-topic-id' => $topic->getId()->getHex(),
- 'data-title' => $title,
+ 'data-title' => $root->isModerated() ? '' : $title,
) );
?>
<div class="flow-element-container">
@@ -29,17 +29,50 @@
?>
<div class="flow-topic-title">
- <h2 class="flow-realtitle">
- <?php echo htmlspecialchars( $title ); ?>
- </h2>
+ <?php if ( $root->isModerated() ): ?>
+ <h2 class='flow-topic-moderated
flow-topic-moderated-<?php echo $root->getModerationState() ?>'>
+ <?php // passing null as user
(unprivileged) gets the hidden/deleted/suppressed text
+ echo htmlspecialchars(
$root->getContent( null ) ) ?>
+ </h2>
+ <?php else: ?>
+ <h2 class="flow-realtitle">
+ <?php echo htmlspecialchars( $title );
?>
+ </h2>
+ <?php endif ?>
</div>
+
<div class="flow-actions">
<a class="flow-actions-link" href="#"><?php echo
wfMessage( 'flow-topic-actions' )->escaped(); ?></a>
<div class="flow-actions-flyout">
<ul>
- <li class="flow-action-hide">
- <a href="#" class="mw-ui-button
mw-ui-destructive">@todo: Hide topic</a>
- </li>
+ <?php if ( $postActionMenu->isAllowed(
'hide-topic' ) ) {
+ echo '<li
class="flow-action-hide">', $postActionMenu->getButton(
+ 'hide-topic',
+ wfMessage(
'flow-topic-action-hide-topic' )->plain(),
+ 'mw-ui-button'
+ ), '</li>';
+ } ?>
+ <?php if ( $postActionMenu->isAllowed(
'delete-topic' ) ) {
+ echo '<li
class="flow-action-delete">', $postActionMenu->getButton(
+ 'delete-topic',
+ wfMessage(
'flow-topic-action-delete-topic' )->plain(),
+ 'mw-ui-button'
+ ), '</li>';
+ } ?>
+ <?php if ( $postActionMenu->isAllowed(
'censor-topic' ) ) {
+ echo '<li
class="flow-action-censor">', $postActionMenu->getButton(
+ 'censor-topic',
+ wfMessage(
'flow-topic-action-censor-topic' )->plain(),
+ 'mw-ui-button'
+ ), '</li>';
+ } ?>
+ <?php if ( $postActionMenu->isAllowed(
'restore-topic' ) ) {
+ echo '<li
class="flow-action-restore">', $postActionMenu->getButton(
+ 'restore-topic',
+ wfMessage(
'flow-topic-action-restore-topic' )->plain(),
+ 'mw-ui-button'
+ ), '</li>';
+ } ?>
<li class="flow-action-close">
<a href="#"
class="mw-ui-button">@todo: Close topic</a>
</li>
@@ -65,7 +98,7 @@
?>
</p>
- <?php
+ <?php if ( $root->isAllowed( $user ) ):
echo Html::element(
'a',
array(
@@ -75,89 +108,91 @@
),
wfMessage( 'flow-topic-action-view' )->text()
);
- ?>
+ ?>
- <ul class="flow-topic-posts-meta">
- <li class="flow-topic-participants">
- <?php echo $this->printParticipants( $root,
$indexParticipants ); ?>
- </li>
- <li class="flow-topic-comments">
- <a href="#" class="flow-reply-link"
data-topic-id="<?php echo $topic->getId()->getHex() ?>">
- <?php
- // get total number of posts in
topic
- $comments =
$root->getRecursiveResult( $indexDescendantCount );
- echo wfMessage(
'flow-topic-comments', $comments )->text();
- ?>
- </a>
- </li>
- </ul>
+ <ul class="flow-topic-posts-meta">
+ <li class="flow-topic-participants">
+ <?php echo $this->printParticipants(
$root, $indexParticipants ); ?>
+ </li>
+ <li class="flow-topic-comments">
+ <a href="#" class="flow-reply-link"
data-topic-id="<?php echo $topic->getId()->getHex() ?>">
+ <?php
+ // get total number of
posts in topic
+ $comments =
$root->getRecursiveResult( $indexDescendantCount );
+ echo wfMessage(
'flow-topic-comments', $comments )->text();
+ ?>
+ </a>
+ </li>
+ </ul>
- <?php
- // @todo: afaik, there's no watchlist functionality
yet; this blurb is just to position it correctly already
+ <?php
+ // @todo: afaik, there's no watchlist
functionality yet; this blurb is just to position it correctly already
- $watchlistActive = false; // @todo: true if already
watchlisted, false if not
- echo Html::element(
- 'a',
- array(
- 'class' => 'flow-icon-watchlist
flow-icon flow-icon-bottom-aligned'
- . ( $watchlistActive ? '
flow-icon-watchlist-active' : '' ),
- 'title' => wfMessage(
'flow-topic-action-watchlist' )->text(),
- 'href' => '#',
- 'onclick' => "alert( '@todo: Not yet
implemented!' ); return false;"
- ),
- wfMessage( 'flow-topic-action-watchlist'
)->text()
- );
- ?>
+ $watchlistActive = false; // @todo: true if
already watchlisted, false if not
+ echo Html::element(
+ 'a',
+ array(
+ 'class' => 'flow-icon-watchlist
flow-icon flow-icon-bottom-aligned'
+ . ( $watchlistActive ?
' flow-icon-watchlist-active' : '' ),
+ 'title' => wfMessage(
'flow-topic-action-watchlist' )->text(),
+ 'href' => '#',
+ 'onclick' => "alert( '@todo:
Not yet implemented!' ); return false;"
+ ),
+ wfMessage(
'flow-topic-action-watchlist' )->text()
+ );
+ ?>
+ <?php endif; /* !$root->isModerated() */ ?>
</div>
</div>
-<?php
-foreach( $root->getChildren() as $child ) {
- echo $this->renderPost( $child, $block, $root );
-}
+<?php if ( $root->isAllowed( $user ) ):
+ foreach( $root->getChildren() as $child ) {
+ echo $this->renderPost( $child, $block, $root );
+ }
-// Topic reply box
-echo Html::openElement( 'div', array(
- 'class' => 'flow-topic-reply-container flow-post-container
flow-element-container',
- 'data-post-id' => $root->getRevisionId()->getHex(),
- 'id' => 'flow-topic-reply-' . $topic->getId()->getHex()
-) );
-?>
- <span class="flow-creator">
- <span class="flow-creator-simple" style="display: inline">
- <?php echo $this->getUserText( $user ); ?>
+ // Topic reply box
+ echo Html::openElement( 'div', array(
+ 'class' => 'flow-topic-reply-container flow-post-container
flow-element-container',
+ 'data-post-id' => $root->getRevisionId()->getHex(),
+ 'id' => 'flow-topic-reply-' . $topic->getId()->getHex()
+ ) );
+ ?>
+ <span class="flow-creator">
+ <span class="flow-creator-simple" style="display:
inline">
+ <?php echo $this->getUserText( $user ); ?>
+ </span>
+ <span class="flow-creator-full" style="display: none">
+ <?php echo $this->userToolLinks(
$user->getId(), $user->getName() ); ?>
+ </span>
</span>
- <span class="flow-creator-full" style="display: none">
- <?php echo $this->userToolLinks( $user->getId(),
$user->getName() ); ?>
- </span>
- </span>
-<?php
- echo Html::openElement( 'form', array(
- 'method' => 'POST',
- 'action' => $this->generateUrl( $block->getWorkflow(), 'reply'
),
- 'class' => 'flow-topic-reply-form',
- ) ),
- Html::element( 'input', array(
- 'type' => 'hidden',
- 'name' => $block->getName() . '[replyTo]',
- 'value' => $topic->getId()->getHex(),
- ) ),
- Html::element( 'input', array(
- 'type' => 'hidden',
- 'name' => 'wpEditToken',
- 'value' => $editToken,
- ) ),
- Html::textarea( $block->getName() . '[topic-reply-content]', '', array(
- 'placeholder' => wfMessage( 'flow-reply-topic-placeholder',
$user->getName(), $title )->text(),
- 'class' => 'flow-input mw-ui-input flow-topic-reply-content',
- ) ),
- Html::openElement( 'div', array( 'class' => 'flow-post-form-controls' )
),
- Html::element( 'input', array(
- 'type' => 'submit',
- 'value' => wfMessage( 'flow-reply-submit', $this->getUserText(
$root->getCreator( $user ), $root ) )->text(),
- 'class' => 'mw-ui-button mw-ui-constructive
flow-topic-reply-submit',
- ) ),
- Html::closeElement( 'div' ),
- Html::closeElement( 'form' ),
- Html::closeElement( 'div' );
-?>
-</div>
+ <?php
+ echo Html::openElement( 'form', array(
+ 'method' => 'POST',
+ 'action' => $this->generateUrl( $block->getWorkflow(),
'reply' ),
+ 'class' => 'flow-topic-reply-form',
+ ) ),
+ Html::element( 'input', array(
+ 'type' => 'hidden',
+ 'name' => $block->getName() . '[replyTo]',
+ 'value' => $topic->getId()->getHex(),
+ ) ),
+ Html::element( 'input', array(
+ 'type' => 'hidden',
+ 'name' => 'wpEditToken',
+ 'value' => $editToken,
+ ) ),
+ Html::textarea( $block->getName() . '[topic-reply-content]',
'', array(
+ 'placeholder' => wfMessage(
'flow-reply-topic-placeholder', $user->getName(), $title )->text(),
+ 'class' => 'flow-input mw-ui-input
flow-topic-reply-content',
+ ) ),
+ Html::openElement( 'div', array( 'class' =>
'flow-post-form-controls' ) ),
+ Html::element( 'input', array(
+ 'type' => 'submit',
+ 'value' => wfMessage( 'flow-reply-submit',
$this->getUserText( $root->getCreator( $user ), $root ) )->text(),
+ 'class' => 'mw-ui-button mw-ui-constructive
flow-topic-reply-submit',
+ ) ),
+ Html::closeElement( 'div' ),
+ Html::closeElement( 'form' ),
+ Html::closeElement( 'div' );
+ ?>
+ </div>
+<?php endif /* !$root->isModerated() */ ?>
--
To view, visit https://gerrit.wikimedia.org/r/91135
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I10cae63c2491c9f467968d6f8d604ea7f743ea96
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Flow
Gerrit-Branch: master
Gerrit-Owner: EBernhardson <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits