Example #1
0
def document_chosen(request, document_id):
    doc = get_object_or_404(get_document_model(), id=document_id)
    Document = get_document_model()
    DocumentMultiForm = get_document_multi_form(Document)

    # handle some updated data if this is a POST
    if request.POST:
        if not request.is_ajax():
            return http.HttpResponseBadRequest(
                "Cannot POST to this view without AJAX")

        form = DocumentMultiForm(
            request.POST, request.FILES, instance=doc, prefix='doc-' + document_id, user=request.user
        )

        if form.is_valid():
            form.save()

        # Reindex the doc to make sure all tags are indexed
        search_index.insert_or_update_object(doc)

    return render_modal_workflow(
        request, None, 'wagtaildocs/chooser/document_chosen.js',
        {'document_json': get_document_json(doc)}
    )
def convert_rich_text(source, request, absolute):
    try:
        from bs4 import BeautifulSoup
    except ImportError:
        return str(RichText(source))

    soup = BeautifulSoup(source, 'html5lib')

    # Make document links absolute.
    for anchor in soup.find_all('a'):
        if anchor.attrs.get('linktype', '') == 'document':
            try:
                doc = get_document_model().objects.get(pk=anchor.attrs['id'])
                new_tag = soup.new_tag(
                    'a',
                    href=resolve_absolute_url(doc.url,
                                              request,
                                              absolute=absolute))
                new_tag.append(*anchor.contents)
                anchor.replace_with(new_tag)
            except get_document_model().DoesNotExist:
                new_tag = soup.new_tag('a')
                new_tag.append(*anchor.contents)
                anchor.replace_with(new_tag)

    return str(RichText(str(soup)))
Example #3
0
    def test_filtering_tags(self):
        get_document_model().objects.get(id=3).tags.add('test')

        response = self.get_response(tags='test')
        content = json.loads(response.content.decode('UTF-8'))

        document_id_list = self.get_document_id_list(content)
        self.assertEqual(document_id_list, [3])
Example #4
0
    def test_filtering_tags(self):
        get_document_model().objects.get(id=3).tags.add('test')

        response = self.get_response(tags='test')
        content = json.loads(response.content.decode('UTF-8'))

        document_id_list = self.get_document_id_list(content)
        self.assertEqual(document_id_list, [3])
Example #5
0
    def test_tags(self):
        get_document_model().objects.get(id=1).tags.add('hello')
        get_document_model().objects.get(id=1).tags.add('world')

        response = self.get_response(1)
        content = json.loads(response.content.decode('UTF-8'))

        self.assertIn('tags', content['meta'])
        self.assertEqual(content['meta']['tags'], ['hello', 'world'])
Example #6
0
    def test_tags(self):
        get_document_model().objects.get(id=1).tags.add('hello')
        get_document_model().objects.get(id=1).tags.add('world')

        response = self.get_response(1)
        content = json.loads(response.content.decode('UTF-8'))

        self.assertIn('tags', content['meta'])
        self.assertEqual(content['meta']['tags'], ['hello', 'world'])
    def test_add_post_with_collections(self):
        """
        This tests that a POST request to the add view saves the document
        and returns an edit form, when collections are active
        """

        root_collection = Collection.get_first_root_node()
        evil_plans_collection = root_collection.add_child(name="Evil plans")

        response = self.client.post(
            reverse('wagtaildocs:add_multiple'), {
                'files[]': SimpleUploadedFile('test.png',
                                              b"Simple text document"),
                'collection': evil_plans_collection.id
            },
            HTTP_X_REQUESTED_WITH='XMLHttpRequest')

        # Check response
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response['Content-Type'], 'application/json')
        self.assertTemplateUsed(response,
                                'wagtaildocs/multiple/edit_form.html')

        # Check document
        self.assertIn('doc', response.context)
        self.assertEqual(response.context['doc'].title, 'test.png')
        self.assertTrue(response.context['doc'].file_size)
        self.assertTrue(response.context['doc'].file_hash)

        # check that it is in the 'evil plans' collection
        doc = models.get_document_model().objects.get(title='test.png')
        root_collection = Collection.get_first_root_node()
        self.assertEqual(doc.collection, evil_plans_collection)

        # Check form
        self.assertIn('form', response.context)
        self.assertEqual(
            set(response.context['form'].fields),
            set(models.get_document_model().admin_form_fields) - {'file'}
            | {'collection'},
        )
        self.assertEqual(response.context['form'].initial['title'], 'test.png')

        # Check JSON
        response_json = json.loads(response.content.decode())
        self.assertIn('doc_id', response_json)
        self.assertIn('form', response_json)
        self.assertIn('success', response_json)
        self.assertEqual(response_json['doc_id'], response.context['doc'].id)
        self.assertTrue(response_json['success'])

        # form should contain a collection chooser
        self.assertIn('Collection', response_json['form'])
