예제 #1
0
파일: former2.py 프로젝트: former2/former2
def form():
    data = MultiDict()
    data.update(request.args)
    data.update(request.form)

    form_name = data.get('_form_name')
    redirect_url = data.get('_redirect_url')

    body = []
    for key, val in data.items():
        body.append('%s: %s' % (key, val))
        body.append('')
    body.append('')
    body.append('--')
    body.append('sent form former2 by Ondrej Sika')
    body.append('https://github.com/former2/former2')

    if form_name:
        subject = '[former2] Submit notification from %s' % form_name
    else:
        subject = '[former2] Submit notification'

    message = Message(subject,
                      sender=args.smtp_email,
                      recipients=args.notify_to.split(','))

    message.body = '\n'.join(body)
    mail.send(message)

    if redirect_url:
        return redirect(redirect_url)
    else:
        return message.body.replace('\n', '\n<br>')
예제 #2
0
def authorship(method: str, params: MultiDict, session: Session,
               submission_id: int, **kwargs) -> Response:
    """Handle the authorship assertion view."""
    submitter, client = user_and_client_from_session(session)

    # Will raise NotFound if there is no such submission.
    submission, submission_events = load_submission(submission_id)

    # The form should be prepopulated based on the current state of the
    # submission.
    if method == 'GET':
        # Update form data based on the current state of the submission.
        if submission.submitter_is_author is not None:
            if submission.submitter_is_author:
                params['authorship'] = AuthorshipForm.YES
            else:
                params['authorship'] = AuthorshipForm.NO
            if submission.submitter_is_author is False:
                params['proxy'] = True

    form = AuthorshipForm(params)
    response_data = {
        'submission_id': submission_id,
        'form': form,
        'submission': submission,
        'submitter': submitter,
        'client': client,
    }

    if method == 'POST':
        if not form.validate():
            raise BadRequest(response_data)
        value = (form.authorship.data == form.YES)

        # No need to do this more than once.
        if submission.submitter_is_author != value:
            command = ConfirmAuthorship(creator=submitter,
                                        client=client,
                                        submitter_is_author=value)

            if not validate_command(form, command, submission, 'authorship'):
                raise BadRequest(response_data)

            try:
                # Create ConfirmAuthorship event
                submission, _ = save(command, submission_id=submission_id)
                response_data['submission'] = submission
            except SaveError as e:
                logger.error('Could not save command: %s', e)
                raise InternalServerError(response_data) from e

        if params.get('action') in ['previous', 'save_exit', 'next']:
            return response_data, status.SEE_OTHER, {}
    return response_data, status.OK, {}
예제 #3
0
def _post_upload(params: MultiDict, files: MultiDict, session: Session,
                 submission: Submission, rdata: Dict[str, Any], token: str) \
        -> Response:
    """
    Compose POST request handling for the file upload endpoint.

    See the :attr:`Submission.source_content` attribute, which is set using
    :class:`SetUploadPackage`.

    Parameters
    ----------
    params : :class:`MultiDict`
        The form data from the request.
    files : :class:`MultiDict`
        File data in the multipart request. Values should be
        :class:`FileStorage` instances.
    session : :class:`Session`
        The authenticated session for the request.
    submission : :class:`Submission`
        The submission for which the request is being made.

    Returns
    -------
    dict
        Response data, to render in template.
    int
        HTTP status code. This should be ``303``, unless something goes wrong.
    dict
        Extra headers to add/update on the response. This should include
        the `Location` header for use in the 303 redirect response.

    """
    try:  # Make sure that we have a file to work with.
        pointer = files['file']
    except KeyError:  # User is going back, saving/exiting, or next step.
        pointer = None

    if not pointer:
        # Don't flash a message if the user is just trying to go back to the
        # previous page.
        logger.debug('No files on request')
        action = params.get('action', None)
        if action:
            logger.debug('User is navigating away from upload UI')
            return {}, status.SEE_OTHER, {}
    if submission.source_content is None:  # New upload package.
        logger.debug('New upload package')
        return _new_upload(params, pointer, session, submission, rdata, token)
    logger.debug('Adding additional files')
    return _new_file(params, pointer, session, submission, rdata, token)
