diff --git a/app/assets/javascripts/admin/controllers/admin-backups-index.js.es6 b/app/assets/javascripts/admin/controllers/admin-backups-index.js.es6 index 8eceb0bb7d0..6a440571217 100644 --- a/app/assets/javascripts/admin/controllers/admin-backups-index.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-backups-index.js.es6 @@ -1,20 +1,20 @@ export default Ember.ArrayController.extend({ needs: ["adminBackups"], status: Em.computed.alias("controllers.adminBackups"), - isOperationRunning: Em.computed.alias("status.isOperationRunning"), - restoreDisabled: Em.computed.alias("status.restoreDisabled"), + isOperationRunning: Em.computed.alias("status.model.isOperationRunning"), + restoreDisabled: Em.computed.alias("status.model.restoreDisabled"), uploadLabel: function() { return I18n.t("admin.backups.upload.label"); }.property(), restoreTitle: function() { - if (!this.get('status.allowRestore')) { + if (!this.get('status.model.allowRestore')) { return "admin.backups.operations.restore.is_disabled"; - } else if (this.get("status.isOperationRunning")) { + } else if (this.get("status.model.isOperationRunning")) { return "admin.backups.operations.is_running"; } else { return "admin.backups.operations.restore.title"; } - }.property("status.{allowRestore,isOperationRunning}"), + }.property("status.model.{allowRestore,isOperationRunning}"), actions: { diff --git a/app/assets/javascripts/admin/controllers/admin-backups.js.es6 b/app/assets/javascripts/admin/controllers/admin-backups.js.es6 index 64bb7ad105b..287ac92e468 100644 --- a/app/assets/javascripts/admin/controllers/admin-backups.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-backups.js.es6 @@ -1,5 +1,5 @@ export default Ember.ObjectController.extend({ - noOperationIsRunning: Em.computed.not("isOperationRunning"), - rollbackEnabled: Em.computed.and("canRollback", "restoreEnabled", "noOperationIsRunning"), + noOperationIsRunning: Em.computed.not("model.isOperationRunning"), + rollbackEnabled: Em.computed.and("model.canRollback", "model.restoreEnabled", "noOperationIsRunning"), rollbackDisabled: Em.computed.not("rollbackEnabled") }); diff --git a/app/assets/javascripts/admin/routes/admin-backups.js.es6 b/app/assets/javascripts/admin/routes/admin-backups.js.es6 index 981dc5394b0..a48d16f16e6 100644 --- a/app/assets/javascripts/admin/routes/admin-backups.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-backups.js.es6 @@ -10,14 +10,14 @@ export default Discourse.Route.extend({ _processLogMessage(log) { if (log.message === "[STARTED]") { - this.controllerFor("adminBackups").set("isOperationRunning", true); + this.controllerFor("adminBackups").set("model.isOperationRunning", true); this.controllerFor("adminBackupsLogs").clear(); } else if (log.message === "[FAILED]") { - this.controllerFor("adminBackups").set("isOperationRunning", false); + this.controllerFor("adminBackups").set("model.isOperationRunning", false); bootbox.alert(I18n.t("admin.backups.operations.failed", { operation: log.operation })); } else if (log.message === "[SUCCESS]") { Discourse.User.currentProp("hideReadOnlyAlert", false); - this.controllerFor("adminBackups").set("isOperationRunning", false); + this.controllerFor("adminBackups").set("model.isOperationRunning", false); if (log.operation === "restore") { // redirect to homepage when the restore is done (session might be lost) window.location.pathname = Discourse.getURL("/"); @@ -30,7 +30,7 @@ export default Discourse.Route.extend({ model() { return PreloadStore.getAndRemove("operations_status", function() { return Discourse.ajax("/admin/backups/status.json"); - }).then(function (status) { + }).then(status => { return Discourse.BackupStatus.create({ isOperationRunning: status.is_operation_running, canRollback: status.can_rollback, @@ -99,7 +99,7 @@ export default Discourse.Route.extend({ function(confirmed) { if (confirmed) { Discourse.Backup.cancel().then(function() { - self.controllerFor("adminBackups").set("isOperationRunning", false); + self.modelFor("adminBackups").set("isOperationRunning", false); }); } } diff --git a/app/assets/javascripts/admin/templates/backups.hbs b/app/assets/javascripts/admin/templates/backups.hbs index d274285b2b6..7562754c08c 100644 --- a/app/assets/javascripts/admin/templates/backups.hbs +++ b/app/assets/javascripts/admin/templates/backups.hbs @@ -6,7 +6,7 @@
- {{#if canRollback}} + {{#if model.canRollback}} {{d-button action="rollback" class="btn-rollback" label="admin.backups.operations.rollback.label" @@ -14,7 +14,7 @@ icon="ambulance" disabled=rollbackDisabled}} {{/if}} - {{#if isOperationRunning}} + {{#if model.isOperationRunning}} {{d-button action="cancelOperation" class="btn-danger" title="admin.backups.operations.cancel.title" diff --git a/app/assets/javascripts/admin/templates/backups_index.hbs b/app/assets/javascripts/admin/templates/backups_index.hbs index c6a6598be64..0b66fcaf73c 100644 --- a/app/assets/javascripts/admin/templates/backups_index.hbs +++ b/app/assets/javascripts/admin/templates/backups_index.hbs @@ -6,9 +6,9 @@
{{resumable-upload target="/admin/backups/upload" success="uploadSuccess" error="uploadError" uploadText=uploadLabel title="admin.backups.upload.title"}} {{#if site.isReadOnly}} - {{d-button icon="eye" action="toggleReadOnlyMode" disabled=isOperationRunning title="admin.backups.read_only.disable.title" label="admin.backups.read_only.disable.label"}} + {{d-button icon="eye" action="toggleReadOnlyMode" disabled=model.isOperationRunning title="admin.backups.read_only.disable.title" label="admin.backups.read_only.disable.label"}} {{else}} - {{d-button icon="eye" action="toggleReadOnlyMode" disabled=isOperationRunning title="admin.backups.read_only.enable.title" label="admin.backups.read_only.enable.label"}} + {{d-button icon="eye" action="toggleReadOnlyMode" disabled=model.isOperationRunning title="admin.backups.read_only.enable.title" label="admin.backups.read_only.enable.label"}} {{/if}}
@@ -20,12 +20,12 @@
{{fa-icon "download"}}{{i18n 'admin.backups.operations.download.label'}} - {{#if isOperationRunning}} + {{#if model.isOperationRunning}} {{d-button icon="trash-o" action="destroyBackup" actionParam=backup class="btn-danger" disabled="true" title="admin.backups.operations.is_running"}} - {{d-button icon="play" action="startRestore" actionParam=backup disabled=restoreDisabled title=restoreTitle label="admin.backups.operations.restore.label"}} + {{d-button icon="play" action="startRestore" actionParam=backup disabled=model.restoreDisabled title=restoreTitle label="admin.backups.operations.restore.label"}} {{else}} {{d-button icon="trash-o" action="destroyBackup" actionParam=backup class="btn-danger" title="admin.backups.operations.destroy.title"}} - {{d-button icon="play" action="startRestore" actionParam=backup disabled=restoreDisabled title=restoreTitle label="admin.backups.operations.restore.label"}} + {{d-button icon="play" action="startRestore" actionParam=backup disabled=model.restoreDisabled title=restoreTitle label="admin.backups.operations.restore.label"}} {{/if}}
diff --git a/app/assets/javascripts/admin/views/admin-backups-logs.js.es6 b/app/assets/javascripts/admin/views/admin-backups-logs.js.es6 index a7c2af453b5..d4f03cd1382 100644 --- a/app/assets/javascripts/admin/views/admin-backups-logs.js.es6 +++ b/app/assets/javascripts/admin/views/admin-backups-logs.js.es6 @@ -5,20 +5,20 @@ export default Discourse.View.extend({ _initialize: function() { this._reset(); }.on("init"), - _reset: function() { + _reset() { this.setProperties({ formattedLogs: "", index: 0 }); }, _updateFormattedLogs: Discourse.debounce(function() { - var logs = this.get("controller.model"); + const logs = this.get("controller.model"); if (logs.length === 0) { this._reset(); // reset the cached logs whenever the model is reset } else { // do the log formatting only once for HELLish performance - var formattedLogs = this.get("formattedLogs"); - for (var i = this.get("index"), length = logs.length; i < length; i++) { - var date = logs[i].get("timestamp"), - message = Handlebars.Utils.escapeExpression(logs[i].get("message")); + let formattedLogs = this.get("formattedLogs"); + for (let i = this.get("index"), length = logs.length; i < length; i++) { + const date = logs[i].get("timestamp"), + message = Handlebars.Utils.escapeExpression(logs[i].get("message")); formattedLogs += "[" + date + "] " + message + "\n"; } // update the formatted logs & cache index @@ -28,8 +28,8 @@ export default Discourse.View.extend({ } }, 150).observes("controller.model.@each"), - render: function(buffer) { - var formattedLogs = this.get("formattedLogs"); + render(buffer) { + const formattedLogs = this.get("formattedLogs"); if (formattedLogs && formattedLogs.length > 0) { buffer.push("
");
       buffer.push(formattedLogs);
@@ -38,13 +38,13 @@ export default Discourse.View.extend({
       buffer.push("

" + I18n.t("admin.backups.logs.none") + "

"); } // add a loading indicator - if (this.get("controller.status.isOperationRunning")) { + if (this.get("controller.status.model.isOperationRunning")) { buffer.push(renderSpinner('small')); } }, _forceScrollToBottom: function() { - var $div = this.$()[0]; + const $div = this.$()[0]; $div.scrollTop = $div.scrollHeight; }.on("didInsertElement") diff --git a/app/jobs/regular/create_daily_backup.rb b/app/jobs/regular/create_backup.rb similarity index 71% rename from app/jobs/regular/create_daily_backup.rb rename to app/jobs/regular/create_backup.rb index 40024fc76db..d455e6ab8c1 100644 --- a/app/jobs/regular/create_daily_backup.rb +++ b/app/jobs/regular/create_backup.rb @@ -1,11 +1,11 @@ require "backup_restore/backup_restore" module Jobs - class CreateDailyBackup < Jobs::Base + class CreateBackup < Jobs::Base sidekiq_options retry: false def execute(args) - return unless SiteSetting.backup_daily? + return unless SiteSetting.backups_enabled? BackupRestore.backup!(Discourse.system_user.id, publish_to_message_bus: false) end end diff --git a/app/jobs/scheduled/schedule_backup.rb b/app/jobs/scheduled/schedule_backup.rb index 7408641e54d..fa1584b6a02 100644 --- a/app/jobs/scheduled/schedule_backup.rb +++ b/app/jobs/scheduled/schedule_backup.rb @@ -5,8 +5,14 @@ module Jobs sidekiq_options retry: false def execute(args) - return unless SiteSetting.backup_daily? - Jobs.enqueue_in(rand(10.minutes), :create_daily_backup) + return unless SiteSetting.backups_enabled? + + if latest_backup = Backup.all[0] + date = Date.parse(latest_backup.filename[/\d{4}-\d{2}-\d{2}/]) + return if date + SiteSetting.backup_frequency.days > Time.now + end + + Jobs.enqueue_in(rand(10.minutes), :create_backup) end end end diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb index 02dad62554d..56c1cb5f95c 100644 --- a/app/models/site_setting.rb +++ b/app/models/site_setting.rb @@ -111,6 +111,10 @@ class SiteSetting < ActiveRecord::Base false end + def self.backups_enabled? + SiteSetting.backup_frequency > 0 + end + end # == Schema Information diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 63034613e8a..7d10fb4eaf5 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -938,7 +938,7 @@ en: allow_restore: "Allow restore, which can replace ALL site data! Leave false unless you plan to restore a backup" maximum_backups: "The maximum amount of backups to keep on disk. Older backups are automatically deleted" - backup_daily: "Automatically create a site backup once a day." + backup_frequency: "How frequently we create a site backup, in days." enable_s3_backups: "Upload backups to S3 when complete. IMPORTANT: requires valid S3 credentials entered in Files settings." s3_backup_bucket: "The remote bucket to hold backups. WARNING: Make sure it is a private bucket." diff --git a/config/site_settings.yml b/config/site_settings.yml index 2bf7eb4369c..e000630c937 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -43,7 +43,6 @@ required: client: true default: '/images/d-logo-sketch-small.png' digest_logo_url: - client: false default: '' mobile_logo_url: client: true @@ -70,7 +69,6 @@ basic: default: 5 min: 0 limit_suggested_to_category: - client: false default: false default_external_links_in_new_tab: false track_external_right_clicks: @@ -789,19 +787,17 @@ legal: backups: allow_restore: - client: false default: false maximum_backups: client: true default: 7 - backup_daily: - client: false - default: false + backup_frequency: + min: 0 + max: 7 + default: 1 enable_s3_backups: - client: false default: false s3_backup_bucket: - client: false default: '' regex: "^[^A-Z_.]+$" # can't use '.' when using HTTPS diff --git a/spec/jobs/create_daily_backup_spec.rb b/spec/jobs/create_daily_backup_spec.rb deleted file mode 100644 index 0430f367d01..00000000000 --- a/spec/jobs/create_daily_backup_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'spec_helper' - -require_dependency 'jobs/regular/create_daily_backup' - -describe Jobs::CreateDailyBackup do - it "does nothing when daily backups are disabled" do - SiteSetting.stubs(:backup_daily?).returns(false) - BackupRestore.expects(:backup!).never - Jobs::CreateDailyBackup.new.execute({}) - end - - it "calls `backup!` when the daily backups are enabled" do - SiteSetting.stubs(:backup_daily?).returns(true) - BackupRestore.expects(:backup!).with(Discourse.system_user.id, { publish_to_message_bus: false }).once - Jobs::CreateDailyBackup.new.execute({}) - end -end -