Example #8
0
    def test_add_post_with_collections(self):
        """
        This tests that a POST request to the add view saves the document
        and returns an edit form, when collections are active
        """

        root_collection = Collection.get_first_root_node()
        evil_plans_collection = root_collection.add_child(name="Evil plans")

        response = self.client.post(reverse('wagtaildocs:add_multiple'), {
            'files[]': SimpleUploadedFile('test.png', b"Simple text document"),
            'collection': evil_plans_collection.id
        }, HTTP_X_REQUESTED_WITH='XMLHttpRequest')

        # Check response
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response['Content-Type'], 'application/json')
        self.assertTemplateUsed(response, 'wagtaildocs/multiple/edit_form.html')

        # Check document
        self.assertIn('doc', response.context)
        self.assertEqual(response.context['doc'].title, 'test.png')
        self.assertTrue(response.context['doc'].file_size)
        self.assertTrue(response.context['doc'].file_hash)

        # check that it is in the 'evil plans' collection
        doc = models.get_document_model().objects.get(title='test.png')
        root_collection = Collection.get_first_root_node()
        self.assertEqual(doc.collection, evil_plans_collection)

        # Check form
        self.assertIn('form', response.context)
        self.assertEqual(
            set(response.context['form'].fields),
            set(models.get_document_model().admin_form_fields) - {'file'} | {'collection'},
        )
        self.assertEqual(response.context['form'].initial['title'], 'test.png')

        # Check JSON
        response_json = json.loads(response.content.decode())
        self.assertIn('doc_id', response_json)
        self.assertIn('form', response_json)
        self.assertIn('success', response_json)
        self.assertEqual(response_json['doc_id'], response.context['doc'].id)
        self.assertTrue(response_json['success'])

        # form should contain a collection chooser
        self.assertIn('Collection', response_json['form'])
Example #9
0
def document_linktype_handler(attrs):
    Document = get_document_model()
    try:
        doc = Document.objects.get(id=attrs['id'])
        return '<a href="%s">' % escape(doc.url)
    except (Document.DoesNotExist, KeyError):
        return "<a>"
Example #10
0
def serve_document_from_s3(document, request):
    """
    Download document from S3.

    This is to avoid reading the whole document by the Wagtail view
    and potentially risking DoS attack and the server timing out.
    """
    # Skip this hook if not using django-storages boto3 backend.
    if not issubclass(get_storage_class(), S3Boto3Storage):
        return

    # Send document_served signal, same as Wagtail does.
    # https://github.com/wagtail/wagtail/blob/7938e81ab48327a084ac1dced9474c998fd44c2d/wagtail/documents/views/serve.py#L32-L33
    document_served.send(sender=get_document_model(),
                         instance=document,
                         request=request)

    # Service file directly from S3.
    file_url = document.file.url

    # Generate redirect response and add never_cache headers.
    # Delete all existing headers.
    response = redirect(file_url)
    del response["Cache-control"]
    add_never_cache_headers(response)
    return response
Example #11
0
def chooser_upload(request):
    Document = get_document_model()
    DocumentForm = get_document_form(Document)

    if request.method == 'POST':
        document = Document(uploaded_by_user=request.user)
        form = DocumentForm(request.POST, request.FILES, instance=document, user=request.user)

        if form.is_valid():
            document.file_size = document.file.size

            # Set new document file hash
            document.file.seek(0)
            document._set_file_hash(document.file.read())
            document.file.seek(0)

            form.save()

            # Reindex the document to make sure all tags are indexed
            search_index.insert_or_update_object(document)

            return render_modal_workflow(
                request, None, None,
                None, json_data={'step': 'document_chosen', 'result': get_document_result_data(document)}
            )
    else:
        form = DocumentForm(user=request.user)

    documents = Document.objects.order_by('title')

    return render_modal_workflow(
        request, 'wagtaildocs/chooser/chooser.html', None,
        {'documents': documents, 'uploadform': form},
        json_data=get_chooser_context()
    )
Example #12
0
def DocumentsQuery():
    registry.documents[WagtailDocument] = DocumentObjectType
    mdl = get_document_model()
    mdl_type = registry.documents[mdl]

    class Mixin:
        document = graphene.Field(mdl_type, id=graphene.ID())
        documents = QuerySetList(
            graphene.NonNull(mdl_type),
            enable_search=True,
            required=True,
            collection=graphene.Argument(
                graphene.ID, description="Filter by collection id"),
        )

        def resolve_document(self, info, id, **kwargs):
            """Returns a document given the id, if in a public collection"""
            try:
                return mdl.objects.filter(
                    collection__view_restrictions__isnull=True).get(pk=id)
            except BaseException:
                return None

        def resolve_documents(self, info, **kwargs):
            """Returns all documents in a public collection"""
            qs = mdl.objects.filter(collection__view_restrictions__isnull=True)
            return resolve_queryset(qs, info, **kwargs)

        def resolve_document_type(self, info, **kwargs):
            return mdl_type

    return Mixin
 def test_document_file_deleted_oncommit(self):
     with transaction.atomic():
         document = get_document_model().objects.create(title="Test Image", file=get_test_image_file())
         self.assertTrue(document.file.storage.exists(document.file.name))
         document.delete()
         self.assertTrue(document.file.storage.exists(document.file.name))
     self.assertFalse(document.file.storage.exists(document.file.name))
