Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
MessageGroupSubscriptionStore
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 5
90
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addSubscription
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 getSubscriptions
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
20
 removeSubscriptions
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 getGroupIdForDatabase
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\MessageGroupProcessing;
5
6use Wikimedia\Rdbms\IConnectionProvider;
7use Wikimedia\Rdbms\IResultWrapper;
8
9/**
10 * Store service for looking up and storing user subscriptions to message group
11 * @since 2024.04
12 * @license GPL-2.0-or-later
13 * @author Abijeet Patro
14 */
15class MessageGroupSubscriptionStore {
16    private const TABLE_NAME = 'translate_message_group_subscriptions';
17    /** @var int Match field tmgs_group byte length */
18    private const MAX_GROUP_LENGTH = 200;
19    private IConnectionProvider $connectionProvider;
20
21    public function __construct( IConnectionProvider $connectionProvider ) {
22        $this->connectionProvider = $connectionProvider;
23    }
24
25    public function addSubscription( string $groupId, int $userId ): void {
26        $this->connectionProvider->getPrimaryDatabase()->replace(
27            self::TABLE_NAME,
28            [ [ 'tmgs_group', 'tmgs_user_id' ] ],
29            [
30                'tmgs_group' => self::getGroupIdForDatabase( $groupId ),
31                'tmgs_user_id' => $userId,
32            ],
33            __METHOD__
34        );
35    }
36
37    public function getSubscriptions( ?array $groupIds, ?int $userId ): IResultWrapper {
38        $queryBuilder = $this->connectionProvider
39            ->getReplicaDatabase()
40            ->newSelectQueryBuilder()
41            ->select( [ 'tmgs_group', 'tmgs_user_id' ] )
42            ->from( self::TABLE_NAME )
43            ->caller( __METHOD__ );
44
45        if ( $groupIds !== null ) {
46            $dbGroupIds = [];
47            foreach ( $groupIds as $groupId ) {
48                $dbGroupIds[] = self::getGroupIdForDatabase( $groupId );
49            }
50            $queryBuilder->where( [ 'tmgs_group' => $dbGroupIds ] );
51        }
52
53        if ( $userId !== null ) {
54            $queryBuilder->andWhere( [ 'tmgs_user_id' => $userId ] );
55        }
56
57        return $queryBuilder->fetchResultSet();
58    }
59
60    public function removeSubscriptions( string $groupId, int $userId ): void {
61        $conditions = [
62            'tmgs_group' => $groupId,
63            'tmgs_user_id' => $userId
64        ];
65
66        $this->connectionProvider->getPrimaryDatabase()
67            ->delete(
68                self::TABLE_NAME,
69                $conditions,
70                __METHOD__
71            );
72    }
73
74    private static function getGroupIdForDatabase( string $groupId ): string {
75        // Check if length is more than 200 bytes
76        if ( strlen( $groupId ) <= self::MAX_GROUP_LENGTH ) {
77            return $groupId;
78        }
79
80        $hash = hash( 'md5', $groupId );
81        // We take 160 bytes of the original string and append the md5 hash (32 bytes)
82        return mb_strcut( $groupId, 0, 160 ) . '||' . $hash;
83    }
84}