|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
declare( strict_types = 1 ); |
|
4
|
|
|
|
|
5
|
|
|
namespace Wikibase\Repo\Api; |
|
6
|
|
|
|
|
7
|
|
|
use ApiMain; |
|
8
|
|
|
use Wikibase\DataModel\Entity\EntityDocument; |
|
9
|
|
|
use Wikibase\DataModel\Entity\Item; |
|
10
|
|
|
use Wikibase\DataModel\Entity\ItemId; |
|
11
|
|
|
use Wikibase\DataModel\SiteLinkList; |
|
12
|
|
|
use Wikibase\Lib\Summary; |
|
13
|
|
|
use Wikibase\Repo\ChangeOp\ChangeOp; |
|
14
|
|
|
use Wikibase\Repo\ChangeOp\ChangeOpException; |
|
15
|
|
|
use Wikibase\Repo\ChangeOp\ChangeOpValidationException; |
|
16
|
|
|
use Wikibase\Repo\ChangeOp\Deserialization\ChangeOpDeserializationException; |
|
17
|
|
|
use Wikibase\Repo\ChangeOp\Deserialization\SiteLinkBadgeChangeOpSerializationValidator; |
|
18
|
|
|
use Wikibase\Repo\ChangeOp\SiteLinkChangeOpFactory; |
|
19
|
|
|
use Wikibase\Repo\WikibaseRepo; |
|
20
|
|
|
|
|
21
|
|
|
/** |
|
22
|
|
|
* API module to associate a page on a site with a Wikibase entity or remove an already made such association. |
|
23
|
|
|
* Requires API write mode to be enabled. |
|
24
|
|
|
* |
|
25
|
|
|
* @license GPL-2.0-or-later |
|
26
|
|
|
*/ |
|
27
|
|
|
class SetSiteLink extends ModifyEntity { |
|
28
|
|
|
|
|
29
|
|
|
/** |
|
30
|
|
|
* @var SiteLinkChangeOpFactory |
|
31
|
|
|
*/ |
|
32
|
|
|
private $siteLinkChangeOpFactory; |
|
33
|
|
|
|
|
34
|
|
|
/** |
|
35
|
|
|
* @var SiteLinkBadgeChangeOpSerializationValidator |
|
36
|
|
|
*/ |
|
37
|
|
|
private $badgeSerializationValidator; |
|
38
|
|
|
|
|
39
|
|
|
public function __construct( |
|
40
|
|
|
ApiMain $mainModule, |
|
41
|
|
|
string $moduleName, |
|
42
|
|
|
SiteLinkChangeOpFactory $siteLinkChangeOpFactory, |
|
43
|
|
|
SiteLinkBadgeChangeOpSerializationValidator $badgeSerializationValidator, |
|
44
|
|
|
bool $federatedPropertiesEnabled |
|
45
|
|
|
) { |
|
46
|
|
|
parent::__construct( $mainModule, $moduleName, $federatedPropertiesEnabled ); |
|
47
|
|
|
|
|
48
|
|
|
$this->siteLinkChangeOpFactory = $siteLinkChangeOpFactory; |
|
49
|
|
|
$this->badgeSerializationValidator = $badgeSerializationValidator; |
|
50
|
|
|
} |
|
51
|
|
|
|
|
52
|
|
|
public static function factory( ApiMain $mainModule, string $moduleName ): self { |
|
53
|
|
|
$wikibaseRepo = WikibaseRepo::getDefaultInstance(); |
|
54
|
|
|
|
|
55
|
|
|
return new self( |
|
56
|
|
|
$mainModule, |
|
57
|
|
|
$moduleName, |
|
58
|
|
|
$wikibaseRepo->getChangeOpFactoryProvider() |
|
59
|
|
|
->getSiteLinkChangeOpFactory(), |
|
60
|
|
|
$wikibaseRepo->getSiteLinkBadgeChangeOpSerializationValidator(), |
|
61
|
|
|
$wikibaseRepo->inFederatedPropertyMode() |
|
62
|
|
|
); |
|
63
|
|
|
} |
|
64
|
|
|
|
|
65
|
|
|
/** |
|
66
|
|
|
* @see ApiBase::isWriteMode() |
|
67
|
|
|
* |
|
68
|
|
|
* @return bool Always true. |
|
69
|
|
|
*/ |
|
70
|
|
|
public function isWriteMode(): bool { |
|
71
|
|
|
return true; |
|
72
|
|
|
} |
|
73
|
|
|
|
|
74
|
|
|
/** |
|
75
|
|
|
* @see ApiBase::needsToken |
|
76
|
|
|
* |
|
77
|
|
|
* @return string |
|
78
|
|
|
*/ |
|
79
|
|
|
public function needsToken(): string { |
|
80
|
|
|
return 'csrf'; |
|
81
|
|
|
} |
|
82
|
|
|
|
|
83
|
|
|
/** |
|
84
|
|
|
* Checks whether the link should be removed based on params |
|
85
|
|
|
* |
|
86
|
|
|
* @param array $params |
|
87
|
|
|
* |
|
88
|
|
|
* @return bool |
|
89
|
|
|
*/ |
|
90
|
|
|
private function shouldRemove( array $params ): bool { |
|
91
|
|
|
if ( $params['linktitle'] === '' || ( !isset( $params['linktitle'] ) && !isset( $params['badges'] ) ) ) { |
|
92
|
|
|
return true; |
|
93
|
|
|
} else { |
|
94
|
|
|
return false; |
|
95
|
|
|
} |
|
96
|
|
|
} |
|
97
|
|
|
|
|
98
|
|
|
protected function modifyEntity( EntityDocument $entity, ChangeOp $changeOp, array $preparedParameters ): Summary { |
|
99
|
|
|
if ( !( $entity instanceof Item ) ) { |
|
100
|
|
|
$this->errorReporter->dieError( "The given entity is not an item", "not-item" ); |
|
|
|
|
|
|
101
|
|
|
} |
|
102
|
|
|
|
|
103
|
|
|
$item = $entity; |
|
104
|
|
|
$summary = $this->createSummary( $preparedParameters ); |
|
105
|
|
|
$linksite = $this->stringNormalizer->trimToNFC( $preparedParameters['linksite'] ); |
|
106
|
|
|
$hasLinkWithSiteId = $item->getSiteLinkList()->hasLinkWithSiteId( $linksite ); |
|
|
|
|
|
|
107
|
|
|
$resultBuilder = $this->getResultBuilder(); |
|
108
|
|
|
|
|
109
|
|
|
if ( $this->shouldRemove( $preparedParameters ) ) { |
|
110
|
|
|
if ( $hasLinkWithSiteId ) { |
|
111
|
|
|
$siteLink = $item->getSiteLinkList()->getBySiteId( $linksite ); |
|
|
|
|
|
|
112
|
|
|
$this->applyChangeOp( $changeOp, $entity, $summary ); |
|
113
|
|
|
$resultBuilder->addRemovedSiteLinks( new SiteLinkList( [ $siteLink ] ), 'entity' ); |
|
114
|
|
|
} |
|
115
|
|
|
} else { |
|
116
|
|
|
try { |
|
117
|
|
|
$result = $changeOp->validate( $entity ); |
|
118
|
|
|
if ( !$result->isValid() ) { |
|
119
|
|
|
throw new ChangeOpValidationException( $result ); |
|
120
|
|
|
} |
|
121
|
|
|
|
|
122
|
|
|
$this->applyChangeOp( $changeOp, $entity, $summary ); |
|
123
|
|
|
|
|
124
|
|
|
$link = $item->getSiteLinkList()->getBySiteId( $linksite ); |
|
|
|
|
|
|
125
|
|
|
$resultBuilder->addSiteLinkList( |
|
126
|
|
|
new SiteLinkList( [ $link ] ), |
|
127
|
|
|
'entity', |
|
128
|
|
|
true // always add the URL |
|
129
|
|
|
); |
|
130
|
|
|
} catch ( ChangeOpException $ex ) { |
|
131
|
|
|
$this->errorReporter->dieException( $ex, 'modification-failed' ); |
|
132
|
|
|
} |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
|
|
return $summary; |
|
136
|
|
|
} |
|
137
|
|
|
|
|
138
|
|
|
protected function getChangeOp( array $preparedParameters, EntityDocument $entity ): ChangeOp { |
|
139
|
|
|
if ( $this->shouldRemove( $preparedParameters ) ) { |
|
140
|
|
|
$linksite = $this->stringNormalizer->trimToNFC( $preparedParameters['linksite'] ); |
|
141
|
|
|
return $this->siteLinkChangeOpFactory->newRemoveSiteLinkOp( $linksite ); |
|
142
|
|
|
} else { |
|
143
|
|
|
$linksite = $this->stringNormalizer->trimToNFC( $preparedParameters['linksite'] ); |
|
144
|
|
|
$sites = $this->siteLinkTargetProvider->getSiteList( $this->siteLinkGroups ); |
|
145
|
|
|
$site = $sites->getSite( $linksite ); |
|
146
|
|
|
|
|
147
|
|
|
if ( $site === false ) { |
|
148
|
|
|
$this->errorReporter->dieError( |
|
|
|
|
|
|
149
|
|
|
'The supplied site identifier was not recognized', |
|
150
|
|
|
'not-recognized-siteid' |
|
151
|
|
|
); |
|
152
|
|
|
} |
|
153
|
|
|
|
|
154
|
|
|
if ( isset( $preparedParameters['linktitle'] ) ) { |
|
155
|
|
|
$page = $site->normalizePageName( $this->stringNormalizer->trimWhitespace( $preparedParameters['linktitle'] ) ); |
|
156
|
|
|
|
|
157
|
|
|
if ( $page === false ) { |
|
158
|
|
|
$this->errorReporter->dieWithError( |
|
159
|
|
|
[ 'wikibase-api-no-external-page', $linksite, $preparedParameters['linktitle'] ], |
|
160
|
|
|
'no-external-page' |
|
161
|
|
|
); |
|
162
|
|
|
} |
|
163
|
|
|
} else { |
|
164
|
|
|
$page = null; |
|
165
|
|
|
} |
|
166
|
|
|
|
|
167
|
|
|
$badges = ( isset( $preparedParameters['badges'] ) ) |
|
168
|
|
|
? $this->parseSiteLinkBadges( $preparedParameters['badges'] ) |
|
169
|
|
|
: null; |
|
170
|
|
|
|
|
171
|
|
|
return $this->siteLinkChangeOpFactory->newSetSiteLinkOp( $linksite, $page, $badges ); |
|
|
|
|
|
|
172
|
|
|
} |
|
173
|
|
|
} |
|
174
|
|
|
|
|
175
|
|
|
private function parseSiteLinkBadges( array $badges ): array { |
|
176
|
|
|
try { |
|
177
|
|
|
$this->badgeSerializationValidator->validateBadgeSerialization( $badges ); |
|
178
|
|
|
} catch ( ChangeOpDeserializationException $exception ) { |
|
179
|
|
|
$this->errorReporter->dieException( $exception, $exception->getErrorCode() ); |
|
180
|
|
|
} |
|
181
|
|
|
|
|
182
|
|
|
return $this->getBadgeItemIds( $badges ); |
|
183
|
|
|
} |
|
184
|
|
|
|
|
185
|
|
|
private function getBadgeItemIds( array $badges ): array { |
|
186
|
|
|
return array_map( function( $badge ) { |
|
187
|
|
|
return new ItemId( $badge ); |
|
188
|
|
|
}, $badges ); |
|
189
|
|
|
} |
|
190
|
|
|
|
|
191
|
|
|
/** |
|
192
|
|
|
* @inheritDoc |
|
193
|
|
|
*/ |
|
194
|
|
|
protected function getAllowedParams(): array { |
|
195
|
|
|
$sites = $this->siteLinkTargetProvider->getSiteList( $this->siteLinkGroups ); |
|
196
|
|
|
|
|
197
|
|
|
return array_merge( |
|
198
|
|
|
parent::getAllowedParams(), |
|
199
|
|
|
[ |
|
200
|
|
|
'linksite' => [ |
|
201
|
|
|
self::PARAM_TYPE => $sites->getGlobalIdentifiers(), |
|
202
|
|
|
self::PARAM_REQUIRED => true, |
|
203
|
|
|
], |
|
204
|
|
|
'linktitle' => [ |
|
205
|
|
|
self::PARAM_TYPE => 'string', |
|
206
|
|
|
], |
|
207
|
|
|
'badges' => [ |
|
208
|
|
|
self::PARAM_TYPE => array_keys( $this->badgeItems ), |
|
209
|
|
|
self::PARAM_ISMULTI => true, |
|
210
|
|
|
], |
|
211
|
|
|
] |
|
212
|
|
|
); |
|
213
|
|
|
} |
|
214
|
|
|
|
|
215
|
|
|
/** |
|
216
|
|
|
* @inheritDoc |
|
217
|
|
|
*/ |
|
218
|
|
|
protected function getExamplesMessages(): array { |
|
219
|
|
|
return [ |
|
220
|
|
|
'action=wbsetsitelink&id=Q42&linksite=enwiki&linktitle=Hydrogen' |
|
221
|
|
|
=> 'apihelp-wbsetsitelink-example-1', |
|
222
|
|
|
'action=wbsetsitelink&id=Q42&linksite=enwiki&linktitle=Hydrogen&summary=Loves%20Oxygen' |
|
223
|
|
|
=> 'apihelp-wbsetsitelink-example-2', |
|
224
|
|
|
'action=wbsetsitelink&site=enwiki&title=Hydrogen&linksite=dewiki&linktitle=Wasserstoff' |
|
225
|
|
|
=> 'apihelp-wbsetsitelink-example-3', |
|
226
|
|
|
'action=wbsetsitelink&site=enwiki&title=Hydrogen&linksite=dewiki' |
|
227
|
|
|
=> 'apihelp-wbsetsitelink-example-4', |
|
228
|
|
|
'action=wbsetsitelink&site=enwiki&title=Hydrogen&linksite=plwiki&linktitle=Wodór&badges=Q149' |
|
229
|
|
|
=> 'apihelp-wbsetsitelink-example-5', |
|
230
|
|
|
'action=wbsetsitelink&id=Q42&linksite=plwiki&badges=Q2|Q149' |
|
231
|
|
|
=> 'apihelp-wbsetsitelink-example-6', |
|
232
|
|
|
'action=wbsetsitelink&id=Q42&linksite=plwiki&linktitle=Warszawa' |
|
233
|
|
|
=> 'apihelp-wbsetsitelink-example-7', |
|
234
|
|
|
'action=wbsetsitelink&id=Q42&linksite=plwiki&linktitle=Wodór&badges=' |
|
235
|
|
|
=> 'apihelp-wbsetsitelink-example-8', |
|
236
|
|
|
]; |
|
237
|
|
|
} |
|
238
|
|
|
|
|
239
|
|
|
} |
|
240
|
|
|
|
This method has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.