diff -r 000000000000 -r 40c8f766c9b8 src/cm/views/texts.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cm/views/texts.py Mon Nov 23 15:14:29 2009 +0100 @@ -0,0 +1,850 @@ +from cm.activity import register_activity +from cm.client import jsonize, get_filter_datas, edit_comment, remove_comment, \ + add_comment, RequestComplexEncoder, comments_thread +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 * +from cm.models_base import generate_key +from cm.security import get_texts_with_perm, has_perm, get_viewable_comments, \ + has_perm_on_text +from cm.utils import get_among, get_among, get_int +from cm.utils.comment_positioning import compute_new_comment_positions, \ + insert_comment_markers +from cm.utils.html import cleanup_textarea +from cm.utils.spannifier import spannify +from cm.views import get_keys_from_dict, get_text_by_keys_or_404, redirect +from cm.views.export import content_export2 +from cm.views.user import AnonUserRoleForm, cm_login +from difflib import unified_diff +from django import forms +from django.conf import settings +from django.contrib.auth import login as django_login +from django.contrib.auth.forms import AuthenticationForm +from django.contrib.auth.models import User +from django.core.urlresolvers import reverse +from django.db.models import Q +from django.forms import ModelForm +from django.forms.models import BaseModelFormSet, modelformset_factory +from django.http import HttpResponse, HttpResponseRedirect, Http404 +from django.shortcuts import render_to_response +from django.template import RequestContext +from django.template.loader import render_to_string +from django.utils.translation import ugettext as _, ugettext_lazy +from django.views.generic.list_detail import object_list +import difflib +import logging +import mimetypes +import simplejson +import sys + + +def get_text_and_admin(key, adminkey, assert_admin = False): + """ + assert_admin => redirect to unauthorized if not admin + """ + admin = False + if adminkey: + text = Text.objects.get(key = key, adminkey = adminkey) + if text: + admin = True + else: + text = Text.objects.get(key=key) + if assert_admin and not admin: + raise UnauthorizedException('Is not admin') + return text, admin + + + +ACTIVITY_PAGINATION = 10 +RECENT_TEXT_NB = 5 +RECENT_COMMENT_NB = RECENT_TEXT_NB + +MODERATE_NB = 5 + +def dashboard(request): + request.session.set_test_cookie() + if request.user.is_authenticated(): + act_view = { + 'view_texts' : get_int(request.GET,'view_texts',1), + 'view_comments' : get_int(request.GET,'view_comments',1), + 'view_users' : get_int(request.GET,'view_users',1), + } + + paginate_by = get_int(request.GET,'paginate',ACTIVITY_PAGINATION) + + # texts with can_view_unapproved_comment perms + moderator_texts = get_texts_with_perm(request, 'can_view_unapproved_comment') + viewer_texts = get_texts_with_perm(request, 'can_view_approved_comment') + all_texts_ids = [t.id for t in moderator_texts] + [t.id for t in viewer_texts] + + span = get_among(request.GET,'span',('day','month','week',),'week') + template_dict = { + 'span' : span, + 'last_texts' : get_texts_with_perm(request, 'can_view_text').order_by('-modified')[:RECENT_TEXT_NB], + 'last_comments' : Comment.objects.filter(text_version__text__in=all_texts_ids).order_by('-created')[:RECENT_COMMENT_NB],# TODO: useful? + #'last_users' : User.objects.all().order_by('-date_joined')[:5], + } + template_dict.update(act_view) + + all_activities = { + 'view_comments' : ['comment_created','comment_removed'], + 'view_users' : ['user_created', 'user_activated', 'user_suspended','user_enabled',], + 'view_texts' : ['text_created','text_removed', 'text_edited', 'text_edited_new_version'], + } + + selected_activities = [] + [selected_activities.extend(all_activities[k]) for k in act_view.keys() if act_view[k]] + + activities = Activity.objects.filter(type__in = selected_activities) + if not has_perm(request,'can_manage_workspace'): + texts = get_texts_with_perm(request, 'can_view_text') + activities = activities.filter(Q(text__in=texts)) + + comments = [] + [comments.extend(get_viewable_comments(request, t.last_text_version.comment_set.all(), t)) for t in texts] + + activities = activities.filter(Q(comment__in=comments) | Q(comment=None) ) + template_dict['to_mod_profiles'] = [] + else: + template_dict['to_mod_profiles'] = UserProfile.objects.filter(user__is_active=False).filter(is_suspended=True).order_by('-user__date_joined')[:MODERATE_NB] + template_dict['to_mod_comments'] = Comment.objects.filter(state='pending').filter(text_version__text__in=moderator_texts).order_by('-modified')[:MODERATE_NB-len(template_dict['to_mod_profiles'])] + + activities = activities.order_by('-created') + return object_list(request, activities, + template_name = 'site/dashboard.html', + paginate_by = paginate_by, + extra_context = template_dict, + ) + + else: + if request.method == 'POST': + form = AuthenticationForm(request, request.POST) + if form.is_valid(): + user = form.get_user() + user.backend = 'django.contrib.auth.backends.ModelBackend' + cm_login(request, user) + display_message(request, _(u"You're logged in!")) + return HttpResponseRedirect(reverse('index')) + else: + form = AuthenticationForm() + + + public_texts = get_texts_with_perm(request, 'can_view_text').order_by('-modified') + + template_dict = { + 'form' : form, + 'texts' : public_texts, + } + return render_to_response('site/non_authenticated_index.html', template_dict, context_instance=RequestContext(request)) + +TEXT_PAGINATION = 10 +# security check inside view +# TODO: set global access perm +def text_list(request): + paginate_by = get_int(request.GET,'paginate',TEXT_PAGINATION) + + order_by = get_among(request.GET,'order',('title','author','modified','-title','-author','-modified'),'-modified') + + if request.method == 'POST': + action = request.POST.get('action',None) + text_keys = get_keys_from_dict(request.POST, 'check-').keys() + if action == 'delete': + for text_key in text_keys: + text = Text.objects.get(key=text_key) + if has_perm(request, 'can_delete_text', text=text): + text.delete() + else: + raise UnauthorizedException('No perm can_delete_text on comment') + display_message(request, _(u'%(nb_texts)i text(s) deleted') %{'nb_texts':len(text_keys)}) + return HttpResponseRedirect(reverse('text')) + + texts = get_texts_with_perm(request, 'can_view_text').order_by(order_by) + return object_list(request, texts, + template_name = 'site/text_list.html', + paginate_by = paginate_by, + ) + +@has_perm_on_text('can_view_text') +def text_view(request, key, adminkey=None): + + text = get_text_by_keys_or_404(key) + register_activity(request, "text_view", text=text) + + text_version = text.get_latest_version() + template_dict = { 'text' : text, 'text_version' : text_version, 'title' : text_version.title, 'content' : text_version.get_content()} + return render_to_response('site/text_view.html', template_dict, context_instance=RequestContext(request)) + +@has_perm_on_text('can_delete_text') +def text_delete(request, key): + text = Text.objects.get(key=key) + if request.method != 'POST': + raise UnauthorizedException('Unauthorized') + display_message(request, _(u'Text %(text_title)s deleted') %{'text_title':text.title}) + register_activity(request, "text_removed", text=text) + text.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, adminkey=None): + text = get_text_by_keys_or_404(key) + #TODO: stupid why restrict to latest ? + text_version = text.get_latest_version() + comments = get_viewable_comments(request, text_version.comment_set.filter(reply_to__isnull=True),text) + filter_datas = get_filter_datas(request, text_version, text) + + get_params = simplejson.dumps(request.GET) + + wrapped_text_version, _ , _ = spannify(text_version.get_content()) + template_dict = {'text' : text, + 'text_version' : text_version, + 'title' : text_version.title, # TODO use it ... + 'get_params' : get_params, + 'json_comments':jsonize(comments, request), + 'json_filter_datas':jsonize(filter_datas, request), + 'content' : wrapped_text_version, + 'client_date_fmt' : settings.CLIENT_DATE_FMT, + } + return render_to_response('site/text_view_comments.html', + template_dict, + context_instance=RequestContext(request)) +def client_exchange(request): + ret = None + function_name = request.POST['fun']# function called from client + user = request.user + if function_name == 'experiment' : + ret = experiment() + elif function_name == 'warn' : +# TODO: (RBE to RBA) send mail withinfos + ret = "warn test" + #print request.POST + elif request.POST: + key = request.POST['key'] + + text = Text.objects.get(key=key) ; + #TODO: stupid why restrict to latest ? + text_version = text.get_latest_version() + + if (text != None) : + if function_name in ('editComment', 'addComment', 'removeComment',) : + if function_name == 'editComment' : + ret = edit_comment(request=request, key=key, comment_key=request.POST['comment_key']) + elif function_name == 'addComment' : + ret = add_comment(request=request, key=key) + elif function_name == 'removeComment' : + ret = remove_comment(request=request, key=key, comment_key=request.POST['comment_key']) + + ret['filterData'] = get_filter_datas(request, text_version, text) + #ret['tagCloud'] = get_tagcloud(key) + if ret : + if type(ret) != HttpResponseRedirect : + ret = HttpResponse(simplejson.dumps(ret, cls=RequestComplexEncoder, request=request)) + else : + ret = HttpResponse(simplejson.dumps({})) + ret.status_code = 403 + + return ret + + +#NOTE : some arguments like : withcolor = "yes" + format = "markdown" are incompatible +#http://localhost:8000/text/text_key_1/export/pdf/1/all/1 +def text_export(request, key, format, download, whichcomments, withcolor, adminkey=None): + text, admin = get_text_and_admin(key, adminkey) + text_version = text.get_latest_version() + original_content = text_version.content + original_format = text_version.format # BD : html or markdown for now ... + + download_response = download == "1" + with_color = withcolor == "1" + + comments = [] # whichcomments=="none" + + if whichcomments == "filtered" or whichcomments == "all": + #comments = text_version.comment_set.filter(reply_to__isnull=True)# whichcomments=="all" + #comments = get_viewable_comments(request, text_version.comment_set.filter(reply_to__isnull=True), text, order_by=('start_wrapper','start_offset','end_wrapper','end_offset'))# whichcomments=="all" + comments = get_viewable_comments(request, text_version.comment_set.all(), text, order_by=('start_wrapper','start_offset','end_wrapper','end_offset'))# whichcomments=="all" + + if whichcomments == "filtered" : + if request.method == 'POST' : + ll = request.POST.get('filteredIds',[]).split(",") + filteredIds = [ int(l) for l in ll if l] + comments = comments.filter(id__in=filteredIds) # security ! TODO CROSS PERMISSIONS WITH POST CONTENT + else : + comments = [] + + if len(comments) == 0 : #want to bypass html conversion in this case + return content_export2(request, original_content, text_version.title, original_format, format, False, download_response) + else : # case comments to be added + #comments = comments.order_by('start_wrapper','start_offset','end_wrapper','end_offset') + html = text_version.get_content() + wrapped_text_version, _ , _ = spannify(html) + with_markers = True + marked_content = insert_comment_markers(wrapped_text_version, comments, with_markers, with_color) + + viewable_comments = comments_thread(request, text_version, text) +# viewable_commentsnoreply = get_viewable_comments(request, commentsnoreply, text, order_by = ('start_wrapper','start_offset','end_wrapper','end_offset')) +# viewable_comments = [] +# for cc in viewable_commentsnoreply : +# viewable_comments += list_viewable_comments(request, [cc], text) + + # numerotation{ id --> numbered as a child} + extended_comments = {} + nb_children = {} + for cc in viewable_comments : + id = 0 #<-- all top comments are children of comment with id 0 + if cc.is_reply() : + id = cc.reply_to_id + + nb_children[id] = nb_children.get(id, 0) + 1 + + cc.num = "%d"%nb_children[id] + + extended_comments[cc.id] = cc + + if cc.is_reply() : + cc.num = "%s.%s"%(extended_comments[cc.reply_to_id].num, cc.num) + +# viewable_comments += list_viewable_comments(request, viewable_commentsnoreply, text) + html_comments=render_to_string('site/macros/text_comments.html',{'comments':viewable_comments }, context_instance=RequestContext(request)) + + content = "%s%s"%(marked_content, html_comments) + content_format = "html" + # impossible to satisfy because of color then no colors instead: + if with_color and format in ('markdown', 'tex') : #TODO : add S5 + with_color = False + + # decide to use pandoc or not + if with_color : + use_pandoc = False # pandoc wouldn't preserve comments scope background colors + else : + if format in ('markdown', 'tex') : + use_pandoc = True + elif format in ('pdf', 'odt') : + use_pandoc = (original_format == "markdown") + elif format in ('doc', 'html') : + use_pandoc = False + + return content_export2(request, content, text_version.title, content_format, format, use_pandoc, download_response) + +def text_print(request, key, adminkey=None): + text, admin = get_text_and_admin(key, adminkey) + + text_version = text.get_latest_version() + +# chosen default url behaviour is export all comments + bckcolor + pdf + comments = Comment.objects.filter(text_version=text_version, reply_to__isnull=True) + + with_markers = True + with_colors = True + download_requested = True + action = 'export' # client origine dialog + requested_format = 'pdf' # requested output format + + if request.method == 'POST': + # colors or not ? + with_colors = (u'yes' == request.POST.get('p_color', u'no')) + + # restrict comments to ones that should be exported / printed + p_comments = request.POST.get('p_comments') + if p_comments == "filtered" or p_comments == "none" : + filteredIds = [] # "none" case + if p_comments == "filtered" : + ll = request.POST.get('filteredIds').split(",") + filteredIds = [ int(l) for l in ll if l] + + comments = comments.filter(id__in=filteredIds) + + # print or export ? + action = request.POST.get('print_export_action') + requested_format = request.POST.get('p_method') + + comments = comments.order_by('start_wrapper','start_offset','end_wrapper','end_offset') + + download_requested = (action == 'export') or (action == 'print' and requested_format != 'html') + + ori_format = text_version.format # BD : html or markdown for now ... + src_format = ori_format # as expected by convert functions ... + src = text_version.content + + if len(comments) > 0 and (with_markers or with_colors) : + html = text_version.get_content() + wrapped_text_version, _ , _ = spannify(html) + marked_text_version = insert_comment_markers(wrapped_text_version, comments, with_markers, with_colors) + + src_format = 'html' + src = marked_text_version + html_comments=render_to_string('site/macros/text_comments.html',{'comments':comments}, context_instance=RequestContext(request)) + src += html_comments + + if download_requested : + use_pandoc = (requested_format == 'html' or requested_format == 'markdown') + return content_export(request, src, text_version.title, src_format, requested_format, use_pandoc) + else : # action == 'print' and requested_format == 'html' (no colors) + template_dict = {'text' : text, + 'text_version' : text_version, + 'title' : text_version.title, # TODO use it ... + 'comments': comments, + 'content' : marked_text_version, + 'client_date_fmt' : settings.CLIENT_DATE_FMT + } + if admin: + template_dict['adminkey'] = text.adminkey + template_dict['admin'] = True + return render_to_response('site/text_print.html', + template_dict, + context_instance=RequestContext(request)) + +@has_perm_on_text('can_view_text') +def text_view_frame(request, key, adminkey=None): + text = get_text_by_keys_or_404(key) + + text_version = text.get_latest_version() + template_dict = {'text' : text} + return render_to_response('site/text_view_frame.html', + template_dict, + context_instance=RequestContext(request)) + + +@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) + + 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 + + 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'))) + + if v1_nid and not v2_nid: + content = v1.get_content() + else: + content = get_uniffied_inner_diff_table(cleanup_textarea(v1.content), cleanup_textarea(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, + 'author_colors' : author_colors, + } + return render_to_response('site/text_history.html', template_dict, context_instance=RequestContext(request)) + +# taken from trac +def _get_change_extent(str1, str2): + """ + Determines the extent of differences between two strings. Returns a tuple + containing the offset at which the changes start, and the negative offset + at which the changes end. If the two strings have neither a common prefix + nor a common suffix, (0, 0) is returned. + """ + start = 0 + limit = min(len(str1), len(str2)) + while start < limit and str1[start] == str2[start]: + start += 1 + end = -1 + limit = limit - start + while - end <= limit and str1[end] == str2[end]: + end -= 1 + return (start, end + 1) + +def diff_decorate(minus, plus): + return minus, plus + +def get_uniffied_inner_diff_table(text1, text2): + """ + Return the inner of the html table for text1 vs text2 diff + """ + gen = unified_diff(text1.split('\n'), text2.split('\n'), n=3) + index = 0 + res = [''] + #res.append('') + + for g in gen: + if index > 1: + col_in = None + if g.startswith('@@'): + line_number = g.split(' ')[1][1:].split(',')[0] + if index != 2: + res.append(' ') + res.append('' % (line_number, line_number)) + if g.startswith(' '): + res.append('' % (g, g)) + if g.startswith('-') or g.startswith('+'): + plus = [] + minus = [] + while g.startswith('-') or g.startswith('+'): + if g.startswith('-'): + minus.append(g[1:]) + else: + plus.append(g[1:]) + try: + g = gen.next() + except StopIteration: + break + minus, plus = diff_decorate(minus, plus) + res.append('' % ('
'.join(minus), '
'.join(plus))) + + index += 1 + res.append('
 
