Beispiel #1
0
 /// <summary>
 /// Removes validation errors for fields that the user wasn't able to edit anyway,
 /// based on information provided by <paramref name="fieldsInfo"/>
 /// </summary>
 public static void RemoveIgnoredErrors(this Controller controller, ModelFieldsAccessibility fieldsInfo)
 {
     controller.ModelState
     .Select(pair => pair.Key)
     .Where(path => fieldsInfo.IgnoredValidationPaths.Any(path.StartsWith))
     .ToList()     // Break reliance on ModelState, otherwise "Collection was modified; enumeration operation may not execute"
     .ForEach(path => controller.ModelState.Remove(path));
 }
Beispiel #2
0
 /// <summary>
 /// Renders the editor template, controlled by <paramref name="fieldsInfo"/>.
 /// Does nothing if this field is not set to be shown in <paramref name="fieldsInfo"/>.
 /// </summary>
 public static MvcHtmlString EditorFor <TModel, TValue>(
     this HtmlHelper <TModel> html,
     Expression <Func <TModel, TValue> > expression,
     ModelFieldsAccessibility fieldsInfo
     )
 {
     return(DoFieldsInfoAwareEditor(
                html, expression, fieldsInfo,
                () => html.EditorFor(expression)
                ));
 }
Beispiel #3
0
        public ActionResult NewCourseRep(ElectionForm form)
        {
            ModelFieldsAccessibility fieldsInfo = NewDefaultFieldsInfo;

            // Ignore stuff that isn't supposed to be in new election
            fieldsInfo.ReplaceUneditableWithOldValues(form, new ElectionForm());
            this.RemoveIgnoredErrors(fieldsInfo);

            if (ModelState.IsValid)
            {
                Election election = Mapper.Map <Election>(form);
                election.Type = ElectionType.CourseRepresentative;
                election.PositionGenerationInProcess = true;

                ElectionStateChange createChangeInfo = new ElectionStateChange
                {
                    Election           = election,
                    PreviousState      = null,
                    TargetState        = election.State,
                    IsCausedByUser     = true,
                    InstigatorUsername = User.Identity.GetUserId(),
                    CompletedAt        = DateTime.Now
                };

                db.Elections.Add(election);
                db.ElectionStateChanges.Add(createChangeInfo);

                using (DbContextTransaction transaction = db.Database.BeginTransaction())
                {
                    db.SaveChanges();

                    // This needs to be after the first big transaction - otherwise EF gets confused about order of actions
                    election.PopulateAutomaticStateTransitions();
                    db.SaveChanges();

                    transaction.Commit();
                }

                AuditLogManager.RecordNewElection(createChangeInfo);

                // Schedule the job to loop over the DB to generate the positions
                BackgroundJob.Enqueue(() => GeneratePositions.Execute(election.Id, JobCancellationToken.Null));

                return(RedirectToAction("Details", new { id = election.Id }));
            }

            ViewData[FormConstants.FieldsInfoKey] = fieldsInfo;

            return(View(form));
        }
Beispiel #4
0
        public ActionResult NewCouncil(CouncilElectionForm form)
        {
            ModelFieldsAccessibility fieldsInfo = NewCouncilFieldsInfo;

            // Ignore stuff that isn't supposed to be in new election
            fieldsInfo.ReplaceUneditableWithOldValues(form, new CouncilElectionForm());
            this.RemoveIgnoredErrors(fieldsInfo);

            if (ModelState.IsValid)
            {
                Election election = Mapper.Map <Election>(form);
                election.Type = ElectionType.StudentCouncil;

                CouncilElectionData councilData = Mapper.Map <CouncilElectionData>(form);
                councilData.Election = election;

                ElectionStateChange createChangeInfo = new ElectionStateChange
                {
                    Election           = election,
                    PreviousState      = null,
                    TargetState        = election.State,
                    IsCausedByUser     = true,
                    InstigatorUsername = User.Identity.GetUserId(),
                    CompletedAt        = DateTime.Now
                };

                db.Elections.Add(election);
                db.CouncilElectionData.Add(councilData);
                db.ElectionStateChanges.Add(createChangeInfo);

                using (DbContextTransaction transaction = db.Database.BeginTransaction())
                {
                    db.SaveChanges();

                    // This needs to be after the first big transaction - otherwise EF gets confused about order of actions
                    election.PopulateAutomaticStateTransitions();
                    db.SaveChanges();

                    transaction.Commit();
                }

                AuditLogManager.RecordNewElection(createChangeInfo);

                return(RedirectToAction("Details", new { id = election.Id }));
            }

            ViewData[FormConstants.FieldsInfoKey] = fieldsInfo;

            return(View(form));
        }
