Live editor finished
- Edit and saves entries in webpage - Autosaves every 10 seconds - Don't save if no changes - django-reversion working
This commit is contained in:
parent
9d6302ead3
commit
086f3529f4
|
@ -32,6 +32,8 @@ class EntryAdmin(VersionAdmin):
|
||||||
|
|
||||||
prepopulated_fields = {"slug": ("title",)}
|
prepopulated_fields = {"slug": ("title",)}
|
||||||
|
|
||||||
|
ignore_duplicate_revisions = True
|
||||||
|
|
||||||
suit_form_tabs = (
|
suit_form_tabs = (
|
||||||
('general', _('General')),
|
('general', _('General')),
|
||||||
('content', _('Content')),
|
('content', _('Content')),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from .views import ListView, EntryView, SearchView, RSSView
|
from .views import ListView, EntryView, EntryLiveEditView, SearchView, RSSView
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
@ -22,6 +22,13 @@ urlpatterns = [
|
||||||
EntryView.as_view(),
|
EntryView.as_view(),
|
||||||
name='item'
|
name='item'
|
||||||
),
|
),
|
||||||
|
# Live edit entry
|
||||||
|
url(
|
||||||
|
r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<slug>[\w\-]+)/edit/$',
|
||||||
|
EntryLiveEditView.as_view(),
|
||||||
|
name='item-liveedit'
|
||||||
|
),
|
||||||
|
|
||||||
# RSS
|
# RSS
|
||||||
url(
|
url(
|
||||||
r'^rss\.xml$',
|
r'^rss\.xml$',
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import json
|
||||||
|
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.http import Http404, HttpResponseRedirect, HttpResponse
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
from django.template import RequestContext
|
from django.template import RequestContext
|
||||||
from django.http import Http404, HttpResponseRedirect, HttpResponse
|
from django.utils.decorators import method_decorator
|
||||||
from django.core.urlresolvers import reverse
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
import fmartingrcom.apps.blog.utils as blog_utils
|
import fmartingrcom.apps.blog.utils as blog_utils
|
||||||
from .models import Entry
|
from .models import Entry
|
||||||
|
@ -57,6 +61,35 @@ class EntryView(View):
|
||||||
return render(request, self.template, self.data)
|
return render(request, self.template, self.data)
|
||||||
|
|
||||||
|
|
||||||
|
class EntryLiveEditView(View):
|
||||||
|
@method_decorator(csrf_exempt)
|
||||||
|
def dispatch(self, *args, **kwargs):
|
||||||
|
return super(EntryLiveEditView, self).dispatch(*args, **kwargs)
|
||||||
|
|
||||||
|
def post(self, request, year, month, day, slug):
|
||||||
|
if not request.user.is_superuser:
|
||||||
|
return HttpResponse(status=403)
|
||||||
|
|
||||||
|
try:
|
||||||
|
filters = {
|
||||||
|
'slug': slug,
|
||||||
|
'date__year': int(year),
|
||||||
|
'date__month': int(month),
|
||||||
|
'date__day': int(day),
|
||||||
|
}
|
||||||
|
|
||||||
|
item = Entry.objects.get(**filters)
|
||||||
|
except Entry.DoesNotExist:
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
data = json.loads(request.body)
|
||||||
|
if 'content' in data:
|
||||||
|
item.content = data['content']
|
||||||
|
item.save()
|
||||||
|
return HttpResponse(status=200)
|
||||||
|
return HttpResponse(status=204)
|
||||||
|
|
||||||
|
|
||||||
class SearchView(ListView):
|
class SearchView(ListView):
|
||||||
template = 'blog/search.jinja'
|
template = 'blog/search.jinja'
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,9 @@
|
||||||
'saveButton': document.querySelector('.blog-entry [data-button="save"]'),
|
'saveButton': document.querySelector('.blog-entry [data-button="save"]'),
|
||||||
'entryContent': document.querySelector('.blog-entry .content')
|
'entryContent': document.querySelector('.blog-entry .content')
|
||||||
}
|
}
|
||||||
|
this.endpoints = {
|
||||||
|
'liveEdit': document.querySelector('.blog-entry').getAttribute('data-liveedit-url')
|
||||||
|
}
|
||||||
this.editorOptions = {
|
this.editorOptions = {
|
||||||
'class': 'liveeditor',
|
'class': 'liveeditor',
|
||||||
'editor': this.UI.entryContent
|
'editor': this.UI.entryContent
|
||||||
|
@ -44,9 +47,11 @@
|
||||||
_this.showButton('save');
|
_this.showButton('save');
|
||||||
});
|
});
|
||||||
this.UI.saveButton.addEventListener('click', function(event){
|
this.UI.saveButton.addEventListener('click', function(event){
|
||||||
_this.unloadEditor();
|
_this.save(function(success){
|
||||||
_this.hideButton('save');
|
_this.unloadEditor();
|
||||||
_this.showButton('liveEdit');
|
_this.hideButton('save');
|
||||||
|
_this.showButton('liveEdit');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,11 +64,47 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
LiveEditCtrl.prototype.loadEditor = function() {
|
LiveEditCtrl.prototype.loadEditor = function() {
|
||||||
|
this._lastSavedContent = this.UI.entryContent.innerHTML
|
||||||
this.editor = new Pen(this.editorOptions);
|
this.editor = new Pen(this.editorOptions);
|
||||||
|
this.startAutoSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
LiveEditCtrl.prototype.unloadEditor = function() {
|
LiveEditCtrl.prototype.unloadEditor = function() {
|
||||||
this.editor.destroy();
|
this.editor.destroy();
|
||||||
|
this.stopAutoSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveEditCtrl.prototype.save = function(callback) {
|
||||||
|
var _this = this,
|
||||||
|
r = new XMLHttpRequest();
|
||||||
|
|
||||||
|
r.open("POST", this.endpoints.liveEdit, true);
|
||||||
|
r.onreadystatechange = function () {
|
||||||
|
if (r.readyState != 4 || r.status != 200) {
|
||||||
|
_this._lastSavedContent = _this.UI.entryContent.innerHTML;
|
||||||
|
if (callback) return callback(true, r.status);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
data = {
|
||||||
|
'content': this.UI.entryContent.innerHTML
|
||||||
|
}
|
||||||
|
r.send(JSON.stringify(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveEditCtrl.prototype.startAutoSave = function() {
|
||||||
|
var _this = this;
|
||||||
|
this._autoSaveInterval = setInterval(function(){
|
||||||
|
if (_this._lastSavedContent != _this.UI.entryContent.innerHTML) {
|
||||||
|
console.log('Auto saved');
|
||||||
|
_this.save();
|
||||||
|
} else {
|
||||||
|
console.log('Not autosaving because content is the same')
|
||||||
|
}
|
||||||
|
}, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveEditCtrl.prototype.stopAutoSave = function() {
|
||||||
|
clearInterval(this._autoSaveInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.LiveEditCtrl = new LiveEditCtrl(window);
|
window.LiveEditCtrl = new LiveEditCtrl(window);
|
||||||
|
@ -72,7 +113,8 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<article class="blog-entry {% if item.draft %}draft{% endif %}">
|
<article class="blog-entry {% if item.draft %}draft{% endif %}"
|
||||||
|
{% if request.user.is_superuser %}data-liveedit-url="{{ item.get_absolute_url() }}edit/"{% endif %}>
|
||||||
<h1 class="entry-title">
|
<h1 class="entry-title">
|
||||||
<a class="dark" href="#">{{ item.title }}</a>
|
<a class="dark" href="#">{{ item.title }}</a>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
Loading…
Reference in New Issue