diff --git a/tests/admin_pages/test_studio.py b/tests/admin_pages/test_studio.py index 9df99afe4..f0a158b2f 100644 --- a/tests/admin_pages/test_studio.py +++ b/tests/admin_pages/test_studio.py @@ -208,7 +208,6 @@ def test_studio_home_recent_changes(pub): pub.cfg['admin-permissions'].update({'settings': ['x']}) pub.write_cfg() - app = login(get_app(pub)) resp = app.get('/backoffice/studio/') # no access to settings for i in range(6): @@ -246,7 +245,6 @@ def test_studio_home_recent_changes(pub): pub.cfg['admin-permissions'].update({'settings': ['x'], 'forms': ['x']}) pub.write_cfg() - app = login(get_app(pub)) resp = app.get('/backoffice/studio/') # no access to settings or forms for i in range(6): @@ -282,7 +280,6 @@ def test_studio_home_recent_changes(pub): pub.cfg['admin-permissions'].update({'settings': ['x'], 'forms': ['x'], 'workflows': ['x']}) pub.write_cfg() - app = login(get_app(pub)) resp = app.get('/backoffice/studio/') # no access to settings, forms or workflows for i in range(6): @@ -307,7 +304,6 @@ def test_studio_home_recent_changes(pub): assert 'backoffice/cards/%s/' % objects[CardDef.xml_root_node][i].id in resp objects[CardDef.xml_root_node][5].remove_self() - app = login(get_app(pub)) resp = app.get('/backoffice/studio/') # too old assert 'backoffice/cards/%s/' % objects[CardDef.xml_root_node][0].id not in resp @@ -317,6 +313,27 @@ def test_studio_home_recent_changes(pub): # deleted assert 'backoffice/cards/%s/' % objects[CardDef.xml_root_node][5].id not in resp + # all changes page: admin user can see all changes (depending on permissions) + resp = resp.click(href='all-changes/') + assert '(1-6/6)' in resp + # he can also see changes from other users + for snapshot in pub.snapshot_class.select(): + snapshot.user_id = other_user.id + snapshot.store() + + pub.cfg['admin-permissions'] = {} + pub.write_cfg() + resp = app.get('/backoffice/studio/all-changes/') + assert '(1-20/42)' in resp + resp = resp.click('') + assert '21-40/42' in resp.text + resp = resp.click('') + assert '41-42/42' in resp.text + + user.is_admin = False + user.store() + app.get('/backoffice/studio/all-changes/', status=403) + def test_studio_workflows(pub): create_superuser(pub) diff --git a/wcs/backoffice/studio.py b/wcs/backoffice/studio.py index 913b04f3c..188b8f665 100644 --- a/wcs/backoffice/studio.py +++ b/wcs/backoffice/studio.py @@ -24,17 +24,65 @@ from wcs.carddef import CardDef from wcs.data_sources import NamedDataSource from wcs.formdef import FormDef from wcs.mail_templates import MailTemplate -from wcs.qommon import _, pgettext, template +from wcs.qommon import _, misc, pgettext, template +from wcs.qommon.backoffice.listing import pagination_links from wcs.qommon.backoffice.menu import html_top from wcs.qommon.form import get_response from wcs.workflows import Workflow from wcs.wscalls import NamedWsCall +class ChangesDirectory(Directory): + _q_exports = [''] + + def _q_index(self): + get_response().breadcrumb.append(('all-changes/', pgettext('studio', 'All changes'))) + html_top(pgettext('studio', 'All Changes')) + limit = misc.get_int_or_400( + get_request().form.get('limit', get_publisher().get_site_option('default-page-size')) or 20 + ) + offset = misc.get_int_or_400(get_request().form.get('offset', 0)) + + backoffice_root = get_publisher().get_backoffice_root() + object_types = [] + if backoffice_root.is_accessible('workflows'): + object_types += [Workflow, MailTemplate] + if backoffice_root.is_accessible('forms'): + object_types += [NamedDataSource, BlockDef, FormDef] + if backoffice_root.is_accessible('workflows'): + object_types += [NamedDataSource] + if backoffice_root.is_accessible('settings'): + object_types += [NamedDataSource, NamedWsCall] + if backoffice_root.is_accessible('cards'): + object_types += [CardDef] + object_types = [ot.xml_root_node for ot in object_types] + + objects = [] + links = '' + if get_publisher().snapshot_class: + objects = get_publisher().snapshot_class.get_recent_changes( + object_types=object_types, limit=limit, offset=offset + ) + total_count = get_publisher().snapshot_class.count_recent_changes(object_types=object_types) + links = pagination_links(offset, limit, total_count, load_js=False) + + return template.QommonTemplateResponse( + templates=['wcs/backoffice/changes.html'], + context={ + 'objects': objects, + 'pagination_links': links, + }, + ) + + def is_accessible(self, user): + return user.is_admin + + class StudioDirectory(Directory): - _q_exports = ['', 'deprecations', ('logged-errors', 'logged_errors_dir')] + _q_exports = ['', 'deprecations', ('logged-errors', 'logged_errors_dir'), ('all-changes', 'changes_dir')] deprecations = DeprecationsDirectory() + changes_dir = ChangesDirectory() def __init__(self): self.logged_errors_dir = LoggedErrorsDirectory(parent_dir=self) @@ -71,10 +119,12 @@ class StudioDirectory(Directory): if backoffice_root.is_accessible('cards'): object_types += [CardDef] + user = get_request().user context = { 'has_sidebar': False, 'extra_links': extra_links, 'recent_errors': LoggedErrorsDirectory.get_errors(offset=0, limit=5)[0], + 'show_all_changes': get_publisher().snapshot_class and user and user.is_admin, } if get_publisher().snapshot_class: context['recent_objects'] = get_publisher().snapshot_class.get_recent_changes( diff --git a/wcs/snapshots.py b/wcs/snapshots.py index 0f1bd599b..090b05d19 100644 --- a/wcs/snapshots.py +++ b/wcs/snapshots.py @@ -184,8 +184,8 @@ class Snapshot: obj.store() @classmethod - def get_recent_changes(cls, object_types, user): - elements = cls._get_recent_changes(object_types, user) + def get_recent_changes(cls, object_types=None, user=None, limit=5, offset=0): + elements = cls._get_recent_changes(object_types=object_types, user=user, limit=limit, offset=offset) instances = [] for object_type, object_id, snapshot_timestamp in elements: klass = cls.get_class(object_type) diff --git a/wcs/sql.py b/wcs/sql.py index 894628420..5ba8d68e4 100644 --- a/wcs/sql.py +++ b/wcs/sql.py @@ -3793,22 +3793,46 @@ class Snapshot(SqlMixin, wcs.snapshots.Snapshot): return cls.get(row[0]) @classmethod - def _get_recent_changes(cls, object_types, user): + def _get_recent_changes(cls, object_types, user=None, limit=5, offset=0): conn, cur = get_connection_and_cursor() - sql_statement = '''SELECT object_type, object_id, MAX(timestamp) AS m - FROM snapshots - WHERE object_type IN %(object_types)s - AND user_id = %(user_id)s - GROUP BY object_type, object_id - ORDER BY m DESC - LIMIT 5''' - parameters = {'object_types': tuple(object_types), 'user_id': str(user.id)} + clause = [Contains('object_type', object_types)] + if user is not None: + clause.append(Equal('user_id', str(user.id))) + where_clauses, parameters, dummy = parse_clause(clause) + + sql_statement = 'SELECT object_type, object_id, MAX(timestamp) AS m FROM snapshots' + sql_statement += ' WHERE ' + ' AND '.join(where_clauses) + sql_statement += ' GROUP BY object_type, object_id ORDER BY m DESC' + + if limit: + sql_statement += ' LIMIT %(limit)s' + parameters['limit'] = limit + if offset: + sql_statement += ' OFFSET %(offset)s' + parameters['offset'] = offset + cur.execute(sql_statement, parameters) result = cur.fetchall() conn.commit() cur.close() return result + @classmethod + def count_recent_changes(cls, object_types): + conn, cur = get_connection_and_cursor() + + clause = [Contains('object_type', object_types)] + where_clauses, parameters, dummy = parse_clause(clause) + sql_statement = 'SELECT COUNT(*) FROM (SELECT object_type, object_id FROM snapshots' + sql_statement += ' WHERE ' + ' AND '.join(where_clauses) + sql_statement += ' GROUP BY object_type, object_id) AS s' + + cur.execute(sql_statement, parameters) + count = cur.fetchone()[0] + conn.commit() + cur.close() + return count + class LoggedError(SqlMixin, wcs.logged_errors.LoggedError): _table_name = 'loggederrors' diff --git a/wcs/templates/wcs/backoffice/changes.html b/wcs/templates/wcs/backoffice/changes.html new file mode 100644 index 000000000..4818a8835 --- /dev/null +++ b/wcs/templates/wcs/backoffice/changes.html @@ -0,0 +1,19 @@ +{% extends "wcs/backoffice/base.html" %} +{% load i18n %} + +{% block appbar-title %}{% trans "All changes" context 'studio' %}{% endblock %} + +{% block content %} + +{{ pagination_links|safe }} +{% endblock %} diff --git a/wcs/templates/wcs/backoffice/studio.html b/wcs/templates/wcs/backoffice/studio.html index ce54e2e4b..bb9455e58 100644 --- a/wcs/templates/wcs/backoffice/studio.html +++ b/wcs/templates/wcs/backoffice/studio.html @@ -40,6 +40,9 @@ {{ obj.snapshot_timestamp }} {% endfor %} + {% if show_all_changes %} +

{% trans "See all changes" %}

+ {% endif %}