Example #14
0
def chooser(request):
    Document = get_document_model()

    if permission_policy.user_has_permission(request.user, 'add'):
        DocumentForm = get_document_form(Document)
        uploadform = DocumentForm(user=request.user)
    else:
        uploadform = None

    documents = Document.objects.all()

    # allow hooks to modify the queryset
    for hook in hooks.get_hooks('construct_document_chooser_queryset'):
        documents = hook(documents, request)

    q = None
    if 'q' in request.GET or 'p' in request.GET or 'collection_id' in request.GET:

        collection_id = request.GET.get('collection_id')
        if collection_id:
            documents = documents.filter(collection=collection_id)

        searchform = SearchForm(request.GET)
        if searchform.is_valid():
            q = searchform.cleaned_data['q']

            documents = documents.search(q)
            is_searching = True
        else:
            documents = documents.order_by('-created_at')
            is_searching = False

        # Pagination
        paginator, documents = paginate(request, documents, per_page=10)

        return render(
            request, "wagtaildocs/chooser/results.html", {
                'documents': documents,
                'query_string': q,
                'is_searching': is_searching,
            })
    else:
        searchform = SearchForm()

        collections = Collection.objects.all()
        if len(collections) < 2:
            collections = None

        documents = documents.order_by('-created_at')
        paginator, documents = paginate(request, documents, per_page=10)

        return render_modal_workflow(
            request, 'wagtaildocs/chooser/chooser.html',
            'wagtaildocs/chooser/chooser.js', {
                'documents': documents,
                'uploadform': uploadform,
                'searchform': searchform,
                'collections': collections,
                'is_searching': False,
            })
Example #15
0
def add(request):
    Document = get_document_model()
    DocumentForm = get_document_form(Document)

    if request.method == 'POST':
        doc = Document(uploaded_by_user=request.user)
        form = DocumentForm(request.POST, request.FILES, instance=doc, user=request.user)
        if form.is_valid():
            doc.file_size = doc.file.size

            # Set new document file hash
            doc.file.seek(0)
            doc._set_file_hash(doc.file.read())
            doc.file.seek(0)

            form.save()

            # Reindex the document to make sure all tags are indexed
            search_index.insert_or_update_object(doc)

            messages.success(request, _("Document '{0}' added.").format(doc.title), buttons=[
                messages.button(reverse('wagtaildocs:edit', args=(doc.id,)), _('Edit'))
            ])
            return redirect('wagtaildocs:index')
        else:
            messages.error(request, _("The document could not be saved due to errors."))
    else:
        form = DocumentForm(user=request.user)

    return render(request, "wagtaildocs/documents/add.html", {
        'form': form,
    })
Example #16
0
def chooser_upload(request):
    Document = get_document_model()
    DocumentForm = get_document_form(Document)

    if request.method == 'POST':
        document = Document(uploaded_by_user=request.user)
        form = DocumentForm(request.POST, request.FILES, instance=document, user=request.user)

        if form.is_valid():
            form.save()

            # Reindex the document to make sure all tags are indexed
            search_index.insert_or_update_object(document)

            return render_modal_workflow(
                request, None, 'wagtaildocs/chooser/document_chosen.js',
                {'document_json': get_document_json(document)}
            )
    else:
        form = DocumentForm(user=request.user)

    documents = Document.objects.order_by('title')

    return render_modal_workflow(
        request, 'wagtaildocs/chooser/chooser.html', 'wagtaildocs/chooser/chooser.js',
        {'documents': documents, 'uploadform': form}
    )
    def get_context(self):
        site_name = get_site_for_user(self.request.user)['site_name']

        return {
            'total_docs': get_document_model().objects.count(),
            'site_name': site_name,
        }
Example #18
0
def add(request):
    Document = get_document_model()
    DocumentForm = get_document_form(Document)

    if request.method == 'POST':
        doc = Document(uploaded_by_user=request.user)
        form = DocumentForm(request.POST, request.FILES, instance=doc, user=request.user)
        if form.is_valid():
            doc.file_size = doc.file.size

            form.save()

            # Reindex the document to make sure all tags are indexed
            search_index.insert_or_update_object(doc)

            messages.success(request, _("Document '{0}' added.").format(doc.title), buttons=[
                messages.button(reverse('wagtaildocs:edit', args=(doc.id,)), _('Edit'))
            ])
            return redirect('wagtaildocs:index')
        else:
            messages.error(request, _("The document could not be saved due to errors."))
    else:
        form = DocumentForm(user=request.user)

    return render(request, "wagtaildocs/documents/add.html", {
        'form': form,
    })
Example #19
0
def generate_pdf(report_id):
    # Generate report HTML
    Report = apps.get_model('report', 'Report')
    report = Report.objects.get(pk=report_id)
    revision = PageRevision.objects.filter(page=report).last().as_page_object()
    contents = generate_report_contents(revision)
    authors = get_report_authors(revision)
    html = loader.get_template('report/pdf.html').render({
        'page': revision,
        'contents': contents,
        'authors': authors,
        'report': report
    })

    # Create a Document with empty file on S3 to hold the PDF
    Document = get_document_model()
    doc = Document(title=report.title)
    last_edited = ' %s.pdf' % strftime('%Y-%m-%d %H:%M:%S', gmtime())
    doc.file.save(revision.title + last_edited, BytesIO())

    # Submit request to PDF generator
    upload_successful = render_pdf(html, doc.file.name)

    if upload_successful:
        revision.report_pdf = doc
        revision.generate_pdf_on_publish = False
        revision.save_revision()
