summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsuelockwood <deathbearbrown@gmail.com>2013-09-09 15:38:56 -0400
committersuelockwood <deathbearbrown@gmail.com>2013-09-09 16:14:54 -0400
commite0e385838e562111c0c9b41cfc2649b1a52e92f8 (patch)
tree5fa40a0f85ea312e74b8a23f7a82d90cd5f1bdf2
parent466a7ec578793b3c56733c42adfe78f89e329ce6 (diff)
downloadcouchdb-1807-Replication.tar.gz
Replication addon sans tests1807-Replication
Fixes from code review Should use _.filter, not _.map when pulling out all the replication active tasks. fixing CSS overrides on the form Fixed Create target. Change swap to icon, added tooltips, made inputs wider. Fixed continuous, scrubbed whitepaces from db names, updated layout
-rw-r--r--.gitignore1
-rw-r--r--src/fauxton/app/addons/replication/assets/less/replication.less175
-rw-r--r--src/fauxton/app/addons/replication/base.js24
-rw-r--r--src/fauxton/app/addons/replication/resources.js68
-rw-r--r--src/fauxton/app/addons/replication/route.js47
-rw-r--r--src/fauxton/app/addons/replication/templates/form.html74
-rw-r--r--src/fauxton/app/addons/replication/templates/progress.html22
-rw-r--r--src/fauxton/app/addons/replication/tests/replicationSpec.js28
-rw-r--r--src/fauxton/app/addons/replication/views.js311
-rw-r--r--src/fauxton/assets/less/fauxton.less4
-rw-r--r--src/fauxton/settings.json.default1
11 files changed, 753 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore
index a022b1412..a4798c913 100644
--- a/.gitignore
+++ b/.gitignore
@@ -91,6 +91,7 @@ src/fauxton/app/addons/*
!src/fauxton/app/addons/config
!src/fauxton/app/addons/logs
!src/fauxton/app/addons/stats
+!src/fauxton/app/addons/replication
!src/fauxton/app/addons/contribute
!src/fauxton/app/addons/auth
!src/fauxton/app/addons/exampleAuth
diff --git a/src/fauxton/app/addons/replication/assets/less/replication.less b/src/fauxton/app/addons/replication/assets/less/replication.less
new file mode 100644
index 000000000..ec3b71822
--- /dev/null
+++ b/src/fauxton/app/addons/replication/assets/less/replication.less
@@ -0,0 +1,175 @@
+/**/
+
+@brown: #3A2C2B;
+@red: #E33F3B;
+@darkRed: #AF2D24;
+@linkRed: #DA4F49;
+@greyBrown: #A59D9D;
+@fontGrey: #808080;
+@secondarySidebar: #E4DFDC;
+
+
+form#replication {
+ position: relative;
+ max-width: none;
+ width: auto;
+
+ .form_set{
+ width: 350px;
+ display: inline-block;
+ border: 1px solid @greyBrown;
+ padding: 15px 10px 0;
+ margin-bottom: 20px;
+ &.middle{
+ width: 100px;
+ border: none;
+ position: relative;
+ height: 100px;
+ margin: 0;
+ }
+ input, select {
+ margin: 0 0 16px 5px;
+ height: 40px;
+ width: 318px;
+ }
+ .btn-group{
+ margin: 0 0 16px 5px;
+ .btn {
+ padding: 10px 57px;
+ }
+ }
+ &.local{
+ .local_option{
+ display: block;
+ }
+ .remote_option{
+ display: none;
+ }
+ .local-btn{
+ background-color: @red;
+ color: #fff;
+ }
+ .remote-btn{
+ background-color: #f5f5f5;
+ color: @fontGrey;
+ }
+ }
+ .local_option{
+ display: none;
+ }
+ .remote-btn{
+ background-color: @red;
+ color: #fff;
+ }
+ }
+
+
+ .options {
+ position: relative;
+ &:after{
+ content: '';
+ display: block;
+ position: absolute;
+ right: -16px;
+ top: 9px;
+ width: 0;
+ height: 0;
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+ border-bottom: 5px solid black;
+ border-top: none;
+ }
+ &.off {
+ &:after{
+ content: '';
+ display: block;
+ position: absolute;
+ right: -16px;
+ top: 9px;
+ width: 0;
+ height: 0;
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+ border-bottom: none;
+ border-top: 5px solid black;
+ }
+ }
+ }
+ .control-group{
+ label{
+ float: left;
+ min-height: 30px;
+ vertical-align: top;
+ padding-right: 5px;
+ min-width: 130px;
+ padding-left: 0px;
+ }
+ input[type=radio],
+ input[type=checkbox]{
+ margin: 0 0 2px 0;
+ }
+ }
+
+ .circle{
+ z-index: 0;
+ position: absolute;
+ top: 20px;
+ left: 15px;
+
+ &:after {
+ width: 70px;
+ height: 70px;
+ content: '';
+ display: block;
+ position: relative;
+ margin: 0 auto;
+ border: 1px solid @greyBrown;
+ -webkit-border-radius: 40px;
+ -moz-border-radius: 40px;
+ border-radius:40px;
+ }
+ }
+ .swap {
+ text-decoration: none;
+ z-index: 30;
+ cursor: pointer;
+ position: absolute;
+ font-size: 40px;
+ width: 27px;
+ height: 12px;
+ top: 31px;
+ left: 30px;
+ &:hover {
+ color: @greyBrown;
+ }
+ }
+
+}
+#replicationStatus{
+ &.showHeader{
+ li.header{
+ display: block;
+ border: none;
+ }
+ ul {
+ border:1px solid @greyBrown;
+ }
+ }
+ li.header{
+ display: none;
+ }
+ ul{
+ margin: 0;
+ li{
+ .progress,
+ p{
+ margin: 0px;
+ vertical-align: bottom;
+ }
+ padding: 10px 10px;
+ margin: 0;
+ list-style: none;
+ border-top: 1px solid @greyBrown;
+ }
+ }
+}
diff --git a/src/fauxton/app/addons/replication/base.js b/src/fauxton/app/addons/replication/base.js
new file mode 100644
index 000000000..93965c1c8
--- /dev/null
+++ b/src/fauxton/app/addons/replication/base.js
@@ -0,0 +1,24 @@
+// 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/replication/route"
+],
+
+function(app, FauxtonAPI, replication) {
+ replication.initialize = function() {
+ FauxtonAPI.addHeaderLink({title: "Replication", href: "#/replication", icon: "fonticon-replicate",});
+ };
+ return replication;
+});
diff --git a/src/fauxton/app/addons/replication/resources.js b/src/fauxton/app/addons/replication/resources.js
new file mode 100644
index 000000000..38ae13988
--- /dev/null
+++ b/src/fauxton/app/addons/replication/resources.js
@@ -0,0 +1,68 @@
+// 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/activetasks/resources'
+],
+
+function (app, FauxtonAPI, ActiveTasks) {
+ var Replication = {};
+
+ //these are probably dupes from the database modules. I'm going to keep them seperate for now.
+ Replication.DBModel = Backbone.Model.extend({
+ label: function () {
+ //for autocomplete
+ return this.get("name");
+ }
+ });
+
+ Replication.DBList = Backbone.Collection.extend({
+ model: Replication.DBModel,
+ url: function() {
+ return app.host + "/_all_dbs";
+ },
+ parse: function(resp) {
+ // TODO: pagination!
+ return _.map(resp, function(database) {
+ return {
+ id: database,
+ name: database
+ };
+ });
+ }
+ });
+
+ Replication.Task = Backbone.Model.extend({});
+
+ Replication.Tasks = Backbone.Collection.extend({
+ model: Replication.Task,
+ url: function () {
+ return app.host + '/_active_tasks';
+ },
+ parse: function(resp){
+ //only want replication tasks to return
+ return _.filter(resp, function(task){
+ return task.type === "replication";
+ });
+ }
+ });
+
+ Replication.Replicate = Backbone.Model.extend({
+ url: function(){
+ return app.host + "/_replicate";
+ }
+ });
+
+ return Replication;
+});
diff --git a/src/fauxton/app/addons/replication/route.js b/src/fauxton/app/addons/replication/route.js
new file mode 100644
index 000000000..8c45d6ff9
--- /dev/null
+++ b/src/fauxton/app/addons/replication/route.js
@@ -0,0 +1,47 @@
+// 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/replication/resources",
+ "addons/replication/views"
+],
+function(app, FauxtonAPI, Replication, Views) {
+ var RepRouteObject = FauxtonAPI.RouteObject.extend({
+ layout: "one_pane",
+ roles: ["_admin"],
+ routes: {
+ "replication": "defaultView"
+ },
+ selectedHeader: "Replication",
+ apiUrl: function() {
+ return app.host+"/_replication";
+ },
+ crumbs: [
+ {"name": "Replicate changes from: ", "link": "replication"}
+ ],
+ defaultView: function(){
+ this.databases = new Replication.DBList({});
+ this.tasks = new Replication.Tasks({id: "ReplicationTasks"});
+ this.setView("#dashboard-content", new Views.ReplicationForm({
+ collection: this.databases,
+ status: this.tasks
+ }));
+ }
+ });
+
+
+ Replication.RouteObjects = [RepRouteObject];
+
+ return Replication;
+});
diff --git a/src/fauxton/app/addons/replication/templates/form.html b/src/fauxton/app/addons/replication/templates/form.html
new file mode 100644
index 000000000..eeb308153
--- /dev/null
+++ b/src/fauxton/app/addons/replication/templates/form.html
@@ -0,0 +1,74 @@
+<!--
+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.
+-->
+
+<form id="replication" class="form-horizontal">
+ <div class="from form_set local">
+ <div class="btn-group">
+ <button class="btn local-btn" type="button" value="local">Local</button>
+ <button class="btn remote-btn" type="button" value="remote">Remote</button>
+ </div>
+
+ <div class="from_local local_option">
+ <select id="from_name" name="source">
+ <% _.each( databases, function( db, i ){ %>
+ <option value="<%=db.name%>"><%=db.name%></option>
+ <% }); %>
+ </select>
+ </div>
+ <div class="from_to_remote remote_option">
+ <input type="text" id="from_url" name="source" size="30" value="http://">
+ </div>
+ </div>
+
+ <div class="form_set middle">
+ <span class="circle "></span>
+ <a href="#" title="Switch Target and Source" class="swap">
+ <span class="fonticon-swap-arrows"></span>
+ </a>
+ </span>
+ </div>
+
+ <div class="to form_set local">
+ <div class="btn-group">
+ <button class="btn local-btn" type="button" value="local">Local</button>
+ <button class="btn remote-btn" type="button" value="remote">Remote</button>
+ </div>
+ <div class="to_local local_option">
+ <input type="text" id="to_name" name="target" size="30" placeholder="database name">
+ </div>
+
+ <div class="to_remote remote_option">
+ <input type="text" id="to_url" name="target" size="30" value="http://">
+ </div>
+ </div>
+
+
+ <div class="actions">
+ <div class="control-group">
+ <label for="continuous">
+ <input type="checkbox" name="continuous" value="true" id="continuous">
+ Continuous
+ </label>
+
+ <label for="createTarget">
+ <input type="checkbox" name="create_target" value="true" id="createTarget">
+ Create Target <i class="icon-question-sign " rel="tooltip" title="Create the target database"></i>
+ </label>
+ </div>
+
+ <button class="btn btn-success btn-large save" type="submit">Replicate</button>
+ </div>
+</form>
+
+<div id="replicationStatus"></div>
diff --git a/src/fauxton/app/addons/replication/templates/progress.html b/src/fauxton/app/addons/replication/templates/progress.html
new file mode 100644
index 000000000..02c47f282
--- /dev/null
+++ b/src/fauxton/app/addons/replication/templates/progress.html
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<p class="span6">Replicating <strong><%=source%></strong> to <strong><%=target%></strong></p>
+
+<div class="span4 progress progress-striped active">
+ <div class="bar" style="width: <%=progress || 0%>%;"><%=progress || "0"%>%</div>
+</div>
+
+<span class="span1">
+ <button class="cancel btn btn-danger btn-large delete" data-source="<%=source%>" data-rep-id="<%=repid%>" data-continuous="<%=continuous%>" data-target="<%=target%>">Cancel</a>
+</span>
diff --git a/src/fauxton/app/addons/replication/tests/replicationSpec.js b/src/fauxton/app/addons/replication/tests/replicationSpec.js
new file mode 100644
index 000000000..788b082f0
--- /dev/null
+++ b/src/fauxton/app/addons/replication/tests/replicationSpec.js
@@ -0,0 +1,28 @@
+// 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([
+ 'addons/replication/base',
+ 'chai'
+], function (Replication, chai) {
+ var expect = chai.expect;
+
+ describe('Replication Addon', function(){
+
+ describe('Replication DBList Collection', function () {
+ var rep;
+
+ beforeEach(function () {
+ rep = new rep.DBList(["foo","bar","baz","bo"]);
+ });
+ });
+ });
+});
diff --git a/src/fauxton/app/addons/replication/views.js b/src/fauxton/app/addons/replication/views.js
new file mode 100644
index 000000000..4b55030fb
--- /dev/null
+++ b/src/fauxton/app/addons/replication/views.js
@@ -0,0 +1,311 @@
+// 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/replication/resources"
+],
+function(app, FauxtonAPI, 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.ReplicationForm = 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.newRepModel = new replication.Replicate({});
+ },
+ afterRender: function(){
+ var dbLimit = 30;
+ var ajaxReq;
+ //re-using what we have on DB search
+ this.$el.find("input#to_name").typeahead({
+ source: function(query, process) {
+ var url = [
+ app.host,
+ "/_all_dbs?startkey=%22",
+ query,
+ "%22&endkey=%22",
+ query,
+ "\u9999%22&limit=",
+ dbLimit
+ ].join('');
+ if (ajaxReq) ajaxReq.abort();
+ ajaxReq = $.ajax({
+ url: url,
+ dataType: 'json',
+ success: function(data) {
+ process(data);
+ }
+ });
+ }
+ });
+
+ },
+
+ beforeRender: function(){
+ this.insertView("#replicationStatus", new View.ReplicationList({
+ 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');
+ }else{
+ $currentTarget.parents('.form_set').removeClass('local');
+ }
+ },
+ establish: function(){
+ return [ this.collection.fetch(), this.status.fetch()];
+ },
+ validate: function(e){
+ e.preventDefault();
+ var notification;
+ if (this.formValidation()){
+ notification = FauxtonAPI.addNotification({
+ msg: "Please enter every field.",
+ type: "error",
+ clear: true
+ });
+ }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){
+ notification = FauxtonAPI.addNotification({
+ msg: "This database doesn't exist. Check create target if you want to create it.",
+ type: "error",
+ clear: true
+ });
+ }
+ }else{
+ this.submit(e);
+ }
+ },
+ formValidation: function(e){
+ var $remote = this.$el.find('input:visible'),
+ error = false;
+ for(var i=0; i<$remote.length; i++){
+ if ($remote[i].value =="http://" || $remote[i].value ===""){
+ error = true;
+ }
+ }
+ return error;
+ },
+ serialize: function(){
+ return {
+ databases: this.collection.toJSON()
+ };
+ },
+ startReplication: function(json){
+ var that = this;
+ this.newRepModel.save(json,{
+ success: function(resp){
+ var notification = 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);
+ var notification = 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){
+ e.preventDefault();
+ //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);
+ }
+ });
+
+
+View.ReplicationList = 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(){
+ clearInterval(pollingInfo.intervalId);
+ },
+ beforeRender: function(){
+ this.collection.forEach(function(item) {
+ this.insertView(new View.replicationItem({
+ model: item
+ }));
+ }, this);
+ },
+ showHeader: function(){
+ if (this.collection.length > 0){
+ this.$el.parent().addClass('showHeader');
+ } else {
+ this.$el.parent().removeClass('showHeader');
+ }
+ },
+ 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){
+ var notification = FauxtonAPI.addNotification({
+ msg: "Replication stopped.",
+ type: "success",
+ clear: true
+ });
+ },
+ error: function(model, xhr, options){
+ var errorMessage = JSON.parse(xhr.responseText);
+ var notification = 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;
+});
diff --git a/src/fauxton/assets/less/fauxton.less b/src/fauxton/assets/less/fauxton.less
index 185113eb8..1e0ccd23d 100644
--- a/src/fauxton/assets/less/fauxton.less
+++ b/src/fauxton/assets/less/fauxton.less
@@ -166,9 +166,9 @@ a:hover{
top: 0px;
display: block;
z-index: 100000;
- left: 340px;
+ left: @navWidth;
.closeMenu & {
- left: 63px;
+ left: @collapsedNavWidth;
}
width: 100%;
}
diff --git a/src/fauxton/settings.json.default b/src/fauxton/settings.json.default
index 2b7fe892c..81cb4cbc3 100644
--- a/src/fauxton/settings.json.default
+++ b/src/fauxton/settings.json.default
@@ -4,6 +4,7 @@
{ "name": "config" },
{ "name": "logs" },
{ "name": "stats" },
+ { "name": "replication" },
{ "name": "contribute" },
{ "name": "auth" }
],