jenkins-bot has submitted this change and it was merged. Change subject: Add new special page for searching assessments ......................................................................
Add new special page for searching assessments This adds Special:PageAssessments with a basic search form and results table. The special page can also be transcluded, in which case the search form isn't shown (all search and sort parameters must be provided in the transclusion). Bug: T120407 Change-Id: I6ee898059ffc62bb5f2788ad83a49b650cdb1335 --- M extension.json M i18n/en.json M i18n/qqq.json A src/NamespaceSelect.php A src/Pager.php A src/SpecialPage.php 6 files changed, 319 insertions(+), 3 deletions(-) Approvals: Kaldari: Looks good to me, approved jenkins-bot: Verified diff --git a/extension.json b/extension.json index 49a9529..bd8ccd7 100644 --- a/extension.json +++ b/extension.json @@ -19,7 +19,10 @@ "PageAssessmentsHooks": "PageAssessments.hooks.php", "PageAssessmentsBody": "PageAssessmentsBody.php", "ApiQueryPageAssessments": "api/ApiQueryPageAssessments.php", - "ApiQueryProjectPages": "api/ApiQueryProjectPages.php" + "ApiQueryProjectPages": "api/ApiQueryProjectPages.php", + "PageAssessments\\SpecialPage": "src/SpecialPage.php", + "PageAssessments\\Pager": "src/Pager.php", + "PageAssessments\\NamespaceSelect": "src/NamespaceSelect.php" }, "ExtensionMessagesFiles": { "PageAssessmentsMagic": "PageAssessments.i18n.magic.php" @@ -47,6 +50,9 @@ "localBasePath": "", "remoteExtPath": "examples/PageAssessments" }, + "SpecialPages": { + "PageAssessments": "PageAssessments\\SpecialPage" + }, "config": { "PageAssessmentsOnTalkPages": true }, diff --git a/i18n/en.json b/i18n/en.json index f5a0313..676a955 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -16,5 +16,14 @@ "apihelp-query+projectpages-param-limit": "The maximum number of pages to return.", "apihelp-query+projectpages-example-simple-1": "Get first 10 pages associated with any WikiProject.", "apihelp-query+projectpages-example-simple-2": "Get first 10 pages associated with WikiProject <kbd>Medicine</kbd>, including assessment data.", - "apihelp-query+projectpages-example-generator": "Get page info for first 10 pages associated with WikiProject <kbd>Textile Arts</kbd>." + "apihelp-query+projectpages-example-generator": "Get page info for first 10 pages associated with WikiProject <kbd>Textile Arts</kbd>.", + "pageassessments-special": "Page assessments", + "pageassessments-project": "Project", + "pageassessments-page-namespace": "Page namespace", + "pageassessments-page-title": "Page title", + "pageassessments-search": "Search", + "pageassessments-importance": "Importance", + "pageassessments-class": "Class", + "pageassessments-timestamp": "Timestamp", + "pageassessments-total-results": "Total results: $1" } diff --git a/i18n/qqq.json b/i18n/qqq.json index 6caf8ec..75e10db 100644 --- a/i18n/qqq.json +++ b/i18n/qqq.json @@ -16,5 +16,14 @@ "apihelp-query+projectpages-param-limit": "{{doc-apihelp-param|query+projectpages|limit}}", "apihelp-query+projectpages-example-simple-1": "{{doc-apihelp-example|query+projectpages}}", "apihelp-query+projectpages-example-simple-2": "{{doc-apihelp-example|query+projectpages}}", - "apihelp-query+projectpages-example-generator": "{{doc-apihelp-example|query+projectpages}}" + "apihelp-query+projectpages-example-generator": "{{doc-apihelp-example|query+projectpages}}", + "pageassessments-special": "Name of the Special page", + "pageassessments-project": "Form label for the WikiProject search field and the results table column header", + "pageassessments-page-namespace": "Form label for the page namespace dropdown select field", + "pageassessments-page-title": "Form label for the page title search field and the results table column header", + "pageassessments-search": "Search form submit button label", + "pageassessments-importance": "Label for the results table column header", + "pageassessments-class": "Label for the results table column header", + "pageassessments-timestamp": "Label for the results table column header", + "pageassessments-total-results": "Summary shown above the search results, with total number found" } diff --git a/src/NamespaceSelect.php b/src/NamespaceSelect.php new file mode 100644 index 0000000..49a09b4 --- /dev/null +++ b/src/NamespaceSelect.php @@ -0,0 +1,37 @@ +<?php + +namespace PageAssessments; + +use HTMLSelectNamespace; +use MediaWiki\Widget\NamespaceInputWidget; +use MWNamespace; + +/** + * This is an HTML form field for selecting non-talk namespaces. It excludes all namespaces with + * an even-numbered ID. + * + * It only overrides the OOUI widget because that's all that the PageAssessments special page needs. + */ +class NamespaceSelect extends HTMLSelectNamespace { + + /** + * Get the widget for selecting one or all non-talkspace namespace(s). + * @param string $value The currently selected value. + * @return NamespaceInputWidget + */ + public function getInputOOUI( $value ) { + $nsIds = array_keys( MWNamespace::getCanonicalNamespaces() ); + $excludedNsIds = array_filter( $nsIds, function( $ns ) { + return MWNamespace::isTalk( $ns ); + } ); + $widget = new NamespaceInputWidget( [ + 'value' => $value, + 'name' => $this->mName, + 'id' => $this->mID, + 'includeAllValue' => $this->mAllValue, + 'exclude' => $excludedNsIds, + ] ); + return $widget; + } + +} diff --git a/src/Pager.php b/src/Pager.php new file mode 100644 index 0000000..5a4eb09 --- /dev/null +++ b/src/Pager.php @@ -0,0 +1,148 @@ +<?php +namespace PageAssessments; + +use LikeMatch; +use MediaWiki\MediaWikiServices; +use TablePager; +use Title; + +class Pager extends TablePager { + + /** @var boolean Should field sorting be enabled? */ + protected $sortable; + + /** + * All parameters for the main paged query. + * @return string[] + */ + public function getQueryInfo() { + $info = [ + 'tables' => [ 'page_assessments', 'page_assessments_projects', 'page', 'revision' ], + 'fields' => [ + 'project' => 'pap_project_title', + 'class' => 'pa_class', + 'importance' => 'pa_importance', + 'timestamp' => 'rev_timestamp', + 'page_title' => 'page_title', + 'page_revision' => 'pa_page_revision', + 'page_namespace' => 'page_namespace', + ], + 'conds' => [], + 'options' => [], + 'join_conds' => [ + 'page_assessments_projects' => [ 'JOIN', 'pa_project_id = pap_project_id' ], + 'page' => [ 'JOIN', 'pa_page_id = page_id' ], + 'revision' => [ 'JOIN', 'page_id = rev_page AND pa_page_revision = rev_id' ], + ], + ]; + // Project. + $project = $this->getRequest()->getVal( 'project', false ); + if ( !empty( $project ) ) { + $info['conds']['pap_project_title'] = $project; + } + // Namespace (if its set, it's either an integer >= 0, 'all', or the empty string). + $namespace = $this->getRequest()->getVal( 'namespace', false ); + if ( $namespace !== 'all' && $namespace !== '' ) { + $info['conds']['page_namespace'] = $namespace; + } + $pageTitle = $this->getRequest()->getVal( 'page_title', false ); + if ( !empty( $pageTitle ) ) { + $title = Title::newFromText( $pageTitle )->getDBkey(); + $info['conds']['page_title'] = $title; + } + return $info; + } + + /** + * Should the table be sortable? It's not when transcluded. + * @param boolean $sortable Whether to sort or not. + */ + public function setSortable( $sortable ) { + $this->sortable = (bool)$sortable; + } + + /** + * Return true if the named field should be sortable by the UI, false otherwise. + * @param string $field The field in question; matches one returned by self::getFieldNames(). + * @return boolean + */ + public function isFieldSortable( $field ) { + // Done enable sorting for transcluded pagers, because the sorting links will not be to + // the current page. + if ( $this->sortable === false ) { + // Strict check, to avoid false negative when this method is used in parent::__construct + return false; + } + $sortable = [ + 'project', + 'page', + 'timestamp', + ]; + return in_array( $field, $sortable ); + } + + /** + * Format a table cell. The return value should be HTML, but use an empty + * string not   for empty cells. Do not include the <td> and </td>. + * + * The current result row is available as $this->mCurrentRow, in case you + * need more context. + * + * @param string $name The database field name + * @param string $value The value retrieved from the database + * @return string + */ + public function formatValue( $name, $value ) { + $renderer = MediaWikiServices::getInstance()->getLinkRenderer(); + $pageTitle = Title::newFromText( + $this->mCurrentRow->page_title, + $this->mCurrentRow->page_namespace + ); + + // Page title. + if ( $name === 'page' ) { + return $renderer->makeKnownLink( $pageTitle ); + } + + // Timestamp of assessed revision. + if ( $name === 'timestamp' ) { + $lang = $this->getLanguage(); + $ts = $lang->userTimeAndDate( $this->mCurrentRow->timestamp, $this->getUser() ); + $linkQuery = [ 'oldid' => $this->mCurrentRow->page_revision ]; + return $renderer->makeKnownLink( $pageTitle, $ts, [], $linkQuery ); + } + + // All field names from self::getFieldNames() have been taken care of above, + // so this shouldn't be used. + return $value; + } + + /** + * An array mapping database field names to a textual description of the + * field name, for use in the table header. The description should be plain + * text, it will be HTML-escaped later. + * + * @return array + */ + public function getFieldNames() { + return [ + 'project' => wfMessage( 'pageassessments-project' )->text(), + 'page' => wfMessage( 'pageassessments-page-title' )->text(), + 'importance' => wfMessage( 'pageassessments-importance' )->text(), + 'class' => wfMessage( 'pageassessments-class' )->text(), + 'timestamp' => wfMessage( 'pageassessments-timestamp' )->text(), + ]; + } + + /** + * The database field name used as a default sort order. + * + * @protected + * + * @return string + */ + public function getDefaultSort() { + return 'project'; + } + +} diff --git a/src/SpecialPage.php b/src/SpecialPage.php new file mode 100644 index 0000000..e31e355 --- /dev/null +++ b/src/SpecialPage.php @@ -0,0 +1,107 @@ +<?php + +namespace PageAssessments; + +use HTMLForm; +use HTMLTextField; +use SpecialPage as MediaWikiSpecialPage; + +/** + * A special page for searching Page Assessments. Can also be transcluded (in which case the + * search results' sorting links will be disabled). + */ +class SpecialPage extends MediaWikiSpecialPage { + + /** + * Create this special page, giving it a name and making it transcludable. + */ + public function __construct() { + parent::__construct(); + $this->mName = 'PageAssessments'; + $this->mIncludable = true; + } + + /** + * Do not include this one in the list of special pages at Special:SpecialPages, at least until + * it's more functional that it currently is. + * @return bool False. + */ + public function isListed() { + return false; + } + + /** + * Returns the name that goes in the \<h1\> in the special page itself, and + * also the name that will be listed in Special:Specialpages. + * + * Overridden here because we want proper sentence casing, rather than 'PageAssessments'. + * + * @return string + */ + function getDescription() { + return $this->msg( 'pageassessments-special' )->text(); + } + + /** + * Output the special page. + * @param string $parameters The parameters to the special page. + */ + public function execute( $parameters ) { + // Set up. + $out = $this->getOutput(); + $out->setPageTitle( $this->getDescription() ); + $this->addHelpLink( 'Help:Extension:PageAssessments' ); + + // Output form. + if ( !$this->including() ) { + $form = $this->getForm(); + $form->show(); + } + + // Output results, if a search has been performed. + $queryValues = $this->getRequest()->getQueryValues(); + // The request has 'title' when this special page is not transcluded. + unset( $queryValues['title'] ); + if ( count( $queryValues ) > 0 ) { + $pager = new Pager(); + $pager->setSortable( !$this->including() ); + // Summary of search resutls. + $total = wfMessage( 'pageassessments-total-results', $pager->getNumRows() ); + $out->addElement( 'p', ['class'=>'total-results'], $total ); + // Table pager. + $out->addParserOutput( $pager->getFullOutput() ); + } + } + + /** + * Get the search form. + * @return HTMLForm + */ + protected function getForm() { + $formDescriptor = [ + 'project' => [ + 'class' => HTMLTextField::class, + 'name' => 'project', + 'label-message' => 'pageassessments-project', + ], + 'namespace' => [ + 'class' => NamespaceSelect::class, + 'name' => 'namespace', + 'label-message' => 'pageassessments-page-namespace', + ], + 'page_title' => [ + 'class' => HTMLTextField::class, + 'name' => 'page_title', + 'label-message' => 'pageassessments-page-title', + ], + ]; + $form = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() ); + $form->setMethod( 'get' ); + $form->setSubmitTextMsg( 'pageassessments-search' ); + $form->setSubmitCallback( function() { + // No callback required, but HTMLForm says we have to set one. + } ); + return $form; + } + +} -- To view, visit https://gerrit.wikimedia.org/r/324657 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I6ee898059ffc62bb5f2788ad83a49b650cdb1335 Gerrit-PatchSet: 4 Gerrit-Project: mediawiki/extensions/PageAssessments Gerrit-Branch: master Gerrit-Owner: Samwilson <s...@samwilson.id.au> Gerrit-Reviewer: Kaldari <rkald...@wikimedia.org> Gerrit-Reviewer: Samwilson <s...@samwilson.id.au> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits