Skip to content

Commit 3fde5e0

Browse files
committed
Add audit filters.
1 parent cf86da6 commit 3fde5e0

File tree

6 files changed

+102
-29
lines changed

6 files changed

+102
-29
lines changed

kotti_audit/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ def kotti_configure(settings):
3737
KOTTI_USER_NAV_LINKS.append(
3838
Link('audit-log', title=_(u'Audit Log'))
3939
)
40+
settings['kotti.fanstatic.view_needed'] += ' kotti_audit.fanstatic.css_and_js'
4041

4142
if 'kotti_controlpanel.kotti_configure' not in settings['kotti.configurators']:
4243
settings['kotti.configurators'] += '\nkotti_controlpanel.kotti_configure'

kotti_audit/static/scripts.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,14 @@
11
/* JS */
2+
3+
window.datepicker = {
4+
toDisplay: function (date, format, language) {
5+
var d = new Date(date);
6+
d.setDate(d.getDate() - 7);
7+
return d.toISOString();
8+
},
9+
toValue: function (date, format, language) {
10+
var d = new Date(date);
11+
d.setDate(d.getDate() + 7);
12+
return new Date(d);
13+
}
14+
};

kotti_audit/templates/audit-log.pt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,25 @@
4747
data-url="${data_url}?${data_search}"
4848
data-page-size="50"
4949
data-id-field="id"
50+
data-filter-control="true"
5051
data-sort-name="modification_date"
51-
data-sort-order="asc">
52+
data-sort-order="desc">
5253
<thead>
5354
<tr>
5455
<th data-sortable="true" data-field="id" >ID</th>
55-
<th data-sortable="true" data-formatter="urlFormatter" data-field="title" >Title</th>
56-
<th data-sortable="true" data-field="context" >Context</th>
56+
<th data-sortable="true" data-filter-control="input" data-formatter="urlFormatter" data-field="title" >Title</th>
57+
<th data-sortable="true" data-filter-control="select" data-field="type" >Context</th>
58+
<th data-sortable="true" data-filter-control="select" data-field="parent" >Container</th>
5759
<th data-field='creation_date'
58-
data-sorter="dateSorter" data-sortable="true">
60+
data-sorter="dateSorter" data-sortable="true"
61+
data-filter-control="datepicker"
62+
data-filter-datepicker-options='{"format": "yyyy-mm-dd"}'>
5963
Created
6064
</th>
6165
<th data-field="modification_date"
62-
data-sorter="dateSorter" data-sortable="true">
66+
data-sorter="dateSorter" data-sortable="true"
67+
data-filter-control="datepicker"
68+
data-filter-datepicker-options='{"format": "yyyy-mm-dd"}'>
6369
Modified
6470
</th>
6571
</tr>

kotti_audit/utils.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from kotti.resources import Content
2+
from sqlalchemy import and_, func
3+
4+
5+
def date_query(query, column_name, value, cls=Content):
6+
column = cls.__dict__.get(column_name)
7+
return query.filter(
8+
and_(
9+
func.date(column) >= func.date(value),
10+
func.date(column) <= func.date(value)
11+
)
12+
)

kotti_audit/views/view.py

Lines changed: 63 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
Created on 2020-03-21
55
:author: Oshane Bailey (b4.oshany@gmail.com)
66
"""
7-
7+
import json
88
from pyramid.view import view_config
99
from pyramid.view import view_defaults
10+
from sqlalchemy import or_
1011

1112
from kotti_audit import _
13+
from kotti_audit import utils
1214
from kotti_audit.fanstatic import css_and_js
1315
from kotti_audit.views import BaseView
1416
from kotti.resources import Content
@@ -21,58 +23,97 @@ class AuditLogViews(BaseView):
2123
@view_config(name='audit-log', permission='view',
2224
renderer='kotti_audit:templates/audit-log.pt')
2325
def default_view(self):
24-
""" Default view for :class:`kotti_audit.resources.CustomContent`
26+
""" Default view`
2527
2628
:result: Dictionary needed to render the template.
2729
:rtype: dict
2830
"""
2931

30-
return {
31-
'foo': _(u'bar'),
32-
}
32+
return {}
3333