Example #20
0
def document_linktype_handler(attrs):
    Document = get_document_model()
    try:
        doc = Document.objects.get(id=attrs['id'])
        return '<a href="%s">' % escape(doc.url)
    except Document.DoesNotExist:
        return "<a>"
Example #21
0
    def test_limit_max_none_gives_no_errors(self):
        response = self.get_response(limit=1000000)
        content = json.loads(response.content.decode('UTF-8'))

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(content['items']),
                         get_document_model().objects.count())
Example #22
0
def document_chosen(request, document_id):
    document = get_object_or_404(get_document_model(), id=document_id)

    return render_modal_workflow(
        request, None, None,
        None, json_data={'step': 'document_chosen', 'result': get_document_result_data(document)}
    )
Example #23
0
def document_chosen(request, document_id):
    document = get_object_or_404(get_document_model(), id=document_id)

    return render_modal_workflow(
        request, None, None,
        None, json_data={'step': 'document_chosen', 'result': get_document_result_data(document)}
    )
Example #24
0
    def test_offset_total_count(self):
        response = self.get_response(offset=10)
        content = json.loads(response.content.decode('UTF-8'))

        # The total count must not be affected by "offset"
        self.assertEqual(content['meta']['total_count'],
                         get_document_model().objects.count())
Example #25
0
def document_chosen(request, document_id):
    document = get_object_or_404(get_document_model(), id=document_id)

    return render_modal_workflow(
        request, None, 'wagtaildocs/chooser/document_chosen.js',
        {'document_json': get_document_json(document)}
    )
Example #26
0
    def test_basic(self):
        response = self.get_response()

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response['Content-type'], 'application/json')

        # Will crash if the JSON is invalid
        content = json.loads(response.content.decode('UTF-8'))

        # Check that the meta section is there
        self.assertIn('meta', content)
        self.assertIsInstance(content['meta'], dict)

        # Check that the total count is there and correct
        self.assertIn('total_count', content['meta'])
        self.assertIsInstance(content['meta']['total_count'], int)
        self.assertEqual(content['meta']['total_count'], get_document_model().objects.count())

        # Check that the documents section is there
        self.assertIn('documents', content)
        self.assertIsInstance(content['documents'], list)

        # Check that each document has a meta section with type and detail_url attributes
        for document in content['documents']:
            self.assertIn('meta', document)
            self.assertIsInstance(document['meta'], dict)
            self.assertEqual(set(document['meta'].keys()), {'type', 'detail_url'})

            # Type should always be wagtaildocs.Document
            self.assertEqual(document['meta']['type'], 'wagtaildocs.Document')

            # Check detail_url
            self.assertEqual(document['meta']['detail_url'], 'http://localhost/api/v1/documents/%d/' % document['id'])
Example #27
0
def chooser_upload(request):
    Document = get_document_model()
    DocumentForm = get_document_form(Document)

    if request.method == 'POST':
        document = Document(uploaded_by_user=request.user)
        form = DocumentForm(request.POST,
                            request.FILES,
                            instance=document,
                            user=request.user)

        if form.is_valid():
            document.file_size = document.file.size

            form.save()

            # Reindex the document to make sure all tags are indexed
            search_index.insert_or_update_object(document)

            return render_modal_workflow(
                request, None, 'wagtaildocs/chooser/document_chosen.js',
                {'document_json': get_document_json(document)})
    else:
        form = DocumentForm(user=request.user)

    documents = Document.objects.order_by('title')

    return render_modal_workflow(request, 'wagtaildocs/chooser/chooser.html',
                                 'wagtaildocs/chooser/chooser.js', {
                                     'documents': documents,
                                     'uploadform': form
                                 })
Example #28
0
def serve(request, document_id, document_filename):
    Document = get_document_model()
    doc = get_object_or_404(Document, id=document_id)

    # We want to ensure that the document filename provided in the URL matches the one associated with the considered
    # document_id. If not we can't be sure that the document the user wants to access is the one corresponding to the
    # <document_id, document_filename> pair.
    if doc.filename != document_filename:
        raise Http404('This document does not match the given filename.')

    for fn in hooks.get_hooks('before_serve_document'):
        result = fn(doc, request)
        if isinstance(result, HttpResponse):
            return result

    # Send document_served signal
    document_served.send(sender=Document, instance=doc, request=request)

    try:
        local_path = doc.file.path
    except NotImplementedError:
        local_path = None

    if local_path:

        # Use wagtail.utils.sendfile to serve the file;
        # this provides support for mimetypes, if-modified-since and django-sendfile backends

        if hasattr(settings, 'SENDFILE_BACKEND'):
            return sendfile(request,
                            local_path,
                            attachment=True,
                            attachment_filename=doc.filename)
        else:
            # Fallback to streaming backend if user hasn't specified SENDFILE_BACKEND
            return sendfile(request,
                            local_path,
                            attachment=True,
                            attachment_filename=doc.filename,
                            backend=sendfile_streaming_backend.sendfile)

    else:

        # We are using a storage backend which does not expose filesystem paths
        # (e.g. storages.backends.s3boto.S3BotoStorage).
        # Fall back on pre-sendfile behaviour of reading the file content and serving it
        # as a StreamingHttpResponse

        wrapper = FileWrapper(doc.file)
        response = StreamingHttpResponse(
            wrapper, content_type='application/octet-stream')

        response[
            'Content-Disposition'] = 'attachment; filename=%s' % doc.filename

        # FIXME: storage backends are not guaranteed to implement 'size'
        response['Content-Length'] = doc.file.size

        return response
