def upload_file(request): """ POSTing a file upload is very different than any other endpoint in assembl API because all of the content will be passed in using a MULTIPART_HEADER, with all of data as well as the file (along with its metadata) """ # Testing purposes on front-end # raise Exception("Upload file exception occured!") db = Document.default_db ctx = request.context user_id = authenticated_userid(request) or Everyone discusison_id = ctx.get_discussion_id() discussion = Discussion.get(discusison_id) mime = request.POST['mime_type'] file_name = request.POST['name'] # Check if the file has previously existed, if so, change the name by appending "(n)" # to it's name try: blob = File(discussion=discussion, mime_type=mime, title=file_name) db.add(blob) with request.POST['file'].file as f: blob.add_file_data(f) db.flush() except Exception as e: raise HTTPServerError(e) view = 'default' return blob.generic_json(view, user_id, ctx.get_permissions())
def mime_type(request): url = request.params.get('url', None) if not url: raise HTTPBadRequest("Missing 'url' parameter") parsed = urlparse(url) if not parsed or parsed.scheme not in ('http', 'https'): raise HTTPBadRequest("Wrong scheme") if parsed.netloc.split(":")[0] == config.get('public_hostname'): # is it one of our own documents? # If so, detect it and shortcut to avoid the pyramid handler calling # another pyramid handler, as this exhausts pyramid threads rapidly # and can deadlock the whole application r = re.match( r'^https?://[\w\.]+(?:\:\d+)?/data/.*/documents/(\d+)/data(?:\?.*)?$', url) if r: document_id = r.groups(0)[0] from sqlalchemy.sql.functions import func mimetype, create_date, file_identity = File.default_db.query( File.mime_type, File.creation_date, file.file_identity).filter_by(id=int(document_id)).first() size = path.getsize(File.path_of(file_identity)) return Response(body=None, content_type=str(mimetype), content_length=size, last_modified=create_date) try: result = requests.head(url, timeout=15) except requests.ConnectionError: return Response(status=503, location=url) return Response(content_type=result.headers.get('Content-Type', None), status=result.status_code, location=result.url)
def get_file(request): # TODO: Add a route that enables the call to have the filename # appended to the end. This is so that gmail can read the services # Read more here: # http://stackoverflow.com/questions/20903967/gmails-new-image-caching-is-breaking-image-links-in-newsletter ctx = request.context document = ctx._instance f = File.get(document.id) return Response(body=f.data, content_type=str(f.mime_type))
def simple_file(request, discussion, test_session): from assembl.models import File f = File(discussion=discussion, mime_type='image/png', title='simple_image.png') test_session.add(f) f.add_raw_data(os.urandom(256)) test_session.flush() def fin(): print("finalizer simple_file") f.delete_file() test_session.delete(f) test_session.flush() request.addfinalizer(fin) return f
def simple_file2(request, discussion, test_session): from assembl.models import File f = File(discussion=discussion, mime_type='application/pdf', title='mydocument.pdf') test_session.add(f) f.add_raw_data(os.urandom(256)) test_session.flush() def fin(): print("finalizer simple_file") f.delete_file() test_session.delete(f) test_session.flush() request.addfinalizer(fin) return f
def get_file_header(request): ctx = request.context document = ctx._instance f = File.get(document.id) if f.infected: raise HTTPNotAcceptable("Infected with a virus") handoff_to_nginx = asbool(config.get('handoff_to_nginx', False)) return Response( content_length=f.size, content_type=str(f.mime_type), last_modified=f.creation_date, expires=datetime.now() + timedelta(days=365), accept_ranges="bytes" if handoff_to_nginx else "none", )
def get_file_header(request): ctx = request.context document = ctx._instance f = File.get(document.id) if f.infected: raise HTTPNotAcceptable("Infected with a virus") handoff_to_nginx = asbool(config.get('handoff_to_nginx', False)) return Response( content_length=f.size, content_type=str(f.mime_type), last_modified=f.creation_date, expires=datetime.now()+timedelta(days=365), accept_ranges="bytes" if handoff_to_nginx else "none", )
def upload_file(request): """ POSTing a file upload is very different than any other endpoint in assembl API because all of the content will be passed in using a MULTIPART_HEADER, with all of data as well as the file (along with its metadata) """ # Testing purposes on front-end # raise Exception("Upload file exception occured!") db = Document.default_db ctx = request.context user_id = request.authenticated_userid or Everyone discusison_id = ctx.get_discussion_id() discussion = Discussion.get(discusison_id) permissions = get_permissions(user_id, discusison_id) mime = request.POST['mime_type'] file_name = request.POST['name'] # Check if the file has previously existed, if so, change the name by appending "(n)" # to it's name try: blob = File(discussion=discussion, mime_type=mime, title=file_name) db.add(blob) with request.POST['file'].file as f: blob.add_file_data(f) db.flush() except Exception as e: raise HTTPServerError(e) view = 'default' return blob.generic_json(view, user_id, permissions)
def get_file(request): # TODO: Add a route that enables the call to have the filename # appended to the end. This is so that gmail can read the services # Read more here: # http://stackoverflow.com/questions/20903967/gmails-new-image-caching-is-breaking-image-links-in-newsletter ctx = request.context document = ctx._instance f = File.get(document.id) if f.infected: raise HTTPNotAcceptable("Infected with a virus") escaped_double_quotes_filename = (f.title .replace(u'"', u'\\"') .encode('iso-8859-1', 'replace')) url_quoted_utf8_filename = url_quote(f.title.encode('utf-8')) handoff_to_nginx = asbool(config.get('handoff_to_nginx', False)) if handoff_to_nginx: kwargs = dict(body='') else: if 'Range' in request.headers: raise HTTPRequestRangeNotSatisfiable() fs = open(f.path, 'rb') app_iter = None environ = request.environ if 'wsgi.file_wrapper' in environ: app_iter = environ['wsgi.file_wrapper'](fs, _BLOCK_SIZE) if app_iter is None: app_iter = FileIter(fs, _BLOCK_SIZE) kwargs=dict(app_iter=app_iter) r = Response( content_length=f.size, content_type=str(f.mime_type), last_modified=f.creation_date, expires=datetime.now()+timedelta(days=365), accept_ranges="bytes" if handoff_to_nginx else "none", content_disposition= 'attachment; filename="%s"; filename*=utf-8\'\'%s' # RFC 6266 % (escaped_double_quotes_filename, url_quoted_utf8_filename), **kwargs ) if handoff_to_nginx: r.headers[b'X-Accel-Redirect'] = f.handoff_url return r
def get_file(request): # TODO: Add a route that enables the call to have the filename # appended to the end. This is so that gmail can read the services # Read more here: # http://stackoverflow.com/questions/20903967/gmails-new-image-caching-is-breaking-image-links-in-newsletter if request.method == 'HEAD': # GET view_config captures HEAD... return get_file_header(request) ctx = request.context document = ctx._instance f = File.get(document.id) if f.infected: raise HTTPNotAcceptable("Infected with a virus") handoff_to_nginx = asbool(config.get('handoff_to_nginx', False)) if handoff_to_nginx: kwargs = dict(body='') else: if 'Range' in request.headers: raise HTTPRequestRangeNotSatisfiable() fs = f.file_stream app_iter = None environ = request.environ if 'wsgi.file_wrapper' in environ and f.path: app_iter = environ['wsgi.file_wrapper'](fs, _BLOCK_SIZE) if app_iter is None: app_iter = FileIter(fs, _BLOCK_SIZE) kwargs = dict(app_iter=app_iter) r = Response( content_length=f.file_size, content_type=str(f.mime_type), last_modified=f.creation_date, expires=datetime.now() + timedelta(days=365), accept_ranges="bytes" if handoff_to_nginx else "none", content_disposition=disposition(f.title), # RFC 6266 **kwargs ) if handoff_to_nginx: r.headers[b'X-Accel-Redirect'] = f.handoff_url return r
def mime_type(request): url = request.params.get('url', None) if not url: raise HTTPBadRequest("Missing 'url' parameter") parsed = urlparse(url) if not parsed or parsed.scheme not in ('http', 'https'): raise HTTPBadRequest("Wrong scheme") if parsed.netloc.split(":")[0] == config.get('public_hostname'): # is it one of our own documents? # If so, detect it and shortcut to avoid the pyramid handler calling # another pyramid handler, as this exhausts pyramid threads rapidly # and can deadlock the whole application r = re.match( r'^https?://[\w\.]+(?:\:\d+)?/data/.*/documents/(\d+)/data(?:\?.*)?$', url) if r: document_id = r.groups(0)[0] from sqlalchemy.sql.functions import func mimetype, create_date, file_identity = File.default_db.query( File.mime_type, File.creation_date, file.file_identity ).filter_by(id=int(document_id)).first() size = path.getsize(File.path_of(file_identity)) return Response( body=None, content_type=str(mimetype), content_length=size, last_modified=create_date) try: result = requests.head(url, timeout=15) except requests.ConnectionError: return Response( status=503, location=url) return Response( content_type=result.headers.get('Content-Type', None), status=result.status_code, location=result.url)
def discussion(request, test_session, participant2_user, default_preferences): """An empty Discussion fixture with default preferences""" from assembl.models import Discussion, DiscussionAttachment, File, LangString, AttachmentPurpose # from assembl.lib.migration import create_default_discussion_data with test_session.no_autoflush: d = Discussion( topic=u"Jack Layton", slug="jacklayton2", subscribe_to_notifications_on_signup=False, creation_date=datetime.datetime.utcnow(), creator=None, session=test_session) d.discussion_locales = ['en', 'fr', 'de'] d.legal_notice = LangString.create( u"We need to input the optical HDD sensor!", "en") tac = LangString.create( u"You can't quantify the driver without quantifying the 1080p JSON protocol!", "en") tac.add_value( u"Vous ne pouvez pas mesurer le driver sans mesurer le protocole JSON en 1080p", u"fr") d.terms_and_conditions = tac title = LangString.create( u"Faut-il manger des bananes ?", u"fr") title.add_value( u"Should we eat bananas?", u"en") d.title = title subtitle = LangString.create( u"Dis-moi ce que tu manges et je te dirai qui tu es", u"fr") subtitle.add_value( u"Tell me what you eat and I will tell you who you are", u"en") d.subtitle = subtitle button_label = LangString.create( u"Discuter des bananes", u"fr") button_label.add_value( u"Discuss bananas", u"en") d.button_label = button_label # add a privacy policy attachment to the discussion document = File( discussion=d, mime_type='image/png', title='simple_image.png' ) test_session.add(document) document.add_raw_data(os.urandom(256)) test_session.add(document) attachment = DiscussionAttachment( discussion=d, document=document, creator_id=participant2_user.id, title='A privacy policy attachment', attachmentPurpose=AttachmentPurpose.PRIVACY_POLICY_ATTACHMENT.value ) test_session.add(attachment) test_session.add(d) # create_default_discussion_data(d) # Don't create default discussion data (permissions, sections) here # because it takes too much time to run all tests. # If you need sections or permissions in your tests, execute # create_default_discussion_data, create_default_discussion_sections # or create_default_permissions in your specific test or # use discussion_with_default_data fixture. # If you do permissions tests, be aware that the admin user # having R_SYSADMIN is actually a special case, see # auth/utils.py:get_permissions, it doesn't use discussion permissions # at all. So you need discussion permissions if you test with the # unauthenticated user Everyone or a user not having the R_SYSADMIN role. test_session.flush() def fin(): print("finalizer discussion") discussion = d if inspect(discussion).detached: # How did this happen? discussion = test_session.query(Discussion).get(d.id) test_session.delete(attachment) test_session.delete(document) test_session.delete(discussion.table_of_contents) test_session.delete(discussion.root_idea) test_session.delete(discussion.next_synthesis) preferences = discussion.preferences discussion.preferences = None discussion.preferences_id = None for ut in discussion.user_templates: for ns in ut.notification_subscriptions: ns.delete() ut.delete() test_session.delete(preferences) test_session.delete(discussion) test_session.flush() request.addfinalizer(fin) return d
def discussion(request, test_session, participant2_user, default_preferences): """An empty Discussion fixture with default preferences""" from assembl.models import Discussion, DiscussionAttachment, File, LangString, AttachmentPurpose # from assembl.lib.migration import create_default_discussion_data with test_session.no_autoflush: d = Discussion( topic=u"Jack Layton", slug="jacklayton2", subscribe_to_notifications_on_signup=False, creator=None, session=test_session) d.discussion_locales = ['en', 'fr', 'de'] d.legal_notice = LangString.create( u"We need to input the optical HDD sensor!", "en") tac = LangString.create( u"You can't quantify the driver without quantifying the 1080p JSON protocol!", "en") tac.add_value( u"Vous ne pouvez pas mesurer le driver sans mesurer le protocole JSON en 1080p", u"fr") d.terms_and_conditions = tac title = LangString.create( u"Faut-il manger des bananes ?", u"fr") title.add_value( u"Should we eat bananas?", u"en") d.title = title subtitle = LangString.create( u"Dis-moi ce que tu manges et je te dirai qui tu es", u"fr") subtitle.add_value( u"Tell me what you eat and I will tell you who you are", u"en") d.subtitle = subtitle button_label = LangString.create( u"Discuter des bananes", u"fr") button_label.add_value( u"Discuss bananas", u"en") d.button_label = button_label # add a privacy policy attachment to the discussion document = File( discussion=d, mime_type='image/png', title='simple_image.png' ) test_session.add(document) document.add_raw_data(os.urandom(256)) test_session.add(document) attachment = DiscussionAttachment( discussion=d, document=document, creator_id=participant2_user.id, title='A privacy policy attachment', attachmentPurpose=AttachmentPurpose.PRIVACY_POLICY_ATTACHMENT.value ) test_session.add(attachment) test_session.add(d) # create_default_discussion_data(d) # Don't create default discussion data (permissions, sections) here # because it takes too much time to run all tests. # If you need sections or permissions in your tests, execute # create_default_discussion_data, create_default_discussion_sections # or create_default_permissions in your specific test or # use discussion_with_default_data fixture. # If you do permissions tests, be aware that the admin user # having R_SYSADMIN is actually a special case, see # auth/utils.py:get_permissions, it doesn't use discussion permissions # at all. So you need discussion permissions if you test with the # unauthenticated user Everyone or a user not having the R_SYSADMIN role. test_session.flush() def fin(): print "finalizer discussion" discussion = d if inspect(discussion).detached: # How did this happen? discussion = test_session.query(Discussion).get(d.id) test_session.delete(attachment) test_session.delete(document) test_session.delete(discussion.table_of_contents) test_session.delete(discussion.root_idea) test_session.delete(discussion.next_synthesis) preferences = discussion.preferences discussion.preferences = None discussion.preferences_id = None for ut in discussion.user_templates: for ns in ut.notification_subscriptions: ns.delete() ut.delete() test_session.delete(preferences) test_session.delete(discussion) test_session.flush() request.addfinalizer(fin) return d