3434
@view_config(name="audit-api", permission="admin",
3535
renderer="json")
3636
def api_dropbox(self):
37+
""" API view`
38+
39+
:result: Dictionary needed to render the template.
40+
:rtype: dict
41+
"""
3742
limit = int(self.request.params.get("limit", 50))
3843
offset = int(self.request.params.get("offset", 0))
39-
sort = self.request.params.get("sort", "creation_date")
44+
sort = self.request.params.get("sort", "modification_date")
4045
order = self.request.params.get("order", "desc")
41-
parent_id = self.context.id if self.context.__parent__ else 0
42-
query = self.request.params.get("search", None)
46+
filters = self.request.params.get("filter", None)
47+
search = self.request.params.get("search", None)
4348

44-
nodes = Content.query;
49+
query = Content.query;
4550

51+
# Use the current context as the base location of the search,
52+
# otherwise, use root as the base location if no parent is found.
53+
parent_id = self.context.id if self.context.__parent__ else 0
4654
if parent_id != 0:
47-
nodes = nodes.filter(
55+
query = query.filter(
4856
Content.path.ilike('{}%'.format(self.context.path)),
4957
Content.id != self.context.id
5058
)
5159

52-
if query is not None:
53-
nodes = nodes.filter(Content.title.ilike("%{}%".format(query)))
60+
if filters is not None:
61+
filters = json.loads(filters)
62+
63+
if 'creation_date' in filters:
64+
creation_date = filters['creation_date']
65+
query = utils.date_query(query, 'creation_date', creation_date)
66+
67+
if 'modification_date' in filters:
68+
modification_date = filters['modification_date']
69+
query = utils.date_query(query, 'modification_date', modification_date)
70+
71+
if 'title' in filters:
72+
query = query.filter(
73+
Content.title.ilike("%{}%".format(filters['title'])))
74+
75+
if 'type' in filters:
76+
query = query.filter_by(type=filters['type'].lower())
77+
78+
if 'parent' in filters:
79+
query = query.filter(
80+
Content.parent.title.ilike(
81+
"%{}%".format(filters['parent'])))
82+
83+
84+
if search:
85+
# TODO: Add an `or` statement to find content with similar (like)
86+
# title and parent title.
87+
query = query.filter(
88+
or_(
89+
Content.title.ilike("%{}%".format(search)),
90+
Content.type==search.lower()
91+
)
92+
)
5493

5594
if order == 'asc':
56-
nodes = nodes.order_by(Content.__dict__.get(sort).asc())
95+
query = query.order_by(Content.__dict__.get(sort).asc())
5796
else:
58-
nodes = nodes.order_by(Content.__dict__.get(sort).desc())
97+
query = query.order_by(Content.__dict__.get(sort).desc())
5998

60-
total_nodes = nodes.count()
99+
total_query = query.count()
61100
if offset > 0:
62-
nodes = nodes.offset(offset)
101+
query = query.offset(offset)
63102
if limit > 0:
64-
nodes = nodes.limit(limit)
103+
query = query.limit(limit)
65104

66-
nodes_json = [{
105+
# build bootstrap table response.
106+
query_json = [{
67107
"title": f.title,
68-
"context": f.parent.title if f.parent is not None else '',
108+
"parent": f.parent.title if f.parent is not None else '',
69109
"path": f.path,
110+
'type': f.type.title(),
70111
"url": f.path,
71112
"id": f.id,
72113
"creation_date": f.creation_date.strftime("%A %d. %B %Y - %I:%M%p %z ") or '',
73114
"modification_date": f.modification_date.strftime("%A %d. %B %Y - %I:%M%p %z ") or ''
74-
} for f in nodes]
115+
} for f in query]
75116
return {
76-
"rows": nodes_json,
77-
"total": total_nodes
117+
"rows": query_json,
118+
"total": total_query
78119
}

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
except IOError:
1515
CHANGES = ''
1616

17-
version = '1.1.0'
17+
version = '1.1.1'
1818

1919
install_requires = [
2020
'Kotti>=1.2.4',
2121
'kotti_tinymce',
2222
'kotti-controlpanel>=1.0.9',
23-
'kotti_bstable'
23+
'kotti_bstable>=1.0.1'
2424
]
2525

2626
tests_require = [

0 commit comments

Comments
 (0)