Dynastie/dynastie/models.py

454 lines
15 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
"""
2014-01-04 13:55:30 +01:00
Copyright 2012-2014 Grégory Soutadé
This file is part of Dynastie.
Dynastie is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Dynastie is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Dynastie. If not, see <http://www.gnu.org/licenses/>.
"""
2012-07-08 20:41:16 +02:00
import os
import shutil
import hashlib
2012-07-15 18:21:26 +02:00
import inspect
import gzip
2012-07-22 10:47:24 +02:00
from unicodedata import normalize
from re import sub
2012-07-20 21:54:43 +02:00
from datetime import datetime
2012-07-08 16:23:39 +02:00
from django.db import models
from django.contrib.auth.models import User
2013-02-09 09:48:30 +01:00
from django.db.models.signals import pre_init, post_init, pre_delete, post_delete
from django.db.models.signals import pre_save, post_save
2012-07-08 20:41:16 +02:00
from django.dispatch import receiver
from dynastie.generators import *
2012-07-08 16:23:39 +02:00
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
2012-07-08 16:23:39 +02:00
class Blog(models.Model):
2012-07-08 20:41:16 +02:00
name = models.CharField(max_length=255, unique=True)
2012-07-08 16:23:39 +02:00
title = models.CharField(max_length=255)
description = models.TextField(max_length=255, blank=True)
keywords = models.TextField(blank=True)
writers = models.ManyToManyField(User)
2012-07-08 20:41:16 +02:00
2012-07-15 18:21:26 +02:00
engines = list()
2012-07-08 20:41:16 +02:00
2012-07-15 18:21:26 +02:00
src_path = ''
output_path = ''
2012-07-20 21:54:43 +02:00
report = ''
2012-07-08 20:41:16 +02:00
2012-07-15 18:21:26 +02:00
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'
2012-07-15 18:21:26 +02:00
def create(self):
2012-07-22 10:47:24 +02:00
self.create_paths()
if not os.path.exists(os.environ['DYNASTIE_ROOT'] + 'sites'):
os.mkdir(os.environ['DYNASTIE_ROOT'] + 'sites')
2012-07-08 20:41:16 +02:00
2012-07-22 10:47:24 +02:00
if not os.path.exists(self.src_path):
os.mkdir(self.src_path)
2012-07-08 20:41:16 +02:00
2012-07-22 10:47:24 +02:00
if not os.path.exists(self.output_path):
os.mkdir(self.output_path)
2012-07-08 20:41:16 +02:00
def remove(self):
2012-07-15 18:21:26 +02:00
if os.path.exists(self.src_path):
shutil.rmtree(self.src_path)
if os.path.exists(self.output_path):
shutil.rmtree(self.output_path)
2012-07-08 20:41:16 +02:00
def load_generators(self):
2012-07-15 18:21:26 +02:00
if os.path.exists(self.src_path + '/_generators'):
f = open(self.src_path + '/_generators', 'r')
2012-07-08 20:41:16 +02:00
for line in f:
2012-07-22 20:49:11 +02:00
if line.startswith("#"):
continue
2012-08-28 09:09:14 +02:00
engine = line.strip()
if not engine in globals():
print 'Engine ' + engine + ' doesn\'t exists'
2014-09-24 20:27:27 +02:00
else:
self.engines.append(globals()[engine])
2012-07-08 20:41:16 +02:00
f.close()
else:
2012-08-28 09:09:14 +02:00
self.engines.append(globals()['post'])
2012-07-15 18:21:26 +02:00
self.engines.append(globals()['index'])
2012-07-22 20:49:11 +02:00
self.engines.append(globals()['category'])
self.engines.append(globals()['tag'])
2012-07-22 20:49:11 +02:00
self.engines.append(globals()['archive'])
self.engines.append(globals()['atom'])
self.engines.append(globals()['rss'])
2013-10-26 09:26:47 +02:00
self.engines.append(globals()['all_posts'])
2014-07-22 20:58:34 +02:00
def get_engines(self):
return self.engines
2012-07-08 20:41:16 +02:00
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)
2012-07-15 18:21:26 +02:00
if os.path.isdir(srcname):
if not os.path.exists(dstname):
os.makedirs(dstname)
2012-07-08 20:41:16 +02:00
self.copytree(srcname, dstname)
else:
copied = False
2012-07-15 18:21:26 +02:00
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():
2012-07-20 21:54:43 +02:00
self.report = self.report + 'Update ' + dstname + '<br/>\n'
2012-07-15 18:21:26 +02:00
shutil.copy2(srcname, dstname)
copied = True
2012-07-15 18:21:26 +02:00
else:
2012-07-20 21:54:43 +02:00
self.report = self.report + 'Add ' + dstname + '<br/>\n'
2012-07-15 18:21:26 +02:00
shutil.copy2(srcname, dstname)
copied = True
if copied:
exts = ('css', 'html', 'htm', 'xhtml', 'js')
found = False
for ext in exts:
2012-08-28 09:09:14 +02:00
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()
2012-07-08 20:41:16 +02:00
# 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
2012-07-15 18:21:26 +02:00
except Exception, err:
2012-07-08 20:41:16 +02:00
errors.extend(err.args[0])
if errors:
2012-07-15 18:21:26 +02:00
raise Exception(errors)
2012-07-08 20:41:16 +02:00
def generate(self):
2012-07-20 21:54:43 +02:00
self.report = '<br/><br/>Generation of ' + datetime.now().strftime("%d/%m/%Y at %H:%M:%S") + '<br/>\n'
2012-07-08 20:41:16 +02:00
self.load_generators()
2012-07-15 18:21:26 +02:00
self.copytree(self.src_path, self.output_path)
2012-07-22 20:49:11 +02:00
generated = []
hash_posts = {}
2013-01-06 18:28:03 +01:00
hash_posts_content = {}
2012-07-15 18:21:26 +02:00
for engine in self.engines:
2012-07-22 20:49:11 +02:00
if not inspect.ismodule(engine):
continue
2012-07-15 18:21:26 +02:00
for name, obj in inspect.getmembers(engine):
2012-10-20 19:05:29 +02:00
if inspect.isclass(obj) and obj.__module__.startswith("dynastie.generators"):
2012-07-22 20:49:11 +02:00
if obj.__module__ in generated: continue
2013-01-06 18:28:03 +01:00
e = obj(hash_posts, hash_posts_content)
2012-07-18 11:30:54 +02:00
r = e.generate(self, self.src_path, self.output_path)
2012-07-22 20:49:11 +02:00
generated.append(obj.__module__)
2012-07-18 11:30:54 +02:00
if not r is None:
2012-07-20 21:54:43 +02:00
self.report = self.report + '<br/>\n' + r
2012-07-22 10:47:24 +02:00
2012-07-20 21:54:43 +02:00
return self.report
2012-07-08 16:23:39 +02:00
class Editor(models.Model):
2012-07-08 20:41:16 +02:00
name = models.CharField(max_length=255, unique=True)
2012-07-08 16:23:39 +02:00
class Category(models.Model):
2012-07-08 20:41:16 +02:00
name = models.CharField(max_length=255, unique=True)
name_slug = models.CharField(max_length=255)
2012-07-08 16:23:39 +02:00
parent = models.ForeignKey('self', blank=True, null=True)
description = models.TextField(max_length=255, blank=True)
2012-10-09 20:47:12 +02:00
blog = models.ForeignKey(Blog)
2012-07-08 16:23:39 +02:00
def save(self):
self.name_slug = slugify(self.name)
super(Category, self).save()
def remove(self):
blog = Blog.objects.get(pk=self.blog.id)
2012-08-04 21:21:04 +02:00
output = blog.output_path + '/category/' + self.name_slug
if os.path.exists(output):
shutil.rmtree(output)
2012-07-22 20:49:11 +02:00
2012-07-08 16:23:39 +02:00
class Tag(models.Model):
2012-07-08 20:41:16 +02:00
name = models.CharField(max_length=255, unique=True)
name_slug = models.CharField(max_length=255)
2012-10-09 20:47:12 +02:00
blog = models.ForeignKey(Blog)
2012-07-08 16:23:39 +02:00
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)
2012-08-28 09:09:14 +02:00
class Post(models.Model):
2012-07-08 16:23:39 +02:00
title = models.CharField(max_length=255)
2012-07-22 10:47:24 +02:00
title_slug = models.CharField(max_length=255)
2012-07-08 16:23:39 +02:00
category = models.ForeignKey(Category, blank=True, null=True, on_delete=models.SET_NULL)
published = models.BooleanField()
2012-08-01 22:04:41 +02:00
creation_date = models.DateTimeField()
2012-09-08 12:35:52 +02:00
modification_date = models.DateTimeField()
2012-07-08 16:23:39 +02:00
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)
2014-01-04 13:43:38 +01:00
CONTENT_HTML = 0
CONTENT_TEXT = 1
CONTENT_FORMAT = (
(CONTENT_HTML, 'HTML'),
(CONTENT_TEXT, 'Text'))
content_format = models.IntegerField(choices=CONTENT_FORMAT, default=CONTENT_HTML, blank=False, null=False)
2012-07-08 16:23:39 +02:00
2012-08-01 22:04:41 +02:00
def getPath(self):
2012-08-28 09:09:14 +02:00
filename = '/post/'
2012-08-01 22:04:41 +02:00
filename = filename + self.creation_date.strftime("%Y") + '/' + self.creation_date.strftime("%m") + '/'
filename = filename + self.title_slug + '.html'
return filename
2012-07-22 10:47:24 +02:00
def save(self):
self.title = self.title.strip()
self.title_slug = slugify(self.title)
2012-08-28 09:09:14 +02:00
super(Post, self).save()
2012-07-22 10:47:24 +02:00
2014-05-27 18:24:14 +02:00
def manageTags(self, tags):
tags_list = Tag.objects.filter(blog_id=self.blog.id)
my_tags = []
# Create new tags
for tag in tags.split(','):
2012-12-13 14:32:03 +01:00
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(','):
2012-12-13 14:32:03 +01:00
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
2012-12-13 14:32:03 +01:00
if tags == '':
for t in post_tags_list:
self.tags.remove(t)
2012-12-13 14:32:03 +01:00
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)
2014-05-27 18:24:14 +02:00
def createPost(self, content, tags):
output = self.blog.src_path
2014-05-27 18:24:14 +02:00
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')
f = open(filename, 'wb')
f.write(content)
f.close()
self.modification_date=datetime.now()
2014-05-27 18:24:14 +02:00
self.manageTags(tags)
2012-07-22 10:47:24 +02:00
def remove(self):
b = self.blog
output = b.src_path
2012-08-28 09:09:14 +02:00
filename = output + '/_post/' + str(self.pk)
2012-07-22 10:47:24 +02:00
if os.path.exists(filename):
os.unlink(filename)
2012-08-01 22:04:41 +02:00
output = b.output_path + self.getPath()
2012-07-22 10:47:24 +02:00
if os.path.exists(filename):
os.unlink(filename)
filename = filename + '.gz'
if os.path.exists(filename):
os.unlink(filename)
2012-07-22 17:28:17 +02:00
filename = b.src_path + '/post/'
2012-07-22 17:28:17 +02:00
filename = filename + self.creation_date.strftime("%Y") + '/' + self.creation_date.strftime("%m") + '/'
if os.path.exists(filename) and len(os.listdir(filename)) == 0:
2012-07-22 17:28:17 +02:00
os.rmdir(filename)
2012-08-28 10:42:33 +02:00
filename = b.output_path + '/post/'
2012-07-22 17:28:17 +02:00
filename = filename + self.creation_date.strftime("%Y") + '/'
if os.path.exists(filename) and len(os.listdir(filename)) == 0:
2012-07-22 17:28:17 +02:00
os.rmdir(filename)
2012-07-22 10:47:24 +02:00
2014-01-04 13:43:38 +01:00
def get_editor(self):
if self.content_format == Post.CONTENT_HTML:
return 'html'
else:
return 'text'
2014-05-27 18:24:14 +02:00
class Draft(Post):
def createDraft(self, content, tags):
b = self.blog
output = b.src_path
if not os.path.exists(output + '/_draft'):
os.mkdir(output + '/_draft')
filename = output + '/_draft/' + 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()
self.manageTags(tags)
self.save()
def remove(self):
b = self.blog
output = b.src_path
filename = output + '/_draft/' + str(self.pk)
if os.path.exists(filename):
os.unlink(filename)
def save(self):
self.published = False
super(Draft, self).save()
2012-07-08 16:23:39 +02:00
class Comment(models.Model):
2012-08-28 09:09:14 +02:00
post = models.ForeignKey(Post)
parent = models.ForeignKey('self', null=True, blank=True)
date = models.DateTimeField()
2012-07-08 16:23:39 +02:00
author = models.CharField(max_length=255)
email = models.EmailField(max_length=255, blank=True)
2012-07-08 16:23:39 +02:00
the_comment = models.TextField(max_length=255)
2012-10-20 19:05:29 +02:00
ip = models.GenericIPAddressField()
2012-07-08 16:23:39 +02:00
2013-02-09 09:48:30 +01:00
def _update_line_returns(self):
self.the_comment = self.the_comment.replace('\n', '<br />')
def _remove_br(self):
self.the_comment = self.the_comment.replace('<br />', '\n')
class FileOutputCache(models.Model):
name = models.CharField(max_length=512)
hash = models.CharField(max_length=512)
2012-07-08 20:41:16 +02:00
@receiver(post_init, sender=Blog)
def init_blog_signal(sender, **kwargs):
2012-08-28 10:42:33 +02:00
kwargs['instance'].create_paths()
2012-07-08 16:23:39 +02:00
2012-07-08 20:41:16 +02:00
@receiver(post_delete, sender=Blog)
def delete_blog_signal(sender, **kwargs):
2012-08-28 10:42:33 +02:00
kwargs['instance'].remove()
2012-07-22 10:47:24 +02:00
@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()
2012-08-28 09:09:14 +02:00
@receiver(post_delete, sender=Post)
def delete_post_signal(sender, **kwargs):
2012-08-28 10:42:33 +02:00
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()
2013-02-09 09:48:30 +01:00
# Replace line returns by <br /> for generation
@receiver(pre_save, sender=Comment)
def pre_save_comment_signal(sender, **kwargs):
kwargs['instance']._update_line_returns()