예제 #4
0
def classification(method: str, params: MultiDict, session: Session,
                   submission_id: int, **kwargs) -> Response:
    """Handle primary classification requests."""
    submitter, client = user_and_client_from_session(session)
    submission, submission_events = load_submission(submission_id)

    if method == 'GET':
        # Prepopulate the form based on the state of the submission.
        if submission.primary_classification \
                and submission.primary_classification.category:
            params['category'] = submission.primary_classification.category

        # Use the user's default category as the default for the form.
        params.setdefault('category', session.user.profile.default_category)

    params['operation'] = PrimaryClassificationForm.ADD

    form = PrimaryClassificationForm(params)
    form.filter_choices(submission, session)

    response_data = {
        'submission_id': submission_id,
        'submission': submission,
        'submitter': submitter,
        'client': client,
        'form': form
    }

    if method == 'POST':
        if not form.validate():
            raise BadRequest(response_data)

        command = SetPrimaryClassification(category=form.category.data,
                                           creator=submitter,
                                           client=client)
        if not validate_command(form, command, submission, 'category'):
            raise BadRequest(response_data)

        try:
            submission, _ = save(command, submission_id=submission_id)
            response_data['submission'] = submission
        except SaveError as e:
            logger.error('Could not save command: %s', e)
            raise InternalServerError(response_data) from e

        if params.get('action') in ['previous', 'save_exit', 'next']:
            return response_data, status.SEE_OTHER, {}
    return response_data, status.OK, {}
예제 #5
0
def license(method: str, params: MultiDict, session: Session,
            submission_id: int, **kwargs) -> Response:
    """Convert license form data into a `SetLicense` event."""
    submitter, client = user_and_client_from_session(session)

    logger.debug(f'method: {method}, submission: {submission_id}. {params}')

    # Will raise NotFound if there is no such submission.
    submission, submission_events = load_submission(submission_id)

    if method == 'GET' and submission.license:
        # The form should be prepopulated based on the current state of the
        # submission.
        params['license'] = submission.license.uri

    form = LicenseForm(params)
    response_data = {
        'submission_id': submission_id,
        'form': form,
        'submission': submission
    }

    if method == 'POST':
        if form.validate():
            logger.debug('Form is valid, with data: %s', str(form.data))
            license_uri = form.license.data

            # If already selected, nothing more to do.
            if not submission.license or submission.license.uri != license_uri:
                command = SetLicense(creator=submitter, client=client,
                                     license_uri=license_uri)
                if not validate_command(form, command, submission, 'license'):
                    raise BadRequest(response_data)

                try:
                    # Create SetLicense event
                    submission, _ = save(command, submission_id=submission_id)
                except SaveError as e:
                    raise InternalServerError(response_data) from e
        else:   # Form data were invalid.
            raise BadRequest(response_data)

    if params.get('action') in ['previous', 'save_exit', 'next']:
        return response_data, status.SEE_OTHER, {}
    return response_data, status.OK, {}
예제 #6
0
def metadata(method: str, params: MultiDict, session: Session,
             submission_id: int, **kwargs) -> Response:
    """Update metadata on the submission."""
    submitter, client = user_and_client_from_session(session)
    logger.debug(f'method: {method}, submission: {submission_id}. {params}')

    # Will raise NotFound if there is no such submission.
    submission, submission_events = load_submission(submission_id)
    # The form should be prepopulated based on the current state of the
    # submission.
    if method == 'GET':
        params = _data_from_submission(params, submission, CoreMetadataForm)

    form = CoreMetadataForm(params)
    response_data = {
        'submission_id': submission_id,
        'form': form,
        'submission': submission
    }

    if method == 'POST':
        if not form.validate():
            raise BadRequest(response_data)

        logger.debug('Form is valid, with data: %s', str(form.data))

        commands, valid = _commands(form, submission, submitter, client)
        # We only want to apply an UpdateMetadata if the metadata has
        # actually changed.
        if commands:  # Metadata has changed.
            if not all(valid):
                logger.debug('Not all commands are valid')
                response_data['form'] = form
                raise BadRequest(response_data)

            try:
                # Save the events created during form validation.
                submission, _ = save(*commands, submission_id=submission_id)
            except SaveError as e:
                raise InternalServerError(response_data) from e
            response_data['submission'] = submission

    if params.get('action') in ['previous', 'save_exit', 'next']:
        return response_data, status.SEE_OTHER, {}
    return response_data, status.OK, {}
예제 #7
0
def finalize(method: str, params: MultiDict, session: Session,
             submission_id: int, **kwargs) -> Response:
    submitter, client = user_and_client_from_session(session)

    logger.debug(f'method: {method}, submission: {submission_id}. {params}')
    submission, submission_events = load_submission(submission_id)

    form = FinalizationForm(params)

    # The abs preview macro expects a specific struct for submission history.
    submission_history = [{
        'submitted_date': s.created,
        'version': s.version
    } for s in submission.versions]
    response_data = {
        'submission_id': submission_id,
        'form': form,
        'submission': submission,
        'submission_history': submission_history
    }

    if method == 'POST':
        if form.validate():
            logger.debug('Form is valid, with data: %s', str(form.data))
            proofread_confirmed = form.proceed.data
            if proofread_confirmed:
                command = FinalizeSubmission(creator=submitter)
                if not validate_command(form, command, submission):
                    raise BadRequest(response_data)

                try:
                    # Create ConfirmPolicy event
                    submission, stack = save(  # pylint: disable=W0612
                        command,
                        submission_id=submission_id)
                except SaveError as e:
                    logger.error('Could not save primary event')
                    raise InternalServerError(response_data) from e
            if params.get('action') in ['previous', 'save_exit', 'next']:
                return response_data, status.SEE_OTHER, {}
        else:  # Form data were invalid.
            raise BadRequest(response_data)

    return response_data, status.OK, {}
예제 #8
0
def file_process(method: str, params: MultiDict, session: Session,
                 submission_id: int, token: str, **kwargs) -> Response:
    """
    Process the file compilation project.

    Parameters
    ----------
    method : str
        ``GET`` or ``POST``
    session : :class:`Session`
        The authenticated session for the request.
    submission_id : int
        The identifier of the submission for which the upload is being made.
    token : str
        The original (encrypted) auth token on the request. Used to perform
        subrequests to the file management service.

    Returns
    -------
    dict
        Response data, to render in template.
    int
        HTTP status code. This should be ``200`` or ``303``, unless something
        goes wrong.
    dict
        Extra headers to add/update on the response. This should include
        the `Location` header for use in the 303 redirect response, if
        applicable.

    """
    logger.debug("%s: %s, %s, %s, %s", method, params, session, submission_id,
                 token)
    if method == "GET":
        return compile_status(params, session, submission_id, token)
    elif method == "POST":
        if params.get('action') in ['previous', 'next', 'save_exit']:
            _check_status(params, session, submission_id, token)
            # User is not actually trying to process anything; let flow control
            # in the routes handle the response.
            return {}, status.SEE_OTHER, {}
        return start_compilation(params, session, submission_id, token)
    raise MethodNotAllowed('Unsupported request')
예제 #9
0
def policy(method: str, params: MultiDict, session: Session,
           submission_id: int, **kwargs) -> Response:
    """Convert policy form data into an `ConfirmPolicy` event."""
    submitter, client = user_and_client_from_session(session)

    logger.debug(f'method: {method}, submission: {submission_id}. {params}')
    submission, submission_events = load_submission(submission_id)

    if method == 'GET' and submission.submitter_accepts_policy:
        params['policy'] = 'true'

    form = PolicyForm(params)
    response_data = {
        'submission_id': submission_id,
        'form': form,
        'submission': submission
    }

    if method == 'POST':
        if not form.validate():
            raise BadRequest(response_data)

        logger.debug('Form is valid, with data: %s', str(form.data))

        accept_policy = form.policy.data
        if accept_policy and not submission.submitter_accepts_policy:
            command = ConfirmPolicy(creator=submitter, client=client)
            if not validate_command(form, command, submission, 'policy'):
                raise BadRequest(response_data)

            try:
                submission, _ = save(command, submission_id=submission_id)
            except SaveError as e:
                raise InternalServerError(response_data) from e
            response_data['submission'] = submission

        if params.get('action') in ['previous', 'save_exit', 'next']:
            return response_data, status.SEE_OTHER, {}
    return response_data, status.OK, {}