Example #29
0
 def setUpClass(cls):
     super().setUpClass()
     Document = models.get_document_model()
     fields = tuple(f for f in Document.admin_form_fields
                    if f != 'collection')
     cls.__patcher = mock.patch.object(Document, 'admin_form_fields',
                                       fields)
     cls.__patcher.start()
Example #30
0
    def setUp(self):
        self.login()

        # Create a document for running tests on
        self.doc = models.get_document_model().objects.create(
            title="Test document",
            file=ContentFile(b("Simple text document")),
        )
Example #31
0
 def test_document_file_deleted_oncommit(self):
     with transaction.atomic():
         document = get_document_model().objects.create(
             title="Test Image", file=get_test_image_file())
         self.assertTrue(document.file.storage.exists(document.file.name))
         document.delete()
         self.assertTrue(document.file.storage.exists(document.file.name))
     self.assertFalse(document.file.storage.exists(document.file.name))
Example #32
0
 def expand_db_attributes(attrs):
     Document = get_document_model()
     try:
         doc = Document.objects.get(id=attrs['id'])
         return '<a data-linktype="document" data-id="%d" href="%s">' % (
             doc.id, escape(doc.url))
     except Document.DoesNotExist:
         return "<a>"
Example #33
0
    def setUp(self):
        self.login()

        # Create a document for running tests on
        self.doc = models.get_document_model().objects.create(
            title="Test document",
            file=ContentFile(b"Simple text document"),
        )
Example #34
0
    def setUp(self):
        self.login()

        # Create a document for running tests on
        self.doc = models.get_document_model().objects.create(
            title="Test document",
            file=get_test_document_file(),
        )
Example #35
0
def chooser(request):
    Document = get_document_model()

    if permission_policy.user_has_permission(request.user, 'add'):
        DocumentForm = get_document_form(Document)
        uploadform = DocumentForm(user=request.user)
    else:
        uploadform = None

    documents = Document.objects.all()

    # allow hooks to modify the queryset
    for hook in hooks.get_hooks('construct_document_chooser_queryset'):
        documents = hook(documents, request)

    q = None
    if 'q' in request.GET or 'p' in request.GET or 'collection_id' in request.GET:

        collection_id = request.GET.get('collection_id')
        if collection_id:
            documents = documents.filter(collection=collection_id)

        searchform = SearchForm(request.GET)
        if searchform.is_valid():
            q = searchform.cleaned_data['q']

            documents = documents.search(q)
            is_searching = True
        else:
            documents = documents.order_by('-created_at')
            is_searching = False

        # Pagination
        paginator, documents = paginate(request, documents, per_page=10)

        return render(request, "wagtaildocs/chooser/results.html", {
            'documents': documents,
            'query_string': q,
            'is_searching': is_searching,
        })
    else:
        searchform = SearchForm()

        collections = Collection.objects.all()
        if len(collections) < 2:
            collections = None

        documents = documents.order_by('-created_at')
        paginator, documents = paginate(request, documents, per_page=10)

        return render_modal_workflow(request, 'wagtaildocs/chooser/chooser.html', 'wagtaildocs/chooser/chooser.js', {
            'documents': documents,
            'uploadform': uploadform,
            'searchform': searchform,
            'collections': collections,
            'is_searching': False,
            'uploadid': uuid.uuid4(),
        })
Example #36
0
def test_document_class():
    """Test that the Document model has the required (search) field.

    Actually, this only tests if Wagtails WAGTAILDOCS_DOCUMENT_MODEL
    still works, and that our test Django settings are correct.
    """
    Document = get_document_model()
    assert hasattr(Document, 'transcription')
    assert 'transcription' in [f.field_name for f in Document.search_fields]
Example #37
0
def document_link_handler(attrs):
    """Handle a link of the form <a linktype="document" id="123">"""
    Document = get_document_model()
    try:
        report = Document.objects.get(id=attrs['id'])
    except (Document.DoesNotExist, KeyError):
        return "<a>"
    base_url = Site.objects.first().root_url
    return '<a href="{}{}">'.format(base_url, report.url)
    def resolve_documents(self, info, **kwargs):
        if settings.WAGTAIL_GRAPHQL_ENABLE_DOCUMENTS is not True:
            raise GraphQLError('Documents endpoint is disabled.')

        request = info.context
        return get_base_queryset_for_model_or_qs(
            exclude_restricted_collection_members(
                request,
                get_document_model().objects.all()), info, **kwargs)
