Grégory Soutadé
0150b9f6cd
Delete comments when a post is deleted Forgot to set email input as hidden in comment responses Forgot to setup cache for post comment generation
370 lines
13 KiB
Python
370 lines
13 KiB
Python
import os
|
|
import shutil
|
|
import hashlib
|
|
import inspect
|
|
import gzip
|
|
from unicodedata import normalize
|
|
from re import sub
|
|
from datetime import datetime
|
|
from django.db import models
|
|
from django.contrib.auth.models import User
|
|
from django.db.models.signals import post_init, pre_delete, post_delete, post_save
|
|
from django.dispatch import receiver
|
|
from dynastie.generators import *
|
|
|
|
def slugify(name):
|
|
name = name.strip()
|
|
name = normalize('NFKD', name).encode('ascii', 'ignore').replace(' ', '-').lower()
|
|
#remove `other` characters
|
|
name = sub('[^a-zA-Z0-9_-]', '', name)
|
|
#nomalize dashes
|
|
name = sub('-+', '-', name)
|
|
return name
|
|
|
|
class Blog(models.Model):
|
|
name = models.CharField(max_length=255, unique=True)
|
|
title = models.CharField(max_length=255)
|
|
description = models.TextField(max_length=255, blank=True)
|
|
keywords = models.TextField(blank=True)
|
|
writers = models.ManyToManyField(User)
|
|
|
|
engines = list()
|
|
|
|
src_path = ''
|
|
output_path = ''
|
|
report = ''
|
|
|
|
def create_paths(self):
|
|
self.src_path = os.environ['DYNASTIE_ROOT'] + 'sites/' + self.name
|
|
self.output_path = os.environ['DYNASTIE_ROOT'] + 'sites/' + self.name + '_output'
|
|
|
|
def create(self):
|
|
self.create_paths()
|
|
if not os.path.exists('sites'):
|
|
os.mkdir('sites')
|
|
|
|
if not os.path.exists(self.src_path):
|
|
os.mkdir(self.src_path)
|
|
|
|
if not os.path.exists(self.output_path):
|
|
os.mkdir(self.output_path)
|
|
|
|
def remove(self):
|
|
if os.path.exists(self.src_path):
|
|
shutil.rmtree(self.src_path)
|
|
if os.path.exists(self.output_path):
|
|
shutil.rmtree(self.output_path)
|
|
|
|
def load_generators(self):
|
|
if os.path.exists(self.src_path + '/_generators'):
|
|
f = open(self.src_path + '/_generators', 'r')
|
|
for line in f:
|
|
if line.startswith("#"):
|
|
continue
|
|
engine = line.strip()
|
|
if not engine in globals():
|
|
print 'Engine ' + engine + ' doesn\'t exists'
|
|
self.engines.append(globals()[engine])
|
|
f.close()
|
|
else:
|
|
self.engines.append(globals()['post'])
|
|
self.engines.append(globals()['index'])
|
|
self.engines.append(globals()['category'])
|
|
self.engines.append(globals()['tag'])
|
|
self.engines.append(globals()['archive'])
|
|
self.engines.append(globals()['atom'])
|
|
self.engines.append(globals()['rss'])
|
|
|
|
def copytree(self, src, dst):
|
|
names = os.listdir(src)
|
|
|
|
errors = []
|
|
for name in names:
|
|
if name.startswith('_') or name.endswith('~'):
|
|
continue
|
|
srcname = os.path.join(src, name)
|
|
dstname = os.path.join(dst, name)
|
|
try:
|
|
if os.path.islink(srcname) and not os.path.exists(dstname):
|
|
linkto = os.readlink(srcname)
|
|
os.symlink(linkto, dstname)
|
|
if os.path.isdir(srcname):
|
|
if not os.path.exists(dstname):
|
|
os.makedirs(dstname)
|
|
self.copytree(srcname, dstname)
|
|
else:
|
|
copied = False
|
|
if os.path.exists(dstname):
|
|
src_md5 = hashlib.md5()
|
|
f = open(srcname,'rb')
|
|
src_md5.update(f.read())
|
|
f.close()
|
|
|
|
dst_md5 = hashlib.md5()
|
|
f = open(dstname,'rb')
|
|
dst_md5.update(f.read())
|
|
f.close()
|
|
|
|
if src_md5.digest() != dst_md5.digest():
|
|
self.report = self.report + 'Update ' + dstname + '<br/>\n'
|
|
shutil.copy2(srcname, dstname)
|
|
copied = True
|
|
|
|
else:
|
|
self.report = self.report + 'Add ' + dstname + '<br/>\n'
|
|
shutil.copy2(srcname, dstname)
|
|
copied = True
|
|
|
|
if copied:
|
|
exts = ('css', 'html', 'htm', 'xhtml', 'js')
|
|
found = False
|
|
for ext in exts:
|
|
if srcname.endswith(ext):
|
|
found = True
|
|
break
|
|
if found:
|
|
dstname = dstname + '.gz'
|
|
if os.path.exists(dstname):
|
|
os.unlink(dstname)
|
|
f = open(srcname)
|
|
content = f.read()
|
|
f.close()
|
|
f = gzip.open(dstname, 'wb')
|
|
f.write(content)
|
|
f.close()
|
|
|
|
|
|
# XXX What about devices, sockets etc.?
|
|
except (IOError, os.error), why:
|
|
errors.append((srcname, dstname, str(why)))
|
|
# catch the Error from the recursive copytree so that we can
|
|
# continue with other files
|
|
except Exception, err:
|
|
errors.extend(err.args[0])
|
|
if errors:
|
|
raise Exception(errors)
|
|
|
|
def generate(self):
|
|
self.report = '<br/><br/>Generation of ' + datetime.now().strftime("%d/%m/%Y at %H:%M:%S") + '<br/>\n'
|
|
self.load_generators()
|
|
self.copytree(self.src_path, self.output_path)
|
|
generated = []
|
|
hash_posts = {}
|
|
hash_posts_content = {}
|
|
for engine in self.engines:
|
|
if not inspect.ismodule(engine):
|
|
continue
|
|
for name, obj in inspect.getmembers(engine):
|
|
if inspect.isclass(obj) and obj.__module__.startswith("dynastie.generators"):
|
|
if obj.__module__ in generated: continue
|
|
e = obj(hash_posts, hash_posts_content)
|
|
r = e.generate(self, self.src_path, self.output_path)
|
|
generated.append(obj.__module__)
|
|
if not r is None:
|
|
self.report = self.report + '<br/>\n' + r
|
|
|
|
return self.report
|
|
|
|
class Editor(models.Model):
|
|
name = models.CharField(max_length=255, unique=True)
|
|
|
|
class Category(models.Model):
|
|
name = models.CharField(max_length=255, unique=True)
|
|
name_slug = models.CharField(max_length=255)
|
|
parent = models.ForeignKey('self', blank=True, null=True)
|
|
description = models.TextField(max_length=255, blank=True)
|
|
blog = models.ForeignKey(Blog)
|
|
|
|
def save(self):
|
|
self.name_slug = slugify(self.name)
|
|
super(Category, self).save()
|
|
|
|
def remove(self):
|
|
blog = Blog.objects.get(pk=self.blog.id)
|
|
output = blog.output_path + '/category/' + self.name_slug
|
|
if os.path.exists(output):
|
|
shutil.rmtree(output)
|
|
|
|
|
|
class Tag(models.Model):
|
|
name = models.CharField(max_length=255, unique=True)
|
|
name_slug = models.CharField(max_length=255)
|
|
blog = models.ForeignKey(Blog)
|
|
|
|
def save(self):
|
|
self.name_slug = slugify(self.name)
|
|
super(Tag, self).save()
|
|
|
|
def remove(self):
|
|
blog = Blog.objects.get(pk=self.blog.id)
|
|
output = blog.output_path + '/tag/' + self.name_slug
|
|
if os.path.exists(output):
|
|
shutil.rmtree(output)
|
|
|
|
class Post(models.Model):
|
|
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)
|
|
published = models.BooleanField()
|
|
creation_date = models.DateTimeField()
|
|
modification_date = models.DateTimeField()
|
|
front_page = models.BooleanField()
|
|
author = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
|
|
description = models.TextField(max_length=255, blank=True)
|
|
keywords = models.TextField(blank=True)
|
|
tags = models.ManyToManyField(Tag, blank=True, null=True)
|
|
blog = models.ForeignKey(Blog)
|
|
|
|
def getPath(self):
|
|
filename = '/post/'
|
|
filename = filename + self.creation_date.strftime("%Y") + '/' + self.creation_date.strftime("%m") + '/'
|
|
filename = filename + self.title_slug + '.html'
|
|
return filename
|
|
|
|
def save(self):
|
|
self.title = self.title.strip()
|
|
self.title_slug = slugify(self.title)
|
|
super(Post, self).save()
|
|
|
|
def createPost(self, content, tags):
|
|
b = self.blog
|
|
output = b.src_path
|
|
if not os.path.exists(output + '/_post'):
|
|
os.mkdir(output + '/_post')
|
|
|
|
filename = output + '/_post/' + str(self.pk)
|
|
content = unicode(content)
|
|
content = content.encode('utf-8')
|
|
modif = True
|
|
|
|
if os.path.exists(filename):
|
|
f = open(filename, 'rb')
|
|
src_md5 = hashlib.md5()
|
|
src_md5.update(f.read())
|
|
f.close()
|
|
|
|
dst_md5 = hashlib.md5()
|
|
dst_md5.update(content)
|
|
|
|
if src_md5.digest() == dst_md5.digest():
|
|
modif = False
|
|
else:
|
|
os.unlink(filename)
|
|
|
|
if modif:
|
|
f = open(filename, 'wb')
|
|
f.write(content)
|
|
f.close()
|
|
self.modification_date=datetime.now()
|
|
|
|
tags_list = Tag.objects.filter(blog_id=self.blog.id)
|
|
my_tags = []
|
|
# Create new tags
|
|
for tag in tags.split(','):
|
|
if tag == '': continue
|
|
tag_slug = slugify(tag)
|
|
found = False
|
|
for t in tags_list:
|
|
if t.name_slug == tag_slug:
|
|
found = True
|
|
break
|
|
if not found and not tag in my_tags:
|
|
t = Tag(blog=self.blog, name=tag.strip(), name_slug=tag_slug)
|
|
t.save()
|
|
# print 'Create ' + tag_slug
|
|
my_tags.append(tag)
|
|
|
|
# Add new tags
|
|
post_tags_list = Tag.objects.filter(post=self.id)
|
|
for tag in tags.split(','):
|
|
if tag == '': continue
|
|
tag_slug = slugify(tag)
|
|
found = False
|
|
for t in post_tags_list:
|
|
if t.name_slug == tag_slug:
|
|
found = True
|
|
break
|
|
if not found:
|
|
for t in tags_list:
|
|
if t.name_slug == tag_slug:
|
|
self.tags.add(t)
|
|
# print 'Add ' + tag_slug
|
|
break
|
|
|
|
# Remove old tags
|
|
if tags == '':
|
|
for t in post_tags_list:
|
|
self.tags.remove(t)
|
|
else:
|
|
for t in post_tags_list:
|
|
found = False
|
|
for tag in tags.split(','):
|
|
tag_slug = slugify(tag)
|
|
if t.name_slug == tag_slug:
|
|
found = True
|
|
break
|
|
if not found:
|
|
# print 'Remove ' + t.name_slug
|
|
self.tags.remove(t)
|
|
|
|
self.save()
|
|
|
|
def remove(self):
|
|
b = self.blog
|
|
|
|
output = b.src_path
|
|
filename = output + '/_post/' + str(self.pk)
|
|
if os.path.exists(filename):
|
|
os.unlink(filename)
|
|
|
|
output = b.output_path + self.getPath()
|
|
if os.path.exists(filename):
|
|
os.unlink(filename)
|
|
filename = filename + '.gz'
|
|
if os.path.exists(filename):
|
|
os.unlink(filename)
|
|
|
|
filename = b.src_path + '/post/'
|
|
filename = filename + self.creation_date.strftime("%Y") + '/' + self.creation_date.strftime("%m") + '/'
|
|
if os.path.exists(filename) and len(os.listdir(filename)) == 0:
|
|
os.rmdir(filename)
|
|
|
|
filename = b.output_path + '/post/'
|
|
filename = filename + self.creation_date.strftime("%Y") + '/'
|
|
if os.path.exists(filename) and len(os.listdir(filename)) == 0:
|
|
os.rmdir(filename)
|
|
|
|
class Comment(models.Model):
|
|
post = models.ForeignKey(Post)
|
|
parent = models.ForeignKey('self', null=True, blank=True)
|
|
date = models.DateTimeField()
|
|
author = models.CharField(max_length=255)
|
|
email = models.EmailField(max_length=255, blank=True)
|
|
the_comment = models.TextField(max_length=255)
|
|
ip = models.GenericIPAddressField()
|
|
|
|
@receiver(post_init, sender=Blog)
|
|
def init_blog_signal(sender, **kwargs):
|
|
kwargs['instance'].create_paths()
|
|
|
|
@receiver(post_delete, sender=Blog)
|
|
def delete_blog_signal(sender, **kwargs):
|
|
kwargs['instance'].remove()
|
|
|
|
@receiver(pre_delete, sender=Category)
|
|
def delete_category_signal(sender, **kwargs):
|
|
kwargs['instance'].remove()
|
|
|
|
@receiver(pre_delete, sender=Tag)
|
|
def delete_tag_signal(sender, **kwargs):
|
|
kwargs['instance'].remove()
|
|
|
|
@receiver(post_delete, sender=Post)
|
|
def delete_post_signal(sender, **kwargs):
|
|
kwargs['instance'].remove()
|
|
|
|
@receiver(pre_delete, sender=Post)
|
|
def pre_delete_post_signal(sender, **kwargs):
|
|
post = kwargs['instance']
|
|
comments = Comment.objects.filter(post=post.id).delete()
|