/// <summary>
 /// Creates a new code question updater.
 /// </summary>
 private static CodeQuestionUpdater <CodeQuestion> GetCodeQuestionUpdater(
     TestDatabase database,
     CodeQuestion question,
     IModelErrorCollection errors)
 {
     return(new Mock <CodeQuestionUpdater <CodeQuestion> >(database.Context, question, errors).Object);
 }
Esempio n. 2
0
        /// <summary>
        /// Returns a new section validator.
        /// </summary>
        private static ISectionValidator CreateMockSectionValidator(
            Section sectionToValidate,
            IModelErrorCollection modelErrors,
            bool valid)
        {
            var sectionValidator = new Mock <ISectionValidator>();

            sectionValidator
            .Setup
            (
                m => m.ValidateSectionAsync
                (
                    sectionToValidate,
                    modelErrors
                )
            )
            .Callback
            (
                (Section section, IModelErrorCollection errors) =>
            {
                if (!valid)
                {
                    errors.AddError("Error", "Error Description");
                }
            }
            ).ReturnsAsync(valid);

            return(sectionValidator.Object);
        }
Esempio n. 3
0
        /// <summary>
        /// Updates a checkpoint.
        /// </summary>
        public async Task <bool> UpdateCheckpointAsync(
            string classroomName,
            string projectName,
            Checkpoint checkpoint,
            IModelErrorCollection modelErrors)
        {
            var project = await LoadProjectAsync(classroomName, projectName);

            checkpoint.ProjectId = project.Id;

            var currentCheckpoint = await _dbContext.Checkpoints
                                    .Where(c => c.Id == checkpoint.Id)
                                    .SingleOrDefaultAsync();

            _dbContext.Entry(currentCheckpoint).State = EntityState.Detached;

            if (!UpdateCheckpoint(checkpoint, modelErrors))
            {
                return(false);
            }

            _dbContext.Update(checkpoint);

            await _dbContext.SaveChangesAsync();

            return(true);
        }
Esempio n. 4
0
        /// <summary>
        /// Validates that the question may be updated, and executes
        /// the corresponding question updater.
        /// </summary>
        private async Task <bool> ValidateAndUpdateQuestionAsync(
            string classroomName,
            Question question,
            IModelErrorCollection errors)
        {
            var isValid = await _questionValidator.ValidateQuestionAsync
                          (
                question,
                errors,
                classroomName
                          );

            if (!isValid)
            {
                return(false);
            }

            await _questionUpdaterFactory
            .CreateQuestionUpdater(question, errors)
            .UpdateQuestionAsync();

            if (errors.HasErrors)
            {
                return(false);
            }

            return(true);
        }
Esempio n. 5
0
        /// <summary>
        /// Updates a assignment.
        /// </summary>
        private async Task <bool> UpdateAssignmentAsync(Assignment assignment, IModelErrorCollection modelErrors)
        {
            UpdateQuestionOrder(assignment.Questions);
            await UpdateQuestionNames(assignment.Questions);

            if (!await _assignmentValidator.ValidateAssignmentAsync(assignment, modelErrors))
            {
                return(false);
            }

            if (string.IsNullOrWhiteSpace(assignment.GroupName))
            {
                assignment.GroupName = assignment.Name;
            }

            _dbContext.RemoveUnwantedObjects
            (
                _dbContext.AssignmentQuestions,
                question => question.Id,
                question => question.AssignmentId == assignment.Id,
                assignment.Questions
            );

            _dbContext.RemoveUnwantedObjects
            (
                _dbContext.AssignmentDueDates,
                dueDate => dueDate.Id,
                dueDate => dueDate.AssignmentId == assignment.Id,
                assignment.DueDates
            );

            return(true);
        }
Esempio n. 6
0
        /// <summary>
        /// Returns a new assignment validator.
        /// </summary>
        private static IAssignmentValidator CreateMockAssignmentValidator(
            Assignment assignmentToValidate,
            IModelErrorCollection modelErrors,
            bool validAssignment)
        {
            var assignmentValidator = new Mock <IAssignmentValidator>();

            assignmentValidator
            .Setup
            (
                m => m.ValidateAssignmentAsync
                (
                    assignmentToValidate,
                    modelErrors
                )
            )
            .Callback
            (
                (Assignment assignment, IModelErrorCollection errors) =>
            {
                if (!validAssignment)
                {
                    errors.AddError("Error", "Error Description");
                }
            }
            ).ReturnsAsync(validAssignment);

            return(assignmentValidator.Object);
        }
