diff --git a/search/indico_search/__init__.py b/search/indico_search/__init__.py index d59cc09..c4a2c7e 100644 --- a/search/indico_search/__init__.py +++ b/search/indico_search/__init__.py @@ -14,6 +14,6 @@ # You should have received a copy of the GNU General Public License # along with Indico; if not, see . -__all__ = ('SearchPluginBase',) +__all__ = ('SearchPluginBase', 'SearchEngine') -from .base import SearchPluginBase +from .base import SearchPluginBase, SearchEngine diff --git a/search/indico_search/base.py b/search/indico_search/base.py index 604edf6..52a6b71 100644 --- a/search/indico_search/base.py +++ b/search/indico_search/base.py @@ -14,10 +14,12 @@ # You should have received a copy of the GNU General Public License # along with Indico; if not, see . +from flask import session from flask_pluginengine import depends from indico.core.plugins import IndicoPlugin +from indico_search.forms import SearchForm from indico_search.plugin import SearchPlugin @@ -25,6 +27,50 @@ from indico_search.plugin import SearchPlugin class SearchPluginBase(IndicoPlugin): """Base class for search engine plugins""" + #: the SearchEngine subclass to use + engine_class = None + #: the SearchForm subclass to use + search_form = SearchForm + def init(self): super(SearchPluginBase, self).init() - SearchPlugin.instance.engine = self + SearchPlugin.instance.engine_plugin = self + + @property + def only_public(self): + """If the search engine only returns public events""" + return session.user is None + + def perform_search(self, values, obj=None, page=1): + """Performs the search. + + For documentation on the parameters and return value, see + the documentation of the :class:`SearchEngine` class. + """ + return self.engine_class(values, obj, page).process() + + +class SearchEngine(object): + """Base class for a search engine""" + + def __init__(self, values, obj, page): + """ + :param values: the values sent by the user + :param obj: object to search in (a `Category` or `Conference`) + :param page: the result page to show (if supported) + """ + self.values = values + self.obj = obj + self.page = page + self.user = session.user + + def build_url(self, **query_params): + """Creates the URL for the search request""" + raise NotImplementedError + + def process(self): + """Executes the search + + :return: an object that's passed directly to the result template + """ + raise NotImplementedError diff --git a/search/indico_search/blueprint.py b/search/indico_search/blueprint.py index 23f4a21..b85047b 100644 --- a/search/indico_search/blueprint.py +++ b/search/indico_search/blueprint.py @@ -18,4 +18,11 @@ from __future__ import unicode_literals from indico.core.plugins import IndicoPluginBlueprint +from indico_search.controllers import RHSearch + + blueprint = IndicoPluginBlueprint('search', 'indico_search') + +blueprint.add_url_rule('/search', 'search', RHSearch, methods=('GET', 'POST')) +blueprint.add_url_rule('/category//search', 'search', RHSearch, methods=('GET', 'POST')) +blueprint.add_url_rule('/event//search', 'search', RHSearch, methods=('GET', 'POST')) diff --git a/search/indico_search/controllers.py b/search/indico_search/controllers.py new file mode 100644 index 0000000..8647ab6 --- /dev/null +++ b/search/indico_search/controllers.py @@ -0,0 +1,52 @@ +# This file is part of Indico. +# Copyright (C) 2002 - 2014 European Organization for Nuclear Research (CERN). +# +# Indico is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# Indico is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Indico; if not, see . + +from __future__ import unicode_literals + +from flask import request, session +from flask_pluginengine import current_plugin + +from MaKaC.conference import ConferenceHolder, Conference, CategoryManager +from MaKaC.webinterface.rh.conferenceBase import RHCustomizable +from indico_search.views import WPSearchCategory, WPSearchEvent + + +class RHSearch(RHCustomizable): + def _checkParams(self): + if 'confId' in request.view_args: + self.obj = ConferenceHolder().getById(request.view_args['confId']) + self.obj_type = 'event' + elif 'categId' in request.view_args: + self.obj = CategoryManager().getById(request.view_args['categId']) + self.obj_type = 'category' if not self.obj.isRoot() else None + else: + self.obj = CategoryManager().getRoot() + self.obj_type = None + + try: + self.page = int(request.values['page']) + except (ValueError, KeyError): + self.page = 1 + + def _process(self): + with current_plugin.engine_plugin.plugin_context(): + form = current_plugin.search_form() + view_class = WPSearchEvent if isinstance(self.obj, Conference) else WPSearchCategory + result = None + if form.validate_on_submit(): + result = current_plugin.perform_search(form.data, self.obj, self.page) + return view_class.render_template('results.html', self.obj, only_public=current_plugin.only_public, + form=form, obj_type=self.obj_type, result=result) diff --git a/search/indico_search/forms.py b/search/indico_search/forms.py new file mode 100644 index 0000000..5b0a6f0 --- /dev/null +++ b/search/indico_search/forms.py @@ -0,0 +1,40 @@ +# This file is part of Indico. +# Copyright (C) 2002 - 2014 European Organization for Nuclear Research (CERN). +# +# Indico is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# Indico is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Indico; if not, see . + +from __future__ import unicode_literals + +from wtforms.ext.dateutil.fields import DateField +from wtforms.fields.core import StringField, SelectField +from wtforms.validators import Optional + +from indico.util.i18n import _ +from indico.util.string import strip_whitespace +from indico.web.forms.base import IndicoForm + + +FIELD_CHOICES = [('', _('Anywhere')), + ('title', _('Title')), + ('abstract', _('Description/Abstract')), + ('author', _('Author/Speaker')), + ('affiliation', _('Affiliation')), + ('keyword', _('Keyword'))] + + +class SearchForm(IndicoForm): + phrase = StringField(_('Phrase'), filters=[strip_whitespace]) + field = SelectField(_('Search in'), choices=FIELD_CHOICES) + start_date = DateField('Start Date', [Optional()], parse_kwargs={'dayfirst': True}) + end_date = DateField('End Date', [Optional()], parse_kwargs={'dayfirst': True}) diff --git a/search/indico_search/plugin.py b/search/indico_search/plugin.py index 613c7f2..fd1d46a 100644 --- a/search/indico_search/plugin.py +++ b/search/indico_search/plugin.py @@ -28,17 +28,17 @@ class SearchPlugin(IndicoPlugin): """ hidden = True - _engine = None # the search engine plugin + _engine_plugin = None # the search engine plugin @property - def engine(self): - return self._engine + def engine_plugin(self): + return self._engine_plugin - @engine.setter - def engine(self, value): - if self._engine is not None: - raise RuntimeError('Another search engine is active: {}'.format(self._engine.name)) - self._engine = value + @engine_plugin.setter + def engine_plugin(self, value): + if self._engine_plugin is not None: + raise RuntimeError('Another search engine plugin is active: {}'.format(self._engine_plugin.name)) + self._engine_plugin = value def get_blueprints(self): return blueprint diff --git a/search/indico_search/templates/results.html b/search/indico_search/templates/results.html new file mode 100644 index 0000000..8f6a8aa --- /dev/null +++ b/search/indico_search/templates/results.html @@ -0,0 +1,72 @@ +
+ {% if only_public %} +
+ {% trans %}Warning: since you are not logged in, only results from public events will appear.{% endtrans %} +
+ {% endif %} + +

