diff -r a69421197502 -r 2fdb7d095d5c src/cm/views/create.py --- a/src/cm/views/create.py Thu Aug 09 16:55:25 2012 +0200 +++ b/src/cm/views/create.py Fri Aug 10 16:12:29 2012 +0200 @@ -1,7 +1,7 @@ from cm.cm_settings import VALID_EMAIL_FOR_PUBLISH, SITE_NAME from cm.converters import convert_from_mimetype from cm.converters.pandoc_converters import pandoc_convert -from cm.models import Text, TextVersion, Attachment +from cm.models import Text, TextVersion, Comment, Attachment from cm.utils.files import remove_extension from cm.utils.mail import EmailMessage from cm.views import get_text_by_keys_or_404 @@ -16,10 +16,14 @@ from django.template import RequestContext from django.template.loader import render_to_string from django.utils.translation import ugettext as _, ugettext_lazy +from django.db import connection, transaction from mimetypes import guess_type from cm.activity import register_activity from cm.security import has_global_perm import os +from BeautifulSoup import BeautifulStoneSoup +import re +from base64 import b64decode class CreateTextUploadForm(ModelForm): file = forms.FileField(required=False, @@ -40,6 +44,41 @@ return cleaned_data +class CreateTextImportForm(ModelForm): + file = forms.FileField(required=True, + label=ugettext_lazy("Upload XML file"), + help_text=ugettext_lazy("Upload a previously exported XML file from your computer"),) + + class Meta: + model = TextVersion + fields = () + + def clean(self): + cleaned_data = self.cleaned_data + if not cleaned_data.get('file', None) : + msg = _("You should specify a file to upload.") + self._errors["file"] = ErrorList([msg]) + return cleaned_data + + uploaded_file = self.cleaned_data['file'] + if (uploaded_file.content_type != 'text/xml'): + msg = _("The imported file should be an XML file generated by co-ment when exporting a text and comments.") + self._errors["file"] = ErrorList([msg]) + return cleaned_data + + soup = BeautifulStoneSoup(uploaded_file) + if not soup.co_ment_text: + msg = _("No co_ment_text node found in XML.") + self._errors["file"] = ErrorList([msg]) + return cleaned_data + for mandatory_child in ['title', 'created', 'modified', 'name', 'email', 'format', 'content']: + if not getattr(soup.co_ment_text, mandatory_child): + msg = _('No %(tag)s node found in XML.' %{"tag":mandatory_child}) + self._errors["file"] = ErrorList([msg]) + return cleaned_data + cleaned_data['soup'] = soup + return cleaned_data + class CreateTextContentForm(ModelForm): title = forms.CharField(required=True, label=ugettext_lazy("Title"), @@ -59,7 +98,6 @@ return HttpResponseRedirect(reverse('text-view', args=[text.key])) def _text_create_content(request, createForm): -# CreateForm = CreateTextContentForm document = "" if request.method == 'POST': @@ -77,8 +115,6 @@ def _text_create_upload(request, createForm): -# CreateForm = CreateTextUploadForm if request.user.is_authenticated() else CreateTextUploadFormAnon - if request.method == 'POST': form = createForm(request.POST, request.FILES) if form.is_valid(): @@ -113,11 +149,115 @@ return None, render_to_response('site/text_create_upload.html', {'form' : form}, context_instance=RequestContext(request)) +def _text_create_import(request, createForm): + if request.method == 'POST': + form = createForm(request.POST, request.FILES) + if form.is_valid(): + soup = form.cleaned_data['soup'] + if not soup.co_ment_text: + raise Exception('Bad Soup') + + # Process attachments first to create new keys. + attachments_keys_map = {} + if soup.co_ment_text.attachments: + for imported_attachement in soup.co_ment_text.attachments.findAll('attachment'): + # Creates attachment object. + filename = 'imported_attachment' + attachment = Attachment.objects.create_attachment(filename=filename, data=b64decode(imported_attachement.data.renderContents()), text_version=None) + # Stores key mapping. + attachments_keys_map[imported_attachement.key.renderContents()] = attachment.key + + # Process text. + form.cleaned_data['title'] = soup.co_ment_text.title.renderContents() + form.cleaned_data['format'] = soup.co_ment_text.format.renderContents() + form.cleaned_data['content'] = re.sub(r'^$', '', soup.co_ment_text.content.renderContents()) + form.cleaned_data['name'] = soup.co_ment_text.find('name').renderContents() + form.cleaned_data['email'] = soup.co_ment_text.email.renderContents() + if soup.co_ment_text.tags: + form.cleaned_data['tags'] = soup.co_ment_text.tags.renderContents() + + # Replaces attachements keys in content. + for old_key in attachments_keys_map.keys(): + form.cleaned_data['content'] = re.sub(old_key, attachments_keys_map[old_key], form.cleaned_data['content']) + + # Creates text. + text = create_text(request.user, form.cleaned_data) + + # Brute updates of dates (cannot do it through django models since fields are set with auto_now or auto_now_add). + created = soup.co_ment_text.created.renderContents() + modified = soup.co_ment_text.modified.renderContents() + cursor = connection.cursor() + cursor.execute("UPDATE cm_textversion SET created = %s, modified = %s WHERE id = %s", [created, modified, text.last_text_version_id]) + cursor.execute("UPDATE cm_text SET created = %s, modified = %s WHERE id = %s", [created, modified, text.id]) + transaction.commit_unless_managed() + + # Process comments. + if soup.co_ment_text.comments: + comments_ids_map = {} + for imported_comment in soup.co_ment_text.comments.findAll('comment'): + # Creates each comment. + comment = Comment.objects.create( + text_version=text.get_latest_version(), + title=imported_comment.title.renderContents(), + state=imported_comment.state.renderContents(), + name=imported_comment.find('name').renderContents(), + email=imported_comment.email.renderContents(), + format=imported_comment.format.renderContents(), + content=re.sub(r'^$', '', imported_comment.content.renderContents()), + content_html=re.sub(r'^$', '', imported_comment.content_html.renderContents()), + ) + + # Stores id for reply_to mapping. + comments_ids_map[imported_comment.id.renderContents()] = comment + + # Process boolean and potentially null integer/foreign key attributes. + save = False + if imported_comment.deleted.renderContents() == 'True': + comment.deleted = True + save = True + if imported_comment.start_wrapper.renderContents() != 'None': + comment.start_wrapper = imported_comment.start_wrapper.renderContents() + save = True + if imported_comment.end_wrapper.renderContents() != 'None': + comment.end_wrapper = imported_comment.end_wrapper.renderContents() + save = True + if imported_comment.start_offset.renderContents() != 'None': + comment.start_offset = imported_comment.start_offset.renderContents() + save = True + if imported_comment.end_offset.renderContents() != 'None': + comment.end_offset = imported_comment.end_offset.renderContents() + save = True + if imported_comment.find('parent'): + comment.reply_to = comments_ids_map.get(imported_comment.find('parent').renderContents()) + save = True + if save: + comment.save() + + # Brute updates of dates (cannot do it through django models since fields are set with auto_now or auto_now_add). + created=imported_comment.created.renderContents(), + modified=imported_comment.modified.renderContents(), + cursor.execute("UPDATE cm_comment SET created = %s, modified = %s WHERE id = %s", [created, modified, comment.id]) + transaction.commit_unless_managed() + + # Logs on activity. + register_activity(request, "text_imported", text) + display_message(request, _(u'Text "%(text_title)s" has been imported')%{"text_title":text.get_latest_version().title}) + return None, HttpResponseRedirect(reverse('text-view', args=[text.key])) + else: + form = createForm() + + return None, render_to_response('site/text_create_import.html', {'form' : form}, context_instance=RequestContext(request)) + + @has_global_perm('can_create_text') def text_create_upload(request): text, rep = _text_create_upload(request, CreateTextUploadForm) return rep +def text_create_import(request): + text, rep = _text_create_import(request, CreateTextImportForm) + return rep + def create_text(user, data): text = Text.objects.create_text(title=data['title'], format=data['format'], @@ -142,4 +282,4 @@ # save updated (attach links) text content text_version.content = text_content text_version.save() - return text \ No newline at end of file + return text