Esempio n. 7
0
        /// <summary>
        /// Updates a section.
        /// </summary>
        public async Task <bool> UpdateSectionAsync(
            string classroomName,
            Section section,
            IModelErrorCollection errors)
        {
            var classroom = await LoadClassroomAsync(classroomName);

            section.ClassroomId = classroom.Id;

            var currentSection = await _dbContext.Sections
                                 .Where(s => s.Id == section.Id)
                                 .SingleOrDefaultAsync();

            _dbContext.Entry(currentSection).State = EntityState.Detached;

            if (!await _sectionValidator.ValidateSectionAsync(section, errors))
            {
                return(false);
            }

            UpdateSection(section);
            _dbContext.Update(section);

            await _dbContext.SaveChangesAsync();

            return(true);
        }
Esempio n. 8
0
        /// <summary>
        /// Ensures that a question to add or update is in a valid state.
        /// </summary>
        public async Task <bool> ValidateQuestionAsync(
            Question question,
            IModelErrorCollection errors,
            string classroomName)
        {
            var existingQuestion = await _dbContext.Questions
                                   .Where(q => q.QuestionCategory.Classroom.Name == classroomName)
                                   .Where(q => q.Id == question.Id)
                                   .Include(q => q.QuestionCategory)
                                   .SingleOrDefaultAsync();

            if (existingQuestion != null)
            {
                _dbContext.Entry(existingQuestion).State = EntityState.Detached;
            }

            var newQuestionCategory = await _dbContext.QuestionCategories
                                      .Include(qc => qc.Classroom)
                                      .SingleOrDefaultAsync
                                      (
                category => category.Id == question.QuestionCategoryId
                                      );

            if (newQuestionCategory.Classroom.Name != classroomName)
            {
                throw new InvalidOperationException(
                          "Category of question is not in the given classroom.");
            }

            if (existingQuestion?.QuestionCategory?.RandomlySelectedQuestionId != null &&
                question.QuestionCategoryId != existingQuestion?.QuestionCategoryId)
            {
                throw new InvalidOperationException
                      (
                          "The category cannot be changed for a randomly selected question choice."
                      );
            }

            if (existingQuestion != null &&
                existingQuestion?.QuestionCategory?.RandomlySelectedQuestionId == null &&
                newQuestionCategory.RandomlySelectedQuestionId != null)
            {
                throw new InvalidOperationException
                      (
                          "The category cannot be changed from a non-random-choice category "
                          + "to a random choice category."
                      );
            }

            if (await _dbContext.Questions.AnyAsync(
                    q => q.Id != question.Id &&
                    q.Name == question.Name &&
                    q.QuestionCategoryId == question.QuestionCategoryId))
            {
                errors.AddError("Name", "Another question with that name already exists.");
            }

            return(!errors.HasErrors);
        }
        /// <summary>
        /// Publish a new announcement.
        /// </summary>
        public async Task <bool> EditAnnouncementAsync(
            string classroomName,
            Announcement announcement,
            Func <DateTime, string> formatDateTime,
            IModelErrorCollection modelErrors)
        {
            var existingAnnouncement = await _dbContext.Announcements
                                       .AsNoTracking()
                                       .Where(a => a.Classroom.Name == classroomName)
                                       .Include(a => a.Sections)
                                       .Include(a => a.Classroom.Sections)
                                       .SingleOrDefaultAsync(a => a.Id == announcement.Id);

            var classroom = existingAnnouncement.Classroom;

            if (!_validator.ValidateAnnouncement(classroom, announcement, modelErrors))
            {
                return(false);
            }

            announcement.ClassroomId = existingAnnouncement.ClassroomId;
            announcement.UserId      = existingAnnouncement.UserId;
            announcement.DatePosted  = existingAnnouncement.DatePosted;
            announcement.Contents    = _htmlSanitizer.SanitizeHtml(announcement.Contents);
            _dbContext.RemoveUnwantedObjects
            (
                _dbContext.AnnouncementSections,
                announcementSection => announcementSection.Id,
                announcementSection => announcementSection.AnnouncementId == announcement.Id,
                announcement.Sections
            );

            var newSectionIds = announcement.Sections
                                .Select(s => s.SectionId)
                                .Where
                                (
                sectionId => !existingAnnouncement.Sections.Any
                (
                    s => s.SectionId == sectionId
                )
                                ).ToList();

            _dbContext.Entry(existingAnnouncement).State = EntityState.Detached;
            _dbContext.Announcements.Update(announcement);

            await _dbContext.SaveChangesAsync();

            await SendAnnouncementEmailAsync
            (
                announcement,
                classroom,
                newSectionIds,
                formatDateTime,
                emailAdmins : false
            );

            return(true);
        }