Example #39
0
def edit(request, document_id):
    Document = get_document_model()
    DocumentForm = get_document_form(Document)

    doc = get_object_or_404(Document, id=document_id)

    if not permission_policy.user_has_permission_for_instance(request.user, 'change', doc):
        return permission_denied(request)

    if request.method == 'POST':
        original_file = doc.file
        form = DocumentForm(request.POST, request.FILES, instance=doc, user=request.user)
        if form.is_valid():
            doc = form.save()
            if 'file' in form.changed_data:
                doc.file_size = doc.file.size

                # if providing a new document file, delete the old one.
                # NB Doing this via original_file.delete() clears the file field,
                # which definitely isn't what we want...
                original_file.storage.delete(original_file.name)

            # Reindex the document to make sure all tags are indexed
            search_index.insert_or_update_object(doc)

            messages.success(request, _("Document '{0}' updated").format(doc.title), buttons=[
                messages.button(reverse('wagtaildocs:edit', args=(doc.id,)), _('Edit'))
            ])
            return redirect('wagtaildocs:index')
        else:
            messages.error(request, _("The document could not be saved due to errors."))
    else:
        form = DocumentForm(instance=doc, user=request.user)

    try:
        local_path = doc.file.path
    except NotImplementedError:
        # Document is hosted externally (eg, S3)
        local_path = None

    if local_path:
        # Give error if document file doesn't exist
        if not os.path.isfile(local_path):
            messages.error(
                request,
                _("The file could not be found. Please change the source or delete the document"),
                buttons=[messages.button(reverse('wagtaildocs:delete', args=(doc.id,)), _('Delete'))]
            )

    return render(request, "wagtaildocs/documents/edit.html", {
        'document': doc,
        'filesize': doc.get_file_size(),
        'form': form,
        'user_can_delete': permission_policy.user_has_permission_for_instance(
            request.user, 'delete', doc
        ),
    })
def edit(request, document_id):
    Document = get_document_model()
    DocumentForm = get_document_form(Document)

    doc = get_object_or_404(Document, id=document_id)

    if not permission_policy.user_has_permission_for_instance(request.user, 'change', doc):
        return permission_denied(request)

    if request.method == 'POST':
        original_file = doc.file
        form = DocumentForm(request.POST, request.FILES, instance=doc, user=request.user)
        if form.is_valid():
            doc = form.save()
            if 'file' in form.changed_data:
                # if providing a new document file, delete the old one.
                # NB Doing this via original_file.delete() clears the file field,
                # which definitely isn't what we want...
                original_file.storage.delete(original_file.name)

            # Reindex the document to make sure all tags are indexed
            search_index.insert_or_update_object(doc)

            messages.success(request, _("Document '{0}' updated").format(doc.title), buttons=[
                messages.button(reverse('wagtaildocs:edit', args=(doc.id,)), _('Edit'))
            ])
            return redirect('wagtaildocs:index')
        else:
            messages.error(request, _("The document could not be saved due to errors."))
    else:
        form = DocumentForm(instance=doc, user=request.user)

    filesize = None

    # Get file size when there is a file associated with the Document object
    if doc.file:
        try:
            filesize = doc.file.size
        except OSError:
            # File doesn't exist
            pass

    if not filesize:
        messages.error(
            request,
            _("The file could not be found. Please change the source or delete the document"),
            buttons=[messages.button(reverse('wagtaildocs:delete', args=(doc.id,)), _('Delete'))]
        )

    return render(request, "wagtaildocs/documents/edit.html", {
        'document': doc,
        'filesize': filesize,
        'form': form,
        'user_can_delete': permission_policy.user_has_permission_for_instance(
            request.user, 'delete', doc
        ),
    })
Example #41
0
def serve(request, document_id, document_filename):
    Document = get_document_model()
    doc = get_object_or_404(Document, id=document_id)

    # We want to ensure that the document filename provided in the URL matches the one associated with the considered
    # document_id. If not we can't be sure that the document the user wants to access is the one corresponding to the
    # <document_id, document_filename> pair.
    if doc.filename != document_filename:
        raise Http404('This document does not match the given filename.')

    for fn in hooks.get_hooks('before_serve_document'):
        result = fn(doc, request)
        if isinstance(result, HttpResponse):
            return result

    # Send document_served signal
    document_served.send(sender=Document, instance=doc, request=request)

    try:
        local_path = doc.file.path
    except NotImplementedError:
        local_path = None

    if local_path:

        # Use wagtail.utils.sendfile to serve the file;
        # this provides support for mimetypes, if-modified-since and django-sendfile backends

        if hasattr(settings, 'SENDFILE_BACKEND'):
            return sendfile(request, local_path, attachment=True, attachment_filename=doc.filename)
        else:
            # Fallback to streaming backend if user hasn't specified SENDFILE_BACKEND
            return sendfile(
                request,
                local_path,
                attachment=True,
                attachment_filename=doc.filename,
                backend=sendfile_streaming_backend.sendfile
            )

    else:

        # We are using a storage backend which does not expose filesystem paths
        # (e.g. storages.backends.s3boto.S3BotoStorage).
        # Fall back on pre-sendfile behaviour of reading the file content and serving it
        # as a StreamingHttpResponse

        wrapper = FileWrapper(doc.file)
        response = StreamingHttpResponse(wrapper, content_type='application/octet-stream')

        response['Content-Disposition'] = 'attachment; filename=%s' % doc.filename

        # FIXME: storage backends are not guaranteed to implement 'size'
        response['Content-Length'] = doc.file.size

        return response
