diff --git a/fmartingrcom/apps/blog/admin.py b/fmartingrcom/apps/blog/admin.py index 76b08ff..baa2176 100644 --- a/fmartingrcom/apps/blog/admin.py +++ b/fmartingrcom/apps/blog/admin.py @@ -32,6 +32,8 @@ class EntryAdmin(VersionAdmin): prepopulated_fields = {"slug": ("title",)} + ignore_duplicate_revisions = True + suit_form_tabs = ( ('general', _('General')), ('content', _('Content')), diff --git a/fmartingrcom/apps/blog/urls.py b/fmartingrcom/apps/blog/urls.py index dd221e7..d9b3d41 100644 --- a/fmartingrcom/apps/blog/urls.py +++ b/fmartingrcom/apps/blog/urls.py @@ -1,6 +1,6 @@ from django.conf.urls import url -from .views import ListView, EntryView, SearchView, RSSView +from .views import ListView, EntryView, EntryLiveEditView, SearchView, RSSView urlpatterns = [ @@ -22,6 +22,13 @@ urlpatterns = [ EntryView.as_view(), name='item' ), + # Live edit entry + url( + r'^(?P\d{4})/(?P\d{2})/(?P\d{2})/(?P[\w\-]+)/edit/$', + EntryLiveEditView.as_view(), + name='item-liveedit' + ), + # RSS url( r'^rss\.xml$', diff --git a/fmartingrcom/apps/blog/views.py b/fmartingrcom/apps/blog/views.py index 53eaeaa..eff44c0 100644 --- a/fmartingrcom/apps/blog/views.py +++ b/fmartingrcom/apps/blog/views.py @@ -1,9 +1,13 @@ # -*- 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.template.loader import get_template from django.template import RequestContext -from django.http import Http404, HttpResponseRedirect, HttpResponse -from django.core.urlresolvers import reverse +from django.utils.decorators import method_decorator +from django.views.decorators.csrf import csrf_exempt import fmartingrcom.apps.blog.utils as blog_utils from .models import Entry @@ -57,6 +61,35 @@ class EntryView(View): 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): template = 'blog/search.jinja' diff --git a/fmartingrcom/themes/v1/templates/blog/entry.jinja b/fmartingrcom/themes/v1/templates/blog/entry.jinja index 320bcd0..1367e66 100644 --- a/fmartingrcom/themes/v1/templates/blog/entry.jinja +++ b/fmartingrcom/themes/v1/templates/blog/entry.jinja @@ -26,6 +26,9 @@ 'saveButton': document.querySelector('.blog-entry [data-button="save"]'), 'entryContent': document.querySelector('.blog-entry .content') } + this.endpoints = { + 'liveEdit': document.querySelector('.blog-entry').getAttribute('data-liveedit-url') + } this.editorOptions = { 'class': 'liveeditor', 'editor': this.UI.entryContent @@ -44,9 +47,11 @@ _this.showButton('save'); }); this.UI.saveButton.addEventListener('click', function(event){ - _this.unloadEditor(); - _this.hideButton('save'); - _this.showButton('liveEdit'); + _this.save(function(success){ + _this.unloadEditor(); + _this.hideButton('save'); + _this.showButton('liveEdit'); + }); }); } @@ -59,11 +64,47 @@ } LiveEditCtrl.prototype.loadEditor = function() { + this._lastSavedContent = this.UI.entryContent.innerHTML this.editor = new Pen(this.editorOptions); + this.startAutoSave(); } LiveEditCtrl.prototype.unloadEditor = function() { 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); @@ -72,7 +113,8 @@ {% endblock %} {% block content %} -