Esempio n. 10
0
        /// <summary>
        /// Registers a user.
        /// </summary>
        public async Task <RegisterNewUserResult> RegisterNewStudentAsync(
            string classroomName,
            string sectionName,
            StudentRegistration registration,
            string confirmationUrlBuilder,
            IModelErrorCollection errors)
        {
            var section = _dbContext.Sections
                          .Where(s => s.Classroom.Name == classroomName)
                          .Include(s => s.Classroom)
                          .SingleOrDefault(s => s.Name == sectionName);

            if (section == null)
            {
                return(RegisterNewUserResult.SectionNotFound);
            }

            if (!section.AllowNewRegistrations)
            {
                return(RegisterNewUserResult.SectionNotOpen);
            }

            var user = await GetAndUpdateCurrentUserAsync();

            if (user != null)
            {
                return(RegisterNewUserResult.AlreadyRegistered);
            }

            if (!await _gitHubUserClient.DoesUserExistAsync(registration.GitHubLogin))
            {
                errors.AddError("GitHubLogin", "The GitHub username does not exist.");
                return(RegisterNewUserResult.Failed);
            }

            user = new User()
            {
                UniqueId              = _identityProvider.CurrentIdentity.UniqueId,
                UserName              = _identityProvider.CurrentIdentity.UserName,
                FirstName             = registration.FirstName,
                LastName              = _identityProvider.CurrentIdentity.LastName,
                EmailAddress          = registration.EmailAddress,
                EmailConfirmationCode = GenerateEmailConfirmationCode(),
                EmailAddressConfirmed = false,
                GitHubLogin           = registration.GitHubLogin,
                SuperUser             = false
            };

            var membership = await EnsureSectionMembershipAsync(user, section, SectionRole.Student);

            await EnsureUserInGithubOrgAsync(user, membership.ClassroomMembership);
            await SendUserInvitationMailAsync(user, confirmationUrlBuilder);

            _dbContext.Users.Add(user);
            await _dbContext.SaveChangesAsync();

            return(RegisterNewUserResult.Success);
        }
Esempio n. 11
0
        /// <summary>
        /// Returns whether or not a section is valid.
        /// </summary>
        public async Task <bool> ValidateSectionAsync(
            Section section,
            IModelErrorCollection errors)
        {
            EnsureNoDuplicateSectionGradebooks(section, errors);
            EnsureNoDuplicateSectionRecipients(section, errors);
            await EnsureSectionRecipientsAreClassAdmins(section, errors);

            return(!errors.HasErrors);
        }
 /// <summary>
 /// Constructor.
 /// </summary>
 public GeneratedQuestionUpdater(
     DatabaseContext dbContext,
     GeneratedQuestionTemplate question,
     IModelErrorCollection errors,
     IQuestionGenerator questionGenerator,
     ITimeProvider timeProvider)
     : base(dbContext, question, errors)
 {
     _questionGenerator = questionGenerator;
     _timeProvider      = timeProvider;
 }
Esempio n. 13
0
        /// <summary>
        /// Updates a question.
        /// </summary>
        public async Task <bool> UpdateQuestionAsync(
            string classroomName,
            Question question,
            IModelErrorCollection errors)
        {
            if (!await ValidateAndUpdateQuestionAsync(classroomName, question, errors))
            {
                return(false);
            }

            _dbContext.Update(question);
            await _dbContext.SaveChangesAsync();

            return(true);
        }
Esempio n. 14
0
        /// <summary>
        /// Validates that an announcement is correctly configured.
        /// </summary>
        public bool ValidateAnnouncement(
            Classroom classroom,
            Announcement announcement,
            IModelErrorCollection modelErrors)
        {
            var sectionIds = announcement.Sections
                             ?.Select(s => s.SectionId)
                             ?.ToList() ?? new List <int>();

            if (!sectionIds.Any())
            {
                modelErrors.AddError
                (
                    "Sections",
                    "At least one section must be included."
                );

                return(false);
            }

            if (sectionIds.Distinct().Count() != sectionIds.Count)
            {
                modelErrors.AddError
                (
                    "Sections",
                    "Duplicate sections are not permitted."
                );

                return(false);
            }

            if (sectionIds
                .Intersect(classroom.Sections.Select(s => s.Id))
                .Count() != sectionIds.Count)
            {
                modelErrors.AddError
                (
                    "Sections",
                    "Invalid sections selected."
                );

                return(false);
            }

            return(true);
        }
Esempio n. 15
0
		/// <summary>
		/// Creates a checkpoint.
		/// </summary>
		public async Task<bool> CreateCheckpointAsync(
			string classroomName,
			string projectName,
			Checkpoint checkpoint, 
			IModelErrorCollection modelErrors)
		{
			var project = await LoadProjectAsync(classroomName, projectName);

			if (!UpdateCheckpoint(checkpoint, modelErrors))
				return false;

			checkpoint.ProjectId = project.Id;
			_dbContext.Add(checkpoint);

			await _dbContext.SaveChangesAsync();

			return true;
		}