예제 #10
0
def cross_list(method: str, params: MultiDict, session: Session,
               submission_id: int, **kwargs) -> Response:

    submitter, client = user_and_client_from_session(session)
    submission, submission_events = load_submission(submission_id)

    form = ClassificationForm(params)
    form.operation._value = lambda: form.operation.data
    form.filter_choices(submission, session)

    # Create a formset to render removal option.
    #
    # We need forms for existing secondaries, to generate removal requests.
    # When the forms in the formset are submitted, they are handled as the
    # primary form in the POST request to this controller.
    formset = ClassificationForm.formset(submission)
    _primary_category = submission.primary_classification.category
    _primary = taxonomy.CATEGORIES[_primary_category]

    response_data = {
        'submission_id': submission_id,
        'submission': submission,
        'submitter': submitter,
        'client': client,
        'form': form,
        'formset': formset,
        'primary': {
            'id': submission.primary_classification.category,
            'name': _primary['name']
        },
    }

    if method == 'POST':
        # Make sure that the user is not attempting to move to a different
        # step.
        #
        # Since the interface provides an "add" button to add cross-list
        # categories, we only want to handle the form data if the user is not
        # attempting to move to a different step.
        action = params.get('action')
        if not action:
            if not form.validate():
                raise BadRequest(response_data)

            if form.operation.data == form.REMOVE:
                command_type = RemoveSecondaryClassification
            else:
                command_type = AddSecondaryClassification
            command = command_type(category=form.category.data,
                                   creator=submitter,
                                   client=client)
            if not validate_command(form, command, submission, 'category'):
                raise BadRequest(response_data)

            try:
                submission, _ = save(command, submission_id=submission_id)
                response_data['submission'] = submission
            except SaveError as e:
                raise InternalServerError(response_data) from e

            # Re-build the formset to reflect changes that we just made, and
            # generate a fresh form for adding another secondary. The POSTed
            # data should now be reflected in the formset.
            response_data['formset'] = ClassificationForm.formset(submission)
            form = ClassificationForm()
            form.operation._value = lambda: form.operation.data
            form.filter_choices(submission, session)
            response_data['form'] = form

            # Warn the user if they have too many secondaries.
            if len(submission.secondary_categories) > 3:
                alerts.flash_warning(
                    Markup(
                        'Adding more than three cross-list classifications will'
                        ' result in a delay in the acceptance of your submission.'
                    ))

        if action in ['previous', 'save_exit', 'next']:
            return response_data, status.SEE_OTHER, {}
    return response_data, status.OK, {}
예제 #11
0
def compile(request_data: MultiDict,
            token: str,
            session: Session,
            is_authorized: Callable = lambda task: True) -> Response:
    """
    Start compilation of an upload workspace.

    Parameters
    ----------
    request_data : :class:`.MultiDict`
        Data payload from the request.
    token : str
        Auth token to be used for subrequests (e.g. to file management
        service).

    Returns
    -------
    dict
        Response data.
    int
        HTTP status code.
    dict
        Headers to add to response.

    """
    # Since these may originate from a JSON payload, values may be deserialized
    # as int; cast to str to ensure that we are passing the correct type.
    source_id = _validate_source_id(str(request_data.get('source_id', '')))
    checksum = _validate_checksum(str(request_data.get('checksum', '')))
    product_format = _validate_output_format(
        request_data.get('output_format', Format.PDF.value))

    # We don't want to compile the same source package twice.
    force = request_data.get('force', False)

    # Support label and link for PS/PDF Stamping
    # Test
    stamp_label: Optional[str] = request_data.get('stamp_label', None)
    stamp_link: Optional[str] = request_data.get('stamp_link', None)

    logger.debug('%s: request compilation with %s', __name__, request_data)

    # Unless we are forcing recompilation, we do not want to compile the same
    # source twice. So we check our storage for a compilation (successful or
    # not) corresponding to the requested source package.
    if not force:
        info = _status_from_store(source_id, checksum, product_format)
        if info is not None:
            if not is_authorized(info):
                raise Forbidden('Not authorized to compile this resource')
            logger.debug('compilation exists, redirecting')
            return _redirect_to_status(source_id, checksum, product_format)

    owner = _get_owner(source_id, checksum, token)
    try:
        compiler.start_compilation(source_id,
                                   checksum,
                                   stamp_label,
                                   stamp_link,
                                   product_format,
                                   token=token,
                                   owner=owner)
    except compiler.TaskCreationFailed as e:
        logger.error('Failed to start compilation: %s', e)
        raise InternalServerError('Failed to start compilation') from e
    return _redirect_to_status(source_id, checksum, product_format,
                               status.ACCEPTED)