1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace WikibaseQuality\ConstraintReport\Maintenance; |
4
|
|
|
|
5
|
|
|
use Config; |
6
|
|
|
use Deserializers\Deserializer; |
7
|
|
|
use Maintenance; |
8
|
|
|
use Serializers\Serializer; |
9
|
|
|
use User; |
10
|
|
|
use Wikibase\DataModel\Entity\Item; |
11
|
|
|
use Wikibase\DataModel\SiteLinkList; |
12
|
|
|
use Wikibase\DataModel\Statement\StatementListProvider; |
13
|
|
|
use Wikibase\Lib\Store\EntityStore; |
14
|
|
|
use Wikibase\Lib\Store\StorageException; |
15
|
|
|
use Wikibase\Repo\WikibaseRepo; |
16
|
|
|
|
17
|
|
|
// @codeCoverageIgnoreStart |
18
|
|
|
$basePath = getenv( "MW_INSTALL_PATH" ) !== false |
19
|
|
|
? getenv( "MW_INSTALL_PATH" ) : __DIR__ . "/../../.."; |
20
|
|
|
|
21
|
|
|
require_once $basePath . "/maintenance/Maintenance.php"; |
22
|
|
|
// @codeCoverageIgnoreEnd |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Imports entities needed for constraint checks from Wikidata into the local repository. |
26
|
|
|
* |
27
|
|
|
* @license GPL-2.0-or-later |
28
|
|
|
*/ |
29
|
|
|
class ImportConstraintEntities extends Maintenance { |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var Serializer |
33
|
|
|
*/ |
34
|
|
|
private $entitySerializer; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var Deserializer |
38
|
|
|
*/ |
39
|
|
|
private $entityDeserializer; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @var EntityStore |
43
|
|
|
*/ |
44
|
|
|
private $entityStore; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @var User |
48
|
|
|
*/ |
49
|
|
|
private $user; |
50
|
|
|
|
51
|
|
|
public function __construct() { |
52
|
|
|
parent::__construct(); |
53
|
|
|
|
54
|
|
|
$this->addDescription( |
55
|
|
|
'Import entities needed for constraint checks ' . |
56
|
|
|
'from Wikidata into the local repository.' |
57
|
|
|
); |
58
|
|
|
$this->addOption( |
59
|
|
|
'config-format', |
60
|
|
|
'The format in which the resulting configuration will be omitted: ' . |
61
|
|
|
'"globals" for directly settings global variables, suitable for inclusion in LocalSettings.php (default), ' . |
62
|
|
|
'or "wgConf" for printing parts of arrays suitable for inclusion in $wgConf->settings.' |
63
|
|
|
); |
64
|
|
|
$this->addOption( |
65
|
|
|
'dry-run', |
66
|
|
|
'Don’t actually import entities, just print which ones would be imported.' |
67
|
|
|
); |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* (This cannot happen in the constructor because the autoloader is not yet initialized there.) |
72
|
|
|
*/ |
73
|
|
|
private function setupServices() { |
74
|
|
|
$repo = WikibaseRepo::getDefaultInstance(); |
75
|
|
|
$this->entitySerializer = $repo->getAllTypesEntitySerializer(); |
76
|
|
|
$this->entityDeserializer = $repo->getInternalFormatEntityDeserializer(); |
77
|
|
|
$this->entityStore = $repo->getEntityStore(); |
78
|
|
|
$this->user = User::newSystemUser( 'WikibaseQualityConstraints importer' ); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
public function execute() { |
82
|
|
|
$this->setupServices(); |
83
|
|
|
|
84
|
|
|
$configUpdates = []; |
85
|
|
|
|
86
|
|
|
$extensionJsonFile = __DIR__ . '/../extension.json'; |
87
|
|
|
$extensionJsonText = file_get_contents( $extensionJsonFile ); |
88
|
|
|
$extensionJson = json_decode( $extensionJsonText, /* assoc = */ true ); |
89
|
|
|
$wikidataEntityIds = $this->getEntitiesToImport( $extensionJson['config'], $this->getConfig() ); |
90
|
|
|
|
91
|
|
|
foreach ( $wikidataEntityIds as $key => $wikidataEntityId ) { |
92
|
|
|
$localEntityId = $this->importEntityFromWikidata( $wikidataEntityId ); |
93
|
|
|
$configUpdates[$key] = [ |
94
|
|
|
'wikidata' => $wikidataEntityId, |
95
|
|
|
'local' => $localEntityId, |
96
|
|
|
]; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
$this->outputConfigUpdates( $configUpdates ); |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* @param array $extensionJsonConfig |
104
|
|
|
* @param Config $wikiConfig |
105
|
|
|
* @return string[] |
106
|
|
|
*/ |
107
|
|
|
private function getEntitiesToImport( array $extensionJsonConfig, Config $wikiConfig ) { |
108
|
|
|
$wikidataEntityIds = []; |
109
|
|
|
|
110
|
|
|
foreach ( $extensionJsonConfig as $key => $value ) { |
111
|
|
|
if ( !preg_match( '/Id$/', $key ) ) { |
112
|
|
|
continue; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
$wikidataEntityId = $value['value']; |
116
|
|
|
$localEntityId = $wikiConfig->get( $key ); |
117
|
|
|
|
118
|
|
|
if ( $localEntityId === $wikidataEntityId ) { |
119
|
|
|
$wikidataEntityIds[$key] = $wikidataEntityId; |
120
|
|
|
} |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
return $wikidataEntityIds; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* @param string $wikidataEntityId |
128
|
|
|
* @return string local entity ID |
129
|
|
|
*/ |
130
|
|
|
private function importEntityFromWikidata( $wikidataEntityId ) { |
131
|
|
|
$wikidataEntityUrl = "https://www.wikidata.org/wiki/Special:EntityData/$wikidataEntityId.json"; |
132
|
|
|
$wikidataEntitiesJson = file_get_contents( $wikidataEntityUrl ); |
133
|
|
|
return $this->importEntityFromJson( $wikidataEntityId, $wikidataEntitiesJson ); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* @param string $wikidataEntityId |
138
|
|
|
* @param string $wikidataEntitiesJson |
139
|
|
|
* @return string local entity ID |
140
|
|
|
*/ |
141
|
|
|
private function importEntityFromJson( $wikidataEntityId, $wikidataEntitiesJson ) { |
142
|
|
|
$wikidataEntityArray = json_decode( $wikidataEntitiesJson, true )['entities'][$wikidataEntityId]; |
143
|
|
|
$wikidataEntity = $this->entityDeserializer->deserialize( $wikidataEntityArray ); |
144
|
|
|
|
145
|
|
|
$wikidataEntity->setId( null ); |
146
|
|
|
|
147
|
|
|
if ( $wikidataEntity instanceof StatementListProvider ) { |
148
|
|
|
$wikidataEntity->getStatements()->clear(); |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
if ( $wikidataEntity instanceof Item ) { |
152
|
|
|
$wikidataEntity->setSiteLinkList( new SiteLinkList() ); |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
if ( $this->getOption( 'dry-run', false ) ) { |
156
|
|
|
$wikidataEntityJson = json_encode( $this->entitySerializer->serialize( $wikidataEntity ) ); |
157
|
|
|
$this->output( $wikidataEntityJson . "\n" ); |
158
|
|
|
return "-$wikidataEntityId"; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
try { |
162
|
|
|
$localEntity = $this->entityStore->saveEntity( |
163
|
|
|
$wikidataEntity, |
164
|
|
|
"imported from [[wikidata:$wikidataEntityId]]", |
165
|
|
|
$this->user, |
166
|
|
|
EDIT_NEW | EDIT_FORCE_BOT |
167
|
|
|
)->getEntity(); |
168
|
|
|
|
169
|
|
|
return $localEntity->getId()->getSerialization(); |
170
|
|
|
} catch ( StorageException $storageException ) { |
|
|
|
|
171
|
|
|
$message = $storageException->getMessage(); |
172
|
|
|
// example message: |
173
|
|
|
// * Item [[Item:Q475|Q475]] already has label "as references" associated with language code en, using the same description text. |
174
|
|
|
// note that the label and language code may vary (conflicts in any language), |
175
|
|
|
// and that the item link may or may not be in the main namespace |
176
|
|
|
$pattern = '/[[|]([^]|]*)]] already has label .* using the same description text/'; |
177
|
|
|
if ( preg_match( $pattern, $message, $matches ) ) { |
178
|
|
|
return $matches[1]; |
179
|
|
|
} else { |
180
|
|
|
throw $storageException; |
181
|
|
|
} |
182
|
|
|
} |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
private function outputConfigUpdates( array $configUpdates ) { |
186
|
|
|
$configFormat = $this->getOption( 'config-format', 'globals' ); |
187
|
|
|
switch ( $configFormat ) { |
188
|
|
|
case 'globals': |
189
|
|
|
$this->outputConfigUpdatesGlobals( $configUpdates ); |
190
|
|
|
break; |
191
|
|
|
case 'wgConf': |
192
|
|
|
$this->outputConfigUpdatesWgConf( $configUpdates ); |
193
|
|
|
break; |
194
|
|
|
default: |
195
|
|
|
$this->error( "Invalid config format \"$configFormat\", using \"globals\"" ); |
196
|
|
|
$this->outputConfigUpdatesGlobals( $configUpdates ); |
197
|
|
|
break; |
198
|
|
|
} |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
private function outputConfigUpdatesGlobals( array $configUpdates ) { |
202
|
|
|
foreach ( $configUpdates as $key => $value ) { |
203
|
|
|
$localValueCode = var_export( $value['local'], true ); |
204
|
|
|
$this->output( "\$wg$key = $localValueCode;\n" ); |
205
|
|
|
} |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
private function outputConfigUpdatesWgConf( array $configUpdates ) { |
209
|
|
|
foreach ( $configUpdates as $key => $value ) { |
210
|
|
|
$keyCode = var_export( $key, true ); |
211
|
|
|
$wikidataValueCode = var_export( $value['wikidata'], true ); |
212
|
|
|
$localValueCode = var_export( $value['local'], true ); |
213
|
|
|
$wikiIdCode = var_export( wfWikiID(), true ); |
214
|
|
|
$block = <<< EOF |
215
|
|
|
$keyCode => [ |
216
|
|
|
'default' => $wikidataValueCode, |
217
|
|
|
$wikiIdCode => $localValueCode, |
218
|
|
|
], |
219
|
|
|
|
220
|
|
|
|
221
|
|
|
EOF; |
222
|
|
|
$this->output( $block ); |
223
|
|
|
} |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
// @codeCoverageIgnoreStart |
229
|
|
|
$maintClass = ImportConstraintEntities::class; |
230
|
|
|
require_once RUN_MAINTENANCE_IF_MAIN; |
231
|
|
|
// @codeCoverageIgnoreEnd |
232
|
|
|
|
Scrutinizer analyzes your
composer.json
/composer.lock
file if available to determine the classes, and functions that are defined by your dependencies.It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.