mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/DiscussionTools
synced 2024-12-11 08:08:37 +00:00
475aa80057
Previously, we have made a query per each topic on the page. Bug: T281000 Change-Id: I1029e62a65fc191ca37e1178ea7ffc55afafa1b9
316 lines
6.7 KiB
PHP
316 lines
6.7 KiB
PHP
<?php
|
|
|
|
namespace MediaWiki\Extension\DiscussionTools;
|
|
|
|
use MediaWiki\Linker\LinkTarget;
|
|
use MediaWiki\User\UserFactory;
|
|
use MediaWiki\User\UserIdentity;
|
|
use ReadOnlyMode;
|
|
use stdClass;
|
|
use TitleValue;
|
|
use Wikimedia\Rdbms\IDatabase;
|
|
use Wikimedia\Rdbms\ILBFactory;
|
|
use Wikimedia\Rdbms\ILoadBalancer;
|
|
use Wikimedia\Rdbms\IResultWrapper;
|
|
|
|
// use Wikimedia\ParamValidator\TypeDef\ExpiryDef;
|
|
// use Wikimedia\Timestamp\ConvertibleTimestamp;
|
|
|
|
class SubscriptionStore {
|
|
/** @var ILBFactory */
|
|
private $lbFactory;
|
|
|
|
/** @var ILoadBalancer */
|
|
private $loadBalancer;
|
|
|
|
/** @var ReadOnlyMode */
|
|
private $readOnlyMode;
|
|
|
|
/** @var UserFactory */
|
|
private $userFactory;
|
|
|
|
/**
|
|
* @param ILBFactory $lbFactory
|
|
* @param ReadOnlyMode $readOnlyMode
|
|
* @param UserFactory $userFactory
|
|
*/
|
|
public function __construct(
|
|
ILBFactory $lbFactory,
|
|
ReadOnlyMode $readOnlyMode,
|
|
UserFactory $userFactory
|
|
) {
|
|
$this->lbFactory = $lbFactory;
|
|
$this->loadBalancer = $lbFactory->getMainLB();
|
|
|
|
$this->userFactory = $userFactory;
|
|
$this->readOnlyMode = $readOnlyMode;
|
|
}
|
|
|
|
/**
|
|
* @param int $dbIndex DB_MASTER or DB_REPLICA
|
|
*
|
|
* @return IDatabase
|
|
*/
|
|
private function getConnectionRef( $dbIndex ) : IDatabase {
|
|
return $this->loadBalancer->getConnectionRef( $dbIndex, [ 'watchlist' ] );
|
|
}
|
|
|
|
/**
|
|
* @param IDatabase $db
|
|
* @param UserIdentity|null $user
|
|
* @param array|null $itemNames
|
|
* @param int|null $state
|
|
* @return IResultWrapper|false
|
|
*/
|
|
private function fetchSubscriptions(
|
|
IDatabase $db,
|
|
?UserIdentity $user = null,
|
|
?array $itemNames = null,
|
|
?int $state = null
|
|
) {
|
|
$conditions = [];
|
|
|
|
if ( $user ) {
|
|
$conditions[ 'sub_user' ] = $user->getId();
|
|
}
|
|
|
|
if ( $itemNames !== null ) {
|
|
$conditions[ 'sub_item' ] = $itemNames;
|
|
}
|
|
|
|
if ( $state !== null ) {
|
|
$conditions[ 'sub_state' ] = $state;
|
|
}
|
|
|
|
return $db->select(
|
|
'discussiontools_subscription',
|
|
[
|
|
'sub_user', 'sub_item', 'sub_namespace', 'sub_title', 'sub_section', 'sub_state',
|
|
'sub_created', 'sub_notified'
|
|
],
|
|
$conditions,
|
|
__METHOD__
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param UserIdentity $user
|
|
* @param array|null $itemNames
|
|
* @param int|null $state
|
|
* @param array $options
|
|
* @return SubscriptionItem[]
|
|
*/
|
|
public function getSubscriptionItemsForUser(
|
|
UserIdentity $user,
|
|
?array $itemNames = null,
|
|
?int $state = null,
|
|
array $options = []
|
|
) : array {
|
|
// Only a registered user can be subscribed
|
|
if ( !$user->isRegistered() ) {
|
|
return [];
|
|
}
|
|
|
|
$options += [ 'forWrite' => false ];
|
|
$db = $this->getConnectionRef( $options['forWrite'] ? DB_MASTER : DB_REPLICA );
|
|
|
|
$rows = $this->fetchSubscriptions(
|
|
$db,
|
|
$user,
|
|
$itemNames,
|
|
$state
|
|
);
|
|
|
|
if ( !$rows ) {
|
|
return [];
|
|
}
|
|
|
|
$items = [];
|
|
foreach ( $rows as $row ) {
|
|
$target = new TitleValue( (int)$row->sub_namespace, $row->sub_title, $row->sub_section );
|
|
$items[] = $this->getSubscriptionItemFromRow( $user, $target, $row );
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
/**
|
|
* @param string $itemName
|
|
* @param int|null $state
|
|
* @param array $options
|
|
* @return array
|
|
*/
|
|
public function getSubscriptionItemsForTopic(
|
|
string $itemName,
|
|
?int $state = null,
|
|
array $options = []
|
|
) : array {
|
|
$options += [ 'forWrite' => false ];
|
|
$db = $this->getConnectionRef( $options['forWrite'] ? DB_MASTER : DB_REPLICA );
|
|
|
|
$rows = $this->fetchSubscriptions(
|
|
$db,
|
|
null,
|
|
[ $itemName ],
|
|
$state
|
|
);
|
|
|
|
if ( !$rows ) {
|
|
return [];
|
|
}
|
|
|
|
$items = [];
|
|
foreach ( $rows as $row ) {
|
|
$target = new TitleValue( (int)$row->sub_namespace, $row->sub_title, $row->sub_section );
|
|
$user = $this->userFactory->newFromId( $row->sub_user );
|
|
$items[] = $this->getSubscriptionItemFromRow( $user, $target, $row );
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
/**
|
|
* @param UserIdentity $user
|
|
* @param LinkTarget $target
|
|
* @param stdClass $row
|
|
* @return SubscriptionItem
|
|
*/
|
|
private function getSubscriptionItemFromRow(
|
|
UserIdentity $user,
|
|
LinkTarget $target,
|
|
stdClass $row
|
|
) : SubscriptionItem {
|
|
return new SubscriptionItem(
|
|
$user,
|
|
$row->sub_item,
|
|
$target,
|
|
$row->sub_state,
|
|
$row->sub_created,
|
|
$row->sub_notified
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param UserIdentity $user
|
|
* @param LinkTarget $target
|
|
* @param string $itemName
|
|
* @return bool
|
|
*/
|
|
public function addSubscriptionForUser(
|
|
UserIdentity $user,
|
|
LinkTarget $target,
|
|
string $itemName
|
|
) : bool {
|
|
if ( $this->readOnlyMode->isReadOnly() ) {
|
|
return false;
|
|
}
|
|
// Only a registered user can subscribe
|
|
if ( !$user->isRegistered() ) {
|
|
return false;
|
|
}
|
|
$dbw = $this->getConnectionRef( DB_MASTER );
|
|
$dbw->upsert(
|
|
'discussiontools_subscription',
|
|
[
|
|
'sub_user' => $user->getId(),
|
|
'sub_namespace' => $target->getNamespace(),
|
|
'sub_title' => $target->getDBkey(),
|
|
'sub_section' => $target->getFragment(),
|
|
'sub_item' => $itemName,
|
|
'sub_state' => 1,
|
|
'sub_created' => $dbw->timestamp(),
|
|
],
|
|
[ [ 'sub_user', 'sub_item' ] ],
|
|
[
|
|
'sub_state' => 1,
|
|
],
|
|
__METHOD__
|
|
);
|
|
return (bool)$dbw->affectedRows();
|
|
}
|
|
|
|
/**
|
|
* @param UserIdentity $user
|
|
* @param string $itemName
|
|
* @return bool
|
|
*/
|
|
public function removeSubscriptionForUser(
|
|
UserIdentity $user,
|
|
string $itemName
|
|
) : bool {
|
|
if ( $this->readOnlyMode->isReadOnly() ) {
|
|
return false;
|
|
}
|
|
// Only a registered user can subscribe
|
|
if ( !$user->isRegistered() ) {
|
|
return false;
|
|
}
|
|
$dbw = $this->getConnectionRef( DB_MASTER );
|
|
$dbw->update(
|
|
'discussiontools_subscription',
|
|
[ 'sub_state' => 0 ],
|
|
[
|
|
'sub_user' => $user->getId(),
|
|
'sub_item' => $itemName,
|
|
],
|
|
__METHOD__
|
|
);
|
|
return (bool)$dbw->affectedRows();
|
|
}
|
|
|
|
/**
|
|
* @param string $field Timestamp field name
|
|
* @param UserIdentity|null $user
|
|
* @param string $itemName
|
|
* @return bool
|
|
*/
|
|
private function updateSubscriptionTimestamp(
|
|
string $field,
|
|
?UserIdentity $user,
|
|
string $itemName
|
|
) : bool {
|
|
if ( $this->readOnlyMode->isReadOnly() ) {
|
|
return false;
|
|
}
|
|
$dbw = $this->getConnectionRef( DB_MASTER );
|
|
|
|
$conditions = [
|
|
'sub_item' => $itemName,
|
|
];
|
|
|
|
if ( $user ) {
|
|
$conditions[ 'sub_user' ] = $user->getId();
|
|
}
|
|
|
|
$dbw->update(
|
|
'discussiontools_subscription',
|
|
[ $field => $dbw->timestamp() ],
|
|
$conditions,
|
|
__METHOD__
|
|
);
|
|
return (bool)$dbw->affectedRows();
|
|
}
|
|
|
|
/**
|
|
* Update the notified timestamp on a subscription
|
|
*
|
|
* This field could be used in future to cleanup notifications
|
|
* that are no longer needed (e.g. because the conversation has
|
|
* been archived), so should be set for muted notifications too.
|
|
*
|
|
* @param UserIdentity|null $user
|
|
* @param string $itemName
|
|
* @return bool
|
|
*/
|
|
public function updateSubscriptionNotifiedTimestamp(
|
|
?UserIdentity $user,
|
|
string $itemName
|
|
) : bool {
|
|
return $this->updateSubscriptionTimestamp(
|
|
'sub_notified',
|
|
$user,
|
|
$itemName
|
|
);
|
|
}
|
|
}
|