Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
80.00% |
8 / 10 |
CRAP | |
92.59% |
75 / 81 |
EntitySchema\MediaWiki\Specials\SetEntitySchemaLabelDescriptionAliases | |
0.00% |
0 / 1 |
|
80.00% |
8 / 10 |
43.75 | |
92.59% |
75 / 81 |
__construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
4 / 4 |
|||
execute | |
100.00% |
1 / 1 |
2 | |
100.00% |
7 / 7 |
|||
submitEditFormCallback | |
0.00% |
0 / 1 |
4.14 | |
79.17% |
19 / 24 |
|||
getDescription | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
getIdFromSubpageOrRequest | |
100.00% |
1 / 1 |
3 | |
100.00% |
4 / 4 |
|||
getLanguageFromSubpageOrRequestOrUI | |
100.00% |
1 / 1 |
3 | |
100.00% |
4 / 4 |
|||
displaySchemaLanguageSelectionForm | |
100.00% |
1 / 1 |
2 | |
100.00% |
11 / 11 |
|||
displayEditForm | n/a |
0 / 0 |
4 | n/a |
0 / 0 |
|||||
isSecondForm | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
|||||
getSchemaNameBadge | n/a |
0 / 0 |
3 | n/a |
0 / 0 |
|||||
getSchemaSelectionFormFields | |
100.00% |
1 / 1 |
2 | |
100.00% |
18 / 18 |
|||
getEditFormFields | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
|||||
isSelectionDataValid | |
100.00% |
1 / 1 |
4 | |
100.00% |
5 / 5 |
|||
displayCopyright | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
|||||
displayWarnings | n/a |
0 / 0 |
2 | n/a |
0 / 0 |
|||||
buildLanguageAndSchemaNotice | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
|||||
getSchemaDisplayLabel | n/a |
0 / 0 |
2 | n/a |
0 / 0 |
|||||
getCopyrightHTML | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
|||||
getWarnings | n/a |
0 / 0 |
2 | n/a |
0 / 0 |
|||||
getGroupName | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
|||||
checkBlocked | |
0.00% |
0 / 1 |
2.15 | |
66.67% |
2 / 3 |
<?php | |
namespace EntitySchema\MediaWiki\Specials; | |
use Html; | |
use HTMLForm; | |
use InvalidArgumentException; | |
use Language; | |
use MediaWiki\MediaWikiServices; | |
use MediaWiki\Revision\SlotRecord; | |
use MWException; | |
use OutputPage; | |
use RuntimeException; | |
use SpecialPage; | |
use Status; | |
use Title; | |
use UserBlockedError; | |
use WebRequest; | |
use EntitySchema\DataAccess\EditConflict; | |
use EntitySchema\DataAccess\MediaWikiPageUpdaterFactory; | |
use EntitySchema\DataAccess\MediaWikiRevisionSchemaUpdater; | |
use EntitySchema\DataAccess\WatchlistUpdater; | |
use EntitySchema\Domain\Model\SchemaId; | |
use EntitySchema\Presentation\InputValidator; | |
use EntitySchema\Services\SchemaConverter\NameBadge; | |
use EntitySchema\Services\SchemaConverter\SchemaConverter; | |
use WikiPage; | |
/** | |
* Page for editing label, description and aliases of a Schema | |
* | |
* @license GPL-2.0-or-later | |
*/ | |
class SetEntitySchemaLabelDescriptionAliases extends SpecialPage { | |
const FIELD_ID = 'ID'; | |
const FIELD_LANGUAGE = 'languagecode'; | |
const FIELD_DESCRIPTION = 'description'; | |
const FIELD_LABEL = 'label'; | |
const FIELD_ALIASES = 'aliases'; | |
const FIELD_BASE_REV = 'base-rev'; | |
const SUBMIT_SELECTION_NAME = 'submit-selection'; | |
const SUBMIT_EDIT_NAME = 'submit-edit'; | |
private $htmlFormProvider; | |
public function __construct( $htmlFormProvider = HTMLForm::class ) { | |
parent::__construct( | |
'SetEntitySchemaLabelDescriptionAliases' | |
); | |
$this->htmlFormProvider = $htmlFormProvider; | |
} | |
public function execute( $subPage ) { | |
parent::execute( $subPage ); | |
$request = $this->getRequest(); | |
$id = $this->getIdFromSubpageOrRequest( $subPage, $request ); | |
$language = $this->getLanguageFromSubpageOrRequestOrUI( $subPage, $request ); | |
if ( $this->isSelectionDataValid( $id, $language ) ) { | |
$baseRevId = $request->getInt( self::FIELD_BASE_REV ); | |
$this->displayEditForm( new SchemaId( $id ), $language, $baseRevId ); | |
return; | |
} | |
$this->displaySchemaLanguageSelectionForm( $id, $language ); | |
} | |
public function submitEditFormCallback( $data ) { | |
$updaterFactory = new MediaWikiPageUpdaterFactory( $this->getContext()->getUser() ); | |
$watchListUpdater = new WatchlistUpdater( $this->getUser(), NS_ENTITYSCHEMA_JSON ); | |
try { | |
$id = new SchemaId( $data[self::FIELD_ID] ); | |
} catch ( InvalidArgumentException $e ) { | |
return Status::newFatal( 'entityschema-error-schemaupdate-failed' ); | |
} | |
$title = Title::makeTitle( NS_ENTITYSCHEMA_JSON, $id->getId() ); | |
$this->checkBlocked( $title ); | |
$aliases = array_map( 'trim', explode( '|', $data[self::FIELD_ALIASES] ) ); | |
$schemaUpdater = new MediaWikiRevisionSchemaUpdater( | |
$updaterFactory, | |
$watchListUpdater, | |
MediaWikiServices::getInstance()->getRevisionLookup() | |
); | |
try { | |
$schemaUpdater->updateSchemaNameBadge( | |
$id, | |
$data[self::FIELD_LANGUAGE], | |
$data[self::FIELD_LABEL], | |
$data[self::FIELD_DESCRIPTION], | |
$aliases, | |
(int)$data[self::FIELD_BASE_REV] | |
); | |
} catch ( EditConflict $e ) { | |
return Status::newFatal( 'entityschema-error-namebadge-conflict' ); | |
} catch ( RuntimeException $e ) { | |
return Status::newFatal( 'entityschema-error-schemaupdate-failed' ); | |
} | |
return Status::newGood( $title->getFullURL() ); | |
} | |
public function getDescription() { | |
return $this->msg( 'entityschema-special-setlabeldescriptionaliases' )->text(); | |
} | |
private function getIdFromSubpageOrRequest( $subpage, WebRequest $request ) { | |
$subpageParts = array_filter( explode( '/', $subpage, 2 ) ); | |
if ( count( $subpageParts ) > 0 ) { | |
return $subpageParts[0]; | |
} | |
return $request->getText( self::FIELD_ID ) ?: null; | |
} | |
private function getLanguageFromSubpageOrRequestOrUI( $subpage, WebRequest $request ) { | |
$subpageParts = array_filter( explode( '/', $subpage, 2 ) ); | |
if ( count( $subpageParts ) === 2 ) { | |
return $subpageParts[1]; | |
} | |
return $request->getText( self::FIELD_LANGUAGE ) ?: $this->getLanguage()->getCode(); | |
} | |
private function displaySchemaLanguageSelectionForm( $defaultId, $defaultLanguage ) { | |
$formDescriptor = $this->getSchemaSelectionFormFields( $defaultId, $defaultLanguage ); | |
$formProvider = $this->htmlFormProvider; // FIXME: PHP7: inline this variable! | |
$form = $formProvider::factory( 'ooui', $formDescriptor, $this->getContext() ) | |
->setSubmitName( self::SUBMIT_SELECTION_NAME ) | |
->setSubmitID( 'wbschema-special-schema-id-submit' ) | |
->setSubmitTextMsg( 'entityschema-special-id-submit' ) | |
->setTitle( $this->getPageTitle() ); | |
$form->prepareForm(); | |
$submitStatus = $form->tryAuthorizedSubmit(); | |
$form->displayForm( $submitStatus ?: Status::newGood() ); | |
} | |
private function displayEditForm( SchemaId $id, $langCode, $baseRevId ) { | |
$output = $this->getOutput(); | |
$title = Title::makeTitle( NS_ENTITYSCHEMA_JSON, $id->getId() ); | |
$schemaNameBadge = $this->getSchemaNameBadge( $title, $langCode, $baseRevId ); | |
$formDescriptor = $this->getEditFormFields( $id, $langCode, $schemaNameBadge, $baseRevId ); | |
$formProvider = $this->htmlFormProvider; // FIXME: PHP7: inline this variable! | |
$form = $formProvider::factory( 'ooui', $formDescriptor, $this->getContext() ) | |
->setSubmitName( self::SUBMIT_EDIT_NAME ) | |
->setSubmitID( 'wbschema-special-schema-id-submit' ) | |
->setSubmitTextMsg( 'entityschema-special-id-submit' ) | |
->setValidationErrorMessage( [ [ | |
'entityschema-error-possibly-multiple-messages-available' | |
] ] ); | |
$form->prepareForm(); | |
if ( !$this->isSecondForm() ) { | |
$form->setSubmitCallback( [ $this, 'submitEditFormCallback' ] ); | |
$submitStatus = $form->tryAuthorizedSubmit(); | |
if ( $submitStatus && $submitStatus->isGood() ) { | |
$output->redirect( | |
$submitStatus->getValue() | |
); | |
return; | |
} | |
} | |
$output->addModules( [ | |
'ext.EntitySchema.special.setSchemaLabelDescriptionAliases.edit' | |
] ); | |
$output->addJsConfigVars( | |
'wgWBSchemaNameBadgeMaxSizeChars', | |
intval( $this->getConfig()->get( 'WBSchemaNameBadgeMaxSizeChars' ) ) | |
); | |
$this->displayWarnings( $output ); | |
$form->displayForm( $submitStatus ?? Status::newGood() ); | |
$this->displayCopyright( $output ); | |
} | |
/** | |
* Check if the second form is requested. | |
* | |
* @return bool | |
*/ | |
private function isSecondForm() { | |
return $this->getContext()->getRequest()->getCheck( self::SUBMIT_SELECTION_NAME ); | |
} | |
/** | |
* Gets the Schema NameBadge (label, desc, aliases) by interface language | |
* | |
* @param Title $title instance of Title for a specific Schema | |
* @param string $langCode | |
* @param int &$revId the revision from which to load data, or 0 to load the latest revision | |
* of $title, in which case &$revId will be replaced with that revision's ID | |
* | |
* @return NameBadge | |
* @throws MWException | |
*/ | |
private function getSchemaNameBadge( Title $title, $langCode, &$revId ) { | |
if ( $revId > 0 ) { | |
$revision = MediaWikiServices::getInstance()->getRevisionLookup() | |
->getRevisionById( $revId ); | |
if ( $revision->getPageId() !== $title->getArticleID() ) { | |
throw new MWException( 'revision does not match title' ); | |
} | |
} else { | |
$wikiPage = WikiPage::factory( $title ); | |
$revision = $wikiPage->getRevisionRecord(); | |
$revId = $revision->getId(); | |
} | |
// @phan-suppress-next-line PhanUndeclaredMethod | |
$schema = $revision->getContent( SlotRecord::MAIN )->getText(); | |
$converter = new SchemaConverter(); | |
return $converter->getMonolingualNameBadgeData( $schema, $langCode ); | |
} | |
private function getSchemaSelectionFormFields( $defaultId, $defaultLanguage ) { | |
$inputValidator = InputValidator::newFromGlobalState(); | |
return [ | |
self::FIELD_ID => [ | |
'name' => self::FIELD_ID, | |
'type' => 'text', | |
'id' => 'wbschema-special-schema-id', | |
'required' => true, | |
'default' => $defaultId ?: '', | |
'placeholder-message' => 'entityschema-special-id-placeholder', | |
'label-message' => 'entityschema-special-id-inputlabel', | |
'validation-callback' => [ | |
$inputValidator, | |
'validateIDExists' | |
], | |
], | |
self::FIELD_LANGUAGE => [ | |
'name' => self::FIELD_LANGUAGE, | |
'type' => 'text', | |
'id' => 'wbschema-language-code', | |
'required' => true, | |
'default' => $defaultLanguage, | |
'label-message' => 'entityschema-special-language-inputlabel', | |
'validation-callback' => [ | |
$inputValidator, | |
'validateLangCodeIsSupported' | |
], | |
], | |
]; | |
} | |
private function getEditFormFields( | |
SchemaId $id, | |
$badgeLangCode, | |
NameBadge $nameBadge, | |
$baseRevId | |
) { | |
$label = $nameBadge->label; | |
$description = $nameBadge->description; | |
$aliases = implode( '|', $nameBadge->aliases ); | |
$uiLangCode = $this->getLanguage()->getCode(); | |
$langName = Language::fetchLanguageName( $badgeLangCode, $uiLangCode ); | |
$inputValidator = InputValidator::newFromGlobalState(); | |
return [ | |
'notice' => [ | |
'type' => 'info', | |
'raw' => true, | |
'default' => $this->buildLanguageAndSchemaNotice( $langName, $label, $id ), | |
], | |
self::FIELD_ID => [ | |
'name' => self::FIELD_ID, | |
'type' => 'hidden', | |
'id' => 'wbschema-id', | |
'required' => true, | |
'default' => $id->getId(), | |
], | |
self::FIELD_LANGUAGE => [ | |
'name' => self::FIELD_LANGUAGE, | |
'type' => 'hidden', | |
'id' => 'wbschema-language-code', | |
'required' => true, | |
'default' => $badgeLangCode, | |
], | |
self::FIELD_LABEL => [ | |
'name' => self::FIELD_LABEL, | |
'type' => 'text', | |
'id' => 'wbschema-title-label', | |
'default' => $label, | |
'placeholder-message' => $this->msg( 'entityschema-label-edit-placeholder' ) | |
->params( $langName ), | |
'label-message' => 'entityschema-special-label', | |
'validation-callback' => [ | |
$inputValidator, | |
'validateStringInputLength' | |
], | |
], | |
self::FIELD_DESCRIPTION => [ | |
'name' => self::FIELD_DESCRIPTION, | |
'type' => 'text', | |
'default' => $description, | |
'id' => 'wbschema-heading-description', | |
'placeholder-message' => $this->msg( 'entityschema-description-edit-placeholder' ) | |
->params( $langName ), | |
'label-message' => 'entityschema-special-description', | |
'validation-callback' => [ | |
$inputValidator, | |
'validateStringInputLength' | |
], | |
], | |
self::FIELD_ALIASES => [ | |
'name' => self::FIELD_ALIASES, | |
'type' => 'text', | |
'default' => $aliases, | |
'id' => 'wbschema-heading-aliases', | |
'placeholder-message' => $this->msg( 'entityschema-aliases-edit-placeholder' ) | |
->params( $langName ), | |
'label-message' => 'entityschema-special-aliases', | |
'validation-callback' => [ | |
$inputValidator, | |
'validateAliasesLength' | |
], | |
], | |
self::FIELD_BASE_REV => [ | |
'name' => self::FIELD_BASE_REV, | |
'type' => 'hidden', | |
'required' => true, | |
'default' => $baseRevId, | |
], | |
]; | |
} | |
/** | |
* Validate ID and Language Code values | |
* | |
* @param string|null $id ID of the Schema | |
* @param string|null $language language code of the Schema | |
* | |
* @return bool | |
*/ | |
private function isSelectionDataValid( $id, $language ) { | |
if ( $id === null || $language === null ) { | |
return false; | |
} | |
$inputValidator = InputValidator::newFromGlobalState(); | |
return $inputValidator->validateIDExists( $id ) === true && | |
$inputValidator->validateLangCodeIsSupported( $language ) === true; | |
} | |
private function displayCopyright( OutputPage $output ) { | |
$output->addHTML( $this->getCopyrightHTML() ); | |
} | |
private function displayWarnings( OutputPage $output ) { | |
foreach ( $this->getWarnings() as $warning ) { | |
$output->addHTML( Html::rawElement( 'div', [ 'class' => 'warning' ], $warning ) ); | |
} | |
} | |
/** | |
* Build the info message atop of the second form | |
* | |
* @return string HTML | |
*/ | |
private function buildLanguageAndSchemaNotice( $langName, $label, SchemaId $schemaId ) { | |
$title = Title::makeTitle( NS_ENTITYSCHEMA_JSON, $schemaId->getId() ); | |
return $this->msg( 'entityschema-special-setlabeldescriptionaliases-info' ) | |
->params( $langName ) | |
->params( $this->getSchemaDisplayLabel( $label, $schemaId ) ) | |
->params( $title->getPrefixedText() ) | |
->parse(); | |
} | |
private function getSchemaDisplayLabel( $label, SchemaId $schemaId ) { | |
if ( !$label ) { | |
return $schemaId->getId(); | |
} | |
return $label . ' ' . $this->msg( 'parentheses' )->params( $schemaId->getId() )->escaped(); | |
} | |
/** | |
* @return string HTML | |
*/ | |
private function getCopyrightHTML() { | |
return $this->msg( 'entityschema-newschema-copyright' ) | |
->params( | |
$this->msg( 'entityschema-special-id-submit' )->text(), | |
$this->msg( 'copyrightpage' )->text(), | |
// FIXME: make license configurable | |
'[https://creativecommons.org/publicdomain/zero/1.0/ Creative Commons CC0 License]' | |
)->parse(); | |
} | |
private function getWarnings(): array { | |
if ( $this->getUser()->isAnon() ) { | |
return [ | |
$this->msg( | |
'entityschema-anonymouseditwarning' | |
)->parse(), | |
]; | |
} | |
return []; | |
} | |
protected function getGroupName() { | |
return 'wikibase'; | |
} | |
private function checkBlocked( Title $title ) { | |
if ( $this->getUser()->isBlockedFrom( $title ) ) { | |
throw new UserBlockedError( $this->getUser()->getBlock() ); | |
} | |
} | |
} |