Tpt has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/370810 )
Change subject: Moves all code related to Index fields management outside of ProofreadIndexPage ...................................................................... Moves all code related to Index fields management outside of ProofreadIndexPage Change-Id: I0bebd9b5c59d60ee5c3b810a3754954cc8d71cda --- M extension.json M includes/Context.php M includes/Parser/PagesTagParser.php R includes/index/CustomIndexField.php A includes/index/CustomIndexFieldsParser.php M includes/index/EditIndexPage.php M includes/index/ProofreadIndexPage.php M includes/page/PageContentBuilder.php M includes/page/PageDisplayHandler.php M tests/phpunit/ContextTest.php M tests/phpunit/ProofreadPageTestCase.php A tests/phpunit/index/CustomIndexFieldsParserTest.php M tests/phpunit/index/ProofreadIndexPageTest.php 13 files changed, 605 insertions(+), 535 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/ProofreadPage refs/changes/10/370810/1 diff --git a/extension.json b/extension.json index 4f95dc1..9efbc29 100644 --- a/extension.json +++ b/extension.json @@ -49,6 +49,8 @@ "ProofreadPage\\Link": "includes/Link.php", "ProofreadIndexEntry": "includes/index/ProofreadIndexEntry.php", "ProofreadIndexPage": "includes/index/ProofreadIndexPage.php", + "ProofreadPage\\Index\\CustomIndexField": "includes/index/CustomIndexField.php", + "ProofreadPage\\Index\\CustomIndexFieldsParser": "includes/index/CustomIndexFieldsParser.php", "ProofreadPage\\Index\\IndexContent": "includes/index/IndexContent.php", "ProofreadPage\\Index\\IndexRedirectContent": "includes/index/IndexRedirectContent.php", "ProofreadPage\\Index\\IndexContentHandler": "includes/index/IndexContentHandler.php", diff --git a/includes/Context.php b/includes/Context.php index 5393f29..129a27d 100644 --- a/includes/Context.php +++ b/includes/Context.php @@ -2,6 +2,7 @@ namespace ProofreadPage; +use ProofreadPage\Index\CustomIndexFieldsParser; use ProofreadPage\Pagination\PaginationFactory; use RepoGroup; @@ -32,14 +33,24 @@ private $fileProvider; /** + * @var CustomIndexFieldsParser + */ + private $customIndexFieldsParser; + + /** * @param int $pageNamespaceId * @param int $indexNamespaceId * @param FileProvider $fileProvider + * @param CustomIndexFieldsParser $customIndexFieldsParser */ - public function __construct( $pageNamespaceId, $indexNamespaceId, FileProvider $fileProvider ) { + public function __construct( + $pageNamespaceId, $indexNamespaceId, FileProvider $fileProvider, + CustomIndexFieldsParser $customIndexFieldsParser + ) { $this->pageNamespaceId = $pageNamespaceId; $this->indexNamespaceId = $indexNamespaceId; $this->fileProvider = $fileProvider; + $this->customIndexFieldsParser = $customIndexFieldsParser; } /** @@ -71,6 +82,13 @@ } /** + * @return CustomIndexFieldsParser + */ + public function getCustomIndexFieldsParser() { + return $this->customIndexFieldsParser; + } + + /** * @param bool $purgeFileProvider * @return Context */ @@ -81,7 +99,8 @@ $defaultContext = new self( ProofreadPageInit::getNamespaceId( 'page' ), ProofreadPageInit::getNamespaceId( 'index' ), - new FileProvider( RepoGroup::singleton() ) + new FileProvider( RepoGroup::singleton() ), + new CustomIndexFieldsParser() ); } if ( $purgeFileProvider ) { diff --git a/includes/Parser/PagesTagParser.php b/includes/Parser/PagesTagParser.php index 90cabe0..5ccf5d2 100644 --- a/includes/Parser/PagesTagParser.php +++ b/includes/Parser/PagesTagParser.php @@ -4,6 +4,7 @@ use OutOfBoundsException; use ProofreadIndexPage; +use ProofreadPage\Context; use ProofreadPage\Pagination\FilePagination; use ProofreadPageDbConnector; use ProofreadPagePage; @@ -62,6 +63,7 @@ return $this->formatError( 'proofreadpage_nosuch_index' ); } $indexPage = ProofreadIndexPage::newFromTitle( $indexTitle ); + $indexContent = $indexPage->getContent(); $pagination = $this->context->getPaginationFactory() ->getPaginationForIndexPage( $indexPage ); $language = $this->parser->getTargetLanguage(); @@ -263,7 +265,7 @@ if ( $header == 'toc' ) { $this->parser->getOutput()->is_toc = true; } - $indexLinks = $indexPage->getContent()->getLinksToNamespace( + $indexLinks = $indexContent->getLinksToNamespace( NS_MAIN, $indexTitle, true ); $pageTitle = $this->parser->getTitle(); @@ -310,7 +312,8 @@ if ( isset( $to_pagenum ) ) { $h_out .= "|to=$to_pagenum"; } - $attributes = $indexPage->getIndexEntriesForHeader(); + $attributes = $this->context->getCustomIndexFieldsParser() + ->parseCustomIndexFieldsForHeader( $indexContent ); foreach ( $attributes as $attribute ) { $key = strtolower( $attribute->getKey() ); if ( array_key_exists( $key, $args ) ) { diff --git a/includes/index/ProofreadIndexEntry.php b/includes/index/CustomIndexField.php similarity index 65% rename from includes/index/ProofreadIndexEntry.php rename to includes/index/CustomIndexField.php index 564c035..570c8e1 100644 --- a/includes/index/ProofreadIndexEntry.php +++ b/includes/index/CustomIndexField.php @@ -1,28 +1,13 @@ <?php -/** - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - * @ingroup ProofreadPage - */ + +namespace ProofreadPage\Index; /** + * @licence GNU GPL v2+ + * * An index entry. */ -class ProofreadIndexEntry { +class CustomIndexField { /** * The key of the entry @@ -84,41 +69,6 @@ } /** - * Return the values of the entry as string and splitted with the delimiter content - * @return array string - */ - public function getStringValues() { - $value = $this->getStringValue(); - - if ( $value === '' ) { - return []; - } - - if ( !isset( $this->config['delimiter'] ) || !$this->config['delimiter'] ) { - return [ $value ]; - } - - $delimiters = $this->config['delimiter']; - if ( !is_array( $delimiters ) ) { - $delimiters = [ $delimiters ]; - } - - $values = [ $value ]; - foreach ( $delimiters as $delimiter ) { - $values2 = []; - foreach ( $values as $val ) { - $values2 = array_merge( $values2, explode( $delimiter, $val ) ); - } - $values = $values2; - } - - foreach ( $values as $id => $value ) { - $values[$id] = trim( $value ); - } - return $values; - } - - /** * Return the type of the entry * @return string */ @@ -147,7 +97,9 @@ * @return int */ public function getSize() { - if ( isset( $this->config['size'] ) && is_numeric( $this->config['size'] ) && + if ( + isset( $this->config['size'] ) && + is_numeric( $this->config['size'] ) && $this->config['size'] >= 1 ) { return (int)$this->config['size']; diff --git a/includes/index/CustomIndexFieldsParser.php b/includes/index/CustomIndexFieldsParser.php new file mode 100644 index 0000000..2a8653f --- /dev/null +++ b/includes/index/CustomIndexFieldsParser.php @@ -0,0 +1,236 @@ +<?php + +namespace ProofreadPage\Index; + +use FormatJson; +use OutOfBoundsException; + +/** + * @licence GNU GPL v2+ + * + * Returns the custom index entries from an IndexContent + */ +class CustomIndexFieldsParser { + + private $configuration; + + public function __construct( array $customIndexFieldsConfiguration = null ) { + $this->configuration = ( $customIndexFieldsConfiguration === null ) + ? $this->loadCustomIndexFieldsConfiguration() + : $customIndexFieldsConfiguration; + } + + /** + * @return array the configuration + * The configuration is a list of properties like this : + * array( + * 'ID' => array( //the property id + * 'type' => 'string', //the property type (for compatibility reasons the values have not + * //to be of this type). Possible values: string, number, page + * 'size' => 1, //for type = string : the size of the form input + * 'default' => '', //the default value + * 'label' => 'ID', //the label of the property + * 'help' => '', //a short help text + * 'values' => null, //an array value => label that list the possible values + * //(for compatibility reasons the stored values have not to be one of these) + * 'header' => false, //give the content of this property to + * //Mediawiki:Proofreadpage_header_template as template parameter + * 'hidden' => false //don't show the property in the index pages form. Useful for data + * //that have always the same value (as language=en for en Wikisource) or are + * //only set at the <pages> tag level. + * ) + * ); + * NB: The values set are the default values + */ + public function getCustomIndexFieldsConfiguration() { + return $this->configuration; + } + + private function loadCustomIndexFieldsConfiguration() { + $data = wfMessage( 'proofreadpage_index_data_config' )->inContentLanguage(); + if ( $data->exists() && $data->plain() != '' ) { + $config = FormatJson::decode( $data->plain(), true ); + if ( $config === null ) { + global $wgOut; + $wgOut->showErrorPage( + 'proofreadpage_dataconfig_badformatted', + 'proofreadpage_dataconfig_badformattedtext' + ); + $config = []; + } + } else { + $attributes = explode( "\n", wfMessage( 'proofreadpage_index_attributes' ) + ->inContentLanguage()->text() ); + $headerAttributes = explode( ' ', wfMessage( 'proofreadpage_js_attributes' ) + ->inContentLanguage()->text() ); + $config = []; + foreach ( $attributes as $attribute ) { + $m = explode( '|', $attribute ); + $params = [ + 'type' => 'string', + 'size' => 1, + 'default' => '', + 'label' => $m[0], + 'help' => '', + 'values' => null, + 'header' => false + ]; + + if ( $m[0] == 'Header' ) { + $params['default'] = wfMessage( 'proofreadpage_default_header' ) + ->inContentLanguage()->plain(); + } + if ( $m[0] == 'Footer' ) { + $params['default'] = wfMessage( 'proofreadpage_default_footer' ) + ->inContentLanguage()->plain(); + } + if ( isset( $m[1] ) && $m[1] !== '' ) { + $params['label'] = $m[1]; + } + if ( isset( $m[2] ) ) { + $params['size'] = intval( $m[2] ); + } + $config[$m[0]] = $params; + } + + foreach ( $headerAttributes as $attribute ) { + if ( isset( $config[$attribute] ) ) { + $config[$attribute]['header'] = true; + } else { + $config[$attribute] = [ + 'type' => 'string', + 'size' => 1, + 'default' => '', + 'label' => $attribute, + 'values' => null, + 'header' => true, + 'hidden' => true + ]; + } + } + } + + if ( !array_key_exists( 'Header', $config ) ) { + $config['Header'] = [ + 'default' => wfMessage( 'proofreadpage_default_header' ) + ->inContentLanguage()->plain(), + 'header' => true, + 'hidden' => true + ]; + } + if ( !array_key_exists( 'Footer', $config ) ) { + $config['Footer'] = [ + 'default' => wfMessage( 'proofreadpage_default_footer' ) + ->inContentLanguage()->plain(), + 'header' => true, + 'hidden' => true + ]; + } + + return $config; + } + + /** + * @param IndexContent $content + * @return CustomIndexField[] + */ + public function parseCustomIndexFields( IndexContent $content ) { + $contentFields = []; + foreach ( $content->getFields() as $key => $value ) { + $contentFields[strtolower( $key )] = $value; + } + + $values = []; + foreach ( $this->configuration as $varName => $property ) { + $key = strtolower( $varName ); + if ( array_key_exists( $key, $contentFields ) ) { + $values[$varName] = new CustomIndexField( + $varName, $contentFields[$key]->getNativeData(), $property + ); + } else { + $values[$varName] = new CustomIndexField( $varName, '', $property ); + } + } + return $values; + } + + /* + * Return metadata from the index page that have to be given to header template. + * @return CustomIndexField[] + */ + public function parseCustomIndexFieldsForHeader( IndexContent $content ) { + $entries = $this->parseCustomIndexFields( $content ); + $headerEntries = []; + foreach ( $entries as $entry ) { + if ( $entry->isHeader() ) { + $headerEntries[$entry->getKey()] = $entry; + } + } + return $headerEntries; + } + + /** + * Return the index entry with the same name or null if it's not found + * Note: the comparison is case insensitive + * @return CustomIndexField + * @throws OutOfBoundsException + */ + public function parseCustomIndexField( IndexContent $content, $fieldName ) { + $fieldName = strtolower( $fieldName ); + $entries = $this->parseCustomIndexFields( $content ); + foreach ( $entries as $entry ) { + if ( strtolower( $entry->getKey() ) === $fieldName ) { + return $entry; + } + } + throw new OutOfBoundsException( 'Custom index entry ' . $fieldName . ' does not exist.' ); + } + + /** + * Return the value of an entry as wikitext with variable replaced with index entries and + * $otherParams + * Example: if 'header' entry is 'Page of {{title}} number {{pagenum}}' with + * $otherParams = array( 'pagenum' => 23 ) + * the function called for 'header' will returns 'Page page my book number 23' + * @param IndexContent $content + * @param $fieldName + * @param array $otherParams associative array other possible values to replace + * @return string the value with variables replaced + * @throws OutOfBoundsException + */ + public function parseCustomIndexFieldWithVariablesReplacedWithIndexEntries( + IndexContent $content, $fieldName, $otherParams + ) { + $entry = $this->parseCustomIndexField( $content, $fieldName ); + + // we can't use the parser here because it replace tags like <references /> by strange UIDs + $params = $this->parseCustomIndexFieldsAsTemplateParams( $content ) + $otherParams; + return preg_replace_callback( + '/{\{\{(.*)(\|(.*))?\}\}\}/U', + function ( $matches ) use ( $params ) { + $paramKey = trim( strtolower( $matches[1] ) ); + if ( array_key_exists( $paramKey, $params ) ) { + return $params[$paramKey]; + } elseif ( array_key_exists( 3, $matches ) ) { + return trim( $matches[3] ); + } else { + return $matches[0]; + } + }, + $entry->getStringValue() + ); + } + + /** + * Returns the index entries formatted in order to be transcluded in templates + * @return string[] + */ + private function parseCustomIndexFieldsAsTemplateParams( IndexContent $content ) { + $indexEntries = $this->parseCustomIndexFieldsForHeader( $content ); + $params = []; + foreach ( $indexEntries as $entry ) { + $params[strtolower( $entry->getKey() )] = $entry->getStringValue(); + } + return $params; + } +} diff --git a/includes/index/EditIndexPage.php b/includes/index/EditIndexPage.php index fc4e82c..b2c9791 100644 --- a/includes/index/EditIndexPage.php +++ b/includes/index/EditIndexPage.php @@ -2,15 +2,13 @@ namespace ProofreadPage\Index; -use ContentHandler; +use Article; use EditPage; -use OOUI\ButtonWidget; +use MWException; use OOUI\DropdownInputWidget; use OOUI\FieldLayout; use OOUI\FieldsetLayout; -use OOUI\HtmlSnippet; use OOUI\TextInputWidget; -use ProofreadIndexEntry; use ProofreadIndexPage; use ProofreadPage\Context; use Status; @@ -20,6 +18,17 @@ * @licence GNU GPL v2+ */ class EditIndexPage extends EditPage { + + /** + * @var Context + */ + private $extContext; + + public function __construct( Article $article ) { + parent::__construct( $article ); + + $this->extContext = Context::getDefaultContext(); + } /** * @see EditPage::isSectionEditSupported @@ -49,8 +58,15 @@ $fields = []; $i = 10; - /** @var ProofreadIndexEntry $entry */ - foreach ( $this->getActualContent()->getIndexEntries() as $entry ) { + $content = $this->getCurrentContent(); + if ( !( $content instanceof IndexContent ) ) { + throw new MWException( 'EditIndexPage is only able to edit IndexContent' ); + } + + /** @var CustomIndexField $entry */ + foreach ( + $this->extContext->getCustomIndexFieldsParser()->parseCustomIndexFields( $content ) as $entry + ) { $inputOptions['tabIndex'] = $i; if ( !$entry->isHidden() ) { $fields[] = $this->buildEntry( $entry, $inputOptions ); @@ -66,7 +82,7 @@ $out->addModules( 'ext.proofreadpage.index' ); } - private function buildEntry( ProofreadIndexEntry $entry, $inputOptions ) { + private function buildEntry( CustomIndexField $entry, $inputOptions ) { $key = $this->getFieldNameForEntry( $entry->getKey() ); $val = $this->safeUnicodeOutput( $entry->getStringValue() ); @@ -129,12 +145,12 @@ return $this->textbox1; } - $config = ProofreadIndexPage::getDataConfig(); + $config = $this->extContext->getCustomIndexFieldsParser()->getCustomIndexFieldsConfiguration(); $fields = []; foreach ( $config as $key => $params ) { $field = $this->getFieldNameForEntry( $key ); $value = $this->cleanInputtedContent( $this->safeUnicodeInput( $request, $field ) ); - $entry = new ProofreadIndexEntry( $key, $value, $params ); + $entry = new CustomIndexField( $key, $value, $params ); if ( !$entry->isHidden() ) { $fields[$entry->getKey()] = new WikitextContent( $entry->getStringValue() ); } @@ -183,7 +199,7 @@ if ( $content instanceof IndexContent ) { // Get list of pages titles $links = $content->getLinksToNamespace( - Context::getDefaultContext()->getPageNamespaceId(), $this->getTitle() + $this->extContext->getPageNamespaceId(), $this->getTitle() ); $linksTitle = []; foreach ( $links as $link ) { @@ -204,13 +220,5 @@ } return parent::internalAttemptSave( $result, $bot ); - } - - private function getActualContent() { - return new ProofreadIndexPage( - $this->mTitle, - ProofreadIndexPage::getDataConfig(), - $this->getCurrentContent() - ); } } diff --git a/includes/index/ProofreadIndexPage.php b/includes/index/ProofreadIndexPage.php index f620c83..9f01aad 100644 --- a/includes/index/ProofreadIndexPage.php +++ b/includes/index/ProofreadIndexPage.php @@ -19,10 +19,7 @@ * @ingroup ProofreadPage */ -use ProofreadPage\Context; -use ProofreadPage\FileNotFoundException; use ProofreadPage\Index\IndexContent; -use ProofreadPage\Pagination\PageList; /** * An index page @@ -40,19 +37,12 @@ protected $content; /** - * @var array configuration array - */ - protected $config = []; - - /** * @param Title $title Reference to a Title object. - * @param array $config the configuration array (see ProofreadIndexPage::getDataConfig) * @param IndexContent|null $content content of the page. Warning: only done for * EditProofreadIndexPage use. */ - public function __construct( Title $title, $config, IndexContent $content = null ) { + public function __construct( Title $title, IndexContent $content = null ) { $this->title = $title; - $this->config = $config; $this->content = $content; } @@ -62,7 +52,7 @@ * @return ProofreadIndexPage */ public static function newFromTitle( Title $title ) { - return new self( $title, self::getDataConfig() ); + return new self( $title ); } /** @@ -81,20 +71,6 @@ */ public function equals( ProofreadIndexPage $that ) { return $this->title->equals( $that->getTitle() ); - } - - /** - * @deprecated use FileProvider::getForIndexPage - * - * Return Scan of the book if it exist or false. - * @return File|false - */ - public function getImage() { - try { - return Context::getDefaultContext()->getFileProvider()->getForIndexPage( $this ); - } catch ( FileNotFoundException $e ) { - return false; - } } /** @@ -119,141 +95,6 @@ } /** - * @return array the configuration - * The configuration is a list of properties like this : - * array( - * 'ID' => array( //the property id - * 'type' => 'string', //the property type (for compatibility reasons the values have not - * //to be of this type). Possible values: string, number, page - * 'size' => 1, //for type = string : the size of the form input - * 'default' => '', //the default value - * 'label' => 'ID', //the label of the property - * 'help' => '', //a short help text - * 'values' => null, //an array value => label that list the possible values - * //(for compatibility reasons the stored values have not to be one of these) - * 'header' => false, //give the content of this property to - * //Mediawiki:Proofreadpage_header_template as template parameter - * 'hidden' => false //don't show the property in the index pages form. Useful for data - * //that have always the same value (as language=en for en Wikisource) or are - * //only set at the <pages> tag level. - * ) - * ); - * NB: The values set are the default values - */ - public static function getDataConfig() { - static $config = null; - if ( $config !== null ) { - return $config; - } - - $data = wfMessage( 'proofreadpage_index_data_config' )->inContentLanguage(); - if ( $data->exists() && $data->plain() != '' ) { - $config = FormatJson::decode( $data->plain(), true ); - if ( $config === null ) { - global $wgOut; - $wgOut->showErrorPage( - 'proofreadpage_dataconfig_badformatted', - 'proofreadpage_dataconfig_badformattedtext' - ); - $config = []; - } - } else { - $attributes = explode( "\n", wfMessage( 'proofreadpage_index_attributes' ) - ->inContentLanguage()->text() ); - $headerAttributes = explode( ' ', wfMessage( 'proofreadpage_js_attributes' ) - ->inContentLanguage()->text() ); - $config = []; - foreach ( $attributes as $attribute ) { - $m = explode( '|', $attribute ); - $params = [ - 'type' => 'string', - 'size' => 1, - 'default' => '', - 'label' => $m[0], - 'help' => '', - 'values' => null, - 'header' => false - ]; - - if ( $m[0] == 'Header' ) { - $params['default'] = wfMessage( 'proofreadpage_default_header' ) - ->inContentLanguage()->plain(); - } - if ( $m[0] == 'Footer' ) { - $params['default'] = wfMessage( 'proofreadpage_default_footer' ) - ->inContentLanguage()->plain(); - } - if ( isset( $m[1] ) && $m[1] !== '' ) { - $params['label'] = $m[1]; - } - if ( isset( $m[2] ) ) { - $params['size'] = intval( $m[2] ); - } - $config[$m[0]] = $params; - } - - foreach ( $headerAttributes as $attribute ) { - if ( isset( $config[$attribute] ) ) { - $config[$attribute]['header'] = true; - } else { - $config[$attribute] = [ - 'type' => 'string', - 'size' => 1, - 'default' => '', - 'label' => $attribute, - 'values' => null, - 'header' => true, - 'hidden' => true - ]; - } - } - } - - if ( !array_key_exists( 'Header', $config ) ) { - $config['Header'] = [ - 'default' => wfMessage( 'proofreadpage_default_header' ) - ->inContentLanguage()->plain(), - 'header' => true, - 'hidden' => true - ]; - } - if ( !array_key_exists( 'Footer', $config ) ) { - $config['Footer'] = [ - 'default' => wfMessage( 'proofreadpage_default_footer' ) - ->inContentLanguage()->plain(), - 'header' => true, - 'hidden' => true - ]; - } - - return $config; - } - - /** - * Return metadata from an index page. - * @return array of ProofreadIndexEntry - */ - public function getIndexEntries() { - $contentFields = []; - foreach ( $this->getContent()->getFields() as $key => $value ) { - $contentFields[strtolower( $key )] = $value; - } - - $values = []; - foreach ( $this->config as $varName => $property ) { - $key = strtolower( $varName ); - if ( array_key_exists( $key, $contentFields ) ) { - $values[$varName] = new ProofreadIndexEntry( - $varName, $contentFields[$key]->getNativeData(), $property - ); - } else { - $values[$varName] = new ProofreadIndexEntry( $varName, '', $property ); - } - } - return $values; - } - - /** * Return mime type of the file linked to the index page * @return string|null */ @@ -264,83 +105,5 @@ } else { return null; } - } - - /* - * Return metadata from the index page that have to be given to header template. - * @return array of ProofreadIndexEntry - */ - public function getIndexEntriesForHeader() { - $entries = $this->getIndexEntries(); - $headerEntries = []; - foreach ( $entries as $entry ) { - if ( $entry->isHeader() ) { - $headerEntries[$entry->getKey()] = $entry; - } - } - return $headerEntries; - } - - /* - * Return the index entry with the same name or null if it's not found - * Note: the comparison is case insensitive - * @return ProofreadIndexEntry|null - */ - public function getIndexEntry( $name ) { - $name = strtolower( $name ); - $entries = $this->getIndexEntries(); - foreach ( $entries as $entry ) { - if ( strtolower( $entry->getKey() ) === $name ) { - return $entry; - } - } - return null; - } - - /** - * Return the value of an entry as wikitext with variable replaced with index entries and - * $otherParams - * Example: if 'header' entry is 'Page of {{title}} number {{pagenum}}' with - * $otherParams = array( 'pagenum' => 23 ) - * the function called for 'header' will returns 'Page page my book number 23' - * @param string $name entry name - * @param array $otherParams associative array other possible values to replace - * @return string|null the value with variables replaced or null if the entry does not exists - */ - public function getIndexEntryWithVariablesReplacedWithIndexEntries( $name, $otherParams ) { - $entry = $this->getIndexEntry( $name ); - if ( $entry === null ) { - return null; - } - - // we can't use the parser here because it replace tags like <references /> by strange UIDs - $params = $this->getIndexEntriesForHeaderAsTemplateParams() + $otherParams; - return preg_replace_callback( - '/{\{\{(.*)(\|(.*))?\}\}\}/U', - function ( $matches ) use ( $params ) { - $paramKey = trim( strtolower( $matches[1] ) ); - if ( array_key_exists( $paramKey, $params ) ) { - return $params[$paramKey]; - } elseif ( array_key_exists( 3, $matches ) ) { - return trim( $matches[3] ); - } else { - return $matches[0]; - } - }, - $entry->getStringValue() - ); - } - - /** - * Returns the index entries formatted in order to be transcluded in templates - * @return string[] - */ - protected function getIndexEntriesForHeaderAsTemplateParams() { - $indexEntries = $this->getIndexEntriesForHeader(); - $params = []; - foreach ( $indexEntries as $entry ) { - $params[strtolower( $entry->getKey() )] = $entry->getStringValue(); - } - return $params; } } diff --git a/includes/page/PageContentBuilder.php b/includes/page/PageContentBuilder.php index e7c0382..52badec 100644 --- a/includes/page/PageContentBuilder.php +++ b/includes/page/PageContentBuilder.php @@ -58,11 +58,12 @@ } catch ( OutOfBoundsException $e ) { } // should not happen - $header = $index->getIndexEntryWithVariablesReplacedWithIndexEntries( - 'header', $params - ); - $footer = $index->getIndexEntryWithVariablesReplacedWithIndexEntries( - 'footer', $params + $indexFieldsParser = $this->context->getCustomIndexFieldsParser(); + $header = $indexFieldsParser->parseCustomIndexFieldWithVariablesReplacedWithIndexEntries( + $index->getContent(), 'header', $params + ); + $footer = $indexFieldsParser->parseCustomIndexFieldWithVariablesReplacedWithIndexEntries( + $index->getContent(), 'footer', $params ); } else { $header = $this->contextSource->msg( 'proofreadpage_default_header' ) diff --git a/includes/page/PageDisplayHandler.php b/includes/page/PageDisplayHandler.php index b84cc14..9f76a17 100644 --- a/includes/page/PageDisplayHandler.php +++ b/includes/page/PageDisplayHandler.php @@ -3,6 +3,7 @@ namespace ProofreadPage\Page; use Html; +use OutOfBoundsException; use ProofreadPage\Context; use ProofreadPage\FileNotFoundException; use ProofreadPagePage; @@ -40,12 +41,15 @@ public function getImageWidth( ProofreadPagePage $page ) { $index = $page->getIndex(); if ( $index ) { - $width = $index->getIndexEntry( 'width' ); - if ( $width !== null ) { - $width = $width->getStringValue(); + try { + $width = $this->context->getCustomIndexFieldsParser()->parseCustomIndexField( + $index->getContent(), 'width' + )->getStringValue(); if ( is_numeric( $width ) ) { return $width; } + } catch ( OutOfBoundsException $e ) { + return self::DEFAULT_IMAGE_WIDTH; } } return self::DEFAULT_IMAGE_WIDTH; @@ -59,15 +63,19 @@ */ public function getCustomCss( ProofreadPagePage $page ) { $index = $page->getIndex(); - if ( $index ) { - $css = $index->getIndexEntry( 'css' ); - if ( $css !== null ) { - return Sanitizer::escapeHtmlAllowEntities( - Sanitizer::checkCss( $css->getStringValue() ) - ); - } + if ( !$index ) { + return ''; } - return ''; + try { + $css = $this->context->getCustomIndexFieldsParser()->parseCustomIndexField( + $index->getContent(), 'css' + ); + return Sanitizer::escapeHtmlAllowEntities( + Sanitizer::checkCss( $css->getStringValue() ) + ); + } catch ( OutOfBoundsException $e ) { + return ''; + } } /** diff --git a/tests/phpunit/ContextTest.php b/tests/phpunit/ContextTest.php index 3aa19b4..84b2dbd 100644 --- a/tests/phpunit/ContextTest.php +++ b/tests/phpunit/ContextTest.php @@ -2,6 +2,7 @@ namespace ProofreadPage; +use ProofreadPage\Index\CustomIndexFieldsParser; use ProofreadPageTestCase; /** @@ -11,24 +12,31 @@ class ContextTest extends ProofreadPageTestCase { public function testGetPageNamespaceId() { - $context = new Context( 42, 44, new FileProviderMock( [] ) ); + $context = new Context( 42, 44, new FileProviderMock( [] ), new CustomIndexFieldsParser() ); $this->assertEquals( 42, $context->getPageNamespaceId() ); } public function testGetIndexNamespaceId() { - $context = new Context( 42, 44, new FileProviderMock( [] ) ); + $context = new Context( 42, 44, new FileProviderMock( [] ), new CustomIndexFieldsParser() ); $this->assertEquals( 44, $context->getIndexNamespaceId() ); } public function testGetFileProvider() { - $context = new Context( 42, 44, new FileProviderMock( [] ) ); + $context = new Context( 42, 44, new FileProviderMock( [] ), new CustomIndexFieldsParser() ); $this->assertInstanceOf( '\ProofreadPage\FileProvider', $context->getFileProvider() ); } public function testGetPaginationFactory() { - $context = new Context( 42, 44, new FileProviderMock( [] ) ); + $context = new Context( 42, 44, new FileProviderMock( [] ), new CustomIndexFieldsParser() ); $this->assertInstanceOf( '\ProofreadPage\Pagination\PaginationFactory', $context->getPaginationFactory() ); } + + public function testCustomIndexFieldsParser() { + $context = new Context( 42, 44, new FileProviderMock( [] ), new CustomIndexFieldsParser() ); + $this->assertInstanceOf( + '\ProofreadPage\Index\CustomIndexFieldsParser', $context->getCustomIndexFieldsParser() + ); + } } diff --git a/tests/phpunit/ProofreadPageTestCase.php b/tests/phpunit/ProofreadPageTestCase.php index 7a1eb7e..cb23d43 100644 --- a/tests/phpunit/ProofreadPageTestCase.php +++ b/tests/phpunit/ProofreadPageTestCase.php @@ -3,11 +3,85 @@ use ProofreadPage\Context; use ProofreadPage\FileProvider; use ProofreadPage\FileProviderMock; +use ProofreadPage\Index\CustomIndexFieldsParser; /** * @group ProofreadPage */ abstract class ProofreadPageTestCase extends MediaWikiLangTestCase { + + protected static $customIndexFieldsConfiguration = [ + 'Title' => [ + 'type' => 'string', + 'size' => 1, + 'default' => '', + 'label' => 'Title', + 'values' => null, + 'header' => true, + 'data' => 'title' + ], + 'Author' => [ + 'type' => 'page', + 'size' => 1, + 'default' => '', + 'label' => 'Author', + 'values' => null, + 'header' => true, + 'data' => 'author' + ], + 'Year' => [ + 'type' => 'number', + 'size' => 1, + 'default' => '', + 'label' => 'Year of publication', + 'values' => null, + 'header' => false, + 'data' => 'year' + ], + 'Pages' => [ + 'type' => 'string', + 'size' => 20, + 'default' => '', + 'label' => 'Pages', + 'values' => null, + 'header' => false + ], + 'Header' => [ + 'type' => 'string', + 'size' => 10, + 'default' => 'head', + 'label' => 'Header', + 'values' => null, + 'header' => false + ], + 'Footer' => [ + 'default' => '<references />', + 'header' => true, + 'hidden' => true + ], + 'TOC' => [ + 'type' => 'string', + 'size' => 1, + 'default' => '', + 'label' => 'Table of content', + 'values' => null, + 'header' => false + ], + 'Comment' => [ + 'header' => true, + 'hidden' => true + ], + 'width' => [ + 'type' => 'number', + 'label' => 'Image width', + 'header' => false + ], + 'CSS' => [ + 'type' => 'string', + 'label' => 'CSS', + 'header' => false + ], + ]; /** * @var Context @@ -33,7 +107,8 @@ $this->context = new Context( 250, 252, - $this->getFileProvider() + $this->getFileProvider(), + new CustomIndexFieldsParser( self::$customIndexFieldsConfiguration ) ); } diff --git a/tests/phpunit/index/CustomIndexFieldsParserTest.php b/tests/phpunit/index/CustomIndexFieldsParserTest.php new file mode 100644 index 0000000..4cead40 --- /dev/null +++ b/tests/phpunit/index/CustomIndexFieldsParserTest.php @@ -0,0 +1,189 @@ +<?php + +namespace ProofreadPage\Index; + +use ContentHandler; +use OutOfBoundsException; +use ProofreadPageTestCase; + +/** + * @group ProofreadPage + * @covers CustomIndexFieldsParser + */ +class CustomIndexFieldsParserTest extends ProofreadPageTestCase { + /** + * @param string $content + * @return IndexContent + */ + private static function buildContent( $content ) { + return ContentHandler::getForModelID( CONTENT_MODEL_PROOFREAD_INDEX ) + ->unserializeContent( $content ); + } + + public function testParseCustomIndexFields() { + $content = self::buildContent( + "{{\n|Title=Test book\n|Author=[[Author:Me]]\n|Year=2012 or 2013\n|Header={{{Title}}}" . + "\n|Pages=<pagelist />\n|TOC=* [[Test/Chapter 1|Chapter 1]]" . + "\n* [[Test/Chapter 2|Chapter 2]]\n}}" + ); + $entries = [ + 'Title' => new CustomIndexField( + 'Title', 'Test book', self::$customIndexFieldsConfiguration['Title'] + ), + 'Author' => new CustomIndexField( + 'Author', '[[Author:Me]]', self::$customIndexFieldsConfiguration['Author'] + ), + 'Year' => new CustomIndexField( + 'Year', '2012 or 2013', self::$customIndexFieldsConfiguration['Year'] + ), + 'Pages' => new CustomIndexField( + 'Pages', '<pagelist />', self::$customIndexFieldsConfiguration['Pages'] + ), + 'Header' => new CustomIndexField( + 'Header', '{{{Title}}}', self::$customIndexFieldsConfiguration['Header'] + ), + 'Footer' => new CustomIndexField( + 'Footer', '', self::$customIndexFieldsConfiguration['Footer'] + ), + 'TOC' => new CustomIndexField( + 'TOC', + "* [[Test/Chapter 1|Chapter 1]]\n* [[Test/Chapter 2|Chapter 2]]", + self::$customIndexFieldsConfiguration['TOC'] + ), + 'Comment' => new CustomIndexField( + 'Comment', '', self::$customIndexFieldsConfiguration['Comment'] + ), + 'width' => new CustomIndexField( + 'width', '', self::$customIndexFieldsConfiguration['width'] + ), + 'CSS' => new CustomIndexField( + 'CSS', '', self::$customIndexFieldsConfiguration['CSS'] + ) + ]; + $this->assertEquals( + $entries, $this->getContext()->getCustomIndexFieldsParser()->parseCustomIndexFields( $content ) + ); + } + + public function testParseCustomIndexFieldsForHeader() { + $content = self::buildContent( + "{{\n|Title=Test book\n|Author=[[Author:Me]]\n|Year=2012 or 2013\n|Pages=<pagelist />" . + "\n|TOC=* [[Test/Chapter 1|Chapter 1]]\n* [[Test/Chapter 2|Chapter 2]]\n}}" + ); + $entries = [ + 'Title' => new CustomIndexField( + 'Title', 'Test book', self::$customIndexFieldsConfiguration['Title'] + ), + 'Author' => new CustomIndexField( + 'Author', '[[Author:Me]]', self::$customIndexFieldsConfiguration['Author'] + ), + 'Comment' => new CustomIndexField( + 'Comment', '', self::$customIndexFieldsConfiguration['Comment'] + ), + 'Header' => new CustomIndexField( + 'Header', '', self::$customIndexFieldsConfiguration['Header'] + ), + 'Footer' => new CustomIndexField( + 'Footer', '', self::$customIndexFieldsConfiguration['Footer'] + ), + 'width' => new CustomIndexField( + 'width', '', self::$customIndexFieldsConfiguration['width'] + ), + 'CSS' => new CustomIndexField( + 'CSS', '', self::$customIndexFieldsConfiguration['CSS'] + ) + ]; + $this->assertEquals( + $entries, + $this->getContext()->getCustomIndexFieldsParser()->parseCustomIndexFieldsForHeader( $content ) + ); + } + + public function testParseCustomIndexField() { + $content = self::buildContent( "{{\n|Year=2012 or 2013\n}}" ); + $parser = $this->getContext()->getCustomIndexFieldsParser(); + $entry = new CustomIndexField( + 'Year', '2012 or 2013', self::$customIndexFieldsConfiguration['Year'] + ); + $this->assertEquals( $entry, $parser->parseCustomIndexField( $content, 'year' ) ); + } + + /** + * @expectedException OutOfBoundsException + */ + public function testParseCustomIndexFieldThatDoesNotExist() { + $content = self::buildContent( "{{\n|Year=2012 or 2013\n}}" ); + $parser = $this->getContext()->getCustomIndexFieldsParser(); + $parser->parseCustomIndexField( $content, 'years' ); + } + + public function replaceVariablesWithIndexEntriesProvider() { + return [ + [ + "{{\n|Title=Test book\n|Header={{{title}}}\n}}", + 'Test book', + 'header', + [] + ], + [ + "{{\n|Title=Test book\n|Header={{{ Pagenum }}}\n}}", + '22', + 'header', + [ 'pagenum' => 22 ] + ], + [ + "{{\n|Title=Test book\n|Header={{{authors}}}\n}}", + '{{{authors}}}', + 'header', + [] + ], + [ + "{{\n|Title=Test book\n|Header={{{authors |a}}}\n}}", + 'a', + 'header', + [] + ], + [ + "{{\n|Title=Test book\n|Header={{template|a=b}}\n}}", + '{{template|a=b}}', + 'header', + [] + ], + [ + "{{\n|Title=Test book\n|Header={{template|a={{{Title |}}}}}\n}}", + '{{template|a=Test book}}', + 'header', + [] + ], + [ + "{{\n|Title=Test book\n|Header=<references/>\n}}", + '<references/>', + 'header', + [] + ], + ]; + } + + /** + * @dataProvider replaceVariablesWithIndexEntriesProvider + */ + public function testReplaceVariablesWithIndexEntries( + $pageContent, $result, $entry, $extraparams + ) { + $content = self::buildContent( $pageContent ); + $this->assertEquals( + $result, + $this->getContext()->getCustomIndexFieldsParser() + ->parseCustomIndexFieldWithVariablesReplacedWithIndexEntries( $content, $entry, $extraparams ) + ); + } + + /** + * @expectedException OutOfBoundsException + */ + public function testReplaceVariablesWithIndexEntriesThatDoesNotExist() { + $content = self::buildContent( "{{\n|Title=Test book\n|Header={{{Pagenum}}}\n}}" ); + $this->getContext()->getCustomIndexFieldsParser() + ->parseCustomIndexFieldWithVariablesReplacedWithIndexEntries( $content, 'headers', [] ); + } +} diff --git a/tests/phpunit/index/ProofreadIndexPageTest.php b/tests/phpunit/index/ProofreadIndexPageTest.php index 496c5fa..a8c9f15 100644 --- a/tests/phpunit/index/ProofreadIndexPageTest.php +++ b/tests/phpunit/index/ProofreadIndexPageTest.php @@ -1,4 +1,6 @@ <?php + +use ProofreadPage\Index\CustomIndexField; use ProofreadPage\Index\IndexContent; /** @@ -6,79 +8,6 @@ * @covers ProofreadIndexPage */ class ProofreadIndexPageTest extends ProofreadPageTestCase { - - protected static $config = [ - 'Title' => [ - 'type' => 'string', - 'size' => 1, - 'default' => '', - 'label' => 'Title', - 'values' => null, - 'header' => true, - 'data' => 'title' - ], - 'Author' => [ - 'type' => 'page', - 'size' => 1, - 'default' => '', - 'label' => 'Author', - 'values' => null, - 'header' => true, - 'data' => 'author' - ], - 'Year' => [ - 'type' => 'number', - 'size' => 1, - 'default' => '', - 'label' => 'Year of publication', - 'values' => null, - 'header' => false, - 'data' => 'year' - ], - 'Pages' => [ - 'type' => 'string', - 'size' => 20, - 'default' => '', - 'label' => 'Pages', - 'values' => null, - 'header' => false - ], - 'Header' => [ - 'type' => 'string', - 'size' => 10, - 'default' => 'head', - 'label' => 'Header', - 'values' => null, - 'header' => false - ], - 'Footer' => [ - 'default' => '<references />', - 'header' => true, - 'hidden' => true - ], - 'TOC' => [ - 'type' => 'string', - 'size' => 1, - 'default' => '', - 'label' => 'Table of content', - 'values' => null, - 'header' => false - ], - 'Comment' => [ - 'header' => true, - 'hidden' => true - ], - 'width' => [ - 'type' => 'number', - 'label' => 'Image width', - 'header' => false - ], - 'CSS' => [ - 'type' => 'string', - 'label' => 'CSS', - 'header' => false - ], - ]; /** * Constructor of a new ProofreadIndexPage @@ -94,7 +23,7 @@ $content = ContentHandler::getForModelID( CONTENT_MODEL_PROOFREAD_INDEX ) ->unserializeContent( $content ); } - return new ProofreadIndexPage( $title, self::$config, $content ); + return new ProofreadIndexPage( $title, $content ); } public function testEquals() { @@ -113,34 +42,6 @@ $this->assertEquals( $title, $page->getTitle() ); } - public function testGetIndexEntries() { - $page = self::newIndexPage( - 'Test.djvu', - "{{\n|Title=Test book\n|Author=[[Author:Me]]\n|Year=2012 or 2013\n|Header={{{Title}}}" . - "\n|Pages=<pagelist />\n|TOC=* [[Test/Chapter 1|Chapter 1]]" . - "\n* [[Test/Chapter 2|Chapter 2]]\n}}" - ); - $entries = [ - 'Title' => new ProofreadIndexEntry( 'Title', 'Test book', self::$config['Title'] ), - 'Author' => new ProofreadIndexEntry( - 'Author', '[[Author:Me]]', self::$config['Author'] - ), - 'Year' => new ProofreadIndexEntry( 'Year', '2012 or 2013', self::$config['Year'] ), - 'Pages' => new ProofreadIndexEntry( 'Pages', '<pagelist />', self::$config['Pages'] ), - 'Header' => new ProofreadIndexEntry( 'Header', '{{{Title}}}', self::$config['Header'] ), - 'Footer' => new ProofreadIndexEntry( 'Footer', '', self::$config['Footer'] ), - 'TOC' => new ProofreadIndexEntry( - 'TOC', - "* [[Test/Chapter 1|Chapter 1]]\n* [[Test/Chapter 2|Chapter 2]]", - self::$config['TOC'] - ), - 'Comment' => new ProofreadIndexEntry( 'Comment', '', self::$config['Comment'] ), - 'width' => new ProofreadIndexEntry( 'width', '', self::$config['width'] ), - 'CSS' => new ProofreadIndexEntry( 'CSS', '', self::$config['CSS'] ) - ]; - $this->assertEquals( $entries, $page->getIndexEntries() ); - } - public function mimeTypesProvider() { return [ [ 'image/vnd.djvu', 'Test.djvu' ], @@ -154,100 +55,5 @@ */ public function testGetMimeType( $mime, $name ) { $this->assertEquals( $mime, self::newIndexPage( $name )->getMimeType() ); - } - - public function testGetIndexEntriesForHeader() { - $page = self::newIndexPage( - 'Test.djvu', - "{{\n|Title=Test book\n|Author=[[Author:Me]]\n|Year=2012 or 2013\n|Pages=<pagelist />" . - "\n|TOC=* [[Test/Chapter 1|Chapter 1]]\n* [[Test/Chapter 2|Chapter 2]]\n}}" - ); - $entries = [ - 'Title' => new ProofreadIndexEntry( 'Title', 'Test book', self::$config['Title'] ), - 'Author' => new ProofreadIndexEntry( - 'Author', '[[Author:Me]]', self::$config['Author'] - ), - 'Comment' => new ProofreadIndexEntry( 'Comment', '', self::$config['Comment'] ), - 'Header' => new ProofreadIndexEntry( 'Header', '', self::$config['Header'] ), - 'Footer' => new ProofreadIndexEntry( 'Footer', '', self::$config['Footer'] ), - 'width' => new ProofreadIndexEntry( 'width', '', self::$config['width'] ), - 'CSS' => new ProofreadIndexEntry( 'CSS', '', self::$config['CSS'] ) - ]; - $this->assertEquals( $entries, $page->getIndexEntriesForHeader() ); - } - - public function testGetIndexEntry() { - $page = self::newIndexPage( 'Test.djvu', "{{\n|Year=2012 or 2013\n}}" ); - - $entry = new ProofreadIndexEntry( 'Year', '2012 or 2013', self::$config['Year'] ); - $this->assertEquals( $entry, $page->getIndexEntry( 'year' ) ); - - $this->assertNull( $page->getIndexEntry( 'years' ) ); - } - - public function replaceVariablesWithIndexEntriesProvider() { - return [ - [ - "{{\n|Title=Test book\n|Header={{{title}}}\n}}", - 'Test book', - 'header', - [] - ], - [ - "{{\n|Title=Test book\n|Header={{{ Pagenum }}}\n}}", - '22', - 'header', - [ 'pagenum' => 22 ] - ], - [ - "{{\n|Title=Test book\n|Header={{{authors}}}\n}}", - '{{{authors}}}', - 'header', - [] - ], - [ - "{{\n|Title=Test book\n|Header={{{authors |a}}}\n}}", - 'a', - 'header', - [] - ], - [ - "{{\n|Title=Test book\n|Header={{template|a=b}}\n}}", - '{{template|a=b}}', - 'header', - [] - ], - [ - "{{\n|Title=Test book\n|Header={{template|a={{{Title |}}}}}\n}}", - '{{template|a=Test book}}', - 'header', - [] - ], - [ - "{{\n|Title=Test book\n|Header=<references/>\n}}", - '<references/>', - 'header', - [] - ], - [ - "{{\n|Title=Test book\n|Header={{{Pagenum}}}\n}}", - null, - 'headers', - [] - ], - ]; - } - - /** - * @dataProvider replaceVariablesWithIndexEntriesProvider - */ - public function testReplaceVariablesWithIndexEntries( - $pageContent, $result, $entry, $extraparams - ) { - $page = self::newIndexPage( 'Test.djvu', $pageContent ); - $this->assertEquals( - $result, - $page->getIndexEntryWithVariablesReplacedWithIndexEntries( $entry, $extraparams ) - ); } } -- To view, visit https://gerrit.wikimedia.org/r/370810 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I0bebd9b5c59d60ee5c3b810a3754954cc8d71cda Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/ProofreadPage Gerrit-Branch: master Gerrit-Owner: Tpt <thoma...@hotmail.fr> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits