variable_get( 'google_cse_results_path', 'search/google' ), 'access' => user_access('search Google CSE'), 'title' => t('Google'), 'description' => t('Google Custom Search Engine'), 'callback' => 'google_cse_results', 'type' => MENU_LOCAL_TASK, ); } $items[] = array( 'path' => 'admin/settings/google_cse', 'access' => user_access('administer site configuration'), 'title' => t('Google CSE'), 'description' => t('Configure the Google Custom Search Engine.'), 'callback' => 'drupal_get_form', 'callback arguments' => array('google_cse_admin_settings'), 'type' => MENU_NORMAL_ITEM, ); } return $items; } /** * Admin settings page for the CSE. */ function google_cse_admin_settings() { $form = array(); $form['google_cse_cx'] = array( '#title' => t('Google Custom Search Engine ID'), '#type' => 'textfield', '#default_value' => variable_get('google_cse_cx', ''), '#description' => t('Enter your alphanumeric Google Custom Search Engine ID (click on control panel and then click on code; the only part you need is the cx value).'), ); $form['google_cse_results_display'] = array( '#title' => t('Display search results'), '#type' => 'radios', '#default_value' => variable_get('google_cse_results_display', 'here'), '#options' => array( 'here' => t('On this site (using JavaScript in a frame)'), 'xml' => t('On this site (without JavaScript or frames, requires current Google Business account)'), 'google' => t('On Google'), ), '#description' => t('Search results can be displayed on this site, using JavaScript, or on Google, which does not require JavaScript. If you have a Business account on Google Custom Search, you can also display results on this site without using JavaScript.'), ); $form['block'] = array( '#title' => t('Search form block settings'), '#type' => 'fieldset', '#collapsible' => TRUE, '#collapsed' => FALSE, ); $form['block']['google_cse_searchbox_width'] = array( '#title' => t('Searchbox width'), '#type' => 'textfield', '#maxlength' => 4, '#size' => 6, '#description' => t('Enter the desired width, in characters, of the searchbox when displayed in a block.'), '#default_value' => variable_get('google_cse_searchbox_width', 15), ); $form['ownpage'] = array( '#title' => t('Search results page settings'), '#type' => 'fieldset', '#collapsible' => TRUE, '#collapsed' => FALSE, ); $form['ownpage']['google_cse_results_path'] = array( '#title' => t('Search results path'), '#type' => 'textfield', '#maxlength' => 50, '#size' => 60, '#description' => t('Enter the desired path of the page where the search results will be displayed. This has no effect if you do not display results on your own site.'), '#default_value' => variable_get('google_cse_results_path', 'search/google'), ); $form['ownpage']['google_cse_domain'] = array( '#title' => t('Search domain'), '#type' => 'textfield', '#maxlength' => 64, '#description' => t('Enter the Google domain to use for search results, e.g. www.google.com. Only applies if you are displaying using JavaScript/frame.'), '#default_value' => variable_get('google_cse_domain', 'www.google.com'), ); $form['ownpage']['google_cse_results_title'] = array( '#title' => t('Page title'), '#type' => 'textfield', '#maxlength' => 50, '#size' => 60, '#description' => t('Enter the desired title of the page where the search results will be displayed. This has no effect if you do not display results on your own site.'), '#default_value' => variable_get('google_cse_results_title', t('Search')), ); $form['ownpage']['google_cse_results_prefix'] = array( '#title' => t('Page prefix text'), '#type' => 'textarea', '#cols' => 50, '#rows' => 4, '#description' => t('Enter text to appear on the search page before the search form. This has no effect if you do not display results on your own site.'), '#default_value' => variable_get('google_cse_results_prefix', ''), ); $form['ownpage']['google_cse_results_suffix'] = array( '#title' => t('Page suffix text'), '#type' => 'textarea', '#cols' => 50, '#rows' => 4, '#description' => t('Enter text to appear on the search page after the search form and results. This has no effect if you do not display results on your own site.'), '#default_value' => variable_get('google_cse_results_suffix', ''), ); $form['ownpage']['google_cse_searchbox_header'] = array( '#title' => t('Searchbox title'), '#type' => 'textfield', '#maxlength' => 50, '#size' => 60, '#description' => t('Enter text to be displayed above the search form on the results page. This has no effect if you do not display results on your own site.'), '#default_value' => variable_get('google_cse_searchbox_header', t('Enter the terms you wish to search for')), ); $form['ownpage']['google_cse_results_searchbox_width'] = array( '#title' => t('Searchbox width'), '#type' => 'textfield', '#maxlength' => 4, '#size' => 6, '#description' => t('Enter the desired width, in characters, of the searchbox when displayed on the results page. This has no effect if you do not display results on your own site.'), '#default_value' => variable_get('google_cse_results_searchbox_width', 40), ); $form['ownpage']['google_cse_results_gadget'] = array( '#title' => t('Display "Add to Google" Google Gadget'), '#type' => 'checkbox', '#default_value' => variable_get('google_cse_results_gadget', 1), '#description' => t('If enabled, an "Add to Google" button will be displayed above the search results. This has no effect if you do not display results on your own site.'), ); $form['ownpage']['google_cse_results_header'] = array( '#title' => t('Header for results'), '#type' => 'textfield', '#maxlength' => 50, '#size' => 60, '#description' => t('Enter header text to be displayed after the search form and before the search results. This has no effect if you do not display results on your own site.'), '#default_value' => variable_get('google_cse_results_header', t('Search Results')), ); $form['ownpage']['google_cse_results_num'] = array( '#title' => t('Number of search results per page'), '#type' => 'textfield', '#maxlength' => 4, '#size' => 6, '#description' => t('Enter the number of search results to display per page. Only applies if you are displaying on this site using non-Javascript/business format.'), '#default_value' => variable_get('google_cse_results_num', 10), ); $form['ownpage']['google_cse_results_width'] = array( '#title' => t('Search results frame width'), '#type' => 'textfield', '#maxlength' => 4, '#size' => 6, '#description' => t('Enter the desired width, in pixels, of the search frame. Only applies if you are displaying using JavaScript/frame.'), '#default_value' => variable_get('google_cse_results_width', 600), ); $form['advanced'] = array( '#title' => t('Advanced settings'), '#type' => 'fieldset', '#collapsible' => TRUE, '#collapsed' => TRUE, ); $form['advanced']['google_cse_cr'] = array( '#title' => t('Country restriction'), '#type' => 'textfield', '#default_value' => variable_get('google_cse_cr', ''), '#description' => t('Enter a 9-letter country code, e.g. countryNZ, and optional boolean operators, to restrict search results to documents (not) originating in particular countries. See the cr parameter.'), ); $form['advanced']['google_cse_gl'] = array( '#title' => t('Country boost'), '#type' => 'textfield', '#default_value' => variable_get('google_cse_gl', ''), '#description' => t('Enter a 2-letter country code, e.g. uk, to boost documents written in a particular country. See the gl parameter.'), ); $form['advanced']['google_cse_hl'] = array( '#title' => t('Interface language'), '#type' => 'textfield', '#default_value' => variable_get('google_cse_hl', ''), '#description' => t('Enter a supported 2- or 5-character language code, e.g. fr, to set the language of the user interface. See the hl parameter.'), ); $form['advanced']['google_cse_locale_hl'] = array( '#title' => t('Set interface language dynamically'), '#type' => 'checkbox', '#default_value' => variable_get('google_cse_locale_hl', ''), '#description' => t('The language restriction can be set dynamically if the locale module is enabled. Note the locale language code must match one of the supported language codes.'), ); $form['advanced']['google_cse_ie'] = array( '#title' => t('Input encoding'), '#type' => 'textfield', '#default_value' => variable_get('google_cse_ie', ''), '#description' => t('The default utf8 is recommended. See the ie parameter.'), ); $form['advanced']['google_cse_lr'] = array( '#title' => t('Language restriction'), '#type' => 'textfield', '#default_value' => variable_get('google_cse_lr', ''), '#description' => t('Enter a supported 7- or 10-character language code, e.g. lang_en, and optional boolean operators, to restrict search results to documents (not) written in particular languages. See the lr parameter.'), ); $form['advanced']['google_cse_locale_lr'] = array( '#title' => t('Set language restriction dynamically'), '#type' => 'checkbox', '#default_value' => variable_get('google_cse_locale_lr', ''), '#description' => t('The language restriction can be set dynamically if the locale module is enabled. Note the locale language code must match one of the supported language codes.'), ); $form['advanced']['google_cse_oe'] = array( '#title' => t('Output encoding'), '#type' => 'textfield', '#default_value' => variable_get('google_cse_oe', ''), '#description' => t('The default utf8 is recommended. See the oe parameter.'), ); $form['advanced']['google_cse_safe'] = array( '#title' => t('SafeSearch filter'), '#type' => 'select', '#options' => array('' => '', 'off' => t('Off'), 'medium' => t('Medium'), 'high' => t('High')), '#default_value' => variable_get('google_cse_safe', ''), '#description' => t('SafeSearch filters search results for adult content. See the safe parameter.'), ); return system_settings_form($form); } /** * Implementation of hook_block(). */ function google_cse_block($op = 'list', $delta = 0, $edit = array()) { if ($op == 'list') { $blocks = array(); $blocks[] = array('info' => t('Google CSE')); return $blocks; } else if ($op == 'view' && user_access('search Google CSE')) { return array('subject' => t('Search'), 'content' => drupal_get_form('google_cse_searchbox_form')); } } /** * Implementation of hook_forms(); */ function google_cse_forms() { $forms = array(); $forms['google_cse_searchbox_form'] = array( 'callback' => 'google_cse_searchbox_formbuilder', 'callback arguments' => array('searchbox'), ); $forms['google_cse_results_searchbox_form'] = array( 'callback' => 'google_cse_searchbox_formbuilder', 'callback arguments' => array('results_searchbox'), ); $forms['google_cse_xml_results_searchbox_form'] = array( 'callback' => 'google_cse_searchbox_formbuilder', 'callback arguments' => array('results_xml_searchbox'), ); return $forms; } /** * Form builder for the searchbox forms. */ function google_cse_searchbox_formbuilder($form_id) { global $locale; $form = array(); drupal_add_css(drupal_get_path('module', 'google_cse') .'/google_cse.css'); // The default form. if (variable_get('google_cse_results_display', 'here') != 'google' ) { $form['#action'] = url( variable_get( 'google_cse_results_path', 'search/google' )); $q = 'query'; // parameter on Drupal site $cof = 'FORID:11'; } else { $form['#action'] = 'http://www.google.com/cse'; $q = 'q'; // parameter on Google $cof = 'FORID:0'; } $form['#method'] = 'get'; $form[$q] = array( '#type' => 'textfield', '#default_value' => $_GET['query'], ); $form['sa'] = array( '#type' => 'submit', '#value' => t('Search'), ); // items needed for non-XML-display versions of search form, // whether it is in the block or on the results page if( variable_get('google_cse_results_display', 'here') != 'xml' ) { $form['cx'] = array( '#type' => 'hidden', '#value' => variable_get('google_cse_cx', ''), ); $form['cof'] = array( '#type' => 'hidden', '#value' => $cof, ); foreach (google_cse_advanced_settings() as $parameter => $setting) { $form[$parameter] = array( '#type' => 'hidden', '#value' => $setting, ); } drupal_add_js(array('googleCSE' => array('locale' => array($locale))), 'setting', 'header'); drupal_add_js(drupal_get_path('module', 'google_cse') .'/google_cse.js', 'module', 'footer'); } // Other small differences between forms if( $form_id == 'searchbox' ) { $form[$q]['#size'] = intval(variable_get('google_cse_searchbox_width', 15)); $form[$q]['#attributes']['title'] = t('Enter the terms you wish to search for.'); } else { $form[$q]['#size'] = intval(variable_get('google_cse_results_searchbox_width', 40)); $form[$q]['#title'] = variable_get('google_cse_searchbox_header', t('Enter the terms you wish to search for')); $form['sa']['#suffix'] = google_cse_results_gadget(); } return $form; } /** * Returns an array of any advanced settings which have been set. */ function google_cse_advanced_settings() { global $locale; $settings = array(); foreach (array('cr', 'gl', 'hl', 'ie', 'lr', 'oe', 'safe') as $parameter) { if ($setting = variable_get("google_cse_$parameter", '')) { $settings[$parameter] = $setting; } } if (variable_get('google_cse_locale_hl', '')) { $settings['hl'] = $locale; } if (variable_get('google_cse_locale_lr', '')) { $settings['lr'] = 'lang_'. $locale; } return $settings; } /** * Display an Add-to-Google button. */ function google_cse_results_gadget() { if (variable_get('google_cse_results_gadget', 1) && $id = explode(':', variable_get('google_cse_cx', ''))) { $output = '
'; $output .= ''; $output .= 'Add to Google'; $output .= '
'; return $output; } else { return ''; } } /** * Render the search page and custom title. */ function google_cse_results() { drupal_set_title(check_plain(variable_get('google_cse_results_title', t('Search')))); if( variable_get('google_cse_results_display', 'here') == 'here' ) { print theme('page', theme('google_cse_results')); } else { $results = _google_cse_get_search_results(); print theme( 'page', theme( 'google_cse_search_result_items', $results )); } } /* Queries Google to get XML results, and parses them into an array * of search items -- see theme_google_cse_result_items() function below * for documentation of this array. */ function _google_cse_get_search_results() { // formulate the query for Google $url = 'http://www.google.com/search'; $query = array( 'q' => $_GET['query'], 'start' => $_GET['start'], 'num' => variable_get( 'google_cse_results_num', 10), 'client' => 'google-csbe', 'output' => 'xml_no_dtd', 'cx' => variable_get('google_cse_cx', ''), ) + google_cse_advanced_settings(); // get the XML results $res = drupal_http_request( $url . '?' . drupal_query_string_encode( $query )); if( $res->code != '200' ) { return array(); } // parse results and return them return _google_cse_parse_xml_results( $res->data ); } /** * The search results page can be themed/customized. This is the * function for theming the JavaScript/Frame version of results page. */ function theme_google_cse_results() { $query = array( 'q' => $_GET['query'], 'cx' => variable_get('google_cse_cx', ''), 'cof' => 'FORID:0', ) + google_cse_advanced_settings(); $output = filter_xss_admin(variable_get('google_cse_results_prefix', '')); $output .= drupal_get_form('google_cse_results_searchbox_form'); $output .= '
'; $output .= '

' . check_plain( variable_get('google_cse_results_header', t('Search Results'))) . "

\n"; $output .= '
'; $output .= filter_xss_admin(variable_get('google_cse_results_suffix', '')); return $output; } /** * The search results page can be themed/customized. This is the * function for theming the Business search results page. * * The input is a "results" array. * * Component 'items' is an array of search results, * where each item is an array with the * following components (not all are displayed in this default function): * 'url' - URL of search result * 'title' - Title of search result * 'crawldate' - Date page was last indexed by Google * 'excerpt' - Excerpt of page with search terms highlighted * 'lang' - Google's guess as to the language of the search result * 'crowd_host' - Indicates that lots more results were found on this * host, and you could do another query on this host to find more * results (value is a URL-encoded host name to be used in a query) * * Components 'from', 'to', and 'total' * indicate "Viewing results [from] to [to] of approximately [total]'. * * */ function theme_google_cse_search_result_items( $results ) { $output = filter_xss_admin(variable_get('google_cse_results_prefix', '')); $output .= drupal_get_form('google_cse_xml_results_searchbox_form'); $output .= '
' . "\n"; $output .= '

' . check_plain( variable_get('google_cse_results_header', t('Search Results'))) . "

\n"; if( empty( $results ) || empty( $results['items'])) { $output .= '

' . t( "Your search returned no results. Please try a different search." ) . "

\n"; } else { $nav = '

' . t( 'Viewing results !from to !to of approximately !total', array( '!from' => intval( $results['from'] ), '!to' => intval( $results['to'] ), '!total' => intval( $results['total'] ))); $perpage = variable_get( 'google_cse_results_num', 10); $query = array( 'query' => $_GET['query'] ); if( $results['from'] > 1 ) { $newstart = intval( $results['from'] ) - $perpage - 1; if( $newstart < 0 ) { $newstart = 0; } $query['start'] = $newstart; $nav .= ' ' . l( t( 'Previous page' ), variable_get( 'google_cse_results_path', 'search/google' ), '', drupal_query_string_encode( $query )); } if( $results['to'] < $results['total'] ) { $query['start'] = intval( $results['to'] ); $nav .= ' ' . l( t( 'Next page' ), variable_get( 'google_cse_results_path', 'search/google' ), '', drupal_query_string_encode( $query )); } $nav .= "

\n"; $output .= $nav; foreach( $results['items'] as $item ) { $output .= '
' . "\n"; $output .= '

' . l( $item['title'], $item['url'], array(), NULL, NULL, FALSE, TRUE ) . "

\n"; $output .= '

' . filter_xss_admin( $item['excerpt'] ). "

\n"; $output .= '

' . l( $item['url'], $item['url'] ) . "

\n"; $output .= "
\n"; } } $output .= $nav; $output .= "
\n"; $output .= filter_xss_admin(variable_get('google_cse_results_suffix', '')); return $output; } /** * Implementation of hook_perm(). */ function google_cse_perm() { return array('search Google CSE'); } /* * Internal function: parses the XML results returned by Google into * a results array (see function theme_google_cse_result_items for doc) */ function _google_cse_parse_xml_results( $data ) { global $google_cse_elem; global $google_cse_item_index; global $google_cse_results; $xml_p = drupal_xml_parser_create( $data ); xml_set_element_handler( $xml_p, '_google_cse_xml_elem_start', '_google_cse_xml_elem_end' ); xml_set_character_data_handler( $xml_p, '_google_cse_xml_character_data' ); $google_cse_elem = ''; $google_cse_item_index = 0; $google_cse_results = array( 'items' => array() ); if( !xml_parse( $xml_p, $data, 1 )) { return array(); } return $google_cse_results; } /* * Internal function for handling XML elements when parsing Google results. */ function _google_cse_xml_elem_start( $parser, $name, $attribs = array() ) { global $google_cse_elem; global $google_cse_item_index; global $google_cse_results; global $google_cse_partial_data; // we only care about some of the XML elements in the Google results // See http://www.google.com/coop/docs/cse/resultsxml.html for doc switch( $name ) { case 'RES': // attributes give the from/to indices of this results set $google_cse_results['from'] = $attribs['SN']; $google_cse_results['to'] = $attribs['EN']; break; case 'M': // gives approx total number of results of search $google_cse_elem = 'total'; break; case 'R': // starts a new search result item $google_cse_item_index = count( $google_cse_results['items'] ); $google_cse_results['items'][$google_cse_item_index] = array(); break; // The rest of these are components of search result items case 'U': $google_cse_elem = 'url'; break; case 'T': $google_cse_elem = 'title'; break; case 'CRAWLDATE': $google_cse_elem = 'crawldate'; break; case 'S': $google_cse_elem = 'excerpt'; break; case 'LANG': $google_cse_elem = 'lang'; break; case 'HN': $google_cse_elem = 'crowd_host'; break; // anything else: make sure we are not getting data now! default: $google_cse_elem = ''; break; } // this flag makes sure the character data handler gets reset $google_cse_partial_data = 0; } /* * Internal function for handling XML elements when parsing Google results */ function _google_cse_xml_elem_end( $parser, $name ) { global $google_cse_elem; global $google_cse_item_index; global $google_cse_partial_data; // close out the element and make sure character data handler gets reset $google_cse_elem = ''; $google_cse_partial_data = 0; } /* * Internal function for handling XML elements when parsing Google results */ function _google_cse_xml_character_data( $parser, $data ) { global $google_cse_elem; global $google_cse_item_index; global $google_cse_results; global $google_cse_partial_data; if( $google_cse_elem == 'total' ) { // handle the "total" element if( !$google_cse_partial_data ) { $google_cse_results['total'] = ''; } $google_cse_results['total'] .= $data; } else if( array_search( $google_cse_elem, array( 'url', 'title', 'crawldate', 'excerpt', 'lang', 'crowd_host' )) !== FALSE ) { // handle the item sub-elements if( !$google_cse_partial_data ) { $google_cse_results['items'][$google_cse_item_index][$google_cse_elem] = ''; } $google_cse_results['items'][$google_cse_item_index][$google_cse_elem] .= html_entity_decode( $data ); } // sometimes this gets called multiple times within the same element, // due to line breaks or whatever, so make sure we append data $google_cse_partial_data = 1; }