Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 76 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 2 |
| FlowUpdateWorkflowPageId | |
0.00% |
0 / 25 |
|
0.00% |
0 / 3 |
12 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
| doDbUpdates | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
2 | |||
| getUpdateKey | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| WorkflowPageIdUpdateGenerator | |
0.00% |
0 / 45 |
|
0.00% |
0 / 4 |
272 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| update | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
90 | |||
| createPage | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
12 | |||
| report | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
12 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace Flow\Maintenance; |
| 4 | |
| 5 | use BatchRowIterator; |
| 6 | use BatchRowUpdate; |
| 7 | use BatchRowWriter; |
| 8 | use Exception; |
| 9 | use Flow\Container; |
| 10 | use Flow\Model\UUID; |
| 11 | use Flow\Model\Workflow; |
| 12 | use Flow\OccupationController; |
| 13 | use MediaWiki\Language\Language; |
| 14 | use MediaWiki\Language\RawMessage; |
| 15 | use MediaWiki\Maintenance\LoggedUpdateMaintenance; |
| 16 | use MediaWiki\MediaWikiServices; |
| 17 | use MediaWiki\Status\Status; |
| 18 | use MediaWiki\StubObject\StubUserLang; |
| 19 | use MediaWiki\Title\Title; |
| 20 | use MediaWiki\WikiMap\WikiMap; |
| 21 | use RowUpdateGenerator; |
| 22 | use RuntimeException; |
| 23 | use stdClass; |
| 24 | use Wikimedia\Rdbms\IDBAccessObject; |
| 25 | |
| 26 | $IP = getenv( 'MW_INSTALL_PATH' ); |
| 27 | if ( $IP === false ) { |
| 28 | $IP = __DIR__ . '/../../..'; |
| 29 | } |
| 30 | |
| 31 | require_once "$IP/maintenance/Maintenance.php"; |
| 32 | |
| 33 | /** |
| 34 | * In some cases we have created workflow instances before the related Title |
| 35 | * has an ArticleID assigned to it. This goes through and sets that value |
| 36 | * |
| 37 | * @ingroup Maintenance |
| 38 | */ |
| 39 | class FlowUpdateWorkflowPageId extends LoggedUpdateMaintenance { |
| 40 | public function __construct() { |
| 41 | parent::__construct(); |
| 42 | $this->addDescription( "Update workflow_page_id with the page id of its specified ns/title" ); |
| 43 | $this->requireExtension( 'Flow' ); |
| 44 | $this->setBatchSize( 300 ); |
| 45 | } |
| 46 | |
| 47 | /** |
| 48 | * Assembles the update components, runs them, and reports |
| 49 | * on what they did |
| 50 | * @return true |
| 51 | */ |
| 52 | public function doDbUpdates() { |
| 53 | global $wgFlowCluster, $wgLang; |
| 54 | |
| 55 | $dbw = Container::get( 'db.factory' )->getDB( DB_PRIMARY ); |
| 56 | |
| 57 | $it = new BatchRowIterator( |
| 58 | $dbw, |
| 59 | 'flow_workflow', |
| 60 | 'workflow_id', |
| 61 | $this->getBatchSize() |
| 62 | ); |
| 63 | $it->setFetchColumns( [ '*' ] ); |
| 64 | $it->addConditions( [ |
| 65 | 'workflow_wiki' => WikiMap::getCurrentWikiId(), |
| 66 | ] ); |
| 67 | $it->setCaller( __METHOD__ ); |
| 68 | |
| 69 | $gen = new WorkflowPageIdUpdateGenerator( $wgLang ); |
| 70 | $writer = new BatchRowWriter( $dbw, 'flow_workflow', $wgFlowCluster ); |
| 71 | $writer->setCaller( __METHOD__ ); |
| 72 | $updater = new BatchRowUpdate( $it, $writer, $gen ); |
| 73 | |
| 74 | $updater->execute(); |
| 75 | |
| 76 | $this->output( $gen->report() ); |
| 77 | |
| 78 | return true; |
| 79 | } |
| 80 | |
| 81 | protected function getUpdateKey() { |
| 82 | return 'FlowUpdateWorkflowPageId'; |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | /** |
| 87 | * Looks at rows from the flow_workflow table and returns an update |
| 88 | * for the workflow_page_id field if necessary. |
| 89 | */ |
| 90 | class WorkflowPageIdUpdateGenerator implements RowUpdateGenerator { |
| 91 | /** |
| 92 | * @var Language|StubUserLang |
| 93 | */ |
| 94 | protected $lang; |
| 95 | /** @var int */ |
| 96 | protected $fixedCount = 0; |
| 97 | /** @var stdClass[] */ |
| 98 | protected $failures = []; |
| 99 | /** @var string[] */ |
| 100 | protected $warnings = []; |
| 101 | |
| 102 | /** |
| 103 | * @param Language|StubUserLang $lang |
| 104 | */ |
| 105 | public function __construct( $lang ) { |
| 106 | $this->lang = $lang; |
| 107 | } |
| 108 | |
| 109 | public function update( $row ) { |
| 110 | $title = Title::makeTitleSafe( $row->workflow_namespace, $row->workflow_title_text ); |
| 111 | if ( $title === null ) { |
| 112 | throw new RuntimeException( sprintf( |
| 113 | 'Could not create title for %s at %s:%s', |
| 114 | UUID::create( $row->workflow_id )->getAlphadecimal(), |
| 115 | $this->lang->getNsText( $row->workflow_namespace ) ?: $row->workflow_namespace, |
| 116 | $row->workflow_title_text |
| 117 | ) ); |
| 118 | } |
| 119 | |
| 120 | // at some point, we failed to create page entries for new workflows: only |
| 121 | // create that page if the workflow was stored with a 0 page id (otherwise, |
| 122 | // we could mistake the $title for a deleted page) |
| 123 | if ( (int)$row->workflow_page_id === 0 && $title->getArticleID() === 0 ) { |
| 124 | $workflow = Workflow::fromStorageRow( (array)$row ); |
| 125 | $status = $this->createPage( $title, $workflow ); |
| 126 | if ( !$status->isGood() ) { |
| 127 | // just warn when we failed to create the page, but keep this code |
| 128 | // going and see if we manage to associate the workflow anyways |
| 129 | // (or if that fails, we'll also get an error there) |
| 130 | $this->warnings[] = $status->getMessage()->text(); |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | // re-associate the workflow with the correct page; only if a page exists |
| 135 | if ( $title->getArticleID() !== 0 && $title->getArticleID() !== (int)$row->workflow_page_id ) { |
| 136 | // This makes the assumption the page has not moved or been deleted? |
| 137 | ++$this->fixedCount; |
| 138 | return [ |
| 139 | 'workflow_page_id' => $title->getArticleID(), |
| 140 | ]; |
| 141 | } elseif ( !$row->workflow_page_id ) { |
| 142 | // No id exists for this workflow? (reason should likely show up in $this->warnings) |
| 143 | $this->failures[] = $row; |
| 144 | } |
| 145 | |
| 146 | return []; |
| 147 | } |
| 148 | |
| 149 | /** |
| 150 | * @param Title $title |
| 151 | * @param Workflow $workflow |
| 152 | * @return Status |
| 153 | */ |
| 154 | protected function createPage( Title $title, $workflow ) { |
| 155 | /** @var OccupationController $occupationController */ |
| 156 | $occupationController = Container::get( 'occupation_controller' ); |
| 157 | |
| 158 | try { |
| 159 | $status = $occupationController->safeAllowCreation( $title, $occupationController->getTalkpageManager() ); |
| 160 | $status2 = $occupationController->ensureFlowRevision( |
| 161 | MediaWikiServices::getInstance()->getWikiPageFactory()->newFromTitle( $title ), |
| 162 | $workflow |
| 163 | ); |
| 164 | |
| 165 | $status->merge( $status2 ); |
| 166 | } catch ( Exception $e ) { |
| 167 | // "convert" exception into Status |
| 168 | $message = new RawMessage( $e->getMessage() ); |
| 169 | $status = Status::newFatal( $message ); |
| 170 | } |
| 171 | |
| 172 | if ( $status->isGood() ) { |
| 173 | // force article id to be refetched from db |
| 174 | $title->getArticleID( IDBAccessObject::READ_LATEST ); |
| 175 | } |
| 176 | |
| 177 | return $status; |
| 178 | } |
| 179 | |
| 180 | public function report() { |
| 181 | $ret = "Updated {$this->fixedCount} workflows\n\n"; |
| 182 | |
| 183 | $warningsCount = count( $this->warnings ); |
| 184 | $ret .= "Warnings: {$warningsCount}\n"; |
| 185 | if ( $warningsCount > 0 ) { |
| 186 | $ret .= print_r( $this->warnings, true ) . "\n\n"; |
| 187 | } |
| 188 | $failureCount = count( $this->failures ); |
| 189 | $ret .= "Failed: {$failureCount}\n"; |
| 190 | if ( $failureCount > 0 ) { |
| 191 | $ret .= print_r( $this->failures, true ); |
| 192 | } |
| 193 | |
| 194 | return $ret; |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | $maintClass = FlowUpdateWorkflowPageId::class; |
| 199 | require_once RUN_MAINTENANCE_IF_MAIN; |