148 lines
5.0 KiB
Python
148 lines
5.0 KiB
Python
import functools
|
|
import collections
|
|
import math
|
|
import peewee
|
|
import flask
|
|
|
|
from poobrains import app
|
|
import poobrains.helpers
|
|
import poobrains.form
|
|
import poobrains.storage
|
|
import poobrains.auth
|
|
|
|
|
|
class SearchField(poobrains.form.fields.Text):
|
|
pass
|
|
|
|
|
|
@app.box('search')
|
|
def box_search():
|
|
try:
|
|
Search.permissions['read'].check(flask.g.user)
|
|
return SearchForm(custom_id='box-search')
|
|
except poobrains.auth.AccessDenied:
|
|
return None
|
|
|
|
|
|
class SearchForm(poobrains.form.Form):
|
|
|
|
pattern = SearchField()
|
|
search = poobrains.form.Button('submit', value='search', label='Search')
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
if not 'action' in kwargs:
|
|
kwargs['action'] = app.site.get_view_url(Search, mode='full')
|
|
|
|
if not self.fields['pattern'].value and 'search_pattern' in flask.session:
|
|
self.fields['pattern'].value = flask.session['search_pattern']
|
|
|
|
super(SearchForm, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class Search(poobrains.auth.Protected):
|
|
|
|
form = None
|
|
offset = None
|
|
results = None
|
|
|
|
def __init__(self, handle='', offset=0, **kwargs):
|
|
|
|
super(Search, self).__init__(name=self.__class__.__name__, handle=handle)
|
|
|
|
self.form = SearchForm()
|
|
self.form.fields['pattern'].value = self.handle_string
|
|
self.form.clear = poobrains.form.Button('submit', value='clear', label='Clear')
|
|
self.offset = offset
|
|
self.results = []
|
|
|
|
|
|
@poobrains.helpers.themed
|
|
def view(self, mode='full', offset=0, **kwargs):
|
|
|
|
if flask.request.method == 'POST':
|
|
|
|
pattern = flask.request.form[self.form.name]['pattern']
|
|
|
|
if 'clear' in flask.request.form:
|
|
flask.session.pop('search_pattern', None)
|
|
return flask.redirect(app.site.get_view_url(self.__class__, mode='full'))
|
|
|
|
self.handle_string = pattern
|
|
flask.session['search_pattern'] = pattern
|
|
return flask.redirect(self.url('full'))
|
|
|
|
if len(self.handle_string) == 0 and 'search_pattern' in flask.session:
|
|
if len(flask.session['search_pattern']) >= 3:
|
|
# redirect requests with empty search handle to the saved search
|
|
self.handle_string = flask.session['search_pattern']
|
|
return flask.redirect(self.url('full'))
|
|
|
|
administerables = poobrains.auth.Administerable.class_children_keyed()
|
|
readable_administerables = []
|
|
|
|
if len(self.handle_string) >= 3:
|
|
|
|
flask.session['search_pattern'] = self.handle_string
|
|
|
|
for key in sorted(administerables):
|
|
|
|
administerable = administerables[key]
|
|
|
|
try:
|
|
administerable.permissions['read'].check(flask.g.user)
|
|
readable_administerables.append(administerable)
|
|
|
|
except poobrains.auth.AccessDenied:
|
|
pass
|
|
|
|
queries = []
|
|
|
|
for administerable in readable_administerables:
|
|
|
|
q = administerable.list('read', flask.g.user)
|
|
clauses = []
|
|
|
|
if isinstance(app.db, peewee.SqliteDatabase):
|
|
term = f"*{self.handle_string.lower()}*"
|
|
else: # postgres
|
|
term = f"%{self.handle_string.lower()}%"
|
|
|
|
if hasattr(administerable._meta, 'search_fields'):
|
|
|
|
for field_name in administerable._meta.search_fields:
|
|
|
|
field = getattr(administerable, field_name)
|
|
clauses.append((peewee.fn.Lower(field) % term))
|
|
|
|
else:
|
|
|
|
if isinstance(getattr(administerable, 'name', None), poobrains.storage.fields.CharField):
|
|
clauses.append((peewee.fn.Lower(administerable.name) % term))
|
|
|
|
if isinstance(getattr(administerable, 'title', None), poobrains.storage.fields.CharField):
|
|
clauses.append((peewee.fn.Lower(administerable.title) % term)) # LIKE clause
|
|
|
|
if isinstance(getattr(administerable, 'text', None), poobrains.storage.fields.TextField):
|
|
clauses.append((peewee.fn.Lower(administerable.text) % term)) # LIKE clause
|
|
|
|
if len(clauses):
|
|
queries.append(q.where(functools.reduce(peewee.operator.or_, clauses)))
|
|
else:
|
|
continue
|
|
|
|
|
|
pagination = poobrains.storage.Pagination(queries, offset, 'site.search_handle_offset', handle=self.handle_string)
|
|
self.results = pagination.results
|
|
self.pagination = pagination.menu
|
|
|
|
|
|
elif len(self.handle_string) > 0:
|
|
flask.flash(u"Search pattern has to be at least 3 characters long.", 'error')
|
|
|
|
return self
|
|
|
|
app.site.add_view(Search, '/search/', mode='full', endpoint='search')
|
|
app.site.add_view(Search, '/search/<handle>/', mode='full', endpoint='search_handle')
|
|
app.site.add_view(Search, '/search/<handle>/+<int:offset>', mode='full', endpoint='search_handle_offset')
|