src/cm/views/create.py
changeset 460 2fdb7d095d5c
parent 216 6f635c8e98c3
child 463 9c7de6dd1723
--- 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'^<!\[CDATA\[|\]\]>$', '', 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'^<!\[CDATA\[|\]\]>$', '', imported_comment.content.renderContents()),
+              content_html=re.sub(r'^<!\[CDATA\[|\]\]>$', '', 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