diff options
Diffstat (limited to 'CommentStreams/includes/Comment.php')
-rw-r--r-- | CommentStreams/includes/Comment.php | 1142 |
1 files changed, 336 insertions, 806 deletions
diff --git a/CommentStreams/includes/Comment.php b/CommentStreams/includes/Comment.php index 755c24c3..4769a4ca 100644 --- a/CommentStreams/includes/Comment.php +++ b/CommentStreams/includes/Comment.php @@ -23,537 +23,374 @@ namespace MediaWiki\Extension\CommentStreams; +use ConfigException; +use FatalError; use Html; -use MediaWiki\MediaWikiServices; +use IContextSource; +use MediaWiki\Linker\LinkRenderer; +use MediaWiki\User\UserIdentity; +use MWException; use MWTimestamp; -use Parser; -use ParserOptions; -use SMWDataItem; -use SMWUpdateJob; +use PageProps; use Title; use User; -use wAvatar; +use Wikimedia\Assert\Assert; use WikiPage; -use WikitextContent; class Comment { + public const CONSTRUCTOR_OPTIONS = [ + 'CommentStreamsUserAvatarPropertyName', + 'CommentStreamsUserRealNamePropertyName', + 'CommentStreamsEnableVoting' + ]; - // wiki page object for this comment wiki page - private $wikipage = null; - - // data for this comment has been loaded from the database - private $loaded = false; - - // int page ID for the wikipage this comment is on - private $assoc_page_id; - - // int page ID for the wikipage this comment is in reply to or null - private $parent_page_id; + /** + * @var CommentStreamsStore + */ + private $commentStreamsStore; - // string title of comment - private $comment_title; + /** + * @var CommentStreamsEchoInterface + */ + private $echoInterface; - // string wikitext of comment - private $wikitext = null; + /** + * @var CommentStreamsSMWInterface + */ + private $smwInterface; - // string HTML of comment - private $html = null; + /** + * @var CommentStreamsSocialProfileInterface + */ + private $socialProfileInterface; - // User user object for the author of this comment - private $user = null; + /** + * @var LinkRenderer + */ + private $linkRenderer; - // Avatar for author of this comment - private $avatar = null; + /** + * @var string + */ + private $userAvatarPropertyName; - // MWTimestamp the earliest revision date for this comment - private $creation_timestamp = null; + /** + * @var string + */ + private $userRealNamePropertyName; - // MWTimestamp the latest revision date for this comment - private $modification_timestamp = null; + /** + * @var mixed + */ + private $enableVoting; - // number of replies to this comment - private $num_replies = null; + /** + * wiki page object for this comment wiki page + * @var WikiPage + */ + private $wikipage; - // number of up votes for this comment - private $num_up_votes = null; + /** + * unique id to identify comment block in a page + * @var ?string + */ + private $comment_block_id; - // number of dow votes for this comment - private $num_down_votes = null; + /** + * page ID for the wiki page this comment is on + * @var int + */ + private $assoc_page_id; /** - * create a new Comment object from existing wiki page - * - * @param WikiPage $wikipage WikiPage object corresponding to comment page - * @return Comment|null the newly created comment or null if there was an - * error + * page ID for the wiki page this comment is in reply to or null + * @var ?int */ - public static function newFromWikiPage( $wikipage ) { - if ( $wikipage !== null && - $wikipage->getTitle()->getNamespace() === NS_COMMENTSTREAMS ) { - $comment = new Comment( $wikipage ); - if ( $wikipage->exists() ) { - $comment->loadFromDatabase(); - } - return $comment; - } - return null; - } + private $parent_page_id; /** - * create a new Comment object from values and save to database - * NOTE: since only head comments can contain a comment title, either - * $comment_title or $parent_page_id must be non null, but not both - * - * @param int $assoc_page_id page ID for the wikipage this comment is on - * @param int $parent_page_id page ID for the wikipage this comment is in - * reply to or null - * @param string $comment_title string title of comment - * @param string $wikitext the wikitext to add - * @param User $user the user - * @return Comment|null new comment object or null if there was a problem - * creating it + * title of comment + * @var ?string */ - public static function newFromValues( $assoc_page_id, $parent_page_id, - $comment_title, $wikitext, $user ) { - if ( $comment_title === null && $parent_page_id === null ) { - return null; - } - if ( $comment_title !== null && $parent_page_id !== null ) { - return null; - } - $annotated_wikitext = self::addAnnotations( $wikitext, $comment_title, - $assoc_page_id ); - $content = new WikitextContent( $annotated_wikitext ); - $success = false; - while ( !$success ) { - $index = wfRandomString(); - $title = Title::newFromText( (string)$index, NS_COMMENTSTREAMS ); - if ( !$title->isDeletedQuick() && !$title->exists() ) { - if ( class_exists( 'MediaWiki\Permissions\PermissionManager' ) ) { - // MW 1.33+ - if ( !MediaWikiServices::getInstance() - ->getPermissionManager() - ->userCan( 'cs-comment', $user, $title ) - ) { - return null; - } - } else { - if ( !$title->userCan( 'cs-comment' ) ) { - return null; - } - } + private $comment_title; - $wikipage = new WikiPage( $title ); - $status = $wikipage->doEditContent( $content, '', - EDIT_NEW | EDIT_SUPPRESS_RC, false, $user, null ); - if ( !$status->isOK() && !$status->isGood() ) { - if ( $status->getMessage()->getKey() == 'edit-already-exists' ) { - $index = wfRandomString(); - } else { - return null; - } - } else { - $success = true; - } - } else { - $index = wfRandomString(); - } - } - $comment = new Comment( $wikipage ); - $comment->wikitext = $wikitext; + /** + * wikitext of comment + * @var ?string + */ + private $wikitext; - $dbw = wfGetDB( DB_MASTER ); - $result = $dbw->insert( - 'cs_comment_data', - [ - 'cst_page_id' => $wikipage->getId(), - 'cst_assoc_page_id' => $assoc_page_id, - 'cst_parent_page_id' => $parent_page_id, - 'cst_comment_title' => $comment_title - ], - __METHOD__ - ); - if ( !$result ) { - return null; - } - $comment->loadFromValues( $assoc_page_id, $parent_page_id, $comment_title ); + /** + * number of replies to this comment + * @var ?int + */ + private $num_replies; - if ( $parent_page_id === null ) { - $comment->watch( $user ); - } else { - self::watchComment( $parent_page_id, $user ); - } + /** + * user object for the author of this comment + * @var User + */ + private $author; - if ( defined( 'SMW_VERSION' ) ) { - $job = new SMWUpdateJob( $title, [] ); - \JobQueueGroup::singleton()->push( $job ); - } + /** + * user object for the last editor of this comment + * @var ?UserIdentity + */ + private $lastEditor; - return $comment; - } + /** + * Avatar for author of this comment + * @var ?string + */ + private $avatar; /** - * constructor - * - * @param WikiPage $wikipage WikiPage object corresponding to comment page + * @var array */ - private function __construct( $wikipage ) { - $this->wikipage = $wikipage; - } + private static $avatarCache = []; /** - * load comment data from database + * the earliest revision date for this comment + * @var ?MWTimestamp */ - private function loadFromDatabase() { - $dbr = wfGetDB( DB_REPLICA ); - $result = $dbr->selectRow( - 'cs_comment_data', - [ - 'cst_assoc_page_id', - 'cst_parent_page_id', - 'cst_comment_title' - ], - [ - 'cst_page_id' => $this->getId() - ], - __METHOD__ - ); - if ( $result ) { - $this->assoc_page_id = (int)$result->cst_assoc_page_id; - $this->parent_page_id = $result->cst_parent_page_id; - if ( $this->parent_page_id !== null ) { - $this->parent_page_id = (int)$this->parent_page_id; - } - $this->comment_title = $result->cst_comment_title; - $this->loaded = true; - } - } + private $creation_timestamp; /** - * load comment data from values - * - * @param int $assoc_page_id page ID for the wikipage this comment is on - * @param int $parent_page_id page ID for the wikipage this comment is in - * reply to or null - * @param string $comment_title string title of comment + * the latest revision date for this comment + * @var ?MWTimestamp */ - private function loadFromValues( $assoc_page_id, $parent_page_id, - $comment_title ) { - $this->assoc_page_id = (int)$assoc_page_id; + private $modification_timestamp; + + /** + * Do not instantiate directly. Use CommentStreamsFactory instead. + * @param \MediaWiki\Config\ServiceOptions|\Config $options + * @param CommentStreamsStore $commentStreamsStore + * @param CommentStreamsEchoInterface $echoInterface + * @param CommentStreamsSMWInterface $smwInterface + * @param CommentStreamsSocialProfileInterface $socialProfileInterface + * @param LinkRenderer $linkRenderer + * @param WikiPage $wikipage WikiPage object corresponding to comment page + * @param ?string $comment_block_id + * @param int $assoc_page_id + * @param ?int $parent_page_id + * @param ?string $comment_title + * @param string $wikitext + * @throws ConfigException + */ + public function __construct( + $options, + CommentStreamsStore $commentStreamsStore, + CommentStreamsEchoInterface $echoInterface, + CommentStreamsSMWInterface $smwInterface, + CommentStreamsSocialProfileInterface $socialProfileInterface, + LinkRenderer $linkRenderer, + WikiPage $wikipage, + ?string $comment_block_id, + int $assoc_page_id, + ?int $parent_page_id, + ?string $comment_title, + string $wikitext + ) { + if ( class_exists( '\MediaWiki\Config\ServiceOptions' ) ) { + $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS ); + } + $this->userAvatarPropertyName = $options->get( 'CommentStreamsUserAvatarPropertyName' ); + $this->userRealNamePropertyName = $options->get( 'CommentStreamsUserRealNamePropertyName' ); + $this->enableVoting = (bool)$options->get( 'CommentStreamsEnableVoting' ); + $this->commentStreamsStore = $commentStreamsStore; + $this->echoInterface = $echoInterface; + $this->smwInterface = $smwInterface; + $this->socialProfileInterface = $socialProfileInterface; + $this->linkRenderer = $linkRenderer; + $this->wikipage = $wikipage; + $this->comment_block_id = $comment_block_id; + $this->assoc_page_id = $assoc_page_id; $this->parent_page_id = $parent_page_id; - if ( $this->parent_page_id !== null ) { - $this->parent_page_id = (int)$this->parent_page_id; - } $this->comment_title = $comment_title; - $this->loaded = true; + $this->wikitext = $wikitext; + $this->num_replies = $commentStreamsStore->getNumReplies( $wikipage->getId() ); + $title = $wikipage->getTitle(); + $user = CommentStreamsUtils::getAuthor( $title ); + if ( $user !== null ) { + $this->author = $user; + } + $this->setAvatar(); + $this->lastEditor = CommentStreamsUtils::getLastEditor( $wikipage ) ?: $this->author; + $timestamp = CommentStreamsUtils::getCreationTimestamp( $title ); + if ( $timestamp !== null ) { + $this->creation_timestamp = MWTimestamp::getLocalInstance( $timestamp ); + } + $this->setModificationTimestamp(); } /** - * @return int page ID of the comment's wikipage + * @return int page ID of the comment's wiki page */ - public function getId() { + public function getId() : int { return $this->wikipage->getId(); } /** - * @return WikiPage wiki page object associate with this comment page + * @return Title Title object associated with this comment page */ - public function getWikiPage() { - return $this->wikipage; + public function getTitle() : Title { + return $this->wikipage->getTitle(); } /** - * @return int page ID for the wikipage this comment is on + * @return ?string comment block id */ - public function getAssociatedId() { - if ( $this->loaded === false ) { - $this->loadFromDatabase(); - } + public function getBlockId() : ?string { + return $this->comment_block_id; + } + + /** + * @return int page ID for the wiki page this comment is on + */ + public function getAssociatedId() : int { return $this->assoc_page_id; } /** - * @return int|null page ID for the wikipage this comment is in reply to or + * @return int|null page ID for the wiki page this comment is in reply to or * null if this comment is a discussion, not a reply */ - public function getParentId() { - if ( $this->loaded === false ) { - $this->loadFromDatabase(); - } + public function getParentId() : ?int { return $this->parent_page_id; } /** - * @return string the title of the comment + * @return ?string the title of the comment */ - public function getCommentTitle() { - if ( $this->loaded === false ) { - $this->loadFromDatabase(); - } + public function getCommentTitle() : ?string { return $this->comment_title; } /** * @return string wikitext of the comment */ - public function getWikiText() { - if ( $this->wikitext === null ) { - $wikitext = \ContentHandler::getContentText( $this->wikipage->getContent( - \Revision::RAW ) ); - $wikitext = $this->removeAnnotations( $wikitext ); - $this->wikitext = $wikitext; - } + public function getWikiText() : string { return $this->wikitext; } /** + * @param IContextSource $context * @return string parsed HTML of the comment */ - public function getHTML() { - if ( $this->html === null ) { - $this->getWikiText(); - if ( $this->wikitext !== null ) { - if ( class_exists( \ParserFactory::class ) ) { - // @requires MediaWiki >= 1.32.0 - $parser = MediaWikiServices::getInstance()->getParserFactory()->create(); - } else { - $parser = new Parser(); - } - - $this->html = $parser->parse( $this->wikitext, - $this->wikipage->getTitle(), new ParserOptions )->getText(); - } - } - return $this->html; + public function getHTML( IContextSource $context ) : string { + return CommentStreamsUtils::parse( $this->wikitext, $this->wikipage, $context ); } /** - * @return User the author of this comment + * @return int number of replies */ - public function getUser() { - if ( $this->user === null ) { - $user_id = $this->wikipage->getOldestRevision()->getUser(); - $this->user = User::newFromId( $user_id ); - } - return $this->user; + public function getNumReplies() : int { + return $this->num_replies; } /** - * @return bool true if the last edit to this comment was not done by the - * original author + * @return ?User the author of this comment */ - public function isLastEditModerated() { - $author = $this->wikipage->getOldestRevision()->getUser(); - $lastEditor = $this->wikipage->getRevision()->getUser(); - return $author !== $lastEditor; + public function getAuthor() : ?User { + return $this->author; } /** * @return string username of the author of this comment */ - public function getUsername() { - return $this->getUser()->getName(); + public function getUsername() : string { + return $this->author->getName(); } /** * @return string display name of the author of this comment linked to * the user's user page if it exists */ - public function getUserDisplayName() { - return self::getDisplayNameFromUser( $this->getUser() ); + public function getUserDisplayName() : string { + return $this->getDisplayNameFromUser( $this->author, true ); } /** * @return string display name of the author of this comment */ - public function getUserDisplayNameUnlinked() { - return self::getDisplayNameFromUser( $this->getUser(), false ); - } - - /** - * @return string the URL of the avatar of the author of this comment - */ - public function getAvatar() { - if ( $this->avatar === null ) { - if ( class_exists( 'wAvatar' ) ) { - // from Extension:SocialProfile - $avatar = new wAvatar( $this->getUser()->getId(), 'l' ); - $this->avatar = $GLOBALS['wgUploadPath'] . '/avatars/' . - $avatar->getAvatarImage(); - } else { - $this->avatar = self::getAvatarFromUser( $this->getUser() ); - } - } - return $this->avatar; + public function getUserDisplayNameUnlinked() : string { + return $this->getDisplayNameFromUser( $this->author, false ); } /** - * @return MWTimestamp the earliest revision date for this + * @return UserIdentity the last editor of this comment */ - public function getCreationTimestamp() { - if ( $this->creation_timestamp === null ) { - $this->creation_timestamp = MWTimestamp::getLocalInstance( - $this->wikipage->getTitle()->getEarliestRevTime() ); - } - return $this->creation_timestamp; + public function getLastEditor() : UserIdentity { + return $this->lastEditor; } /** - * @return MWTimestamp the earliest revision date for this + * @return bool true if the last edit to this comment was not done by the + * original author */ - public function getCreationDate() { - if ( $this->getCreationTimestamp() !== null ) { - return $this->creation_timestamp->format( "M j \a\\t g:i a" ); - } - return ""; + public function isLastEditModerated() : bool { + return $this->author->getId() !== $this->lastEditor->getId(); } /** - * @return MWTimestamp the latest revision date for this + * @return MWTimestamp */ - public function getModificationTimestamp() { - if ( $this->modification_timestamp === null ) { - $title = $this->wikipage->getTitle(); - if ( $title->getFirstRevision()->getId() === $title->getLatestRevID() ) { - return null; - } - - $revStore = MediaWikiServices::getInstance()->getRevisionStore(); - $latestRev = $title->getLatestRevId(); - if ( version_compare( MW_VERSION, '1.34', '<' ) ) { - $timestamp = $revStore->getTimestampFromId( $title, $latestRev ); - } else { - $timestamp = $revStore->getTimestampFromId( $latestRev ); - } - - $this->modification_timestamp = MWTimestamp::getLocalInstance( - $timestamp ); - } - return $this->modification_timestamp; + public function getCreationTimestamp() : MWTimestamp { + return $this->creation_timestamp; } /** - * @return MWTimestamp the earliest revision date for this + * @return string */ - public function getModificationDate() { - if ( $this->getModificationTimestamp() !== null ) { - return $this->modification_timestamp->format( "M j \a\\t g:i a" ); - } - return null; + public function getCreationDate() : string { + return $this->creation_timestamp->format( "M j \a\\t g:i a" ); } /** - * @return int number of replies + * @return ?string */ - public function getNumReplies() { - if ( $this->num_replies === null ) { - $dbr = wfGetDB( DB_REPLICA ); - $this->num_replies = $dbr->selectRowCount( - 'cs_comment_data', - '*', - [ - 'cst_parent_page_id' => $this->getId() - ], - __METHOD__ - ); - } - return $this->num_replies; + public function getModificationDate() : ?string { + return $this->modification_timestamp ? + $this->modification_timestamp->format( "M j \a\\t g:i a" ) : null; } /** + * @param IContextSource $context * @return array get comment data in array suitable for JSON */ - public function getJSON() { + public function getJSON( IContextSource $context ) : array { $json = [ - 'commenttitle' => $this->getCommentTitle(), + 'pageid' => $this->wikipage->getId(), + 'commentblockid' => $this->comment_block_id, + 'associatedid' => $this->assoc_page_id, + 'parentid' => $this->parent_page_id, + 'commenttitle' => $this->comment_title, + 'wikitext' => htmlentities( $this->wikitext ), + 'html' => $this->getHTML( $context ), 'username' => $this->getUsername(), + 'numreplies' => $this->num_replies, 'userdisplayname' => $this->getUserDisplayName(), - 'avatar' => $this->getAvatar(), - 'created' => $this->getCreationDate(), - 'created_timestamp' => $this->getCreationTimestamp()->format( "U" ), - 'modified' => $this->getModificationDate(), + 'avatar' => $this->avatar, 'moderated' => $this->isLastEditModerated() ? "moderated" : null, - 'wikitext' => htmlentities( $this->getWikiText() ), - 'html' => $this->getHTML(), - 'pageid' => $this->getId(), - 'associatedid' => $this->getAssociatedId(), - 'parentid' => $this->getParentId(), - 'numreplies' => $this->getNumReplies(), + 'created' => $this->getCreationDate(), + 'created_timestamp' => $this->creation_timestamp->format( "U" ), + 'modified' => $this->getModificationDate() ]; - if ( $GLOBALS['wgCommentStreamsEnableVoting'] ) { - $json['numupvotes'] = $this->getNumUpVotes(); - $json['numdownvotes'] = $this->getNumDownVotes(); - } - return $json; - } - /** - * get vote for user - * - * @param User $user the author of the edit - * @return +1 for up vote, -1 for down vote, 0 for no vote - */ - public function getVote( $user ) { - $dbr = wfGetDB( DB_REPLICA ); - $result = $dbr->selectRow( - 'cs_votes', - [ - 'cst_v_vote' - ], - [ - 'cst_v_page_id' => $this->getId(), - 'cst_v_user_id' => $user->getId() - ], - __METHOD__ - ); - if ( $result ) { - $vote = (int)$result->cst_v_vote; - if ( $vote > 0 ) { - return 1; + $user = $context->getUser(); + if ( $this->parent_page_id === null ) { + if ( $this->enableVoting ) { + $json['numupvotes'] = $this->commentStreamsStore->getNumUpVotes( $this->getId() ); + $json['numdownvotes'] = + $this->commentStreamsStore->getNumDownVotes( $this->getId() ); + $json['vote'] = $this->getVote( $user ); } - if ( $vote < 0 ) { - return -1; + if ( $this->echoInterface->isLoaded() ) { + $json['watching'] = $this->isWatching( $user ) ? 1 : 0; } } - return 0; - } - - /** - * @return int number of up votes - */ - public function getNumUpVotes() { - if ( $this->num_up_votes === null ) { - $dbr = wfGetDB( DB_REPLICA ); - $this->num_up_votes = $dbr->selectRowCount( - 'cs_votes', - '*', - [ - 'cst_v_page_id' => $this->getId(), - 'cst_v_vote' => 1 - ], - __METHOD__ - ); - } - return $this->num_up_votes; - } - /** - * @return int number of down votes - */ - public function getNumDownVotes() { - if ( $this->num_down_votes === null ) { - $dbr = wfGetDB( DB_REPLICA ); - $this->num_down_votes = $dbr->selectRowCount( - 'cs_votes', - '*', - [ - 'cst_v_page_id' => $this->getId(), - 'cst_v_vote' => -1 - ], - __METHOD__ - ); - } - return $this->num_down_votes; + return $json; } /** @@ -563,184 +400,44 @@ class Comment { * @param User $user the user voting on the comment * @return bool database status code */ - public function vote( $vote, $user ) { - if ( $vote !== "-1" && $vote !== "0" && $vote !== "1" ) { - return false; - } - $vote = (int)$vote; - $dbr = wfGetDB( DB_REPLICA ); - $result = $dbr->selectRow( - 'cs_votes', - [ - 'cst_v_vote' - ], - [ - 'cst_v_page_id' => $this->getId(), - 'cst_v_user_id' => $user->getId() - ], - __METHOD__ - ); - if ( $result ) { - if ( $vote === (int)$result->cst_v_vote ) { - return true; - } - if ( $vote === 1 || $vote === -1 ) { - $dbw = wfGetDB( DB_MASTER ); - $result = $dbw->update( - 'cs_votes', - [ - 'cst_v_vote' => $vote - ], - [ - 'cst_v_page_id' => $this->getId(), - 'cst_v_user_id' => $user->getId() - ], - __METHOD__ - ); - } else { - $dbw = wfGetDB( DB_MASTER ); - $result = $dbw->delete( - 'cs_votes', - [ - 'cst_v_page_id' => $this->getId(), - 'cst_v_user_id' => $user->getId() - ], - __METHOD__ - ); - } - } else { - if ( $vote === 0 ) { - return true; - } - $dbw = wfGetDB( DB_MASTER ); - $result = $dbw->insert( - 'cs_votes', - [ - 'cst_v_page_id' => $this->getId(), - 'cst_v_user_id' => $user->getId(), - 'cst_v_vote' => $vote - ], - __METHOD__ - ); - } - return $result; - } - - /** - * watch a comment (get page ID from this comment) - * - * @param User $user the user watching the comment - * @return bool database true for OK, false for error - */ - public function watch( $user ) { - return self::watchComment( $this->getID(), $user ); - } - - /** - * watch a comment (get page ID from parameter) - * - * @param int $pageid the page ID of the comment to watch - * @param User $user the user watching the comment - * @return bool database true for OK, false for error - */ - private static function watchComment( $pageid, $user ) { - if ( self::isWatchingComment( $pageid, $user ) ) { - return true; - } - $dbw = wfGetDB( DB_MASTER ); - $result = $dbw->insert( - 'cs_watchlist', - [ - 'cst_wl_page_id' => $pageid, - 'cst_wl_user_id' => $user->getId() - ], - __METHOD__ - ); + public function vote( string $vote, User $user ) : bool { + Assert::parameter( $vote === "-1" || $vote === "0" || $vote === "1", '$vote', + 'must be "-1", "0", or "1"' ); + $result = $this->commentStreamsStore->vote( (int)$vote, $this->getId(), $user->getId() ); + $this->smwInterface->update( $this->getTitle() ); return $result; } /** - * unwatch a comment - * - * @param User $user the user unwatching the comment - * @return bool database true for OK, false for error + * @param User $user + * @return int */ - public function unwatch( $user ) { - if ( !$this->isWatching( $user ) ) { - return true; - } - $dbw = wfGetDB( DB_MASTER ); - $result = $dbw->delete( - 'cs_watchlist', - [ - 'cst_wl_page_id' => $this->getId(), - 'cst_wl_user_id' => $user->getId() - ], - __METHOD__ - ); - return $result; + public function getVote( User $user ) : int { + return $this->commentStreamsStore->getVote( $this->getId(), $user->getId() ); } /** - * Check if a particular user is watching this comment - * - * @param User $user the user watching the comment - * @return bool database true for OK, false for error + * @param int $user_id + * @return bool */ - public function isWatching( $user ) { - return self::isWatchingComment( $this->getId(), $user ); + public function watch( int $user_id ) : bool { + return $this->commentStreamsStore->watch( $this->getId(), $user_id ); } /** - * Check if a particular user is watching a comment - * - * @param int $pageid the page ID of the comment to check - * @param User $user the user watching the comment - * @return bool database true for OK, false for error + * @param int $user_id + * @return bool */ - private static function isWatchingComment( $pageid, $user ) { - $dbr = wfGetDB( DB_REPLICA ); - $result = $dbr->selectRow( - 'cs_watchlist', - [ - 'cst_wl_page_id' - ], - [ - 'cst_wl_page_id' => $pageid, - 'cst_wl_user_id' => $user->getId() - ], - __METHOD__ - ); - if ( $result ) { - return true; - } - return false; + public function unwatch( int $user_id ) : bool { + return $this->commentStreamsStore->unwatch( $this->getId(), $user_id ); } /** - * Get an array of watchers for this comment - * - * @return array of user IDs + * @param User $user + * @return bool */ - public function getWatchers() { - $dbr = wfGetDB( DB_REPLICA ); - $result = $dbr->select( - 'cs_watchlist', - [ - 'cst_wl_user_id' - ], - [ - 'cst_wl_page_id' => $this->getId() - ], - __METHOD__ - ); - $users = []; - foreach ( $result as $row ) { - $user_id = $row->cst_wl_user_id; - $user = User::newFromId( $user_id ); - $users[$user_id] = $user; - } - return $users; + public function isWatching( User $user ) : bool { + return $this->commentStreamsStore->isWatching( $this->getId(), $user->getId() ); } /** @@ -749,47 +446,43 @@ class Comment { * $comment_title may only be non null if this comment has a null parent id * and vice versa * - * @param string $comment_title the new title for the comment + * @param ?string $comment_title the new title for the comment * @param string $wikitext the wikitext to add * @param User $user the author of the edit * @return bool true if successful - */ - public function update( $comment_title, $wikitext, $user ) { - if ( $comment_title === null && $this->getParentId() === null ) { - return false; - } - if ( $comment_title !== null && $this->getParentId() !== null ) { - return false; - } - $annotated_wikitext = - self::addAnnotations( $wikitext, $comment_title, - $this->getAssociatedId() ); - $content = new WikitextContent( $annotated_wikitext ); - $status = $this->wikipage->doEditContent( $content, '', - EDIT_UPDATE | EDIT_SUPPRESS_RC, false, $user, null ); - if ( !$status->isOK() && !$status->isGood() ) { - return false; - } - $this->wikitext = $wikitext; - $this->modification_timestamp = null; - $this->wikipage = WikiPage::newFromID( $this->wikipage->getId() ); - - $dbw = wfGetDB( DB_MASTER ); - $result = $dbw->update( - 'cs_comment_data', - [ - 'cst_comment_title' => $comment_title - ], - [ - 'cst_page_id' => $this->getId() - ], - __METHOD__ + * @throws MWException + */ + public function update( + ?string $comment_title, + string $wikitext, + User $user + ) : bool { + Assert::parameter( + ( $comment_title === null && $this->parent_page_id !== null ) || + ( $comment_title !== null && $this->parent_page_id === null ), + '$comment_title', + 'must be null if parent page ID is non-null or non-null if parent page ID is null' + ); + $result = $this->commentStreamsStore->updateComment( + $this->wikipage, + $comment_title, + $wikitext, + $user ); if ( !$result ) { return false; } $this->comment_title = $comment_title; - + $this->wikitext = $wikitext; + $this->modification_timestamp = null; + $wikipage = CommentStreamsUtils::newWikiPageFromId( $this->wikipage->getId(), + 'fromdbmaster' ); + if ( $wikipage !== null ) { + $this->wikipage = $wikipage; + } + if ( $this->parent_page_id === null ) { + $this->smwInterface->update( $this->getTitle() ); + } return true; } @@ -798,134 +491,56 @@ class Comment { * * @param User $deleter * @return bool true if successful + * @throws FatalError + * @throws MWException */ - public function delete( User $deleter ) { - if ( version_compare( MW_VERSION, '1.35', '<' ) ) { - $status = $this->getWikiPage()->doDeleteArticleReal( - 'comment deleted', - true - ); - } else { - $status = $this->getWikiPage()->doDeleteArticleReal( - 'comment deleted', - $deleter, - true - ); - } + public function delete( User $deleter ) : bool { + return $this->commentStreamsStore->deleteComment( $this->wikipage, $deleter ); + } - if ( !$status->isOK() && !$status->isGood() ) { - return false; + private function setAvatar() { + if ( array_key_exists( $this->author->getId(), self::$avatarCache ) ) { + $this->avatar = self::$avatarCache[ $this->author->getId() ]; + return; } - $pageid = $this->getId(); - $dbw = wfGetDB( DB_MASTER ); - $result = $dbw->delete( - 'cs_comment_data', - [ - 'cst_page_id' => $pageid - ], - __METHOD__ - ); - return $result; - } + $this->avatar = $this->socialProfileInterface->getAvatar( $this->author ); - /** - * add extra information to wikitext before storage - * - * @param string $wikitext the wikitext to which to add - * @param string $comment_title string title of comment - * @param int $assoc_page_id page ID for the wikipage this comment is on - * @return string annotated wikitext - */ - public static function addAnnotations( $wikitext, $comment_title, - $assoc_page_id ) { - if ( $comment_title !== null ) { - $wikitext .= <<<EOT -{{DISPLAYTITLE: -$comment_title -}} -EOT; + if ( $this->avatar === null && $this->userAvatarPropertyName !== null ) { + $title = $this->smwInterface->getUserProperty( $this->author, + $this->userAvatarPropertyName ); + if ( $title !== null ) { + if ( is_string( $title ) ) { + $title = Title::newFromText( $title ); + } + if ( $title->isKnown() && $title->getNamespace() === NS_FILE ) { + $file = CommentStreamsUtils::findFile( $title->getText() ); + if ( $file ) { + $this->avatar = $file->createThumb( 48, 48 ); + } + } + } } - return $wikitext; + + self::$avatarCache[ $this->author->getId() ] = $this->avatar; } - /** - * add extra information to wikitext before storage - * - * @param string $wikitext the wikitext to which to add - * @return string wikitext without annotations - */ - public function removeAnnotations( $wikitext ) { - $comment_title = $this->getCommentTitle(); - if ( $comment_title !== null ) { - $strip = <<<EOT -{{DISPLAYTITLE: -$comment_title -}} -EOT; - $wikitext = str_replace( $strip, '', $wikitext ); + private function setModificationTimestamp() { + $title = $this->wikipage->getTitle(); + $firstRevisionId = CommentStreamsUtils::getFirstRevisionId( $title ); + if ( $firstRevisionId === null ) { + return; } - return $wikitext; - } - /** - * get comments for the given page - * - * @param int $assoc_page_id ID of page to get comments for - * @return array array of comments for the given page - */ - public static function getAssociatedComments( $assoc_page_id ) { - $dbr = wfGetDB( DB_REPLICA ); - $result = $dbr->select( - 'cs_comment_data', - [ - 'cst_page_id' - ], - [ - 'cst_assoc_page_id' => $assoc_page_id - ], - __METHOD__ - ); - $comments = []; - foreach ( $result as $row ) { - $page_id = $row->cst_page_id; - $wikipage = WikiPage::newFromId( $page_id ); - $comment = self::newFromWikiPage( $wikipage ); - if ( $comment !== null ) { - $comments[] = $comment; - } + $latestRevisionId = $title->getLatestRevId(); + if ( $firstRevisionId === $latestRevisionId ) { + return; } - return $comments; - } - /** - * get replies for the given comment - * - * @param int $parent_page_id ID of page to get comments for - * @return array array of comments for the given page - */ - public static function getReplies( $parent_page_id ) { - $dbr = wfGetDB( DB_REPLICA ); - $result = $dbr->select( - 'cs_comment_data', - [ - 'cst_page_id' - ], - [ - 'cst_parent_page_id' => $parent_page_id - ], - __METHOD__ - ); - $comments = []; - foreach ( $result as $row ) { - $page_id = $row->cst_page_id; - $wikipage = WikiPage::newFromId( $page_id ); - $comment = self::newFromWikiPage( $wikipage ); - if ( $comment !== null ) { - $comments[] = $comment; - } + $timestamp = CommentStreamsUtils::getTimestampFromId( $latestRevisionId, $title ); + if ( $timestamp !== false ) { + $this->modification_timestamp = MWTimestamp::getLocalInstance( $timestamp ); } - return $comments; } /** @@ -933,31 +548,33 @@ EOT; * * @param User $user the user * @param bool $linked whether to link the display name to the user page, - * if it exists + * if it exists * @return string display name for user */ - public static function getDisplayNameFromUser( $user, $linked = true ) { + private function getDisplayNameFromUser( + User $user, + bool $linked + ) : string { if ( $user->isAnon() ) { - $html = Html::openElement( 'span', [ + return Html::openElement( 'span', [ 'class' => 'cs-comment-author-anonymous' ] ) . wfMessage( 'commentstreams-author-anonymous' ) . Html::closeElement( 'span' ); - return $html; } $userpage = $user->getUserPage(); $displayname = null; - if ( $GLOBALS['wgCommentStreamsUserRealNamePropertyName'] !== null ) { - $displayname = self::getUserProperty( $user, - $GLOBALS['wgCommentStreamsUserRealNamePropertyName'] ); + if ( $this->userRealNamePropertyName !== null ) { + $displayname = $this->smwInterface->getUserProperty( + $user, + $this->userRealNamePropertyName + ); } if ( $displayname === null || strlen( $displayname ) == 0 ) { - if ( class_exists( 'PageProps' ) ) { - $values = \PageProps::getInstance()->getProperties( $userpage, - 'displaytitle' ); - if ( array_key_exists( $userpage->getArticleID(), $values ) ) { - $displayname = $values[$userpage->getArticleID()]; - } + $values = PageProps::getInstance()->getProperties( $userpage, + 'displaytitle' ); + if ( array_key_exists( $userpage->getArticleID(), $values ) ) { + $displayname = $values[$userpage->getArticleID()]; } } if ( $displayname === null || strlen( $displayname ) == 0 ) { @@ -967,95 +584,8 @@ EOT; $displayname = $user->getName(); } if ( $linked && $userpage->exists() ) { - $displayname = CommentStreamsUtils::link( $userpage, $displayname ); + $displayname = $this->linkRenderer->makeLink( $userpage, $displayname ); } return $displayname; } - - /** - * return the name of the file page containing the user's avatar - * - * @param User $user the user - * @return string URL of avatar - */ - public static function getAvatarFromUser( $user ) { - $avatar = null; - if ( $GLOBALS['wgCommentStreamsUserAvatarPropertyName'] !== null ) { - $avatar = self::getUserProperty( $user, - $GLOBALS['wgCommentStreamsUserAvatarPropertyName'] ); - if ( $avatar !== null ) { - if ( gettype( $avatar ) === 'string' ) { - $avatar = Title::newFromText( $avatar ); - if ( $avatar === null ) { - return null; - } - } - if ( !get_class( $avatar ) === 'Title' ) { - return null; - } - if ( $avatar->isKnown() && $avatar->getNamespace() === NS_FILE ) { - if ( method_exists( MediaWikiServices::class, 'getRepoGroup' ) ) { - // MediaWiki 1.34+ - $file = MediaWikiServices::getInstance()->getRepoGroup() - ->findFile( $avatar ); - } else { - $file = wfFindFile( $avatar ); - } - if ( $file ) { - return $file->getFullUrl(); - } - } - } - } - return null; - } - - /** - * return the value of a property on a user page - * - * @param User $user the user - * @param string $propertyName the name of the property - * @return string|null the value of the property - */ - private static function getUserProperty( $user, $propertyName ) { - if ( defined( 'SMW_VERSION' ) ) { - $userpage = $user->getUserPage(); - if ( $userpage->exists() ) { - $store = \SMW\StoreFactory::getStore(); - $subject = \SMWDIWikiPage::newFromTitle( $userpage ); - $data = $store->getSemanticData( $subject ); - $property = \SMWDIProperty::newFromUserLabel( $propertyName ); - $values = $data->getPropertyValues( $property ); - if ( count( $values ) > 0 ) { - // this property should only have one value so pick the first one - $value = $values[0]; - if ( ( defined( 'SMWDataItem::TYPE_STRING' ) && - $value->getDIType() == SMWDataItem::TYPE_STRING ) || - $value->getDIType() == SMWDataItem::TYPE_BLOB ) { - return $value->getString(); - } elseif ( $value->getDIType() == SMWDataItem::TYPE_WIKIPAGE ) { - return $value->getTitle(); - } - } - } - } - return null; - } - - /** - * Used by Echo to locate the users watching a comment being replied to. - * @param EchoEvent $event the Echo event - * @return array array mapping user id to User object - */ - public static function locateUsersWatchingComment( $event ) { - $id = $event->getExtraParam( 'parent_id' ); - $wikipage = WikiPage::newFromId( $id ); - if ( $wikipage !== null ) { - $comment = self::newFromWikiPage( $wikipage ); - if ( $comment !== null ) { - return $comment->getWatchers(); - } - } - return []; - } } |