-
{% blocktrans %}Versions:{% endblocktrans %}
-{% for author,color in author_colors %}
- {{ author }}
-{% endfor %}
-
-
-
-{% for v1, v2, color in paired_versions %}
-
- {{ forloop.counter }}
+
- {% if v2 %}
-
-
-
-
-
- {% endif %}
-{% endfor %}
-
+
-{% endif %}
+{% include "site/macros/paginator.html" %}
+
+
-{% if v2_nid %}
-
{% blocktrans with v1_nid as v1_nid and v1_nid as v1_nid %}Differences between version #{{ v1_nid }} and version #{{ v2_nid }}:{% endblocktrans %}
-{% else %}
-
{{ version1.title }}
-
{% blocktrans with version1.created|local_date:tz as date and v1_nid as version_number %}Version #{{ version_number }} created on {{ date }}{% endblocktrans %}
-
-{% endif %}
-{% autoescape off %}
+
+
+
+ {% blocktrans %}Version{% endblocktrans %}
+ {% blocktrans %}Selection{% endblocktrans %}
+ {% blocktrans %}Title{% endblocktrans %}
+ {% blocktrans %}Author{% endblocktrans %}
+ {% blocktrans %}Created{% endblocktrans %}
+ {% blocktrans %}# comments{% endblocktrans %}
+
+
+
+ {% for text_version in object_list %}
+
+ {% get_local_text_perm request text can_edit_text as can_edit_text %}
+ {% get_local_text_perm request text can_delete_text as can_delete_text %}
+ {% get_local_text_perm request text can_manage_text as can_manage_text %}
+
+
+ {% if can_manage_workspace %} {% endif %}
+ #{{ forloop.counter0|add:page_obj.start_index|invneg:paginator.count|add:"1" }}
+
+
+
+ {{ text_version.title }}
+
+
+
+
+
+ {{ text_version.get_name }}
+ {{ text_version.created|local_date }}
+ {{ text_version|nb_comments:request }}
-{% if v2_nid %}
-
-
-
-
- {{ version1.title }}
-
- {% blocktrans with version1.created|local_date:tz as date and v1_nid as version_number %}Version #{{ version_number }} created on {{ date }}{% endblocktrans %}
-
-
-
- {{ version2.title }}
-
- {% blocktrans with version2.created|local_date:tz as date and v2_nid as version_number %}Version #{{ version_number }} created on {{ date }}{% endblocktrans %}
-
-
+
+ {% endfor %}
+
-{% endif %}
-
-{% if is_diff %}
-{{ content }}
-{% else %}
-{% blocktrans %}No differences{% endblocktrans %}
-{% endif %}
+
-{% if embed_code %}
-
-{{ embed_code }}
-{% endif %}
-
-
-{% endautoescape %}
{% endblock %}
diff -r 8cb89c153140 -r f436e2fef076 src/cm/templates/site/text_history_compare.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cm/templates/site/text_history_compare.html Tue Feb 09 22:20:08 2010 +0100
@@ -0,0 +1,50 @@
+{% extends "site/layout/base_text.html" %}
+{% load com %}
+{% load i18n %}
+{% load local_perms %}
+{% block main %}
+
+{% get_local_text_perm request text can_edit_text as can_edit_text %}
+
+
+
+
+
+
+
+
+
+
+
+
+{% autoescape off %}
+{% if empty %}
+
{% blocktrans %}No differences{% endblocktrans %}
+{% else %}
+{{ content }}
+{% endif %}
+{% endautoescape %}
+
+
+{% endblock %}
diff -r 8cb89c153140 -r f436e2fef076 src/cm/templates/site/text_history_version.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cm/templates/site/text_history_version.html Tue Feb 09 22:20:08 2010 +0100
@@ -0,0 +1,52 @@
+{% extends "site/layout/base_text.html" %}
+{% load com %}
+{% load i18n %}
+{% load local_perms %}
+{% block main %}
+
+{% get_local_text_perm request text can_edit_text as can_edit_text %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% autoescape off %}
+{{ embed_code }}
+{% endautoescape %}
+
+
+
+{% endblock %}
diff -r 8cb89c153140 -r f436e2fef076 src/cm/templates/site/text_list.html
--- a/src/cm/templates/site/text_list.html Fri Feb 05 18:43:58 2010 +0100
+++ b/src/cm/templates/site/text_list.html Tue Feb 09 22:20:08 2010 +0100
@@ -136,7 +136,7 @@
{% up_down title %}{% blocktrans %}Text{% endblocktrans %}{% endup_down %}
{% blocktrans %}Author{% endblocktrans %}
{% up_down -modified %}{% blocktrans %}Modified{% endblocktrans %}{% endup_down %}
- # {% blocktrans %}comments{% endblocktrans %}
+ {% blocktrans %}# comments{% endblocktrans %}
{% if can_manage_workspace %}{% blocktrans %}Last week activity{% endblocktrans %} {% endif %}
diff -r 8cb89c153140 -r f436e2fef076 src/cm/templatetags/com.py
--- a/src/cm/templatetags/com.py Fri Feb 05 18:43:58 2010 +0100
+++ b/src/cm/templatetags/com.py Tue Feb 09 22:20:08 2010 +0100
@@ -209,11 +209,14 @@
from cm.security import get_viewable_comments
+from cm.models import Text
@register.filter(name='nb_comments')
def nb_comments(text, request):
- return len(get_viewable_comments(request, text.last_text_version.comment_set.all(), text))
-
+ if type(text) == Text:
+ return len(get_viewable_comments(request, text.last_text_version.comment_set.all(), text))
+ else:
+ return len(get_viewable_comments(request, text.comment_set.all(), text))
## number tags
from cm.security import get_texts_with_perm, get_viewable_comments
@@ -297,3 +300,7 @@
all_params = all_params[1:]
return NewParams([a for a in all_params])
+@register.filter(name='invneg')
+def do_invneg(value, arg):
+ """."""
+ return int(arg) - int(value)
diff -r 8cb89c153140 -r f436e2fef076 src/cm/tests/test_history.py
--- a/src/cm/tests/test_history.py Fri Feb 05 18:43:58 2010 +0100
+++ b/src/cm/tests/test_history.py Tue Feb 09 22:20:08 2010 +0100
@@ -10,6 +10,7 @@
def test_revert(self):
text = Text.objects.all()[0]
#for i in range(1,text.get_versions_number()+1):
- text.revert_to_version(1)
+ text_version = Text.last_text_version
+ text.revert_to_version(text_version.key)
#self.assertEqual(Comment.objects.count(), 16)
diff -r 8cb89c153140 -r f436e2fef076 src/cm/urls.py
--- a/src/cm/urls.py Fri Feb 05 18:43:58 2010 +0100
+++ b/src/cm/urls.py Tue Feb 09 22:20:08 2010 +0100
@@ -63,14 +63,16 @@
url(r'^text/(?P\w*)/pre_edit/$', text_pre_edit, name="text-preedit"),
url(r'^text/(?P\w*)/settings/$', text_settings, name="text-settings"),
url(r'^text/(?P\w*)/history/$', text_history, name="text-history"),
- url(r'^text/(?P\w*)/history-version/(?P\d*)/$', text_history, name="text-history-version"),
- url(r'^text/(?P\w*)/history/(?P\w*)/(?P\d*)/$', text_history, name="text-history-compare"),
- url(r'^text/(?P\w*)/revert/(?P\w*)/$', text_revert, name="text-revert"),
+ url(r'^text/(?P\w*)/history-version/(?P\w*)/$', text_history_version, name="text-history-version"),
+ url(r'^text/(?P\w*)/history-compare/(?P\w*)/(?P\w*)/$', text_history_compare, name="text-history-compare"),
+ url(r'^text/(?P\w*)/history-compare/(?P\w*)/(?P\w*)/(?P\d*)$', text_history_compare, name="text-history-compare2"),
+ url(r'^text/(?P\w*)/revert/(?P\w*)/$', text_revert, name="text-revert"),
url(r'^text/(?P\w*)/attach/(?P\w*)/$', text_attach, name="text-attach"),
- url(r'^text/(?P\w*)/delete/$', text_delete, name="text-delete"),
+ url(r'^text/(?P\w*)/delete/$', text_delete, name="text-delete"),
+ url(r'^text/(?P\w*)/(?P\w*)/delete/$', text_version_delete, name="text-version-delete"),
url(r'^text/(?P\w*)/export/(?P\w*)/(?P\w*)/(?P\w*)/(?P\w*)/$', text_export, name="text-export"),
url(r'^text/(?P\w*)/history/$', text_history, name="text-history"),
- url(r'^text/(?P\w*)/diff/(?P\w*)/(?P\w*)/$', text_diff, name="text-diff"),
+ #url(r'^text/(?P\w*)/diff/(?P\w*)/(?P\w*)/$', text_diff, name="text-diff"),
# url(r'^text/(?P\w*)/version/(?P\w*)/$', text_version, name="text-version"),
# main client frame
diff -r 8cb89c153140 -r f436e2fef076 src/cm/utils/diff.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cm/utils/diff.py Tue Feb 09 22:20:08 2010 +0100
@@ -0,0 +1,115 @@
+import difflib, string
+from django.utils.translation import ugettext as _
+
+def isatag(x): return x.startswith("<") and x.endswith(">")
+
+def text_diff(a, b):
+ res = []
+ a, b = createlist(a), createlist(b)
+ s = difflib.SequenceMatcher(isatag, a, b)
+ for e in s.get_opcodes():
+ if e[0] == "replace":
+ res.append(''+''.join(a[e[1]:e[2]]) + ''+''.join(b[e[3]:e[4]])+" ")
+ elif e[0] == "delete":
+ res.append(''+ ''.join(a[e[1]:e[2]]) + "")
+ elif e[0] == "insert":
+ res.append(''+''.join(b[e[3]:e[4]]) + " ")
+ elif e[0] == "equal":
+ res.append(''.join(b[e[3]:e[4]]))
+ else:
+ raise "error parsing %s" %(e[0])
+ return ''.join(res)
+
+COLORS = [
+'#FF0000',
+'#EE0000',
+'#DD0000',
+'#CC0000',
+'#BB0000',
+'#AA0000',
+'#990000',
+'#880000',
+'#770000',
+]
+
+from colorsys import hls_to_rgb, hsv_to_rgb
+
+def generatePastelColors(n):
+ s = .4
+ v = .99
+ return ['#'+''.join((hex(int(r*255))[2:],hex(int(g*255))[2:],hex(int(b*255))[2:])) for r,g,b in [hsv_to_rgb(h/200.,s,v) for h in range(1,100,100/n)]]
+
+
+DEFAULT_COLOR = '#D9D9D9'
+def get_colors(authors, last_white = False):
+ """
+ returns a dict for authors's colors (with last one white if 'last_white')
+ """
+ authors = set(authors)
+ #new_colors = list(tuple(COLORS))
+ new_colors = generatePastelColors(len(set(authors)))
+ res = []
+ if None in authors or '' in authors:
+ res = [(_(u'unknown'),DEFAULT_COLOR) ]
+ res.extend([(author,new_colors.pop()) for author in authors if author])
+ #if authors[-1]:
+ # res[authors[-1]] = '#FFFFFF'
+ return res
+
+
+def text_history(versions, authors):
+ res = versions[0]
+ colors = get_colors(authors)
+ for ind in range(len(versions)-1):
+ author = authors[ind]
+ color = colors.get(author,DEFAULT_COLOR)
+ v_2 = versions[ind + 1]
+ res = text_diff_add(v_2, res, color)
+ return res,colors.items()
+
+from cm.utils.html import surrond_text_node
+
+def text_diff_add(a,b, color):
+ res = []
+ a, b = createlist(a), createlist(b)
+ s = difflib.SequenceMatcher(isatag, a, b)
+ for e in s.get_opcodes():
+ if e[0] == "replace":
+ html_chunk = ''.join(b[e[3]:e[4]])
+ new_html_chunk = surrond_text_node(html_chunk,'' %color,' ')
+ res.append(new_html_chunk)
+ #res.append('' %color+''.join(b[e[3]:e[4]])+" ")
+ elif e[0] == "delete":
+ pass
+ elif e[0] == "insert":
+ html_chunk = ''.join(b[e[3]:e[4]])
+ new_html_chunk = surrond_text_node(html_chunk,'' %color,' ')
+ res.append(new_html_chunk)
+ #res.append('' %color+''.join(b[e[3]:e[4]]) + " ")
+ elif e[0] == "equal":
+ res.append(''.join(b[e[3]:e[4]]))
+ else:
+ raise "error parsing %s" %(e[0])
+ return ''.join(res)
+
+def createlist(x, b=0):
+ mode = 'char'
+ cur = ''
+ out = []
+ for c in x:
+ if mode == 'tag':
+ if c == '>':
+ if b: cur += ']'
+ else: cur += c
+ out.append(cur); cur = ''; mode = 'char'
+ else: cur += c
+ elif mode == 'char':
+ if c == '<':
+ out.append(cur)
+ if b: cur = '['
+ else: cur = c
+ mode = 'tag'
+ elif c in string.whitespace: out.append(cur+c); cur = ''
+ else: cur += c
+ out.append(cur)
+ return filter(lambda x: x is not '', out)
diff -r 8cb89c153140 -r f436e2fef076 src/cm/utils/string.py
--- a/src/cm/utils/string.py Fri Feb 05 18:43:58 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-import chardet
-
-def to_unicode(input):
- if type(input) == str:
- res = None
- for encoding in [chardet.detect(input)['encoding'], 'utf8', 'latin1']:
- try:
- res = unicode(input, encoding)
- break;
- except UnicodeDecodeError:
- pass
- if not res:
- raise Exception('UnicodeDecodeError: could not decode')
- return res
- return input
\ No newline at end of file
diff -r 8cb89c153140 -r f436e2fef076 src/cm/utils/string_utils.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cm/utils/string_utils.py Tue Feb 09 22:20:08 2010 +0100
@@ -0,0 +1,15 @@
+import chardet
+
+def to_unicode(input):
+ if type(input) == str:
+ res = None
+ for encoding in [chardet.detect(input)['encoding'], 'utf8', 'latin1']:
+ try:
+ res = unicode(input, encoding)
+ break;
+ except UnicodeDecodeError:
+ pass
+ if not res:
+ raise Exception('UnicodeDecodeError: could not decode')
+ return res
+ return input
\ No newline at end of file
diff -r 8cb89c153140 -r f436e2fef076 src/cm/views/__init__.py
--- a/src/cm/views/__init__.py Fri Feb 05 18:43:58 2010 +0100
+++ b/src/cm/views/__init__.py Tue Feb 09 22:20:08 2010 +0100
@@ -1,5 +1,3 @@
-from cm.diff import text_diff as _text_diff, \
- text_history as inner_text_history, get_colors
from cm.message import display_message
from cm.models import Text, TextVersion, Attachment, Comment, Configuration
from cm.security import has_global_perm
diff -r 8cb89c153140 -r f436e2fef076 src/cm/views/texts.py
--- a/src/cm/views/texts.py Fri Feb 05 18:43:58 2010 +0100
+++ b/src/cm/views/texts.py Tue Feb 09 22:20:08 2010 +0100
@@ -3,8 +3,6 @@
from cm.client import jsonize, get_filter_datas, edit_comment, remove_comment, \
add_comment, RequestComplexEncoder, comments_thread, own_notify
from cm.cm_settings import NEW_TEXT_VERSION_ON_EDIT
-from cm.diff import text_diff as _text_diff, text_history as inner_text_history, \
- get_colors
from cm.exception import UnauthorizedException
from cm.message import *
from cm.models import *
@@ -211,6 +209,19 @@
text.delete()
return HttpResponse('') # no redirect because this is called by js
+@has_perm_on_text('can_delete_text')
+def text_version_delete(request, key, text_version_key):
+ text_version = TextVersion.objects.get(key=text_version_key)
+ text=text_version.text
+ if request.method != 'POST':
+ raise UnauthorizedException('Unauthorized')
+ display_message(request, _(u'Text version %(text_version_title)s deleted') %{'text_version_title':text_version.title})
+ register_activity(request, "text_version_removed", text=text)
+ import pdb;pdb.set_trace()
+ text_version.delete()
+ return HttpResponse('') # no redirect because this is called by js
+
+
@has_perm_on_text('can_view_text') # only protected by text_view / comment filtering done in view
def text_view_comments(request, key, version_key=None, adminkey=None):
text = get_text_by_keys_or_404(key)
@@ -445,59 +456,61 @@
@has_perm_on_text('can_view_text')
-def text_history(request, key, v1_nid=None, v2_nid=None, adminkey=False):
- text = get_text_by_keys_or_404(key)
- text_versions = text.get_versions()
- author_colors = get_colors([t.get_name() for t in text.get_inversed_versions()])
-
- if v1_nid:
- v1_nid = int(v1_nid)
- else:
- v1_nid = text.get_versions_number()
-
- v1 = text.get_version(v1_nid)
+def text_history_version(request, key, version_key):
+ text = get_text_by_keys_or_404(key)
+ text_version = get_textversion_by_keys_or_404(version_key, key=key)
+ template_dict = {'text' : text,
+ 'text_version' : text_version,
+ 'embed_code' : embed_html(key, 'id="text_view_frame" name="text_view_frame"', version_key),
+ }
+ return render_to_response('site/text_history_version.html',
+ template_dict,
+ context_instance=RequestContext(request))
- v1_id = v1.id
-
- v2_id = None
- v2 = None
- if v2_nid:
- v2_nid = int(v2_nid)
- v2 = text.get_version(v2_nid)
- v2_id = v2.id
+@has_perm_on_text('can_view_text')
+def text_history_compare(request, key, v1_version_key, v2_version_key, mode=''):
+ text = get_text_by_keys_or_404(key)
+ v1 = get_textversion_by_keys_or_404(v1_version_key, key=key)
+ v2 = get_textversion_by_keys_or_404(v2_version_key, key=key)
+
+ content = get_uniffied_inner_diff_table(v1.content, v2.content)
+ if mode=='1':
+ # alternate diff
+ from cm.utils.diff import text_diff
+ content = text_diff(v1.get_content(), v2.get_content())
- versions = text.get_inversed_versions()
- paired_versions = []
- colors_dict = dict(author_colors)
- for index in range(len(versions)):
- vv1 = versions[index]
- if index + 1 < len(versions):
- vv2 = versions[index + 1]
- else:
- vv2 = None
- paired_versions.append((vv1, vv2, colors_dict.get(vv1.get_name(), '#D9D9D9')))
+ template_dict = {
+ 'text' : text,
+ 'v1': v1,
+ 'v2': v2,
+ 'content' : content.strip(),
+ 'empty' : ''==content,
+ }
+ return render_to_response('site/text_history_compare.html',
+ template_dict,
+ context_instance=RequestContext(request))
+
+@has_perm_on_text('can_view_text')
+def text_history(request, key):
+ text = get_text_by_keys_or_404(key)
+
+ if request.method == 'POST':
+ v1_key = request.POST.get('newkey',None)
+ v2_key = request.POST.get('oldkey',None)
+ if v1_key and v2_key:
+ return redirect(request, 'text-history-compare', args=[text.key, v2_key, v1_key ])
+
+ text_versions = text.get_versions()
+ paginate_by = get_int(request.GET,'paginate',TEXT_PAGINATION)
- embed_code = ""
- content = ""
- if v1_nid and not v2_nid:
- embed_code = embed_html(key, 'id="text_view_frame" name="text_view_frame"', v1.key)
- else:
- content = get_uniffied_inner_diff_table(v1.content, v2.content)
-
- template_dict = {'paired_versions' : paired_versions,
- 'text' : text,
- 'v1_nid' : v1_nid,
- 'v2_nid' : v2_nid,
- 'v1_id' : v1_id,
- 'v2_id' : v2_id,
- 'version1': v1,
- 'version2': v2,
- 'content' : content.strip(),
- 'embed_code':embed_code,
- 'is_diff' : content !='',
- 'author_colors' : author_colors,
- }
- return render_to_response('site/text_history.html', template_dict, context_instance=RequestContext(request))
+ last_last_version = text_versions[1] if len(text_versions)>1 else None
+ context = {'text':text, 'last_version':text.last_text_version, 'last_last_version':last_last_version}
+ return object_list(request, text_versions,
+ template_name = 'site/text_history.html',
+ paginate_by = paginate_by,
+ extra_context=context,
+ )
+
# taken from trac
def _get_change_extent(str1, str2):
@@ -558,22 +571,6 @@
res.append('
')
return ''.join(res)
-@has_perm_on_text('can_view_text')
-def text_history_compare(request, key, v1_nid=None, v2_nid=None, adminkey=None):
- text = get_text_by_keys_or_404(key)
-
- vis_diff = difflib.HtmlDiff()
- v1 = text.get_version(int(v1_nid))
- v2 = text.get_version(int(v2_nid))
- content = _text_diff(v2.get_content(), v1.get_content())
- #content = vis_diff.make_table(v1.content.split('\n'), v2.content.split('\n'), v1_nid, v2_nid, context=None)
-
- template_dict = {
- 'text' : text,
- 'content' : content,
- }
- return render_to_response('site/text_history_compare.html', template_dict, context_instance=RequestContext(request))
-
#def text_history_version(request, key):
# text = get_text_by_keys_or_404(key=key)
# return _text_history_version(request, text)
@@ -586,32 +583,11 @@
# template_dict['admin'] = True
# return render_to_response('site/text_history.html', template_dict, context_instance=RequestContext(request))
#
-#def _text_history_version(request, text, admin=False):
-# pass
-#
class TextVersionForm(ModelForm):
class Meta:
model = TextVersion
fields = ('title', 'content', 'format')
-@has_perm_on_text('can_view_text')
-def text_diff(request, key, id_v1, id_v2):
- text = get_text_by_keys_or_404(key)
- text_version_1 = TextVersion.objects.get(pk=id_v1)
- text_version_2 = TextVersion.objects.get(pk=id_v2)
- content = _text_diff(text_version_1.get_content(), text_version_2.get_content())
- title = _text_diff(text_version_1.title, text_version_2.title)
- return render_to_response('site/text_view.html', {'text' : text, 'text_version_1' : text_version_1, 'text_version_2' : text_version_2, 'title' : title, 'content' : content}, context_instance=RequestContext(request))
-
-# commented out, unused suspected
-#@has_perm_on_text('can_view_text')
-#def text_version(request, key, id_version):
-# text = get_text_by_keys_or_404(key)
-# text_version = TextVersion.objects.get(pk=id_version)
-# # TODO : assert text_v in text ...
-# # TODO : do not use db id
-# return render_to_response('site/text_view.html', {'text' : text, 'text_version' : text_version, 'title' : text_version.title, 'content' : text_version.get_content()}, context_instance=RequestContext(request))
-
class EditTextForm(ModelForm):
title = forms.CharField(label=_("Title"), widget=forms.TextInput)
#format = forms.CharField(label=_("Format"))
@@ -749,11 +725,11 @@
# TODO: modif de la base => if POST
@has_perm_on_text('can_edit_text')
-def text_revert(request, key, v1_nid, adminkey=None):
+def text_revert(request, key, text_version_key):
text = get_text_by_keys_or_404(key)
- text.revert_to_version(v1_nid)
- display_message(request, _(u'A new version (copied from version %(version_id)s) has been created') % {'version_id':v1_nid})
+ text_version = text.revert_to_version(text_version_key)
+ display_message(request, _(u'A new version (copied from version %(version_title)s) has been created') % {'version_title':text_version.title})
return HttpResponseRedirect(reverse('text-history', args=[text.key]))