diff --git a/tailbone/grids/search.py b/tailbone/grids/search.py index 2b0c01df..74ced5ee 100644 --- a/tailbone/grids/search.py +++ b/tailbone/grids/search.py @@ -85,6 +85,23 @@ class BooleanSearchFilter(SearchFilter): ["True", "False"]) +class ChoiceSearchFilter(SearchFilter): + """ + Generic search filter where the user may only select among a specific set + of choices. + """ + + def __init__(self, choices): + self.choices = choices + + def __call__(self, name, label=None, **kwargs): + super(ChoiceSearchFilter, self).__init__(name, label=label, **kwargs) + return self + + def value_control(self): + return tags.select(self.name, self.search.config.get(self.name), self.choices) + + def EnumSearchFilter(enum): options = enum.items() diff --git a/tailbone/views/bouncer.py b/tailbone/views/bouncer.py index d5a1c684..d06ef422 100644 --- a/tailbone/views/bouncer.py +++ b/tailbone/views/bouncer.py @@ -31,6 +31,7 @@ import datetime from rattail.db import model from rattail.bouncer import get_handler +from rattail.bouncer.config import get_profile_keys import formalchemy from pyramid.response import FileResponse @@ -41,7 +42,7 @@ from tailbone.db import Session from tailbone.views import SearchableAlchemyGridView, CrudView from tailbone.forms import renderers from tailbone.forms.renderers.bouncer import BounceMessageFieldRenderer -from tailbone.grids.search import BooleanSearchFilter +from tailbone.grids.search import BooleanSearchFilter, ChoiceSearchFilter class EmailBouncesGrid(SearchableAlchemyGridView): @@ -51,6 +52,10 @@ class EmailBouncesGrid(SearchableAlchemyGridView): mapped_class = model.EmailBounce config_prefix = 'emailbounces' + def __init__(self, request): + super(EmailBouncesGrid, self).__init__(request) + self.handler_options = [('', '(any)')] + sorted(get_profile_keys(self.rattail_config)) + def join_map(self): return { 'processed_by': lambda q: q.outerjoin(model.User), @@ -71,7 +76,8 @@ class EmailBouncesGrid(SearchableAlchemyGridView): return q.filter(model.EmailBounce.processed != None) return self.make_filter_map( - ilike=['config_key', 'bounce_recipient_address', 'intended_recipient_address'], + exact=['config_key'], + ilike=['bounce_recipient_address', 'intended_recipient_address'], processed={'is': processed_is, 'nt': processed_nt}, processed_by=self.filter_ilike(model.User.username)) @@ -80,6 +86,7 @@ class EmailBouncesGrid(SearchableAlchemyGridView): include_filter_config_key=True, filter_type_config_key='lk', filter_label_config_key="Source", + filter_factory_config_key=ChoiceSearchFilter(self.handler_options), filter_factory_processed=BooleanSearchFilter, filter_type_processed='is', processed=False,