Esempio n. 16
0
        /// <summary>
        /// Updates an assignment.
        /// </summary>
        public async Task <bool> UpdateAssignmentAsync(
            string classroomName,
            Assignment assignment,
            IModelErrorCollection modelErrors)
        {
            var classroom = await LoadClassroomAsync(classroomName);

            if (!await UpdateAssignmentAsync(assignment, modelErrors))
            {
                return(false);
            }

            assignment.ClassroomId = classroom.Id;
            _dbContext.Update(assignment);

            await _dbContext.SaveChangesAsync();

            return(true);
        }
Esempio n. 17
0
        /// <summary>
        /// Updates a checkpoint.
        /// </summary>
        private bool UpdateCheckpoint(Checkpoint checkpoint, IModelErrorCollection modelErrors)
        {
            if (checkpoint.SectionDates != null)
            {
                var sections = checkpoint.SectionDates.Select(d => d.SectionId).ToList();
                if (sections.Distinct().Count() != sections.Count)
                {
                    modelErrors.AddError("SectionDates", "You may only have one due date per section.");
                }
            }

            if (checkpoint.TestClasses != null)
            {
                var testClasses = checkpoint.TestClasses.Select(tc => tc.TestClassId).ToList();
                if (testClasses.Distinct().Count() != testClasses.Count)
                {
                    modelErrors.AddError("TestClasses", "You may only have one entry per test class.");
                }
            }

            if (modelErrors.HasErrors)
            {
                return(false);
            }

            _dbContext.RemoveUnwantedObjects
            (
                _dbContext.CheckpointDates,
                checkpointDates => checkpointDates.Id,
                checkpointDates => checkpointDates.CheckpointId == checkpoint.Id,
                checkpoint.SectionDates
            );

            _dbContext.RemoveUnwantedObjects
            (
                _dbContext.CheckpointTestClasses,
                testClass => testClass.Id,
                testClass => testClass.CheckpointId == checkpoint.Id,
                checkpoint.TestClasses
            );

            return(true);
        }
Esempio n. 18
0
        /// <summary>
        /// Creates a checkpoint.
        /// </summary>
        public async Task <bool> CreateCheckpointAsync(
            string classroomName,
            string projectName,
            Checkpoint checkpoint,
            IModelErrorCollection modelErrors)
        {
            var project = await LoadProjectAsync(classroomName, projectName);

            if (!UpdateCheckpoint(checkpoint, modelErrors))
            {
                return(false);
            }

            checkpoint.ProjectId = project.Id;
            _dbContext.Add(checkpoint);

            await _dbContext.SaveChangesAsync();

            return(true);
        }
Esempio n. 19
0
        /// <summary>
        /// Ensures that there are no duplicate section recipients.
        /// </summary>
        private void EnsureNoDuplicateSectionRecipients(
            Section section,
            IModelErrorCollection errors)
        {
            if (section.SectionRecipients != null)
            {
                var cmIds = section.SectionRecipients
                            .Select(d => d.ClassroomMembershipId)
                            .ToList();

                if (cmIds.Distinct().Count() != cmIds.Count)
                {
                    errors.AddError
                    (
                        "SectionRecipients",
                        "Duplicate section recipients are not permitted."
                    );
                }
            }
        }
Esempio n. 20
0
        /// <summary>
        /// Ensures that there are no duplicate section gradebooks.
        /// </summary>
        private void EnsureNoDuplicateSectionGradebooks(
            Section section,
            IModelErrorCollection errors)
        {
            if (section.SectionGradebooks != null)
            {
                var classroomGradebookIds = section.SectionGradebooks
                                            .Select(d => d.ClassroomGradebookId)
                                            .ToList();

                if (classroomGradebookIds.Distinct().Count() != classroomGradebookIds.Count)
                {
                    errors.AddError
                    (
                        "SectionGradebooks",
                        "You may only have one section gradebook per classroom gradebook."
                    );
                }
            }
        }
Esempio n. 21
0
        /// <summary>
        /// Creates a section.
        /// </summary>
        public async Task <bool> CreateSectionAsync(
            string classroomName,
            Section section,
            IModelErrorCollection errors)
        {
            var classroom = await LoadClassroomAsync(classroomName);

            section.ClassroomId = classroom.Id;

            if (!await _sectionValidator.ValidateSectionAsync(section, errors))
            {
                return(false);
            }

            _dbContext.Add(section);

            await _dbContext.SaveChangesAsync();

            return(true);
        }
