EBernhardson (WMF) has uploaded a new change for review. https://gerrit.wikimedia.org/r/84893
Change subject: See that a post has been edited ...................................................................... See that a post has been edited Adds three database fields to track in what revision and by what user a post's content was most recently edited, and some template code to display that to a user. Change-Id: I92577798d4f9da7fa916cd1fede93e6c171c7ee3 --- M Flow.i18n.php M flow.sql M includes/Model/AbstractRevision.php M includes/Model/PostRevision.php M includes/ParsoidUtils.php M templates/post.html.php 6 files changed, 108 insertions(+), 27 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Flow refs/changes/93/84893/1 diff --git a/Flow.i18n.php b/Flow.i18n.php index 33428b6..807a2e2 100644 --- a/Flow.i18n.php +++ b/Flow.i18n.php @@ -36,6 +36,7 @@ 'flow-edit-post-submit' => 'Submit changes', + 'flow-post-edited' => 'Post {{GENDER:$1|edited}} by $1 $2', 'flow-post-action-view' => 'Permalink', 'flow-post-action-post-history' => 'Post history', 'flow-post-action-censor-post' => 'Censor post', @@ -138,6 +139,9 @@ * $1 - username', 'flow-reply-submit' => 'Used as label for the Submit button.', 'flow-edit-post-submit' => 'Used as label for the Submit button.', + 'flow-post-edited' => 'Text displayed to notify the user a post has been modified +* $1 - Username that created the most recent revision of the post +* $2 - Timestamp, relative to post creation date, of when the edit occured', 'flow-post-action-view' => 'Used as text for the link which is used to view. {{Identical|Permalink}}', 'flow-post-action-post-history' => 'Used as text for the link which is used to view post-history of the topic.', diff --git a/flow.sql b/flow.sql index 2cac549..349692a 100644 --- a/flow.sql +++ b/flow.sql @@ -119,6 +119,10 @@ rev_mod_user_text varchar(255) binary, rev_mod_timestamp varchar(14) binary, + -- track who made the most recent content edit + rev_edit_user_id bigint unsigned, + rev_edit_user_text varchar(255) binary, + PRIMARY KEY (rev_id) ) /*$wgDBTableOptions*/; diff --git a/includes/Model/AbstractRevision.php b/includes/Model/AbstractRevision.php index 0f48a93..5f0863d 100644 --- a/includes/Model/AbstractRevision.php +++ b/includes/Model/AbstractRevision.php @@ -64,6 +64,10 @@ protected $moderatedByUserId; protected $moderatedByUserText; + protected $lastEditId; + protected $lastEditUserId; + protected $lastEditUserText; + static public function fromStorageRow( array $row, $obj = null ) { if ( $obj === null ) { $obj = new static; @@ -86,6 +90,11 @@ $obj->moderatedByUserText = $row['rev_mod_user_text']; $obj->moderationTimestamp = $row['rev_mod_timestamp']; + // isset required because there is a possible db migration, cached data will not have it + $obj->lastEditId = isset( $row['rev_last_edit_id'] ) ? UUID::create( $row['rev_last_edit_id'] ) : null; + $obj->lastEditUserId = isset( $row['rev_edit_user_id'] ) ? $row['rev_edit_user_id'] : null; + $obj->lastEditUserText = isset( $row['rev_edit_user_text'] ) ? $row['rev_edit_user_text'] : null; + return $obj; } @@ -106,6 +115,10 @@ 'rev_mod_user_id' => $obj->moderatedByUserId, 'rev_mod_user_text' => $obj->moderatedByUserText, 'rev_mod_timestamp' => $obj->moderationTimestamp, + + 'rev_last_edit_id' => $obj->lastEditId ? $obj->lastEditId->getBinary() : null, + 'rev_edit_user_id' => $obj->lastEditUserId, + 'rev_edit_user_text' => $obj->lastEditUserText, ); } @@ -123,9 +136,12 @@ return $obj; } + /** + * Create the next revision with new content + */ public function newNextRevision( User $user, $content, $comment ) { $obj = $this->newNullRevision( $user ); - $obj->setContent( $content ); + $obj->setNextContent( $user, $content ); $obj->comment = $comment; return $obj; } @@ -165,8 +181,14 @@ return $this->revId; } + /** + * @param User $user The user requesting access. When null assumes a user with no permissions. + * @param int $state One of the self::MODERATED_* constants. When null the internal moderation state is used. + * @return boolean True when the user is allowed to see the current revision + */ // Is the user allowed to see this revision ? protected function isAllowed( $user = null, $state = null ) { + // allowing a $state to be passed is a bit hackish if ( $state === null ) { $state = $this->moderationState; } @@ -238,30 +260,44 @@ * @throws \Exception */ protected function setContent( $content ) { + if ( $this->content !== null ) { + throw new \Exception( 'Updating content must use setNextContent method' ); + } + // TODO: How is this guarantee of only receiving wikitext made? + $this->convertedContent = array( 'wikitext' => $content ); + + // convert content to desired storage format + global $wgFlowContentFormat; + if ( !isset( $this->convertedContent[$wgFlowContentFormat] ) ) { + $this->convertedContent[$wgFlowContentFormat] = + ParsoidUtils::convert( + 'wikitext', + $wgFlowContentFormat, + $this->convertedContent['wikitext'] + ); + } + + $this->content = $this->decompressedContent = $this->convertedContent[$wgFlowContentFormat]; + $this->contentUrl = null; + + // should this only remove a subset of flags? + $this->flags = array_filter( explode( ',', \Revision::compressRevisionText( $this->content ) ) ); + $this->flags[] = $wgFlowContentFormat; + } + + /** + * Apply new content to a revision. + */ + protected function setNextContent( User $user, $content ) { if ( $this->moderationState !== self::MODERATED_NONE ) { throw new \Exception( 'Cannot change content of restricted revision' ); } - - if ( $content !== $this->getContent( null, 'wikitext') ) { - $this->convertedContent['wikitext'] = $content; - - // convert content to desired storage format - global $wgFlowContentFormat; - if ( !isset( $this->convertedContent[$wgFlowContentFormat] ) ) { - $this->convertedContent[$wgFlowContentFormat] = - ParsoidUtils::convert( - 'wikitext', - $wgFlowContentFormat, - $this->convertedContent['wikitext'] - ); - } - - $this->content = $this->decompressedContent = $this->convertedContent[$wgFlowContentFormat]; - $this->contentUrl = null; - - // should this only remove a subset of flags? - $this->flags = array_filter( explode( ',', \Revision::compressRevisionText( $this->content ) ) ); - $this->flags[] = $wgFlowContentFormat; + if ( $content !== $this->getContent() ) { + $this->content = null; + $this->setContent( $content ); + $this->lastEditId = $this->getRevisionId(); + $this->lastEditUserId = $user->getId(); + $this->lastEditUserText = $user->getName(); } } @@ -306,4 +342,33 @@ } return true; } + + public function isFirstRevision() { + return $this->prevRevision === null; + } + + public function isOriginalContent() { + return $this->lastEditId === null; + } + + /** + * @param $user User requesting access to last content editor + * @return string + */ + public function getLastContentEditorName( $user = null ) { + // TODO: to write this function properly will need to flesh out how + // oversighting works. Prefer to create an external security class that is + // configurable per-wiki, pass revisions into it(or wrap them in it for + // view objects?) to get possibly protected content. + if ( $this->isAllowed( $user ) ) { + return $this->lastEditUserText; + } else { + return ''; + } + } + + public function getLastContentEditId() { + return $this->lastEditId; + } + } diff --git a/includes/Model/PostRevision.php b/includes/Model/PostRevision.php index d450452..bea549c 100644 --- a/includes/Model/PostRevision.php +++ b/includes/Model/PostRevision.php @@ -32,7 +32,6 @@ $obj->prevRevId = null; // no parent revision $obj->comment = 'flow-rev-message-new-post'; $obj->setContent( $content ); - return $obj; } @@ -47,7 +46,6 @@ $obj->origCreateTime = $row['tree_orig_create_time']; $obj->origUserId = $row['tree_orig_user_id']; $obj->origUserText = $row['tree_orig_user_text']; - return $obj; } diff --git a/includes/ParsoidUtils.php b/includes/ParsoidUtils.php index c507bb7..280d272 100644 --- a/includes/ParsoidUtils.php +++ b/includes/ParsoidUtils.php @@ -63,6 +63,7 @@ true // POST ); + wfDebugLog( __CLASS__, __FUNCTION__ . ": Roundtripping parsoid for $from => $to" ); $api = new \ApiMain( $params, true ); $api->execute(); $result = $api->getResultData(); diff --git a/templates/post.html.php b/templates/post.html.php index 49da763..30cef35 100644 --- a/templates/post.html.php +++ b/templates/post.html.php @@ -95,6 +95,7 @@ $replyForm = ''; // Build the actions for the post +// TODO: this whole action menu building should be some sort of class and not a few closures in a template switch( $post->getModerationState() ) { case $post::MODERATED_NONE: if ( $user->isAllowed( 'flow-hide' ) ) { @@ -146,6 +147,7 @@ // The actual output echo Html::openElement( 'div', array( + 'class' => 'flow-post-container', 'data-post-id' => $post->getRevisionId()->getHex(), ) ); @@ -173,8 +175,15 @@ </span> </span> </div> - <?php if ( $post->isModerated() ): ?> - <?php endif; ?> + <?php if ( !$post->isOriginalContent() ): ?> + <div class="flow-post-edited"> + <?php echo wfMessage( + 'flow-post-edited', + $post->getLastContentEditorName( $user ), + $post->getLastContentEditId()->getHumanTimestamp() + ); ?> + </div> + <?php endif ?> </div> <div class="flow-post-content"> <?php echo $content ?> @@ -187,7 +196,7 @@ <ul> <?php foreach( $actions as $key => $action ) { - echo '<li class="flow-action-'.$key.'">' . $action . "</li>\n"; + echo Html::rawElement( 'li', array( 'class' => "flow-action-$key" ), $action ); } ?> </ul> -- To view, visit https://gerrit.wikimedia.org/r/84893 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I92577798d4f9da7fa916cd1fede93e6c171c7ee3 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/Flow Gerrit-Branch: master Gerrit-Owner: EBernhardson (WMF) <ebernhard...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits