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