Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 66 |
|
0.00% |
0 / 6 |
CRAP | |
0.00% |
0 / 1 |
UserMerger | |
0.00% |
0 / 66 |
|
0.00% |
0 / 6 |
272 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
2 | |||
getAccountFields | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
12 | |||
finalizeMerge | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
20 | |||
purgeTable | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
30 | |||
loadFromTreeRevision | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
loadFromRevision | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | |
3 | namespace Flow\Data\Utils; |
4 | |
5 | use BatchRowIterator; |
6 | use Flow\Data\ManagerGroup; |
7 | use Flow\DbFactory; |
8 | use Flow\Model\AbstractRevision; |
9 | use Flow\Model\PostRevision; |
10 | use Flow\Model\UUID; |
11 | use Iterator; |
12 | |
13 | class UserMerger { |
14 | /** |
15 | * @var DbFactory |
16 | */ |
17 | protected $dbFactory; |
18 | |
19 | /** |
20 | * @var ManagerGroup |
21 | */ |
22 | protected $storage; |
23 | |
24 | /** |
25 | * @var array[][] |
26 | */ |
27 | protected $config; |
28 | |
29 | /** |
30 | * @param DbFactory $dbFactory |
31 | * @param ManagerGroup $storage |
32 | */ |
33 | public function __construct( DbFactory $dbFactory, ManagerGroup $storage ) { |
34 | $this->dbFactory = $dbFactory; |
35 | $this->storage = $storage; |
36 | $this->config = [ |
37 | 'flow_tree_revision' => [ |
38 | 'pk' => [ 'tree_rev_id' ], |
39 | 'userColumns' => [ |
40 | 'tree_orig_user_id' => 'getCreatorTuple', |
41 | ], |
42 | 'load' => [ $this, 'loadFromTreeRevision' ], |
43 | ], |
44 | |
45 | 'flow_revision' => [ |
46 | 'pk' => [ 'rev_id' ], |
47 | 'userColumns' => [ |
48 | 'rev_user_id' => 'getUserTuple', |
49 | 'rev_mod_user_id' => 'getModeratedByTuple', |
50 | 'rev_edit_user_id' => 'getLastContentEditUserTuple', |
51 | ], |
52 | 'load' => [ $this, 'loadFromRevision' ], |
53 | 'loadColumns' => [ 'rev_type' ], |
54 | ], |
55 | ]; |
56 | } |
57 | |
58 | /** |
59 | * @return array |
60 | */ |
61 | public function getAccountFields() { |
62 | $fields = []; |
63 | $dbw = $this->dbFactory->getDB( DB_PRIMARY ); |
64 | foreach ( $this->config as $table => $config ) { |
65 | $row = [ |
66 | 'db' => $dbw, |
67 | $table, |
68 | ]; |
69 | foreach ( array_keys( $config['userColumns'] ) as $column ) { |
70 | $row[] = $column; |
71 | } |
72 | $fields[] = $row; |
73 | } |
74 | return $fields; |
75 | } |
76 | |
77 | /** |
78 | * Called after all databases have been updated. Needs to purge any |
79 | * cache that contained data about $oldUser |
80 | * |
81 | * @param int $oldUserId |
82 | * @param int $newUserId |
83 | */ |
84 | public function finalizeMerge( $oldUserId, $newUserId ) { |
85 | $dbw = $this->dbFactory->getDB( DB_PRIMARY ); |
86 | foreach ( $this->config as $table => $config ) { |
87 | foreach ( $config['userColumns'] as $column => $userTupleGetter ) { |
88 | $it = new BatchRowIterator( $dbw, $table, $config['pk'], 500 ); |
89 | // The database is migrated, so look for the new user id |
90 | $it->addConditions( [ $column => $newUserId ] ); |
91 | $it->setCaller( __METHOD__ ); |
92 | if ( isset( $config['loadColumns'] ) ) { |
93 | $it->setFetchColumns( $config['loadColumns'] ); |
94 | } |
95 | $this->purgeTable( $it, $oldUserId, $config['load'], $userTupleGetter ); |
96 | } |
97 | } |
98 | } |
99 | |
100 | /** |
101 | * @param Iterator $it |
102 | * @param int $oldUserId |
103 | * @param callable $callback Receives a single row, returns domain object or null |
104 | * @param string $userTupleGetter Method to call on domain object that will return |
105 | * a UserTuple instance. |
106 | */ |
107 | protected function purgeTable( Iterator $it, $oldUserId, $callback, $userTupleGetter ) { |
108 | foreach ( $it as $batch ) { |
109 | foreach ( $batch as $pkRow ) { |
110 | $obj = $callback( $pkRow ); |
111 | if ( !$obj ) { |
112 | continue; |
113 | } |
114 | // This is funny looking because the loaded objects may have come from |
115 | // the db with new user ids, or the cache with old user ids. |
116 | // We need to tweak this object to look like the old user ids and then |
117 | // purge caches so they get the old user id cache keys. |
118 | $tuple = $obj->$userTupleGetter(); |
119 | if ( !$tuple ) { |
120 | continue; |
121 | } |
122 | $tuple->id = $oldUserId; |
123 | $om = $this->storage->getStorage( get_class( $obj ) ); |
124 | $om->clear(); |
125 | $om->merge( $obj ); |
126 | $om->cachePurge( $obj ); |
127 | } |
128 | $this->storage->clear(); |
129 | } |
130 | } |
131 | |
132 | /** |
133 | * @param object $row Single row from database |
134 | * @return PostRevision|null |
135 | */ |
136 | protected function loadFromTreeRevision( $row ) { |
137 | return $this->storage->get( PostRevision::class, $row->tree_rev_id ); |
138 | } |
139 | |
140 | /** |
141 | * @param object $row Single row from database |
142 | * @return AbstractRevision|null |
143 | */ |
144 | protected function loadFromRevision( $row ) { |
145 | $revTypes = [ |
146 | 'header' => \Flow\Model\Header::class, |
147 | 'post-summary' => \Flow\Model\PostSummary::class, |
148 | 'post' => PostRevision::class, |
149 | ]; |
150 | if ( !isset( $revTypes[$row->rev_type] ) ) { |
151 | wfDebugLog( 'Flow', __METHOD__ . ': Unknown revision type ' . $row->rev_type . ' did not merge ' . |
152 | UUID::create( $row->rev_id )->getAlphadecimal() ); |
153 | return null; |
154 | } |
155 | |
156 | return $this->storage->get( $revTypes[$row->rev_type], $row->rev_id ); |
157 | } |
158 | } |