private async Task LoadSectionAsync(Proposal proposal, IFormSectionHandler sectionHandler)
        {
            var userId = _userManager.GetUserId(User);

            await sectionHandler.LoadAsync(this, proposal);

            var model = sectionHandler.GetModel(this);

            model.CanEdit = await AuthorizeAsync(proposal, FormSectionOperation.Edit(sectionHandler.ModelType));

            model.CanSubmit = await AuthorizeAsync(proposal, FormSectionOperation.Submit(sectionHandler.ModelType));

            model.CanRetract = await AuthorizeAsync(proposal, FormSectionOperation.Retract(sectionHandler.ModelType));

            model.Approvals = await proposal.Approvals
                              .Where(a => sectionHandler.HasApprovalAuthorityRole(a.AuthorityRole))
                              .ToAsyncEnumerable()
                              .SelectAwait(async approval =>
            {
                var authorities = await _authorityProvider.GetAuthoritiesByRoleAsync(proposal, approval.AuthorityRole);

                ProjectDbUser authority;
                if (approval.Status is ApprovalStatus.Approved or ApprovalStatus.Rejected)
                {
                    // Show authority who approved the section at the time as opposed to current approval role
                    authority = await _userManager.GetUserByIdAsync(approval.ValidatedBy);
                }
        public async Task <IActionResult> OnPostRemoveLabAsync(int proposalId, [Required] int labId)
        {
            if (!ModelState.IsValid)
            {
                return(BadRequest(ModelState));
            }

            var(proposal, error) = await LoadProposalAsync(proposalId);

            if (proposal == null)
            {
                return(error);
            }

            if (!await AuthorizeAsync(proposal, FormSectionOperation.Edit <ExperimentSectionModel>()))
            {
                return(Forbid());
            }

            var lab = proposal.Labs.FirstOrDefault(e => e.Id == labId);

            if (lab == null)
            {
                return(NotFound());
            }

            proposal.Labs.Remove(lab);

            await UpdateProposalAsync(proposal);

            return(new JsonResult(new
            {
                proposal.Timestamp
            }));
        }
        public async Task <IActionResult> OnPostRetractAsync(int proposalId)
        {
            await TryUpdateModelAsync(RetractSection, nameof(RetractSection));

            if (!ModelState.IsValid)
            {
                return(BadRequest(ModelState));
            }

            var(proposal, error) = await LoadProposalAsync(proposalId);

            if (proposal == null)
            {
                return(error);
            }

            var sectionHandler = _sectionHandlers.Single(h => h.Id == RetractSection.Section);

            if (!await AuthorizeAsync(proposal, FormSectionOperation.Retract(sectionHandler.ModelType)))
            {
                return(Forbid());
            }

            foreach (var approval in sectionHandler.GetAssociatedApprovals(proposal))
            {
                approval.Status      = ApprovalStatus.NotSubmitted;
                approval.ValidatedBy = null;
            }

            InvalidateDependentSections(proposal, sectionHandler);

            await UpdateProposalAsync(proposal);

            return(RedirectToPage(null, null, new { proposalId }, RetractSection.Section));
        }
        public async Task <IActionResult> OnPostAsync(int proposalId, [Required] string sectionId)
        {
            if (!ModelState.IsValid)
            {
                return(BadRequest(ModelState));
            }

            var(proposal, error) = await LoadProposalAsync(proposalId);

            if (proposal == null)
            {
                return(error);
            }

            var sectionHandler = _sectionHandlers.Single(h => h.Id == sectionId);

            if (!await AuthorizeAsync(proposal, FormSectionOperation.Edit(sectionHandler.ModelType)))
            {
                return(Forbid());
            }

            if (await TryUpdateModelAsync(sectionHandler.GetModel(this), sectionHandler.ModelType, sectionHandler.Id))
            {
                await sectionHandler.StoreAsync(this, proposal);
                await UpdateProposalAsync(proposal);
            }

            return(new JsonResult(new
            {
                proposal.Timestamp,
                Success = ModelState.IsValid,
                Errors = new SerializableError(ModelState)
            }));
        }
        public async Task <IActionResult> OnPostAddLabAsync(int proposalId, [Required] string modality)
        {
            if (!ModelState.IsValid)
            {
                return(BadRequest(ModelState));
            }

            var(proposal, error) = await LoadProposalAsync(proposalId);

            if (proposal == null)
            {
                return(error);
            }

            if (!await AuthorizeAsync(proposal, FormSectionOperation.Edit <ExperimentSectionModel>()))
            {
                return(Forbid());
            }

            var lab = new Lab
            {
                Modality = modality
            };

            proposal.Labs.Add(lab);

            await UpdateProposalAsync(proposal);
            await LoadSectionAsync(proposal, _sectionHandlers.Single(h => h.ModelType == typeof(ExperimentSectionModel)));

            Timestamp = proposal.Timestamp;

            return(PartialWithViewData("Shared/_Lab", new
            {
                LabId = lab.Id
            }));
        }
        public async Task <IActionResult> OnPostSubmitAsync(int proposalId)
        {
            await TryUpdateModelAsync(SubmitSection, nameof(SubmitSection));

            if (!ModelState.IsValid)
            {
                return(BadRequest(ModelState));
            }

            var(proposal, error) = await LoadProposalAsync(proposalId);

            if (proposal == null)
            {
                return(error);
            }

            var sectionHandler = _sectionHandlers.Single(h => h.Id == SubmitSection.Section);

            if (!await AuthorizeAsync(proposal, FormSectionOperation.Submit(sectionHandler.ModelType)))
            {
                return(Forbid());
            }

            if (!await sectionHandler.ValidateSubmissionAsync(this, proposal))
            {
                await LoadFormAsync(proposal);

                return(Page());
            }

            foreach (var approval in sectionHandler.GetAssociatedApprovals(proposal))
            {
                if (sectionHandler.IsAuthorityApplicable(proposal, approval.AuthorityRole))
                {
                    var authorities = await _authorityProvider.GetAuthoritiesByRoleAsync(proposal, approval.AuthorityRole);

                    // Is self approved (no approval required)?
                    if (!authorities.Any() || authorities.Any(authority => authority.Id == _userManager.GetUserId(User)))
                    {
                        if (!await sectionHandler.ValidateApprovalAsync(this, proposal))
                        {
                            await LoadFormAsync(proposal);

                            return(Page());
                        }

                        approval.Status      = ApprovalStatus.Approved;
                        approval.ValidatedBy = _userManager.GetUserId(User);
                    }
                    else
                    {
                        var primary = authorities.First();

                        await _emailService.QueueMessageAsync(User, new ApprovalRequest
                        {
                            ApplicantName = _userManager.GetUserName(User),
                            ProposalTitle = proposal.Title,
                            SectionName   = sectionHandler.DisplayName,
                            PageLink      = Url.Page(null, null, new { proposalId }, "https", null, sectionHandler.Id)
                        }, new MailAddress(primary.Email, primary.DisplayName), true);

                        approval.Status = ApprovalStatus.ApprovalPending;
                    }
                }
                else
                {
                    approval.Status = ApprovalStatus.NotApplicable;
                }
            }

            await UpdateProposalAsync(proposal);
            await SendMailIfProposalApprovedAsync(proposal);

            return(RedirectToPage(null, null, new { proposalId }, SubmitSection.Section));
        }