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)))
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])
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'])
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'])
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>"
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
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() )
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))
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, })
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, })
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, }
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, })
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()
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>"
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())
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)} )
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())
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)} )
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'])
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 })
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
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()
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")), )
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))
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>"
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"), )
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(), )
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(), })
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]
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)
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 ), })
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
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 })
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 })
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, }
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>"
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
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, }
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'])
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} )
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']))
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, })
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(), }