Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 78 |
|
0.00% |
0 / 9 |
CRAP | |
0.00% |
0 / 2 |
| FlowFixLog | |
0.00% |
0 / 27 |
|
0.00% |
0 / 5 |
30 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
| getUpdateKey | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| doDBUpdates | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
2 | |||
| output | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| error | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| LogRowUpdateGenerator | |
0.00% |
0 / 45 |
|
0.00% |
0 / 4 |
306 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| update | |
0.00% |
0 / 29 |
|
0.00% |
0 / 1 |
156 | |||
| loadTopic | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| loadPost | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
12 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace Flow\Maintenance; |
| 4 | |
| 5 | use BatchRowIterator; |
| 6 | use BatchRowUpdate; |
| 7 | use BatchRowWriter; |
| 8 | use Flow\Collection\PostCollection; |
| 9 | use Flow\Container; |
| 10 | use Flow\Data\ManagerGroup; |
| 11 | use Flow\Exception\InvalidDataException; |
| 12 | use Flow\Model\PostRevision; |
| 13 | use Flow\Model\UUID; |
| 14 | use MediaWiki\Maintenance\LoggedUpdateMaintenance; |
| 15 | use RowUpdateGenerator; |
| 16 | |
| 17 | $IP = getenv( 'MW_INSTALL_PATH' ); |
| 18 | if ( $IP === false ) { |
| 19 | $IP = __DIR__ . '/../../..'; |
| 20 | } |
| 21 | |
| 22 | require_once "$IP/maintenance/Maintenance.php"; |
| 23 | |
| 24 | /** |
| 25 | * Fixes Flow log entries. |
| 26 | * |
| 27 | * @ingroup Maintenance |
| 28 | */ |
| 29 | class FlowFixLog extends LoggedUpdateMaintenance { |
| 30 | public function __construct() { |
| 31 | parent::__construct(); |
| 32 | |
| 33 | $this->addDescription( 'Fixes Flow log entries' ); |
| 34 | |
| 35 | $this->setBatchSize( 300 ); |
| 36 | |
| 37 | $this->requireExtension( 'Flow' ); |
| 38 | } |
| 39 | |
| 40 | protected function getUpdateKey() { |
| 41 | return 'FlowFixLog:version2'; |
| 42 | } |
| 43 | |
| 44 | protected function doDBUpdates() { |
| 45 | $iterator = new BatchRowIterator( $this->getReplicaDB(), 'logging', 'log_id', $this->getBatchSize() ); |
| 46 | $iterator->setFetchColumns( [ 'log_id', 'log_params' ] ); |
| 47 | $iterator->addConditions( [ |
| 48 | 'log_type' => [ 'delete', 'suppress' ], |
| 49 | 'log_action' => [ |
| 50 | 'flow-delete-post', 'flow-suppress-post', 'flow-restore-post', |
| 51 | 'flow-delete-topic', 'flow-suppress-topic', 'flow-restore-topic', |
| 52 | ], |
| 53 | ] ); |
| 54 | $iterator->setCaller( __METHOD__ ); |
| 55 | |
| 56 | $writer = new BatchRowWriter( $this->getPrimaryDB(), 'logging' ); |
| 57 | $writer->setCaller( __METHOD__ ); |
| 58 | |
| 59 | $updater = new BatchRowUpdate( |
| 60 | $iterator, |
| 61 | $writer, |
| 62 | new LogRowUpdateGenerator( $this ) |
| 63 | ); |
| 64 | $updater->setOutput( [ $this, 'output' ] ); |
| 65 | $updater->execute(); |
| 66 | |
| 67 | return true; |
| 68 | } |
| 69 | |
| 70 | /** |
| 71 | * parent::output() is a protected method, only way to access it from a |
| 72 | * callback in php5.3 is to make a public function. In 5.4 can replace with |
| 73 | * a Closure. |
| 74 | * |
| 75 | * @param string $out |
| 76 | * @param string|null $channel |
| 77 | */ |
| 78 | public function output( $out, $channel = null ) { |
| 79 | parent::output( $out, $channel ); |
| 80 | } |
| 81 | |
| 82 | /** |
| 83 | * parent::error() is a protected method, only way to access it from the |
| 84 | * outside is to make it public. |
| 85 | * |
| 86 | * @param string $err |
| 87 | * @param int $die |
| 88 | */ |
| 89 | public function error( $err, $die = 0 ) { |
| 90 | parent::error( $err, $die ); |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | class LogRowUpdateGenerator implements RowUpdateGenerator { |
| 95 | /** |
| 96 | * @var FlowFixLog |
| 97 | */ |
| 98 | protected $maintenance; |
| 99 | |
| 100 | public function __construct( FlowFixLog $maintenance ) { |
| 101 | $this->maintenance = $maintenance; |
| 102 | } |
| 103 | |
| 104 | public function update( $row ) { |
| 105 | $updates = []; |
| 106 | $logId = (int)$row->log_id; |
| 107 | |
| 108 | $params = unserialize( $row->log_params ); |
| 109 | if ( !$params ) { |
| 110 | $this->maintenance->error( "Failed to unserialize log_params for log_id $logId" ); |
| 111 | return []; |
| 112 | } |
| 113 | |
| 114 | $topic = false; |
| 115 | $post = false; |
| 116 | if ( isset( $params['topicId'] ) ) { |
| 117 | $topic = $this->loadTopic( UUID::create( $params['topicId'] ) ); |
| 118 | } |
| 119 | if ( isset( $params['postId'] ) ) { |
| 120 | $post = $this->loadPost( UUID::create( $params['postId'] ) ); |
| 121 | $topic = $topic ?: $post->getRoot(); |
| 122 | } |
| 123 | |
| 124 | if ( !$topic ) { |
| 125 | $this->maintenance->error( "Missing topicId & postId for log_id $logId" ); |
| 126 | return []; |
| 127 | } |
| 128 | |
| 129 | try { |
| 130 | // log_namespace & log_title used to be board, should be topic |
| 131 | $updates['log_namespace'] = $topic->getTitle()->getNamespace(); |
| 132 | $updates['log_title'] = $topic->getTitle()->getDBkey(); |
| 133 | } catch ( \Exception ) { |
| 134 | $this->maintenance->error( "Couldn't load Title for log_id $logId" ); |
| 135 | $updates = []; |
| 136 | } |
| 137 | |
| 138 | if ( isset( $params['postId'] ) && $post ) { |
| 139 | // posts used to save revision id instead of post id, let's make |
| 140 | // sure it's actually the post id that's being saved!... |
| 141 | $params['postId'] = $post->getId(); |
| 142 | } |
| 143 | |
| 144 | if ( !isset( $params['topicId'] ) ) { |
| 145 | // posts didn't use to also store topicId, but we'll be using it to |
| 146 | // enrich log entries' output - might as well store it right away |
| 147 | $params['topicId'] = $topic->getId(); |
| 148 | } |
| 149 | |
| 150 | // we used to save (serialized) UUID objects; now we just save the |
| 151 | // alphanumeric representation |
| 152 | foreach ( $params as $key => $value ) { |
| 153 | $params[$key] = $value instanceof UUID ? $value->getAlphadecimal() : $value; |
| 154 | } |
| 155 | |
| 156 | // re-serialize params (UUID used to serialize more verbose; might |
| 157 | // as well shrink that down now that we're updating anyway...) |
| 158 | $updates['log_params'] = serialize( $params ); |
| 159 | |
| 160 | return $updates; |
| 161 | } |
| 162 | |
| 163 | /** |
| 164 | * @param UUID $topicId |
| 165 | * @return PostCollection |
| 166 | */ |
| 167 | protected function loadTopic( UUID $topicId ) { |
| 168 | return PostCollection::newFromId( $topicId ); |
| 169 | } |
| 170 | |
| 171 | /** |
| 172 | * @param UUID $postId |
| 173 | * @return PostCollection|false |
| 174 | */ |
| 175 | protected function loadPost( UUID $postId ) { |
| 176 | try { |
| 177 | $collection = PostCollection::newFromId( $postId ); |
| 178 | |
| 179 | // validate collection by attempting to fetch latest revision - if |
| 180 | // this fails (likely will for old data), catch will be invoked |
| 181 | $collection->getLastRevision(); |
| 182 | return $collection; |
| 183 | } catch ( InvalidDataException ) { |
| 184 | // posts used to mistakenly store revision ID instead of post ID |
| 185 | |
| 186 | /** @var ManagerGroup $storage */ |
| 187 | $storage = Container::get( 'storage' ); |
| 188 | $result = $storage->find( |
| 189 | 'PostRevision', |
| 190 | [ 'rev_id' => $postId ], |
| 191 | [ 'LIMIT' => 1 ] |
| 192 | ); |
| 193 | |
| 194 | if ( $result ) { |
| 195 | /** @var PostRevision $revision */ |
| 196 | $revision = reset( $result ); |
| 197 | |
| 198 | // now build collection from real post ID |
| 199 | return $this->loadPost( $revision->getPostId() ); |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | return false; |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | $maintClass = FlowFixLog::class; |
| 208 | require_once RUN_MAINTENANCE_IF_MAIN; |