Beispiel #5
0
        private static MvcHtmlString DoFieldsInfoAwareEditor <TModel, TValue>(
            HtmlHelper <TModel> html,
            Expression <Func <TModel, TValue> > expression,
            ModelFieldsAccessibility fieldsInfo,
            Func <MvcHtmlString> displayDelegate
            )
        {
            string property = html.PropertyName(expression);

            if (!fieldsInfo.IsVisible(property))
            {
                return(MvcHtmlString.Empty);
            }

            ViewDataDictionary          viewData         = html.ViewDataContainer.ViewData;
            List <string>               dataKeysToRemove = new List <string>();
            Dictionary <string, object> dataPrevValues   = new Dictionary <string, object>();

            // "Hide" current fields info from new editor
            viewData.PrepareChangeReversal(FormConstants.FieldsInfoKey, dataKeysToRemove, dataPrevValues);
            viewData.Remove(FormConstants.FieldsInfoKey);

            // Augment new additionalViewData with information from fieldsInfo
            if (fieldsInfo.IsComplex(property))
            {
                viewData[FormConstants.FieldsInfoKey] = fieldsInfo.GetSubFieldInfo(property);
            }
            else if (!fieldsInfo.CanBeChangedByUser(property))
            {
                viewData.PrepareChangeReversal(FormConstants.DisabledKey, dataKeysToRemove, dataPrevValues);
                viewData[FormConstants.DisabledKey] = true;
            }

            // Render the editor
            MvcHtmlString result = displayDelegate();

            // Reverse the changes to ViewData
            dataKeysToRemove.ForEach(s => viewData.Remove(s));
            dataPrevValues.ForEach(pair => viewData[pair.Key] = pair.Value);

            return(result);
        }
Beispiel #6
0
        public ActionResult Edit(int id)
        {
            Election election = db.Elections.Find(id);

            if (election == null)
            {
                return(HttpNotFound());
            }

            CouncilElectionData councilData = null;
            CouncilElectionForm councilForm = null;
            ElectionForm        form;

            if (election.Type == ElectionType.StudentCouncil)
            {
                councilData = db.CouncilElectionData.First(data => data.ElectionId == election.Id);
                form        = councilForm = GenerateFormForCouncil(election, councilData);
            }
            else
            {
                form = GenerateFormForCourseRep(election);
            }

            ModelFieldsAccessibility fieldsInfo = ElectionLifecycleInfo.GetWhatCanBeEditedCouncil(election);

            ViewData[FormConstants.FieldsInfoKey] = fieldsInfo;
            ViewBag.Election = election;

            fieldsInfo.EnsureAllowedDefaultKind(
                ModelFieldsAccessibility.Kind.Editable,
                nameof(AdminElectionsController) + "." + nameof(Edit)
                );

            if (Request.HttpMethod.ToUpper() != "POST")
            {
                // Just show the template
                return(View("Edit", form));
            }

            AntiForgery.Validate();

            // Update the form based on data that we received
            // ReSharper disable once ConvertIfStatementToNullCoalescingExpression - we need the compiler to specify different generic arguments
            if (councilForm != null)
            {
                TryUpdateModel(councilForm);
            }
            else
            {
                TryUpdateModel(form);
            }

            // Get the original form so that we use old values for uneditable fields
            CouncilElectionForm councilOriginalForm = null;
            ElectionForm        originalForm;

            if (councilForm != null)
            {
                originalForm = councilOriginalForm = GenerateFormForCouncil(election, councilData);
            }
            else
            {
                originalForm = GenerateFormForCourseRep(election);
            }

            // Replace all uneditable values with old ones
            fieldsInfo.ReplaceUneditableWithOldValues(form, originalForm);

            // As the role IDs are sent from user, we need to make sure that they weren't changed
            if (councilForm != null && fieldsInfo.CanBeChangedByUser(nameof(CouncilElectionForm.Roles)))
            {
                IEnumerable <int?> initialRoleIds = councilOriginalForm.Roles.Select(role => role.Id);
                IEnumerable <int?> newRoleIds     = councilForm.Roles.Select(role => role.Id);

                if (!initialRoleIds.SequenceEqual(newRoleIds))
                {
                    throw new Exception("The IDs of roles were changed by user input");
                }
            }

            // Validate again (since some rules are relative to other fields and can be affected by operations above)
            TryValidateModel(form);

            // Ignore the failures from uneditable fields
            this.RemoveIgnoredErrors(fieldsInfo);

            if (!ModelState.IsValid)
            {
                // The validation failed so we just display the form again
                return(View("Edit", form));
            }

            // Record the admin action
            AdminActionRecord actionRecord = CreateActionRecord(election, AdminActionRecord.RecordType.Edit);

            actionRecord.SetFormChangeSet(FormChangeSet.Generate(form, originalForm));
            db.AdminActionRecords.Add(actionRecord);

            // Validation passed with the fields that are allowed to change. Persist the changes
            Mapper.Map(form, election);
            if (councilData != null)
            {
                Mapper.Map(form, councilData);
            }

            db.SaveChanges();

            BackgroundJob.Enqueue <SynchronizeDelayedJobsJob>(job => job.Execute(election.Id));
            AuditLogManager.RecordElectionEdit(User, election);

            return(RedirectToAction("Details", new { id }));
        }