Example #42
0
def usage(request, document_id):
    Document = get_document_model()
    doc = get_object_or_404(Document, id=document_id)

    paginator, used_by = paginate(request, doc.get_usage())

    return render(request, "wagtaildocs/documents/usage.html", {
        'document': doc,
        'used_by': used_by
    })
Example #43
0
def usage(request, document_id):
    Document = get_document_model()
    doc = get_object_or_404(Document, id=document_id)

    paginator, used_by = paginate(request, doc.get_usage())

    return render(request, "wagtaildocs/documents/usage.html", {
        'document': doc,
        'used_by': used_by
    })
Example #44
0
def usage(request, document_id):
    Document = get_document_model()
    doc = get_object_or_404(Document, id=document_id)

    paginator = Paginator(doc.get_usage(), per_page=20)
    used_by = paginator.get_page(request.GET.get('p'))

    return render(request, "wagtaildocs/documents/usage.html", {
        'document': doc,
        'used_by': used_by
    })
Example #45
0
 def expand_db_attributes(attrs):
     Document = get_document_model()
     try:
         doc = Document.objects.get(id=attrs['id'])
         return '<a data-linktype="document" data-id="%d" href="%s">' % (doc.id, escape(doc.url))
     except Document.DoesNotExist:
         # Preserve the ID attribute for troubleshooting purposes, even though it
         # points to a missing document
         return '<a data-linktype="document" data-id="%s">' % attrs['id']
     except KeyError:
         return '<a data-linktype="document">'
def describe_collection_docs(collection):
    docs_count = get_document_model().objects.filter(collection=collection).count()
    if docs_count:
        url = reverse('wagtaildocs:index') + ('?collection_id=%d' % collection.id)
        return {
            'count': docs_count,
            'count_text': ungettext(
                "%(count)s document",
                "%(count)s documents",
                docs_count
            ) % {'count': docs_count},
            'url': url,
        }
Example #47
0
    def expand_db_attributes(attrs, for_editor):
        Document = get_document_model()
        try:
            doc = Document.objects.get(id=attrs['id'])

            if for_editor:
                editor_attrs = 'data-linktype="document" data-id="%d" ' % doc.id
            else:
                editor_attrs = ''

            return '<a %shref="%s">' % (editor_attrs, escape(doc.url))
        except Document.DoesNotExist:
            return "<a>"
Example #48
0
def delete(request, document_id):
    Document = get_document_model()
    doc = get_object_or_404(Document, id=document_id)

    if not permission_policy.user_has_permission_for_instance(request.user, 'delete', doc):
        return permission_denied(request)

    if request.method == 'POST':
        doc.delete()
        messages.success(request, _("Document '{0}' deleted.").format(doc.title))
        return redirect('wagtaildocs:index')

    return render(request, "wagtaildocs/documents/confirm_delete.html", {
        'document': doc,
    })
def serve_document_from_s3(document, request):
    # Skip this hook if not using django-storages boto3 backend.
    if not issubclass(get_storage_class(), S3Boto3Storage):
        return

    # Send document_served signal.
    document_served.send(sender=get_document_model(), instance=document,
                         request=request)

    # Get direct S3 link.
    file_url = document.file.url

    # Generate redirect response and add never_cache headers.
    response = redirect(file_url)
    del response['Cache-control']
    add_never_cache_headers(response)
    return response
Example #50
0
    def get_attribute_data(self, attrs):
        Document = get_document_model()
        try:
            id = int(attrs['id'])
        except (KeyError, ValueError):
            return {}

        try:
            doc = Document.objects.get(id=id)
        except Document.DoesNotExist:
            return {'id': id}

        return {
            'id': doc.id,
            'url': doc.url,
            'filename': doc.filename,
        }
Example #51
0
    def test_delete_post(self):
        """
        This tests that a POST request to the delete view deletes the document
        """
        # Send request
        response = self.client.post(reverse('wagtaildocs:delete_multiple', args=(self.doc.id, )), HTTP_X_REQUESTED_WITH='XMLHttpRequest')

        # Check response
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response['Content-Type'], 'application/json')

        # Make sure the document is deleted
        self.assertFalse(models.get_document_model().objects.filter(id=self.doc.id).exists())

        # Check JSON
        response_json = json.loads(response.content.decode())
        self.assertIn('doc_id', response_json)
        self.assertIn('success', response_json)
        self.assertEqual(response_json['doc_id'], self.doc.id)
        self.assertTrue(response_json['success'])
