diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 88d143b9..0a234c2e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -55,7 +55,6 @@ test:lint:3.8: - tags - rc - demo - allow_failure: true test:py:3.6: stage: test @@ -106,7 +105,6 @@ test:py:3.8: artifacts: reports: junit: .reports/burpui.junit.xml - allow_failure: true build:py3: stage: build diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4b3dd321..4117d9e9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -18,6 +18,7 @@ Current - Add: allow to delete clients data upon removal `#232 `_ - Add: allow to create clients from templates in one call `#266 `_ - Add: allow to rename clients/templates `#274 `_ +- Add: allow to set a custom timezone in which to display UI dates `#329 `_ - Fix: sync pkgs requirements with burp-ui's `#300 `__ - Fix: wrong command suggestion `#296 `__ - Fix: allow templates removal `#290 `__ diff --git a/burpui/api/prefs.py b/burpui/api/prefs.py index 76c19fcc..78560a7d 100644 --- a/burpui/api/prefs.py +++ b/burpui/api/prefs.py @@ -149,6 +149,12 @@ class PrefsUI(Resource): required=False, help='Date format' ) + parser.add_argument( + 'timezone', + type=str, + required=False, + help='Timezone' + ) @staticmethod def _user_language(language): diff --git a/burpui/app.py b/burpui/app.py index ee32c505..a4944061 100644 --- a/burpui/app.py +++ b/burpui/app.py @@ -381,6 +381,7 @@ def create_app(conf=None, verbose=0, logfile=None, **kwargs): g.locale = get_locale() g.now = round(time.time()) g.date_format = session.get('dateFormat', 'llll') + g.timezone = session.get('timezone') # make sure to store secure cookie if required if app.config['BUI_SCOOKIE']: criteria = ( diff --git a/burpui/static/vendor b/burpui/static/vendor index 7e90a146..8a343173 160000 --- a/burpui/static/vendor +++ b/burpui/static/vendor @@ -1 +1 @@ -Subproject commit 7e90a1461b1c118bbc190c003184365890320729 +Subproject commit 8a343173cd3b7fd07a27579ff291fb144f594a2e diff --git a/burpui/templates/gerard.js b/burpui/templates/gerard.js index 7276342e..883e5558 100644 --- a/burpui/templates/gerard.js +++ b/burpui/templates/gerard.js @@ -7,6 +7,12 @@ var SESSION_TAG = $('meta[name=session]').attr("content"); var _EXTRA = $('meta[name=_extra]').attr('content'); var AJAX_CACHE = true; +{% if g.timezone -%} +var TIMEZONE = "{{ g.timezone }}"; +{% else -%} +var TIMEZONE = moment.tz.guess(); +{% endif -%} + var _ajax_setup = function() { $.ajaxSetup({ headers: { 'X-From-UI': true, 'X-Session-Tag': SESSION_TAG }, diff --git a/burpui/templates/js/admin/sessions.js b/burpui/templates/js/admin/sessions.js index 183c9c1d..91e16a93 100644 --- a/burpui/templates/js/admin/sessions.js +++ b/burpui/templates/js/admin/sessions.js @@ -113,7 +113,7 @@ var _sessions_table = $('#table-sessions').DataTable( { if (type === 'filter') { return data; } - return ''+moment(data, moment.ISO_8601).subtract(3, 'seconds').fromNow()+''; + return ''+moment(data, moment.ISO_8601).tz(TIMEZONE).subtract(3, 'seconds').fromNow()+''; } }, { @@ -174,7 +174,7 @@ var _sessions_table = $('#table-sessions').DataTable( { if (type === 'filter') { return data; } - return ''+moment(data, moment.ISO_8601).fromNow()+''; + return ''+moment(data, moment.ISO_8601).tz(TIMEZONE).fromNow()+''; } }, ], diff --git a/burpui/templates/js/calendar.js b/burpui/templates/js/calendar.js index 7bbcbcb1..8dedfddb 100644 --- a/burpui/templates/js/calendar.js +++ b/burpui/templates/js/calendar.js @@ -48,6 +48,7 @@ $(document).ready(function() { $('#calendar').fullCalendar({ {{ macros.translate_calendar() }} + timezone: TIMEZONE, editable: false, eventLimit: true, eventLimitClick: 'day', diff --git a/burpui/templates/js/client-browse.js b/burpui/templates/js/client-browse.js index 10e3ab97..0a809318 100644 --- a/burpui/templates/js/client-browse.js +++ b/burpui/templates/js/client-browse.js @@ -118,7 +118,7 @@ $( document ).ready(function() { $tdList.eq(2).text(node.data.uid); $tdList.eq(3).text(node.data.gid); $tdList.eq(4).text(node.data.size); - $tdList.eq(5).html(''+moment(node.data.date).format({{ g.date_format|tojson }})+''); + $tdList.eq(5).html(''+moment(node.data.date).tz(TIMEZONE).format({{ g.date_format|tojson }})+''); }, select: function(event, data) { toggleRestorationForms(data.tree); diff --git a/burpui/templates/js/client.js b/burpui/templates/js/client.js index eea47f75..c6a55546 100644 --- a/burpui/templates/js/client.js +++ b/burpui/templates/js/client.js @@ -115,7 +115,7 @@ var _client_table = $('#table-client').DataTable( { data: 'date', type: 'timestamp', render: function ( data, type, row ) { - return ''+moment(data, moment.ISO_8601).format({{ g.date_format|tojson }})+''; + return ''+moment(data, moment.ISO_8601).tz(TIMEZONE).format({{ g.date_format|tojson }})+''; } }, { diff --git a/burpui/templates/js/clients.js b/burpui/templates/js/clients.js index 217e4e2b..2b1907a7 100644 --- a/burpui/templates/js/clients.js +++ b/burpui/templates/js/clients.js @@ -135,7 +135,7 @@ var _clients_table = $('#table-clients').DataTable( { return data; } if (!(data in __status || data in __date)) { - return ''+moment(data, moment.ISO_8601).format({{ g.date_format|tojson }})+''; + return ''+moment(data, moment.ISO_8601).tz(TIMEZONE).format({{ g.date_format|tojson }})+''; } return data; } diff --git a/burpui/templates/js/user.js b/burpui/templates/js/user.js index 68d3cbee..39754cfc 100644 --- a/burpui/templates/js/user.js +++ b/burpui/templates/js/user.js @@ -282,7 +282,7 @@ var _sessions_table = $('#table-sessions').DataTable( { if (type === 'filter') { return data; } - return ''+moment(data, moment.ISO_8601).subtract(3, 'seconds').fromNow()+''; + return ''+moment(data, moment.ISO_8601).tz(TIMEZONE).subtract(3, 'seconds').fromNow()+''; } }, { @@ -343,7 +343,7 @@ var _sessions_table = $('#table-sessions').DataTable( { if (type === 'filter') { return data; } - return ''+moment(data, moment.ISO_8601).fromNow()+''; + return ''+moment(data, moment.ISO_8601).tz(TIMEZONE).fromNow()+''; } }, ], diff --git a/burpui/templates/layout.html b/burpui/templates/layout.html index 003edff7..008d7b6d 100644 --- a/burpui/templates/layout.html +++ b/burpui/templates/layout.html @@ -77,6 +77,7 @@ + {% if g.locale and g.locale != 'en' -%} {% endif -%} diff --git a/burpui/templates/macros.html b/burpui/templates/macros.html index 52eaf275..26925aea 100644 --- a/burpui/templates/macros.html +++ b/burpui/templates/macros.html @@ -164,11 +164,11 @@ jQuery.extend( jQuery.fn.dataTableExt.oSort, { var $obj = $(a); var title = $obj.attr('title'); if (typeof title !== typeof undefined && title !== false) - return moment(title, moment.ISO_8601).valueOf(); + return moment(title, moment.ISO_8601).tz(TIMEZONE).valueOf(); // we are using the filter the "right" way here if (moment(a, moment.ISO_8601).isValid()) - return ''+moment(a, moment.ISO_8601).valueOf(); + return ''+moment(a, moment.ISO_8601).tz(TIMEZONE).valueOf(); // return some string that should be "last" if (a === '{{ _("now") }}') { return 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'+a; diff --git a/burpui/templates/user.html b/burpui/templates/user.html index f516aba4..ffd27999 100644 --- a/burpui/templates/user.html +++ b/burpui/templates/user.html @@ -85,6 +85,13 @@ {{ _('See here for available formats', url='http://momentjs.com/docs/#/displaying/') }} +
+ +
+ +
+ {{ _('See here for available formats', url='https://momentjs.com/timezone/') }} +
diff --git a/setup.py b/setup.py index ded7d728..5087898c 100755 --- a/setup.py +++ b/setup.py @@ -68,6 +68,7 @@ VENDOR_TO_KEEP = [ 'burpui/static/vendor/moment/locale/fr.js', 'burpui/static/vendor/moment/locale/es.js', 'burpui/static/vendor/moment/locale/it.js', + 'burpui/static/vendor/moment-timezone/builds/moment-timezone-with-data-10-year-range.min.js', 'burpui/static/vendor/angular-ui-calendar/src/calendar.js', 'burpui/static/vendor/fullcalendar/dist/fullcalendar.min.css', 'burpui/static/vendor/fullcalendar/dist/fullcalendar.print.min.css', diff --git a/tests/functional/test_prefs_api.py b/tests/functional/test_prefs_api.py index 14289096..c373e359 100644 --- a/tests/functional/test_prefs_api.py +++ b/tests/functional/test_prefs_api.py @@ -52,11 +52,11 @@ def test_prefs(client, app): URL = url_for('api.prefs_ui') response = client.get(URL) - assert response.json == {'language': 'en', 'dateFormat': None, 'pageLength': None} + assert response.json == {'language': 'en', 'dateFormat': None, 'pageLength': None, 'timezone': None} - response = client.put(URL, data=dict(language='fr', dateFormat='llll', pageLength=25)) + response = client.put(URL, data=dict(language='fr', dateFormat='llll', pageLength=25, timezone='UTC')) assert response.status_code == 201 - assert response.json == {'language': 'fr', 'dateFormat': 'llll', 'pageLength': 25} + assert response.json == {'language': 'fr', 'dateFormat': 'llll', 'pageLength': 25, 'timezone': 'UTC'} response = client.post(URL, data=dict(language='en')) assert response.status_code == 200 @@ -64,6 +64,6 @@ def test_prefs(client, app): response = client.delete(URL, data=dict(pageLength=25)) assert response.status_code == 200 - assert response.json == {'language': 'en', 'dateFormat': 'llll', 'pageLength': None} + assert response.json == {'language': 'en', 'dateFormat': 'llll', 'pageLength': None, 'timezone': 'UTC'} logout(client)