diff --git a/autoload.php b/autoload.php index b871535fc51..40aa7a3ce8c 100644 --- a/autoload.php +++ b/autoload.php @@ -934,8 +934,11 @@ $wgAutoloadLocalClasses = [ 'MediaWiki\\Widget\\Search\\DidYouMeanWidget' => __DIR__ . '/includes/widget/search/DidYouMeanWidget.php', 'MediaWiki\\Widget\\Search\\FullSearchResultWidget' => __DIR__ . '/includes/widget/search/FullSearchResultWidget.php', 'MediaWiki\\Widget\\Search\\InterwikiSearchResultSetWidget' => __DIR__ . '/includes/widget/search/InterwikiSearchResultSetWidget.php', + 'MediaWiki\\Widget\\Search\\InterwikiSearchResultWidget' => __DIR__ . '/includes/widget/search/InterwikiSearchResultWidget.php', 'MediaWiki\\Widget\\Search\\SearchFormWidget' => __DIR__ . '/includes/widget/search/SearchFormWidget.php', + 'MediaWiki\\Widget\\Search\\SearchResultSetWidget' => __DIR__ . '/includes/widget/search/SearchResultSetWidget.php', 'MediaWiki\\Widget\\Search\\SearchResultWidget' => __DIR__ . '/includes/widget/search/SearchResultWidget.php', + 'MediaWiki\\Widget\\Search\\SimpleSearchResultSetWidget' => __DIR__ . '/includes/widget/search/SimpleSearchResultSetWidget.php', 'MediaWiki\\Widget\\Search\\SimpleSearchResultWidget' => __DIR__ . '/includes/widget/search/SimpleSearchResultWidget.php', 'MediaWiki\\Widget\\TitleInputWidget' => __DIR__ . '/includes/widget/TitleInputWidget.php', 'MediaWiki\\Widget\\UserInputWidget' => __DIR__ . '/includes/widget/UserInputWidget.php', diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index c7c7fb7ac75..42253abb07a 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -8575,6 +8575,25 @@ $wgLearnerMemberSince = 4; # days $wgExperiencedUserEdits = 500; $wgExperiencedUserMemberSince = 30; # days +/** + * Mapping of interwiki index prefixes to descriptors that + * can be used to change the display of interwiki search results. + * + * Descriptors are appended to CSS classes of interwiki results + * which using InterwikiSearchResultWidget. + * + * Predefined descriptors include the following words: + * definition, textbook, news, quotation, book, travel, course + * + * @par Example: + * @code + * $wgInterwikiPrefixDisplayTypes = [ + * 'iwprefix' => 'definition' + *]; + * @endcode + */ +$wgInterwikiPrefixDisplayTypes = []; + /** * For really cool vim folding this needs to be at the end: * vim: foldmarker=@{,@} foldmethod=marker diff --git a/includes/specials/SpecialSearch.php b/includes/specials/SpecialSearch.php index 34620ff9e20..b7356e75093 100644 --- a/includes/specials/SpecialSearch.php +++ b/includes/specials/SpecialSearch.php @@ -25,9 +25,11 @@ use MediaWiki\MediaWikiServices; use MediaWiki\Widget\Search\BasicSearchResultSetWidget; -use MediaWiki\Widget\Search\InterwikiSearchResultSetWidget; use MediaWiki\Widget\Search\FullSearchResultWidget; +use MediaWiki\Widget\Search\InterwikiSearchResultWidget; +use MediaWiki\Widget\Search\InterwikiSearchResultSetWidget; use MediaWiki\Widget\Search\SimpleSearchResultWidget; +use MediaWiki\Widget\Search\SimpleSearchResultSetWidget; /** * implements Special:Search - Run text & title search and display the output @@ -385,13 +387,26 @@ class SpecialSearch extends SpecialPage { // results to display. $linkRenderer = $this->getLinkRenderer(); $mainResultWidget = new FullSearchResultWidget( $this, $linkRenderer ); - $sidebarResultWidget = new SimpleSearchResultWidget( $this, $linkRenderer ); - $sidebarResultsWidget = new InterwikiSearchResultSetWidget( - $this, - $sidebarResultWidget, - $linkRenderer, - MediaWikiServices::getInstance()->getInterwikiLookup() - ); + + if ( $search->getFeatureData( 'enable-new-crossproject-page' ) ) { + + $sidebarResultWidget = new InterwikiSearchResultWidget( $this, $linkRenderer ); + $sidebarResultsWidget = new InterwikiSearchResultSetWidget( + $this, + $sidebarResultWidget, + $linkRenderer, + MediaWikiServices::getInstance()->getInterwikiLookup() + ); + } else { + $sidebarResultWidget = new SimpleSearchResultWidget( $this, $linkRenderer ); + $sidebarResultsWidget = new SimpleSearchResultSetWidget( + $this, + $sidebarResultWidget, + $linkRenderer, + MediaWikiServices::getInstance()->getInterwikiLookup() + ); + } + $widget = new BasicSearchResultSetWidget( $this, $mainResultWidget, $sidebarResultsWidget ); $out->addHTML( $widget->render( diff --git a/includes/widget/search/BasicSearchResultSetWidget.php b/includes/widget/search/BasicSearchResultSetWidget.php index 2c31bd2a434..07094afca69 100644 --- a/includes/widget/search/BasicSearchResultSetWidget.php +++ b/includes/widget/search/BasicSearchResultSetWidget.php @@ -22,7 +22,7 @@ class BasicSearchResultSetWidget { public function __construct( SpecialSearch $specialPage, SearchResultWidget $resultWidget, - InterwikiSearchResultSetWidget $sidebarWidget + SearchResultSetWidget $sidebarWidget ) { $this->specialPage = $specialPage; $this->resultWidget = $resultWidget; diff --git a/includes/widget/search/InterwikiSearchResultSetWidget.php b/includes/widget/search/InterwikiSearchResultSetWidget.php index c7384835621..af1ed0525b6 100644 --- a/includes/widget/search/InterwikiSearchResultSetWidget.php +++ b/includes/widget/search/InterwikiSearchResultSetWidget.php @@ -7,13 +7,14 @@ use MediaWiki\Linker\LinkRenderer; use SearchResultSet; use SpecialSearch; use Title; +use Html; /** * Renders one or more SearchResultSets into a sidebar grouped by * interwiki prefix. Includes a per-wiki header indicating where * the results are from. */ -class InterwikiSearchResultSetWidget { +class InterwikiSearchResultSetWidget implements SearchResultSetWidget { /** @var SpecialSearch */ protected $specialSearch; /** @var SearchResultWidget */ @@ -24,6 +25,10 @@ class InterwikiSearchResultSetWidget { protected $linkRenderer; /** @var InterwikiLookup */ protected $iwLookup; + /** @var $output */ + protected $output; + /** @var $iwPrefixDisplayTypes */ + protected $iwPrefixDisplayTypes; public function __construct( SpecialSearch $specialSearch, @@ -35,8 +40,11 @@ class InterwikiSearchResultSetWidget { $this->resultWidget = $resultWidget; $this->linkRenderer = $linkRenderer; $this->iwLookup = $iwLookup; + $this->output = $specialSearch->getOutput(); + $this->iwPrefixDisplayTypes = $specialSearch->getConfig()->get( + 'InterwikiPrefixDisplayTypes' + ); } - /** * @param string $term User provided search term * @param SearchResultSet|SearchResultSet[] $resultSets List of interwiki @@ -50,6 +58,9 @@ class InterwikiSearchResultSetWidget { $this->loadCustomCaptions(); + $this->output->addModules( 'mediawiki.special.search.commonsInterwikiWidget' ); + $this->output->addModuleStyles( 'mediawiki.special.search.interwikiwidget.styles' ); + $iwResults = []; foreach ( $resultSets as $resultSet ) { $result = $resultSet->next(); @@ -61,56 +72,99 @@ class InterwikiSearchResultSetWidget { } } - $out = ''; + $iwResultSetPos = 1; + $iwResultListOutput = ''; + foreach ( $iwResults as $iwPrefix => $results ) { - $out .= $this->headerHtml( $iwPrefix, $term ); - $out .= ""; + + $headerHtml = $this->headerHtml( $term, $iwPrefix ); + $footerHtml = $this->footerHtml( $term, $iwPrefix ); + $iwResultListOutput .= Html::rawElement( 'li', + [ + 'class' => 'iw-resultset iw-resultset--' . $iwDisplayType, + 'data-iw-resultset-pos' => $iwResultSetPos + ], + $headerHtml . + $iwResultItemOutput . + $footerHtml + ); + + $iwResultSetPos++; } - return - "
" . - "
" . - $this->specialSearch->msg( 'search-interwiki-caption' )->text() . - '
' . - $out . - "
"; + return Html::rawElement( + 'div', + [ 'id' => 'mw-interwiki-results' ], + Html::rawElement( + 'p', + [ 'class' => 'iw-headline' ], + $this->specialSearch->msg( 'search-interwiki-caption' )->escaped() + ) . + Html::rawElement( + 'ul', [ 'class' => 'iw-results', ], $iwResultListOutput + ) + ); } /** * Generates an appropriate HTML header for the given interwiki prefix * - * @param string $iwPrefix Interwiki prefix of wiki to show header for * @param string $term User provided search term + * @param string $iwPrefix Interwiki prefix of wiki to show header for * @return string HTML */ - protected function headerHtml( $iwPrefix, $term ) { + protected function headerHtml( $term, $iwPrefix ) { + + $iwDisplayType = isset( $this->iwPrefixDisplayTypes[$iwPrefix] ) + ? $this->iwPrefixDisplayTypes[$iwPrefix] + : ""; + if ( isset( $this->customCaptions[$iwPrefix] ) ) { + /* customCaptions composed by loadCustomCaptions() with pre-escaped content.*/ $caption = $this->customCaptions[$iwPrefix]; } else { $interwiki = $this->iwLookup->fetch( $iwPrefix ); $parsed = wfParseUrl( wfExpandUrl( $interwiki ? $interwiki->getURL() : '/' ) ); - $caption = $this->specialSearch->msg( 'search-interwiki-default', $parsed['host'] )->text(); + $caption = $this->specialSearch->msg( 'search-interwiki-default', $parsed['host'] )->escaped(); } - $searchLink = $this->linkRenderer->makeLink( - Title::newFromText( "$iwPrefix:Special:Search" ), - $this->specialSearch->msg( 'search-interwiki-more' )->text(), - [], - [ - 'search' => $term, - 'fulltext' => 1, - ] + + return Html::rawElement( 'div', [ 'class' => 'iw-result__header' ], + Html::rawElement( 'span', [ 'class' => 'iw-result__icon iw-result__icon--' . $iwDisplayType ] ) + . $caption ); - return - "
" . - "{$searchLink}" . - $caption . - "
"; + } + + /** + * Generates an HTML footer for the given interwiki prefix + * + * @param string $term User provided search term + * @param string $iwPrefix Interwiki prefix of wiki to show footer for + * @return string HTML + */ + protected function footerHtml( $term, $iwPrefix ) { + + $href = Title::makeTitle( NS_SPECIAL, 'Search', null, $iwPrefix )->getLocalURL( + [ 'search' => $term, 'fulltext' => 1 ] + ); + + $searchLink = Html::rawElement( + 'a', + [ 'href' => $href ], + $this->specialSearch->msg( 'search-interwiki-more' )->escaped() + ); + + return Html::rawElement( 'div', [ 'class' => 'iw-result__footer' ], $searchLink ); } protected function loadCustomCaptions() { @@ -119,7 +173,7 @@ class InterwikiSearchResultSetWidget { } $this->customCaptions = []; - $customLines = explode( "\n", $this->specialSearch->msg( 'search-interwiki-custom' )->text() ); + $customLines = explode( "\n", $this->specialSearch->msg( 'search-interwiki-custom' )->escaped() ); foreach ( $customLines as $line ) { $parts = explode( ':', $line, 2 ); if ( count( $parts ) === 2 ) { diff --git a/includes/widget/search/InterwikiSearchResultWidget.php b/includes/widget/search/InterwikiSearchResultWidget.php new file mode 100644 index 00000000000..44229b83a5e --- /dev/null +++ b/includes/widget/search/InterwikiSearchResultWidget.php @@ -0,0 +1,86 @@ +specialSearch = $specialSearch; + $this->linkRenderer = $linkRenderer; + $this->iwPrefixDisplayTypes = $specialSearch->getConfig()->get( 'InterwikiPrefixDisplayTypes' ); + } + + /** + * @param SearchResult $result The result to render + * @param string $terms Terms to be highlighted (@see SearchResult::getTextSnippet) + * @param int $position The result position, including offset + * @return string HTML + */ + public function render( SearchResult $result, $terms, $position ) { + + $title = $result->getTitle(); + $iwPrefix = $result->getTitle()->getInterwiki(); + $titleSnippet = $result->getTitleSnippet(); + $snippet = $result->getTextSnippet( $terms ); + $displayType = isset( $this->iwPrefixDisplayTypes[$iwPrefix] ) + ? $this->iwPrefixDisplayTypes[$iwPrefix] + : ""; + + if ( $titleSnippet ) { + $titleSnippet = new HtmlArmor( $titleSnippet ); + } else { + $titleSnippet = null; + } + + $link = $this->linkRenderer->makeLink( $title, $titleSnippet ); + + $redirectTitle = $result->getRedirectTitle(); + $redirect = ''; + if ( $redirectTitle !== null ) { + + $redirectText = $result->getRedirectSnippet(); + + if ( $redirectText ) { + $redirectText = new HtmlArmor( $redirectText ); + } else { + $redirectText = null; + } + + $redirect = Html::rawElement( 'span', [ 'class' => 'iw-result__redirect' ], + $this->specialSearch->msg( 'search-redirect' )->rawParams( + $this->linkRenderer->makeLink( $redirectTitle, $redirectText ) + )->escaped() + ); + } + + switch ( $displayType ) { + case 'definition': + return "
" . + "{$link} {$redirect}: " . + $snippet . + "
"; + case 'quotation': + return "
{$snippet}
" . + "
{$link} {$redirect}
"; + default: + return "
{$link} {$redirect}
" . + "
{$snippet}
"; + }; + } +} diff --git a/includes/widget/search/SearchResultSetWidget.php b/includes/widget/search/SearchResultSetWidget.php new file mode 100644 index 00000000000..6df6e65c2ac --- /dev/null +++ b/includes/widget/search/SearchResultSetWidget.php @@ -0,0 +1,18 @@ +specialSearch = $specialSearch; + $this->resultWidget = $resultWidget; + $this->linkRenderer = $linkRenderer; + $this->iwLookup = $iwLookup; + } + + /** + * @param string $term User provided search term + * @param SearchResultSet|SearchResultSet[] $resultSets List of interwiki + * results to render. + * @return string HTML + */ + public function render( $term, $resultSets ) { + if ( !is_array( $resultSets ) ) { + $resultSets = [ $resultSets ]; + } + + $this->loadCustomCaptions(); + + $iwResults = []; + foreach ( $resultSets as $resultSet ) { + $result = $resultSet->next(); + while ( $result ) { + if ( !$result->isBrokenTitle() ) { + $iwResults[$result->getTitle()->getInterwiki()][] = $result; + } + $result = $resultSet->next(); + } + } + + $out = ''; + foreach ( $iwResults as $iwPrefix => $results ) { + $out .= $this->headerHtml( $iwPrefix, $term ); + $out .= ""; + } + + return + "
" . + "
" . + $this->specialSearch->msg( 'search-interwiki-caption' )->escaped() . + '
' . + $out . + "
"; + } + + /** + * Generates an appropriate HTML header for the given interwiki prefix + * + * @param string $iwPrefix Interwiki prefix of wiki to show header for + * @param string $term User provided search term + * @return string HTML + */ + protected function headerHtml( $iwPrefix, $term ) { + if ( isset( $this->customCaptions[$iwPrefix] ) ) { + $caption = $this->customCaptions[$iwPrefix]; + } else { + $interwiki = $this->iwLookup->fetch( $iwPrefix ); + $parsed = wfParseUrl( wfExpandUrl( $interwiki ? $interwiki->getURL() : '/' ) ); + $caption = $this->specialSearch->msg( 'search-interwiki-default', $parsed['host'] )->escaped(); + } + + $href = Title::makeTitle( NS_SPECIAL, 'Search', null, $iwPrefix )->getLocalURL( + [ 'search' => $term, 'fulltext' => 1 ] + ); + $searchLink = Html::rawElement( + 'a', + [ 'href' => $href ], + $this->specialSearch->msg( 'search-interwiki-more' )->escaped() + ); + + return + "
" . + "{$searchLink}" . + $caption . + "
"; + } + + protected function loadCustomCaptions() { + if ( $this->customCaptions !== null ) { + return; + } + + $this->customCaptions = []; + $customLines = explode( "\n", $this->specialSearch->msg( 'search-interwiki-custom' )->escaped() ); + foreach ( $customLines as $line ) { + $parts = explode( ':', $line, 2 ); + if ( count( $parts ) === 2 ) { + $this->customCaptions[$parts[0]] = $parts[1]; + } + } + } +} diff --git a/languages/i18n/en.json b/languages/i18n/en.json index c771447c366..ba62bb8b66d 100644 --- a/languages/i18n/en.json +++ b/languages/i18n/en.json @@ -1002,6 +1002,7 @@ "search-interwiki-default": "Results from $1:", "search-interwiki-custom": "", "search-interwiki-more": "(more)", + "search-interwiki-more-results": "more results", "search-relatedarticle": "Related", "searchrelated": "related", "searchall": "all", diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json index d5c16a9577f..2e02447210b 100644 --- a/languages/i18n/qqq.json +++ b/languages/i18n/qqq.json @@ -1188,6 +1188,7 @@ "search-interwiki-default": "Parameters:\n* $1 - the hostname of the remote wiki from where the additional results listed below are returned", "search-interwiki-custom": "#REDIRECT [[MediaWiki:Wmf-search-interwiki-custom/qqq]]", "search-interwiki-more": "{{Identical|More}}", + "search-interwiki-more-results": "Label for a link that leads to more search results from a given wiki.", "search-relatedarticle": "This is a search result (and I guess search engine) dependent messages. I do not know how to trigger the feature. The message is displayed if the search result contains information that related pages can also be provided from the search engine. I assume this is \"More Like This\" functionality. Microsoft glossary defines MLT as \"A way to refine search by identifying the right set of documents and then locating similar documents. This allows the searcher to control the direction of the search and focus on the most fruitful lines of inquiry.\"[http://www.microsoft.com/enterprisesearch/en/us/search-glossary.aspx]\n{{Identical|Related}}", "searchrelated": "This is a search result (and I guess search engine) dependent messages. I do not know how to trigger the feature. The message is displayed if the search result contains information that related pages can also be provided from the search engine. I assume this is \"More Like This\" functionality. Microsoft glossary defines MLT as \"A way to refine search by identifying the right set of documents and then locating similar documents. This allows the searcher to control the direction of the search and focus on the most fruitful lines of inquiry.\"[http://www.microsoft.com/enterprisesearch/en/us/search-glossary.aspx]\n{{Identical|Related}}", "searchall": "{{Identical|All}}", diff --git a/resources/Resources.php b/resources/Resources.php index 70eef62f811..9bdf1a0be08 100644 --- a/resources/Resources.php +++ b/resources/Resources.php @@ -1983,6 +1983,23 @@ return [ 'styles' => 'resources/src/mediawiki.special/mediawiki.special.search.styles.css', 'targets' => [ 'desktop', 'mobile' ], ], + 'mediawiki.special.search.interwikiwidget.styles' => [ + 'styles' => 'resources/src/mediawiki.special/' + . 'mediawiki.special.search.interwikiwidget.styles.less', + 'targets' => [ 'desktop', 'mobile' ] + ], + 'mediawiki.special.search.commonsInterwikiWidget' => [ + 'scripts' => 'resources/src/mediawiki.special/mediawiki.special.search.commonsInterwikiWidget.js', + 'dependencies' => [ + 'mediawiki.Uri', + 'mediawiki.jqueryMsg' + ], + 'targets' => [ 'desktop', 'mobile' ], + 'messages' => [ + 'search-interwiki-more', + 'searchprofile-images' + ], + ], 'mediawiki.special.undelete' => [ 'scripts' => 'resources/src/mediawiki.special/mediawiki.special.undelete.js', ], diff --git a/resources/src/mediawiki.special/images/special.search/book-icon.png b/resources/src/mediawiki.special/images/special.search/book-icon.png new file mode 100644 index 00000000000..07e3ec746e7 Binary files /dev/null and b/resources/src/mediawiki.special/images/special.search/book-icon.png differ diff --git a/resources/src/mediawiki.special/images/special.search/book-icon.svg b/resources/src/mediawiki.special/images/special.search/book-icon.svg new file mode 100644 index 00000000000..6c3fa5f2aa7 --- /dev/null +++ b/resources/src/mediawiki.special/images/special.search/book-icon.svg @@ -0,0 +1,36 @@ + + + + wikisource-icon + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/src/mediawiki.special/images/special.search/course-icon.png b/resources/src/mediawiki.special/images/special.search/course-icon.png new file mode 100644 index 00000000000..9aad230aac1 Binary files /dev/null and b/resources/src/mediawiki.special/images/special.search/course-icon.png differ diff --git a/resources/src/mediawiki.special/images/special.search/course-icon.svg b/resources/src/mediawiki.special/images/special.search/course-icon.svg new file mode 100644 index 00000000000..4fab1f3d4aa --- /dev/null +++ b/resources/src/mediawiki.special/images/special.search/course-icon.svg @@ -0,0 +1,35 @@ + + + + wikiversity-icon + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/src/mediawiki.special/images/special.search/definition-icon.png b/resources/src/mediawiki.special/images/special.search/definition-icon.png new file mode 100644 index 00000000000..b279f4ed049 Binary files /dev/null and b/resources/src/mediawiki.special/images/special.search/definition-icon.png differ diff --git a/resources/src/mediawiki.special/images/special.search/definition-icon.svg b/resources/src/mediawiki.special/images/special.search/definition-icon.svg new file mode 100644 index 00000000000..1d589062440 --- /dev/null +++ b/resources/src/mediawiki.special/images/special.search/definition-icon.svg @@ -0,0 +1,22 @@ + + + + wiktionary-icon + Created with Sketch. + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/src/mediawiki.special/images/special.search/dna-icon.png b/resources/src/mediawiki.special/images/special.search/dna-icon.png new file mode 100644 index 00000000000..76ae7b97c30 Binary files /dev/null and b/resources/src/mediawiki.special/images/special.search/dna-icon.png differ diff --git a/resources/src/mediawiki.special/images/special.search/dna-icon.svg b/resources/src/mediawiki.special/images/special.search/dna-icon.svg new file mode 100644 index 00000000000..b6472d2975d --- /dev/null +++ b/resources/src/mediawiki.special/images/special.search/dna-icon.svg @@ -0,0 +1,34 @@ + + + + wikispecies-icon + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/src/mediawiki.special/images/special.search/image-icon.png b/resources/src/mediawiki.special/images/special.search/image-icon.png new file mode 100644 index 00000000000..1ebbc7414e8 Binary files /dev/null and b/resources/src/mediawiki.special/images/special.search/image-icon.png differ diff --git a/resources/src/mediawiki.special/images/special.search/image-icon.svg b/resources/src/mediawiki.special/images/special.search/image-icon.svg new file mode 100644 index 00000000000..b68762e09cc --- /dev/null +++ b/resources/src/mediawiki.special/images/special.search/image-icon.svg @@ -0,0 +1,16 @@ + + + + commons-icon + Created with Sketch. + + + + + + + + + + + \ No newline at end of file diff --git a/resources/src/mediawiki.special/images/special.search/news-icon.png b/resources/src/mediawiki.special/images/special.search/news-icon.png new file mode 100644 index 00000000000..6b79590dd37 Binary files /dev/null and b/resources/src/mediawiki.special/images/special.search/news-icon.png differ diff --git a/resources/src/mediawiki.special/images/special.search/news-icon.svg b/resources/src/mediawiki.special/images/special.search/news-icon.svg new file mode 100644 index 00000000000..996bc84b100 --- /dev/null +++ b/resources/src/mediawiki.special/images/special.search/news-icon.svg @@ -0,0 +1,31 @@ + + + + wikinews-icon + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/src/mediawiki.special/images/special.search/quotation-icon.png b/resources/src/mediawiki.special/images/special.search/quotation-icon.png new file mode 100644 index 00000000000..9d3ade30b80 Binary files /dev/null and b/resources/src/mediawiki.special/images/special.search/quotation-icon.png differ diff --git a/resources/src/mediawiki.special/images/special.search/quotation-icon.svg b/resources/src/mediawiki.special/images/special.search/quotation-icon.svg new file mode 100644 index 00000000000..0a24b3ef82e --- /dev/null +++ b/resources/src/mediawiki.special/images/special.search/quotation-icon.svg @@ -0,0 +1,12 @@ + + + + wikiquote-icon + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/resources/src/mediawiki.special/images/special.search/textbook-icon.png b/resources/src/mediawiki.special/images/special.search/textbook-icon.png new file mode 100644 index 00000000000..0de2821fadb Binary files /dev/null and b/resources/src/mediawiki.special/images/special.search/textbook-icon.png differ diff --git a/resources/src/mediawiki.special/images/special.search/textbook-icon.svg b/resources/src/mediawiki.special/images/special.search/textbook-icon.svg new file mode 100644 index 00000000000..52446b88b19 --- /dev/null +++ b/resources/src/mediawiki.special/images/special.search/textbook-icon.svg @@ -0,0 +1,28 @@ + + + + wikibooks-icon + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/src/mediawiki.special/images/special.search/travel-icon.png b/resources/src/mediawiki.special/images/special.search/travel-icon.png new file mode 100644 index 00000000000..9540e5b568a Binary files /dev/null and b/resources/src/mediawiki.special/images/special.search/travel-icon.png differ diff --git a/resources/src/mediawiki.special/images/special.search/travel-icon.svg b/resources/src/mediawiki.special/images/special.search/travel-icon.svg new file mode 100644 index 00000000000..c61da34f030 --- /dev/null +++ b/resources/src/mediawiki.special/images/special.search/travel-icon.svg @@ -0,0 +1,15 @@ + + + + wikivoyage-icon + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/resources/src/mediawiki.special/mediawiki.special.search.commonsInterwikiWidget.js b/resources/src/mediawiki.special/mediawiki.special.search.commonsInterwikiWidget.js new file mode 100644 index 00000000000..3810ac1b0a9 --- /dev/null +++ b/resources/src/mediawiki.special/mediawiki.special.search.commonsInterwikiWidget.js @@ -0,0 +1,80 @@ +( function ( mw, $ ) { + + var api = new mw.Api(), + pageUrl = new mw.Uri(), + imagesText = new mw.Message( mw.messages, 'searchprofile-images' ), + moreResultsText = new mw.Message( mw.messages, 'search-interwiki-more' ); + + function itemTemplate( results ) { + + var resultOutput = '', i, result, imageCaption, imageThumbnailSrc; + + for ( i = 0; i < results.length; i++ ) { + result = results[ i ], + imageCaption = mw.html.element( 'span', { 'class': 'iw-result__mini-gallery__caption' }, result.title ); + imageThumbnailSrc = ( result.thumbnail ) ? result.thumbnail.source : ''; + resultOutput += ''; + } + + return resultOutput; + } + + function itemWrapperTemplate( pageQuery, itemTemplateOutput ) { + + return '
  • ' + + '
    ' + + '' + + '' + imagesText.escaped() + '' + + '
    ' + + '
    ' + + /* template output has been sanitized by mw.html.element */ + itemTemplateOutput + + '
    ' + + '' + + '
  • '; + + } + + api.get( { + action: 'query', + generator: 'search', + gsrsearch: pageUrl.query.search, + gsrnamespace: mw.config.get( 'wgNamespaceIds' ).file, + gsrlimit: 3, + prop: 'pageimages', + pilimit: 3, + piprop: 'thumbnail', + pithumbsize: 300, + formatversion: 2 + } ) + .done( function ( resp ) { + var results = ( resp.query && resp.query.pages ) ? resp.query.pages : false, + multimediaWidgetTemplate; + + if ( !results ) { + return; + } + + results.sort( function( a, b ) { + return b.index - a.index; + } ); + + multimediaWidgetTemplate = itemWrapperTemplate( pageUrl.query.search, itemTemplate( results ) ); + /* we really only need to wait for document ready for DOM manipulation */ + $( function () { + $( '.iw-results' ).prepend( multimediaWidgetTemplate ); + } ); + } ); + +}( mediaWiki, jQuery ) ); diff --git a/resources/src/mediawiki.special/mediawiki.special.search.interwikiwidget.styles.less b/resources/src/mediawiki.special/mediawiki.special.search.interwikiwidget.styles.less new file mode 100644 index 00000000000..923b81a0194 --- /dev/null +++ b/resources/src/mediawiki.special/mediawiki.special.search.interwikiwidget.styles.less @@ -0,0 +1,222 @@ +/* interwiki search results */ +/*==========================*/ + +#mw-interwiki-results { + float: right; + width: 30%; +} + +.iw-headline { + font-weight: bold; + font-size: 1rem; + font-size: 16px; + opacity: 0.7; +} + +.iw-results { + list-style: none; + margin: 0; +} + +.iw-resultset { + margin-bottom: 1.2em; + background-color: #f2f4f7; + vertical-align: top; + width: 100%; + float: left; + list-style-type: none; +} + +/* clearfix */ +.iw-result:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; +} + +* html .interwiki-result { zoom: 1; } /* IE6 */ +*:first-child+html .iw-resultset { zoom: 1; } /* IE7 */ + +/* padding each .iw-resultset section seperately. +This allows us greater flexibility in the design. +For example changing the background color on the +header and footer. */ +.iw-result__header, +.iw-result__title, +.iw-result__content, +.iw-result__footer { + padding-left: 0.85em; + padding-right: 0.85em; + padding-top: 0.25em; + padding-bottom: 0.25em; +} + +/* definition titles appear inline, +to resemble a traditional dictionary definition */ +.iw-resultset--definition .iw-result__title { + display: inline; + padding: 0; +} + +.iw-resultset > div:first-child { + padding-top: 0.85em; +} + +.iw-resultset > div:last-child { + padding-bottom: 0.85em; +} + +.iw-result__title { + font-size: 16px; /* rem fallback */ + font-size: 1rem; +} + +.iw-result__title a.extiw { + color: #252525; + font-weight: bold; +} + +.iw-result__content:after { /* clearfix */ + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; +} + +.iw-result__footer { + float: right; +} + +.iw-result__icon { + display: inline-block; + width: 24px; + height: 24px; + vertical-align: middle; + margin-right: 0.25em; + background: url( images/special.search/definition-icon.svg ) no-repeat 0 0; + background-size: 100% 100%; +} + +@interwikiContentTypes: definition, travel, quotation, book, course, news, textbook, image; + +.generate-iwIcons(); + +.generate-iwIcons( @i:1 ) when ( @i =< length( @interwikiContentTypes ) ) { + @iwIcon: extract( @interwikiContentTypes, @i ); + + .iw-result__icon--@{iwIcon} { + /* stylelint-disable-next-line function-url-quotes */ + background-image: url( 'images/special.search/@{iwIcon}-icon.png' ); + /* stylelint-disable-next-line function-url-quotes */ + background-image: url( 'images/special.search/@{iwIcon}-icon.svg' ); + } + + .generate-iwIcons( @i + 1 ); +} + +/* image search result */ +.iw-result__mini-gallery { + position: relative; + float: left; + width: 60%; + height: 200px; + box-sizing: border-box; + padding: 0.25rem; +} + +.iw-result__mini-gallery__image { + display: block; + position: relative; + width: 100%; + height: 100%; + background-size: cover; + background-repeat: no-repeat; + background-position: center center; +} + +.iw-result__mini-gallery__image:hover > .iw-result__mini-gallery__caption { + visibility: visible; +} + +.iw-result__mini-gallery__image > .iw-result__mini-gallery__caption { /* image gallery text */ + visibility: hidden; + position: absolute; + bottom: 0; + left: 0; + text-align: center; + color: #fff; + text-shadow: 0 0 10px rgba( 0, 0, 0, 0.4 ); /* improves legibility on white background*/ + font-size: 0.8em; + padding: 5px; + background-color: rgba( 0, 0, 0, 0.5 ); +} + +.iw-result__mini-gallery:nth-child(2), +.iw-result__mini-gallery:nth-child(3) { /* second and third images are small */ + width: 40%; + height: 100px; +} + +/* different types of interwiki result boxes */ +/* quotation box */ +.iw-resultset--quotation .iw-result__content { + border-left: 4px solid #afb1b5; + margin-left: 1em; + padding-top: 0; + margin-top: 0.25em; +} +.iw-resultset--quotation .iw-result__title{ + margin-left: 1em; +} +.iw-result--quotation .iw-result__title:before{ + content: ' — '; + display: inline-block; +} +.iw-result--quotation .iw-result__footer { + text-align: right; +} + +/* no results +span the interwiki results across the bottom of the page. +*/ + +.mw-search-nonefound ~ #mw-search-interwiki { + width: 100%; +} + +.mw-search-nonefound ~ #mw-search-interwiki .iw-resultset { + width: 30%; + max-width: 300px; + margin-left: 0.5em; + margin-right: 0.5em; +} + +/* mobile */ +@media only screen and ( max-width:768px ) { + #mw-interwiki-results { + width: 100%; + } + .mw-search-results { + max-width: none !important; + } + .iw-resultset { + width: 45% !important; + margin-left: 0.5em !important; + margin-right: 0.5em !important; + } + +} + +@media only screen and ( max-width:600px ) { + .iw-resultset { + width: 100% !important; + margin-left: 0 !important; + margin-right: 0 !important; + max-width: none !important; + } +} diff --git a/resources/src/mediawiki.special/mediawiki.special.search.styles.css b/resources/src/mediawiki.special/mediawiki.special.search.styles.css index ebe9ed94ce0..9559f20cd34 100644 --- a/resources/src/mediawiki.special/mediawiki.special.search.styles.css +++ b/resources/src/mediawiki.special/mediawiki.special.search.styles.css @@ -29,13 +29,11 @@ .mw-search-nonefound + .mw-search-interwiki-header { margin-top: 0; } -.searchresult { - font-size: 95%; - max-width: 38em; -} + .mw-search-results { - margin-left: 0; + margin: 0; float: left; + max-width: 60%; } .mw-search-visualclear { clear: both; @@ -93,6 +91,10 @@ float: left; width: 100%; } + +/* Advanced options menu */ +/*==========================*/ + #mw-searchoptions { margin: 0; padding: 0.5em 0.75em 0.75em 0.75em; @@ -127,12 +129,14 @@ padding-left: 6em; font-size: 85%; } + #mw-search-interwiki { float: right; width: 18em; border: 1px solid #a2a9b1; margin-top: 2ex; } + .searchalttitle, #mw-search-interwiki li { font-size: 95%; @@ -153,6 +157,7 @@ background-color: #eaecf0; border-top: 1px solid #c8ccd1; } + .searchdidyoumean { font-size: 127%; margin-top: 0.8em;