Esempio n. 22
0
        /// <summary>
        /// Publish a new announcement.
        /// </summary>
        public async Task <bool> PostAnnouncementAsync(
            string classroomName,
            int userId,
            Announcement announcement,
            Func <DateTime, string> formatDateTime,
            IModelErrorCollection modelErrors)
        {
            var classroom = await _dbContext.Classrooms
                            .Where(c => c.Name == classroomName)
                            .Include(c => c.Sections)
                            .SingleAsync();

            var sectionIds = announcement.Sections
                             ?.Select(s => s.SectionId)
                             ?.ToList() ?? new List <int>();

            if (!_validator.ValidateAnnouncement(classroom, announcement, modelErrors))
            {
                return(false);
            }

            announcement.ClassroomId = classroom.Id;
            announcement.UserId      = userId;
            announcement.Contents    = _htmlSanitizer.SanitizeHtml(announcement.Contents);
            announcement.DatePosted  = _timeProvider.UtcNow;

            _dbContext.Announcements.Add(announcement);

            await _dbContext.SaveChangesAsync();

            await SendAnnouncementEmailAsync
            (
                announcement,
                classroom,
                sectionIds,
                formatDateTime,
                emailAdmins : true
            );

            return(true);
        }
Esempio n. 23
0
        /// <summary>
        /// Ensures that section recipients are class admins.
        /// </summary>
        private async Task EnsureSectionRecipientsAreClassAdmins(
            Section section,
            IModelErrorCollection errors)
        {
            var classroomMemberships = await _dbContext.ClassroomMemberships
                                       .Where(cm => cm.ClassroomId == section.ClassroomId)
                                       .Where(cm => cm.Role >= ClassroomRole.Admin)
                                       .ToListAsync();

            var cmIds = classroomMemberships.Select(cm => cm.Id).ToHashSet();

            if (section.SectionRecipients != null &&
                section.SectionRecipients.Any(sr => !cmIds.Contains(sr.ClassroomMembershipId)))
            {
                errors.AddError
                (
                    "SectionRecipients",
                    "All section recipients must be class admins."
                );
            }
        }
Esempio n. 24
0
        /// <summary>
        /// Called to register the first super-user.
        /// </summary>
        public async Task <RegisterNewUserResult> RegisterFirstSuperUserAsync(
            SuperUserRegistration registration,
            IModelErrorCollection errors)
        {
            if (await AnyRegisteredUsersAsync())
            {
                return(RegisterNewUserResult.AlreadyRegistered);
            }

            if (registration.ActivationToken != _activationToken.Value)
            {
                errors.AddError("ActivationToken", "Incorrect activation token.");
                return(RegisterNewUserResult.Failed);
            }

            if (!await _gitHubUserClient.DoesUserExistAsync(registration.GitHubLogin))
            {
                errors.AddError("GitHubLogin", "The GitHub username does not exist.");
                return(RegisterNewUserResult.Failed);
            }

            User user = new User()
            {
                UniqueId              = _identityProvider.CurrentIdentity.UniqueId,
                UserName              = _identityProvider.CurrentIdentity.UserName,
                FirstName             = registration.FirstName,
                LastName              = registration.LastName,
                EmailAddress          = registration.EmailAddress,
                EmailAddressConfirmed = true,
                GitHubLogin           = registration.GitHubLogin,
                SuperUser             = true
            };

            _dbContext.Users.Add(user);
            await _dbContext.SaveChangesAsync();

            return(RegisterNewUserResult.Success);
        }
        /// <summary>
        /// Returns a mock question updater factory.
        /// </summary>
        private Mock <IQuestionUpdaterFactory> GetMockQuestionUpdaterFactory(
            bool isValid)
        {
            var updater = new Mock <IQuestionUpdater>();
            IModelErrorCollection errors = null;

            var updaterFactory = new Mock <IQuestionUpdaterFactory>();

            updaterFactory
            .Setup
            (
                m => m.CreateQuestionUpdater
                (
                    It.IsNotNull <Question>(),
                    It.IsNotNull <IModelErrorCollection>()
                )
            ).Callback <Question, IModelErrorCollection>
            (
                (_unused, modelErrors) => errors = modelErrors
            ).Returns(updater.Object);

            updater
            .Setup(m => m.UpdateQuestionAsync())
            .Callback
            (
                () =>
            {
                if (!isValid)
                {
                    errors.AddError("Error", "ErrorDescription");
                }
            }
            ).Returns(Task.CompletedTask);

            return(updaterFactory);
        }
Esempio n. 26
0
 /// <summary>
 /// Constructor.
 /// </summary>
 public MethodQuestionUpdater(DatabaseContext dbContext, MethodQuestion question, IModelErrorCollection errors)
     : base(dbContext, question, errors)
 {
 }
Esempio n. 27
0
 /// <summary>
 /// Constructor.
 /// </summary>
 public MultipleChoiceQuestionUpdater(
     DatabaseContext dbContext,
     MultipleChoiceQuestion question,
     IModelErrorCollection errors) : base(dbContext, question, errors)
 {
 }
