Add article generator

This commit is contained in:
Grégory Soutadé 2012-07-22 10:47:24 +02:00
parent fa07825546
commit be306c6643
11 changed files with 237 additions and 34 deletions

View File

@ -8,7 +8,7 @@ class BlogForm(ModelForm):
class ArticleForm(ModelForm): class ArticleForm(ModelForm):
class Meta: class Meta:
model = Article model = Article
exclude = ('creation_date', 'author', 'blog', 'tags') exclude = ('title_slug', 'creation_date', 'author', 'blog', 'tags')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(ArticleForm, self).__init__(*args, **kwargs) super(ArticleForm, self).__init__(*args, **kwargs)
@ -23,4 +23,3 @@ class UserForm(ModelForm):
class Meta: class Meta:
model = User model = User
exclude = ('is_staff', 'is_active', 'last_login', 'last_joined', 'user_permissions', 'groups', 'date_joined') exclude = ('is_staff', 'is_active', 'last_login', 'last_joined', 'user_permissions', 'groups', 'date_joined')

View File

@ -1 +1 @@
__all__ = ["generator", "index"] __all__ = ["generator", "index", "article"]

62
generators/article.py Normal file
View File

@ -0,0 +1,62 @@
import os
import datetime
from xml.dom.minidom import parse, parseString
from dynastie.generators.generator import DynastieGenerator
from django.db import models
# TODO : content
class Article(DynastieGenerator):
def createArticle(self, article, dom, article_elem, root):
values = {}
values['title'] = article.title
values['author'] = article.author.first_name
values['date'] = article.creation_date.strftime("%d/%m/%Y")
values['content'] = ''
self.simpleTransform(values, dom, article_elem, root)
def parse(self, article, dom, root):
for node in root.childNodes:
if node.prefix == 'dyn':
if node.localName == 'content':
article_elem = self.createElement(dom, 'article')
self.createArticle(article, dom, article_elem, node)
root.replaceChild(article_elem, node)
continue
if node.hasChildNodes():
self.parse(article, dom, node)
return
def generate(self, blog, src, output):
from dynastie.models import Article, Blog
if not os.path.exists(src + '/_article.html'):
self.addError('No _article.html found, exiting')
return self.report
try:
dom = parse(src + '/_article.html')
except xml.dom.DOMException as e:
self.addError('Error parsing _article.html : ' + e)
return self.report
if not os.path.exists(output + '/articles'):
os.mkdir(output + '/articles')
articles = Article.objects.all()
for article in articles:
#print 'Generate ' + filename
nodes = dom.getElementsByTagName("*")
self.parse(article, dom, nodes[0])
self.writeIfNotTheSame(output + '/articles/' + article.title_slug + '.html', nodes[0].toxml('utf8'))
dom = parse(src + '/_article.html')
if not self.somethingWrote:
self.addReport('Nothing changed')
return self.report

View File

@ -7,9 +7,11 @@ class DynastieGenerator:
URI = "http://indefero.soutade.fr/p/dynastie" URI = "http://indefero.soutade.fr/p/dynastie"
report = '' report = ''
somethingWrote = False
def __init__(self): def __init__(self):
self.report = '' self.report = ''
self.somethingWrote = False
def addReport(self, string, color=''): def addReport(self, string, color=''):
if color != '': if color != '':
@ -40,10 +42,8 @@ class DynastieGenerator:
dst_md5.update(content) dst_md5.update(content)
if src_md5.digest() == dst_md5.digest(): if src_md5.digest() == dst_md5.digest():
self.addReport(filename + ' regenerated with the same content, skipping...')
filename = filename + '.gz' filename = filename + '.gz'
if not os.path.exists(filename): if not os.path.exists(filename):
self.addReport(filename + ' was not previously compressed')
f = gzip.open(filename, 'wb') f = gzip.open(filename, 'wb')
f.write(content) f.write(content)
f.close() f.close()
@ -60,6 +60,8 @@ class DynastieGenerator:
f = gzip.open(filename, 'wb') f = gzip.open(filename, 'wb')
f.write(content) f.write(content)
f.close() f.close()
self.somethingWrote = True
def createElement(self, dom, name='', content=''): def createElement(self, dom, name='', content=''):
div = dom.createElement('div') div = dom.createElement('div')

View File

@ -135,5 +135,8 @@ class Index(DynastieGenerator):
self.cur_page = self.cur_page + 1 self.cur_page = self.cur_page + 1
filename = output + '/index' + str(self.cur_page) + '.html' filename = output + '/index' + str(self.cur_page) + '.html'
if not self.somethingWrote:
self.addReport('Nothing changed')
return self.report return self.report

View File

@ -2,6 +2,8 @@ import os
import shutil import shutil
import hashlib import hashlib
import inspect import inspect
from unicodedata import normalize
from re import sub
from datetime import datetime from datetime import datetime
from django.db import models from django.db import models
from django.contrib.auth.models import User from django.contrib.auth.models import User
@ -27,14 +29,16 @@ class Blog(models.Model):
self.output_path = 'sites/' + self.name + '_output' self.output_path = 'sites/' + self.name + '_output'
def create(self): def create(self):
self.create_paths()
if not os.path.exists('sites'): if not os.path.exists('sites'):
os.mkdir('sites') os.mkdir('sites')
self.remove() if not os.path.exists(self.src_path):
os.mkdir(self.src_path)
os.mkdir(self.src_path)
os.mkdir(self.output_path)
if not os.path.exists(self.output_path):
os.mkdir(self.output_path)
def remove(self): def remove(self):
if os.path.exists(self.src_path): if os.path.exists(self.src_path):
shutil.rmtree(self.src_path) shutil.rmtree(self.src_path)
@ -109,6 +113,7 @@ class Blog(models.Model):
r = e.generate(self, self.src_path, self.output_path) r = e.generate(self, self.src_path, self.output_path)
if not r is None: if not r is None:
self.report = self.report + '<br/>\n' + r self.report = self.report + '<br/>\n' + r
return self.report return self.report
class Editor(models.Model): class Editor(models.Model):
@ -124,6 +129,7 @@ class Tag(models.Model):
class Article(models.Model): class Article(models.Model):
title = models.CharField(max_length=255) title = models.CharField(max_length=255)
title_slug = models.CharField(max_length=255)
category = models.ForeignKey(Category, blank=True, null=True, on_delete=models.SET_NULL) category = models.ForeignKey(Category, blank=True, null=True, on_delete=models.SET_NULL)
published = models.BooleanField() published = models.BooleanField()
creation_date = models.DateField() creation_date = models.DateField()
@ -134,6 +140,51 @@ class Article(models.Model):
tags = models.ManyToManyField(Tag, blank=True, null=True) tags = models.ManyToManyField(Tag, blank=True, null=True)
blog = models.ForeignKey(Blog) blog = models.ForeignKey(Blog)
def slugify(self):
name = normalize('NFKD', self.title).encode('ascii', 'ignore').replace(' ', '-').lower()
#remove `other` characters
name = sub('[^a-zA-Z0-9_-]', '', name)
#nomalize dashes
name = sub('-+', '-', name)
self.title_slug = name
def save(self):
self.slugify()
super(Article, self).save()
def createArticle(self, content):
b = self.blog
b.create_paths()
output = b.src_path
if not os.path.exists(output + '/_articles'):
os.mkdir(output + '/_articles')
filename = output + '/_articles/' + str(self.pk)
if os.path.exists(filename):
os.unlink(filename)
f = open(filename, 'wb')
f.write(content)
f.close()
def remove(self):
b = self.blog
b.create_paths()
output = b.src_path
filename = output + '/_articles/' + str(self.pk)
if os.path.exists(filename):
os.unlink(filename)
output = b.output_path
filename = output + '/articles/' + self.title_slug + '.html'
if os.path.exists(filename):
os.unlink(filename)
filename = filename + '.gz'
if os.path.exists(filename):
os.unlink(filename)
class Comment(models.Model): class Comment(models.Model):
article = models.ForeignKey(Article) article = models.ForeignKey(Article)
parent = models.ForeignKey('Comment') parent = models.ForeignKey('Comment')
@ -149,3 +200,7 @@ def delete_blog_signal(sender, **kwargs):
@receiver(post_delete, sender=Blog) @receiver(post_delete, sender=Blog)
def delete_blog_signal(sender, **kwargs): def delete_blog_signal(sender, **kwargs):
sender.remove() sender.remove()
@receiver(post_delete, sender=Article)
def delete_article_signal(sender, **kwargs):
sender.remove()

View File

