Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
35.71% |
15 / 42 |
|
25.00% |
1 / 4 |
CRAP | |
0.00% |
0 / 1 |
| LogStore | |
35.71% |
15 / 42 |
|
25.00% |
1 / 4 |
43.15 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| thank | |
87.50% |
14 / 16 |
|
0.00% |
0 / 1 |
4.03 | |||
| haveThanked | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
2 | |||
| getLogEntryFromId | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
30 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace MediaWiki\Extension\Thanks\Storage; |
| 4 | |
| 5 | use InvalidArgumentException; |
| 6 | use MediaWiki\CheckUser\Services\CheckUserInsert; |
| 7 | use MediaWiki\Config\ServiceOptions; |
| 8 | use MediaWiki\Extension\Thanks\Storage\Exceptions\InvalidLogType; |
| 9 | use MediaWiki\Extension\Thanks\Storage\Exceptions\LogDeleted; |
| 10 | use MediaWiki\Logging\DatabaseLogEntry; |
| 11 | use MediaWiki\Logging\ManualLogEntry; |
| 12 | use MediaWiki\MediaWikiServices; |
| 13 | use MediaWiki\Registration\ExtensionRegistry; |
| 14 | use MediaWiki\User\ActorNormalization; |
| 15 | use MediaWiki\User\User; |
| 16 | use Wikimedia\Rdbms\IConnectionProvider; |
| 17 | |
| 18 | /** |
| 19 | * Manages the storage for Thank events. |
| 20 | * Thanks are stored as logs with relations between users. |
| 21 | * Each thank action should have an unique ID generated by callers. |
| 22 | */ |
| 23 | class LogStore { |
| 24 | |
| 25 | public const CONSTRUCTOR_OPTIONS = [ 'ThanksLogging', 'ThanksAllowedLogTypes' ]; |
| 26 | |
| 27 | public function __construct( |
| 28 | protected readonly IConnectionProvider $conn, |
| 29 | protected readonly ActorNormalization $actorNormalization, |
| 30 | protected readonly ExtensionRegistry $extensionRegistry, |
| 31 | protected readonly ServiceOptions $serviceOptions, |
| 32 | ) { |
| 33 | $serviceOptions->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS ); |
| 34 | } |
| 35 | |
| 36 | /** |
| 37 | * @param User $user The user performing the thanks (and the log entry). |
| 38 | * @param User $recipient The target of the thanks (and the log entry). |
| 39 | * @param string $uniqueId A unique Id to identify the event being thanked for, to use |
| 40 | * when checking for duplicate thanks |
| 41 | */ |
| 42 | public function thank( User $user, User $recipient, string $uniqueId ): void { |
| 43 | if ( $user->isTemp() ) { |
| 44 | throw new InvalidArgumentException( |
| 45 | 'Temporary accounts may not thank other users.' |
| 46 | ); |
| 47 | } |
| 48 | if ( !$this->serviceOptions->get( 'ThanksLogging' ) ) { |
| 49 | return; |
| 50 | } |
| 51 | $logEntry = new ManualLogEntry( 'thanks', 'thank' ); |
| 52 | $logEntry->setPerformer( $user ); |
| 53 | $logEntry->setRelations( [ 'thankid' => $uniqueId ] ); |
| 54 | $target = $recipient->getUserPage(); |
| 55 | $logEntry->setTarget( $target ); |
| 56 | $logId = $logEntry->insert(); |
| 57 | $logEntry->publish( $logId, 'udp' ); |
| 58 | |
| 59 | if ( $this->extensionRegistry->isLoaded( 'CheckUser' ) ) { |
| 60 | // TODO: This should be done in a separate hook handler |
| 61 | /** @var CheckUserInsert $checkUserInsert */ |
| 62 | $checkUserInsert = MediaWikiServices::getInstance()->get( 'CheckUserInsert' ); |
| 63 | $checkUserInsert->updateCheckUserData( $logEntry->getRecentChange( $logId ) ); |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | /** |
| 68 | * This checks the log_search data. |
| 69 | * |
| 70 | * @param User $thanker The user sending the thanks. |
| 71 | * @param string $uniqueId The identifier for the thanks. |
| 72 | * @return bool Whether thanks has already been sent |
| 73 | */ |
| 74 | public function haveThanked( User $thanker, string $uniqueId ): bool { |
| 75 | // TODO: Figure out why it's not getting the data from a replica |
| 76 | $dbw = $this->conn->getPrimaryDatabase(); |
| 77 | $thankerActor = $this->actorNormalization->acquireActorId( $thanker, $dbw ); |
| 78 | return (bool)$dbw->newSelectQueryBuilder() |
| 79 | ->select( 'ls_value' ) |
| 80 | ->from( 'log_search' ) |
| 81 | ->join( 'logging', null, [ 'ls_log_id=log_id' ] ) |
| 82 | ->where( |
| 83 | [ |
| 84 | 'log_actor' => $thankerActor, |
| 85 | 'ls_field' => 'thankid', |
| 86 | 'ls_value' => $uniqueId, |
| 87 | ] |
| 88 | ) |
| 89 | ->caller( __METHOD__ ) |
| 90 | ->fetchRow(); |
| 91 | } |
| 92 | |
| 93 | /** |
| 94 | * @throws InvalidLogType |
| 95 | * @throws LogDeleted |
| 96 | */ |
| 97 | public function getLogEntryFromId( int $logId ): ?DatabaseLogEntry { |
| 98 | $logEntry = DatabaseLogEntry::newFromId( $logId, $this->conn->getPrimaryDatabase() ); |
| 99 | |
| 100 | if ( !$logEntry ) { |
| 101 | return null; |
| 102 | } |
| 103 | |
| 104 | // Make sure this log type is allowed. |
| 105 | $allowedLogTypes = $this->serviceOptions->get( 'ThanksAllowedLogTypes' ); |
| 106 | if ( !in_array( $logEntry->getType(), $allowedLogTypes ) |
| 107 | && !in_array( $logEntry->getType() . '/' . $logEntry->getSubtype(), $allowedLogTypes ) ) { |
| 108 | throw new InvalidLogType( $logEntry->getType() ); |
| 109 | } |
| 110 | |
| 111 | // Don't permit thanks if any part of the log entry is deleted. |
| 112 | if ( $logEntry->getDeleted() ) { |
| 113 | throw new LogDeleted(); |
| 114 | } |
| 115 | return $logEntry; |
| 116 | } |
| 117 | } |