Esempio n. 28
0
		/// <summary>
		/// Updates a question.
		/// </summary>
		public async Task<bool> UpdateQuestionAsync(
			string classroomName,
			Question question, 
			IModelErrorCollection errors)
		{
			var classroom = await LoadClassroomAsync(classroomName);

			var questionCategory = await _dbContext.QuestionCategories
				.SingleOrDefaultAsync
				(
					category => category.Id == question.QuestionCategoryId
				);

			if (questionCategory.ClassroomId != classroom.Id)
			{
				throw new InvalidOperationException(
					"Category of question is not in the given classroom.");
			}

			if (await _dbContext.Questions.AnyAsync(
				   q => q.Id != question.Id 
				&& q.Name == question.Name
				&& q.QuestionCategoryId == question.QuestionCategoryId))
			{
				errors.AddError("Name", "Another question with that name already exists.");
				return false;
			}

			await _questionUpdaterFactory.CreateQuestionUpdater(question, errors)
				.UpdateQuestionAsync();

			if (errors.HasErrors)
			{
				return false;
			}

			_dbContext.Update(question);
			await _dbContext.SaveChangesAsync();

			return true;
		}
 /// <summary>
 /// Constructor.
 /// </summary>
 public RandomlySelectedQuestionUpdater(
     DatabaseContext dbContext,
     RandomlySelectedQuestion question,
     IModelErrorCollection errors) : base(dbContext, question, errors)
 {
 }
Esempio n. 30
0
		/// <summary>
		/// Updates the given user.
		/// </summary>
		public async Task<bool> UpdateUserAsync(
			User user,
			string confirmationUrlBuilder,
			IModelErrorCollection modelErrors)
		{
			var existingUser = await _dbContext.Users
				.Include(u => u.ClassroomMemberships)
					.ThenInclude(cm => cm.Classroom)
				.SingleAsync(u => u.Id == user.Id);

			bool updatedEmail = false;

			if (user.GitHubLogin != existingUser.GitHubLogin)
			{
				if (!await _gitHubUserClient.DoesUserExistAsync(user.GitHubLogin))
				{
					modelErrors.AddError("GitHubLogin", "The GitHub username does not exist.");
					return false;
				}

				if (existingUser.ClassroomMemberships != null)
				{
					foreach (var membership in existingUser.ClassroomMemberships)
					{
						var orgName = membership.Classroom.GitHubOrganization;

						var team = await _gitHubTeamClient.GetTeamAsync
						(
							orgName,
							membership.GitHubTeam
						);

						await _gitHubTeamClient.InviteUserToTeamAsync
						(
							orgName,
							team,
							user.GitHubLogin
						);

						await _gitHubTeamClient.RemoveUserFromTeamAsync
						(
							orgName,
							team,
							existingUser.GitHubLogin
						);

						membership.InGitHubOrganization = false;
					}
				}

				existingUser.GitHubLogin = user.GitHubLogin;
			}

			if (user.EmailAddress != existingUser.EmailAddress)
			{
				existingUser.EmailAddress = user.EmailAddress;
				existingUser.EmailAddressConfirmed = false;
				existingUser.EmailConfirmationCode = GenerateEmailConfirmationCode();

				updatedEmail = true;
			}

			await _dbContext.SaveChangesAsync();

			if (updatedEmail)
			{
				await SendUserInvitationMailAsync(existingUser, confirmationUrlBuilder);
			}

			return true;
		}
Esempio n. 31
0
		/// <summary>
		/// Updates a checkpoint.
		/// </summary>
		public async Task<bool> UpdateCheckpointAsync(
			string classroomName,
			string projectName,
			Checkpoint checkpoint,
			IModelErrorCollection modelErrors)
		{
			var project = await LoadProjectAsync(classroomName, projectName);

			checkpoint.ProjectId = project.Id;

			var currentCheckpoint = await _dbContext.Checkpoints
				.Where(c => c.Id == checkpoint.Id)
				.SingleOrDefaultAsync();

			_dbContext.Entry(currentCheckpoint).State = EntityState.Detached;

			if (!UpdateCheckpoint(checkpoint, modelErrors))
				return false;

			_dbContext.Update(checkpoint);

			await _dbContext.SaveChangesAsync();

			return true;
		}
Esempio n. 32
0
		/// <summary>
		/// Called to register the first super-user.
		/// </summary>
		public async Task<RegisterNewUserResult> RegisterFirstSuperUserAsync(
			SuperUserRegistration registration,
			IModelErrorCollection errors)
		{
			if (await AnyRegisteredUsersAsync())
			{
				return RegisterNewUserResult.AlreadyRegistered;
			}

			if (registration.ActivationToken != _activationToken.Value)
			{
				errors.AddError("ActivationToken", "Incorrect activation token.");
				return RegisterNewUserResult.Failed;
			}

			if (!await _gitHubUserClient.DoesUserExistAsync(registration.GitHubLogin))
			{
				errors.AddError("GitHubLogin", "The GitHub username does not exist.");
				return RegisterNewUserResult.Failed;
			}

			User user = new User()
			{
				UniqueId = _identityProvider.CurrentIdentity.UniqueId,
				UserName = _identityProvider.CurrentIdentity.UserName,
				FirstName = registration.FirstName,
				LastName = registration.LastName,
				EmailAddress = registration.EmailAddress,
				EmailAddressConfirmed = true,
				GitHubLogin = registration.GitHubLogin,
				SuperUser = true
			};
			
			_dbContext.Users.Add(user);
			await _dbContext.SaveChangesAsync();

			return RegisterNewUserResult.Success;
		}
