blob: 81473c40fb9784905388af20e6a515db1b35c6a2 [file] [log] [blame]
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
define([
'app',
'api',
'addons/fauxton/components',
'addons/replication/resources'
],
function (app, FauxtonAPI, Components, Replication) {
var View = {},
Events = {},
pollingInfo = {
rate: 5,
intervalId: null
};
_.extend(Events, Backbone.Events);
// NOTES: http://wiki.apache.org/couchdb/Replication
// Replication form view is huge
// -----------------------------------
// afterRender: autocomplete on the target input field
// beforeRender: add the status table
// disableFields: disable non active fields on submit
// enableFields: enable field when radio btns are clicked
// establish: get the DB list for autocomplete
// formValidation: make sure fields aren't empty
// showProgress: make a call to active_tasks model and show only replication types. Poll every 5 seconds. (make this it's own view)
// startReplication: saves to the model, starts replication
// submit: form submit handler
// swapFields: change to and from target
// toggleAdvancedOptions: toggle advanced
View.ReplicationFormForAdmins = FauxtonAPI.View.extend({
template: 'addons/replication/templates/form',
events: {
'submit #replication': 'validate',
'click .btn-group .btn': 'showFields',
'click .swap': 'swapFields',
'click .options': 'toggleAdvancedOptions'
},
initialize: function (options) {
this.status = options.status;
this.selectedDB = options.selectedDB;
this.newRepModel = new Replication.Replicate({});
},
afterRender: function () {
this.dbSearchTypeahead = new Components.DbSearchTypeahead({
dbLimit: 30,
el: 'input#to_name'
});
this.dbSearchTypeahead.render();
},
beforeRender: function () {
this.insertView('#replicationStatus', new View.ReplicationListForAdmins({
collection: this.status
}));
},
cleanup: function () {
clearInterval(pollingInfo.intervalId);
},
enableFields: function () {
this.$el.find('input', 'select').attr('disabled', false);
},
disableFields: function () {
this.$el.find('input:hidden', 'select:hidden').attr('disabled', true);
},
showFields: function (e) {
var $currentTarget = this.$(e.currentTarget),
targetVal = $currentTarget.val();
if (targetVal === 'local') {
$currentTarget.parents('.form_set').addClass('local');
return;
}
$currentTarget.parents('.form_set').removeClass('local');
},
establish: function () {
return [this.collection.fetch(), this.status.fetch()];
},
validate: function (e) {
e.preventDefault();
if (this.formValidation()) {
FauxtonAPI.addNotification({
msg: 'Please enter every field.',
type: 'error',
clear: true
});
return;
} else if (this.$('input#to_name').is(':visible') && !this.$('input[name=create_target]').is(':checked')) {
var alreadyExists = this.collection.where({
"name": this.$('input#to_name').val()
});
if (alreadyExists.length === 0) {
FauxtonAPI.addNotification({
msg: 'This database doesn\'t exist. Check create target if you want to create it.',
type: 'error',
clear: true
});
return;
}
}
this.submit(e);
},
formValidation: function () {
var $remote = this.$el.find('input:visible'),
error = false;
_.each($remote, function (item) {
if (item.value === 'http://' || item.value === '') {
error = true;
}
});
return error;
},
serialize: function () {
return {
databases: this.collection.toJSON(),
selectedDB: this.selectedDB
};
},
startReplication: function (json) {
var that = this;
this.newRepModel.save(json, {
success: function (resp) {
FauxtonAPI.addNotification({
msg: 'Replication from ' + resp.get('source') + ' to ' + resp.get('target') + ' has begun.',
type: 'success',
clear: true
});
that.updateButtonText(false);
Events.trigger('update:tasks');
},
error: function (model, xhr, options) {
var errorMessage = JSON.parse(xhr.responseText);
FauxtonAPI.addNotification({
msg: errorMessage.reason,
type: 'error',
clear: true
});
that.updateButtonText(false);
}
});
this.enableFields();
},
updateButtonText: function (wait) {
var $button = this.$('#replication button[type=submit]');
if (wait) {
$button.text('Starting replication...').attr('disabled', true);
} else {
$button.text('Replication').attr('disabled', false);
}
},
submit: function (e) {
this.disableFields();
var formJSON = {};
_.map(this.$(e.currentTarget).serializeArray(), function (formData) {
if (formData.value !== '') {
formJSON[formData.name] = (formData.value === "true" ? true : formData.value.replace(/\s/g, '').toLowerCase());
}
});
this.updateButtonText(true);
this.startReplication(formJSON);
},
swapFields: function (e) {
// WALL O' VARIABLES
var $fromSelect = this.$('#from_name'),
$toSelect = this.$('#to_name'),
$toInput = this.$('#to_url'),
$fromInput = this.$('#from_url'),
fromSelectVal = $fromSelect.val(),
fromInputVal = $fromInput.val(),
toSelectVal = $toSelect.val(),
toInputVal = $toInput.val();
$fromSelect.val(toSelectVal);
$toSelect.val(fromSelectVal);
$fromInput.val(toInputVal);
$toInput.val(fromInputVal);
// prevent other click handlers from running
return false;
}
});
View.ReplicationForm = View.ReplicationFormForAdmins.extend({
template: 'addons/replication/templates/form',
events: {
'submit #replication': 'validate',
'click .btn-group .btn': 'showFields',
'click .swap': 'swapFields',
'click .options': 'toggleAdvancedOptions'
},
initialize: function (options) {
this.selectedDB = options.selectedDB;
this.newRepModel = new Replication.Replicate({});
},
beforeRender: function () {},
establish: function () {
return [this.collection.fetch()];
}
});
View.ReplicationListForAdmins = FauxtonAPI.View.extend({
tagName: 'ul',
initialize: function () {
Events.bind('update:tasks', this.establish, this);
this.listenTo(this.collection, 'reset', this.render);
this.$el.prepend('<li class="header"><h4>Active Replication Tasks</h4></li>');
},
establish: function () {
return [this.collection.fetch({ reset: true })];
},
setPolling: function () {
var that = this;
this.cleanup();
pollingInfo.intervalId = setInterval(function () {
that.establish();
}, pollingInfo.rate * 1000);
},
cleanup: function () {
Events.unbind('update:tasks');
clearInterval(pollingInfo.intervalId);
},
beforeRender: function () {
this.collection.forEach(function (item) {
this.insertView(new View.replicationItem({
model: item
}));
}, this);
},
showHeader: function () {
this.$el.parent()
.toggleClass('showHeader', this.collection.length > 0);
},
afterRender: function () {
this.showHeader();
this.setPolling();
}
});
//make this a table row item.
View.replicationItem = FauxtonAPI.View.extend({
tagName: 'li',
className: 'row',
template: 'addons/replication/templates/progress',
events: {
'click .cancel': 'cancelReplication'
},
initialize: function () {
this.newRepModel = new Replication.Replicate({});
},
establish: function () {
return [this.model.fetch()];
},
cancelReplication: function (e) {
// need to pass "cancel": true with source & target
var $currentTarget = this.$(e.currentTarget),
repID = $currentTarget.attr('data-rep-id');
this.newRepModel.save({
"replication_id": repID,
"cancel": true
},
{
success: function (model, xhr, options) {
FauxtonAPI.addNotification({
msg: 'Replication stopped.',
type: 'success',
clear: true
});
},
error: function (model, xhr, options) {
var errorMessage = JSON.parse(xhr.responseText);
FauxtonAPI.addNotification({
msg: errorMessage.reason,
type: 'error',
clear: true
});
}
});
},
afterRender: function () {
if (this.model.get('continuous')) {
this.$el.addClass('continuous');
}
},
serialize: function () {
return {
progress: this.model.get('progress'),
target: this.model.get('target'),
source: this.model.get('source'),
continuous: this.model.get('continuous'),
repid: this.model.get('replication_id')
};
}
});
return View;
});