Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
90.24% |
74 / 82 |
|
75.00% |
3 / 4 |
CRAP | |
0.00% |
0 / 1 |
MigrateCampaigns | |
97.37% |
74 / 76 |
|
75.00% |
3 / 4 |
14 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
1 | |||
doDBUpdates | |
90.00% |
18 / 20 |
|
0.00% |
0 / 1 |
5.03 | |||
doCampaign | |
100.00% |
45 / 45 |
|
100.00% |
1 / 1 |
7 | |||
getUpdateKey | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\MediaUploader\Maintenance; |
4 | |
5 | use CommentStoreComment; |
6 | use LoggedUpdateMaintenance; |
7 | use MediaWiki\Extension\MediaUploader\Campaign\CampaignContent; |
8 | use MediaWiki\Extension\MediaUploader\MediaUploaderServices; |
9 | use MediaWiki\MediaWikiServices; |
10 | use MediaWiki\Page\ExistingPageRecord; |
11 | use MediaWiki\Revision\SlotRecord; |
12 | use Message; |
13 | use Symfony\Component\Yaml\Yaml; |
14 | use WikiPage; |
15 | |
16 | $IP = getenv( 'MW_INSTALL_PATH' ); |
17 | if ( $IP === false ) { |
18 | $IP = dirname( __DIR__, 3 ); |
19 | } |
20 | require_once "$IP/maintenance/Maintenance.php"; |
21 | |
22 | class MigrateCampaigns extends LoggedUpdateMaintenance { |
23 | |
24 | public function __construct() { |
25 | parent::__construct(); |
26 | $this->requireExtension( 'MediaUploader' ); |
27 | $this->addDescription( |
28 | "Inspects campaign definitions carried over from UploadWizard and\n" . |
29 | "converts them to YAML.\n" |
30 | ); |
31 | $this->addOption( |
32 | 'dry-run', |
33 | 'Do not convert campaign pages to YAML, just list the issues.' |
34 | ); |
35 | } |
36 | |
37 | /** |
38 | * @inheritDoc |
39 | */ |
40 | protected function doDBUpdates() { |
41 | $services = MediaWikiServices::getInstance(); |
42 | $pageStore = $services->getPageStore(); |
43 | $wikiPageFactory = $services->getWikiPageFactory(); |
44 | |
45 | $dbr = $this->getDB( DB_REPLICA ); |
46 | |
47 | $this->output( "Inspecting MediaUploader campaigns...\n" ); |
48 | $pageRecords = $pageStore->newSelectQueryBuilder( $dbr ) |
49 | ->whereNamespace( NS_CAMPAIGN ) |
50 | ->fetchPageRecords(); |
51 | |
52 | /** @var ExistingPageRecord $record */ |
53 | foreach ( $pageRecords as $record ) { |
54 | if ( $record->isRedirect() ) { |
55 | continue; |
56 | } |
57 | if ( $record->getDBkey() === CampaignContent::GLOBAL_CONFIG_ANCHOR_DBKEY ) { |
58 | continue; |
59 | } |
60 | |
61 | $page = $wikiPageFactory->newFromTitle( $record ); |
62 | $content = $page->getContent(); |
63 | if ( !( $content instanceof CampaignContent ) ) { |
64 | continue; |
65 | } |
66 | |
67 | $this->doCampaign( $content, $page ); |
68 | } |
69 | |
70 | $this->output( "\n\n" ); |
71 | return true; |
72 | } |
73 | |
74 | /** |
75 | * Applies fixes/prettification to a campaign. |
76 | * |
77 | * @param CampaignContent $content |
78 | * @param WikiPage $page |
79 | */ |
80 | private function doCampaign( CampaignContent $content, WikiPage $page ) { |
81 | $status = $content->getValidationStatus(); |
82 | $titleText = $page->getTitle()->getPrefixedText(); |
83 | $dryRun = $this->getOption( 'dry-run' ); |
84 | $toFix = 0; |
85 | |
86 | $this->output( "\n\n---- $titleText\n" ); |
87 | |
88 | if ( $status->isGood() ) { |
89 | $this->output( "VALID\n" ); |
90 | } |
91 | |
92 | $data = null; |
93 | if ( $content->getData()->isGood() ) { |
94 | // Parse the YAML again, but this time use objects to avoid |
95 | // PHP's array type ambiguity (associative vs list). |
96 | $data = Yaml::parse( |
97 | $content->getText(), |
98 | Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE |
99 | | Yaml::PARSE_OBJECT_FOR_MAP |
100 | ); |
101 | } |
102 | |
103 | foreach ( $status->getErrors() as $error ) { |
104 | $this->output( |
105 | wfMessage( $error['message'], ...$error['params'] )->text() . "\n" |
106 | ); |
107 | |
108 | $this->output( "ERROR: This must be fixed manually.\n" ); |
109 | $toFix++; |
110 | } |
111 | |
112 | if ( $toFix ) { |
113 | $this->output( "Issues to fix: $toFix" ); |
114 | } |
115 | |
116 | if ( $dryRun ) { |
117 | return; |
118 | } |
119 | |
120 | if ( $data ) { |
121 | // Save changes |
122 | $text = Yaml::dump( |
123 | $data, |
124 | 10, |
125 | 2, |
126 | Yaml::DUMP_OBJECT_AS_MAP | Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE |
127 | ); |
128 | // Symfony for some reason prefers to start objects in a new line following |
129 | // a dash (-). This is contrary to my preference, and it seems most YAML |
130 | // style guides out there, so this regex removes the extra newline. |
131 | $text = preg_replace( |
132 | '/^( *)-\n +([^-])/m', |
133 | '$1- $2', |
134 | $text |
135 | ); |
136 | $updater = $page->newPageUpdater( MediaUploaderServices::getSystemUser() ); |
137 | $content = new CampaignContent( $text ); |
138 | $updater->setContent( SlotRecord::MAIN, $content ); |
139 | $comment = CommentStoreComment::newUnsavedComment( |
140 | new Message( 'mediauploader-fix-campaign-comment-prettified' ) |
141 | ); |
142 | $updater->saveRevision( $comment, EDIT_UPDATE ); |
143 | $this->output( "Saved changes.\n" ); |
144 | } else { |
145 | $this->output( "Cannot process this campaign.\n" ); |
146 | } |
147 | } |
148 | |
149 | /** |
150 | * @inheritDoc |
151 | */ |
152 | protected function getUpdateKey() { |
153 | return 'migrate MediaUploader campaigns'; |
154 | } |
155 | } |
156 | |
157 | $maintClass = MigrateCampaigns::class; |
158 | |
159 | require_once RUN_MAINTENANCE_IF_MAIN; |