Esempio n. 33
0
		/// <summary>
		/// Updates a checkpoint.
		/// </summary>
		private bool UpdateCheckpoint(Checkpoint checkpoint, IModelErrorCollection modelErrors)
		{
			if (checkpoint.SectionDates != null)
			{
				var sections = checkpoint.SectionDates.Select(d => d.SectionId).ToList();
				if (sections.Distinct().Count() != sections.Count)
				{
					modelErrors.AddError("SectionDates", "You may only have one due date per section.");
				}
			}

			if (checkpoint.TestClasses != null)
			{
				var testClasses = checkpoint.TestClasses.Select(tc => tc.TestClassId).ToList();
				if (testClasses.Distinct().Count() != testClasses.Count)
				{
					modelErrors.AddError("TestClasses", "You may only have one entry per test class.");
				}
			}

			if (modelErrors.HasErrors)
			{
				return false;
			}

			_dbContext.RemoveUnwantedObjects
			(
				_dbContext.CheckpointDates,
				checkpointDates => checkpointDates.Id,
				checkpointDates => checkpointDates.CheckpointId == checkpoint.Id,
				checkpoint.SectionDates
			);

			_dbContext.RemoveUnwantedObjects
			(
				_dbContext.CheckpointTestClasses,
				testClass => testClass.Id,
				testClass => testClass.CheckpointId == checkpoint.Id,
				checkpoint.TestClasses
			);

			return true;
		}
Esempio n. 34
0
		/// <summary>
		/// Registers a user.
		/// </summary>
		public async Task<RegisterNewUserResult> RegisterNewStudentAsync(
			string classroomName,
			string sectionName,
			StudentRegistration registration,
			string confirmationUrlBuilder,
			IModelErrorCollection errors)
		{
			var section = _dbContext.Sections
				.Where(s => s.Classroom.Name == classroomName)
				.Include(s => s.Classroom)
				.SingleOrDefault(s => s.Name == sectionName);

			if (section == null)
			{
				return RegisterNewUserResult.SectionNotFound;
			}

			if (!section.AllowNewRegistrations)
			{
				return RegisterNewUserResult.SectionNotOpen;
			}

			var user = await GetAndUpdateCurrentUserAsync();
			if (user != null)
			{
				return RegisterNewUserResult.AlreadyRegistered;
			}
			
			if (!await _gitHubUserClient.DoesUserExistAsync(registration.GitHubLogin))
			{
				errors.AddError("GitHubLogin", "The GitHub username does not exist.");
				return RegisterNewUserResult.Failed;
			}

			user = new User()
			{
				UniqueId = _identityProvider.CurrentIdentity.UniqueId,
				UserName = _identityProvider.CurrentIdentity.UserName,
				FirstName = registration.FirstName,
				LastName = _identityProvider.CurrentIdentity.LastName,
				EmailAddress = registration.EmailAddress,
				EmailConfirmationCode = GenerateEmailConfirmationCode(),
				EmailAddressConfirmed = false,
				GitHubLogin = registration.GitHubLogin,
				SuperUser = false
			};

			var membership = await EnsureSectionMembershipAsync(user, section, SectionRole.Student);		
			await EnsureUserInGithubOrgAsync(user, membership.ClassroomMembership);
			await SendUserInvitationMailAsync(user, confirmationUrlBuilder);

			_dbContext.Users.Add(user);
			await _dbContext.SaveChangesAsync();

			return RegisterNewUserResult.Success;
		}
		/// <summary>
		/// Creates a new code question updater.
		/// </summary>
		private static CodeQuestionUpdater<CodeQuestion> GetCodeQuestionUpdater(
			TestDatabase database,
			CodeQuestion question,
			IModelErrorCollection errors)
		{
			return new Mock<CodeQuestionUpdater<CodeQuestion>>(database.Context, question, errors).Object;
		}
