application/js/publish.js
// console.log('Loading Publishing...')
Spontaneous.Publishing = (function($, S) {
var dom = S.Dom, Dialogue = Spontaneous.Dialogue, User = S.User;
var PublishingDialogue = new JS.Class(Dialogue, {
initialize: function(content) {
this.change_sets = [];
var callback = this.page_lock_removed.bind(this);
var page_lock_removed = function(event) {
callback($.parseJSON(event.data));
};
S.EventSource.addEventListener('page_lock_status', page_lock_removed);
this.page_lock_removed_listener = page_lock_removed;
},
page_lock_removed: function(page_ids) {
var changes = this.change_sets, toUnlock = [];
page_ids.forEach(function(id) {
toUnlock = toUnlock.concat(changes.filter(function(c) { return c.change.id == id; }));
});
toUnlock.forEach(function(cs) {
cs.unlock();
});
},
cleanup: function() {
S.EventSource.removeEventListener('page_lock_status', this.page_lock_removed_listener);
},
width: function() {
return '90%';
},
title: function() {
return 'Publish Changes';
},
class_name: function() {
return 'publishing';
},
body: function() {
var wrapper = dom.div('#publishing-dialogue');
var spinner = dom.div('.spinner');
wrapper.append(spinner);
this.spinnerWrap = spinner;
this.wrapper = wrapper;
this.spinner = S.Progress(spinner[0], 48, {period: 800, segment_length: 0.30});
this.spinner.start();
spinner.append(dom.div().append(dom.p().text('Loading changes...')));
Spontaneous.Ajax.get('/changes', this.change_list_loaded.bind(this));
return wrapper;
},
buttons: function() {
btns = {};
if (User.is_developer()) {
btns['Rerender'] = this.rerender.bind(this);
}
btns['Publish'] = this.publish.bind(this);
return btns;
},
publish: function() {
var ids = [], changes = this.changes_to_publish();
for (var i = 0, ii = changes.length; i < ii; i++) {
ids = ids.concat(changes[i].page_ids());
}
if (ids.length > 0) {
Spontaneous.Ajax.post('/changes', {'page_ids': ids}, this.publish_requested.bind(this));
} else {
}
},
rerender: function() {
Spontaneous.Ajax.post('/changes/rerender', {}, this.publish_requested.bind(this));
},
publish_requested: function() {
Spontaneous.TopBar.publishing_started();
this.close();
},
change_list_loaded: function(outstanding) {
var change_list = outstanding.changes
, w = this.wrapper
, self = this
, changed_wrap = dom.div('#changes.change-list').css('opacity', 0)
, publish_wrap = dom.div('#to-publish.change-list').css('opacity', 0)
, must_publish_all = outstanding.first_publish || outstanding.must_publish_all
, spinner = this.spinner
, spinnerWrap = this.spinnerWrap
, append_to
, stop_spinner = function() {
spinner.stop();
spinnerWrap.remove();
};
if (must_publish_all) {
w.addClass('first-publish');
}
if (change_list.length === 0) {
var summary = dom.p('.publish-up-to-date').text('The site is up to date');
w.append(summary);
self.disable_button('Publish');
stop_spinner();
} else {
var publish_all = dom.a('.button').text('Publish All').click(function() {
self.set_publish_all(true);
}.bind(self));
var clear_all = dom.a('.button').text('Clear All').click(function() {
self.set_publish_all(false);
}.bind(self));
var search_input = dom.input({type: 'search', placeholder: 'Search...'}).on('click keyup blur search', function(e) {
var val = $(this).val();
if (!val) {
self.change_sets.forEach(function(cs) {
cs.show();
});
}
var query = new RegExp(RegExp.escape(val), 'i')
self.change_sets.forEach(function(cs) {
if (cs.matchesSearch(query)) {
cs.show();
} else {
cs.hide();
}
});
});
var changed_toolbar = dom.div('.actions')
.append(dom.div()
.text('Modified pages'))
.append(search_input)
.append(publish_all);
var publish_toolbar = dom.div('.actions').append(dom.div().text('Publish pages'));
if (!must_publish_all) {
publish_toolbar.append(clear_all);
}
var changed_entries = dom.div('.change-sets'), publish_entries = dom.div('.change-sets');
changed_wrap.append(changed_toolbar, changed_entries);
publish_wrap.append(publish_toolbar, publish_entries);
append_to = changed_entries;
if (must_publish_all) {
append_to = publish_entries;
}
for (var i = 0, ii = change_list.length; i < ii; i++) {
var cs = new ChangeSet(i, self, change_list[i], must_publish_all);
self.change_sets.push(cs);
append_to.append(cs.panel());
}
if (!must_publish_all) {
publish_entries.append(dom.div('.instructions').text('Add pages to publish from the list on the left'));
}
stop_spinner();
w.empty();
w.append(changed_wrap, publish_wrap);
changed_wrap.add(publish_wrap).velocity({opacity: 1});
self.changed_entries = changed_entries;
self.publish_entries = publish_entries;
this.spinner = this.spinnerWrap = null;
}
},
set_publish_all: function(state) {
this.publish_all = state;
$.each(this.change_sets, function() {
this.select(state);
});
},
change_set_state: function(change_set, state) {
var id = 'cs-' + change_set.id, panel, __this = this;
if (state) {
this.publish_entries.find('.instructions').hide();
panel = change_set.selected_panel(id).hide();
this.publish_entries.prepend(panel);
change_set.panel().disappear();
panel.appear();
} else {
panel = this.publish_entries.find('#'+id);
panel.disappear(function() {
panel.remove();
var entries = __this.publish_entries;
if (entries.find('.change-set').length === 0) {
entries.find('.instructions').fadeIn();
}
});
change_set.panel().appear();
}
},
change_set_dependency_state: function(dependentPage) {
var self = this;
var dependentSet = this.change_sets.filter(function(set) {
return set.change.id === dependentPage.id;
});
var dependentGraph = this.change_sets.filter(function(set) {
return set.has_dependency(dependentPage);
});
var group = dependentSet.concat(dependentGraph);
group.forEach(function(set) {
set.select(true);
});
},
dependency_forces_publish: function(set) {
var dependencyPage = set.page();
return this.change_sets.some(function(set) {
return set.selected && set.dependent_pages().some(function(page) {
return page.id === dependencyPage.id;
});
});
},
selected: function() {
var selected = [], cs;
for (var i = 0, ii = this.change_sets.length; i < ii; i++) {
cs = this.change_sets[i];
if (cs.selected) {
selected.push(cs);
}
}
return selected;
},
changes_to_publish: function() {
return this.selected();
}
});
var Page = new JS.Class({
initialize: function(details) {
$.extend(this, details);
},
isUnpublished: function() {
return !this.published_at;
},
isDependent: function() {
return !this.hasOwnProperty('dependent');
},
panel: function() {
var self = this
, pageTitle = dom.span('.page-title').html(this.title)
, classes = '.title' + (this.isDependent() ? '.dependent' : '')
, modificationDate = dom.div('.modification-date').html(this.modifiedAt())
, publicationDate = dom.div('.publication-date').html(this.publishedAt())
, metadata = dom.div('.dates').append(modificationDate, publicationDate);
if (this.isUnpublished()) {
pageTitle.attr('title', 'This page is new and has never been published');
pageTitle.append(dom.span('.page-title__unpublished').text('new'))
}
return dom.div(classes).append(pageTitle, dom.div('.url').text(this.url)).append(metadata).click(function() {
S.Dialogue.close();
S.Location.load_id(self.id);
});
},
modifiedAt: function() {
return 'Modified: ' + this.formatDate(this.modified_at);
},
publishedAt: function() {
var date;
if (this.isUnpublished()) {
date = 'Never';
} else {
date = this.formatDate(this.published_at);
}
return 'Published: ' + date;
},
formatDate: function(dateString) {
if (!dateString) { return ''; }
var d = new Date(dateString);
// use date.js formatting
return d.toString('d MMM yyyy, HH:mm');
}
});
var ChangeSet = new JS.Class({
initialize: function(id, dialogue, change, selected) {
this.id = id;
this.dialogue = dialogue;
this.change = change;
this.selected = selected || false;
this.mustPublish = selected || false;
},
locks: function(){
return this.change.update_locks;
},
isLocked: function() {
return this.locks().length > 0;
},
unlock: function() {
var w = this.wrapper;
w.removeClass('locked').find('.lock-state').remove();
this.locks().length = 0;
},
page_ids: function() {
var ids = [this.change.id];
return ids.concat(this.dependent_pages().map(function(p) { return p.id; }));
},
panel: function() {
if (!this._panel) {
this._panel = this.createPanel();
this.wrapper = this._panel;
}
return this._panel;
},
title: function() {
return this.page().title;
},
matchesSearch: function(re) {
return re.test(this.title());
},
hide: function() {
this._panel.hide();
},
show: function() {
this._panel.show();
},
createPanel: function() {
var w = dom.div('.change-set')
, inner = dom.div('.inner')
, page_list = dom.div('.pages')
, add = dom.div('.add').append(dom.span().text(''))
, page = this.page()
, pages = this.dependent_pages()
, locked = this.isLocked()
, info = dom.div('.info').hide();
if (page.isUnpublished()) {
w.addClass('unpublished');
}
if (locked) {
w.addClass('locked');
var lockState = dom.div('.lock-state')
, title = dom.h3().html('<strong>Cannot publish page</strong> until the following actions have completed:')
, details = dom.div('.locks')
, locks = this.locks();
locks.forEach(function(lock) {
var line = dom.p();
line.text(lock.description);
line.prepend(dom.strong().text(lock.location));
details.append(line);
});
lockState.append(title, details);
info.append(lockState);
page_list.append(info);
add.hover(function() {
info.show().velocity({'width': '100%'}, 150);
}, function() {
info.hide().css('width', 0);
});
}
page_list.append(page.panel());
for (var i = 0, ii = pages.length; i < ii; i++) {
page_list.append(pages[i].panel());
}
add.click(function() {
this.select_toggle();
}.bind(this));
inner.append(page_list);
if (!this.mustPublish) {
inner.append(add);
}
w.append(inner);
return w;
},
selected_panel: function(id) {
var panel = this.createPanel().attr('id', id);
panel.find('.add').find('span').addClass('active');
return panel;
},
select: function(state) {
var self = this;
if (this.isLocked()) { return; }
if (state === self.selected) { return; }
if (!state && self.dependency_forces_publish()) {
return;
}
self.selected = state;
self.dialogue.change_set_state(self, self.selected);
if (state) {
self.dependent_pages().forEach(function(page) {
self.dialogue.change_set_dependency_state(page);
});
var page = self.page();
if (page.isUnpublished()) {
self.dialogue.change_set_dependency_state(page);
}
}
},
select_toggle: function() {
this.select(!this.selected);
},
update_view: function() {
if (this.selected) {
this.wrapper.addClass('selected');
} else {
this.wrapper.removeClass('selected');
}
},
page: function() {
return new Page(this.change);
}.cache('_page_'),
dependency_forces_publish: function() {
return this.dialogue.dependency_forces_publish(this);
},
dependent_pages: function() {
if (!this._dependent_pages) {
this._dependent_pages = this.change.dependent.map(function(p) {
return new Page(p);
}).sort(function(p1, p2) {
var a = p2.depth, b = p1.depth;
if (a == b) return 0;
return (a < b ? -1 : 1);
});
}
return this._dependent_pages;
},
has_dependency: function(onPage) {
var dependents = this.dependent_pages();
if (dependents.length === 0) { return false; }
var matches = dependents.filter(function(dep) {
return dep.id === onPage.id;
});
return matches.length > 0;
}
});
var Publishing = new JS.Singleton({
open_dialogue: function() {
Dialogue.open(new PublishingDialogue());
}
});
return Publishing;
})(jQuery, Spontaneous);