Line %sLine %s
%s%s
-%s+%s
') + 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) +# +#def text_history_version_admin(request, key, adminkey): +# text = get_text_by_keys_or_404(key=key, adminkey=adminkey) +# return _text_history_version(request, text, True) +# if admin: +# template_dict['adminkey'] = text.adminkey +# 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)) + + +@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")) + #content = forms.TextField(label=_("Content")) + + note = forms.CharField(label=_("Note (optional)"), + widget=forms.TextInput, + required=False, + help_text=_("Add a note to explain the modifications made to the text") + ) + + #tags = forms.CharField(label=_("Tags (optional)"), + # widget=forms.TextInput, + # required=False, + # #help_text=_("Add a note to explain the modifications made to the text") + # ) + + + new_version = forms.BooleanField(label=_("New version (optional)"), + required=False, + initial=True, + help_text=_("Create a new version of this text (recommended)") + ) + + keep_comments = forms.BooleanField(label=_("Keep comments (optional)"), + required=False, + initial=True, + help_text=_("Keep comments (if not affected by the edit)") + ) + + class Meta: + model = TextVersion + fields = ('title', 'format', 'content', 'new_version', 'tags', 'note') + + def save_into_text(self, text, request): + new_content = request.POST.get('content') + new_title = request.POST.get('title') + new_format = request.POST.get('format') + new_note = request.POST.get('note',None) + new_tags = request.POST.get('tags',None) + version = text.get_latest_version() + version.edit(new_title, new_format, new_content, new_tags, new_note, True) + + return version + + def save_new_version(self, text, request): + new_text_version = TextVersion.objects.duplicate(text.get_latest_version(), True) + new_text_version.user = request.user if request.user.is_authenticated() else None + new_text_version.note = request.POST.get('note','') + new_text_version.email = request.POST.get('email','') + new_text_version.name = request.POST.get('name','') + new_text_version.save() + + new_content = request.POST.get('content') + new_title = request.POST.get('title') + new_format = request.POST.get('format') + new_note = request.POST.get('note',None) + new_tags = request.POST.get('tags',None) + new_text_version.edit(new_title, new_format, new_content, new_tags, new_note, True) + + return new_text_version + +@has_perm_on_text('can_edit_text') +def text_pre_edit(request, key, adminkey=None): + text = get_text_by_keys_or_404(key) + + text_version = text.get_latest_version() + comments = text_version.get_comments() ; + new_content = request.POST['new_content'] + new_format = request.POST['new_format'] + + # TODO: RBE : si commentaire mal forme : (position non existante : boom par key error) + _tomodify_comments, toremove_comments = compute_new_comment_positions(text_version.content, text_version.format, new_content, new_format, comments) + return HttpResponse(simplejson.dumps({'nb_removed' : len(toremove_comments) })) + +class EditTextFormAnon(EditTextForm): + name = forms.CharField(label=ugettext_lazy("Name (optional)"), widget=forms.TextInput, required=False) + email = forms.EmailField(label=ugettext_lazy("Email (optional)"), required=False) + content = forms.CharField(label=ugettext_lazy("Content"), required=True, widget=forms.Textarea(attrs={'rows':'30', 'cols': '70'})) + + class Meta: + model = TextVersion + fields = ('title', 'format', 'content', 'tags', 'note', 'name', 'email') + +@has_perm_on_text('can_edit_text') +def text_edit(request, key, adminkey=None): + text = get_text_by_keys_or_404(key) + text_version = text.get_latest_version() + if request.method == 'POST': + if request.user.is_authenticated(): + form = EditTextForm(request.POST) + else: + form = EditTextFormAnon(request.POST) + + if form.is_valid(): + if request.POST.get('new_version'): + new_version = form.save_new_version(text, request) + register_activity(request, "text_edited_new_version", text=text, text_version=new_version) + else: + form.save_into_text(text, request) + register_activity(request, "text_edited", text=text) + return redirect(request, 'text-view', args=[text.key]) + else: + default_data = { + 'content': text_version.content, + 'title': text_version.title, + 'format': text_version.format, + 'tags': text_version.tags, + 'new_version': NEW_TEXT_VERSION_ON_EDIT, + 'note' : text_version.note, + 'keep_comments' : True, + } + if request.user.is_authenticated(): + form = EditTextForm(default_data) + else: + form = EditTextFormAnon(default_data) + + template_dict = {'text' : text, 'form' : form} + + return render_to_response('site/text_edit.html', template_dict , context_instance=RequestContext(request)) + +# TODO: modif de la base => if POST +@has_perm_on_text('can_edit_text') +def text_revert(request, key, v1_nid, adminkey=None): + 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}) + + return HttpResponseRedirect(reverse('text-history', args=[text.key])) + +@has_perm_on_text('can_view_text') +def text_attach(request, key, attach_key): + attach = Attachment.objects.get(key=attach_key, text_version__text__key=key) + content = file(attach.data.path).read() + mimetype, _encoding = mimetypes.guess_type(attach.data.path) + response = HttpResponse(content, mimetype) + return response + + + + +def fix_anon_in_formset(formset): + # fix role choice in formset for anon (not all role are allowed) + role_field = [f.fields['role'] for f in formset.forms if f.instance.user == None][0] + role_field.choices = [(u'', u'---------')] + [(r.id, str(r)) for r in Role.objects.filter(anon = True)] # limit anon choices + +class BaseUserRoleFormSet(BaseModelFormSet): + def clean(self): + """Checks that anon users are given roles with anon=True.""" + for i in range(0, self.total_form_count()): + form = self.forms[i] + print form.cleaned_data + user_role = form.cleaned_data['id'] + if user_role.user == None: + role = form.cleaned_data['role'] + if not role.anon: + # nasty stuff: cannot happen so not dealt with in tempate + logging.warn('Cannot give such role to anon user.') + raise forms.ValidationError, "Cannot give such role to anon user." + +#@has_perm_on_text('can_manage_text') +#def xtext_share(request, key): +# text = get_text_by_keys_or_404(key) +# order_by = get_among(request.GET,'order',('user__username','-user__username','role__name','-role__name'),'user__username') +# +# UserRole.objects.create_userroles_text(text) +# UserRoleFormSet = modelformset_factory(UserRole, fields=('role', ), extra=0, formset = BaseUserRoleFormSet) +# +# # put anon users on top no matter what the order says (TODO: ?) +# userrole_queryset = UserRole.objects.filter(text=text).extra(select={'anon':'"cm_userrole"."user_id">-1'}).order_by('-anon',order_by) +# if request.method == 'POST': +# formset = UserRoleFormSet(request.POST, queryset = userrole_queryset) +# +# if formset.is_valid(): +# formset.save() +# display_message(request, "Sharing updated.") +# return HttpResponseRedirect(reverse('text-share',args=[text.key])) +# +# else: +# formset = UserRoleFormSet(queryset = userrole_queryset) +# fix_anon_in_formset(formset) +# +# global_anon_userrole = UserRole.objects.get(text=None, user=None) +# return render_to_response('site/text_share.html', {'text' : text, +# 'formset' : formset, +# 'global_anon_userrole' : global_anon_userrole, +# } , context_instance=RequestContext(request)) + +# TODO: permission protection ? format value check ? +def text_wysiwyg_preview(request, format): + html_content = "" + if request.POST : # if satisfied in the no html case, in html case : no POST (cf. markitup) previewTemplatePath and previewParserPath + html_content = pandoc_convert(request.POST['data'], format, "html", full=False) + + return render_to_response('site/wysiwyg_preview.html', {'content':html_content} , context_instance=RequestContext(request)) + #return HttpResponse(pandoc_convert(content, format, "html", full=False)) + +@has_perm_on_text('can_manage_text') +def text_share(request, key): + text = get_text_by_keys_or_404(key) + order_by = get_among(request.GET,'order',('user__username', + 'user__email', + '-user__username', + '-user__email', + 'role__name', + '-role__name', + ), + 'user__username') + paginate_by = 10 + + UserRole.objects.create_userroles_text(text) + + if request.method == 'POST': + if 'save' in request.POST: + user_profile_keys_roles = get_keys_from_dict(request.POST, 'user-role-') + count = 0 + for user_profile_key in user_profile_keys_roles: + role_id = user_profile_keys_roles[user_profile_key] + if not user_profile_key: + user_role = UserRole.objects.get(user = None, text = text) + else: + user_role = UserRole.objects.get(user__userprofile__key = user_profile_key, text = text) + if (role_id != u'' or user_role.role_id!=None) and role_id!=unicode(user_role.role_id): + if role_id: + user_role.role_id = int(role_id) + else: + user_role.role_id = None + user_role.save() + count += 1 + display_message(request, _(u'%(count)i user(s) role modified') %{'count':count}) + return HttpResponseRedirect(reverse('text-share', args=[text.key])) + + anon_role = UserRole.objects.get(user = None, text = text).role + global_anon_role = UserRole.objects.get(user = None, text = None).role + + context = { + 'anon_role' : anon_role, + 'global_anon_role' : global_anon_role, + 'all_roles' : Role.objects.all(), + 'anon_roles' : Role.objects.filter(anon = True), + 'text' : text, + } + + return object_list(request, UserRole.objects.filter(text=text).filter(~Q(user=None)).order_by(order_by), + template_name = 'site/text_share.html', + paginate_by = paginate_by, + extra_context = context, + ) + + +class SettingsTextForm(ModelForm): + # example name = forms.CharField(label=_("Name (optional)"), widget=forms.TextInput, required=False) + + class Meta: + model = TextVersion + fields = ('mod_posteriori',) + +@has_perm_on_text('can_manage_text') +def text_settings(request, key): + text = get_text_by_keys_or_404(key) + + text_version = text.get_latest_version() + if request.method == 'POST': + form = SettingsTextForm(request.POST, instance = text_version) + + if form.is_valid(): + form.save() + display_message(request, _(u'Text settings updated')) + return redirect(request, 'text-view', args=[text.key]) + else: + form = SettingsTextForm(instance = text_version) + + template_dict = {'text' : text, 'form' : form} + + return render_to_response('site/text_settings.html', template_dict , context_instance=RequestContext(request)) +