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 + '
\n' shutil.copy2(srcname, dstname) copied = True else: self.report = self.report + 'Add ' + dstname + '
\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 = '

Generation of ' + datetime.now().strftime("%d/%m/%Y at %H:%M:%S") + '
\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 + '
\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()