/[drupal]/contributions/modules/opensearch/opensearch.module
ViewVC logotype

Contents of /contributions/modules/opensearch/opensearch.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.10 - (show annotations) (download) (as text)
Sun Apr 27 20:15:23 2008 UTC (19 months ago) by hunmaat
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-5, DRUPAL-6--1
Changes since 1.9: +2 -5 lines
File MIME type: text/x-php
One more clear_url issue.
1 <?php
2 // $Id: opensearch.module,v 1.9 2008/04/27 20:08:36 hunmaat Exp $
3
4 /**
5 * Implementation of hook_menu().
6 */
7 function opensearch_menu($may_cache) {
8 $items = array();
9
10 if ($may_cache) {
11 $items[] = array(
12 'path' => 'admin/settings/opensearch',
13 'title' => t('opensearch'),
14 'callback' => 'opensearch_admin_settings',
15 'access' => user_access('administer site configuration'),
16 'type' => MENU_NORMAL_ITEM,
17 );
18
19 foreach (module_list() as $name) {
20 if (module_hook($name, 'search') && $title = module_invoke($name, 'search', 'name')) {
21 $items[] = array(
22 'path' => 'opensearch/' . $name,
23 'title' => $title,
24 'callback' => 'opensearch_view',
25 'access' => user_access('search content'),
26 'type' => MENU_LOCAL_TASK);
27 }
28 }
29 }
30 else {
31 $type = (arg(0) == 'search' && arg(1) != '') ? arg(1) : 'node';
32 $search_type = check_plain(module_invoke($type, 'search', 'name'));
33 $opensearch_shortname = t(check_plain(variable_get('opensearch_shortname', '@type search')), array('@type' => $search_type));
34 drupal_add_link(array(
35 'rel' => 'search',
36 'href' => url('opensearch/'. $type, NULL, NULL, TRUE),
37 'type' => 'application/opensearchdescription+xml',
38 'title' => $opensearch_shortname
39 ));
40 }
41
42 return $items;
43 }
44
45 function opensearch_admin_settings() {
46 extract(opensearch_get_description_defaults(), EXTR_SKIP);
47
48 foreach (module_implements('search') as $type) {
49 $links[] = l("opensearch/$type", "opensearch/$type");
50 }
51
52 $form['description'] = array(
53 '#type' => 'fieldset',
54 '#title' => t('Description file'),
55 '#collapsible' => true,
56 );
57
58 $form['description']['opensearch_prefix'] = array(
59 '#type' => 'markup',
60 '#value' => '<p>' . t('Your site offers the following OpenSearch description files: %links', array('%links' => theme('item_list', $links))) . '</p>',
61 );
62 $form['description']['opensearch_shortname'] = array(
63 '#type' => 'textfield',
64 '#title' => t('Short name'),
65 '#default_value' => $opensearch_shortname,
66 '#maxlength' => 16,
67 '#size' => 16,
68 '#description' => t("A brief name that will appear in buttons, UI controls, etc., that reference this search content provider. The placeholder <code>@type</code> will be replaced with the appropriate search type (content, user etc.)"),
69 '#required' => true,
70 );
71 $form['description']['opensearch_description'] = array(
72 '#type' => 'textfield',
73 '#title' => t('Description'),
74 '#default_value' => $opensearch_description,
75 '#maxlength' => 1024,
76 '#description' => t("A human readable text description of the search content provider. The placeholder <code>@site</code> will be replaced with your site's name, and <code>@type</code> will be replaced with the appropriate search type (content, user etc.)"),
77 '#required' => true,
78 );
79 $form['description']['opensearch_contact'] = array(
80 '#type' => 'textfield',
81 '#title' => t('Contact'),
82 '#default_value' => $opensearch_contact,
83 '#maxlength' => 64,
84 '#description' => t("An email address at which the developer can be reached."),
85 );
86 $form['description']['opensearch_tags'] = array(
87 '#type' => 'textfield',
88 '#title' => t('Tags'),
89 '#default_value' => $opensearch_tags,
90 '#maxlength' => 256,
91 '#description' => t("A space-delimited set of words that are used as keywords to identify and categorize this site's search content."),
92 );
93 $form['description']['opensearch_longname'] = array(
94 '#type' => 'textfield',
95 '#title' => t('Long name'),
96 '#default_value' => $opensearch_longname,
97 '#maxlength' => 48,
98 '#size' => 48,
99 '#description' => t("The name by which this search content provider is referred to in hypertext links, etc. The placeholder <code>@site</code> will be replaced with your site's name, and <code>@type</code> will be replaced with the appropriate search type (content, user etc.)"),
100 );
101 $form['description']['opensearch_attribution'] = array(
102 '#type' => 'textfield',
103 '#title' => t('Attribution'),
104 '#default_value' => $opensearch_attribution,
105 '#maxlength' => 256,
106 '#description' => t("A list of all content sources or platforms that should be credited."),
107 );
108 $form['description']['opensearch_syndication_right'] = array(
109 '#type' => 'radios',
110 '#title' => t('Syndication right'),
111 '#default_value' => $opensearch_syndication_right,
112 '#options' => array(
113 'open' => t('open—search results can be published or re-published without restriction. This is the default.'),
114 'limited' => t('limited—search results can be published on the client site, but not further republished.'),
115 'private' => t('private—search feed may be queried, but the results may not be displayed at the client site.'),
116 'closed' => t('closed—search feed should not be queried, and will disable the column for searches.'),
117 ),
118 '#description' => t('The degree to which the search results provided by this search engine can be distributed.'),
119 );
120 $form['description']['opensearch_adult_content'] = array(
121 '#type' => 'checkbox',
122 '#title' => t('Adult content'),
123 '#description' => t('Should be checked if the search results may contain material intended only for adults.'),
124 '#default_value' => $opensearch_adult_content,
125 );
126
127 return system_settings_form('opensearch_admin_settings', $form);
128 }
129
130
131 function opensearch_form_alter($form_id, &$form) {
132 if ($form_id == 'search_form' && arg(2)) {
133 // for pagination etc.
134 $get = drupal_query_string_encode($_GET, array('q'));
135 $form['basic']['inline']['rss'] = array('#type' => 'markup', '#value' => theme('xml_icon', url('opensearch/' . arg(1). '/'. urlencode(search_get_keys()), trim($get)?$get:NULL)));
136 }
137 }
138
139 /**
140 * Menu callback; presents an opensearch results page.
141 */
142 function opensearch_view() {
143 $type = arg(1);
144 $keys = search_get_keys();
145
146 // OpenSearch pages are 1-indexed
147 $_GET['page']--;
148
149 // Only perform search if there is non-whitespace search term:
150 if (!module_hook($type, 'search')) {
151 return drupal_not_found();
152 }
153
154 if (trim($keys)) {
155 // Log the search keys:
156 watchdog('search', t('OpenSearch: %keys (@type).', array('%keys' => $keys, '@type' => module_invoke($type, 'search', 'name'))), WATCHDOG_NOTICE, l(t('results'), 'search/' . $type . '/' . $keys));
157
158 // Collect the search results:
159 $results = module_invoke($type, 'search', 'search', $keys);
160
161 return opensearch_feed($type, $keys, $results);
162 }
163 else {
164 return opensearch_description($type);
165 }
166 }
167
168 /**
169 * Return an open search results feed.
170 */
171 function opensearch_feed($type, $keys, $results) {
172 global $base_url, $locale;
173
174 $namespaces = array(
175 'xmlns:dc="http://purl.org/dc/elements/1.1/"',
176 'xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"',
177 'xmlns:relevance="http://a9.com/-/opensearch/extensions/relevance/1.0/"'
178 );
179
180 foreach ($results as $result) {
181 $items .= format_rss_item($result['title'], $result['link'], $result['snippet'], array('relevance:score' => (float)$result['score']));
182 }
183
184 $args = array(
185 'opensearch:totalResults' => $GLOBALS['pager_total_items'][0],
186 'opensearch:startIndex' => $GLOBALS['pager_page_array'][0] * 10 + 1,
187 'opensearch:itemsPerPage' => 10,
188 array('key' => 'opensearch:link', 'attributes' => array('href' => url('opensearch/'. $type, NULL, NULL, TRUE), 'type' => 'application/opensearchdescription+xml')),
189 array('key' => 'opensearch:Query', 'attributes' => array('role' => 'request', 'searchTerms' => $keys)),
190 );
191
192 $output = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
193 $output .= "<rss version=\"2.0\" xml:base=\"". url('', NULL, NULL, TRUE) ."\" ". implode(' ', $namespaces) .">\n";
194 $output .= format_rss_channel(
195 $keys .' - '. variable_get('site_name', 'drupal') .' '. module_invoke($type, 'search', 'name') .' '. t('search'),
196 $base_url,
197 variable_get('site_mission', ''),
198 $items,
199 $locale,
200 $args
201 );
202 $output .= "</rss>\n";
203
204 drupal_set_header('Content-Type: application/rss+xml; charset=utf-8');
205 print $output;
206 }
207
208 /**
209 * Defines some default values for the description file
210 *
211 * @param string $type
212 * 'node', 'user', or any other type for which hook_search is implemented
213 * @return Array $args
214 * meant to be added to the local scope of the calling function using extract()
215 */
216 function opensearch_get_description_defaults() {
217 $args = array();
218 // check_plain()s are needed for valid XML
219 $args['opensearch_shortname'] = check_plain(variable_get('opensearch_shortname', '@type search'));
220 $args['opensearch_description'] = check_plain(variable_get('opensearch_description', t('@type search for @site.')));
221 $args['opensearch_contact'] = check_plain(variable_get('opensearch_contact', variable_get('site_mail', ini_get('sendmail_from'))));
222 $args['opensearch_tags'] = check_plain(variable_get('opensearch_tags', ''));
223 $args['opensearch_longname'] = check_plain(variable_get('opensearch_longname', $args['opensearch_description']));
224 $args['opensearch_attribution'] = check_plain(variable_get('opensearch_attribution', ''));
225 $args['opensearch_syndication_right'] = check_plain(variable_get('opensearch_syndication_right', 'open'));
226 $args['opensearch_adult_content'] = check_plain(variable_get('opensearch_adult_content', 0));
227
228 return $args;
229 }
230
231 /**
232 * Return an open search description feed.
233 */
234 function opensearch_description($type) {
235 global $base_url, $locale;
236 extract(opensearch_get_description_defaults(), EXTR_SKIP);
237
238 $search_type = check_plain(module_invoke($type, 'search', 'name'));
239 $site_name = check_plain(variable_get('site_name', 'drupal'));
240 $opensearch_shortname = t($opensearch_shortname, array('@type' => $search_type));
241 $opensearch_description = t($opensearch_description, array('@site' => $site_name, '@type' => $search_type));
242 $opensearch_longname = t($opensearch_longname, array('@site' => $site_name, '@type' => $search_type));
243 $url = check_url($base_url);
244 $url_search = str_replace(array('%7B', '%7D', '&'), array('{', '}', '&amp;'), url('search/' . $type . '/{searchTerms}', 'page={startPage}', NULL, TRUE));
245 $url_opensearch = str_replace(array('%7B', '%7D', '&'), array('{', '}', '&amp;'), url('opensearch/' . $type . '/{searchTerms}', 'page={startPage}', NULL, TRUE));
246 $output = <<<DOC
247 <?xml version="1.0" encoding="UTF-8"?>
248 <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
249 <ShortName>$opensearch_shortname</ShortName>
250 <Description>$opensearch_description</Description>
251 <Contact>$opensearch_contact</Contact>
252 <Url type="text/html"
253 template="$url_search"/>
254 <Url type="application/rss+xml"
255 template="$url_opensearch"/>
256 DOC;
257
258 if ($opensearch_tags != '') {
259 $output .= "\n <Tags>$opensearch_tags</Tags>";
260 }
261 if ($opensearch_longname != '') {
262 $output .= "\n <LongName>$opensearch_longname</LongName>";
263 }
264 if ($opensearch_attribution) {
265 $output .= "\n <Attribution>$opensearch_attribution</Attribution>";
266 }
267 if ($opensearch_syndication_right) {
268 $output .= "\n <SyndicationRight>$opensearch_syndication_right</SyndicationRight>";
269 }
270 $output .= "\n <AdultContent>$opensearch_adult_content</AdultContent>";
271
272 // Locales
273 if (module_exists('locale')) {
274 $locales = locale_supported_languages();
275 $locales = $locales['name'];
276 }
277 else {
278 $locales = array($locale => $locale);
279 }
280 foreach (array_keys($locales) as $language) {
281 $output .= "\n <Language>$language</Language>";
282 }
283
284 // Images: both logo and favicon can be provided
285 // Themes haven't been initialized yet, so do it explicitely
286 init_theme();
287 $logo = theme_get_setting('logo');
288 if (theme_get_setting('toggle_logo') && ($logo_url = check_url(theme_get_setting('logo_path')))) {
289 list($width, $height, $type, $image_attributes) = @getimagesize($logo_url);
290 $logo_url = $url. '/'. $logo_url;
291 if ($height) {
292 $height = 'height="'. $height. '"';
293 }
294 if ($width) {
295 $width = 'width="'. $width. '"';
296 }
297 if ($type) {
298 $type = 'type="'. image_type_to_mime_type($type). '"';
299 }
300 $output .= "\n <Image $height $width $type>$logo_url</Image>";
301 }
302 if (theme_get_setting('toggle_favicon') && ($favicon_url = check_url(theme_get_setting('favicon')))) {
303 // The favicon URL comes (inconveniently) with an extra /
304 if (substr($favicon_url, 0, 1) == '/') {
305 $favicon_url = substr($favicon_url, 1);
306 }
307 list($width, $height, $type, $image_attributes) = @getimagesize($favicon_url);
308 $favicon_url = $url . '/' . $favicon_url;
309 if ($height) {
310 $height = 'height="'. $height. '"';
311 }
312 if ($width) {
313 $width = 'width="'. $width. '"';
314 }
315 if ($type) {
316 $type = 'type="' . image_type_to_mime_type($type) . '"';
317 }
318 $output .= "\n <Image $height $width $type>$favicon_url</Image>";
319 }
320
321 $output .= "\n</OpenSearchDescription>";
322 // application/opensearchdescription+xml is not standard.
323 drupal_set_header('Content-Type: application/xml; charset=utf-8');
324 print $output;
325 }

  ViewVC Help
Powered by ViewVC 1.1.2