Esempio n. 36
0
		/// <summary>
		/// Updates a assignment.
		/// </summary>
		private bool UpdateAssignment(Assignment assignment, IModelErrorCollection modelErrors)
		{
			if (assignment.DueDates != null)
			{
				var sections = assignment.DueDates.Select(d => d.SectionId).ToList();
				if (sections.Distinct().Count() != sections.Count)
				{
					modelErrors.AddError("DueDates", "You may only have one due date per section.");

					return false;
				}
			}

			UpdateQuestionOrder(assignment.Questions);

			_dbContext.RemoveUnwantedObjects
			(
				_dbContext.AssignmentQuestions,
				question => question.Id,
				question => question.AssignmentId == assignment.Id,
				assignment.Questions
			);

			_dbContext.RemoveUnwantedObjects
			(
				_dbContext.AssignmentDueDates,
				dueDate => dueDate.Id,
				dueDate => dueDate.AssignmentId == assignment.Id,
				assignment.DueDates
			);

			return true;
		}
Esempio n. 37
0
 /// <summary>
 /// Constructor.
 /// </summary>
 protected CodeQuestionUpdater(DatabaseContext dbContext, TQuestion question, IModelErrorCollection errors)
     : base(dbContext, question, errors)
 {
 }
Esempio n. 38
0
        /// <summary>
        /// Updates the given user.
        /// </summary>
        public async Task <bool> UpdateUserAsync(
            User user,
            string confirmationUrlBuilder,
            IModelErrorCollection modelErrors)
        {
            var existingUser = await _dbContext.Users
                               .Include(u => u.ClassroomMemberships)
                               .ThenInclude(cm => cm.Classroom)
                               .Include(u => u.AdditionalContacts)
                               .SingleAsync(u => u.Id == user.Id);

            bool updatedEmail = false;

            if (user.GitHubLogin != existingUser.GitHubLogin)
            {
                if (!await _gitHubUserClient.DoesUserExistAsync(user.GitHubLogin))
                {
                    modelErrors.AddError("GitHubLogin", "The GitHub username does not exist.");
                    return(false);
                }

                if (existingUser.ClassroomMemberships != null)
                {
                    foreach (var membership in existingUser.ClassroomMemberships)
                    {
                        var orgName = membership.Classroom.GitHubOrganization;

                        var team = await _gitHubTeamClient.GetTeamAsync
                                   (
                            orgName,
                            membership.GitHubTeam
                                   );

                        await _gitHubTeamClient.InviteUserToTeamAsync
                        (
                            orgName,
                            team,
                            user.GitHubLogin
                        );

                        await _gitHubTeamClient.RemoveUserFromTeamAsync
                        (
                            orgName,
                            team,
                            existingUser.GitHubLogin
                        );

                        membership.InGitHubOrganization = false;
                    }
                }

                existingUser.GitHubLogin = user.GitHubLogin;
            }

            if (user.PublicName != existingUser.PublicName)
            {
                existingUser.PublicName = user.PublicName;
            }

            if (user.EmailAddress != existingUser.EmailAddress)
            {
                existingUser.EmailAddress          = user.EmailAddress;
                existingUser.EmailAddressConfirmed = false;
                existingUser.EmailConfirmationCode = GenerateEmailConfirmationCode();

                updatedEmail = true;
            }

            foreach (var additionalContact in existingUser.AdditionalContacts.ToList())
            {
                var modifiedContact = user.AdditionalContacts
                                      ?.SingleOrDefault(ac => ac.Id == additionalContact.Id);
                if (modifiedContact != null)
                {
                    additionalContact.LastName     = modifiedContact.LastName;
                    additionalContact.FirstName    = modifiedContact.FirstName;
                    additionalContact.EmailAddress = modifiedContact.EmailAddress;
                }
                else
                {
                    existingUser.AdditionalContacts.Remove(additionalContact);
                }
            }

            if (user.AdditionalContacts != null)
            {
                foreach (var potentialNewContact in user.AdditionalContacts)
                {
                    if (!existingUser.AdditionalContacts.Any(ac => ac.Id == potentialNewContact.Id))
                    {
                        existingUser.AdditionalContacts.Add(potentialNewContact);
                    }
                }
            }

            await _dbContext.SaveChangesAsync();

            if (updatedEmail)
            {
                await SendUserInvitationMailAsync(existingUser, confirmationUrlBuilder);
            }

            return(true);
        }
Esempio n. 39
0
 /// <summary>
 /// Constructor.
 /// </summary>
 protected QuestionUpdater(DatabaseContext dbContext, TQuestion question, IModelErrorCollection errors)
 {
     DbContext = dbContext;
     Question  = question;
     Errors    = errors;
 }
Esempio n. 40
0
		/// <summary>
		/// Updates an assignment.
		/// </summary>
		public async Task<bool> UpdateAssignmentAsync(
			string classroomName, 
			Assignment assignment,
			IModelErrorCollection modelErrors)
		{
			var classroom = await LoadClassroomAsync(classroomName);

			if (!UpdateAssignment(assignment, modelErrors))
				return false;

			assignment.ClassroomId = classroom.Id;
			_dbContext.Update(assignment);

			await _dbContext.SaveChangesAsync();

			return true;
		}