Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
27bd360abe | ||
|
c5744585c7 | ||
|
2bd6f8ae21 | ||
|
0d5d5ce535 | ||
|
4ece9e9079 | ||
|
1b0c92e45a | ||
|
fa98b0b0e0 | ||
|
96e12bce83 | ||
|
b7fb9738a1 | ||
|
1805967833 | ||
|
10347ec588 | ||
|
7cb4f1d3d7 | ||
|
4b642fa48a | ||
|
f99bea97ef | ||
|
e029a1fc35 | ||
|
05c92a1738 | ||
|
7b93925d00 |
|
@ -1,3 +1,12 @@
|
|||
v0.5 (05/06/2016)
|
||||
** User **
|
||||
|
||||
** Dev **
|
||||
|
||||
** Bugs **
|
||||
Some robots doesn't set referer when they try to add comment
|
||||
|
||||
|
||||
v0.4 (22/05/2016)
|
||||
|
||||
** User **
|
||||
|
|
2
README
2
README
|
@ -1,6 +1,6 @@
|
|||
Dynastie is static blog generator delivered under GPL v3 licence terms.
|
||||
|
||||
Current version is 0.4
|
||||
Current version is 0.5
|
||||
|
||||
Requirements :
|
||||
Django >= 1.8, libapache2-mod-wsgi if you want to use Dynastie with Apache. PyGments (Optional).
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"""
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from dynastie.models import Language
|
||||
|
||||
class UserProfile(models.Model):
|
||||
# This field is required.
|
||||
|
@ -26,3 +27,6 @@ class UserProfile(models.Model):
|
|||
|
||||
# Other fields here
|
||||
editor = models.CharField(max_length=20, default="TinyMCE")
|
||||
|
||||
default_language = models.ForeignKey(Language, on_delete=models.CASCADE)
|
||||
|
||||
|
|
|
@ -33,16 +33,17 @@ class PostForm(ModelForm):
|
|||
|
||||
class Meta:
|
||||
model = Post
|
||||
exclude = ('title_slug', 'creation_date', 'modification_date', 'author', 'blog', 'tags', 'content_format')
|
||||
exclude = ('title_slug', 'creation_date', 'modification_date', 'author', 'blog', 'tags', 'content_format', 'post_type')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PostForm, self).__init__(*args, **kwargs)
|
||||
self.fields['category'].choices = [(cat.id, cat.name) for cat in Category.objects.all()]
|
||||
self.fields['language'].choices = [(lang.id, lang.name) for lang in Language.objects.all()]
|
||||
|
||||
class DraftForm(PostForm):
|
||||
class Meta:
|
||||
model = Draft
|
||||
exclude = ('title_slug', 'creation_date', 'modification_date', 'author', 'blog', 'tags', 'content_format', 'published')
|
||||
exclude = ('title_slug', 'creation_date', 'modification_date', 'author', 'blog', 'tags', 'content_format', 'published', 'post_type')
|
||||
|
||||
class CategoryForm(ModelForm):
|
||||
class Meta:
|
||||
|
|
|
@ -77,7 +77,7 @@ class Atom(RSS):
|
|||
return
|
||||
|
||||
f = open(filename, 'rb')
|
||||
post_content = '<![CDATA[' + f.read() + ']]>'
|
||||
post_content = '<![CDATA[' + f.read().decode('utf-8') + ']]>'
|
||||
f.close()
|
||||
|
||||
if post.content_format == Post.CONTENT_TEXT:
|
||||
|
|
|
@ -22,6 +22,7 @@ import hashlib
|
|||
import gzip
|
||||
import math
|
||||
import codecs
|
||||
import re
|
||||
from xml.dom import *
|
||||
from xml.dom.minidom import parse
|
||||
from xml.parsers.expat import *
|
||||
|
@ -32,7 +33,7 @@ class StrictUTF8Writer(codecs.StreamWriter):
|
|||
value = ''
|
||||
|
||||
def __init__(self):
|
||||
self.value = u''
|
||||
self.value = ''
|
||||
|
||||
def write(self, object):
|
||||
object = object.replace('<', '<')
|
||||
|
@ -41,17 +42,17 @@ class StrictUTF8Writer(codecs.StreamWriter):
|
|||
object = object.replace(''', "'")
|
||||
object = object.replace('&', '&')
|
||||
|
||||
if not type(object) == unicode:
|
||||
self.value = self.value + unicode(object, 'utf-8')
|
||||
if type(object) == bytes:
|
||||
self.value = self.value + object.decode('utf-8')
|
||||
else:
|
||||
self.value = self.value + object
|
||||
return self.value
|
||||
|
||||
def reset(self):
|
||||
self.value = u''
|
||||
self.value = ''
|
||||
|
||||
def getvalue(self):
|
||||
return self.value
|
||||
return self.value.encode('utf-8')
|
||||
|
||||
class DynastieGenerator:
|
||||
|
||||
|
@ -64,7 +65,8 @@ class DynastieGenerator:
|
|||
self.hash_posts = hash_posts
|
||||
self.hash_posts_content = hash_posts_content
|
||||
self.user = request and request.user or None
|
||||
|
||||
self.iframe_re = re.compile(r'(<iframe.*)/>')
|
||||
|
||||
def addReport(self, string, color=''):
|
||||
if string in self.report: return
|
||||
if color:
|
||||
|
@ -94,8 +96,10 @@ class DynastieGenerator:
|
|||
|
||||
writer = StrictUTF8Writer()
|
||||
node.writexml(writer)
|
||||
content = writer.getvalue().encode('utf-8')
|
||||
|
||||
content = writer.getvalue().decode('utf-8')
|
||||
# iframe tag must be like <iframe ... ></iframe>, not optimized <iframe ... />
|
||||
content = self.iframe_re.sub('\\1></iframe>', content).encode('utf-8')
|
||||
|
||||
dst_md5 = hashlib.md5()
|
||||
dst_md5.update(content)
|
||||
|
||||
|
@ -105,6 +109,10 @@ class DynastieGenerator:
|
|||
else:
|
||||
cache_obj = cache_objs[0]
|
||||
|
||||
if cache_obj and not os.path.exists(filename):
|
||||
cache_obj.delete()
|
||||
cache_obj = None
|
||||
|
||||
if cache_obj or os.path.exists(filename):
|
||||
if not cache_obj:
|
||||
src_md5 = hashlib.md5()
|
||||
|
@ -160,8 +168,8 @@ class DynastieGenerator:
|
|||
for node in subtree.childNodes:
|
||||
div.appendChild(node.cloneNode(True))
|
||||
|
||||
def createElement(self, dom, name='', content='', subtree=None):
|
||||
div = dom.createElement('div')
|
||||
def createElement(self, dom, name='', content='', subtree=None, _type='div'):
|
||||
div = dom.createElement(_type)
|
||||
if name:
|
||||
div.setAttribute('class', name)
|
||||
if content:
|
||||
|
@ -186,7 +194,7 @@ class DynastieGenerator:
|
|||
if node.prefix == 'dyn':
|
||||
if node.localName in values:
|
||||
content = values[node.localName]
|
||||
if isinstance(content, basestring):
|
||||
if isinstance(content, str):
|
||||
new_elem = self.createElement(dom, node.localName, content)
|
||||
else:
|
||||
new_elem = self.createElement(dom, node.localName)
|
||||
|
@ -245,7 +253,7 @@ class DynastieGenerator:
|
|||
dom2 = root
|
||||
try:
|
||||
dom2 = parse(src + '/' + filename)
|
||||
except ExpatError, e:
|
||||
except ExpatError as e:
|
||||
self.addError('Error parsing ' + src + '/' + filename)
|
||||
return root
|
||||
|
||||
|
@ -257,12 +265,11 @@ class DynastieGenerator:
|
|||
return root
|
||||
blockname = block.getAttribute('name')
|
||||
for target_block in target_blocks:
|
||||
|
||||
if blockname != target_block.getAttribute('name'):
|
||||
continue
|
||||
|
||||
for child in target_block.childNodes:
|
||||
block.parentNode.appendChild(child.cloneNode(True))
|
||||
block.parentNode.insertBefore(child.cloneNode(True), block)
|
||||
block_found = True
|
||||
break
|
||||
block.parentNode.removeChild(block)
|
||||
|
|
|
@ -157,10 +157,10 @@ class Index(DynastieGenerator):
|
|||
create_link = (title_elem.getAttribute('link') == '1')
|
||||
post = self.cur_post_obj
|
||||
if create_link == True:
|
||||
node = self.createElement(dom, 'title')
|
||||
node = self.createElement(dom, 'title', _type='h2')
|
||||
node.appendChild(self.createLinkElem(dom, post.getPath(), post.title))
|
||||
else:
|
||||
node = self.createElement(dom, 'title', post.title)
|
||||
node = self.createElement(dom, 'title', post.title, _type='h2')
|
||||
|
||||
root.replaceChild(node, title_elem)
|
||||
|
||||
|
@ -240,10 +240,11 @@ class Index(DynastieGenerator):
|
|||
if not user: user = post.author
|
||||
# Markdown replace
|
||||
if not post or (post and post.content_format == Post.CONTENT_TEXT):
|
||||
internal_posts = re.search('\[\[([0-9]+)\]\]', text)
|
||||
internal_posts = re.finditer('\[\[([0-9]+)\]\]', text)
|
||||
if internal_posts:
|
||||
for post_id in internal_posts.groups():
|
||||
post_id = int(post_id)
|
||||
for post in internal_posts:
|
||||
post_id = post.groups()
|
||||
post_id = int(post_id[0])
|
||||
if post_id in self.parent_posts: continue
|
||||
_,post = self._have_I_right(user, post_id)
|
||||
if not post: continue
|
||||
|
@ -291,7 +292,7 @@ class Index(DynastieGenerator):
|
|||
filename = filename2
|
||||
|
||||
if not filename in self.hash_posts_content:
|
||||
f = codecs.open(filename, 'rb', 'utf-8')
|
||||
f = codecs.open(filename, 'r', 'utf-8')
|
||||
post_content = f.read()
|
||||
f.close()
|
||||
self.parent_posts.append(post.id)
|
||||
|
@ -308,7 +309,7 @@ class Index(DynastieGenerator):
|
|||
from dynastie.models import Post
|
||||
post = self.cur_post_obj
|
||||
|
||||
if post.id in self.hash_posts and not self.first_try:
|
||||
if post.id > 0 and post.id in self.hash_posts.keys() and not self.first_try:
|
||||
node,_ = self.hash_posts[post.id]
|
||||
return node.cloneNode(0)
|
||||
|
||||
|
@ -318,6 +319,9 @@ class Index(DynastieGenerator):
|
|||
except:
|
||||
pass
|
||||
|
||||
if post.language != None:
|
||||
post_elem.setAttribute('lang', post.language.abbrev)
|
||||
|
||||
self.parent_posts = []
|
||||
post_content = self._loadPostContent(post)
|
||||
if not post_content: return None
|
||||
|
@ -334,27 +338,28 @@ class Index(DynastieGenerator):
|
|||
continue
|
||||
new_node = dom.createTextNode(post_content)
|
||||
content_node.appendChild(new_node)
|
||||
|
||||
writer = StrictUTF8Writer()
|
||||
post_elem.writexml(writer)
|
||||
content = writer.getvalue().encode('utf-8')
|
||||
|
||||
md5 = hashlib.md5()
|
||||
md5.update(content)
|
||||
|
||||
if post.id in self.hash_posts:
|
||||
# Here, we are in first_try, check that computed
|
||||
# post has the same result than the one in cache
|
||||
self.first_try = False
|
||||
|
||||
_,md5_2 = self.hash_posts[post.id]
|
||||
# Disable this cache
|
||||
# writer = StrictUTF8Writer()
|
||||
# post_elem.writexml(writer)
|
||||
# content = writer.getvalue().encode('utf-8')
|
||||
|
||||
# If not, clear cache
|
||||
if md5.digest() != md5_2:
|
||||
self.hash_posts = {}
|
||||
self.hash_posts[post.id] = (post_elem.cloneNode(0), md5.digest())
|
||||
else:
|
||||
self.hash_posts[post.id] = (post_elem.cloneNode(0), md5.digest())
|
||||
# md5 = hashlib.md5()
|
||||
# md5.update(content)
|
||||
|
||||
# if post.id in self.hash_posts:
|
||||
# # Here, we are in first_try, check that computed
|
||||
# # post has the same result than the one in cache
|
||||
# self.first_try = False
|
||||
|
||||
# _,md5_2 = self.hash_posts[post.id]
|
||||
|
||||
# # If not, clear cache
|
||||
# if md5.digest() != md5_2:
|
||||
# self.hash_posts = {}
|
||||
# self.hash_posts[post.id] = (post_elem.cloneNode(0), md5.digest())
|
||||
# else:
|
||||
# self.hash_posts[post.id] = (post_elem.cloneNode(0), md5.digest())
|
||||
|
||||
return post_elem
|
||||
|
||||
|
@ -363,10 +368,9 @@ class Index(DynastieGenerator):
|
|||
create_link = (node.getAttribute('link') == '1')
|
||||
for i in range(0, self.posts_per_page):
|
||||
post_elem = self.createElement(dom, 'post')
|
||||
if len(posts) > self.cur_post:
|
||||
if self.cur_post < len(posts):
|
||||
self.cur_post_obj = posts[self.cur_post]
|
||||
post_elem = self.createPost(posts, dom, post_elem, node)
|
||||
if post_elem is None: continue
|
||||
else:
|
||||
post_elem = self.createElement(dom, '', '<b>No posts yet</b>')
|
||||
self.cur_post_obj = None
|
||||
|
@ -490,19 +494,20 @@ class Index(DynastieGenerator):
|
|||
|
||||
writer = StrictUTF8Writer()
|
||||
node.firstChild.writexml(writer)
|
||||
code = writer.getvalue().encode('utf-8')
|
||||
code = writer.getvalue().decode('utf-8')
|
||||
|
||||
r,w = os.pipe()
|
||||
r,w=os.fdopen(r,'r',0), os.fdopen(w,'w',0)
|
||||
r,w=os.fdopen(r,'rb'), os.fdopen(w,'wb')
|
||||
highlight(code, lexer, formatter, w)
|
||||
w.close()
|
||||
|
||||
code = r.read()
|
||||
code = code.decode('utf-8')
|
||||
r.close()
|
||||
|
||||
# Remove <pre> after <div class="highlight">
|
||||
code = code[28:-13]
|
||||
code = u'<div class="highlight">' + unicode(code, 'utf-8') + u'</div>'
|
||||
code = '<div class="highlight">' + code + u'</div>'
|
||||
|
||||
return code
|
||||
|
||||
|
@ -554,8 +559,6 @@ class Index(DynastieGenerator):
|
|||
self.cur_page = self.cur_page + 1
|
||||
filename = self.dirname + '/' + self.filename + str(self.cur_page) + '.html'
|
||||
|
||||
filename = output + filename
|
||||
|
||||
while os.path.exists(filename):
|
||||
self.addReport('Removing unused ' + filename)
|
||||
os.unlink(filename)
|
||||
|
@ -565,7 +568,6 @@ class Index(DynastieGenerator):
|
|||
os.unlink(filename)
|
||||
self.cur_page = self.cur_page + 1
|
||||
filename = self.dirname + '/' + self.filename + str(self.cur_page) + '.html'
|
||||
filename = output + filename
|
||||
|
||||
def generate(self, blog, src, output):
|
||||
from dynastie.models import Post, Blog
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -20,6 +20,7 @@
|
|||
import datetime
|
||||
import os
|
||||
import xml
|
||||
import re
|
||||
from xml.dom.minidom import parse, parseString
|
||||
from dynastie.generators.generator import DynastieGenerator, StrictUTF8Writer
|
||||
from dynastie.generators.index import Index
|
||||
|
@ -133,7 +134,13 @@ class Post(Index):
|
|||
new_elem = self.createMeta(dom, name, post.author.first_name + ' ' + post.author.last_name)
|
||||
except:
|
||||
return None
|
||||
|
||||
elif name == 'lang':
|
||||
meta_elem.removeChild(root)
|
||||
# Add attribute lang in <html> tag
|
||||
if post.language != None:
|
||||
meta_elem.parentNode.setAttribute('lang', post.language.abbrev)
|
||||
return None
|
||||
|
||||
if not new_elem is None:
|
||||
root.parentNode.replaceChild(new_elem, root)
|
||||
return new_elem
|
||||
|
@ -146,15 +153,14 @@ class Post(Index):
|
|||
self.replaceByText(dom, root, node, value)
|
||||
return None
|
||||
|
||||
def _createPost(self, post, dom, post_elem, root):
|
||||
def _createPost(self, post, dom, root, node):
|
||||
self.cur_post_obj = post
|
||||
posts = [post]
|
||||
self.createPost(posts, dom, post_elem, root)
|
||||
post_elem = self.createElement(dom, 'post')
|
||||
post_elem = self.createPost(posts, dom, post_elem, node)
|
||||
|
||||
# Post are appended by index. Remove template
|
||||
post_nodes = dom.getElementsByTagNameNS(self.URI, 'post')
|
||||
post_elem = post_nodes[0]
|
||||
post_elem.parentNode.removeChild(post_elem)
|
||||
root.replaceChild(post_elem, node)
|
||||
|
||||
title_nodes = dom.getElementsByTagName('title')
|
||||
|
||||
|
@ -164,7 +170,7 @@ class Post(Index):
|
|||
node.removeChild(node.childNodes[0])
|
||||
node.appendChild(dom.createTextNode(post.title))
|
||||
|
||||
return node
|
||||
return post_elem
|
||||
|
||||
def _generate(self, blog, src, output, posts):
|
||||
from dynastie.search import Search
|
||||
|
@ -244,8 +250,8 @@ class Post(Index):
|
|||
if not the_class in post_transform:
|
||||
continue
|
||||
if the_class == 'post_content':
|
||||
s = u'<div>' + post_content + u'</div>'
|
||||
new_node = parseString(s.encode('utf-8'))
|
||||
s = '<div>' + post_content + u'</div>'
|
||||
new_node = parseString(s)
|
||||
for n in new_node.childNodes[0].childNodes:
|
||||
content_node.appendChild(n)
|
||||
break
|
||||
|
@ -258,6 +264,7 @@ class Post(Index):
|
|||
|
||||
def preview(self, request, src, values):
|
||||
from dynastie.models import Blog
|
||||
iframe_re = re.compile(r'(<iframe.*)/>')
|
||||
|
||||
self.user = request.user
|
||||
|
||||
|
@ -287,4 +294,8 @@ class Post(Index):
|
|||
writer = StrictUTF8Writer()
|
||||
nodes[0].writexml(writer)
|
||||
|
||||
return writer.getvalue().encode('utf-8')
|
||||
content = writer.getvalue().decode('utf-8')
|
||||
# iframe tag must be like <iframe ... ></iframe>, not optimized <iframe ... />
|
||||
content = self.iframe_re.sub('\\1></iframe>', content)
|
||||
|
||||
return content
|
||||
|
|
|
@ -31,7 +31,7 @@ class RSS(DynastieGenerator):
|
|||
def appendElement(self, dom, root, name='', content='', attributes=None):
|
||||
elem = dom.createElement(name)
|
||||
if attributes:
|
||||
for k, v in attributes.iteritems():
|
||||
for k, v in attributes.items():
|
||||
elem.setAttribute(k, v)
|
||||
if content != '':
|
||||
elem.appendChild(dom.createTextNode(content))
|
||||
|
@ -77,7 +77,7 @@ class RSS(DynastieGenerator):
|
|||
return
|
||||
|
||||
f = open(filename, 'rb')
|
||||
post_content = f.read()
|
||||
post_content = f.read().decode('utf-8')
|
||||
f.close()
|
||||
|
||||
if post.content_format == Post.CONTENT_TEXT:
|
||||
|
|
|
@ -71,5 +71,5 @@ class Search(Index):
|
|||
|
||||
writer = StrictUTF8Writer()
|
||||
nodes[0].writexml(writer)
|
||||
return writer.getvalue().encode('utf-8')
|
||||
return writer.getvalue().decode('utf-8')
|
||||
|
||||
|
|
|
@ -34,13 +34,17 @@ from dynastie.generators import *
|
|||
|
||||
def slugify(name):
|
||||
name = name.strip()
|
||||
name = normalize('NFKD', name).encode('ascii', 'ignore').replace(' ', '-').lower()
|
||||
name = normalize('NFKD', name).replace(' ', '-').lower()
|
||||
#remove `other` characters
|
||||
name = sub('[^a-zA-Z0-9_-]', '', name)
|
||||
#nomalize dashes
|
||||
name = sub('-+', '-', name)
|
||||
return name
|
||||
|
||||
class Language(models.Model):
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
abbrev = models.CharField(max_length=4, unique=True)
|
||||
|
||||
class Blog(models.Model):
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
title = models.CharField(max_length=255)
|
||||
|
@ -83,7 +87,7 @@ class Blog(models.Model):
|
|||
continue
|
||||
engine = line.strip()
|
||||
if not engine in globals():
|
||||
print 'Engine ' + engine + ' doesn\'t exists'
|
||||
print('Engine ' + engine + ' doesn\'t exists')
|
||||
else:
|
||||
self.engines.append(globals()[engine])
|
||||
f.close()
|
||||
|
@ -116,7 +120,7 @@ class Blog(models.Model):
|
|||
if os.path.isdir(srcname):
|
||||
if not os.path.exists(dstname):
|
||||
os.makedirs(dstname)
|
||||
self.copytree(srcname, dstname, ignore=True)
|
||||
shutil.copytree(srcname, dstname, ignore=True)
|
||||
else:
|
||||
return self.copytree(srcname, dstname)
|
||||
else:
|
||||
|
@ -162,11 +166,11 @@ class Blog(models.Model):
|
|||
|
||||
|
||||
# XXX What about devices, sockets etc.?
|
||||
except (IOError, os.error), why:
|
||||
except (IOError, os.error) as 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:
|
||||
except Exception as err:
|
||||
errors.extend(err.args[0])
|
||||
if errors:
|
||||
raise Exception(errors)
|
||||
|
@ -186,7 +190,12 @@ class Blog(models.Model):
|
|||
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)
|
||||
print('Go for {}'.format(e))
|
||||
try:
|
||||
r = e.generate(self, self.src_path, self.output_path)
|
||||
except Exception as err:
|
||||
self.report += err
|
||||
continue
|
||||
generated.append(obj.__module__)
|
||||
if not r is None:
|
||||
self.report = self.report + '<br/>\n' + r
|
||||
|
@ -225,9 +234,9 @@ class Editor(models.Model):
|
|||
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)
|
||||
parent = models.ForeignKey('self', blank=True, null=True, on_delete=models.CASCADE)
|
||||
description = models.TextField(max_length=255, blank=True)
|
||||
blog = models.ForeignKey(Blog)
|
||||
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
|
||||
|
||||
def save(self):
|
||||
self.name_slug = slugify(self.name)
|
||||
|
@ -243,7 +252,7 @@ class Category(models.Model):
|
|||
class Tag(models.Model):
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
name_slug = models.CharField(max_length=255)
|
||||
blog = models.ForeignKey(Blog)
|
||||
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
|
||||
|
||||
def save(self):
|
||||
self.name_slug = slugify(self.name)
|
||||
|
@ -255,7 +264,15 @@ class Tag(models.Model):
|
|||
if os.path.exists(output):
|
||||
shutil.rmtree(output)
|
||||
|
||||
class PostOnlyManager(models.Manager):
|
||||
|
||||
def get_queryset(self):
|
||||
return super(PostOnlyManager, self).get_queryset().filter(post_type='P')
|
||||
|
||||
class Post(models.Model):
|
||||
|
||||
objects = PostOnlyManager()
|
||||
|
||||
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)
|
||||
|
@ -267,13 +284,15 @@ class Post(models.Model):
|
|||
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)
|
||||
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
|
||||
language = models.ForeignKey(Language, null=True, on_delete=models.CASCADE)
|
||||
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)
|
||||
content_format = models.IntegerField(choices=CONTENT_FORMAT, default=CONTENT_TEXT, blank=False, null=False)
|
||||
post_type = models.CharField(max_length=1, default='P')
|
||||
|
||||
def getPath(self):
|
||||
filename = '/post/'
|
||||
|
@ -344,7 +363,6 @@ class Post(models.Model):
|
|||
os.mkdir(output + '/_post')
|
||||
|
||||
filename = output + '/_post/' + str(self.pk)
|
||||
content = unicode(content)
|
||||
content = content.encode('utf-8')
|
||||
|
||||
f = open(filename, 'wb')
|
||||
|
@ -386,6 +404,9 @@ class Post(models.Model):
|
|||
return 'text'
|
||||
|
||||
class Draft(Post):
|
||||
|
||||
objects = models.Manager()
|
||||
|
||||
def createDraft(self, content, tags):
|
||||
b = self.blog
|
||||
output = b.src_path
|
||||
|
@ -393,7 +414,6 @@ class Draft(Post):
|
|||
os.mkdir(output + '/_draft')
|
||||
|
||||
filename = output + '/_draft/' + str(self.pk)
|
||||
content = unicode(content)
|
||||
content = content.encode('utf-8')
|
||||
modif = True
|
||||
|
||||
|
@ -432,8 +452,8 @@ class Draft(Post):
|
|||
super(Draft, self).save()
|
||||
|
||||
class Comment(models.Model):
|
||||
post = models.ForeignKey(Post)
|
||||
parent = models.ForeignKey('self', null=True, blank=True)
|
||||
post = models.ForeignKey(Post, on_delete=models.CASCADE)
|
||||
parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE)
|
||||
date = models.DateTimeField()
|
||||
author = models.CharField(max_length=255)
|
||||
email = models.EmailField(max_length=255, blank=True)
|
||||
|
@ -445,7 +465,7 @@ class Comment(models.Model):
|
|||
|
||||
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)
|
||||
|
@ -483,3 +503,7 @@ def pre_delete_post_signal(sender, **kwargs):
|
|||
@receiver(pre_save, sender=Comment)
|
||||
def pre_save_comment_signal(sender, **kwargs):
|
||||
kwargs['instance']._update_line_returns()
|
||||
|
||||
@receiver(pre_save, sender=Draft)
|
||||
def pre_save_draft_signal(sender, **kwargs):
|
||||
kwargs['instance'].post_type = 'D'
|
||||
|
|
|
@ -61,7 +61,7 @@ class Search:
|
|||
def _saveDatabase(self, blog, hashtable):
|
||||
d = pickle.dumps(hashtable)
|
||||
|
||||
f = open(blog.src_path + '/_search.db', 'w')
|
||||
f = open(blog.src_path + '/_search.db', 'wb')
|
||||
f.write(d)
|
||||
f.close()
|
||||
|
||||
|
@ -69,7 +69,7 @@ class Search:
|
|||
filename = blog.src_path + '/_search.db'
|
||||
|
||||
if not os.path.exists(filename):
|
||||
print 'No search index !'
|
||||
print('No search index !')
|
||||
return None
|
||||
|
||||
f = open(filename, 'rb')
|
||||
|
@ -100,7 +100,7 @@ class Search:
|
|||
|
||||
def _prepare_string(self, content):
|
||||
content = self._remove_tag(content)
|
||||
content = self._strip_accents(unicode(content, 'utf8'))
|
||||
content = self._strip_accents(content)
|
||||
|
||||
return content
|
||||
|
||||
|
@ -128,12 +128,12 @@ class Search:
|
|||
except:
|
||||
return
|
||||
|
||||
f = open(filename, 'r')
|
||||
content = f.read()
|
||||
f = open(filename, 'rb')
|
||||
content = f.read().decode('utf-8')
|
||||
f.close()
|
||||
|
||||
self._indexContent(hashtable, index, content, 1)
|
||||
self._indexContent(hashtable, index, post.title.encode('utf-8'), 5)
|
||||
self._indexContent(hashtable, index, post.title, 5)
|
||||
|
||||
def create_index(self, blog):
|
||||
hashtable = {}
|
||||
|
@ -192,7 +192,7 @@ class Search:
|
|||
def search(self, blog, string):
|
||||
hashtable = self._loadDatabase(blog)
|
||||
|
||||
string = self._prepare_string(string.encode('utf-8'))
|
||||
string = self._prepare_string(string)
|
||||
|
||||
wordlist = string.split(' ')
|
||||
|
||||
|
@ -207,8 +207,7 @@ class Search:
|
|||
for post in hashtable[key]:
|
||||
res[post[0]] = res.get(post[0],0) + post[1]
|
||||
|
||||
sorted_res = sorted(res.iteritems(), key=operator.itemgetter(1))
|
||||
sorted_res.reverse()
|
||||
sorted_res = sorted(res.items(), key=operator.itemgetter(1), reverse=True)
|
||||
|
||||
res = [sorted_res[i][0] for i in range(len(sorted_res))]
|
||||
|
||||
|
|
|
@ -1,104 +1,115 @@
|
|||
<html xmlns:dyn="http://indefero.soutade.fr/p/dynastie" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<html xmlns:dyn="http://indefero.soutade.fr/p/dynastie" xmlns="http://www.w3.org/1999/xhtml" lang="fr">
|
||||
<head>
|
||||
<meta content="text/html; charset=UTF-8" http-equiv="content-type"/>
|
||||
<meta content="index, follow" name="robots"/>
|
||||
<meta content="blog, gregory, soutade, grégory, soutadé, Grégory, Soutadé" name="keywords"/>
|
||||
<meta content="Blog de Grégory Soutadé" name="description"/>
|
||||
<meta content="Dynastie" name="generator"/>
|
||||
<meta name="viewport" content="width=width, initial-scale=1"/>
|
||||
<title>Blog de Grégory Soutadé</title>
|
||||
<link rel="icon" type="image/png" href="/images/favicon.png" />
|
||||
<link href="/rss.xml" rel="alternate" type="application/rss+xml" title="RSS 2.0" />
|
||||
<link href="/atom.xml" rel="alternate" type="application/atom+xml" title="Atom 1.0" />
|
||||
<link href="/css/blog.css" rel="stylesheet" type="text/css"/>
|
||||
<script type="text/javascript" src="/js/blog.js"> </script>
|
||||
<script src="/js/blog.js"> </script>
|
||||
<dyn:block name="head"/>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/"><img id="logo" src="/images/tux_final.png"/></a>
|
||||
<div class="body">
|
||||
<div class="table-row">
|
||||
<header>
|
||||
<div class="table-cell" id="header">
|
||||
<h2 id="title"><a href="/">Blog de Grégory Soutadé</a></h2>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="content">
|
||||
<dyn:block name="content"/>
|
||||
<div class="content" id="content">
|
||||
<header>
|
||||
<a href="/"><img id="logo" alt="Logo Tux" src="/images/tux_final2.png"/></a>
|
||||
<div id="header">
|
||||
<span id="title"><a href="/">Blog de Grégory Soutadé</a></span>
|
||||
</div>
|
||||
<div class="menu">
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Recherche</div>
|
||||
<div id="menu_main">
|
||||
<dyn:replace div_name="form" id="search_form" method="POST" action="/search/dyn:blog_id">
|
||||
<input type="text" name="text" id="search_text" onkeypress="handleKeyPress(event,this.form)"/>
|
||||
</dyn:replace>
|
||||
</div>
|
||||
</div>
|
||||
<nav>
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Menu principal</div>
|
||||
<div id="menu_main">
|
||||
<div class="menu_content_content"><a href="/">Première page</a></div>
|
||||
<div class="menu_content_content"><a href="/about.html">À propos</a></div>
|
||||
<div class="menu_content_content"><a href="/all_posts.html">Tous les articles</a></div>
|
||||
<div class="menu_content_content"><a href="http://indefero.soutade.fr">Projets personnels</a></div>
|
||||
<div class="menu_content_content"><a href="/ljdc">Les joies du code</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Catégories</div>
|
||||
<div class="menu_content_content">
|
||||
<ul>
|
||||
<li><a href="/category/articles">Articles</a></li>
|
||||
<li><a href="/category/musique">Musique</a></li>
|
||||
<li><a href="/category/informatique">Informatique</a></li>
|
||||
<li><a href="/category/cinema">Cinéma</a></li>
|
||||
<li><a href="/category/sport">Sport</a></li>
|
||||
<li><a href="/category/configurations-pc">Configurations PC</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Tags</div>
|
||||
<div class="menu_content_content">
|
||||
<ul>
|
||||
<li><a href="/tag/inenglish">In English</a></li>
|
||||
<li><a href="/tag/python">Python</a></li>
|
||||
<li><a href="/tag/sheevaplug">SheevaPlug</a></li>
|
||||
<li><a href="/tag/course-a-pied">Course à pied</a></li>
|
||||
<li><a href="/tag/tag-rugby">Tag rugby</a></li>
|
||||
<li><a href="/tag/politique">Politique</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Archives</div>
|
||||
<div class="menu_content_content">
|
||||
<ul>
|
||||
<li><a href="/archive/2015">2015</a></li>
|
||||
<li><a href="/archive/2014">2014</a></li>
|
||||
<li><a href="/archive/2013">2013</a></li>
|
||||
<li><a href="/archive/2012">2012</a></li>
|
||||
<li><a href="/archive/2011">2011</a></li>
|
||||
<li><a href="/archive/2010">2010</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feed">
|
||||
<a href="/rss.xml"><img src="/images/rss.png" alt="RSS feeds"></img> RSS</a><a href="/atom.xml"><img src="/images/atom.png" alt="Atom feeds"></img> Atom</a>
|
||||
</div>
|
||||
</nav>
|
||||
<p style="text-align:center">Généré avec <a href="http://indefero.soutade.fr/p/dynastie">Dynastie</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<dyn:block name="content"/>
|
||||
<footer>
|
||||
<div class="footer">
|
||||
Copyright © 2010-2016 Grégory Soutadé.<br/>
|
||||
Copyright © 2010-2022 Grégory Soutadé.<br/>
|
||||
Tous droits réservés.
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="hamburger" id="hamburger">
|
||||
<a href="#" onclick="switchMenu();return false;">
|
||||
<img id="hamburger_logo" alt="Menu" src="/images/hamburger.png"/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="menu" id="menu" lang="fr">
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Recherche</div>
|
||||
<div class="menu_main">
|
||||
<dyn:replace div_name="form" id="search_form" method="POST" action="/search/dyn:blog_id">
|
||||
<input type="text" name="text" id="search_text" onkeypress="handleKeyPress(event,this.form)"/>
|
||||
</dyn:replace>
|
||||
</div>
|
||||
</div>
|
||||
<nav>
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Menu principal</div>
|
||||
<div class="menu_main">
|
||||
<div class="menu_content_content"><a href="/">Première page</a></div>
|
||||
<div class="menu_content_content"><a href="/about.html">À propos</a></div>
|
||||
<div class="menu_content_content"><a href="/all_posts.html">Tous les articles</a></div>
|
||||
<div class="menu_content_content"><a href="http://indefero.soutade.fr">Projets personnels</a></div>
|
||||
<div class="menu_content_content"><a href="/ljdc">Les joies du code</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Catégories</div>
|
||||
<div class="menu_content_content">
|
||||
<ul>
|
||||
<li><a href="/category/articles">Articles</a></li>
|
||||
<li><a href="/category/musique">Musique</a></li>
|
||||
<li><a href="/category/informatique">Informatique</a></li>
|
||||
<li><a href="/category/sport">Sport</a></li>
|
||||
<li><a href="/category/configurations-pc">Configurations PC</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Tags</div>
|
||||
<div class="menu_content_content">
|
||||
<ul>
|
||||
<li><a href="/tag/inenglish">In English</a></li>
|
||||
<li><a href="/tag/python">Python</a></li>
|
||||
<li><a href="/tag/sheevaplug">SheevaPlug</a></li>
|
||||
<li><a href="/tag/course-a-pied">Course à pied</a></li>
|
||||
<li><a href="/tag/tag-rugby">Tag rugby</a></li>
|
||||
<li><a href="/tag/politique">Politique</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Archives</div>
|
||||
<div class="menu_content_content">
|
||||
<ul>
|
||||
<li><a href="/archive/2021">2021</a></li>
|
||||
<li><a href="/archive/2020">2020</a></li>
|
||||
<li><a href="/archive/2019">2019</a></li>
|
||||
<li><a href="/archive/2018">2018</a></li>
|
||||
<li><a href="/archive/2017">2017</a></li>
|
||||
<li><a href="/archive/2016">2016</a></li>
|
||||
<li><a href="/archive/2015">2015</a></li>
|
||||
<li><a href="/archive/2014">2014</a></li>
|
||||
<li><a href="/archive/2013">2013</a></li>
|
||||
<li><a href="/archive/2012">2012</a></li>
|
||||
<li><a href="/archive/2011">2011</a></li>
|
||||
<li><a href="/archive/2010">2010</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feed">
|
||||
<a href="/rss.xml"><img src="/images/rss.png" alt="RSS feeds"/> RSS</a><a href="/atom.xml"><img src="/images/atom.png" alt="Atom feeds"/> Atom</a>
|
||||
</div>
|
||||
<div class="newsletter">
|
||||
<p>Newsletter
|
||||
<input id="pannous_email" type="email" onclick="clear_your_email();" value="Your email"/>
|
||||
<input type="button" value="Subscribe" onclick="pannous_list_subscribe();"/>
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
<p style="text-align:center">Généré avec <a href="http://indefero.soutade.fr/p/dynastie">Dynastie</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -6,100 +6,112 @@
|
|||
<dyn:meta name="keywords"/>
|
||||
<dyn:meta name="title"/>
|
||||
<dyn:meta name="author"/>
|
||||
<dyn:meta name="lang"/>
|
||||
<meta content="Dynastie" name="generator"/>
|
||||
<meta name="viewport" content="width=width, initial-scale=1"/>
|
||||
<title>Blog de Grégory Soutadé</title>
|
||||
<link rel="icon" type="image/png" href="/images/favicon.png" />
|
||||
<link href="/rss.xml" rel="alternate" type="application/rss+xml" title="RSS 2.0" />
|
||||
<link href="/atom.xml" rel="alternate" type="application/atom+xml" title="Atom 1.0" />
|
||||
<link href="/css/blog.css" rel="stylesheet" type="text/css"/>
|
||||
<script type="text/javascript" src="/js/blog.js"> </script>
|
||||
<script src="/js/blog.js"> </script>
|
||||
</head>
|
||||
<body onLoad="javascript:init();">
|
||||
<a href="/"><img id="logo" src="/images/tux_final.png"/></a>
|
||||
<div class="body">
|
||||
<div class="table-row">
|
||||
<header>
|
||||
<div class="table-cell" id="header">
|
||||
<h2 id="title"><a href="/">Blog de Grégory Soutadé</a></h2>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="content">
|
||||
<dyn:block name="content"/>
|
||||
<div class="content" id="content">
|
||||
<header>
|
||||
<a href="/"><img id="logo" alt="Logo Tux" src="/images/tux_final2.png"/></a>
|
||||
<div id="header">
|
||||
<span id="title"><a href="/">Blog de Grégory Soutadé</a></span>
|
||||
</div>
|
||||
<div class="menu">
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Recherche</div>
|
||||
<div id="menu_main">
|
||||
<dyn:replace div_name="form" id="search_form" method="POST" action="/search/dyn:blog_id">
|
||||
<input type="text" name="text" id="search_text" onkeypress="handleKeyPress(event,this.form)"/>
|
||||
</dyn:replace>
|
||||
</div>
|
||||
</div>
|
||||
<nav>
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Menu principal</div>
|
||||
<div id="menu_main">
|
||||
<div class="menu_content_content"><a href="/">Première page</a></div>
|
||||
<div class="menu_content_content"><a href="/about.html">À propos</a></div>
|
||||
<div class="menu_content_content"><a href="/all_posts.html">Tous les articles</a></div>
|
||||
<div class="menu_content_content"><a href="http://indefero.soutade.fr">Projets personnels</a></div>
|
||||
<div class="menu_content_content"><a href="/ljdc">Les joies du code</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Catégories</div>
|
||||
<div class="menu_content_content">
|
||||
<ul>
|
||||
<li><a href="/category/articles">Articles</a></li>
|
||||
<li><a href="/category/musique">Musique</a></li>
|
||||
<li><a href="/category/informatique">Informatique</a></li>
|
||||
<li><a href="/category/cinema">Cinéma</a></li>
|
||||
<li><a href="/category/sport">Sport</a></li>
|
||||
<li><a href="/category/configurations-pc">Configurations PC</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Tags</div>
|
||||
<div class="menu_content_content">
|
||||
<ul>
|
||||
<li><a href="/tag/inenglish">In English</a></li>
|
||||
<li><a href="/tag/python">Python</a></li>
|
||||
<li><a href="/tag/sheevaplug">SheevaPlug</a></li>
|
||||
<li><a href="/tag/course-a-pied">Course à pied</a></li>
|
||||
<li><a href="/tag/tag-rugby">Tag rugby</a></li>
|
||||
<li><a href="/tag/politique">Politique</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Archives</div>
|
||||
<div class="menu_content_content">
|
||||
<ul>
|
||||
<li><a href="/archive/2015">2015</a></li>
|
||||
<li><a href="/archive/2014">2014</a></li>
|
||||
<li><a href="/archive/2013">2013</a></li>
|
||||
<li><a href="/archive/2012">2012</a></li>
|
||||
<li><a href="/archive/2011">2011</a></li>
|
||||
<li><a href="/archive/2010">2010</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feed">
|
||||
<a href="/rss.xml"><img src="/images/rss.png" alt="RSS feeds"></img> RSS</a><a href="/atom.xml"><img src="/images/atom.png" alt="Atom feeds"></img> Atom</a>
|
||||
</div>
|
||||
</nav>
|
||||
<p style="text-align:center">Généré avec <a href="http://indefero.soutade.fr/p/dynastie">Dynastie</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<dyn:block name="content"/>
|
||||
<footer>
|
||||
<div class="footer">
|
||||
Copyright © 2010-2016 Grégory Soutadé.<br/>
|
||||
Copyright © 2010-2022 Grégory Soutadé.<br/>
|
||||
Tous droits réservés.
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="hamburger" id="hamburger">
|
||||
<a href="#" onclick="switchMenu();return false;">
|
||||
<img id="hamburger_logo" alt="Menu" src="/images/hamburger.png"/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="menu" id="menu" lang="fr">
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Recherche</div>
|
||||
<div class="menu_main">
|
||||
<dyn:replace div_name="form" id="search_form" method="POST" action="/search/dyn:blog_id">
|
||||
<input type="text" name="text" id="search_text" onkeypress="handleKeyPress(event,this.form)"/>
|
||||
</dyn:replace>
|
||||
</div>
|
||||
</div>
|
||||
<nav>
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Menu principal</div>
|
||||
<div class="menu_main">
|
||||
<div class="menu_content_content"><a href="/">Première page</a></div>
|
||||
<div class="menu_content_content"><a href="/about.html">À propos</a></div>
|
||||
<div class="menu_content_content"><a href="/all_posts.html">Tous les articles</a></div>
|
||||
<div class="menu_content_content"><a href="http://indefero.soutade.fr">Projets personnels</a></div>
|
||||
<div class="menu_content_content"><a href="/ljdc">Les joies du code</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Catégories</div>
|
||||
<div class="menu_content_content">
|
||||
<ul>
|
||||
<li><a href="/category/articles">Articles</a></li>
|
||||
<li><a href="/category/musique">Musique</a></li>
|
||||
<li><a href="/category/informatique">Informatique</a></li>
|
||||
<li><a href="/category/sport">Sport</a></li>
|
||||
<li><a href="/category/configurations-pc">Configurations PC</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Tags</div>
|
||||
<div class="menu_content_content">
|
||||
<ul>
|
||||
<li><a href="/tag/inenglish">In English</a></li>
|
||||
<li><a href="/tag/python">Python</a></li>
|
||||
<li><a href="/tag/sheevaplug">SheevaPlug</a></li>
|
||||
<li><a href="/tag/course-a-pied">Course à pied</a></li>
|
||||
<li><a href="/tag/tag-rugby">Tag rugby</a></li>
|
||||
<li><a href="/tag/politique">Politique</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu_content">
|
||||
<div class="menu_content_header">Archives</div>
|
||||
<div class="menu_content_content">
|
||||
<ul>
|
||||
<li><a href="/archive/2021">2021</a></li>
|
||||
<li><a href="/archive/2020">2020</a></li>
|
||||
<li><a href="/archive/2019">2019</a></li>
|
||||
<li><a href="/archive/2018">2018</a></li>
|
||||
<li><a href="/archive/2017">2017</a></li>
|
||||
<li><a href="/archive/2016">2016</a></li>
|
||||
<li><a href="/archive/2015">2015</a></li>
|
||||
<li><a href="/archive/2014">2014</a></li>
|
||||
<li><a href="/archive/2013">2013</a></li>
|
||||
<li><a href="/archive/2012">2012</a></li>
|
||||
<li><a href="/archive/2011">2011</a></li>
|
||||
<li><a href="/archive/2010">2010</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feed">
|
||||
<a href="/rss.xml"><img src="/images/rss.png" alt="RSS feeds"/> RSS</a><a href="/atom.xml"><img src="/images/atom.png" alt="Atom feeds"/> Atom</a>
|
||||
</div>
|
||||
<div class="newsletter">
|
||||
<p>Newsletter
|
||||
<input id="pannous_email" type="email" onclick="clear_your_email();" value="Your email"/>
|
||||
<input type="button" value="Subscribe" onclick="pannous_list_subscribe();"/>
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
<p style="text-align:center">Généré avec <a href="http://indefero.soutade.fr/p/dynastie">Dynastie</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|