images in epub
[redakcja.git] / apps / catalogue / views.py
1 # -*- coding: utf-8 -*-
2 from datetime import date, timedelta
3 import logging
4 import os
5 import shutil
6
7 from django.conf import settings
8 from django.contrib import auth
9 from django.contrib.auth.models import User
10 from django.contrib.auth.decorators import login_required
11 from django.core.urlresolvers import reverse
12 from django.db.models import Count
13 from django import http
14 from django.http import Http404
15 from django.shortcuts import get_object_or_404, render, redirect
16 from django.utils.http import urlquote_plus
17 from django.views.decorators.http import require_POST
18
19 from catalogue import forms
20 from catalogue import helpers
21 from catalogue.helpers import active_tab
22 from .constants import STAGES
23 from .models import Document, Plan
24 from dvcs.models import Revision
25 from organizations.models import Organization
26 from fileupload.views import UploadView, PackageView
27
28 #
29 # Quick hack around caching problems, TODO: use ETags
30 #
31 from django.views.decorators.cache import never_cache
32 # from fnpdjango.utils.text.slughifi import slughifi
33
34 logger = logging.getLogger("fnp.catalogue")
35
36
37 @active_tab('all')
38 @never_cache
39 def document_list(request):
40     return render(request, 'catalogue/document_list.html')
41
42
43 @never_cache
44 def user(request, username):
45     user = get_object_or_404(User, username=username)
46     return render(request, 'catalogue/user_page.html', {"viewed_user": user})
47
48
49 @login_required
50 @active_tab('my')
51 @never_cache
52 def my(request):
53     return render(request, 'catalogue/my_page.html', {
54         'last_books': sorted(
55             request.session.get("wiki_last_books", {}).items(), key=lambda x: x[1]['time'], reverse=True),
56
57         "logout_to": '/',
58         })
59
60
61 @active_tab('users')
62 def users(request):
63     return render(request, 'catalogue/user_list.html', {
64         'users': User.objects.all().annotate(count=Count('chunk')).order_by(
65             '-count', 'last_name', 'first_name'),
66     })
67
68
69 @active_tab('activity')
70 def activity(request, isodate=None):
71     today = date.today()
72     try:
73         day = helpers.parse_isodate(isodate)
74     except ValueError:
75         day = today
76
77     if day > today:
78         raise Http404
79     if day != today:
80         next_day = day + timedelta(1)
81     prev_day = day - timedelta(1)
82
83     return render(request, 'catalogue/activity.html', locals())
84
85
86 @never_cache
87 def logout_then_redirect(request):
88     auth.logout(request)
89     return http.HttpResponseRedirect(urlquote_plus(request.GET.get('next', '/'), safe='/?='))
90
91
92 # @permission_required('catalogue.add_book')
93 @login_required
94 @active_tab('create')
95 def create_missing(request):
96     if request.method == "POST":
97         form = forms.DocumentCreateForm(request.POST, request.FILES)
98         if form.is_valid():
99             
100             if request.user.is_authenticated():
101                 creator = request.user
102             else:
103                 creator = None
104
105             title = form.cleaned_data['title']
106             try:
107                 org = request.user.membership_set.get(
108                     organization=int(form.cleaned_data['owner_organization'])).organization
109                 kwargs = {'owner_organization': org}
110             except:
111                 kwargs = {'owner_user': request.user}
112
113             doc = Document.objects.create(**kwargs)
114
115             cover = request.FILES.get('cover')
116             if cover:
117                 uppath = 'uploads/%d/' % doc.pk
118                 path = settings.MEDIA_ROOT + uppath
119                 if not os.path.isdir(path):
120                     os.makedirs(path)
121                 dest_path = path + cover.name   # UNSAFE
122                 with open(dest_path, 'w') as destination:
123                     for chunk in cover.chunks():
124                         destination.write(chunk)
125                 cover_url = 'http://milpeer.eu/media/dynamic/' + uppath + cover.name
126             else:
127                 cover_url = ''
128
129             doc.commit(
130                 text='''<section xmlns="http://nowoczesnapolska.org.pl/sst#" xmlns:dc="http://purl.org/dc/elements/1.1/">
131                 <metadata>
132                     <dc:publisher>''' + form.cleaned_data['publisher'] + '''</dc:publisher>
133                     <dc:description>''' + form.cleaned_data['description'] + '''</dc:description>
134                     <dc:language>''' + form.cleaned_data['language'] + '''</dc:language>
135                     <dc:rights>''' + form.cleaned_data['rights'] + '''</dc:rights>
136                     <dc:audience>''' + form.cleaned_data['audience'] + '''</dc:audience>
137                     <dc:relation.coverImage.url>''' + cover_url + '''</dc:relation.coverImage.url>
138                 </metadata>
139                 <header>''' + title + '''</header>
140                 <div class="p"> </div>
141                 </section>''',
142                 author=creator
143             )
144             doc.assigned_to = request.user
145             doc.save()
146
147             return http.HttpResponseRedirect(reverse("wiki_editor", args=[doc.pk]))
148     else:
149         org_pk = request.GET.get('organization')
150         if org_pk:
151             try:
152                 org = Organization.objects.get(pk=org_pk)
153             except Organization.DoesNotExist:
154                 org = None
155             else:
156                 if not org.is_member(request.user):
157                     org = None
158         else:
159             org = None
160         if org is not None:
161             org = org.pk
162
163         form = forms.DocumentCreateForm(initial={'owner_organization': org})
164
165     return render(request, "catalogue/document_create_missing.html", {
166         "form": form,
167
168         "logout_to": '/',
169     })
170
171
172 # @permission_required('catalogue.add_book')
173 # @active_tab('upload')
174 # def upload(request):
175 #     if request.method == "POST":
176 #         form = forms.DocumentsUploadForm(request.POST, request.FILES)
177 #         if form.is_valid():
178 #             import slughifi
179 #
180 #             if request.user.is_authenticated():
181 #                 creator = request.user
182 #             else:
183 #                 creator = None
184 #
185 #             zip = form.cleaned_data['zip']
186 #             skipped_list = []
187 #             ok_list = []
188 #             error_list = []
189 #             slugs = {}
190 #             existing = [book.slug for book in Book.objects.all()]
191 #             for filename in zip.namelist():
192 #                 if filename[-1] == '/':
193 #                     continue
194 #                 title = os.path.basename(filename)[:-4]
195 #                 slug = slughifi(title)
196 #                 if not (slug and filename.endswith('.xml')):
197 #                     skipped_list.append(filename)
198 #                 elif slug in slugs:
199 #                     error_list.append((filename, slug, _('Slug already used for %s' % slugs[slug])))
200 #                 elif slug in existing:
201 #                     error_list.append((filename, slug, _('Slug already used in repository.')))
202 #                 else:
203 #                     try:
204 #                         zip.read(filename).decode('utf-8') # test read
205 #                         ok_list.append((filename, slug, title))
206 #                     except UnicodeDecodeError:
207 #                         error_list.append((filename, title, _('File should be UTF-8 encoded.')))
208 #                     slugs[slug] = filename
209 #
210 #             if not error_list:
211 #                 for filename, slug, title in ok_list:
212 #                     book = Book.create(
213 #                         text=zip.read(filename).decode('utf-8'),
214 #                         creator=creator,
215 #                         slug=slug,
216 #                         title=title,
217 #                     )
218 #
219 #             return render(request, "catalogue/document_upload.html", {
220 #                 "form": form,
221 #                 "ok_list": ok_list,
222 #                 "skipped_list": skipped_list,
223 #                 "error_list": error_list,
224 #
225 #                 "logout_to": '/',
226 #             })
227 #     else:
228 #         form = forms.DocumentsUploadForm()
229 #
230 #     return render(request, "catalogue/document_upload.html", {
231 #         "form": form,
232 #
233 #         "logout_to": '/',
234 #     })
235
236
237 # @never_cache
238 # def book_xml(request, slug):
239 #     book = get_object_or_404(Book, slug=slug)
240 #     if not book.accessible(request):
241 #         return HttpResponseForbidden("Not authorized.")
242 #     xml = book.materialize()
243 #
244 #     response = http.HttpResponse(xml, content_type='application/xml', mimetype='application/wl+xml')
245 #     response['Content-Disposition'] = 'attachment; filename=%s.xml' % slug
246 #     return response
247
248
249 # @never_cache
250 # def book_txt(request, slug):
251 #     book = get_object_or_404(Book, slug=slug)
252 #     if not book.accessible(request):
253 #         return HttpResponseForbidden("Not authorized.")
254 #
255 #     doc = book.wldocument()
256 #     text = doc.as_text().get_string()
257 #     response = http.HttpResponse(text, content_type='text/plain', mimetype='text/plain')
258 #     response['Content-Disposition'] = 'attachment; filename=%s.txt' % slug
259 #     return response
260
261
262 @never_cache
263 def book_html(request, pk, rev_pk=None, preview=False):
264     from librarian.document import Document as SST
265     from librarian.formats.html import HtmlFormat
266
267     doc = get_object_or_404(Document, pk=pk, deleted=False)
268
269     try:
270         published_revision = doc.publish_log.all()[0].revision
271     except IndexError:
272         published_revision = None
273
274     if rev_pk is None:
275         if preview:
276             revision = doc.revision
277         else:
278             if published_revision is not None:
279                 revision = published_revision
280             else:
281                 # No published version, fallback to preview mode.
282                 preview = True
283                 revision = doc.revision
284     else:
285         revision = get_object_or_404(Revision, pk=rev_pk)
286
287     was_published = revision == published_revision or doc.publish_log.filter(revision=revision).exists()
288
289     sst = SST.from_string(revision.materialize())
290     html = HtmlFormat(sst).build(
291         files_path='http://%s/media/dynamic/uploads/%s/' % (request.get_host(), pk)).get_string()
292
293     # response = http.HttpResponse(html, content_type='text/html', mimetype='text/html')
294     # return response
295     # book_themes = {}
296     # for fragment in book.fragments.all().iterator():
297     #     for theme in fragment.tags.filter(category='theme').iterator():
298     #         book_themes.setdefault(theme, []).append(fragment)
299
300     # book_themes = book_themes.items()
301     # book_themes.sort(key=lambda s: s[0].sort_key)
302     return render(request, 'catalogue/book_text.html', {
303         'doc': doc,
304         'preview': preview,
305         'revision': revision,
306         'published_revision': published_revision,
307         'specific': rev_pk is not None,
308         'html': html,
309         'can_edit': doc.can_edit(request.user) if doc else None,
310         'was_published': was_published,
311     })
312
313
314 @never_cache
315 def book_pdf(request, pk, rev_pk):
316     from librarian.utils import Context
317     from librarian.document import Document as SST
318     from librarian.formats.pdf import PdfFormat
319
320     doc = get_object_or_404(Document, pk=pk)
321     rev = get_object_or_404(Revision, pk=rev_pk)
322     # Test
323
324     sst = SST.from_string(rev.materialize())
325     
326     ctx = Context(
327         files_path='http://%s/media/dynamic/uploads/%s/' % (request.get_host(), pk),
328         source_url='http://%s%s' % (request.get_host(), reverse('catalogue_html', args=[doc.pk])),
329     )
330     if doc.owner_organization is not None and doc.owner_organization.logo:
331         ctx.cover_logo = 'http://%s%s' % (request.get_host(), doc.owner_organization.logo.url)
332     pdf_file = PdfFormat(sst).build(ctx)
333
334     from catalogue.ebook_utils import serve_file
335     return serve_file(pdf_file.get_filename(), '%d.pdf' % doc.pk, 'application/pdf')
336
337
338 @never_cache
339 def book_epub(request, pk, rev_pk):
340     from librarian.utils import Context
341     from librarian.document import Document as SST
342     from librarian.formats.epub import EpubFormat
343
344     doc = get_object_or_404(Document, pk=pk)
345     rev = get_object_or_404(Revision, pk=rev_pk)
346     # Test
347
348     sst = SST.from_string(rev.materialize())
349
350     ctx = Context(
351         files_path='http://%s/media/dynamic/uploads/%s/' % (request.get_host(), pk),
352         source_url='http://%s%s' % (request.get_host(), reverse('catalogue_html', args=[doc.pk])),
353     )
354     if doc.owner_organization is not None and doc.owner_organization.logo:
355         ctx.cover_logo = 'http://%s%s' % (request.get_host(), doc.owner_organization.logo.url)
356     epub_file = EpubFormat(sst).build(ctx)
357
358     from catalogue.ebook_utils import serve_file
359     return serve_file(epub_file.get_filename(), '%d.epub' % doc.pk, 'application/epub+zip')
360
361
362 # @never_cache
363 # def revision(request, slug, chunk=None):
364 #     try:
365 #         doc = Chunk.get(slug, chunk)
366 #     except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
367 #         raise Http404
368 #     if not doc.book.accessible(request):
369 #         return HttpResponseForbidden("Not authorized.")
370 #     return http.HttpResponse(str(doc.revision()))
371
372
373 @login_required
374 def book_schedule(request, pk):
375     book = get_object_or_404(Document, pk=pk, deleted=False)
376     if request.method == 'POST':
377         Plan.objects.filter(document=book).delete()
378         for i, s in enumerate(STAGES):
379             user_id = request.POST.get('s%d-user' % i)
380             deadline = request.POST.get('s%d-deadline' % i) or None
381             Plan.objects.create(document=book, stage=s, user_id=user_id, deadline=deadline)
382
383         book.set_stage(request.POST.get('stage', ''))
384         return redirect('catalogue_user')
385
386     current = {}
387     for p in Plan.objects.filter(document=book):
388         current[p.stage] = (getattr(p.user, 'pk', None), (p.deadline.isoformat() if p.deadline else None))
389
390     schedule = [(i, s, current.get(s, ())) for (i, s) in enumerate(STAGES)]
391     
392     if book.owner_organization:
393         people = [m.user for m in book.owner_organization.membership_set.exclude(status='pending')]
394     else:
395         people = [book.owner_user]
396     return render(request, 'catalogue/book_schedule.html', {
397         'book': book,
398         'schedule': schedule,
399         'people': people,
400     })
401
402
403 @login_required
404 def book_owner(request, pk):
405     doc = get_object_or_404(Document, pk=pk, deleted=False)
406     user_is_owner = doc.owner_organization and doc.owner_organization.is_member(request.user)
407     if not (doc.owner_user == request.user or user_is_owner):
408         raise Http404
409
410     error = ''
411
412     if request.method == 'POST':
413         # TODO: real form
414         new_org_pk = request.POST.get('owner_organization')
415         if not new_org_pk:
416             doc.owner_organization = None
417             doc.owner_user = request.user
418             doc.save()
419         else:
420             org = Organization.objects.get(pk=new_org_pk)
421             if not org.is_member(request.user):
422                 error = 'Bad organization'
423             else:
424                 doc.owner_organization = org
425                 doc.owner_user = None
426                 doc.save()
427         if not error:
428             return redirect('catalogue_user')
429
430     return render(request, 'catalogue/book_owner.html', {
431         'doc': doc,
432         'error': error,
433     })
434
435
436 @login_required
437 def book_delete(request, pk):
438     doc = get_object_or_404(Document, pk=pk, deleted=False)
439     if not (doc.owner_user == request.user or doc.owner_organization.is_member(request.user)):
440         raise Http404
441
442     if request.method == 'POST':
443         doc.deleted = True
444         doc.save()
445         return redirect('catalogue_user')
446
447     return render(request, 'catalogue/book_delete.html', {
448         'doc': doc,
449     })
450
451
452 # def book(request, slug):
453 #     book = get_object_or_404(Book, slug=slug)
454 #     if not book.accessible(request):
455 #         return HttpResponseForbidden("Not authorized.")
456 #
457 #     if request.user.has_perm('catalogue.change_book'):
458 #         if request.method == "POST":
459 #             form = forms.BookForm(request.POST, instance=book)
460 #             if form.is_valid():
461 #                 form.save()
462 #                 return http.HttpResponseRedirect(book.get_absolute_url())
463 #         else:
464 #             form = forms.BookForm(instance=book)
465 #         editable = True
466 #     else:
467 #         form = forms.ReadonlyBookForm(instance=book)
468 #         editable = False
469 #
470 #     publish_error = book.publishable_error()
471 #     publishable = publish_error is None
472 #
473 #     return render(request, "catalogue/book_detail.html", {
474 #         "book": book,
475 #         "publishable": publishable,
476 #         "publishable_error": publish_error,
477 #         "form": form,
478 #         "editable": editable,
479 #     })
480
481
482 # @permission_required('catalogue.add_chunk')
483 # def chunk_add(request, slug, chunk):
484 #     try:
485 #         doc = Chunk.get(slug, chunk)
486 #     except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
487 #         raise Http404
488 #     if not doc.book.accessible(request):
489 #         return HttpResponseForbidden("Not authorized.")
490 #
491 #     if request.method == "POST":
492 #         form = forms.ChunkAddForm(request.POST, instance=doc)
493 #         if form.is_valid():
494 #             if request.user.is_authenticated():
495 #                 creator = request.user
496 #             else:
497 #                 creator = None
498 #             doc.split(creator=creator,
499 #                 slug=form.cleaned_data['slug'],
500 #                 title=form.cleaned_data['title'],
501 #                 gallery_start=form.cleaned_data['gallery_start'],
502 #                 user=form.cleaned_data['user'],
503 #                 stage=form.cleaned_data['stage']
504 #             )
505 #
506 #             return http.HttpResponseRedirect(doc.book.get_absolute_url())
507 #     else:
508 #         form = forms.ChunkAddForm(initial={
509 #                 "slug": str(doc.number + 1),
510 #                 "title": "cz. %d" % (doc.number + 1, ),
511 #         })
512 #
513 #     return render(request, "catalogue/chunk_add.html", {
514 #         "chunk": doc,
515 #         "form": form,
516 #     })
517
518
519 # @login_required
520 # def chunk_edit(request, slug, chunk):
521 #     try:
522 #         doc = Chunk.get(slug, chunk)
523 #     except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
524 #         raise Http404
525 #     if not doc.book.accessible(request):
526 #         return HttpResponseForbidden("Not authorized.")
527 #
528 #     if request.method == "POST":
529 #         form = forms.ChunkForm(request.POST, instance=doc)
530 #         if form.is_valid():
531 #             form.save()
532 #             go_next = request.GET.get('next', None)
533 #             if go_next:
534 #                 go_next = urlquote_plus(unquote(iri_to_uri(go_next)), safe='/?=&')
535 #             else:
536 #                 go_next = doc.book.get_absolute_url()
537 #             return http.HttpResponseRedirect(go_next)
538 #     else:
539 #         form = forms.ChunkForm(instance=doc)
540 #
541 #     referer = request.META.get('HTTP_REFERER')
542 #     if referer:
543 #         parts = urlsplit(referer)
544 #         parts = ['', ''] + list(parts[2:])
545 #         go_next = urlquote_plus(urlunsplit(parts))
546 #     else:
547 #         go_next = ''
548 #
549 #     return render(request, "catalogue/chunk_edit.html", {
550 #         "chunk": doc,
551 #         "form": form,
552 #         "go_next": go_next,
553 #     })
554
555
556 # @transaction.atomic
557 # @login_required
558 # def chunk_mass_edit(request):
559 #     if request.method == 'POST':
560 #         ids = map(int, filter(lambda i: i.strip()!='', request.POST.get('ids').split(',')))
561 #         chunks = map(lambda i: Chunk.objects.get(id=i), ids)
562 #
563 #         stage = request.POST.get('stage')
564 #         if stage:
565 #             try:
566 #                 stage = Chunk.tag_model.objects.get(slug=stage)
567 #             except Chunk.DoesNotExist, e:
568 #                 stage = None
569 #
570 #             for c in chunks: c.stage = stage
571 #
572 #         username = request.POST.get('user')
573 #         logger.info("username: %s" % username)
574 #         logger.info(request.POST)
575 #         if username:
576 #             try:
577 #                 user = User.objects.get(username=username)
578 #             except User.DoesNotExist, e:
579 #                 user = None
580 #
581 #             for c in chunks: c.user = user
582 #
583 #         status = request.POST.get('status')
584 #         if status:
585 #             books_affected = set()
586 #             for c in chunks:
587 #                 if status == 'publish':
588 #                     c.head.publishable = True
589 #                     c.head.save()
590 #                 elif status == 'unpublish':
591 #                     c.head.publishable = False
592 #                     c.head.save()
593 #                 c.touch()  # cache
594 #                 books_affected.add(c.book)
595 #             for b in books_affected:
596 #                 b.touch()  # cache
597 #
598 #         project_id = request.POST.get('project')
599 #         if project_id:
600 #             try:
601 #                 project = Project.objects.get(pk=int(project_id))
602 #             except (Project.DoesNotExist, ValueError), e:
603 #                 project = None
604 #             for c in chunks:
605 #                 book = c.book
606 #                 book.project = project
607 #                 book.save()
608 #
609 #         for c in chunks: c.save()
610 #
611 #         return HttpResponse("", content_type="text/plain")
612 #     else:
613 #         raise Http404
614
615
616 # @permission_required('catalogue.change_book')
617 # def book_append(request, slug):
618 #     book = get_object_or_404(Book, slug=slug)
619 #     if not book.accessible(request):
620 #         return HttpResponseForbidden("Not authorized.")
621 #
622 #     if request.method == "POST":
623 #         form = forms.BookAppendForm(book, request.POST)
624 #         if form.is_valid():
625 #             append_to = form.cleaned_data['append_to']
626 #             append_to.append(book)
627 #             return http.HttpResponseRedirect(append_to.get_absolute_url())
628 #     else:
629 #         form = forms.BookAppendForm(book)
630 #     return render(request, "catalogue/book_append_to.html", {
631 #         "book": book,
632 #         "form": form,
633 #
634 #         "logout_to": '/',
635 #     })
636
637
638 @require_POST
639 @login_required
640 def publish(request, pk):
641     from wiki import forms
642     from .models import PublishRecord
643     from dvcs.models import Revision
644
645     # FIXME: check permissions
646
647     doc = get_object_or_404(Document, pk=pk, deleted=False)
648     form = forms.DocumentTextPublishForm(request.POST, prefix="textpublish")
649     if form.is_valid():
650         rev = Revision.objects.get(pk=form.cleaned_data['revision'])
651         # FIXME: check if in tree
652         # if PublishRecord.objects.filter(revision=rev, document=doc).exists():
653         #     return http.HttpResponse('exists')
654         PublishRecord.objects.create(revision=rev, document=doc, user=request.user)
655         if request.is_ajax():
656             return http.HttpResponse('ok')
657         else:
658             return redirect('catalogue_html', doc.pk)
659     else:
660         if request.is_ajax():
661             return http.HttpResponse('error')
662         else:
663             try:
664                 return redirect('catalogue_preview_rev', doc.pk, form.cleaned_data['revision'])
665             except KeyError:
666                 return redirect('catalogue_preview', doc.pk)
667
668
669 @require_POST
670 @login_required
671 def unpublish(request, pk):
672     # FIXME: check permissions
673
674     doc = get_object_or_404(Document, pk=pk, deleted=False)
675     doc.publish_log.all().delete()
676     if request.is_ajax():
677         return http.HttpResponse('ok')
678     else:
679         return redirect('catalogue_html', doc.pk)
680
681
682 class GalleryMixin(object):
683     def get_directory(self):
684         # return "%s%s/" % (settings.IMAGE_DIR, 'org%d' % self.org.pk if self.org is not None else self.request.user.pk)
685         return "uploads/%d/" % self.doc.pk
686
687
688 class GalleryView(GalleryMixin, UploadView):
689
690     def breadcrumbs(self):
691         return [
692                 (self.doc.meta()['title'], '/documents/%d/' % self.doc.pk),
693             ]
694
695     def get_object(self, request, pk=None):
696         self.doc = Document.objects.get(pk=pk, deleted=False)
697
698
699 class GalleryPackageView(GalleryMixin, PackageView):
700
701     def get_redirect_url(self, slug):
702         return reverse('catalogue_book_gallery', kwargs=dict(slug=slug))
703
704
705 @login_required
706 def fork(request, pk):
707     doc = get_object_or_404(Document, pk=pk, deleted=False)
708     if request.method == "POST":
709         form = forms.DocumentForkForm(request.POST, request.FILES)
710         if form.is_valid():
711             try:
712                 org = request.user.membership_set.get(
713                     organization=int(form.cleaned_data['owner_organization'])).organization
714                 kwargs = {'owner_organization': org}
715             except:
716                 kwargs = {'owner_user': request.user}
717
718             new_doc = Document.objects.create(revision=doc.revision, **kwargs)
719
720             if os.path.isdir(settings.MEDIA_ROOT + "uploads/%d" % doc.pk):
721                 shutil.copytree(
722                     settings.MEDIA_ROOT + "uploads/%d" % doc.pk,
723                     settings.MEDIA_ROOT + "uploads/%d" % new_doc.pk
724                 )
725
726             new_doc.assigned_to = request.user
727             new_doc.save()
728
729             return http.HttpResponseRedirect(reverse("wiki_editor", args=[new_doc.pk]))
730     else:
731         form = forms.DocumentForkForm()
732
733     return render(request, "catalogue/document_fork.html", {
734         "form": form,
735
736         "logout_to": '/',
737     })
738
739
740 def upcoming(request):
741     return render(request, "catalogue/upcoming.html", {
742         'objects_list': Document.objects.filter(deleted=False).filter(publish_log=None),
743     })
744
745
746 def finished(request):
747     return render(request, "catalogue/finished.html", {
748         'objects_list': Document.objects.filter(deleted=False).exclude(publish_log=None),
749     })