From 9249e6ddc35e7cc7077fa5035fbc27397fbb70a9 Mon Sep 17 00:00:00 2001
From: Radek Czajka
Date: Thu, 30 Jun 2011 17:58:46 +0200
Subject: [PATCH] django 1.3, comments on books, last activity log, some minor
changes
---
apps/dvcs/admin.py | 15 ++-
apps/dvcs/migrations/0001_initial.py | 19 ++--
...document_user__add_field_document_stage.py | 96 ------------------
apps/dvcs/models.py | 37 ++++---
...unique_chunk_book_number__add_unique_ch.py | 23 ++++-
apps/wiki/models.py | 11 ++-
apps/wiki/templates/wiki/book_detail.html | 5 +-
.../wiki/document_create_missing.html | 3 +-
.../templates/wiki/document_details_base.html | 1 +
apps/wiki/templates/wiki/document_list.html | 3 +
apps/wiki/templates/wiki/document_upload.html | 1 +
apps/wiki/templates/wiki/wall.html | 32 ++++++
apps/wiki/templatetags/wiki.py | 99 +++++++++++++++++++
redakcja/settings/__init__.py | 17 ++--
redakcja/settings/common.py | 6 ++
redakcja/static/css/filelist.css | 4 +-
redakcja/static/js/wiki/view_history.js | 14 +++
redakcja/static/js/wiki/wikiapi.js | 11 +++
redakcja/urls.py | 2 +
requirements.txt | 3 +-
20 files changed, 263 insertions(+), 139 deletions(-)
delete mode 100644 apps/dvcs/migrations/0002_auto__add_field_document_user__add_field_document_stage.py
create mode 100755 apps/wiki/templates/wiki/wall.html
diff --git a/apps/dvcs/admin.py b/apps/dvcs/admin.py
index 984798df..f6a1ab39 100644
--- a/apps/dvcs/admin.py
+++ b/apps/dvcs/admin.py
@@ -1,6 +1,11 @@
-from django.contrib.admin import site
-from dvcs.models import Document, Change, Tag
+from django.contrib import admin
-site.register(Tag)
-site.register(Document)
-site.register(Change)
+from dvcs import models
+
+
+class TagAdmin(admin.ModelAdmin):
+ list_display = ['name', 'slug', 'ordering']
+
+admin.site.register(models.Tag, TagAdmin)
+admin.site.register(models.Document)
+admin.site.register(models.Change)
diff --git a/apps/dvcs/migrations/0001_initial.py b/apps/dvcs/migrations/0001_initial.py
index 6bff0950..bf8d3e24 100644
--- a/apps/dvcs/migrations/0001_initial.py
+++ b/apps/dvcs/migrations/0001_initial.py
@@ -4,7 +4,6 @@ from south.db import db
from south.v2 import SchemaMigration
from django.db import models
-
class Migration(SchemaMigration):
def forwards(self, orm):
@@ -22,7 +21,8 @@ class Migration(SchemaMigration):
db.create_table('dvcs_change', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
- ('author_desc', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)),
+ ('author_name', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)),
+ ('author_email', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)),
('patch', self.gf('django.db.models.fields.TextField')(blank=True)),
('tree', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['dvcs.Document'])),
('revision', self.gf('django.db.models.fields.IntegerField')(db_index=True)),
@@ -48,8 +48,10 @@ class Migration(SchemaMigration):
# Adding model 'Document'
db.create_table('dvcs_document', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('creator', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
+ ('creator', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='created_documents', null=True, to=orm['auth.User'])),
('head', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['dvcs.Change'], null=True, blank=True)),
+ ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
+ ('stage', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['dvcs.Tag'], null=True, blank=True)),
))
db.send_create_signal('dvcs', ['Document'])
@@ -84,7 +86,7 @@ class Migration(SchemaMigration):
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
- 'Meta': {'ordering': "('content_type__app_label', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
@@ -116,7 +118,8 @@ class Migration(SchemaMigration):
'dvcs.change': {
'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'Change'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
- 'author_desc': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+ 'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+ 'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
@@ -130,9 +133,11 @@ class Migration(SchemaMigration):
},
'dvcs.document': {
'Meta': {'object_name': 'Document'},
- 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_documents'", 'null': 'True', 'to': "orm['auth.User']"}),
'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['dvcs.Change']", 'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dvcs.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
},
'dvcs.tag': {
'Meta': {'ordering': "['ordering']", 'object_name': 'Tag'},
diff --git a/apps/dvcs/migrations/0002_auto__add_field_document_user__add_field_document_stage.py b/apps/dvcs/migrations/0002_auto__add_field_document_user__add_field_document_stage.py
deleted file mode 100644
index 41ab9b24..00000000
--- a/apps/dvcs/migrations/0002_auto__add_field_document_user__add_field_document_stage.py
+++ /dev/null
@@ -1,96 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
-
- # Adding field 'Document.user'
- db.add_column('dvcs_document', 'user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True), keep_default=False)
-
- # Adding field 'Document.stage'
- db.add_column('dvcs_document', 'stage', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['dvcs.Tag'], null=True), keep_default=False)
-
-
- def backwards(self, orm):
-
- # Deleting field 'Document.user'
- db.delete_column('dvcs_document', 'user_id')
-
- # Deleting field 'Document.stage'
- db.delete_column('dvcs_document', 'stage_id')
-
-
- models = {
- 'auth.group': {
- 'Meta': {'object_name': 'Group'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
- 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
- },
- 'auth.permission': {
- 'Meta': {'ordering': "('content_type__app_label', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
- 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
- },
- 'auth.user': {
- 'Meta': {'object_name': 'User'},
- 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
- 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
- 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
- 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
- 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
- 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
- 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
- },
- 'contenttypes.contenttype': {
- 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
- 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
- },
- 'dvcs.change': {
- 'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'Change'},
- 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
- 'author_desc': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
- 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
- 'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['dvcs.Change']"}),
- 'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['dvcs.Change']"}),
- 'patch': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
- 'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
- 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['dvcs.Tag']", 'symmetrical': 'False'}),
- 'tree': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dvcs.Document']"})
- },
- 'dvcs.document': {
- 'Meta': {'object_name': 'Document'},
- 'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_documents'", 'null': 'True', 'to': "orm['auth.User']"}),
- 'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['dvcs.Change']", 'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dvcs.Tag']", 'null': 'True'}),
- 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
- },
- 'dvcs.tag': {
- 'Meta': {'ordering': "['ordering']", 'object_name': 'Tag'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
- 'ordering': ('django.db.models.fields.IntegerField', [], {}),
- 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
- }
- }
-
- complete_apps = ['dvcs']
diff --git a/apps/dvcs/models.py b/apps/dvcs/models.py
index f81f2cab..86f66bc9 100644
--- a/apps/dvcs/models.py
+++ b/apps/dvcs/models.py
@@ -60,7 +60,8 @@ class Change(models.Model):
Data contains a pickled diff needed to reproduce the initial document.
"""
author = models.ForeignKey(User, null=True, blank=True)
- author_desc = models.CharField(max_length=128, null=True, blank=True)
+ author_name = models.CharField(max_length=128, null=True, blank=True)
+ author_email = models.CharField(max_length=128, null=True, blank=True)
patch = models.TextField(blank=True)
tree = models.ForeignKey('Document')
revision = models.IntegerField(db_index=True)
@@ -94,7 +95,10 @@ class Change(models.Model):
self.author.last_name,
self.author.email)
else:
- return self.author_desc
+ return "%s <%s>" % (
+ self.author_name,
+ self.author_email
+ )
def save(self, *args, **kwargs):
@@ -127,20 +131,22 @@ class Change(models.Model):
return text
def make_child(self, patch, description, author=None,
- author_desc=None, tags=None):
+ author_name=None, author_email=None, tags=None):
ch = self.children.create(patch=patch,
tree=self.tree, author=author,
- author_desc=author_desc,
+ author_name=author_name,
+ author_email=author_email,
description=description)
if tags is not None:
ch.tags = tags
return ch
def make_merge_child(self, patch, description, author=None,
- author_desc=None, tags=None):
+ author_name=None, author_email=None, tags=None):
ch = self.merge_children.create(patch=patch,
tree=self.tree, author=author,
- author_desc=author_desc,
+ author_name=author_name,
+ author_email=author_email,
description=description,
tags=tags)
if tags is not None:
@@ -150,7 +156,8 @@ class Change(models.Model):
def apply_to(self, text):
return mdiff.patch(text, pickle.loads(self.patch.encode('ascii')))
- def merge_with(self, other, author=None, author_desc=None,
+ def merge_with(self, other, author=None,
+ author_name=None, author_email=None,
description=u"Automatic merge."):
assert self.tree_id == other.tree_id # same tree
if other.parent_id == self.pk:
@@ -166,7 +173,9 @@ class Change(models.Model):
patch = self.make_patch(local, result)
return self.children.create(
patch=patch, merge_parent=other, tree=self.tree,
- author=author, author_desc=author_desc,
+ author=author,
+ author_name=author_name,
+ author_email=author_email,
description=description)
def revert(self, **kwargs):
@@ -225,7 +234,8 @@ class Document(models.Model):
patch = kwargs['patch']
author = kwargs.get('author', None)
- author_desc = kwargs.get('author_desc', None)
+ author_name = kwargs.get('author_name', None)
+ author_email = kwargs.get('author_email', None)
tags = kwargs.get('tags', [])
if tags:
# set stage to next tag after the commited one
@@ -234,15 +244,18 @@ class Document(models.Model):
old_head = self.head
if parent != old_head:
change = parent.make_merge_child(patch, author=author,
- author_desc=author_desc,
+ author_name=author_name,
+ author_email=author_email,
description=kwargs.get('description', ''),
tags=tags)
# not Fast-Forward - perform a merge
self.head = old_head.merge_with(change, author=author,
- author_desc=author_desc)
+ author_name=author_name,
+ author_email=author_email)
else:
self.head = parent.make_child(patch, author=author,
- author_desc=author_desc,
+ author_name=author_name,
+ author_email=author_email,
description=kwargs.get('description', ''),
tags=tags)
diff --git a/apps/wiki/migrations/0003_auto__add_book__add_chunk__add_unique_chunk_book_number__add_unique_ch.py b/apps/wiki/migrations/0003_auto__add_book__add_chunk__add_unique_chunk_book_number__add_unique_ch.py
index 3db141ae..deb649f9 100644
--- a/apps/wiki/migrations/0003_auto__add_book__add_chunk__add_unique_chunk_book_number__add_unique_ch.py
+++ b/apps/wiki/migrations/0003_auto__add_book__add_chunk__add_unique_chunk_book_number__add_unique_ch.py
@@ -15,6 +15,7 @@ from slughifi import slughifi
META_REGEX = re.compile(r'\s*', re.DOTALL | re.MULTILINE)
STAGE_TAGS_RE = re.compile(r'^#stage-finished: (.*)$', re.MULTILINE)
+AUTHOR_RE = re.compile(r'\s*(.*?)\s*<(.*)>\s*')
def urlunquote(url):
@@ -84,8 +85,7 @@ def migrate_file_from_hg(orm, fname, entry):
chunk = orm.Chunk.objects.create(
book=book,
number=1,
- slug='1',
- comment='cz. 1')
+ slug='1')
head = orm['dvcs.Change'].objects.create(
tree=chunk,
revision=-1,
@@ -123,13 +123,27 @@ def migrate_file_from_hg(orm, fname, entry):
description = STAGE_TAGS_RE.sub('', description)
+ author = author_name = author_email = None
+ author_desc = fctx.user().decode("utf-8", 'replace')
+ m = AUTHOR_RE.match(author_desc)
+ if m:
+ try:
+ author = orm['auth.User'].objects.get(username=m.group(1), email=m.group(2))
+ except orm['auth.User'].DoesNotExist:
+ author_name = m.group(1)
+ author_email = m.group(2)
+ else:
+ author_name = author_desc
+
head = orm['dvcs.Change'].objects.create(
tree=chunk,
revision=rev + 1,
patch=make_patch(old_data, data),
created_at=datetime.datetime.fromtimestamp(fctx.date()[0]),
description=description,
- author_desc=fctx.user().decode("utf-8", 'replace'),
+ author=author,
+ author_name=author_name,
+ author_email=author_email,
parent=chunk.head
)
head.tags = tags
@@ -252,7 +266,8 @@ class Migration(SchemaMigration):
'dvcs.change': {
'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'Change'},
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
- 'author_desc': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+ 'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+ 'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
diff --git a/apps/wiki/models.py b/apps/wiki/models.py
index 9eb77a5c..28fdab8b 100644
--- a/apps/wiki/models.py
+++ b/apps/wiki/models.py
@@ -157,7 +157,7 @@ class Chunk(dvcs_models.Document):
book = models.ForeignKey(Book, editable=False)
number = models.IntegerField()
slug = models.SlugField()
- comment = models.CharField(max_length=255)
+ comment = models.CharField(max_length=255, blank=True)
class Meta:
unique_together = [['book', 'number'], ['book', 'slug']]
@@ -177,8 +177,13 @@ class Chunk(dvcs_models.Document):
return cls.objects.get(book__slug=slug, slug=chunk)
def pretty_name(self):
- return "%s, %s (%d/%d)" % (self.book.title, self.comment,
- self.number, len(self.book))
+ title = self.book.title
+ if self.comment:
+ title += ", %s" % self.comment
+ count = len(self.book)
+ if count > 1:
+ title += " (%d/%d)" % (self.number, len(self.book))
+ return title
def split(self, slug, comment='', creator=None):
""" Create an empty chunk after this one """
diff --git a/apps/wiki/templates/wiki/book_detail.html b/apps/wiki/templates/wiki/book_detail.html
index 347b7a37..84d2097b 100755
--- a/apps/wiki/templates/wiki/book_detail.html
+++ b/apps/wiki/templates/wiki/book_detail.html
@@ -1,5 +1,5 @@
{% extends "wiki/base.html" %}
-{% load i18n %}
+{% load comments i18n %}
{% block leftcolumn %}
@@ -97,6 +97,7 @@
{% endblock leftcolumn %}
{% block rightcolumn %}
-
+{% render_comment_list for book %}
+{% render_comment_form for book %}
{% endblock rightcolumn %}
diff --git a/apps/wiki/templates/wiki/document_create_missing.html b/apps/wiki/templates/wiki/document_create_missing.html
index 351e87a2..9414f7cb 100644
--- a/apps/wiki/templates/wiki/document_create_missing.html
+++ b/apps/wiki/templates/wiki/document_create_missing.html
@@ -3,6 +3,7 @@
{% block leftcolumn %}
diff --git a/apps/wiki/templates/wiki/wall.html b/apps/wiki/templates/wiki/wall.html
new file mode 100755
index 00000000..ed4685a0
--- /dev/null
+++ b/apps/wiki/templates/wiki/wall.html
@@ -0,0 +1,32 @@
+{% load i18n %}
+{% load gravatar %}
+
+
+{% for item in wall %}
+ -
+
+ {% if item.get_email %}
+ {% gravatar_img_for_email item.get_email 32 %}
+
+ {% endif %}
+
+
+
+
+ {{ item.timestamp }}
+
+ {% if item.user %}
+
+ {{ item.user.first_name }} {{ item.user.last_name }}
+ <{{ item.user.email }}>
+ {% else %}
+ {{ item.user_name }}
+ {% if item.get_email %}
+ <{{ item.get_email }}>
+ {% endif %}
+ {% endif %}
+
{{ item.title }}
+
{{ item.summary }}
+
+{% endfor %}
+
diff --git a/apps/wiki/templatetags/wiki.py b/apps/wiki/templatetags/wiki.py
index 1fb1a2ea..337afa65 100644
--- a/apps/wiki/templatetags/wiki.py
+++ b/apps/wiki/templatetags/wiki.py
@@ -1,10 +1,13 @@
from __future__ import absolute_import
from django.core.urlresolvers import reverse
+from django.contrib.comments.models import Comment
from django.template.defaultfilters import stringfilter
from django import template
from django.utils.translation import ugettext as _
+from wiki.models import Book
+from dvcs.models import Change
register = template.Library()
@@ -39,3 +42,99 @@ def main_tabs(context):
tabs.append(Tab('admin', _('Admin'), reverse("admin:index")))
return {"tabs": tabs, "active_tab": active}
+
+
+class WallItem(object):
+ title = ''
+ summary = ''
+ url = ''
+ timestamp = ''
+ user = None
+ email = ''
+
+ def __init__(self, tag):
+ self.tag = tag
+
+ def get_email(self):
+ if self.user:
+ return self.user.email
+ else:
+ return self.email
+
+
+def changes_wall(max_len):
+ qs = Change.objects.filter(revision__gt=-1).order_by('-created_at').select_related()
+ qs = qs[:max_len]
+ for item in qs:
+ tag = 'stage' if item.tags.count() else 'change'
+ chunk = item.tree.chunk
+ w = WallItem(tag)
+ w.title = chunk.pretty_name()
+ w.summary = item.description
+ w.url = reverse('wiki_editor',
+ args=[chunk.book.slug, chunk.slug]) + '?diff=%d' % item.revision
+ w.timestamp = item.created_at
+ w.user = item.author
+ w.email = item.author_email
+ yield w
+
+
+def published_wall(max_len):
+ qs = Book.objects.exclude(last_published=None).order_by('-last_published')
+ qs = qs[:max_len]
+ for item in qs:
+ w = WallItem('publish')
+ w.title = item.title
+ w.summary = item.title
+ w.url = chunk.book.get_absolute_url()
+ w.timestamp = item.last_published
+ w.user = item.last_published_by
+ yield w
+
+
+def comments_wall(max_len):
+ qs = Comment.objects.filter(is_public=True).select_related().order_by('-submit_date')
+ qs = qs[:max_len]
+ for item in qs:
+ w = WallItem('comment')
+ w.title = item.content_object
+ w.summary = item.comment
+ w.url = item.content_object.get_absolute_url()
+ w.timestamp = item.submit_date
+ w.user = item.user
+ w.email = item.user_email
+ yield w
+
+
+def big_wall(max_len, *args):
+ """
+ Takes some WallItem iterators and zips them into one big wall.
+ Input iterators must already be sorted by timestamp.
+ """
+ subwalls = []
+ for w in args:
+ try:
+ subwalls.append([next(w), w])
+ except StopIteration:
+ pass
+
+ while max_len and subwalls:
+ i, next_item = max(enumerate(subwalls), key=lambda x: x[1][0].timestamp)
+ yield next_item[0]
+ max_len -= 1
+ try:
+ next_item[0] = next(next_item[1])
+ except StopIteration:
+ del subwalls[i]
+
+
+@register.inclusion_tag("wiki/wall.html", takes_context=True)
+def wall(context, max_len=10):
+ return {
+ "request": context['request'],
+ "STATIC_URL": context['STATIC_URL'],
+ "wall": big_wall(max_len,
+ changes_wall(max_len),
+ published_wall(max_len),
+ comments_wall(max_len),
+ )}
diff --git a/redakcja/settings/__init__.py b/redakcja/settings/__init__.py
index 6f1c094e..600533b9 100644
--- a/redakcja/settings/__init__.py
+++ b/redakcja/settings/__init__.py
@@ -1,12 +1,17 @@
from __future__ import absolute_import
+from os import path
from redakcja.settings.common import *
-DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
-DATABASE_NAME = PROJECT_ROOT + '/dev.sqlite' # Or path to database file if using sqlite3.
-DATABASE_USER = '' # Not used with sqlite3.
-DATABASE_PASSWORD = '' # Not used with sqlite3.
-DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
-DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
+ 'NAME': path.join(PROJECT_ROOT, 'dev.sqlite'), # Or path to database file if using sqlite3.
+ 'USER': '', # Not used with sqlite3.
+ 'PASSWORD': '', # Not used with sqlite3.
+ 'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
+ 'PORT': '', # Set to empty string for default. Not used with sqlite3.
+ }
+}
try:
LOGGING_CONFIG_FILE
diff --git a/redakcja/settings/common.py b/redakcja/settings/common.py
index 36c54b00..f55a877b 100644
--- a/redakcja/settings/common.py
+++ b/redakcja/settings/common.py
@@ -36,6 +36,8 @@ SITE_ID = 1
# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True
+USE_L10N = True
+
# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
@@ -69,12 +71,14 @@ TEMPLATE_CONTEXT_PROCESSORS = (
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"redakcja.context_processors.settings", # this is instead of media
+ 'django.core.context_processors.csrf',
"django.core.context_processors.request",
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
@@ -112,6 +116,7 @@ INSTALLED_APPS = (
'django.contrib.sites',
'django.contrib.admin',
'django.contrib.admindocs',
+ 'django.contrib.comments',
'django_cas',
'compress',
@@ -119,6 +124,7 @@ INSTALLED_APPS = (
'sorl.thumbnail',
'filebrowser',
'pagination',
+ 'gravatar',
'dvcs',
'wiki',
diff --git a/redakcja/static/css/filelist.css b/redakcja/static/css/filelist.css
index 4c5ffb3f..61e6af22 100644
--- a/redakcja/static/css/filelist.css
+++ b/redakcja/static/css/filelist.css
@@ -58,7 +58,7 @@ body {
-#file-list {
+#wiki_layout_left_column {
overflow: visible;
float: left;
/*max-width: 50%;*/
@@ -67,7 +67,7 @@ body {
}
-#last-edited-list {
+#wiki_layout_right_column {
float: left;
max-width: 35%;
margin-left: 5%;
diff --git a/redakcja/static/js/wiki/view_history.js b/redakcja/static/js/wiki/view_history.js
index fe3af69a..bd6b27b0 100644
--- a/redakcja/static/js/wiki/view_history.js
+++ b/redakcja/static/js/wiki/view_history.js
@@ -5,6 +5,20 @@
options.callback = function() {
var self = this;
+ if (CurrentDocument.diff) {
+ rev_from = CurrentDocument.diff[0];
+ rev_to = CurrentDocument.diff[1];
+ this.doc.fetchDiff({
+ from: rev_from,
+ to: rev_to,
+ success: function(doc, data){
+ var result = $.wiki.newTab(doc, ''+rev_from +' -> ' + rev_to, 'DiffPerspective');
+
+ $(result.view).html(data);
+ $.wiki.switchToTab(result.tab);
+ }
+ });
+ }
// first time page is rendered
$('#make-diff-button').click(function() {
diff --git a/redakcja/static/js/wiki/wikiapi.js b/redakcja/static/js/wiki/wikiapi.js
index a1f2fb92..cbed73b8 100644
--- a/redakcja/static/js/wiki/wikiapi.js
+++ b/redakcja/static/js/wiki/wikiapi.js
@@ -71,6 +71,17 @@
this.readonly = !!$("*[data-key='readonly']", meta).text();
this.galleryLink = $("*[data-key='gallery']", meta).text();
+
+ var diff = $("*[data-key='diff']", meta).text();
+ diff = diff.split(',');
+ if (diff.length == 2 && diff[0] < diff[1])
+ this.diff = diff;
+ else if (diff.length == 1) {
+ diff = parseInt(diff);
+ if (diff != NaN)
+ this.diff = [diff - 1, diff];
+ }
+
this.galleryImages = [];
this.text = null;
this.has_local_changes = false;
diff --git a/redakcja/urls.py b/redakcja/urls.py
index 8aa569b2..3fd6ff52 100644
--- a/redakcja/urls.py
+++ b/redakcja/urls.py
@@ -18,6 +18,8 @@ urlpatterns = patterns('',
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
(r'^admin/', include(admin.site.urls)),
+ (r'^comments/', include('django.contrib.comments.urls')),
+
url(r'^$', 'django.views.generic.simple.redirect_to', {'url': '/documents/'}),
url(r'^documents/', include('wiki.urls')),
url(r'^storage/', include('dvcs.urls')),
diff --git a/requirements.txt b/requirements.txt
index 6ba77393..c5d6b044 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -10,10 +10,11 @@ httplib2 # oauth2 dependency
# git+git://github.com/fnp/librarian.git@master#egg=librarian
## Django
-Django>=1.1.1,<1.2
+Django>=1.3,<1.4
sorl-thumbnail>=3.2
django-maintenancemode>=0.9
django-pagination
+django-gravatar
# migrations
south>=0.6
--
2.20.1