@ -1,8 +1,35 @@
{% extends "templates/base.html" %} {% extends "templates/base.html" %}
{% block head %}
<script type="text/javascript" src="/static/js/tinymce/jscripts/tiny_mce/tiny_mce.js"></script>
<script type="text/javascript">
tinyMCE.init({
// General options
mode : "textareas",
theme : "advanced",
plugins : "autolink,lists,spellchecker,pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions,iespell,inlinepopups,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template",
editor_selector : "mceAdvanced",
// Theme options
theme_advanced_buttons1 : "bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,formatselect,fontselect,fontsizeselect",
theme_advanced_buttons2 : "bullist,numlist,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code,|,insertdate,inserttime,preview,|,forecolor,backcolor",
theme_advanced_buttons3 : "tablecontrols,|,hr,removeformat,visualaid,|,charmap,emotions,iespell,media,advhr",
// theme_advanced_buttons4 : "insertlayer,moveforward,movebackward,absolute,|,styleprops,spellchecker,|,cite,abbr,acronym,del,ins,attribs,|,visualchars,nonbreaking,template,blockquote,pagebreak,|,insertfile,insertimage",
theme_advanced_toolbar_location : "top",
theme_advanced_toolbar_align : "left",
theme_advanced_statusbar_location : "bottom",
theme_advanced_resizing : true,
width: "100%",
height: "400"
});
</script>
{% endblock %}
{% block content %} {% block content %}
<form action="/article/add/{{ blog_id }}" method="post">{% csrf_token %} <form action="/article/add/{{ blog_id }}" method="post">{% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
<textarea name="content" class="mceAdvanced"></textarea><br/><br/>
<input type="submit" name="add" value="Add" /><input type="submit" name="preview" value="Preview" /><input type="submit" name="cancel" value="Cancel" /> <input type="submit" name="add" value="Add" /><input type="submit" name="preview" value="Preview" /><input type="submit" name="cancel" value="Cancel" />
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,6 +1,7 @@
<html> <html>
<head> <head>
<title>Dynastie</title> <title>Dynastie</title>
{% block head %} {% endblock %}
</head> </head>
<body> <body>
<a href="/user">Users</a> <a href="/blog">Blogs</a> <a href="/category">Categories</a> <a href="/disconnect">Disconnect</a><br/><br/> <a href="/user">Users</a> <a href="/blog">Blogs</a> <a href="/category">Categories</a> <a href="/disconnect">Disconnect</a><br/><br/>

View File

@ -1,8 +1,35 @@
{% extends "templates/base.html" %} {% extends "templates/base.html" %}
{% block head %}
<script type="text/javascript" src="/static/js/tinymce/jscripts/tiny_mce/tiny_mce.js"></script>
<script type="text/javascript">
tinyMCE.init({
// General options
mode : "textareas",
theme : "advanced",
plugins : "autolink,lists,spellchecker,pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions,iespell,inlinepopups,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template",
editor_selector : "mceAdvanced",
// Theme options
theme_advanced_buttons1 : "bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,formatselect,fontselect,fontsizeselect",
theme_advanced_buttons2 : "bullist,numlist,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code,|,insertdate,inserttime,preview,|,forecolor,backcolor",
theme_advanced_buttons3 : "tablecontrols,|,hr,removeformat,visualaid,|,charmap,emotions,iespell,media,advhr",
// theme_advanced_buttons4 : "insertlayer,moveforward,movebackward,absolute,|,styleprops,spellchecker,|,cite,abbr,acronym,del,ins,attribs,|,visualchars,nonbreaking,template,blockquote,pagebreak,|,insertfile,insertimage",
theme_advanced_toolbar_location : "top",
theme_advanced_toolbar_align : "left",
theme_advanced_statusbar_location : "bottom",
theme_advanced_resizing : true,
width: "100%",
height: "400"
});
</script>
{% endblock %}
{% block content %} {% block content %}
<form action="/article/edit/{{ article_id }}" method="post">{% csrf_token %} <form action="/article/edit/{{ article_id }}" method="post">{% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
<input type="submit" name="edit" value="Edit" /><input type="submit" name="cancel" value="Cancel" /> <textarea name="content" class="mceAdvanced">{{ content }}</textarea>
<input type="submit" name="edit" value="Edit" /><input type="submit" name="preview" value="Preview" /><input type="submit" name="cancel" value="Cancel" />
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,6 +1,9 @@
{% extends "templates/base.html" %} {% extends "templates/base.html" %}
{% block content %} {% block content %}
{% if edited %}
<p style="color:green">User successfuly updated</p>
{% endif %}
{% if user.is_superuser or user.id == user_to_edit.id %} {% if user.is_superuser or user.id == user_to_edit.id %}
<form action="/user/edit/{{ user_to_edit.id }}" method="post"> <form action="/user/edit/{{ user_to_edit.id }}" method="post">
{% csrf_token %} {% csrf_token %}

View File

@ -1,3 +1,4 @@
import os
from datetime import datetime, date, time from datetime import datetime, date, time
from django.shortcuts import render from django.shortcuts import render
from django.contrib.auth import authenticate, login, logout from django.contrib.auth import authenticate, login, logout
@ -23,13 +24,13 @@ def index(request):
c = {'auth_key': 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',\ c = {'auth_key': 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',\
'login_failed' : login_failed} 'login_failed' : login_failed}
return render(request, 'templates/login.html', c); return render(request, 'templates/login.html', c)
def disconnect(request): def disconnect(request):
logout(request) logout(request)
c = {'auth_key': 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',\ c = {'auth_key': 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',\
'login_failed' : False} 'login_failed' : False}
return render(request, 'templates/login.html', c); return render(request, 'templates/login.html', c)
@login_required @login_required
def user(request): def user(request):
@ -37,7 +38,7 @@ def user(request):
c = {'users' : users} c = {'users' : users}
return render(request, 'templates/user.html', c); return render(request, 'templates/user.html', c)
@login_required @login_required
def add_user(request): def add_user(request):
@ -66,21 +67,24 @@ def add_user(request):
@login_required @login_required
def edit_user(request, user_id): def edit_user(request, user_id):
if user_id != request.user.id and not request.user.is_superuser:
return HttpResponseRedirect('/user')
user = User.objects.get(pk=user_id) user = User.objects.get(pk=user_id)
if user is None: if user is None:
raise Http404 raise Http404
edited = False
if request.method == 'POST': # If the form has been submitted... if request.method == 'POST': # If the form has been submitted...
if int(user_id) != int(request.user.id) and (not request.user.is_superuser):
return HttpResponseRedirect('/user/' + str(user_id))
if 'edit' in request.POST: if 'edit' in request.POST:
form = UserForm(request.POST, instance=user) # A form bound to the POST data form = UserForm(request.POST, instance=user, initial={'password':''}) # A form bound to the POST data
if form.is_valid(): # All validation rules pass if form.is_valid(): # All validation rules pass
form.save() form.save()
user.set_password(request.POST['password']) if request.POST['password'] != '':
user.set_password(request.POST['password'])
user.save() user.save()
edited = True
else: else:
if 'delete' in request.POST and request.user.is_superuser: if 'delete' in request.POST and request.user.is_superuser:
User.objects.get(pk=user_id).delete() User.objects.get(pk=user_id).delete()
@ -88,11 +92,11 @@ def edit_user(request, user_id):
if 'cancel' in request.POST: if 'cancel' in request.POST:
return HttpResponseRedirect('/user') return HttpResponseRedirect('/user')
else: else:
form = UserForm(instance=user) # An unbound form form = UserForm(instance=user, initial={'password':''}) # An unbound form
c = {'user_to_edit' : user, 'form' : form, 'edited' : edited}
c = {'user_to_edit' : user, 'form' : form} return render(request, 'templates/edit_user.html', c)
return render(request, 'templates/edit_user.html', c);
@login_required @login_required
def category(request): def category(request):
@ -100,7 +104,7 @@ def category(request):
c = {'categories' : categories} c = {'categories' : categories}
return render(request, 'templates/category.html', c); return render(request, 'templates/category.html', c)
@login_required @login_required
def add_category(request): def add_category(request):
@ -143,7 +147,7 @@ def edit_category(request, category_id):
c = {'category' : category, 'form' : form} c = {'category' : category, 'form' : form}
return render(request, 'templates/edit_category.html', c); return render(request, 'templates/edit_category.html', c)
@login_required @login_required
def delete_category(request, category_id): def delete_category(request, category_id):
@ -168,7 +172,7 @@ def blog(request):
c = {'blogs' : b} c = {'blogs' : b}
return render(request, 'templates/blog.html', c); return render(request, 'templates/blog.html', c)
@login_required @login_required
def add_blog(request): def add_blog(request):
@ -207,7 +211,7 @@ def view_blog(request, blog_id):
c = {'blog' : b, 'articles' : articles, 'form' : form} c = {'blog' : b, 'articles' : articles, 'form' : form}
return render(request, 'templates/view_blog.html', c); return render(request, 'templates/view_blog.html', c)
@login_required @login_required
def edit_blog(request, blog_id): def edit_blog(request, blog_id):
@ -236,12 +240,12 @@ def edit_blog(request, blog_id):
c = {'blog' : b, 'articles' : articles, 'form' : form} c = {'blog' : b, 'articles' : articles, 'form' : form}
return render(request, 'templates/view_blog.html', c); return render(request, 'templates/view_blog.html', c)
@login_required @login_required
def add_article(request, blog_id): def add_article(request, blog_id):
if not request.user.is_superuser: if not request.user.is_superuser:
b = Blog.objects.filter(id=blog_id).filter(writers=request.user.id) b = Blog.objects.filter(id=blog_id).filter(writers=request.user.id)[0]
if b is None: if b is None:
raise Http404 raise Http404
@ -249,9 +253,12 @@ def add_article(request, blog_id):
if request.method == 'POST': # If the form has been submitted... if request.method == 'POST': # If the form has been submitted...
if 'add' in request.POST: if 'add' in request.POST:
article = Article(blog=Blog.objects.get(pk=blog_id), author=User.objects.get(pk=request.user.id), creation_date=datetime.now()) article = Article(blog=Blog.objects.get(pk=blog_id), author=User.objects.get(pk=request.user.id), creation_date=datetime.now())
content = request.POST['content']
# del request.POST['content']
form = ArticleForm(request.POST, instance=article) # A form bound to the POST data form = ArticleForm(request.POST, instance=article) # A form bound to the POST data
if form.is_valid(): # All validation rules pass if form.is_valid(): # All validation rules pass
form.save() form = form.save()
form.createArticle(content)
# Process the data in form.cleaned_data # Process the data in form.cleaned_data
# ... # ...
return HttpResponseRedirect('/blog/' + blog_id) # Redirect after POST return HttpResponseRedirect('/blog/' + blog_id) # Redirect after POST
@ -271,18 +278,24 @@ def edit_article(request, article_id):
if article is None: if article is None:
raise Http404 raise Http404
title = article.title
blog_id = article.blog.id blog_id = article.blog.id
if not request.user.is_superuser: if not request.user.is_superuser:
b = Blog.objects.filter(id=article.blog.id).filter(writers=request.user.id) b = Blog.objects.filter(pk=article.blog.id).filter(writers=request.user.id)[0]
if b is None: if b is None:
raise Http404 raise Http404
else:
b = Blog.objects.get(pk=article.blog.id)
if request.method == 'POST': # If the form has been submitted... if request.method == 'POST': # If the form has been submitted...
if 'edit' in request.POST: if 'edit' in request.POST:
form = ArticleForm(request.POST, instance=article) # A form bound to the POST data form = ArticleForm(request.POST, instance=article) # A form bound to the POST data
if form.is_valid(): # All validation rules pass if form.is_valid(): # All validation rules pass
if title != article.title:
article.remove()
form.save() form.save()
# Process the data in form.cleaned_data # Process the data in form.cleaned_data
# ... # ...
@ -293,8 +306,17 @@ def edit_article(request, article_id):
else: else:
form = ArticleForm(instance=article) # An unbound form form = ArticleForm(instance=article) # An unbound form
b.create_paths()
filename = b.src_path + '/_articles/' + str(article.pk)
if os.path.exists(filename):
f = open(filename, 'rb')
content = f.read()
f.close()
else:
content = 'Empty article'
return render(request, 'edit_article.html', { return render(request, 'edit_article.html', {
'form': form, 'article_id' : article_id 'form': form, 'article_id' : article_id, 'content' : content
}) })
@login_required @login_required
@ -304,14 +326,16 @@ def delete_article(request, article_id):
if article is None: if article is None:
raise Http404 raise Http404
b = Blog.objects.filter(writers=request.user.id).filter(pk=article.blog.id) b = Blog.objects.filter(writers=request.user.id).filter(pk=article.blog.pk)
if b is None: if b is None:
raise Http404 raise Http404
blog_id = article.blog.pk
article.delete() article.delete()
return HttpResponseRedirect('/blog/' + str(article.blog.id)) return HttpResponseRedirect('/blog/' + str(blog_id))
@login_required @login_required
def generate(request, blog_id): def generate(request, blog_id):