Show More
@@ -25,18 +25,89 b'' | |||||
25 |
|
25 | |||
26 | import logging |
|
26 | import logging | |
27 |
|
27 | |||
28 | from pylons import request, tmpl_context as c |
|
28 | from pylons import request, tmpl_context as c, url | |
29 | from sqlalchemy.orm import joinedload |
|
29 | from sqlalchemy.orm import joinedload | |
30 | from webhelpers.paginate import Page |
|
30 | from webhelpers.paginate import Page | |
|
31 | from whoosh.qparser.default import QueryParser | |||
|
32 | from whoosh import query | |||
|
33 | from sqlalchemy.sql.expression import or_ | |||
31 |
|
34 | |||
32 | from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator |
|
35 | from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator | |
33 | from rhodecode.lib.base import BaseController, render |
|
36 | from rhodecode.lib.base import BaseController, render | |
34 | from rhodecode.model.db import UserLog |
|
37 | from rhodecode.model.db import UserLog, User | |
35 | from rhodecode.lib.utils2 import safe_int |
|
38 | from rhodecode.lib.utils2 import safe_int, remove_prefix | |
|
39 | from rhodecode.lib.indexers import JOURNAL_SCHEMA | |||
|
40 | ||||
36 |
|
41 | |||
37 | log = logging.getLogger(__name__) |
|
42 | log = logging.getLogger(__name__) | |
38 |
|
43 | |||
39 |
|
44 | |||
|
45 | def _filter(user_log, search_term): | |||
|
46 | """ | |||
|
47 | Filters sqlalchemy user_log based on search_term with whoosh Query language | |||
|
48 | http://packages.python.org/Whoosh/querylang.html | |||
|
49 | ||||
|
50 | :param user_log: | |||
|
51 | :param search_term: | |||
|
52 | """ | |||
|
53 | qry = None | |||
|
54 | if search_term: | |||
|
55 | qp = QueryParser('repository', schema=JOURNAL_SCHEMA) | |||
|
56 | qry = qp.parse(unicode(search_term)) | |||
|
57 | log.debug('Filtering using query %r' % qry) | |||
|
58 | ||||
|
59 | def get_filterion(field, val, term): | |||
|
60 | if field == 'repository': | |||
|
61 | field = getattr(UserLog, 'repository_name') | |||
|
62 | elif field == 'ip': | |||
|
63 | field = getattr(UserLog, 'user_ip') | |||
|
64 | elif field == 'date': | |||
|
65 | field = getattr(UserLog, 'action_date') | |||
|
66 | elif field == 'username': | |||
|
67 | ##special case for username | |||
|
68 | if isinstance(term, query.Wildcard): | |||
|
69 | #only support wildcards with * at beggining | |||
|
70 | val = remove_prefix(val, prefix='*') | |||
|
71 | return getattr(UserLog, 'user_id').in_( | |||
|
72 | [x.user_id for x in | |||
|
73 | User.query().filter(User.username.endswith(val))]) | |||
|
74 | elif isinstance(term, query.Prefix): | |||
|
75 | return getattr(UserLog, 'user_id').in_( | |||
|
76 | [x.user_id for x in | |||
|
77 | User.query().filter(User.username.startswith(val))]) | |||
|
78 | # term == exact match, case insensitive | |||
|
79 | field = getattr(UserLog, 'user') | |||
|
80 | val = User.get_by_username(val, case_insensitive=True) | |||
|
81 | ||||
|
82 | else: | |||
|
83 | field = getattr(UserLog, field) | |||
|
84 | ||||
|
85 | #sql filtering | |||
|
86 | if isinstance(term, query.Wildcard): | |||
|
87 | return field.endsswith(val) | |||
|
88 | elif isinstance(term, query.Prefix): | |||
|
89 | return field.startswith(val) | |||
|
90 | return field == val | |||
|
91 | ||||
|
92 | if isinstance(qry, (query.And, query.Term, query.Prefix, query.Wildcard)): | |||
|
93 | if not isinstance(qry, query.And): | |||
|
94 | qry = [qry] | |||
|
95 | for term in qry: | |||
|
96 | field = term.fieldname | |||
|
97 | val = term.text | |||
|
98 | user_log = user_log.filter(get_filterion(field, val, term)) | |||
|
99 | elif isinstance(qry, query.Or): | |||
|
100 | filters = [] | |||
|
101 | for term in qry: | |||
|
102 | field = term.fieldname | |||
|
103 | val = term.text | |||
|
104 | if isinstance(term, query.Term): | |||
|
105 | filters.append(get_filterion(field, val, term)) | |||
|
106 | user_log = user_log.filter(or_(*filters)) | |||
|
107 | ||||
|
108 | return user_log | |||
|
109 | ||||
|
110 | ||||
40 | class AdminController(BaseController): |
|
111 | class AdminController(BaseController): | |
41 |
|
112 | |||
42 | @LoginRequired() |
|
113 | @LoginRequired() | |
@@ -45,14 +116,26 b' class AdminController(BaseController):' | |||||
45 |
|
116 | |||
46 | @HasPermissionAllDecorator('hg.admin') |
|
117 | @HasPermissionAllDecorator('hg.admin') | |
47 | def index(self): |
|
118 | def index(self): | |
48 |
|
||||
49 | users_log = UserLog.query()\ |
|
119 | users_log = UserLog.query()\ | |
50 | .options(joinedload(UserLog.user))\ |
|
120 | .options(joinedload(UserLog.user))\ | |
51 |
.options(joinedload(UserLog.repository)) |
|
121 | .options(joinedload(UserLog.repository)) | |
52 | .order_by(UserLog.action_date.desc()) |
|
122 | ||
|
123 | #FILTERING | |||
|
124 | c.search_term = request.GET.get('filter') | |||
|
125 | try: | |||
|
126 | users_log = _filter(users_log, c.search_term) | |||
|
127 | except: | |||
|
128 | # we want this to crash for now | |||
|
129 | raise | |||
|
130 | ||||
|
131 | users_log = users_log.order_by(UserLog.action_date.desc()) | |||
53 |
|
132 | |||
54 | p = safe_int(request.params.get('page', 1), 1) |
|
133 | p = safe_int(request.params.get('page', 1), 1) | |
55 | c.users_log = Page(users_log, page=p, items_per_page=10) |
|
134 | ||
|
135 | def url_generator(**kw): | |||
|
136 | return url.current(filter=c.search_term, **kw) | |||
|
137 | ||||
|
138 | c.users_log = Page(users_log, page=p, items_per_page=10, url=url_generator) | |||
56 | c.log_data = render('admin/admin_log.html') |
|
139 | c.log_data = render('admin/admin_log.html') | |
57 |
|
140 | |||
58 | if request.environ.get('HTTP_X_PARTIAL_XHR'): |
|
141 | if request.environ.get('HTTP_X_PARTIAL_XHR'): |
@@ -35,7 +35,7 b' from string import strip' | |||||
35 | from shutil import rmtree |
|
35 | from shutil import rmtree | |
36 |
|
36 | |||
37 | from whoosh.analysis import RegexTokenizer, LowercaseFilter, StopFilter |
|
37 | from whoosh.analysis import RegexTokenizer, LowercaseFilter, StopFilter | |
38 | from whoosh.fields import TEXT, ID, STORED, NUMERIC, BOOLEAN, Schema, FieldType |
|
38 | from whoosh.fields import TEXT, ID, STORED, NUMERIC, BOOLEAN, Schema, FieldType, DATETIME | |
39 | from whoosh.index import create_in, open_dir |
|
39 | from whoosh.index import create_in, open_dir | |
40 | from whoosh.formats import Characters |
|
40 | from whoosh.formats import Characters | |
41 | from whoosh.highlight import highlight, HtmlFormatter, ContextFragmenter |
|
41 | from whoosh.highlight import highlight, HtmlFormatter, ContextFragmenter | |
@@ -89,6 +89,15 b' CHGSETS_SCHEMA = Schema(' | |||||
89 |
|
89 | |||
90 | CHGSET_IDX_NAME = 'CHGSET_INDEX' |
|
90 | CHGSET_IDX_NAME = 'CHGSET_INDEX' | |
91 |
|
91 | |||
|
92 | # used only to generate queries in journal | |||
|
93 | JOURNAL_SCHEMA = Schema( | |||
|
94 | username=TEXT(), | |||
|
95 | date=DATETIME(), | |||
|
96 | action=TEXT(), | |||
|
97 | repository=TEXT(), | |||
|
98 | ip=TEXT(), | |||
|
99 | ) | |||
|
100 | ||||
92 |
|
101 | |||
93 | class MakeIndex(BasePasterCommand): |
|
102 | class MakeIndex(BasePasterCommand): | |
94 |
|
103 |
@@ -6,7 +6,12 b'' | |||||
6 | </%def> |
|
6 | </%def> | |
7 |
|
7 | |||
8 | <%def name="breadcrumbs_links()"> |
|
8 | <%def name="breadcrumbs_links()"> | |
|
9 | <form id="filter_form"> | |||
|
10 | <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${c.search_term or _('quick filter...')}"/> | |||
|
11 | <input type='submit' value="${_('filter')}" class="ui-btn"/> | |||
9 | ${_('Admin journal')} |
|
12 | ${_('Admin journal')} | |
|
13 | </form> | |||
|
14 | ${h.end_form()} | |||
10 | </%def> |
|
15 | </%def> | |
11 |
|
16 | |||
12 | <%def name="page_nav()"> |
|
17 | <%def name="page_nav()"> | |
@@ -25,4 +30,16 b'' | |||||
25 | </div> |
|
30 | </div> | |
26 | </div> |
|
31 | </div> | |
27 | </div> |
|
32 | </div> | |
|
33 | ||||
|
34 | <script> | |||
|
35 | YUE.on('q_filter','click',function(){ | |||
|
36 | YUD.get('q_filter').value = ''; | |||
|
37 | }); | |||
|
38 | YUE.on('filter_form','submit',function(e){ | |||
|
39 | YUE.preventDefault(e) | |||
|
40 | var val = YUD.get('q_filter').value; | |||
|
41 | window.location = "${url.current(filter='__FILTER__')}".replace('__FILTER__',val); | |||
|
42 | }); | |||
|
43 | </script> | |||
28 | </%def> |
|
44 | </%def> | |
|
45 |
General Comments 0
You need to be logged in to leave comments.
Login now