def test_affiliated_institutions_are_copied_from_user( self, app, user, url_draft_registrations, payload): project = ProjectFactory(is_public=True, creator=user) InstitutionFactory() payload['data']['relationships']['branched_from']['data'][ 'id'] = project._id res = app.post_json_api(url_draft_registrations, payload, auth=user.auth, expect_errors=True) assert res.status_code == 201 draft_registration = DraftRegistration.load(res.json['data']['id']) assert not draft_registration.affiliated_institutions.exists() project = ProjectFactory(is_public=True, creator=user) payload['data']['relationships']['branched_from']['data'][ 'id'] = project._id user.affiliated_institutions.set(Institution.objects.filter(id__lt=3)) res = app.post_json_api(url_draft_registrations, payload, auth=user.auth, expect_errors=True) assert res.status_code == 201 draft_registration = DraftRegistration.load(res.json['data']['id']) assert not draft_registration.affiliated_institutions.all( ) == user.affiliated_institutions.all()
def post(self, request, *args, **kwargs): try: data = json.loads(request.body).get('schema_data', {}) draft = DraftRegistration.load(self.kwargs.get('draft_pk')) draft.update_metadata(data) draft.save() log_message = list() for key, value in data.iteritems(): comments = data.get(key, {}).get('comments', []) for comment in comments: log_message.append('{}: {}'.format(key, comment['value'])) update_admin_log( user_id=request.user.id, object_id=draft._id, object_repr='Draft Registration', message='Comments: <p>{}</p>'.format('</p><p>'.join(log_message)), action_flag=COMMENT_PREREG ) return JsonResponse(serializers.serialize_draft_registration(draft)) except AttributeError: raise Http404('{} with id "{}" not found.'.format( self.context_object_name.title(), self.kwargs.get('draft_pk') )) except NodeStateError as e: return bad_request(request, e)
def dispatch(self, request, *args, **kwargs): self.draft = DraftRegistration.load(self.kwargs.get('draft_pk')) if self.draft is None: raise Http404('{} with id "{}" not found.'.format( self.context_object_name.title(), self.kwargs.get('draft_pk') )) return super(DraftFormView, self).dispatch(request, *args, **kwargs)
def test_create_draft_with_provider(self, app, user, url_draft_registrations, non_default_provider, payload_with_non_default_provider): res = app.post_json_api(url_draft_registrations, payload_with_non_default_provider, auth=user.auth) assert res.status_code == 201 data = res.json['data'] assert data['relationships']['provider']['links']['related']['href'] == \ f'{settings.API_DOMAIN}v2/providers/registrations/{non_default_provider._id}/' draft = DraftRegistration.load(data['id']) assert draft.provider == non_default_provider
def test_admin_can_create_draft( self, app, user, project_public, url_draft_registrations, payload, metaschema_open_ended): url = '{}embed=branched_from&embed=initiator'.format(url_draft_registrations) res = app.post_json_api(url, payload, auth=user.auth) assert res.status_code == 201 data = res.json['data'] assert metaschema_open_ended._id in data['relationships']['registration_schema']['links']['related']['href'] assert data['attributes']['registration_metadata'] == {} assert data['embeds']['branched_from']['data']['id'] == DraftRegistration.objects.get(_id=data['id']).branched_from._id assert data['embeds']['initiator']['data']['id'] == user._id draft = DraftRegistration.load(data['id']) assert draft.creator == user assert draft.has_permission(user, ADMIN) is True
def prereg_reminder(email): """ Check make sure the draft still exists, has not already received a reminder, and has not been submitted. :param email: QueuedMail object, with the 'draft_id' in its data field :return: boolean based on whether the email should be sent """ # In line import to prevent circular importing from osf.models.queued_mail import QueuedMail from osf.models import DraftRegistration draft_id = email.data['draft_id'] draft = DraftRegistration.load(draft_id) reminders_sent = QueuedMail.objects.filter(data__draft_id=draft_id).exclude(sent_at=None).exists() return draft and not draft.deleted and not reminders_sent and not draft.registered_node
def prereg_reminder(email): """ Check make sure the draft still exists, has not already received a reminder, and has not been submitted. :param email: QueuedMail object, with the 'draft_id' in its data field :return: boolean based on whether the email should be sent """ # In line import to prevent circular importing from osf.models.queued_mail import QueuedMail from osf.models import DraftRegistration draft_id = email.data['draft_id'] draft = DraftRegistration.load(draft_id) reminders_sent = QueuedMail.objects.filter( data__draft_id=draft_id).exclude(sent_at=None).exists() return draft and not draft.deleted and not reminders_sent and not draft.registered_node
def test_create_no_project_draft_emails_initiator(self, app, user, url_draft_registrations, payload): post_url = url_draft_registrations + 'embed=branched_from&embed=initiator' # Intercepting the send_mail call from website.project.views.contributor.notify_added_contributor with mock.patch.object(mails, 'send_mail') as mock_send_mail: resp = app.post_json_api(post_url, payload, auth=user.auth) assert mock_send_mail.called # Python 3.6 does not support mock.call_args.args/kwargs # Instead, mock.call_args[0] is positional args, mock.call_args[1] is kwargs # (note, this is compatible with later versions) mock_send_kwargs = mock_send_mail.call_args[1] assert mock_send_kwargs[ 'mail'] == mails.CONTRIBUTOR_ADDED_DRAFT_REGISTRATION assert mock_send_kwargs['user'] == user assert mock_send_kwargs['node'] == DraftRegistration.load( resp.json['data']['id'])
def addon_view_or_download_file(auth, path, provider, **kwargs): extras = request.args.to_dict() extras.pop('_', None) # Clean up our url params a bit action = extras.get('action', 'view') guid = kwargs.get('guid') guid_target = getattr(Guid.load(guid), 'referent', None) target = guid_target or kwargs.get('node') or kwargs['project'] provider_safe = markupsafe.escape(provider) path_safe = markupsafe.escape(path) if not path: raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if hasattr(target, 'get_addon'): node_addon = target.get_addon(provider) if not isinstance(node_addon, BaseStorageAddon): object_text = markupsafe.escape( getattr(target, 'project_or_component', 'this object')) raise HTTPError( http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'The {} add-on containing {} is no longer connected to {}.' .format(provider_safe, path_safe, object_text) }) if not node_addon.has_auth: raise HTTPError( http_status.HTTP_401_UNAUTHORIZED, data={ 'message_short': 'Unauthorized', 'message_long': 'The {} add-on containing {} is no longer authorized.'. format(provider_safe, path_safe) }) if not node_addon.complete: raise HTTPError( http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'The {} add-on containing {} is no longer configured.'. format(provider_safe, path_safe) }) savepoint_id = transaction.savepoint() file_node = BaseFileNode.resolve_class(provider, BaseFileNode.FILE).get_or_create( target, path) # Note: Cookie is provided for authentication to waterbutler # it is overriden to force authentication as the current user # the auth header is also pass to support basic auth version = file_node.touch( request.headers.get('Authorization'), **dict(extras, cookie=request.cookies.get(settings.COOKIE_NAME))) if version is None: # File is either deleted or unable to be found in the provider location # Rollback the insertion of the file_node transaction.savepoint_rollback(savepoint_id) if not file_node.pk: file_node = BaseFileNode.load(path) if not file_node: raise HTTPError(http_status.HTTP_404_NOT_FOUND, data={ 'message_short': 'File Not Found', 'message_long': 'The requested file could not be found.' }) if file_node.kind == 'folder': raise HTTPError( http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'You cannot request a folder from this endpoint.' }) # Allow osfstorage to redirect if the deep url can be used to find a valid file_node if file_node.provider == 'osfstorage' and not file_node.is_deleted: return redirect( file_node.target.web_url_for('addon_view_or_download_file', path=file_node._id, provider=file_node.provider)) return addon_deleted_file(target=target, file_node=file_node, path=path, **kwargs) else: transaction.savepoint_commit(savepoint_id) # TODO clean up these urls and unify what is used as a version identifier if request.method == 'HEAD': return make_response(('', http_status.HTTP_302_FOUND, { 'Location': file_node.generate_waterbutler_url( **dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render')) })) if action == 'download': format = extras.get('format') _, extension = os.path.splitext(file_node.name) # avoid rendering files with the same format type. if format and '.{}'.format(format.lower()) != extension.lower(): return redirect('{}/export?format={}&url={}'.format( get_mfr_url(target, provider), format, quote( file_node.generate_waterbutler_url( **dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render'))))) return redirect( file_node.generate_waterbutler_url( **dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render'))) if action == 'get_guid': draft_id = extras.get('draft') draft = DraftRegistration.load(draft_id) if draft is None or draft.is_approved: raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'File not associated with required object.' }) guid = file_node.get_guid(create=True) guid.referent.save() return dict(guid=guid._id) if len(request.path.strip('/').split('/')) > 1: guid = file_node.get_guid(create=True) return redirect( furl.furl('/{}/'.format(guid._id)).set(args=extras).url) if isinstance(target, Preprint): # Redirecting preprint file guids to the preprint detail page return redirect('/{}/'.format(target._id)) return addon_view_file(auth, target, file_node, version)
def addon_view_or_download_file(auth, path, provider, **kwargs): extras = request.args.to_dict() extras.pop('_', None) # Clean up our url params a bit action = extras.get('action', 'view') guid = kwargs.get('guid') guid_target = getattr(Guid.load(guid), 'referent', None) target = guid_target or kwargs.get('node') or kwargs['project'] provider_safe = markupsafe.escape(provider) path_safe = markupsafe.escape(path) if not path: raise HTTPError(httplib.BAD_REQUEST) if hasattr(target, 'get_addon'): node_addon = target.get_addon(provider) if not isinstance(node_addon, BaseStorageAddon): object_text = markupsafe.escape(getattr(target, 'project_or_component', 'this object')) raise HTTPError(httplib.BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'The {} add-on containing {} is no longer connected to {}.'.format(provider_safe, path_safe, object_text) }) if not node_addon.has_auth: raise HTTPError(httplib.UNAUTHORIZED, data={ 'message_short': 'Unauthorized', 'message_long': 'The {} add-on containing {} is no longer authorized.'.format(provider_safe, path_safe) }) if not node_addon.complete: raise HTTPError(httplib.BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'The {} add-on containing {} is no longer configured.'.format(provider_safe, path_safe) }) savepoint_id = transaction.savepoint() file_node = BaseFileNode.resolve_class(provider, BaseFileNode.FILE).get_or_create(target, path) # Note: Cookie is provided for authentication to waterbutler # it is overriden to force authentication as the current user # the auth header is also pass to support basic auth version = file_node.touch( request.headers.get('Authorization'), **dict( extras, cookie=request.cookies.get(settings.COOKIE_NAME) ) ) if version is None: # File is either deleted or unable to be found in the provider location # Rollback the insertion of the file_node transaction.savepoint_rollback(savepoint_id) if not file_node.pk: file_node = BaseFileNode.load(path) if file_node.kind == 'folder': raise HTTPError(httplib.BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'You cannot request a folder from this endpoint.' }) # Allow osfstorage to redirect if the deep url can be used to find a valid file_node if file_node and file_node.provider == 'osfstorage' and not file_node.is_deleted: return redirect( file_node.target.web_url_for('addon_view_or_download_file', path=file_node._id, provider=file_node.provider) ) return addon_deleted_file(target=target, file_node=file_node, path=path, **kwargs) else: transaction.savepoint_commit(savepoint_id) # TODO clean up these urls and unify what is used as a version identifier if request.method == 'HEAD': return make_response(('', httplib.FOUND, { 'Location': file_node.generate_waterbutler_url(**dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render')) })) if action == 'download': format = extras.get('format') _, extension = os.path.splitext(file_node.name) # avoid rendering files with the same format type. if format and '.{}'.format(format.lower()) != extension.lower(): return redirect('{}/export?format={}&url={}'.format(get_mfr_url(target, provider), format, urllib.quote(file_node.generate_waterbutler_url( **dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render') )))) return redirect(file_node.generate_waterbutler_url(**dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render'))) if action == 'get_guid': draft_id = extras.get('draft') draft = DraftRegistration.load(draft_id) if draft is None or draft.is_approved: raise HTTPError(httplib.BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'File not associated with required object.' }) guid = file_node.get_guid(create=True) guid.referent.save() return dict(guid=guid._id) if len(request.path.strip('/').split('/')) > 1: guid = file_node.get_guid(create=True) return redirect(furl.furl('/{}/'.format(guid._id)).set(args=extras).url) if isinstance(target, Preprint): # Redirecting preprint file guids to the preprint detail page return redirect('/{}/'.format(target._id)) return addon_view_file(auth, target, file_node, version)
def get_object(self, *args, **kwargs): return DraftRegistration.load(self.kwargs.get('draft_pk'))
def get_resource(self, kwargs): resource_id = kwargs.get('draft_id') return DraftRegistration.load(resource_id)
def load_resource(self, context, view): return DraftRegistration.load(context['draft_id'])