2020-05-15 17:19:03 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace EchoPush;
|
|
|
|
|
|
|
|
use CentralIdLookup;
|
|
|
|
use EchoAbstractMapper;
|
|
|
|
use IDatabase;
|
|
|
|
use MediaWiki\Storage\NameTableStore;
|
2020-08-12 21:33:06 +00:00
|
|
|
use OverflowException;
|
2020-05-15 17:19:03 +00:00
|
|
|
use User;
|
|
|
|
use Wikimedia\Rdbms\DBError;
|
|
|
|
|
|
|
|
class SubscriptionManager extends EchoAbstractMapper {
|
|
|
|
|
|
|
|
/** @var IDatabase */
|
|
|
|
private $dbw;
|
|
|
|
|
|
|
|
/** @var IDatabase */
|
|
|
|
private $dbr;
|
|
|
|
|
|
|
|
/** @var CentralIdLookup */
|
|
|
|
private $centralIdLookup;
|
|
|
|
|
|
|
|
/** @var NameTableStore */
|
|
|
|
private $pushProviderStore;
|
|
|
|
|
2020-08-12 21:33:06 +00:00
|
|
|
/** @var int */
|
|
|
|
private $maxSubscriptionsPerUser;
|
|
|
|
|
2020-05-15 17:19:03 +00:00
|
|
|
/**
|
|
|
|
* @param IDatabase $dbw primary DB connection (for writes)
|
|
|
|
* @param IDatabase $dbr replica DB connection (for reads)
|
|
|
|
* @param CentralIdLookup $centralIdLookup
|
|
|
|
* @param NameTableStore $pushProviderStore
|
2020-08-12 21:33:06 +00:00
|
|
|
* @param int $maxSubscriptionsPerUser
|
2020-05-15 17:19:03 +00:00
|
|
|
*/
|
|
|
|
public function __construct(
|
|
|
|
IDatabase $dbw,
|
|
|
|
IDatabase $dbr,
|
|
|
|
CentralIdLookup $centralIdLookup,
|
2020-08-12 21:33:06 +00:00
|
|
|
NameTableStore $pushProviderStore,
|
|
|
|
int $maxSubscriptionsPerUser
|
2020-05-15 17:19:03 +00:00
|
|
|
) {
|
|
|
|
parent::__construct();
|
|
|
|
$this->dbw = $dbw;
|
|
|
|
$this->dbr = $dbr;
|
|
|
|
$this->centralIdLookup = $centralIdLookup;
|
|
|
|
$this->pushProviderStore = $pushProviderStore;
|
2020-08-12 21:33:06 +00:00
|
|
|
$this->maxSubscriptionsPerUser = $maxSubscriptionsPerUser;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the configured maximum number of stored subscriptions per user.
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
public function getMaxSubscriptionsPerUser(): int {
|
|
|
|
return $this->maxSubscriptionsPerUser;
|
2020-05-15 17:19:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Store push subscription information for a user.
|
|
|
|
* @param User $user
|
|
|
|
* @param string $provider Provider name string (validated by presence in the PARAM_TYPE array)
|
|
|
|
* @param string $token Subscriber token provided by the push provider
|
2020-06-09 23:08:14 +00:00
|
|
|
* @return bool true if the subscription was created; false if the token already exists
|
2020-08-12 21:33:06 +00:00
|
|
|
* @throws OverflowException if the user already has >= the configured max subscriptions
|
2020-05-15 17:19:03 +00:00
|
|
|
*/
|
2020-06-09 23:08:14 +00:00
|
|
|
public function create( User $user, string $provider, string $token ): bool {
|
2020-08-12 21:33:06 +00:00
|
|
|
$centralId = $this->getCentralId( $user );
|
|
|
|
if ( $this->userHasMaxAllowedSubscriptions( $centralId ) ) {
|
|
|
|
throw new OverflowException( 'Max subscriptions exceeded' );
|
|
|
|
}
|
2020-05-15 17:19:03 +00:00
|
|
|
$this->dbw->insert(
|
|
|
|
'echo_push_subscription',
|
|
|
|
[
|
2020-08-12 21:33:06 +00:00
|
|
|
'eps_user' => $centralId,
|
2020-05-15 17:19:03 +00:00
|
|
|
'eps_provider' => $this->pushProviderStore->acquireId( $provider ),
|
|
|
|
'eps_token' => $token,
|
|
|
|
'eps_token_sha256' => hash( 'sha256', $token ),
|
|
|
|
'eps_updated' => $this->dbw->timestamp()
|
2020-06-07 00:59:39 +00:00
|
|
|
],
|
2020-06-09 23:08:14 +00:00
|
|
|
__METHOD__,
|
|
|
|
[ 'IGNORE' ]
|
2020-05-15 17:19:03 +00:00
|
|
|
);
|
2020-06-09 23:08:14 +00:00
|
|
|
return (bool)$this->dbw->affectedRows();
|
2020-05-15 17:19:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-08-12 21:33:06 +00:00
|
|
|
* Get full data for all registered subscriptions for a user (by central ID).
|
2020-05-22 22:02:34 +00:00
|
|
|
* @param int $centralId
|
2020-05-15 17:19:03 +00:00
|
|
|
* @return array array of Subscription objects
|
|
|
|
*/
|
2020-05-22 22:02:34 +00:00
|
|
|
public function getSubscriptionsForUser( int $centralId ) {
|
2020-05-15 17:19:03 +00:00
|
|
|
$res = $this->dbr->select(
|
2020-06-02 23:20:06 +00:00
|
|
|
[ 'echo_push_subscription', 'echo_push_provider' ],
|
2020-05-15 17:19:03 +00:00
|
|
|
'*',
|
2020-06-07 00:59:39 +00:00
|
|
|
[ 'eps_user' => $centralId ],
|
2020-06-02 23:20:06 +00:00
|
|
|
__METHOD__,
|
|
|
|
[],
|
|
|
|
[ 'echo_push_provider' => [ 'INNER JOIN', [ 'eps_provider = epp_id' ] ] ]
|
2020-05-15 17:19:03 +00:00
|
|
|
);
|
|
|
|
$result = [];
|
|
|
|
foreach ( $res as $row ) {
|
|
|
|
$result[] = Subscription::newFromRow( $row );
|
|
|
|
}
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete a push subscription for a user.
|
|
|
|
* Note: Selecting for the user in addition to the token should be redundant, since tokens
|
|
|
|
* are globally unique and user-specific, but it's probably safest to keep it as a sanity check.
|
|
|
|
* Also, currently the eps_user column is indexed but eps_token is not.
|
|
|
|
* @param User $user
|
|
|
|
* @param string $token Delete the subscription with this token
|
|
|
|
* @return int number of rows deleted
|
|
|
|
* @throws DBError
|
|
|
|
*/
|
|
|
|
public function delete( User $user, string $token ): int {
|
|
|
|
$this->dbw->delete(
|
|
|
|
'echo_push_subscription',
|
|
|
|
[
|
|
|
|
'eps_user' => $this->getCentralId( $user ),
|
|
|
|
'eps_token' => $token,
|
2020-06-07 00:59:39 +00:00
|
|
|
],
|
|
|
|
__METHOD__
|
2020-05-15 17:19:03 +00:00
|
|
|
);
|
|
|
|
return $this->dbw->affectedRows();
|
|
|
|
}
|
|
|
|
|
2020-08-12 21:33:06 +00:00
|
|
|
/**
|
|
|
|
* Get count of all registered subscriptions for a user (by central ID).
|
|
|
|
* @param int $centralId
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
private function getSubscriptionCountForUser( int $centralId ) {
|
|
|
|
return $this->dbr->selectRowCount(
|
|
|
|
'echo_push_subscription',
|
|
|
|
'eps_id',
|
|
|
|
[ 'eps_user' => $centralId ],
|
|
|
|
__METHOD__
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the central user has >= the configured maximum push subscriptions in the DB
|
|
|
|
* @param int $centralId
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
private function userHasMaxAllowedSubscriptions( int $centralId ): bool {
|
|
|
|
return $this->getSubscriptionCountForUser( $centralId ) >= $this->maxSubscriptionsPerUser;
|
|
|
|
}
|
|
|
|
|
2020-05-15 17:19:03 +00:00
|
|
|
/**
|
|
|
|
* Get the user's central ID.
|
|
|
|
* @param User $user
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
private function getCentralId( User $user ): int {
|
|
|
|
return $this->centralIdLookup->centralIdFromLocalUser(
|
|
|
|
$user,
|
|
|
|
CentralIdLookup::AUDIENCE_RAW
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|