class ISearchSettings(Interface): display_searchbox = schema.Bool( title=_(u"Display search box"), description=_(u"If this option is selected, a search input box " u"is shown on the search page."), default=True, ) collection = schema.TextLine( title=_(u"Collection"), description=_(u"You must define exactly one search collection."), required=True, default=u"default_collection", ) frontend = schema.TextLine( title=_(u"Front end"), description=_(u"You must define exactly one front end."), required=True, default=u"default_frontend", ) use_xslt_transform = schema.Bool( title=_(u"Use XSLT"), description=_(u"Apply an XSLT transform to search results " u"instead of default view."), default=False) xslt_transform = schema.Text( title=_(u"XSLT transform"), description=_(u"This transform is applied to transform the " u"search results document into HTML."), default=u"", required=False, )
class EditForm(controlpanel.RegistryEditForm): schema = ISettings fields = field.Fields(IGeneralSettings) groups = ConnectionSettingsGroup, SearchSettingsGroup label = _(u"Settings for integration with Google Search Appliance") def getContent(self): return AbstractRecordsProxy(self.schema)
def _make_request(self, query): conn = get_connection() try: conn.request( "GET", "/search?%s" % urllib.urlencode(query), headers={"Accept-language": self.get_language()} ) response = conn.getresponse() except IOError, exc: self.error = _("Search is unavailable: ${message}.", mapping={"message": str(exc)}) raise
def _make_request(self, query): conn = get_connection() try: conn.request("GET", "/search?%s" % urllib.urlencode(query), headers={ 'Accept-language': self.get_language(), }) response = conn.getresponse() except IOError, exc: self.error = _("Search is unavailable: ${message}.", mapping={'message': str(exc)}) raise
class SearchView(BrowserView): """Search view. The functionality is split out into smaller methods and properties to allow easy customization. In particular ``build_query`` returns the query that is sent to the GSA to request search results. """ template = ViewPageTemplateFile("search.pt") error = None estimated_results = 0 @property def __call__(self): if self.enabled: return self.template # fall back to default search skin skin = self.context.portal_skins.getSkin() template = getattr(skin, 'search', None) # it may not exist if template is None: raise NotFound('search') # aq-wrap in current context and return return template.__of__(self.context) @property def enabled(self): registry = component.getUtility(IRegistry) settings = registry.forInterface(IGeneralSettings) return settings.enabled @memoize_contextless def get_settings(self): registry = component.getUtility(IRegistry) return registry.forInterface(ISearchSettings) def get_collection(self): return self.get_settings().collection def get_frontend(self): return self.get_settings().frontend @property def display_searchbox(self): return self.get_settings().display_searchbox @memoize_contextless def get_language(self): return self.portal_state.language().encode('utf-8') @property def use_transform(self): return self.get_settings().use_xslt_transform @property def search_terms(self): return self.request.form.get('SearchableText') or \ self.request.form.get('q', '') @property def search_query(self): return self.query['q'] @property @memoize_contextless def query(self): return self.build_query() @property @memoize_contextless def results(self): iter = self.iter_results() return tuple(iter) @property @memoize_contextless def url(self): return self.request.getURL() @property @memoize_contextless def portal_state(self): context = aq_inner(self.context) return component.getMultiAdapter((context, self.request), name=u'plone_portal_state') @property def dynamic_navigation(self): results = [] document = self._get_results() for element in document.findall('.//PMT'): results.append( (element.attrib['NM'], element.attrib['DN'], dict((category.attrib['V'], int(category.attrib['C'])) for category in element.findall('PV')))) return results def build_query(self): query = self.request.form.copy() # main query query['q'] = self.search_terms # determine current language query['hl'] = self.get_language() # request full metadata fields query['getfields'] = '*' # XML output query['output'] = 'xml_no_dtd' # encoding query['ie'] = 'UTF-8' query['oe'] = 'UTF-8' # search collection query['site'] = self.get_collection() # search frontend query['client'] = self.get_frontend() # only public access is supported query['access'] = 'p' # don't filter results query['filter'] = 0 return query @memoize_contextless def _get_results(self): t = time.time() try: response = self._make_request(self.query) if response.status == 200: document = lxml.etree.parse(response) elapsed = time.time() - t try: self.estimated_results = int(document.find('.//M').text) query_time = float(document.find('.//TM').text) except AttributeError: query_time = -1 log.info("received %d search results in %3.3f seconds " % (self.estimated_results, elapsed) + "(query time: %3.3f seconds)." % query_time) return document else: reason = "Bad server response (%d %s)" % (response.status, response.reason) log.warn(reason) except IOError, exc: log.warn(exc) reason = str(exc) self.error = _("An error occurred during search: ${message}.", mapping={'message': reason}) return trivial_search
class SearchSettingsGroup(group.Group): label = _(u"Search settings") fields = field.Fields(ISearchSettings)
class ConnectionSettingsGroup(group.Group): label = _(u"Connection settings") fields = field.Fields(IConnectionSettings)
class IConnectionSettings(Interface): name = schema.TextLine(title=_(u"Data source name"), description=_( u"This identifies the data source within the " u"search appliance."), required=True, default=u"plone") host = schema.TextLine( title=_(u"Host"), description=_(u"The hostname or IP-address of the appliance " u"(e.g. 192.168.1.200, \"gsa.local\")."), required=True, default=u"", ) feed_protocol_port = schema.Int( title=_(u"Feed protocol port"), description=_(u"The network port for the feed protocol service. " u"Normally you do not need to change this."), default=19900, required=True, ) use_secure_search = schema.Bool( title=_(u"Secure search"), description=_(u"Use SSL-encrypted search (recommended). " u"You should disable this only if the appliance is " u"located on a private network."), default=True) unsecured_search_port = schema.Int( title=_(u"HTTP search port"), description=_(u"The network port for the unsecured search service. " u"This service runs on HTTP."), default=80, required=True, ) secured_search_port = schema.Int( title=_(u"HTTPS search port"), description=_(u"The network port for the unsecured search service. " u"This service runs on HTTPS."), default=443, required=True, ) timeout = schema.Int( title=_(u"Time out"), description=_(u"The connection time-out limit in seconds."), default=3, required=True, )
class IGeneralSettings(Interface): enabled = schema.Bool( title=_(u"Enable"), description=_(u"Integration with the Google Search Appliance is only " u"active when this field is set."), default=True)