Beispiel #7
0
        public static ModelFieldsAccessibility GetWhatCanBeEditedCouncil(Election election)
        {
            if (!CanEdit(election))
            {
                throw new Exception(
                          $"Cannot call {nameof(GetWhatCanBeEditedCouncil)} when the election isn't editable"
                          );
            }

            bool isCouncil = election.Type == ElectionType.StudentCouncil;

            // Mark everything editable by default
            ModelFieldsAccessibility fieldsInfo = isCouncil
                ? CouncilElectionForm.DefaultCouncilFieldsInfo(ModelFieldsAccessibility.Kind.Editable)
                : ElectionForm.DefaultFieldsInfo(ModelFieldsAccessibility.Kind.Editable);

            if (!ShowResultsAdmin(election))
            {
                fieldsInfo.MarkNotShown(nameof(ElectionForm.ResultsText));
            }

            if (election.State == ElectionState.Disabled)
            {
                // Everything (expect ResultsText) can be edited
                return(fieldsInfo);
            }

            if (election.State == ElectionState.PreNominations || IsAfter(election, ElectionState.PreNominations))
            {
                if (isCouncil)
                {
                    fieldsInfo.MarkNotEditable(nameof(CouncilElectionForm.Roles));
                }

                fieldsInfo
                .GetSubFieldInfo(nameof(ElectionForm.Nominations))
                .MarkNotEditable(nameof(ElectionPhaseForm.BeginsAt));

                if (election.State == ElectionState.PreNominations)
                {
                    return(fieldsInfo);
                }
            }

            if (election.State == ElectionState.Nominations || IsAfter(election, ElectionState.Nominations))
            {
                fieldsInfo.MarkNotShown(nameof(ElectionForm.NominationsStartedEmail));

                // Eligibility is not refreshed automatically after nominations have started
                fieldsInfo.MarkNotShown(nameof(ElectionForm.DisableAutomaticEligibility));

                if (election.State == ElectionState.Nominations)
                {
                    return(fieldsInfo);
                }
            }

            if (election.State == ElectionState.PreVoting || IsAfter(election, ElectionState.PreVoting))
            {
                fieldsInfo.MarkNotShown(nameof(ElectionForm.Nominations));
                if (isCouncil)
                {
                    fieldsInfo.MarkNotShown(nameof(CouncilElectionForm.NominationsAlmostOverEmail));
                }
                fieldsInfo.MarkNotShown(nameof(ElectionForm.PostNominationsEmail));

                if (election.State == ElectionState.PreVoting)
                {
                    return(fieldsInfo);
                }
            }

            if (election.State == ElectionState.Voting || IsAfter(election, ElectionState.Voting))
            {
                if (isCouncil)
                {
                    fieldsInfo.MarkNotShown(nameof(CouncilElectionForm.VotingStartedEmail));
                }

                fieldsInfo
                .GetSubFieldInfo(nameof(ElectionForm.Voting))
                .MarkNotEditable(nameof(ElectionPhaseForm.BeginsAt));

                if (election.State == ElectionState.Voting)
                {
                    return(fieldsInfo);
                }
            }

            if (election.State == ElectionState.Closed || IsAfter(election, ElectionState.Closed))
            {
                if (isCouncil)
                {
                    fieldsInfo.MarkNotShown(nameof(CouncilElectionForm.Roles));
                }
                fieldsInfo.MarkNotShown(nameof(ElectionForm.Voting));
                if (isCouncil)
                {
                    fieldsInfo.MarkNotShown(nameof(CouncilElectionForm.VotingAlmostOverEmail));
                }
                fieldsInfo.MarkNotShown(nameof(ElectionForm.PostVotingEmail));

                if (election.State == ElectionState.Closed)
                {
                    return(fieldsInfo);
                }
            }

            return(fieldsInfo);
        }