Commit 0063318b authored by Benjamin "Ziirish" SANS's avatar Benjamin "Ziirish" SANS

Merge branch 'ui-timezone' into 'master'

add: allow to choose a custom timezone for the UI (fix #329)

Closes #329

See merge request !113
parents 57d2d417 902fa1eb
Pipeline #1770 failed with stages
in 38 minutes and 5 seconds
......@@ -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
......
......@@ -18,6 +18,7 @@ Current
- Add: allow to delete clients data upon removal `#232 <https://git.ziirish.me/ziirish/burp-ui/issues/232>`_
- Add: allow to create clients from templates in one call `#266 <https://git.ziirish.me/ziirish/burp-ui/issues/266>`_
- Add: allow to rename clients/templates `#274 <https://git.ziirish.me/ziirish/burp-ui/issues/274>`_
- Add: allow to set a custom timezone in which to display UI dates `#329 <https://git.ziirish.me/ziirish/burp-ui/issues/329>`_
- Fix: sync pkgs requirements with burp-ui's `#300 <https://git.ziirish.me/ziirish/burp-ui/issues/300>`__
- Fix: wrong command suggestion `#296 <https://git.ziirish.me/ziirish/burp-ui/issues/296>`__
- Fix: allow templates removal `#290 <https://git.ziirish.me/ziirish/burp-ui/issues/290>`__
......
......@@ -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):
......
......@@ -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 = (
......
Subproject commit 7e90a1461b1c118bbc190c003184365890320729
Subproject commit 8a343173cd3b7fd07a27579ff291fb144f594a2e
......@@ -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 },
......
......@@ -113,7 +113,7 @@ var _sessions_table = $('#table-sessions').DataTable( {
if (type === 'filter') {
return data;
}
return '<span data-toggle="tooltip" title="'+data+'">'+moment(data, moment.ISO_8601).subtract(3, 'seconds').fromNow()+'</span>';
return '<span data-toggle="tooltip" title="'+data+'">'+moment(data, moment.ISO_8601).tz(TIMEZONE).subtract(3, 'seconds').fromNow()+'</span>';
}
},
{
......@@ -174,7 +174,7 @@ var _sessions_table = $('#table-sessions').DataTable( {
if (type === 'filter') {
return data;
}
return '<span data-toggle="tooltip" title="'+data+'">'+moment(data, moment.ISO_8601).fromNow()+'</span>';
return '<span data-toggle="tooltip" title="'+data+'">'+moment(data, moment.ISO_8601).tz(TIMEZONE).fromNow()+'</span>';
}
},
],
......
......@@ -48,6 +48,7 @@ $(document).ready(function() {
$('#calendar').fullCalendar({
{{ macros.translate_calendar() }}
timezone: TIMEZONE,
editable: false,
eventLimit: true,
eventLimitClick: 'day',
......
......@@ -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('<span title="'+node.data.date+'">'+moment(node.data.date).format({{ g.date_format|tojson }})+'</span>');
$tdList.eq(5).html('<span title="'+node.data.date+'">'+moment(node.data.date).tz(TIMEZONE).format({{ g.date_format|tojson }})+'</span>');
},
select: function(event, data) {
toggleRestorationForms(data.tree);
......
......@@ -115,7 +115,7 @@ var _client_table = $('#table-client').DataTable( {
data: 'date',
type: 'timestamp',
render: function ( data, type, row ) {
return '<span data-toggle="tooltip" title="'+data+'">'+moment(data, moment.ISO_8601).format({{ g.date_format|tojson }})+'</span>';
return '<span data-toggle="tooltip" title="'+data+'">'+moment(data, moment.ISO_8601).tz(TIMEZONE).format({{ g.date_format|tojson }})+'</span>';
}
},
{
......
......@@ -135,7 +135,7 @@ var _clients_table = $('#table-clients').DataTable( {
return data;
}
if (!(data in __status || data in __date)) {
return '<span data-toggle="tooltip" title="'+data+'">'+moment(data, moment.ISO_8601).format({{ g.date_format|tojson }})+'</span>';
return '<span data-toggle="tooltip" title="'+data+'">'+moment(data, moment.ISO_8601).tz(TIMEZONE).format({{ g.date_format|tojson }})+'</span>';
}
return data;
}
......
......@@ -282,7 +282,7 @@ var _sessions_table = $('#table-sessions').DataTable( {
if (type === 'filter') {
return data;
}
return '<span data-toggle="tooltip" title="'+data+'">'+moment(data, moment.ISO_8601).subtract(3, 'seconds').fromNow()+'</span>';
return '<span data-toggle="tooltip" title="'+data+'">'+moment(data, moment.ISO_8601).tz(TIMEZONE).subtract(3, 'seconds').fromNow()+'</span>';
}
},
{
......@@ -343,7 +343,7 @@ var _sessions_table = $('#table-sessions').DataTable( {
if (type === 'filter') {
return data;
}
return '<span data-toggle="tooltip" title="'+data+'">'+moment(data, moment.ISO_8601).fromNow()+'</span>';
return '<span data-toggle="tooltip" title="'+data+'">'+moment(data, moment.ISO_8601).tz(TIMEZONE).fromNow()+'</span>';
}
},
],
......
......@@ -77,6 +77,7 @@
<!-- MomentJS
================================================== -->
<script src="{{ url_for('bower.static', filename='moment/min/moment.min.js') }}"></script>
<script src="{{ url_for('bower.static', filename='moment-timezone/builds/moment-timezone-with-data-10-year-range.min.js') }}"></script>
{% if g.locale and g.locale != 'en' -%}
<script src="{{ url_for('bower.static', filename='moment/locale/{}.js'.format(g.locale)) }}"></script>
{% endif -%}
......
......@@ -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;
......
......@@ -85,6 +85,13 @@
</div>
<span class="help-inline text-default">{{ _('See <a href="%(url)s">here</a> for available formats', url='http://momentjs.com/docs/#/displaying/') }}</span>
</div>
<div class="form-group">
<label for="timezone" class="col-lg-2 control-label">{{ _('Timezone') }}</label>
<div class="col-lg-3">
<input class="form-control" name="timezone" id="timezone" type="text" ng-model="prefs.timezone" placeholder="{{ _('UI timezone') }}">
</div>
<span class="help-inline text-default">{{ _('See <a href="%(url)s">here</a> for available formats', url='https://momentjs.com/timezone/') }}</span>
</div>
<div class="form-group">
<div class="col-lg-10 col-lg-offset-2">
<button type="submit" class="btn btn-primary"><i class="fa fa-fw fa-floppy-o" aria-hidden="true"></i>{{ _('Save') }}</button>
......
......@@ -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',
......
......@@ -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)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment