From a1541e6a5992278dc24d3c30222126a205c0d9a0 Mon Sep 17 00:00:00 2001 From: Memo Garcia Date: Fri, 11 Mar 2016 15:01:51 +0000 Subject: [PATCH] UX improvements Fix calendar to better display in small windows Bump the number of results for listing items Quick workaround to successfully listing items after being created Closes bug: 1556939 Change-Id: Ibaf0a999a729197900b18c1f22fd819f5e11e797 --- .../templates/actions}/_action.html | 5 +- .../templates/actions}/_advanced.html | 2 +- .../actions/templates/actions/_rules.html | 16 + .../actions/templates/actions/_snapshot.html | 13 + disaster_recovery/actions/workflows/action.py | 20 +- disaster_recovery/api/api.py | 35 +- disaster_recovery/backups/tables.py | 8 +- .../jobs/templates/jobs/_info.html | 10 +- .../jobs/templates/jobs/_rules.html | 11 - .../jobs/templates/jobs/_scheduling.html | 24 -- .../jobs/templates/jobs/_snapshot.html | 12 - .../jobs/_workflow_step_update_members.html | 61 --- disaster_recovery/jobs/workflows/create.py | 12 +- .../jobs/workflows/update_job.py | 2 +- .../sessions/templates/sessions/_info.html | 9 +- .../sessions/workflows/create.py | 15 +- .../css/bootstrap-datetimepicker.min.css | 373 ++++++++++++++++++ .../freezer/js/freezer.actions.advanced.js | 11 +- .../freezer/js/freezer.datetimepicker.js | 32 +- 19 files changed, 510 insertions(+), 161 deletions(-) rename disaster_recovery/{jobs/templates/jobs => actions/templates/actions}/_action.html (73%) rename disaster_recovery/{jobs/templates/jobs => actions/templates/actions}/_advanced.html (59%) create mode 100644 disaster_recovery/actions/templates/actions/_rules.html create mode 100644 disaster_recovery/actions/templates/actions/_snapshot.html delete mode 100644 disaster_recovery/jobs/templates/jobs/_rules.html delete mode 100644 disaster_recovery/jobs/templates/jobs/_scheduling.html delete mode 100644 disaster_recovery/jobs/templates/jobs/_snapshot.html delete mode 100644 disaster_recovery/jobs/templates/jobs/_workflow_step_update_members.html create mode 100644 disaster_recovery/static/freezer/css/bootstrap-datetimepicker.min.css diff --git a/disaster_recovery/jobs/templates/jobs/_action.html b/disaster_recovery/actions/templates/actions/_action.html similarity index 73% rename from disaster_recovery/jobs/templates/jobs/_action.html rename to disaster_recovery/actions/templates/actions/_action.html index 6c919a3..107615b 100644 --- a/disaster_recovery/jobs/templates/jobs/_action.html +++ b/disaster_recovery/actions/templates/actions/_action.html @@ -4,11 +4,8 @@

{% blocktrans %}Action{% endblocktrans %}

-

{% blocktrans %}Specify the details for this action.{% endblocktrans %}

-

{% blocktrans %}An action is the simplest execution within a job, it could be a backup or a restore - and it contains rules that the action must follow (max retries, interval between intervals, etc) - .{% endblocktrans %}

+ and it contains rules that the action must follow (max retries, interval between executions, etc).{% endblocktrans %}

{% endblock %} diff --git a/disaster_recovery/jobs/templates/jobs/_advanced.html b/disaster_recovery/actions/templates/actions/_advanced.html similarity index 59% rename from disaster_recovery/jobs/templates/jobs/_advanced.html rename to disaster_recovery/actions/templates/actions/_advanced.html index e6d98e7..f70bc4e 100644 --- a/disaster_recovery/jobs/templates/jobs/_advanced.html +++ b/disaster_recovery/actions/templates/actions/_advanced.html @@ -4,7 +4,7 @@

{% blocktrans %}Advanced configuration{% endblocktrans %}

-

{% blocktrans %}Define extra information for the action to execute in the client.{% endblocktrans %}

+

{% blocktrans %}Define extra information for the action executed by the client.{% endblocktrans %}

{% endblock %} diff --git a/disaster_recovery/actions/templates/actions/_rules.html b/disaster_recovery/actions/templates/actions/_rules.html new file mode 100644 index 0000000..5999642 --- /dev/null +++ b/disaster_recovery/actions/templates/actions/_rules.html @@ -0,0 +1,16 @@ +{% load i18n horizon humanize %} + +{% block help_message %} + +

{% blocktrans %}Rules{% endblocktrans %}

+ +

{% blocktrans %}Define the following action rules: +

+{% endblocktrans %}

+ +{% endblock %} + diff --git a/disaster_recovery/actions/templates/actions/_snapshot.html b/disaster_recovery/actions/templates/actions/_snapshot.html new file mode 100644 index 0000000..e05d824 --- /dev/null +++ b/disaster_recovery/actions/templates/actions/_snapshot.html @@ -0,0 +1,13 @@ +{% load i18n horizon humanize %} + +{% block help_message %} + +

{% blocktrans %}Snapshot{% endblocktrans %}

+ +

{% blocktrans %}The snapshot technologies currently supported are LVM for Linux and Volume Shadow Copy for Windows.{% endblocktrans %}

+ +

{% blocktrans %}For Linux, in order to use snapshot features, the data has to reside on an LVM volume.{% endblocktrans %}

+ +{% endblock %} + + \ No newline at end of file diff --git a/disaster_recovery/actions/workflows/action.py b/disaster_recovery/actions/workflows/action.py index 1f6252b..fdae745 100644 --- a/disaster_recovery/actions/workflows/action.py +++ b/disaster_recovery/actions/workflows/action.py @@ -32,6 +32,10 @@ class ActionConfigurationAction(workflows.Action): widget=forms.HiddenInput(), required=False) + backup_name = forms.CharField( + label=_("Action Name *"), + required=False) + action = forms.ChoiceField( help_text=_("Set the action to be taken"), required=True) @@ -44,10 +48,6 @@ class ActionConfigurationAction(workflows.Action): help_text=_("Set storage backend for a backup"), required=True) - backup_name = forms.CharField( - label=_("Action Name *"), - required=False) - mysql_conf = forms.CharField( label=_("MySQL Configuration File *"), help_text=_("Set the path where the MySQL configuration file " @@ -228,7 +228,7 @@ class ActionConfigurationAction(workflows.Action): class Meta(object): name = _("Action") - help_text_template = "disaster_recovery/jobs" \ + help_text_template = "disaster_recovery/actions" \ "/_action.html" @@ -268,7 +268,7 @@ class SnapshotConfigurationAction(workflows.Action): class Meta(object): name = _("Snapshot") - help_text_template = "disaster_recovery/jobs" \ + help_text_template = "disaster_recovery/actions" \ "/_snapshot.html" @@ -453,7 +453,7 @@ class AdvancedConfigurationAction(workflows.Action): class Meta(object): name = _("Advanced") - help_text_template = "disaster_recovery/jobs" \ + help_text_template = "disaster_recovery/actions" \ "/_advanced.html" @@ -493,19 +493,19 @@ class RulesConfigurationAction(workflows.Action): label=_("Max Retries Interval"), initial=0, min_value=0, - help_text=_("Set the interval between intervals " + help_text=_("Set the interval between executions " "for retries in seconds"), required=False) mandatory = forms.BooleanField( label=_("Mandatory"), - help_text=_("Set this job as mandatory"), + help_text=_("Set this action as mandatory"), widget=forms.CheckboxInput(), required=False) class Meta(object): name = _("Rules") - help_text_template = "disaster_recovery/jobs" \ + help_text_template = "disaster_recovery/actions" \ "/_rules.html" diff --git a/disaster_recovery/api/api.py b/disaster_recovery/api/api.py index 0ac8ec9..72040dc 100644 --- a/disaster_recovery/api/api.py +++ b/disaster_recovery/api/api.py @@ -14,6 +14,7 @@ # from horizon. import logging +import time from django.conf import settings @@ -88,7 +89,7 @@ class Job(object): self.request = request self.client = client(request) - def list(self, json=False, limit=100, offset=0, search=None): + def list(self, json=False, limit=500, offset=0, search=None): if search: search = {"match": [{"_all": search}, ], } @@ -120,7 +121,13 @@ class Job(object): job.get('client_id')) def create(self, job): - return self._build(job) + job = self._build(job) + # due to elasticsearch replication time, sometimes the newly created + # file is not reflected in the ui immediately, so a quick and dirty + # workaround is to sleep for 3 seconds to make sure that the changes + # are reflected + time.sleep(3) + return job def update(self, job_id, job): scheduling = {} @@ -248,7 +255,7 @@ class Session(object): self.request = request self.client = client(request) - def list(self, json=False, limit=30, offset=0, search=None): + def list(self, json=False, limit=500, offset=0, search=None): if search: search = {"match": [{"_all": search}, ], } @@ -285,7 +292,13 @@ class Session(object): session.get('schedule', {}).get('schedule_end_date')) def create(self, session): - return self._build(session) + session = self._build(session) + # due to elasticsearch replication time, sometimes the newly created + # file is not reflected in the ui immediately, so a quick and dirty + # workaround is to sleep for 3 seconds to make sure that the changes + # are reflected + time.sleep(3) + return session def update(self, session, session_id): return self.client.sessions.update(session_id, session) @@ -338,7 +351,7 @@ class Action(object): self.request = request self.client = client(request) - def list(self, json=False, limit=100, offset=0, search=None): + def list(self, json=False, limit=500, offset=0, search=None): if search: search = {"match": [{"_all": search}, ], } @@ -374,7 +387,13 @@ class Action(object): action['freezer_action'].get('storage')) def create(self, action): - return self._build(action) + action = self._build(action) + # due to elasticsearch replication time, sometimes the newly created + # file is not reflected in the ui immediately, so a quick and dirty + # workaround is to sleep for 3 seconds to make sure that the changes + # are reflected + time.sleep(3) + return action def update(self, action, action_id): updated_action = {} @@ -427,7 +446,7 @@ class Client(object): self.request = request self.client = client(request) - def list(self, json=False, limit=100, offset=0, search=None): + def list(self, json=False, limit=500, offset=0, search=None): if search: search = {"match": [{"_all": search}, ], } @@ -465,7 +484,7 @@ class Backup(object): self.request = request self.client = client(request) - def list(self, json=False, limit=30, offset=0, search=None): + def list(self, json=False, limit=500, offset=0, search=None): if search: search = {"match": [{"_all": search}, ], } diff --git a/disaster_recovery/backups/tables.py b/disaster_recovery/backups/tables.py index 337b635..8125bbf 100644 --- a/disaster_recovery/backups/tables.py +++ b/disaster_recovery/backups/tables.py @@ -49,12 +49,12 @@ def icons(backup): placeholder = '' try: - level_txt = "Level: {} ({} backup) out of {}".format( - backup.level, "Full" if backup.level == 0 else "Incremental", - backup.max_level) + level_txt = "Level: {} ({} backup)".format( + backup.curr_backup_level, "Full" + if backup.curr_backup_level == 0 else "Incremental") result.append( '{}'.format( - level_txt, backup.level)) + level_txt, backup.curr_backup_level)) except Exception: result.append("Level: {}".format("Full")) diff --git a/disaster_recovery/jobs/templates/jobs/_info.html b/disaster_recovery/jobs/templates/jobs/_info.html index 773dd46..d4c87f7 100644 --- a/disaster_recovery/jobs/templates/jobs/_info.html +++ b/disaster_recovery/jobs/templates/jobs/_info.html @@ -2,10 +2,12 @@ {% block help_message %}

{% blocktrans %}Job{% endblocktrans %}

+

{% blocktrans %}Describes actions to be executed by a client.{% endblocktrans %}

+

{% blocktrans %}Job executions can be executed and scheduled, by providing the following settings.{% endblocktrans %}

-

{% blocktrans %}Specify the details for creating a job.{% endblocktrans %}

- -

{% blocktrans %}A job defines an ordered list of actions to be executed in a client with scheduling information.{% endblocktrans %}

- + + + + {% endblock %} \ No newline at end of file diff --git a/disaster_recovery/jobs/templates/jobs/_rules.html b/disaster_recovery/jobs/templates/jobs/_rules.html deleted file mode 100644 index 7a3f2b5..0000000 --- a/disaster_recovery/jobs/templates/jobs/_rules.html +++ /dev/null @@ -1,11 +0,0 @@ -{% load i18n horizon humanize %} - -{% block help_message %} - -

{% blocktrans %}Rules{% endblocktrans %}

- -

{% blocktrans %}Define rules for this action, from how many times it will retry the action, the interval - between interval and whether or not the action is mandatory. {% endblocktrans %}

- -{% endblock %} - diff --git a/disaster_recovery/jobs/templates/jobs/_scheduling.html b/disaster_recovery/jobs/templates/jobs/_scheduling.html deleted file mode 100644 index 834a87e..0000000 --- a/disaster_recovery/jobs/templates/jobs/_scheduling.html +++ /dev/null @@ -1,24 +0,0 @@ -{% load i18n horizon humanize %} - -{% block help_message %} -

{% blocktrans %}Start and End Date Time{% endblocktrans %}

-

{% blocktrans %}Set a start date and time to execute jobs{% endblocktrans %}

- -

{% blocktrans %}Interval{% endblocktrans %}

-

{% blocktrans %}Set the interval in the following format:{% endblocktrans %}

- -

{% blocktrans %}If no start date is provided the job will start immediately{% endblocktrans %}

- - - - - - -{% endblock %} \ No newline at end of file diff --git a/disaster_recovery/jobs/templates/jobs/_snapshot.html b/disaster_recovery/jobs/templates/jobs/_snapshot.html deleted file mode 100644 index 8dc75cc..0000000 --- a/disaster_recovery/jobs/templates/jobs/_snapshot.html +++ /dev/null @@ -1,12 +0,0 @@ -{% load i18n horizon humanize %} - -{% block help_message %} - -

{% blocktrans %}Action{% endblocktrans %}

- -

{% blocktrans %}Specify whether this action should execute a snapshot on the client file system. - In Linux and it's distros it will use LVM and in Windows it will use Volume Shadow Copy{% endblocktrans %}

- -{% endblock %} - - \ No newline at end of file diff --git a/disaster_recovery/jobs/templates/jobs/_workflow_step_update_members.html b/disaster_recovery/jobs/templates/jobs/_workflow_step_update_members.html deleted file mode 100644 index 0507419..0000000 --- a/disaster_recovery/jobs/templates/jobs/_workflow_step_update_members.html +++ /dev/null @@ -1,61 +0,0 @@ -{% load i18n %} - - - - - -
- {% include "horizon/common/_form_fields.html" %} -
- - - diff --git a/disaster_recovery/jobs/workflows/create.py b/disaster_recovery/jobs/workflows/create.py index a618261..c9ba710 100644 --- a/disaster_recovery/jobs/workflows/create.py +++ b/disaster_recovery/jobs/workflows/create.py @@ -115,7 +115,15 @@ class InfoConfigurationAction(workflows.Action): schedule_interval = forms.CharField( label=_("Interval"), required=False, - help_text=_("Repeat this configuration in a minutes interval.")) + help_text=_("""Set the interval in the following format: + continuous, + N weeks, + N days, + N hours, + N minutes, + N seconds, + If no start date is provided the job + will start immediately""")) schedule_end_date = forms.CharField( label=_("End Date and Time"), @@ -170,7 +178,7 @@ class InfoConfigurationAction(workflows.Action): name = _("Job Info") slug = "info" help_text_template = "disaster_recovery/jobs" \ - "/_scheduling.html" + "/_info.html" class InfoConfiguration(workflows.Step): diff --git a/disaster_recovery/jobs/workflows/update_job.py b/disaster_recovery/jobs/workflows/update_job.py index 67571a5..6f5922c 100644 --- a/disaster_recovery/jobs/workflows/update_job.py +++ b/disaster_recovery/jobs/workflows/update_job.py @@ -90,7 +90,7 @@ class InfoConfigurationAction(workflows.Action): name = _("Job Info") slug = "info" help_text_template = "disaster_recovery/jobs" \ - "/_scheduling.html" + "/_info.html" class InfoConfiguration(workflows.Step): diff --git a/disaster_recovery/sessions/templates/sessions/_info.html b/disaster_recovery/sessions/templates/sessions/_info.html index 26bd441..d5982d3 100644 --- a/disaster_recovery/sessions/templates/sessions/_info.html +++ b/disaster_recovery/sessions/templates/sessions/_info.html @@ -3,10 +3,11 @@ {% block help_message %}

{% blocktrans %}Session{% endblocktrans %}

-

{% blocktrans %}Specify the name for this session.{% endblocktrans %}

- -

{% blocktrans %}A session is a collection of jobs which share scheduling information and will coordinate one - or more clients.{% endblocktrans %}

+

{% blocktrans %}A group of jobs which share the same scheduling time.{% endblocktrans %}

+ + + + {% endblock %} \ No newline at end of file diff --git a/disaster_recovery/sessions/workflows/create.py b/disaster_recovery/sessions/workflows/create.py index 7a8b680..321821f 100644 --- a/disaster_recovery/sessions/workflows/create.py +++ b/disaster_recovery/sessions/workflows/create.py @@ -44,7 +44,16 @@ class SessionConfigurationAction(workflows.Action): schedule_interval = forms.CharField( label=_("Interval"), - required=False) + required=False, + help_text=_("""Set the interval in the following format: + continuous, + N weeks, + N days, + N hours, + N minutes, + N seconds, + If no start date is provided the job + will start immediately""")) schedule_end_date = forms.CharField( label=_("End Date and Time"), @@ -80,8 +89,8 @@ class SessionConfigurationAction(workflows.Action): class Meta: name = _("Session Information") slug = "sessions" - help_text_template = "disaster_recovery/jobs" \ - "/_scheduling.html" + help_text_template = "disaster_recovery/sessions" \ + "/_info.html" class SessionConfiguration(workflows.Step): diff --git a/disaster_recovery/static/freezer/css/bootstrap-datetimepicker.min.css b/disaster_recovery/static/freezer/css/bootstrap-datetimepicker.min.css new file mode 100644 index 0000000..ed4eb8f --- /dev/null +++ b/disaster_recovery/static/freezer/css/bootstrap-datetimepicker.min.css @@ -0,0 +1,373 @@ +/*! + * Datetimepicker for Bootstrap 3 + * version : 4.17.37 + * https://github.com/Eonasdan/bootstrap-datetimepicker/ + */ +.bootstrap-datetimepicker-widget { + list-style: none; +} +.bootstrap-datetimepicker-widget.dropdown-menu { + margin: 2px 0; + padding: 4px; + width: 19em; +} +@media (min-width: 768px) { + .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { + width: 38em; + } +} +@media (min-width: 992px) { + .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { + width: 38em; + } +} +@media (min-width: 1200px) { + .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { + width: 38em; + } +} +.bootstrap-datetimepicker-widget.dropdown-menu:before, +.bootstrap-datetimepicker-widget.dropdown-menu:after { + content: ''; + display: inline-block; + position: absolute; +} +.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before { + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #cccccc; + border-bottom-color: rgba(0, 0, 0, 0.2); + top: -7px; + left: 7px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.bottom:after { + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid white; + top: -6px; + left: 8px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.top:before { + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-top: 7px solid #cccccc; + border-top-color: rgba(0, 0, 0, 0.2); + bottom: -7px; + left: 6px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.top:after { + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-top: 6px solid white; + bottom: -6px; + left: 7px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:before { + left: auto; + right: 6px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:after { + left: auto; + right: 7px; +} +.bootstrap-datetimepicker-widget .list-unstyled { + margin: 0; +} +.bootstrap-datetimepicker-widget a[data-action] { + padding: 6px 0; +} +.bootstrap-datetimepicker-widget a[data-action]:active { + box-shadow: none; +} +.bootstrap-datetimepicker-widget .timepicker-hour, +.bootstrap-datetimepicker-widget .timepicker-minute, +.bootstrap-datetimepicker-widget .timepicker-second { + width: 54px; + font-weight: bold; + font-size: 1.2em; + margin: 0; +} +.bootstrap-datetimepicker-widget button[data-action] { + padding: 6px; +} +.bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Increment Hours"; +} +.bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Increment Minutes"; +} +.bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Decrement Hours"; +} +.bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Decrement Minutes"; +} +.bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Show Hours"; +} +.bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Show Minutes"; +} +.bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Toggle AM/PM"; +} +.bootstrap-datetimepicker-widget .btn[data-action="clear"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Clear the picker"; +} +.bootstrap-datetimepicker-widget .btn[data-action="today"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Set the date to today"; +} +.bootstrap-datetimepicker-widget .picker-switch { + text-align: center; +} +.bootstrap-datetimepicker-widget .picker-switch::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Toggle Date and Time Screens"; +} +.bootstrap-datetimepicker-widget .picker-switch td { + padding: 0; + margin: 0; + height: auto; + width: auto; + line-height: inherit; +} +.bootstrap-datetimepicker-widget .picker-switch td span { + line-height: 2.5; + height: 2.5em; + width: 100%; +} +.bootstrap-datetimepicker-widget table { + width: 100%; + margin: 0; +} +.bootstrap-datetimepicker-widget table td, +.bootstrap-datetimepicker-widget table th { + text-align: center; + border-radius: 4px; +} +.bootstrap-datetimepicker-widget table th { + height: 20px; + line-height: 20px; + width: 20px; +} +.bootstrap-datetimepicker-widget table th.picker-switch { + width: 145px; +} +.bootstrap-datetimepicker-widget table th.disabled, +.bootstrap-datetimepicker-widget table th.disabled:hover { + background: none; + color: #777777; + cursor: not-allowed; +} +.bootstrap-datetimepicker-widget table th.prev::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Previous Month"; +} +.bootstrap-datetimepicker-widget table th.next::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Next Month"; +} +.bootstrap-datetimepicker-widget table thead tr:first-child th { + cursor: pointer; +} +.bootstrap-datetimepicker-widget table thead tr:first-child th:hover { + background: #eeeeee; +} +.bootstrap-datetimepicker-widget table td { + height: 54px; + line-height: 54px; + width: 54px; +} +.bootstrap-datetimepicker-widget table td.cw { + font-size: .8em; + height: 20px; + line-height: 20px; + color: #777777; +} +.bootstrap-datetimepicker-widget table td.day { + height: 20px; + line-height: 20px; + width: 20px; +} +.bootstrap-datetimepicker-widget table td.day:hover, +.bootstrap-datetimepicker-widget table td.hour:hover, +.bootstrap-datetimepicker-widget table td.minute:hover, +.bootstrap-datetimepicker-widget table td.second:hover { + background: #eeeeee; + cursor: pointer; +} +.bootstrap-datetimepicker-widget table td.old, +.bootstrap-datetimepicker-widget table td.new { + color: #777777; +} +.bootstrap-datetimepicker-widget table td.today { + position: absolute; +} +.bootstrap-datetimepicker-widget table td.today:before { + content: ''; + display: inline-block; + border: solid transparent; + border-width: 0 0 7px 7px; + border-bottom-color: #337ab7; + border-top-color: rgba(0, 0, 0, 0.2); + position: absolute; + bottom: 4px; + right: 4px; +} +.bootstrap-datetimepicker-widget table td.active, +.bootstrap-datetimepicker-widget table td.active:hover { + background-color: #337ab7; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.bootstrap-datetimepicker-widget table td.active.today:before { + border-bottom-color: #fff; +} +.bootstrap-datetimepicker-widget table td.disabled, +.bootstrap-datetimepicker-widget table td.disabled:hover { + background: none; + color: #777777; + cursor: not-allowed; +} +.bootstrap-datetimepicker-widget table td span { + display: inline-block; + width: 54px; + height: 54px; + line-height: 54px; + margin: 2px 1.5px; + cursor: pointer; + border-radius: 4px; +} +.bootstrap-datetimepicker-widget table td span:hover { + background: #eeeeee; +} +.bootstrap-datetimepicker-widget table td span.active { + background-color: #337ab7; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.bootstrap-datetimepicker-widget table td span.old { + color: #777777; +} +.bootstrap-datetimepicker-widget table td span.disabled, +.bootstrap-datetimepicker-widget table td span.disabled:hover { + background: none; + color: #777777; + cursor: not-allowed; +} +.bootstrap-datetimepicker-widget.usetwentyfour td.hour { + height: 27px; + line-height: 27px; +} +.bootstrap-datetimepicker-widget.wider { + width: 21em; +} +.bootstrap-datetimepicker-widget .datepicker-decades .decade { + line-height: 1.8em !important; +} +.input-group.date .input-group-addon { + cursor: pointer; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} diff --git a/disaster_recovery/static/freezer/js/freezer.actions.advanced.js b/disaster_recovery/static/freezer/js/freezer.actions.advanced.js index 726fc2b..f28c88f 100644 --- a/disaster_recovery/static/freezer/js/freezer.actions.advanced.js +++ b/disaster_recovery/static/freezer/js/freezer.actions.advanced.js @@ -18,12 +18,6 @@ "use strict"; -function hideIncrementalOptions () { - $("#id_max_level").closest(".form-group").hide(); - $("#id_always_level").closest(".form-group").hide(); - $("#id_restart_always_level").closest(".form-group").hide(); -} - $("#id_no_incremental").click(function () { if ($("#id_no_incremental").is(":checked")) { $("#id_max_level").closest(".form-group").hide(); @@ -34,7 +28,4 @@ $("#id_no_incremental").click(function () { $("#id_always_level").closest(".form-group").show(); $("#id_restart_always_level").closest(".form-group").show(); } -}); - - -hideIncrementalOptions(); \ No newline at end of file +}); \ No newline at end of file diff --git a/disaster_recovery/static/freezer/js/freezer.datetimepicker.js b/disaster_recovery/static/freezer/js/freezer.datetimepicker.js index ba4a314..7f11413 100644 --- a/disaster_recovery/static/freezer/js/freezer.datetimepicker.js +++ b/disaster_recovery/static/freezer/js/freezer.datetimepicker.js @@ -20,10 +20,38 @@ $(function () { $('#id_schedule_start_date').datetimepicker({ - format: 'YYYY-MM-DDTHH:mm:ss' + format: 'YYYY-MM-DDTHH:mm:ss', + showClose: true, + tooltips: { + today: 'Go to today', + clear: 'Clear selection', + close: 'Close the picker' + }, + widgetPositioning: { + horizontal: 'left', + vertical: 'bottom' + } }); $('#id_schedule_end_date').datetimepicker({ - format: 'YYYY-MM-DDTHH:mm:ss' + format: 'YYYY-MM-DDTHH:mm:ss', + showClose: true, + tooltips: { + today: 'Go to today', + clear: 'Clear selection', + close: 'Close the picker' + }, + widgetPositioning: { + horizontal: 'left', + vertical: 'bottom' + }, + useCurrent: false //Important! See issue #1075 + }); + + $("#id_schedule_start_date").on("dp.change", function (e) { + $('#id_schedule_end_date').data("DateTimePicker").minDate(e.date); + }); + $("#id_schedule_end_date").on("dp.change", function (e) { + $('#id_schedule_start_date').data("DateTimePicker").maxDate(e.date); }); });