Example #52
0
def chooser_upload(request):
    Document = get_document_model()
    DocumentForm = get_document_form(Document)
    DocumentMultiForm = get_document_multi_form(Document)

    if request.method == 'POST':
        if not request.is_ajax():
            return http.HttpResponseBadRequest(
                "Cannot POST to this view without AJAX")

        if not request.FILES:
            return http.HttpResponseBadRequest("Must upload a file")

        # Save it
        document = Document(uploaded_by_user=request.user,
                            title=request.FILES['files[]'].name,
                            file=request.FILES['files[]'])
        document.save()

        # Success! Send back an edit form for this image to the user
        form = DocumentMultiForm(instance=document,
                                 prefix='doc-%d' % document.id,
                                 user=request.user)

        return http.JsonResponse({
            'success': True,
            'doc_id': int(document.id),
            'form': render_to_string('wagtaildocs/chooser/update.html', {
                'doc': document,
                'form': form,
            }, request=request),
        })
    else:
        form = DocumentForm(user=request.user)

    documents = Document.objects.order_by('title')

    return render_modal_workflow(
        request, 'wagtaildocs/chooser/chooser.html', 'wagtaildocs/chooser/chooser.js',
        {'documents': documents, 'uploadform': form}
    )
Example #53
0
    def test_basic(self):
        response = self.get_response()

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response['Content-type'], 'application/json')

        # Will crash if the JSON is invalid
        content = json.loads(response.content.decode('UTF-8'))

        # Check that the meta section is there
        self.assertIn('meta', content)
        self.assertIsInstance(content['meta'], dict)

        # Check that the total count is there and correct
        self.assertIn('total_count', content['meta'])
        self.assertIsInstance(content['meta']['total_count'], int)
        self.assertEqual(content['meta']['total_count'], get_document_model().objects.count())

        # Check that the items section is there
        self.assertIn('items', content)
        self.assertIsInstance(content['items'], list)

        # Check that each document has a meta section with type and detail_url attributes
        for document in content['items']:
            self.assertIn('meta', document)
            self.assertIsInstance(document['meta'], dict)
            self.assertEqual(set(document['meta'].keys()), {'type', 'detail_url', 'download_url', 'tags'})

            # Type should always be wagtaildocs.Document
            self.assertEqual(document['meta']['type'], 'wagtaildocs.Document')

            # Check detail_url
            self.assertEqual(document['meta']['detail_url'], 'http://localhost/api/v2beta/documents/%d/' % document['id'])

            # Check download_url
            self.assertTrue(document['meta']['download_url'].startswith('http://localhost/documents/%d/' % document['id']))
Example #54
0
def index(request):
    Document = get_document_model()

    # Get documents (filtered by user permission)
    documents = permission_policy.instances_user_has_any_permission_for(
        request.user, ['change', 'delete']
    )

    # Ordering
    if 'ordering' in request.GET and request.GET['ordering'] in ['title', '-created_at']:
        ordering = request.GET['ordering']
    else:
        ordering = '-created_at'
    documents = documents.order_by(ordering)

    # Filter by collection
    current_collection = None
    collection_id = request.GET.get('collection_id')
    if collection_id:
        try:
            current_collection = Collection.objects.get(id=collection_id)
            documents = documents.filter(collection=current_collection)
        except (ValueError, Collection.DoesNotExist):
            pass

    # Search
    query_string = None
    if 'q' in request.GET:
        form = SearchForm(request.GET, placeholder=_("Search documents"))
        if form.is_valid():
            query_string = form.cleaned_data['q']
            documents = documents.search(query_string)
    else:
        form = SearchForm(placeholder=_("Search documents"))

    # Pagination
    paginator, documents = paginate(request, documents)

    collections = permission_policy.collections_user_has_any_permission_for(
        request.user, ['add', 'change']
    )
    if len(collections) < 2:
        collections = None

    # Create response
    if request.is_ajax():
        return render(request, 'wagtaildocs/documents/results.html', {
            'ordering': ordering,
            'documents': documents,
            'query_string': query_string,
            'is_searching': bool(query_string),
        })
    else:
        return render(request, 'wagtaildocs/documents/index.html', {
            'ordering': ordering,
            'documents': documents,
            'query_string': query_string,
            'is_searching': bool(query_string),

            'search_form': form,
            'popular_tags': popular_tags_for_model(Document),
            'user_can_add': permission_policy.user_has_permission(request.user, 'add'),
            'collections': collections,
            'current_collection': current_collection,
        })
Example #55
0
 def setUpClass(cls):
     super().setUpClass()
     Document = models.get_document_model()
     fields = tuple(f for f in Document.admin_form_fields if f != 'collection')
     cls.__patcher = mock.patch.object(Document, 'admin_form_fields', fields)
     cls.__patcher.start()
Example #56
0
from wagtail.core.permission_policies.collections import CollectionOwnershipPermissionPolicy
from wagtail.documents.models import Document, get_document_model

permission_policy = CollectionOwnershipPermissionPolicy(
    get_document_model(),
    auth_model=Document,
    owner_field_name='uploaded_by_user'
)
 def get_context(self):
     return {
         'total_docs': get_document_model().objects.count(),
     }