+ {% trans %}Search{% endtrans %} + {% if obj_type == 'event' %} + {% trans %}Event{% endtrans %} + {% elif obj_type == 'category' %} + {% trans %}Category{% endtrans %} + {% endif %} +

+ +
+
+
+
+ Search powered by + {% block banner %}{% endblock %} +
+ +
+
+ {{ form.phrase(style='width: 300px; height: 20px; font-size: 17px; vertical-align: middle;') }} + + {% block tooltip %}{% endblock %} +
+ + + + +
+
+
+ {% block results %}{% endblock %} +
+
+ + + +{% block scripts %}{% endblock %} diff --git a/search/indico_search/views.py b/search/indico_search/views.py new file mode 100644 index 0000000..235e424 --- /dev/null +++ b/search/indico_search/views.py @@ -0,0 +1,31 @@ +# This file is part of Indico. +# Copyright (C) 2002 - 2014 European Organization for Nuclear Research (CERN). +# +# Indico is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. +# +# Indico is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Indico; if not, see . + +from __future__ import unicode_literals + +from indico.core.plugins import WPJinjaMixinPlugin +from MaKaC.webinterface.pages.category import WPCategoryDisplayBase +from MaKaC.webinterface.pages.conferences import WPConferenceDisplayBase + + +class WPSearchCategory(WPJinjaMixinPlugin, WPCategoryDisplayBase): + def _getBody(self, params): + return self._getPageContent(params) + + +class WPSearchEvent(WPJinjaMixinPlugin, WPConferenceDisplayBase): + def _getBody(self, params): + return self._getPageContent(params)