MediaWiki master
OpenSearchDescriptionHandler.php
Go to the documentation of this file.
1<?php
2
10
15use MediaWiki\MainConfigSchema;
24
38
39 private UrlUtils $urlUtils;
40
42 private string $favicon;
43
45 private array $templates;
46
47 public function __construct( Config $config, UrlUtils $urlUtils ) {
48 $this->favicon = $config->get( MainConfigNames::Favicon );
49 $this->templates = $config->get( MainConfigNames::OpenSearchTemplates );
50 $this->urlUtils = $urlUtils;
51 }
52
53 public function execute(): Response {
54 $ctype = $this->getContentType();
55
56 $response = $this->getResponseFactory()->create();
57 $response->setHeader( 'Content-type', $ctype );
58
59 // Set an Expires header so that CDN can cache it for a short time
60 // Short enough so that the sysadmin barely notices when $wgSitename is changed
61 $expiryTime = 600; # 10 minutes
62 $response->setHeader( 'Expires', gmdate( 'D, d M Y H:i:s', time() + $expiryTime ) . ' GMT' );
63 $response->setHeader( 'Cache-control', 'max-age=600' );
64
65 $body = new StringStream();
66
67 $body->write( '<?xml version="1.0"?>' );
68 $body->write( Xml::openElement( 'OpenSearchDescription',
69 [
70 'xmlns' => 'http://a9.com/-/spec/opensearch/1.1/',
71 'xmlns:moz' => 'http://www.mozilla.org/2006/browser/search/' ] ) );
72
73 // The spec says the ShortName must be no longer than 16 characters,
74 // but 16 is *realllly* short. In practice, browsers don't appear to care
75 // when we give them a longer string, so we're no longer attempting to trim.
76 //
77 // Note: ShortName and the <link title=""> need to match; they are used as
78 // a key for identifying if the search engine has been added already, *and*
79 // as the display name presented to the end-user.
80 //
81 // Behavior seems about the same between Firefox and IE 7/8 here.
82 // 'Description' doesn't appear to be used by either.
83 $fullName = wfMessage( 'opensearch-desc' )->inContentLanguage()->text();
84 $body->write( Xml::element( 'ShortName', null, $fullName ) );
85 $body->write( Xml::element( 'Description', null, $fullName ) );
86
87 // By default we'll use the site favicon.
88 // Double-check if IE supports this properly?
89 $body->write( Xml::element( 'Image',
90 [
91 'height' => 16,
92 'width' => 16,
93 'type' => 'image/x-icon'
94 ],
95 (string)$this->urlUtils->expand( $this->favicon, PROTO_CURRENT )
96 ) );
97
98 $urls = [];
99
100 // General search template. Given an input term, this should bring up
101 // search results or a specific found page.
102 // At least Firefox and IE 7 support this.
103 $searchPage = SpecialPage::getTitleFor( 'Search' );
104 $urls[] = [
105 'type' => 'text/html',
106 'method' => 'get',
107 'template' => $searchPage->getCanonicalURL( 'search={searchTerms}' ) ];
108
109 // TODO: add v1/search/ endpoints?
110
111 foreach ( $this->templates as $type => $template ) {
112 if ( !$template ) {
113 $template = ApiOpenSearch::getOpenSearchTemplate( $type );
114 }
115
116 if ( $template ) {
117 $urls[] = [
118 'type' => $type,
119 'method' => 'get',
120 'template' => $template,
121 ];
122 }
123 }
124
125 // Allow hooks to override the suggestion URL settings in a more
126 // general way than overriding the whole search engine...
127 ( new HookRunner( $this->getHookContainer() ) )->onOpenSearchUrls( $urls );
128
129 foreach ( $urls as $attribs ) {
130 $body->write( Xml::element( 'Url', $attribs ) );
131 }
132
133 // And for good measure, add a link to the straight search form.
134 // This is a custom format extension for Firefox, which otherwise
135 // sends you to the domain root if you hit "enter" with an empty
136 // search box.
137 $body->write( Xml::element( 'moz:SearchForm', null,
138 $searchPage->getCanonicalURL() ) );
139
140 $body->write( Xml::closeElement( 'OpenSearchDescription' ) );
141
142 $response->setBody( $body );
143 return $response;
144 }
145
153 private function getContentType(): string {
154 $params = $this->getValidatedParams();
155 if ( $params['ctype'] == 'application/xml' ) {
156 // Makes testing tweaks about a billion times easier
157 return 'application/xml';
158 }
159
160 $acceptHeader = $this->getRequest()->getHeader( 'accept' );
161
162 if ( $acceptHeader ) {
163 $parser = new HttpAcceptParser();
164 $acceptableTypes = $parser->parseAccept( $acceptHeader[0] );
165
166 foreach ( $acceptableTypes as $acc ) {
167 if ( $acc['type'] === 'application/xml' ) {
168 return 'application/xml';
169 }
170 }
171 }
172
173 return 'application/opensearchdescription+xml';
174 }
175
176 protected function generateResponseSpec( string $method ): array {
177 $spec = parent::generateResponseSpec( $method );
178
179 $spec['200']['content']['application/opensearchdescription+xml']['schema']['type'] = 'string';
180
181 return $spec;
182 }
183
185 public function getParamSettings() {
186 return [
187 'ctype' => [
188 self::PARAM_SOURCE => 'query',
189 Handler::PARAM_DESCRIPTION => new MessageValue( 'rest-param-desc-opensearch-ctype' ),
190 ]
191 ];
192 }
193
194}
const PROTO_CURRENT
Definition Defines.php:222
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
static getOpenSearchTemplate( $type)
Fetch the template for a type.
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
static openElement( $element, $attribs=[])
Identical to rawElement(), but has no third parameter and omits the end tag (and the self-closing '/'...
Definition Html.php:255
static closeElement( $element)
Returns "</$element>".
Definition Html.php:319
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition Html.php:231
A class containing constants representing the names of configuration variables.
const OpenSearchTemplates
Name constant for the OpenSearchTemplates setting, for use with Config::get()
const Favicon
Name constant for the Favicon setting, for use with Config::get()
Handler for generating an OpenSearch description document.
getParamSettings()
Fetch ParamValidator settings for parameters.Every setting must include self::PARAM_SOURCE to specify...
generateResponseSpec(string $method)
Returns an OpenAPI Responses Object specification structure as an associative array.
Base class for REST route handlers.
Definition Handler.php:25
getHookContainer()
Get a HookContainer, for running extension hooks or for hook metadata.
Definition Handler.php:1124
getRequest()
Get the current request.
Definition Handler.php:336
getValidatedParams()
Fetch the validated parameters.
Definition Handler.php:936
getResponseFactory()
Get the ResponseFactory which can be used to generate Response objects.
Definition Handler.php:365
setHeader( $name, $value)
Set or replace the specified header.While header names are case-insensitive, the casing of the header...
Definition Response.php:105
A stream class which uses a string as the underlying storage.
Parent class for all special pages.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
A service to expand, parse, and otherwise manipulate URLs.
Definition UrlUtils.php:16
Module of static functions for generating XML.
Definition Xml.php:19
Value object representing a message for i18n.
Interface for configuration instances.
Definition Config.php:18
get( $name)
Get a configuration variable such as "Sitename" or "UploadMaintenance.".
array $params
The job parameters.
Copyright (C) 2011-2020 Wikimedia Foundation and others.