14 from django.http import HttpResponse, HttpResponseRedirect |
14 from django.http import HttpResponse, HttpResponseRedirect |
15 from django.shortcuts import render_to_response |
15 from django.shortcuts import render_to_response |
16 from django.template import RequestContext |
16 from django.template import RequestContext |
17 from django.template.loader import render_to_string |
17 from django.template.loader import render_to_string |
18 from django.utils.translation import ugettext as _, ugettext_lazy |
18 from django.utils.translation import ugettext as _, ugettext_lazy |
|
19 from django.db import connection, transaction |
19 from mimetypes import guess_type |
20 from mimetypes import guess_type |
20 from cm.activity import register_activity |
21 from cm.activity import register_activity |
21 from cm.security import has_global_perm |
22 from cm.security import has_global_perm |
22 import os |
23 import os |
|
24 from BeautifulSoup import BeautifulStoneSoup |
|
25 import re |
|
26 from base64 import b64decode |
23 |
27 |
24 class CreateTextUploadForm(ModelForm): |
28 class CreateTextUploadForm(ModelForm): |
25 file = forms.FileField(required=False, |
29 file = forms.FileField(required=False, |
26 label=ugettext_lazy("Upload file (optional)"), |
30 label=ugettext_lazy("Upload file (optional)"), |
27 help_text=ugettext_lazy("Upload a file from your computer instead of using the direct input above"),) |
31 help_text=ugettext_lazy("Upload a file from your computer instead of using the direct input above"),) |
36 cleaned_data = self.cleaned_data |
40 cleaned_data = self.cleaned_data |
37 if not cleaned_data.get('file', None) : |
41 if not cleaned_data.get('file', None) : |
38 msg = _("You should specify a file to upload.") |
42 msg = _("You should specify a file to upload.") |
39 self._errors["file"] = ErrorList([msg]) |
43 self._errors["file"] = ErrorList([msg]) |
40 |
44 |
|
45 return cleaned_data |
|
46 |
|
47 class CreateTextImportForm(ModelForm): |
|
48 file = forms.FileField(required=True, |
|
49 label=ugettext_lazy("Upload XML file"), |
|
50 help_text=ugettext_lazy("Upload a previously exported XML file from your computer"),) |
|
51 |
|
52 class Meta: |
|
53 model = TextVersion |
|
54 fields = () |
|
55 |
|
56 def clean(self): |
|
57 cleaned_data = self.cleaned_data |
|
58 if not cleaned_data.get('file', None) : |
|
59 msg = _("You should specify a file to upload.") |
|
60 self._errors["file"] = ErrorList([msg]) |
|
61 return cleaned_data |
|
62 |
|
63 uploaded_file = self.cleaned_data['file'] |
|
64 if (uploaded_file.content_type != 'text/xml'): |
|
65 msg = _("The imported file should be an XML file generated by co-ment when exporting a text and comments.") |
|
66 self._errors["file"] = ErrorList([msg]) |
|
67 return cleaned_data |
|
68 |
|
69 soup = BeautifulStoneSoup(uploaded_file) |
|
70 if not soup.co_ment_text: |
|
71 msg = _("No co_ment_text node found in XML.") |
|
72 self._errors["file"] = ErrorList([msg]) |
|
73 return cleaned_data |
|
74 for mandatory_child in ['title', 'created', 'modified', 'name', 'email', 'format', 'content']: |
|
75 if not getattr(soup.co_ment_text, mandatory_child): |
|
76 msg = _('No %(tag)s node found in XML.' %{"tag":mandatory_child}) |
|
77 self._errors["file"] = ErrorList([msg]) |
|
78 return cleaned_data |
|
79 cleaned_data['soup'] = soup |
41 return cleaned_data |
80 return cleaned_data |
42 |
81 |
43 class CreateTextContentForm(ModelForm): |
82 class CreateTextContentForm(ModelForm): |
44 title = forms.CharField(required=True, |
83 title = forms.CharField(required=True, |
45 label=ugettext_lazy("Title"), |
84 label=ugettext_lazy("Title"), |
111 else: |
147 else: |
112 form = createForm() |
148 form = createForm() |
113 |
149 |
114 return None, render_to_response('site/text_create_upload.html', {'form' : form}, context_instance=RequestContext(request)) |
150 return None, render_to_response('site/text_create_upload.html', {'form' : form}, context_instance=RequestContext(request)) |
115 |
151 |
|
152 def _text_create_import(request, createForm): |
|
153 if request.method == 'POST': |
|
154 form = createForm(request.POST, request.FILES) |
|
155 if form.is_valid(): |
|
156 soup = form.cleaned_data['soup'] |
|
157 if not soup.co_ment_text: |
|
158 raise Exception('Bad Soup') |
|
159 |
|
160 # Process attachments first to create new keys. |
|
161 attachments_keys_map = {} |
|
162 if soup.co_ment_text.attachments: |
|
163 for imported_attachement in soup.co_ment_text.attachments.findAll('attachment'): |
|
164 # Creates attachment object. |
|
165 filename = 'imported_attachment' |
|
166 attachment = Attachment.objects.create_attachment(filename=filename, data=b64decode(imported_attachement.data.renderContents()), text_version=None) |
|
167 # Stores key mapping. |
|
168 attachments_keys_map[imported_attachement.key.renderContents()] = attachment.key |
|
169 |
|
170 # Process text. |
|
171 form.cleaned_data['title'] = soup.co_ment_text.title.renderContents() |
|
172 form.cleaned_data['format'] = soup.co_ment_text.format.renderContents() |
|
173 form.cleaned_data['content'] = re.sub(r'^<!\[CDATA\[|\]\]>$', '', soup.co_ment_text.content.renderContents()) |
|
174 form.cleaned_data['name'] = soup.co_ment_text.find('name').renderContents() |
|
175 form.cleaned_data['email'] = soup.co_ment_text.email.renderContents() |
|
176 if soup.co_ment_text.tags: |
|
177 form.cleaned_data['tags'] = soup.co_ment_text.tags.renderContents() |
|
178 |
|
179 # Replaces attachements keys in content. |
|
180 for old_key in attachments_keys_map.keys(): |
|
181 form.cleaned_data['content'] = re.sub(old_key, attachments_keys_map[old_key], form.cleaned_data['content']) |
|
182 |
|
183 # Creates text. |
|
184 text = create_text(request.user, form.cleaned_data) |
|
185 |
|
186 # Brute updates of dates (cannot do it through django models since fields are set with auto_now or auto_now_add). |
|
187 created = soup.co_ment_text.created.renderContents() |
|
188 modified = soup.co_ment_text.modified.renderContents() |
|
189 cursor = connection.cursor() |
|
190 cursor.execute("UPDATE cm_textversion SET created = %s, modified = %s WHERE id = %s", [created, modified, text.last_text_version_id]) |
|
191 cursor.execute("UPDATE cm_text SET created = %s, modified = %s WHERE id = %s", [created, modified, text.id]) |
|
192 transaction.commit_unless_managed() |
|
193 |
|
194 # Process comments. |
|
195 if soup.co_ment_text.comments: |
|
196 comments_ids_map = {} |
|
197 for imported_comment in soup.co_ment_text.comments.findAll('comment'): |
|
198 # Creates each comment. |
|
199 comment = Comment.objects.create( |
|
200 text_version=text.get_latest_version(), |
|
201 title=imported_comment.title.renderContents(), |
|
202 state=imported_comment.state.renderContents(), |
|
203 name=imported_comment.find('name').renderContents(), |
|
204 email=imported_comment.email.renderContents(), |
|
205 format=imported_comment.format.renderContents(), |
|
206 content=re.sub(r'^<!\[CDATA\[|\]\]>$', '', imported_comment.content.renderContents()), |
|
207 content_html=re.sub(r'^<!\[CDATA\[|\]\]>$', '', imported_comment.content_html.renderContents()), |
|
208 ) |
|
209 |
|
210 # Stores id for reply_to mapping. |
|
211 comments_ids_map[imported_comment.id.renderContents()] = comment |
|
212 |
|
213 # Process boolean and potentially null integer/foreign key attributes. |
|
214 save = False |
|
215 if imported_comment.deleted.renderContents() == 'True': |
|
216 comment.deleted = True |
|
217 save = True |
|
218 if imported_comment.start_wrapper.renderContents() != 'None': |
|
219 comment.start_wrapper = imported_comment.start_wrapper.renderContents() |
|
220 save = True |
|
221 if imported_comment.end_wrapper.renderContents() != 'None': |
|
222 comment.end_wrapper = imported_comment.end_wrapper.renderContents() |
|
223 save = True |
|
224 if imported_comment.start_offset.renderContents() != 'None': |
|
225 comment.start_offset = imported_comment.start_offset.renderContents() |
|
226 save = True |
|
227 if imported_comment.end_offset.renderContents() != 'None': |
|
228 comment.end_offset = imported_comment.end_offset.renderContents() |
|
229 save = True |
|
230 if imported_comment.find('parent'): |
|
231 comment.reply_to = comments_ids_map.get(imported_comment.find('parent').renderContents()) |
|
232 save = True |
|
233 if save: |
|
234 comment.save() |
|
235 |
|
236 # Brute updates of dates (cannot do it through django models since fields are set with auto_now or auto_now_add). |
|
237 created=imported_comment.created.renderContents(), |
|
238 modified=imported_comment.modified.renderContents(), |
|
239 cursor.execute("UPDATE cm_comment SET created = %s, modified = %s WHERE id = %s", [created, modified, comment.id]) |
|
240 transaction.commit_unless_managed() |
|
241 |
|
242 # Logs on activity. |
|
243 register_activity(request, "text_imported", text) |
|
244 display_message(request, _(u'Text "%(text_title)s" has been imported')%{"text_title":text.get_latest_version().title}) |
|
245 return None, HttpResponseRedirect(reverse('text-view', args=[text.key])) |
|
246 else: |
|
247 form = createForm() |
|
248 |
|
249 return None, render_to_response('site/text_create_import.html', {'form' : form}, context_instance=RequestContext(request)) |
|
250 |
|
251 |
116 @has_global_perm('can_create_text') |
252 @has_global_perm('can_create_text') |
117 def text_create_upload(request): |
253 def text_create_upload(request): |
118 text, rep = _text_create_upload(request, CreateTextUploadForm) |
254 text, rep = _text_create_upload(request, CreateTextUploadForm) |
|
255 return rep |
|
256 |
|
257 def text_create_import(request): |
|
258 text, rep = _text_create_import(request, CreateTextImportForm) |
119 return rep |
259 return rep |
120 |
260 |
121 def create_text(user, data): |
261 def create_text(user, data): |
122 text = Text.objects.create_text(title=data['title'], |
262 text = Text.objects.create_text(title=data['title'], |
123 format=data['format'], |
263 format=data['format'], |