From d2cd5b95bb347d901b48e7d96fe225f02b7f4c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janis=20Daniel=20Da=CC=88hne?= <janis.daehne2@student.uni-halle.de> Date: Mon, 30 Sep 2019 13:59:50 +0200 Subject: [PATCH] - added download personal data feature --- src/ClientServer/Config/Constants.cs | 9 +- .../Core/Exercises/SubmissionController.cs | 3 +- src/ClientServer/Controllers/Core/Init.cs | 1 + .../Core/Testing/TestingController.cs | 2 + .../Core/Users/PrivacyController.cs | 521 ++++ .../Core/Users/SystemRolesController.cs | 29 +- .../Controllers/Core/Users/UsersController.cs | 3 +- src/ClientServer/Helpers/Files.cs | 4 +- ...missionCanDownloadPersonalData.Designer.cs | 2164 +++++++++++++++++ ...emRolePermissionCanDownloadPersonalData.cs | 25 + .../Migrations/YapexDbContextModelSnapshot.cs | 2 + ...stomProjectTestWithSolutionAsTestResult.cs | 4 +- .../Exercises/AfterSolutions/AfterSolution.cs | 1 - ...CustomTestWithAfterSolutionAsTestResult.cs | 2 +- .../TestWithAfterSolutionAsTestResult.cs | 2 +- ...ustomTestWithSingleSolutionAsTestResult.cs | 2 +- .../ExerciseReleaseWithUserAsParticipation.cs | 4 +- .../Models/Exercises/Solution/Solution.cs | 3 +- .../TestWithSingleSolutionAsTestResult.cs | 2 +- .../Models/Exercises/Tests/Test.cs | 6 + .../Files/FileReferenceMarkdownAsset.cs | 4 +- .../Models/Files/FileReferenceTestAsset.cs | 6 +- .../Files/FileReferenceUserFileAsset.cs | 4 +- .../Models/Files/IFileReference.cs | 3 + .../Models/Users/SystemRolePermission.cs | 5 + src/ClientServer/Models/Users/User.cs | 2 +- 26 files changed, 2785 insertions(+), 28 deletions(-) create mode 100644 src/ClientServer/Controllers/Core/Users/PrivacyController.cs create mode 100755 src/ClientServer/Migrations/20190914132004_SystemRolePermissionCanDownloadPersonalData.Designer.cs create mode 100755 src/ClientServer/Migrations/20190914132004_SystemRolePermissionCanDownloadPersonalData.cs diff --git a/src/ClientServer/Config/Constants.cs b/src/ClientServer/Config/Constants.cs index e39f097..6440579 100644 --- a/src/ClientServer/Config/Constants.cs +++ b/src/ClientServer/Config/Constants.cs @@ -13,7 +13,7 @@ namespace ClientServer.Helpers /// </summary> public static class Constants { - public static string VersionString = "2.4.2"; + public static string VersionString = "2.5.1"; /// <summary> /// this is only set once at program.cs!! @@ -24,6 +24,13 @@ namespace ClientServer.Helpers /// the port to use /// </summary> public static int Port = 5000; + + /// <summary> + /// needed when the user downloads personal data and we need to provide the schema some explanation (only the root dir) + /// </summary> + public static string SourceCodeUrlBase = "https://gitlab.informatik.uni-halle.de/Syndrom/ClientServer"; + + public static string SourceCodeModelsDir = "/tree/master/src/ClientServer/Models"; /// <summary> /// break the automatic assessment loop after an assessment threw an error (e.g. test server not reachable...) diff --git a/src/ClientServer/Controllers/Core/Exercises/SubmissionController.cs b/src/ClientServer/Controllers/Core/Exercises/SubmissionController.cs index a46c28d..0b1dccb 100644 --- a/src/ClientServer/Controllers/Core/Exercises/SubmissionController.cs +++ b/src/ClientServer/Controllers/Core/Exercises/SubmissionController.cs @@ -1286,7 +1286,7 @@ namespace ClientServer.Controllers.Core.Exercises /// /// include the csv file for the tutors that can then be filled and uploaded again /// for every plang we create a separate directory (better sorted) - /// we also include in every plang direcotry a default submission (with all template files) to be able to compare submissions against the code templates + /// we also include in every plang directory a default submission (with all template files) to be able to compare submissions against the code templates /// /// include also the file name for the submission in the csv file e.g in case we needed to change the filename (ä,ö,ü, ... bad for cross platform filenames) /// </summary> @@ -1294,6 +1294,7 @@ namespace ClientServer.Controllers.Core.Exercises [HttpGet("download/submission/all/{releaseCode}")] public async Task DownloadAllSubmissionsAsZip(string releaseCode) { + // because we use a link we don't get and need the csrf token... if (!await base.IsLoggedIn(null, false, false)) return; int userId = GetUserId(); diff --git a/src/ClientServer/Controllers/Core/Init.cs b/src/ClientServer/Controllers/Core/Init.cs index ee6fe3b..6de39c4 100644 --- a/src/ClientServer/Controllers/Core/Init.cs +++ b/src/ClientServer/Controllers/Core/Init.cs @@ -99,6 +99,7 @@ namespace ClientServer.Controllers.Core CanChangeUserData = true, CanManageTags = true, CanViewDashboard = true, + CanDownloadPersonalDataFromOthers = true, }; //the first system role cannot be changed/deleted! diff --git a/src/ClientServer/Controllers/Core/Testing/TestingController.cs b/src/ClientServer/Controllers/Core/Testing/TestingController.cs index 88eb480..4e20fcc 100644 --- a/src/ClientServer/Controllers/Core/Testing/TestingController.cs +++ b/src/ClientServer/Controllers/Core/Testing/TestingController.cs @@ -1278,6 +1278,8 @@ namespace ClientServer.Controllers.Core.Testing /// <summary> /// validates the file names for the soltuion files and the test assets (files) + /// + /// TODO maybe use <see cref="Files.ValidFileName"/>?? /// </summary> /// <param name="solutionFiles">the solution files</param> /// <param name="testAssets">the test assets to check too </param> diff --git a/src/ClientServer/Controllers/Core/Users/PrivacyController.cs b/src/ClientServer/Controllers/Core/Users/PrivacyController.cs new file mode 100644 index 0000000..60ffed1 --- /dev/null +++ b/src/ClientServer/Controllers/Core/Users/PrivacyController.cs @@ -0,0 +1,521 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using ClientServer.Controllers.Core.Exercises; +using ClientServer.Db; +using ClientServer.Helpers; +using ClientServer.Models; +using ClientServer.Models.Files; +using ClientServer.Models.Users; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json; +using NuGet.Packaging; + +namespace ClientServer.Controllers.Core.Users +{ + [Route(Constants.ApiPrefix + "users/privacy")] + public class PrivacyController : ControllerWithDb + { + public PrivacyController(YapexDbContext context) : base(context) + { + } + + + /// <summary> + /// returns the user data if the user is logged in else not + /// + /// <param name="targetUser">the target user (needs system permission) or null for the current user</param> + /// + /// <remarks> + /// here is a list with the current personal data from the user (only types, [] is an array, (is the property) (2019.09.13) - v2.4.2 + /// + ///- user + /// - UserWithUserGroup [] + /// - GroupRole + /// (- GroupRolePermission) + /// - UserSetting + /// - CodeEditorSetting + /// - Lang + /// + /// - Exercise (owner) + /// - MetaData + /// - TagWithMetaDatas [] + /// - Tag + /// - CodeTemplates [] + /// - TemplateFiles [] + /// - PLang + /// - Description + /// - AssetReferences [] + /// - FileReferenceMarkdownAsset + /// - Releases [] + /// - PLang + /// //ignore ExerciseReleaseWithUserAsParticipations here because this is not really from the user | would hurt other ///users privacy + /// - Tests [] + /// - AssetReferences [] + /// - FileReferenceTestAsset + /// - TestSettings + /// - TestType + /// - DefaultCustomTestSettings + /// + /// - CustomProject + /// - CustomProjectDescription + /// - CustomProjectSolution [] + /// - PLang + /// - CustomProjectSolutionFile (main) + /// - CustomProjectSolutionFile [] + /// - CustomProjectTestWithSolutionAsTestResult [] + /// - CustomProjectTest [] + /// - TestType + /// - CustomProjectTestWithFileAsAssetReference [] n-n + /// - FileReferenceUserFileAsset + /// - CustomProjectTestSettings + /// - CustomProjectTestWithSolutionAsTestResult [] + /// + /// - SystemRole + /// (- SystemRolePermission) + /// + /// - ExternalUser + /// - ExerciseReleaseWithUserAsParticipation [] + /// - ExerciseRelease (omit release code because hidden in overviews?) + /// - CustomTest + /// - CustomTestWithFileAsAssetReference [] + /// - FileReferenceUserFileAsset + /// - TestType + /// - CustomTestWithSingleSolutionAsTestResult [] (TestResultsNew) + /// - CustomTestWithAfterSolutionAsTestResult [] (AfterTestResults) + /// - Solution + /// - PLang + /// - SolutionAssessment + /// - SolutionFile (main) + /// (- TemplateFile (not really "from" the user...)(might be hidden??)) + /// - SolutionFile [] + /// (- TemplateFile (not really "from" the user...)(might be hidden??)) + /// - TestWithSingleSolutionAsTestResult [] + /// - CustomTestWithSingleSolutionAsTestResult [] + /// - AfterSolution [] + /// - AfterSolutionFile (main) + /// - AfterSolutionFile [] + /// - TestWithAfterSolutionAsTestResult + /// - CustomTestWithAfterSolutionAsTestResult + /// - PLang + /// + /// </remarks> + /// </summary> + [HttpGet("download/{targetUser?}")] + public async Task DownloadAllPrivateDataAsZip(int? targetUser) + { + // because we use a link we don't get and need the csrf token... + if (!await base.IsLoggedIn(null, false, false)) return; + + int userId = GetUserId(); + + if (targetUser == null) + { + targetUser = userId; + } + + if (targetUser != userId) + { + if (!await HasSystemPermission( + permission => permission != null && permission.SystemRolePermission.CanDownloadPersonalDataFromOthers)) + { + await + Response.WriteAsync( + Jc.Serialize(new BasicResponse(ResponseCode.NoPermission, "no permission"))); + return; + } + } + + + User user = await _context.Users + .FirstOrDefaultAsync(p => p.Id == targetUser); + + if (user == null) + { + await Response.WriteAsync(Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "user not found"))); + return; + } + + var allUserData = await _context.Users + .Include(p => p.UserWithUserGroups) + .ThenInclude(p => p.GroupRole) + .Include(p => p.UserWithUserGroups) + .ThenInclude(p => p.UserGroup) + //-- + .Include(p => p.UserSettings.CodeEditorSetting) + //-- + .Include(p => p.CustomProjects) + .ThenInclude(p => p.Description.AssetReferences) + .ThenInclude(p => p.FileReferenceUserFileAsset) + //-- + .Include(p => p.Exercises) + .ThenInclude(p => p.MetaData) + .ThenInclude(p => p.TagWithMetaDatas) + .ThenInclude(p => p.Tag) + //---- + .Include(p => p.Exercises) + .ThenInclude(p => p.CodeTemplates) + .ThenInclude(p => p.TemplateFiles) //includes main file (no extra include) + //---- + .Include(p => p.Exercises) + .ThenInclude(p => p.CodeTemplates) + .ThenInclude(p => p.PLang) + //---- + .Include(p => p.Exercises) + .ThenInclude(p => p.Description) + .ThenInclude(p => p.AssetReferences) + .ThenInclude(p => p.FileReferenceMarkdownAsset) + //---- + .Include(p => p.Exercises) + .ThenInclude(p => p.Releases) + .ThenInclude(p => p.PLang) + //res + .Include(p => p.Exercises) + .ThenInclude(p => p.Tests) + .ThenInclude(p => p.AssetReferences) + .ThenInclude(p => p.FileReferenceTestAsset) + //---- + .Include(p => p.Exercises) + .ThenInclude(p => p.Tests) + .ThenInclude(p => p.TestSettings) + //---- + .Include(p => p.Exercises) + .ThenInclude(p => p.Tests) + .ThenInclude(p => p.TestType) + //---- + .Include(p => p.Exercises) + .ThenInclude(p => p.DefaultCustomTestSettings) + //---- + .Include(p => p.CustomProjects) + .ThenInclude(p => p.Solutions) + .ThenInclude(p => p.PLang) + //---- + .Include(p => p.CustomProjects) + .ThenInclude(p => p.Solutions) + .ThenInclude(p => p.SolutionFiles) //includes main file (no extra include) + //---- + .Include(p => p.CustomProjects) + .ThenInclude(p => p.Solutions) + .ThenInclude(p => p.TestResults) + //---- + .Include(p => p.CustomProjects) + .ThenInclude(p => p.Tests) + .ThenInclude(p => p.TestType) + //---- + .Include(p => p.CustomProjects) + .ThenInclude(p => p.Tests) + .ThenInclude(p => p.AssetReferences) + .ThenInclude(p => p.FileReferenceUserFileAsset) + //---- + .Include(p => p.CustomProjects) + .ThenInclude(p => p.Tests) + .ThenInclude(p => p.CustomProjectTestSettings) + //---- + .Include(p => p.CustomProjects) + .ThenInclude(p => p.Tests) + .ThenInclude(p => p.TestResults) + //-- + .Include(p => p.SystemRole) + //-- + .Include(p => p.ExternalUser) + //-- + .Include(p => p.ExerciseReleaseWithUserAsParticipations) + .ThenInclude(p => p.ExerciseRelease) + //---- + .Include(p => p.ExerciseReleaseWithUserAsParticipations) + .ThenInclude(p => p.CustomTests) + .ThenInclude(p => p.AssetReferences) + .ThenInclude(p => p.FileReferenceUserFileAsset) + //---- + .Include(p => p.ExerciseReleaseWithUserAsParticipations) + .ThenInclude(p => p.CustomTests) + .ThenInclude(p => p.TestType) + //---- + .Include(p => p.ExerciseReleaseWithUserAsParticipations) + .ThenInclude(p => p.CustomTests) + .ThenInclude(p => p.TestResultsNew) + //---- + .Include(p => p.ExerciseReleaseWithUserAsParticipations) + .ThenInclude(p => p.CustomTests) + .ThenInclude(p => p.AfterTestResults) + //---- + .Include(p => p.ExerciseReleaseWithUserAsParticipations) + .ThenInclude(p => p.Solutions) + .ThenInclude(p => p.PLang) + //---- + .Include(p => p.ExerciseReleaseWithUserAsParticipations) + .ThenInclude(p => p.Solutions) + .ThenInclude(p => p.Assessment) + //---- + .Include(p => p.ExerciseReleaseWithUserAsParticipations) + .ThenInclude(p => p.Solutions) + .ThenInclude(p => p.SolutionFiles) //includes main file (no extra include) + //---- + .Include(p => p.ExerciseReleaseWithUserAsParticipations) + .ThenInclude(p => p.Solutions) + .ThenInclude(p => p.TestResults) + //---- + .Include(p => p.ExerciseReleaseWithUserAsParticipations) + .ThenInclude(p => p.Solutions) + .ThenInclude(p => p.CustomTestResults) + //---- + .Include(p => p.ExerciseReleaseWithUserAsParticipations) + .ThenInclude(p => p.Solutions) + .ThenInclude(p => p.AfterSolution) + .ThenInclude(p => p.SolutionFiles) //includes main file (no extra include) + //---- + .Include(p => p.ExerciseReleaseWithUserAsParticipations) + .ThenInclude(p => p.Solutions) + .ThenInclude(p => p.AfterSolution) + .ThenInclude(p => p.TestResults) + //---- + .Include(p => p.ExerciseReleaseWithUserAsParticipations) + .ThenInclude(p => p.Solutions) + .ThenInclude(p => p.AfterSolution) + .ThenInclude(p => p.CustomTestResults) + //---- + .Include(p => p.ExerciseReleaseWithUserAsParticipations) + .ThenInclude(p => p.LastEditedPLang) + //-- + .FirstOrDefaultAsync(p => p.Id == targetUser) + ; + + var jsonSerializerSettings = new JsonSerializerSettings + { + PreserveReferencesHandling = PreserveReferencesHandling.Objects + }; + + + #region files + + + var exerciseTestAssetsBasePath = Files.GetUploadFilePath(UploadDirType.TestAssets); + var exerciseDescriptionAssetsBasePath = Files.GetUploadFilePath(UploadDirType.MarkdownAssets); + var userAssetsBasePath = Files.GetUploadFilePath(UploadDirType.UserAssets); + + + var userOwnExerciseTestIdsToRead = new List<IFileReference>(); + var userOwnExerciseDescriptionIdsToRead = new List<IFileReference>(); + var userAssetIdsToRead = new List<IFileReference>(); + + //get all asset ids from own exercises + + foreach (var exercise in allUserData.Exercises) + { + userOwnExerciseDescriptionIdsToRead.AddRange( + exercise.Description.AssetReferences.Select(p => p.FileReferenceMarkdownAsset)); + + foreach (var exerciseTest in exercise.Tests) + { + userOwnExerciseTestIdsToRead.AddRange( + exerciseTest.AssetReferences.Select(p => p.FileReferenceTestAsset)); + } + } + + //get all asset ids from custom projects + foreach (var customProject in allUserData.CustomProjects) + { + foreach (var customProjectTest in customProject.Tests) + { + userAssetIdsToRead.AddRange( + customProjectTest.AssetReferences.Select(p => p.FileReferenceUserFileAsset)); + } + + userAssetIdsToRead.AddRange( + customProject.Description.AssetReferences.Select(p => p.FileReferenceUserFileAsset)); + } + + //get all asset ids from participations + foreach (var releaseWithUserAsParticipation in allUserData.ExerciseReleaseWithUserAsParticipations) + { + foreach (var customTest in releaseWithUserAsParticipation.CustomTests) + { + userAssetIdsToRead.AddRange(customTest.AssetReferences.Select(p => p.FileReferenceUserFileAsset)); + } + } + + + var exerciseDescriptionFileContentsDict = + await Files.ReadUploadedFilesAsArray(exerciseDescriptionAssetsBasePath, + userOwnExerciseDescriptionIdsToRead.Select(p => p.Id).ToArray()); + + var exerciseTestFileContentsDict = + await Files.ReadUploadedFilesAsArray(exerciseTestAssetsBasePath, + userOwnExerciseTestIdsToRead.Select(p => p.Id).ToArray()); + + var userFileContentsDict = + await Files.ReadUploadedFilesAsArray(userAssetsBasePath, + userAssetIdsToRead.Select(p => p.Id).ToArray()); + + + #endregion + + //for schema give gitlab url to models folder... + + try + { + var dateTime = DateTimeHelper.GetUtcNow(); + +AppConfiguration.FileUploadDirRelativePath + string dirName = + $"yapex_personalData_{dateTime.Year}_{dateTime.Month}_{dateTime.Day}__{dateTime.Hour}_{dateTime.Minute}_{dateTime.Second}"; + + var jsonResult = JsonConvert.SerializeObject(allUserData, Formatting.Indented, jsonSerializerSettings); + + var memStream = new MemoryStream(); + + var zipArchive = new ZipArchive(memStream, ZipArchiveMode.Create, true, + SubmissionController.UTF8WithoutBom); + + var entryData = zipArchive.CreateEntry(dirName + "/data.json", CompressionLevel.Fastest); + using (var streamWriter = new StreamWriter(entryData.Open(), SubmissionController.UTF8WithoutBom)) + { + streamWriter.Write(jsonResult); + } + + var ownExerciseDescriptionAssetDirName = AppConfiguration.MarkdownAssetFilesUploadDirName; + var ownExerciseTestAssetDirName = AppConfiguration.TestAssetFilesUploadDirName; + var userAssetDirName = AppConfiguration.UserAssetFilesUploadDirName; + + var entryInfo = zipArchive.CreateEntry(dirName + "/readme.txt", CompressionLevel.Fastest); + using (var streamWriter = new StreamWriter(entryInfo.Open(), SubmissionController.UTF8WithoutBom)) + { + streamWriter.WriteLine("Note that the .json file was serialized with references"); + streamWriter.WriteLine( + "You can deserialize the data e.g. with Json.NET (https://www.newtonsoft.com/json | https://github.com/JamesNK/Newtonsoft.Json)"); + streamWriter.WriteLine( + "The files are placed inside the corresponding directories (split between user files, exercise files, markdown files)"); + //even though frontend and backend don't allow spaces in file names... but legacy + streamWriter.WriteLine("If a file name contains spaces then they are replaced by underscore(s)"); + streamWriter.WriteLine("All file names are prefixed with the file id followed by an underscore"); + streamWriter.WriteLine( + "If then a file name contains some invalid characters the name is replaced with a unique guid"); + + streamWriter.WriteLine(); + streamWriter.WriteLine($"Files for exercise descriptions can be found in /{ownExerciseDescriptionAssetDirName} (if any)"); + streamWriter.WriteLine($"Files for exercise tests can be found in /{ownExerciseTestAssetDirName} (if any)"); + streamWriter.WriteLine($"Files for your custom project description and tests can be found in /{userAssetDirName} (if any)"); + streamWriter.WriteLine(); + + streamWriter.WriteLine( + $"The structure/schema of the data can be found here: {Constants.SourceCodeUrlBase}, specifically {Constants.SourceCodeUrlBase}{Constants.SourceCodeModelsDir}"); + } + + #region (own) exercise description markdown files + + foreach (var assetReference in userOwnExerciseDescriptionIdsToRead) + { + var content = exerciseDescriptionFileContentsDict[assetReference.Id]; + var fileName = $"{assetReference.Id}_{assetReference.OriginalName}"; + + if (Files.ValidFileName(fileName) == false) + { + fileName = fileName.Replace(' ', '_'); + + if (Files.ValidFileName(fileName) == false) + { + fileName = $"{assetReference.Id}_{Guid.NewGuid()}"; + } + } + + var entryFile = zipArchive.CreateEntry(dirName + "/" + ownExerciseDescriptionAssetDirName + "/" + fileName, + CompressionLevel.Fastest); + using (var streamWriter = new StreamWriter(entryFile.Open(), SubmissionController.UTF8WithoutBom)) + { + streamWriter.BaseStream.Write(content, 0, content.Length); + } + } + + #endregion + + #region (own) exercise test files + + foreach (var assetReference in userOwnExerciseTestIdsToRead) + { + var content = exerciseTestFileContentsDict[assetReference.Id]; + var fileName = $"{assetReference.Id}_{assetReference.OriginalName}"; + + if (Files.ValidFileName(fileName) == false) + { + fileName = fileName.Replace(' ', '_'); + + if (Files.ValidFileName(fileName) == false) + { + fileName = $"{assetReference.Id}_{Guid.NewGuid()}"; + } + } + + var entryFile = zipArchive.CreateEntry(dirName + "/" + ownExerciseTestAssetDirName + "/" + fileName, + CompressionLevel.Fastest); + using (var streamWriter = new StreamWriter(entryFile.Open(), SubmissionController.UTF8WithoutBom)) + { + streamWriter.BaseStream.Write(content, 0, content.Length); + } + } + + #endregion + + + #region custom user files + + foreach (var assetReference in userAssetIdsToRead) + { + var content = userFileContentsDict[assetReference.Id]; + var fileName = $"{assetReference.Id}_{assetReference.OriginalName}"; + + if (Files.ValidFileName(fileName) == false) + { + fileName = fileName.Replace(' ', '_'); + + if (Files.ValidFileName(fileName) == false) + { + fileName = $"{assetReference.Id}_{Guid.NewGuid()}"; + } + } + + var entryFile = zipArchive.CreateEntry(dirName + "/" + userAssetDirName + "/" + fileName, + CompressionLevel.Fastest); + using (var streamWriter = new StreamWriter(entryFile.Open(), SubmissionController.UTF8WithoutBom)) + { + streamWriter.BaseStream.Write(content, 0, content.Length); + } + } + + #endregion + + + zipArchive.Dispose(); + var t = base.File(memStream.ToArray(), "application/zip", dirName + ".zip"); + + await t.ExecuteResultAsync(this.ControllerContext); + + return; + } + catch (Exception e) + { + Console.WriteLine($"[ERROR] downloading personal data for user id: {targetUser}, error: {e.Message}"); + + if (AppConfiguration.IsDebugMode) + { + await + Response.WriteAsync( + Jc.Serialize(new BasicResponse(ResponseCode.ServerError, + $"could not create personal data zip file, error: {e.Message}"))); + } + else + { + await + Response.WriteAsync( + Jc.Serialize(new BasicResponse(ResponseCode.ServerError, + "could not create personal data zip file"))); + } + } + } + } +} diff --git a/src/ClientServer/Controllers/Core/Users/SystemRolesController.cs b/src/ClientServer/Controllers/Core/Users/SystemRolesController.cs index 325479b..9cce606 100644 --- a/src/ClientServer/Controllers/Core/Users/SystemRolesController.cs +++ b/src/ClientServer/Controllers/Core/Users/SystemRolesController.cs @@ -66,14 +66,15 @@ namespace ClientServer.Controllers.Core.Users CanChangeUserData = p.SystemRolePermission.CanChangeUserData, CanManageTags = p.SystemRolePermission.CanManageTags, CanChangeSystemSettings = p.SystemRolePermission.CanChangeSystemSettings, - CanViewDashboard = p.SystemRolePermission.CanViewDashboard + CanViewDashboard = p.SystemRolePermission.CanViewDashboard, + CanDownloadPersonalDataFromOthers = p.SystemRolePermission.CanDownloadPersonalDataFromOthers, }).ToListAsync(); await Response.WriteAsync( Jc.Serialize(new BasicResponseWithData<List<SystemRoleFullBase>>(ResponseCode.Ok, - "system roles queried", displayRoles))); + "", displayRoles))); } /// <summary> @@ -110,7 +111,7 @@ namespace ClientServer.Controllers.Core.Users await Response.WriteAsync( Jc.Serialize(new BasicResponseWithData<List<int>>(ResponseCode.Ok, - "system roles queried", new List<int>() {first.Id}))); + "", new List<int>() {first.Id}))); } /// <summary> @@ -150,7 +151,8 @@ namespace ClientServer.Controllers.Core.Users CanChangeUserData = roleForBackend.CanChangeUserData, CanChangeSystemSettings = roleForBackend.CanChangeSystemSettings, CanManageTags = roleForBackend.CanManageTags, - CanViewDashboard = roleForBackend.CanViewDashboard + CanViewDashboard = roleForBackend.CanViewDashboard, + CanDownloadPersonalDataFromOthers = roleForBackend.CanDownloadPersonalDataFromOthers, } }; @@ -183,13 +185,14 @@ namespace ClientServer.Controllers.Core.Users CanChangeUserData = roleForBackend.CanChangeUserData, CanChangeSystemSettings = roleForBackend.CanChangeSystemSettings, CanManageTags = roleForBackend.CanManageTags, - CanViewDashboard = roleForBackend.CanViewDashboard + CanViewDashboard = roleForBackend.CanViewDashboard, + CanDownloadPersonalDataFromOthers = roleForBackend.CanDownloadPersonalDataFromOthers, }; await Response.WriteAsync( Jc.Serialize(new BasicResponseWithData<SystemRoleFullBase>(ResponseCode.Ok, - "group independendt role created", newRoleForFrontend))); + "", newRoleForFrontend))); } /// <summary> @@ -250,6 +253,8 @@ namespace ClientServer.Controllers.Core.Users oldRole.SystemRolePermission.CanChangeSystemSettings = roleForBackend.CanChangeSystemSettings; oldRole.SystemRolePermission.CanManageTags = roleForBackend.CanManageTags; oldRole.SystemRolePermission.CanViewDashboard = roleForBackend.CanViewDashboard; + oldRole.SystemRolePermission.CanDownloadPersonalDataFromOthers = + roleForBackend.CanDownloadPersonalDataFromOthers; try { @@ -278,13 +283,14 @@ namespace ClientServer.Controllers.Core.Users CanChangeUserData = oldRole.SystemRolePermission.CanChangeUserData, CanManageTags = oldRole.SystemRolePermission.CanManageTags, CanChangeSystemSettings = oldRole.SystemRolePermission.CanChangeSystemSettings, - CanViewDashboard = oldRole.SystemRolePermission.CanViewDashboard + CanViewDashboard = oldRole.SystemRolePermission.CanViewDashboard, + CanDownloadPersonalDataFromOthers = oldRole.SystemRolePermission.CanDownloadPersonalDataFromOthers, }; await Response.WriteAsync( - Jc.Serialize(new BasicResponseWithData<SystemRoleFullBase>(ResponseCode.Ok, "cahnged system role", + Jc.Serialize(new BasicResponseWithData<SystemRoleFullBase>(ResponseCode.Ok, "", roleForFrontend))); } @@ -338,7 +344,7 @@ namespace ClientServer.Controllers.Core.Users return; } - await Response.WriteAsync(Jc.Serialize(new BasicResponse(ResponseCode.Ok, "system role deleted"))); + await Response.WriteAsync(Jc.Serialize(new BasicResponse(ResponseCode.Ok, ""))); } } @@ -419,5 +425,10 @@ namespace ClientServer.Controllers.Core.Users /// true: can, false: not /// </summary> public bool CanViewDashboard { get; set; } + + /// <summary> + /// true: can download personal data from other users, false: not + /// </summary> + public bool CanDownloadPersonalDataFromOthers { get; set; } } } diff --git a/src/ClientServer/Controllers/Core/Users/UsersController.cs b/src/ClientServer/Controllers/Core/Users/UsersController.cs index 2b19120..b8fc74f 100644 --- a/src/ClientServer/Controllers/Core/Users/UsersController.cs +++ b/src/ClientServer/Controllers/Core/Users/UsersController.cs @@ -85,7 +85,8 @@ namespace ClientServer.Controllers.Core.Users CanChangeUserData = role.SystemRolePermission.CanChangeUserData, CanManageTags = role.SystemRolePermission.CanManageTags, CanChangeSystemSettings = role.SystemRolePermission.CanChangeSystemSettings, - CanViewDashboard = role.SystemRolePermission.CanViewDashboard + CanViewDashboard = role.SystemRolePermission.CanViewDashboard, + CanDownloadPersonalDataFromOthers = role.SystemRolePermission.CanDownloadPersonalDataFromOthers, }; } diff --git a/src/ClientServer/Helpers/Files.cs b/src/ClientServer/Helpers/Files.cs index 0626930..fa3f0e4 100644 --- a/src/ClientServer/Helpers/Files.cs +++ b/src/ClientServer/Helpers/Files.cs @@ -28,6 +28,8 @@ namespace ClientServer.Helpers /// <summary> /// checks if the given name is a valid file name /// umlauts are NOT valid e.g. getting a file from windows with umlauts to a mac ... the mac can't display them (or vice versa?) + /// + /// no spaces because they are bad for paths... /// </summary> /// <param name="name">the file name</param> /// <returns>true: valid, false: not</returns> @@ -339,7 +341,7 @@ namespace ClientServer.Helpers /// </summary> MarkdownAssets, /// <summary> - /// asset files for tests + /// asset files for tests (normal, submit) /// </summary> TestAssets, /// <summary> diff --git a/src/ClientServer/Migrations/20190914132004_SystemRolePermissionCanDownloadPersonalData.Designer.cs b/src/ClientServer/Migrations/20190914132004_SystemRolePermissionCanDownloadPersonalData.Designer.cs new file mode 100755 index 0000000..6ad929c --- /dev/null +++ b/src/ClientServer/Migrations/20190914132004_SystemRolePermissionCanDownloadPersonalData.Designer.cs @@ -0,0 +1,2164 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using ClientServer.Db; +using ClientServer.Models.Exercises.Release; + +namespace ClientServer.Migrations +{ + [DbContext(typeof(YapexDbContext))] + [Migration("20190914132004_SystemRolePermissionCanDownloadPersonalData")] + partial class SystemRolePermissionCanDownloadPersonalData + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.0.3"); + + modelBuilder.Entity("ClientServer.Models.AwaitDummy", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.HasKey("Id"); + + b.ToTable("AwaitDummies"); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProject", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<string>("DisplayName") + .HasMaxLength(2000); + + b.Property<int>("LastEditorPLangId"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<int>("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("CustomProjects"); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProjectDescription", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<string>("Content") + .HasMaxLength(50000); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("CustomProjectId"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.HasKey("Id"); + + b.HasIndex("CustomProjectId") + .IsUnique(); + + b.ToTable("CustomProjectDescriptions"); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProjectDescriptionWithFileAsAssetReference", b => + { + b.Property<int>("CustomProjectDescriptionId"); + + b.Property<int>("FileReferenceUserFileAssetId"); + + b.HasKey("CustomProjectDescriptionId", "FileReferenceUserFileAssetId"); + + b.HasIndex("CustomProjectDescriptionId"); + + b.HasIndex("FileReferenceUserFileAssetId"); + + b.ToTable("CustomProjectDescriptionWithFileAsAssetReferences"); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProjectSolution", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("CustomProjectId"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<int?>("MainFileId"); + + b.Property<int>("PLangId"); + + b.HasKey("Id"); + + b.HasIndex("CustomProjectId"); + + b.HasIndex("MainFileId") + .IsUnique(); + + b.HasIndex("PLangId"); + + b.ToTable("CustomProjectSolutions"); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProjectSolutionFile", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<string>("Content") + .HasMaxLength(70000); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int?>("CustomProjectSolutionId") + .IsRequired(); + + b.Property<int>("DisplayIndex"); + + b.Property<string>("FileNameWithExtension") + .HasMaxLength(2000); + + b.Property<bool>("IsDisplayed"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.HasKey("Id"); + + b.HasIndex("CustomProjectSolutionId"); + + b.ToTable("CustomProjectSolutionFiles"); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProjectTest", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<string>("Content") + .HasMaxLength(80000); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("CustomProjectId"); + + b.Property<int>("DisplayIndex"); + + b.Property<string>("DisplayName") + .HasMaxLength(2000); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<int>("TestTypeId"); + + b.Property<int>("Weight"); + + b.HasKey("Id"); + + b.HasIndex("CustomProjectId"); + + b.HasIndex("TestTypeId"); + + b.ToTable("CustomProjectTests"); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProjectTestAsset", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<byte[]>("Content"); + + b.Property<DateTime>("CreatedAt"); + + b.Property<string>("DisplayName") + .HasMaxLength(2000); + + b.Property<string>("Hash") + .HasMaxLength(2000); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<string>("MimeType") + .HasMaxLength(2000); + + b.HasKey("Id"); + + b.ToTable("CustomProjectTestAssets"); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProjectTestSettings", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<int>("CompileTimeoutInMs"); + + b.Property<string>("CompilerOptions") + .HasMaxLength(2000); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("CustomProjectTestId"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<int>("MaxDiskSpaceInKb"); + + b.Property<int>("MemoryLimitInKb"); + + b.Property<int>("TimeoutInMs"); + + b.HasKey("Id"); + + b.HasIndex("CustomProjectTestId") + .IsUnique(); + + b.ToTable("CustomProjectTestSettings"); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProjectTestWithFileAsAssetReference", b => + { + b.Property<int>("CustomProjectTestId"); + + b.Property<int>("FileReferenceUserFileAssetId"); + + b.HasKey("CustomProjectTestId", "FileReferenceUserFileAssetId"); + + b.HasIndex("CustomProjectTestId"); + + b.HasIndex("FileReferenceUserFileAssetId"); + + b.ToTable("CustomProjectTestWithFileAsAssetReferences"); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProjectTestWithSolutionAsTestResult", b => + { + b.Property<int>("CustomProjectSolutionId"); + + b.Property<int>("CustomProjectTestId"); + + b.Property<bool>("CharacterLimitExceeded"); + + b.Property<int?>("CharacterLimitUsed"); + + b.Property<int?>("CompileTimeoutInMsUsed"); + + b.Property<DateTime>("CreatedAt"); + + b.Property<bool?>("HasCompiled"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<bool?>("Passed"); + + b.Property<int?>("ProgramExitCode"); + + b.Property<string>("Protocol") + .HasMaxLength(160100); + + b.Property<string>("RunnerVersion") + .HasMaxLength(2000); + + b.Property<int?>("TestResultCode"); + + b.Property<string>("TestServerVersion") + .HasMaxLength(2000); + + b.Property<int?>("TimeForCompiling"); + + b.Property<int?>("TimeForUserProgram"); + + b.Property<int?>("TimeoutInMsUsed"); + + b.HasKey("CustomProjectSolutionId", "CustomProjectTestId"); + + b.HasIndex("CustomProjectSolutionId"); + + b.HasIndex("CustomProjectTestId"); + + b.ToTable("CustomProjectTestWithSolutionAsTestResults"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.AfterSolutions.AfterSolution", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<int?>("MainFileId"); + + b.Property<int>("SolutionExerciseReleaseId"); + + b.Property<int>("SolutionPLangId"); + + b.Property<int>("SolutionUserId"); + + b.HasKey("Id"); + + b.HasIndex("MainFileId") + .IsUnique(); + + b.HasIndex("SolutionUserId", "SolutionExerciseReleaseId", "SolutionPLangId") + .IsUnique(); + + b.ToTable("AfterSolutions"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.AfterSolutions.AfterSolutionFile", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<int>("AfterSolutionId"); + + b.Property<string>("Content") + .HasMaxLength(70000); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("DisplayIndex"); + + b.Property<string>("FileNameWithExtension") + .HasMaxLength(2000); + + b.Property<bool>("IsDisplayed"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<int?>("TemplateFileId"); + + b.HasKey("Id"); + + b.HasIndex("AfterSolutionId"); + + b.HasIndex("TemplateFileId"); + + b.ToTable("AfterSolutionFiles"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.AfterSolutions.CustomTestWithAfterSolutionAsTestResult", b => + { + b.Property<int>("CustomTestId"); + + b.Property<int>("AfterSolutionId"); + + b.Property<bool>("CharacterLimitExceeded"); + + b.Property<int?>("CharacterLimitUsed"); + + b.Property<int?>("CompileTimeoutInMsUsed"); + + b.Property<DateTime>("CreatedAt"); + + b.Property<bool?>("HasCompiled"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<bool?>("Passed"); + + b.Property<int?>("ProgramExitCode"); + + b.Property<string>("Protocol") + .HasMaxLength(160100); + + b.Property<string>("RunnerVersion") + .HasMaxLength(2000); + + b.Property<int?>("TestResultCode"); + + b.Property<string>("TestServerVersion") + .HasMaxLength(2000); + + b.Property<int?>("TimeForCompiling"); + + b.Property<int?>("TimeForUserProgram"); + + b.Property<int?>("TimeoutInMsUsed"); + + b.HasKey("CustomTestId", "AfterSolutionId"); + + b.HasIndex("AfterSolutionId"); + + b.HasIndex("CustomTestId"); + + b.ToTable("CustomTestWithAfterSolutionAsTestResults"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.AfterSolutions.TestWithAfterSolutionAsTestResult", b => + { + b.Property<int>("TestId"); + + b.Property<int>("AfterSolutionId"); + + b.Property<bool>("CharacterLimitExceeded"); + + b.Property<int?>("CharacterLimitUsed"); + + b.Property<int?>("CompileTimeoutInMsUsed"); + + b.Property<DateTime>("CreatedAt"); + + b.Property<bool?>("HasCompiled"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<bool?>("Passed"); + + b.Property<int?>("ProgramExitCode"); + + b.Property<string>("Protocol") + .HasMaxLength(160100); + + b.Property<string>("RunnerVersion") + .HasMaxLength(2000); + + b.Property<int?>("TestResultCode"); + + b.Property<string>("TestServerVersion") + .HasMaxLength(2000); + + b.Property<int?>("TimeForCompiling"); + + b.Property<int?>("TimeForUserProgram"); + + b.Property<int?>("TimeoutInMsUsed"); + + b.HasKey("TestId", "AfterSolutionId"); + + b.HasIndex("AfterSolutionId"); + + b.HasIndex("TestId"); + + b.ToTable("TestWithAfterSolutionAsTestResults"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.CodeTemplate", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("ExerciseId"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<int?>("MainFileId"); + + b.Property<int>("PLangId"); + + b.HasKey("Id"); + + b.HasIndex("ExerciseId"); + + b.HasIndex("MainFileId") + .IsUnique(); + + b.HasIndex("PLangId"); + + b.ToTable("Templates"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.CustomTestWithFileAsAssetReference", b => + { + b.Property<int>("CustomTestId"); + + b.Property<int>("FileReferenceUserFileAssetId"); + + b.HasKey("CustomTestId", "FileReferenceUserFileAssetId"); + + b.HasIndex("CustomTestId"); + + b.HasIndex("FileReferenceUserFileAssetId"); + + b.ToTable("CustomTestWithFileAsAssetReferences"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Exercise", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<bool>("CanUserCreateCustomTests"); + + b.Property<bool>("CanUserCreateFiles"); + + b.Property<DateTime>("CreatedAt"); + + b.Property<string>("DisplayName") + .HasMaxLength(2000); + + b.Property<bool>("IsOnlyVisibleToOwner"); + + b.Property<bool>("IsPermanentlyLocked"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<string>("Note") + .HasMaxLength(10000); + + b.Property<string>("ShortDescription") + .HasMaxLength(2000); + + b.Property<int>("UserGroupId"); + + b.Property<int?>("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserGroupId"); + + b.HasIndex("UserId"); + + b.ToTable("Exercises"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.ExerciseDescription", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<string>("Content") + .HasMaxLength(50000); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("ExerciseId"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.HasKey("Id"); + + b.HasIndex("ExerciseId") + .IsUnique(); + + b.ToTable("ExerciseDescriptions"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.ExerciseDescriptionWithFileAsAssetReference", b => + { + b.Property<int>("ExerciseDescriptionId"); + + b.Property<int>("FileReferenceMarkdownAssetId"); + + b.HasKey("ExerciseDescriptionId", "FileReferenceMarkdownAssetId"); + + b.HasIndex("ExerciseDescriptionId"); + + b.HasIndex("FileReferenceMarkdownAssetId"); + + b.ToTable("ExerciseDescriptionWithFileAsAssetReferences"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.MetaData", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("ExerciseId"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.HasKey("Id"); + + b.HasIndex("ExerciseId") + .IsUnique(); + + b.ToTable("MetaDatas"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Release.ExerciseRelease", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime?>("AutomaticEndAt"); + + b.Property<DateTime?>("AutomaticStartAt"); + + b.Property<int>("AvailableWorkingTimeInMinutes"); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("ExerciseId"); + + b.Property<string>("GeneratedCode") + .IsRequired() + .HasMaxLength(2000); + + b.Property<bool>("HadAutomaticAssessmentErrors"); + + b.Property<bool>("HasAutomaticAssessmentFinished"); + + b.Property<bool>("HasAutomaticAssessmentStarted"); + + b.Property<bool>("HasLimitedWorkingTime"); + + b.Property<bool>("HideExerciseLeaveActions"); + + b.Property<bool>("HideInOverviews"); + + b.Property<bool>("HidePrintOptions"); + + b.Property<bool>("HideSiteHeaderBar"); + + b.Property<bool>("IsReleased"); + + b.Property<bool>("IsVisibleToAllAfterRelease"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<int?>("MaxManualPoint"); + + b.Property<string>("Note") + .HasMaxLength(10000); + + b.Property<int>("PLangId"); + + b.Property<int>("ReleaseDurationType"); + + b.Property<int>("ReleaseStartType"); + + b.Property<DateTime?>("ReleasedAt"); + + b.Property<bool>("RunAlsoNormalTests"); + + b.Property<bool>("ShouldAutomaticAssessSubmissions"); + + b.Property<bool>("ShouldClearClipboard"); + + b.Property<bool>("ShowAdditionalLogButton"); + + b.HasKey("Id"); + + b.HasAlternateKey("GeneratedCode") + .HasName("Unique_GeneratedCode"); + + b.HasIndex("ExerciseId"); + + b.HasIndex("PLangId"); + + b.ToTable("ExerciseReleases"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Solution.CustomTestWithSingleSolutionAsTestResult", b => + { + b.Property<int>("CustomTestId"); + + b.Property<int>("SolutionUserId"); + + b.Property<int>("SolutionExerciseReleaseId"); + + b.Property<int>("SolutionPLangId"); + + b.Property<bool>("CharacterLimitExceeded"); + + b.Property<int?>("CharacterLimitUsed"); + + b.Property<int?>("CompileTimeoutInMsUsed"); + + b.Property<DateTime>("CreatedAt"); + + b.Property<bool?>("HasCompiled"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<bool?>("Passed"); + + b.Property<int?>("ProgramExitCode"); + + b.Property<string>("Protocol") + .HasMaxLength(160100); + + b.Property<string>("RunnerVersion") + .HasMaxLength(2000); + + b.Property<int?>("TestResultCode"); + + b.Property<string>("TestServerVersion") + .HasMaxLength(2000); + + b.Property<int?>("TimeForCompiling"); + + b.Property<int?>("TimeForUserProgram"); + + b.Property<int?>("TimeoutInMsUsed"); + + b.HasKey("CustomTestId", "SolutionUserId", "SolutionExerciseReleaseId", "SolutionPLangId"); + + b.HasIndex("CustomTestId"); + + b.HasIndex("SolutionUserId", "SolutionExerciseReleaseId", "SolutionPLangId"); + + b.ToTable("CustomTestWithSingleSolutionAsTestResult"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Solution.ExerciseReleaseWithUserAsParticipation", b => + { + b.Property<int>("UserId"); + + b.Property<int>("ExerciseReleaseId"); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("LastEditedPLangId"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<bool>("LockSolutionsFlag"); + + b.Property<bool>("ShouldNotCount"); + + b.HasKey("UserId", "ExerciseReleaseId"); + + b.HasIndex("ExerciseReleaseId"); + + b.HasIndex("LastEditedPLangId"); + + b.HasIndex("UserId"); + + b.ToTable("ExerciseReleaseWithUserAsParticipations"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Solution.Solution", b => + { + b.Property<int>("UserId"); + + b.Property<int>("ExerciseReleaseId"); + + b.Property<int>("PLangId"); + + b.Property<DateTime>("CreatedAt"); + + b.Property<string>("LastEditingIpAddress") + .HasMaxLength(2000); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<int?>("MainFileId"); + + b.Property<string>("Note") + .HasMaxLength(10000); + + b.HasKey("UserId", "ExerciseReleaseId", "PLangId"); + + b.HasIndex("MainFileId") + .IsUnique(); + + b.HasIndex("PLangId"); + + b.HasIndex("UserId", "ExerciseReleaseId"); + + b.ToTable("Solutions"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Solution.SolutionAssessment", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("ExerciseReleaseId"); + + b.Property<string>("FeedbackForStudent") + .HasMaxLength(10000); + + b.Property<string>("LastAssessmentErrorMessage") + .HasMaxLength(80000); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<int?>("ManualPoints"); + + b.Property<int>("MaxNormalTestPoints"); + + b.Property<int>("MaxSubmitTestPoints"); + + b.Property<int?>("NormalTestPoints"); + + b.Property<string>("NoteForOtherTutors") + .HasMaxLength(10000); + + b.Property<int>("PLangId"); + + b.Property<int?>("SubmitTestPoints"); + + b.Property<int>("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "ExerciseReleaseId", "PLangId") + .IsUnique(); + + b.ToTable("SolutionAssessment"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Solution.SolutionFile", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<string>("Content") + .HasMaxLength(70000); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("DisplayIndex"); + + b.Property<string>("FileNameWithExtension") + .HasMaxLength(2000); + + b.Property<bool>("IsDisplayed"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<int>("SolutionExerciseReleaseId"); + + b.Property<int>("SolutionPLangId"); + + b.Property<int>("SolutionUserId"); + + b.Property<int?>("TemplateFileId"); + + b.HasKey("Id"); + + b.HasIndex("TemplateFileId"); + + b.HasIndex("SolutionUserId", "SolutionExerciseReleaseId", "SolutionPLangId"); + + b.ToTable("SolutionFiles"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Solution.TestWithSingleSolutionAsTestResult", b => + { + b.Property<int>("TestId"); + + b.Property<int>("SolutionUserId"); + + b.Property<int>("SolutionExerciseReleaseId"); + + b.Property<int>("SolutionPLangId"); + + b.Property<bool>("CharacterLimitExceeded"); + + b.Property<int?>("CharacterLimitUsed"); + + b.Property<int?>("CompileTimeoutInMsUsed"); + + b.Property<DateTime>("CreatedAt"); + + b.Property<bool?>("HasCompiled"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<bool?>("Passed"); + + b.Property<int?>("ProgramExitCode"); + + b.Property<string>("Protocol") + .HasMaxLength(160100); + + b.Property<string>("RunnerVersion") + .HasMaxLength(2000); + + b.Property<int?>("TestResultCode"); + + b.Property<string>("TestServerVersion") + .HasMaxLength(2000); + + b.Property<int?>("TimeForCompiling"); + + b.Property<int?>("TimeForUserProgram"); + + b.Property<int?>("TimeoutInMsUsed"); + + b.HasKey("TestId", "SolutionUserId", "SolutionExerciseReleaseId", "SolutionPLangId"); + + b.HasIndex("TestId"); + + b.HasIndex("SolutionUserId", "SolutionExerciseReleaseId", "SolutionPLangId"); + + b.ToTable("TestWithSingleSolutionAsTestResult"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Tag", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<string>("Description") + .HasMaxLength(2000); + + b.Property<string>("DisplayName") + .HasMaxLength(2000); + + b.Property<string>("HtmlBackgroundColor") + .HasMaxLength(2000); + + b.Property<string>("HtmlColor") + .HasMaxLength(2000); + + b.Property<string>("HtmlIcon") + .HasMaxLength(2000); + + b.Property<DateTime>("LastUpdatedAt"); + + b.HasKey("Id"); + + b.HasIndex("DisplayName") + .HasName("Unique_Tag"); + + b.ToTable("Tags"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.TagWithMetaData", b => + { + b.Property<int>("TagId"); + + b.Property<int>("MetaDataId"); + + b.Property<DateTime>("CreatedAt"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.HasKey("TagId", "MetaDataId"); + + b.HasIndex("MetaDataId"); + + b.HasIndex("TagId"); + + b.ToTable("TagWithMetaDatas"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.TemplateFile", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<int?>("CodeTemplateId") + .IsRequired(); + + b.Property<string>("Content") + .HasMaxLength(70000); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("DisplayIndex"); + + b.Property<string>("FileNameWithExtension") + .HasMaxLength(2000); + + b.Property<bool>("IsContentVisibleForUser"); + + b.Property<bool>("IsEditableByUser"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.HasKey("Id"); + + b.HasIndex("CodeTemplateId"); + + b.ToTable("TemplateFiles"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Tests.CustomTest", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<string>("Content") + .HasMaxLength(80000); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("DisplayIndex"); + + b.Property<string>("DisplayName") + .HasMaxLength(2000); + + b.Property<int>("ExerciseReleaseId"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<int>("TestTypeId"); + + b.Property<int>("UserId"); + + b.HasKey("Id"); + + b.HasIndex("TestTypeId"); + + b.HasIndex("UserId", "ExerciseReleaseId"); + + b.ToTable("CustomTest"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Tests.DefaultCustomTestSettings", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<int>("CompileTimeoutInMs"); + + b.Property<string>("CompilerOptions") + .HasMaxLength(2000); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("ExerciseId"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<int>("MaxDiskSpaceInKb"); + + b.Property<int>("MemoryLimitInKb"); + + b.Property<int>("TimeoutInMs"); + + b.HasKey("Id"); + + b.HasIndex("ExerciseId") + .IsUnique(); + + b.ToTable("DefaultCustomTestSettings"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Tests.Test", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<string>("Content") + .HasMaxLength(80000); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("DisplayIndex"); + + b.Property<string>("DisplayName") + .HasMaxLength(2000); + + b.Property<int>("ExerciseId"); + + b.Property<bool>("IsSubmitTest"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<int>("TestTypeId"); + + b.Property<int>("Weight"); + + b.HasKey("Id"); + + b.HasIndex("ExerciseId"); + + b.HasIndex("TestTypeId"); + + b.ToTable("Tests"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Tests.TestSettings", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<int>("CompileTimeoutInMs"); + + b.Property<string>("CompilerOptions") + .HasMaxLength(2000); + + b.Property<DateTime>("CreatedAt"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<int>("MaxDiskSpaceInKb"); + + b.Property<int>("MemoryLimitInKb"); + + b.Property<int>("TestId"); + + b.Property<int>("TimeoutInMs"); + + b.HasKey("Id"); + + b.HasIndex("TestId") + .IsUnique(); + + b.ToTable("TestCaseSettingses"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Tests.TestType", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<string>("DisplayName") + .HasMaxLength(2000); + + b.Property<string>("InternalName") + .HasMaxLength(2000); + + b.Property<DateTime>("LastUpdatedAt"); + + b.HasKey("Id"); + + b.ToTable("TestTypes"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.TestWithFileAsAssetReference", b => + { + b.Property<int>("TestId"); + + b.Property<int>("FileReferenceTestAssetId"); + + b.HasKey("TestId", "FileReferenceTestAssetId"); + + b.HasIndex("FileReferenceTestAssetId"); + + b.HasIndex("TestId"); + + b.ToTable("TestWithFileAsAssetReferences"); + }); + + modelBuilder.Entity("ClientServer.Models.Files.FileReferenceMarkdownAsset", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<string>("Hash") + .HasMaxLength(2000); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<string>("MimeType") + .HasMaxLength(2000); + + b.Property<string>("OriginalName") + .HasMaxLength(2000); + + b.Property<long>("SizeInBytes"); + + b.HasKey("Id"); + + b.ToTable("FileReferenceMarkdownAssets"); + }); + + modelBuilder.Entity("ClientServer.Models.Files.FileReferenceTestAsset", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<string>("Hash") + .HasMaxLength(2000); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<string>("MimeType") + .HasMaxLength(2000); + + b.Property<string>("OriginalName") + .HasMaxLength(2000); + + b.Property<long>("SizeInBytes"); + + b.HasKey("Id"); + + b.ToTable("FileReferenceTestAssets"); + }); + + modelBuilder.Entity("ClientServer.Models.Files.FileReferenceUserFileAsset", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<string>("Hash") + .HasMaxLength(2000); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<string>("MimeType") + .HasMaxLength(2000); + + b.Property<string>("OriginalName") + .HasMaxLength(2000); + + b.Property<long>("SizeInBytes"); + + b.HasKey("Id"); + + b.ToTable("FileReferenceUserFileAssets"); + }); + + modelBuilder.Entity("ClientServer.Models.Lang", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<string>("LangShortcut") + .HasMaxLength(2000); + + b.Property<string>("Language") + .HasMaxLength(2000); + + b.Property<DateTime>("LastUpdatedAt"); + + b.HasKey("Id"); + + b.ToTable("Langs"); + }); + + modelBuilder.Entity("ClientServer.Models.PLang", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<string>("DisplayName") + .HasMaxLength(2000); + + b.Property<string>("EditorHighlightModeName") + .HasMaxLength(2000); + + b.Property<string>("FileExtensionsWithDot") + .HasMaxLength(2000); + + b.Property<string>("InternalName") + .HasMaxLength(2000); + + b.Property<DateTime>("LastUpdatedAt"); + + b.HasKey("Id"); + + b.ToTable("PLangs"); + }); + + modelBuilder.Entity("ClientServer.Models.Users.AuthToken", b => + { + b.Property<int>("UserId"); + + b.Property<DateTime>("CreatedAt"); + + b.Property<string>("CsrfToken") + .HasMaxLength(2000); + + b.Property<DateTime>("ExpirationDateTime"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<string>("RandomSecret") + .HasMaxLength(2000); + + b.Property<string>("UserAuthToken") + .HasMaxLength(2000); + + b.HasKey("UserId"); + + b.HasIndex("UserAuthToken") + .IsUnique(); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("AuthTokens"); + }); + + modelBuilder.Entity("ClientServer.Models.Users.ExternalUser", b => + { + b.Property<int>("ExternalId") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<string>("Email") + .HasMaxLength(2000); + + b.Property<string>("FirstName") + .HasMaxLength(2000); + + b.Property<string>("LastName") + .HasMaxLength(2000); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<bool>("NeedToRefreshData"); + + b.Property<string>("Token") + .IsRequired() + .HasMaxLength(2000); + + b.Property<int>("UserId"); + + b.HasKey("ExternalId"); + + b.HasAlternateKey("Token") + .HasName("Unique_ExternalToken"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("ExternalUsers"); + }); + + modelBuilder.Entity("ClientServer.Models.Users.GroupRole", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<string>("DisplayName") + .HasMaxLength(2000); + + b.Property<DateTime>("LastUpdatedAt"); + + b.HasKey("Id"); + + b.ToTable("GroupRoles"); + }); + + modelBuilder.Entity("ClientServer.Models.Users.GroupRolePermission", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<bool>("CanAddUserToGroup"); + + b.Property<bool>("CanAssessExercises"); + + b.Property<bool>("CanChangeExercises"); + + b.Property<bool>("CanChangeGroupData"); + + b.Property<bool>("CanChangeOtherMembersRole"); + + b.Property<bool>("CanCreateExercises"); + + b.Property<bool>("CanDeleteExercises"); + + b.Property<bool>("CanLockExercisesPermanently"); + + b.Property<bool>("CanManageExerciseReleases"); + + b.Property<bool>("CanRemoveMemberFromGroup"); + + b.Property<bool>("CanSeeExercisesFromOthersInGroup"); + + b.Property<bool>("CanSeeOtherMembers"); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("GroupRoleId"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.HasKey("Id"); + + b.HasIndex("GroupRoleId") + .IsUnique(); + + b.ToTable("GroupRolePermissions"); + }); + + modelBuilder.Entity("ClientServer.Models.Users.Settings.CodeEditorSetting", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("FontSize"); + + b.Property<bool>("HighlightCurrentLine"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<bool>("ShowInvisibles"); + + b.Property<bool>("ShowLineIndentions"); + + b.Property<bool>("ShowLineNumbers"); + + b.Property<int>("TabSize"); + + b.Property<string>("Theme") + .HasMaxLength(2000); + + b.Property<bool>("UseWrapping"); + + b.Property<int>("UserSettingId"); + + b.HasKey("Id"); + + b.HasIndex("UserSettingId") + .IsUnique(); + + b.ToTable("CodeEditorSettings"); + }); + + modelBuilder.Entity("ClientServer.Models.Users.Settings.UserSetting", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int?>("LangId"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<string>("Theme") + .HasMaxLength(2000); + + b.Property<int?>("UserId"); + + b.HasKey("Id"); + + b.HasIndex("LangId"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("UserSettingses"); + }); + + modelBuilder.Entity("ClientServer.Models.Users.SystemRole", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<string>("DisplayName") + .HasMaxLength(2000); + + b.Property<string>("Email") + .HasMaxLength(2000); + + b.Property<DateTime>("LastUpdatedAt"); + + b.HasKey("Id"); + + b.ToTable("SystemRoles"); + }); + + modelBuilder.Entity("ClientServer.Models.Users.SystemRolePermission", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<bool>("CanChangeOtherUsersSystemRole"); + + b.Property<bool>("CanChangeRoles"); + + b.Property<bool>("CanChangeSystemSettings"); + + b.Property<bool>("CanChangeUserData"); + + b.Property<bool>("CanCreateGroups"); + + b.Property<bool>("CanCreateRoles"); + + b.Property<bool>("CanDeleteActivatedUsers"); + + b.Property<bool>("CanDeleteGroups"); + + b.Property<bool>("CanDeleteRoles"); + + b.Property<bool>("CanDownloadPersonalDataFromOthers"); + + b.Property<bool>("CanManageNewUsers"); + + b.Property<bool>("CanManageTags"); + + b.Property<bool>("CanViewDashboard"); + + b.Property<DateTime>("CreatedAt"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<int>("SystemRoleId"); + + b.HasKey("Id"); + + b.HasIndex("SystemRoleId") + .IsUnique(); + + b.ToTable("SystemRolePermissions"); + }); + + modelBuilder.Entity("ClientServer.Models.Users.SystemSetting", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<string>("CurrentServerMessage") + .HasMaxLength(10000); + + b.Property<int>("CustomProjectTestCompileTimeoutInMs"); + + b.Property<int>("CustomProjectTestMaxDiskSpaceInKb"); + + b.Property<int>("CustomProjectTestMemoryLimitInKb"); + + b.Property<int>("CustomProjectTestTimeoutInMs"); + + b.Property<int>("DefaultGroupCreatorGroupRoleId"); + + b.Property<int>("DefaultGroupRoleId"); + + b.Property<int>("DefaultUserGroupId"); + + b.Property<int>("JustRunProgramCompileTimeoutInMs"); + + b.Property<int>("JustRunProgramMaxDiskSpaceInKb"); + + b.Property<int>("JustRunProgramMemoryLimitInKb"); + + b.Property<int>("JustRunProgramTimeoutInMs"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<int>("MaxCustomProjectsPerUser"); + + b.Property<int>("MaxCustomTestsPerParticipation"); + + b.Property<int>("MaxNumberOfTestsWithOneRequest"); + + b.Property<int>("MaxNumberOfTestsWithOneRequestSubmitTestServer"); + + b.Property<int>("SubmitTestServerTimeoutInMs"); + + b.Property<string>("SubmitTestServerUrl") + .HasMaxLength(2000); + + b.Property<string>("TestServerConfigUiUrl") + .HasMaxLength(2000); + + b.Property<string>("TestServerStatsUrl") + .HasMaxLength(2000); + + b.Property<int>("TestServerTimeoutInMs"); + + b.Property<string>("TestServerUrl") + .HasMaxLength(2000); + + b.HasKey("Id"); + + b.HasIndex("DefaultGroupCreatorGroupRoleId") + .IsUnique(); + + b.HasIndex("DefaultGroupRoleId") + .IsUnique(); + + b.HasIndex("DefaultUserGroupId") + .IsUnique(); + + b.ToTable("SystemSettings"); + }); + + modelBuilder.Entity("ClientServer.Models.Users.User", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<string>("Email") + .HasMaxLength(2000); + + b.Property<string>("FirstName") + .HasMaxLength(2000); + + b.Property<bool>("IsActivated"); + + b.Property<DateTime>("LastLoginAt"); + + b.Property<string>("LastName") + .HasMaxLength(2000); + + b.Property<DateTime>("LastUpdatedAt"); + + b.Property<string>("Password") + .HasMaxLength(2000); + + b.Property<int?>("SystemRoleId"); + + b.Property<string>("Token") + .IsRequired() + .HasMaxLength(2000); + + b.HasKey("Id"); + + b.HasAlternateKey("Token") + .HasName("Unique_Token"); + + b.HasIndex("SystemRoleId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("ClientServer.Models.Users.UserGroup", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd(); + + b.Property<DateTime>("CreatedAt"); + + b.Property<string>("DisplayName") + .HasMaxLength(2000); + + b.Property<string>("Email") + .HasMaxLength(2000); + + b.Property<DateTime>("LastUpdatedAt"); + + b.HasKey("Id"); + + b.ToTable("UserGroups"); + }); + + modelBuilder.Entity("ClientServer.Models.Users.UserWithUserGroup", b => + { + b.Property<int>("UserId"); + + b.Property<int>("UserGroupId"); + + b.Property<DateTime>("CreatedAt"); + + b.Property<int>("GroupRoleId"); + + b.Property<DateTime>("LastUpdatedAt"); + + b.HasKey("UserId", "UserGroupId"); + + b.HasIndex("GroupRoleId"); + + b.HasIndex("UserGroupId"); + + b.HasIndex("UserId"); + + b.ToTable("UserWithUserGroups"); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProject", b => + { + b.HasOne("ClientServer.Models.Users.User", "User") + .WithMany("CustomProjects") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProjectDescription", b => + { + b.HasOne("ClientServer.Models.CustomProjects.CustomProject", "CustomProject") + .WithOne("Description") + .HasForeignKey("ClientServer.Models.CustomProjects.CustomProjectDescription", "CustomProjectId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProjectDescriptionWithFileAsAssetReference", b => + { + b.HasOne("ClientServer.Models.CustomProjects.CustomProjectDescription", "CustomProjectDescription") + .WithMany("AssetReferences") + .HasForeignKey("CustomProjectDescriptionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.Files.FileReferenceUserFileAsset", "FileReferenceUserFileAsset") + .WithMany("CustomProjectDescriptionWithFileAsAssetReferences") + .HasForeignKey("FileReferenceUserFileAssetId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProjectSolution", b => + { + b.HasOne("ClientServer.Models.CustomProjects.CustomProject", "CustomProject") + .WithMany("Solutions") + .HasForeignKey("CustomProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.CustomProjects.CustomProjectSolutionFile", "MainFile") + .WithOne() + .HasForeignKey("ClientServer.Models.CustomProjects.CustomProjectSolution", "MainFileId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ClientServer.Models.PLang", "PLang") + .WithMany() + .HasForeignKey("PLangId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProjectSolutionFile", b => + { + b.HasOne("ClientServer.Models.CustomProjects.CustomProjectSolution") + .WithMany("SolutionFiles") + .HasForeignKey("CustomProjectSolutionId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProjectTest", b => + { + b.HasOne("ClientServer.Models.CustomProjects.CustomProject", "CustomProject") + .WithMany("Tests") + .HasForeignKey("CustomProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.Exercises.Tests.TestType", "TestType") + .WithMany() + .HasForeignKey("TestTypeId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProjectTestSettings", b => + { + b.HasOne("ClientServer.Models.CustomProjects.CustomProjectTest", "CustomProjectTest") + .WithOne("CustomProjectTestSettings") + .HasForeignKey("ClientServer.Models.CustomProjects.CustomProjectTestSettings", "CustomProjectTestId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProjectTestWithFileAsAssetReference", b => + { + b.HasOne("ClientServer.Models.CustomProjects.CustomProjectTest", "CustomProjectTest") + .WithMany("AssetReferences") + .HasForeignKey("CustomProjectTestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.Files.FileReferenceUserFileAsset", "FileReferenceUserFileAsset") + .WithMany("CustomProjectTestWithFileAsAssetReferences") + .HasForeignKey("FileReferenceUserFileAssetId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.CustomProjects.CustomProjectTestWithSolutionAsTestResult", b => + { + b.HasOne("ClientServer.Models.CustomProjects.CustomProjectSolution", "CustomProjectSolution") + .WithMany("TestResults") + .HasForeignKey("CustomProjectSolutionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.CustomProjects.CustomProjectTest", "CustomProjectTest") + .WithMany("TestResults") + .HasForeignKey("CustomProjectTestId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.AfterSolutions.AfterSolution", b => + { + b.HasOne("ClientServer.Models.Exercises.AfterSolutions.AfterSolutionFile", "MainFile") + .WithOne() + .HasForeignKey("ClientServer.Models.Exercises.AfterSolutions.AfterSolution", "MainFileId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ClientServer.Models.Exercises.Solution.Solution", "Solution") + .WithOne("AfterSolution") + .HasForeignKey("ClientServer.Models.Exercises.AfterSolutions.AfterSolution", "SolutionUserId", "SolutionExerciseReleaseId", "SolutionPLangId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.AfterSolutions.AfterSolutionFile", b => + { + b.HasOne("ClientServer.Models.Exercises.AfterSolutions.AfterSolution", "AfterSolution") + .WithMany("SolutionFiles") + .HasForeignKey("AfterSolutionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.Exercises.TemplateFile", "TemplateFile") + .WithMany() + .HasForeignKey("TemplateFileId"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.AfterSolutions.CustomTestWithAfterSolutionAsTestResult", b => + { + b.HasOne("ClientServer.Models.Exercises.AfterSolutions.AfterSolution", "AfterSolution") + .WithMany("CustomTestResults") + .HasForeignKey("AfterSolutionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.Exercises.Tests.CustomTest", "CustomTest") + .WithMany("AfterTestResults") + .HasForeignKey("CustomTestId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.AfterSolutions.TestWithAfterSolutionAsTestResult", b => + { + b.HasOne("ClientServer.Models.Exercises.AfterSolutions.AfterSolution", "AfterSolution") + .WithMany("TestResults") + .HasForeignKey("AfterSolutionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.Exercises.Tests.Test", "Test") + .WithMany("AfterTestResults") + .HasForeignKey("TestId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.CodeTemplate", b => + { + b.HasOne("ClientServer.Models.Exercises.Exercise", "Exercise") + .WithMany("CodeTemplates") + .HasForeignKey("ExerciseId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.Exercises.TemplateFile", "MainFile") + .WithOne() + .HasForeignKey("ClientServer.Models.Exercises.CodeTemplate", "MainFileId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ClientServer.Models.PLang", "PLang") + .WithMany() + .HasForeignKey("PLangId"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.CustomTestWithFileAsAssetReference", b => + { + b.HasOne("ClientServer.Models.Exercises.Tests.CustomTest", "CustomTest") + .WithMany("AssetReferences") + .HasForeignKey("CustomTestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.Files.FileReferenceUserFileAsset", "FileReferenceUserFileAsset") + .WithMany("CustomTestWithFileAsAssetReferences") + .HasForeignKey("FileReferenceUserFileAssetId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Exercise", b => + { + b.HasOne("ClientServer.Models.Users.UserGroup", "UserGroup") + .WithMany("Exercises") + .HasForeignKey("UserGroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.Users.User", "User") + .WithMany("Exercises") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.ExerciseDescription", b => + { + b.HasOne("ClientServer.Models.Exercises.Exercise", "Exercise") + .WithOne("Description") + .HasForeignKey("ClientServer.Models.Exercises.ExerciseDescription", "ExerciseId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.ExerciseDescriptionWithFileAsAssetReference", b => + { + b.HasOne("ClientServer.Models.Exercises.ExerciseDescription", "ExerciseDescription") + .WithMany("AssetReferences") + .HasForeignKey("ExerciseDescriptionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.Files.FileReferenceMarkdownAsset", "FileReferenceMarkdownAsset") + .WithMany("AssetReferences") + .HasForeignKey("FileReferenceMarkdownAssetId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.MetaData", b => + { + b.HasOne("ClientServer.Models.Exercises.Exercise", "Exercise") + .WithOne("MetaData") + .HasForeignKey("ClientServer.Models.Exercises.MetaData", "ExerciseId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Release.ExerciseRelease", b => + { + b.HasOne("ClientServer.Models.Exercises.Exercise", "Exercise") + .WithMany("Releases") + .HasForeignKey("ExerciseId"); + + b.HasOne("ClientServer.Models.PLang", "PLang") + .WithMany() + .HasForeignKey("PLangId"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Solution.CustomTestWithSingleSolutionAsTestResult", b => + { + b.HasOne("ClientServer.Models.Exercises.Tests.CustomTest", "CustomTest") + .WithMany("TestResultsNew") + .HasForeignKey("CustomTestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.Exercises.Solution.Solution", "Solution") + .WithMany("CustomTestResults") + .HasForeignKey("SolutionUserId", "SolutionExerciseReleaseId", "SolutionPLangId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Solution.ExerciseReleaseWithUserAsParticipation", b => + { + b.HasOne("ClientServer.Models.Exercises.Release.ExerciseRelease", "ExerciseRelease") + .WithMany("ExerciseReleaseWithUserAsParticipations") + .HasForeignKey("ExerciseReleaseId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.PLang", "LastEditedPLang") + .WithMany() + .HasForeignKey("LastEditedPLangId"); + + b.HasOne("ClientServer.Models.Users.User", "User") + .WithMany("ExerciseReleaseWithUserAsParticipations") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Solution.Solution", b => + { + b.HasOne("ClientServer.Models.Exercises.Solution.SolutionFile", "MainFile") + .WithOne() + .HasForeignKey("ClientServer.Models.Exercises.Solution.Solution", "MainFileId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ClientServer.Models.PLang", "PLang") + .WithMany() + .HasForeignKey("PLangId"); + + b.HasOne("ClientServer.Models.Exercises.Solution.ExerciseReleaseWithUserAsParticipation", "ExerciseReleaseWithUserAsParticipation") + .WithMany("Solutions") + .HasForeignKey("UserId", "ExerciseReleaseId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Solution.SolutionAssessment", b => + { + b.HasOne("ClientServer.Models.Exercises.Solution.Solution", "Solution") + .WithOne("Assessment") + .HasForeignKey("ClientServer.Models.Exercises.Solution.SolutionAssessment", "UserId", "ExerciseReleaseId", "PLangId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Solution.SolutionFile", b => + { + b.HasOne("ClientServer.Models.Exercises.TemplateFile", "TemplateFile") + .WithMany("SolutionParts") + .HasForeignKey("TemplateFileId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ClientServer.Models.Exercises.Solution.Solution", "Solution") + .WithMany("SolutionFiles") + .HasForeignKey("SolutionUserId", "SolutionExerciseReleaseId", "SolutionPLangId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Solution.TestWithSingleSolutionAsTestResult", b => + { + b.HasOne("ClientServer.Models.Exercises.Tests.Test", "Test") + .WithMany("TestResultsNew") + .HasForeignKey("TestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.Exercises.Solution.Solution", "Solution") + .WithMany("TestResults") + .HasForeignKey("SolutionUserId", "SolutionExerciseReleaseId", "SolutionPLangId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.TagWithMetaData", b => + { + b.HasOne("ClientServer.Models.Exercises.MetaData", "MetaData") + .WithMany("TagWithMetaDatas") + .HasForeignKey("MetaDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.Exercises.Tag", "Tag") + .WithMany() + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.TemplateFile", b => + { + b.HasOne("ClientServer.Models.Exercises.CodeTemplate") + .WithMany("TemplateFiles") + .HasForeignKey("CodeTemplateId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Tests.CustomTest", b => + { + b.HasOne("ClientServer.Models.Exercises.Tests.TestType", "TestType") + .WithMany() + .HasForeignKey("TestTypeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.Exercises.Solution.ExerciseReleaseWithUserAsParticipation") + .WithMany("CustomTests") + .HasForeignKey("UserId", "ExerciseReleaseId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Tests.DefaultCustomTestSettings", b => + { + b.HasOne("ClientServer.Models.Exercises.Exercise", "Exercise") + .WithOne("DefaultCustomTestSettings") + .HasForeignKey("ClientServer.Models.Exercises.Tests.DefaultCustomTestSettings", "ExerciseId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Tests.Test", b => + { + b.HasOne("ClientServer.Models.Exercises.Exercise", "Exercise") + .WithMany("Tests") + .HasForeignKey("ExerciseId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.Exercises.Tests.TestType", "TestType") + .WithMany() + .HasForeignKey("TestTypeId"); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.Tests.TestSettings", b => + { + b.HasOne("ClientServer.Models.Exercises.Tests.Test", "Test") + .WithOne("TestSettings") + .HasForeignKey("ClientServer.Models.Exercises.Tests.TestSettings", "TestId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Exercises.TestWithFileAsAssetReference", b => + { + b.HasOne("ClientServer.Models.Files.FileReferenceTestAsset", "FileReferenceTestAsset") + .WithMany("AssetReferences") + .HasForeignKey("FileReferenceTestAssetId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.Exercises.Tests.Test", "Test") + .WithMany("AssetReferences") + .HasForeignKey("TestId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Users.AuthToken", b => + { + b.HasOne("ClientServer.Models.Users.User", "User") + .WithOne() + .HasForeignKey("ClientServer.Models.Users.AuthToken", "UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Users.ExternalUser", b => + { + b.HasOne("ClientServer.Models.Users.User", "User") + .WithOne("ExternalUser") + .HasForeignKey("ClientServer.Models.Users.ExternalUser", "UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Users.GroupRolePermission", b => + { + b.HasOne("ClientServer.Models.Users.GroupRole", "GroupRole") + .WithOne("GroupRolePermission") + .HasForeignKey("ClientServer.Models.Users.GroupRolePermission", "GroupRoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Users.Settings.CodeEditorSetting", b => + { + b.HasOne("ClientServer.Models.Users.Settings.UserSetting", "UserSetting") + .WithOne("CodeEditorSetting") + .HasForeignKey("ClientServer.Models.Users.Settings.CodeEditorSetting", "UserSettingId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Users.Settings.UserSetting", b => + { + b.HasOne("ClientServer.Models.Lang", "Lang") + .WithMany() + .HasForeignKey("LangId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("ClientServer.Models.Users.User", "User") + .WithOne("UserSettings") + .HasForeignKey("ClientServer.Models.Users.Settings.UserSetting", "UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Users.SystemRolePermission", b => + { + b.HasOne("ClientServer.Models.Users.SystemRole", "SystemRole") + .WithOne("SystemRolePermission") + .HasForeignKey("ClientServer.Models.Users.SystemRolePermission", "SystemRoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ClientServer.Models.Users.SystemSetting", b => + { + b.HasOne("ClientServer.Models.Users.GroupRole", "DefaultGroupCreatorGroupRole") + .WithOne() + .HasForeignKey("ClientServer.Models.Users.SystemSetting", "DefaultGroupCreatorGroupRoleId"); + + b.HasOne("ClientServer.Models.Users.GroupRole", "DefaultGroupRole") + .WithOne() + .HasForeignKey("ClientServer.Models.Users.SystemSetting", "DefaultGroupRoleId"); + + b.HasOne("ClientServer.Models.Users.UserGroup", "DefaultUserGroup") + .WithOne() + .HasForeignKey("ClientServer.Models.Users.SystemSetting", "DefaultUserGroupId"); + }); + + modelBuilder.Entity("ClientServer.Models.Users.User", b => + { + b.HasOne("ClientServer.Models.Users.SystemRole", "SystemRole") + .WithMany() + .HasForeignKey("SystemRoleId") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("ClientServer.Models.Users.UserWithUserGroup", b => + { + b.HasOne("ClientServer.Models.Users.GroupRole", "GroupRole") + .WithMany("UserUserGroups") + .HasForeignKey("GroupRoleId"); + + b.HasOne("ClientServer.Models.Users.UserGroup", "UserGroup") + .WithMany("UserWithUserGroups") + .HasForeignKey("UserGroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ClientServer.Models.Users.User", "User") + .WithMany("UserWithUserGroups") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/src/ClientServer/Migrations/20190914132004_SystemRolePermissionCanDownloadPersonalData.cs b/src/ClientServer/Migrations/20190914132004_SystemRolePermissionCanDownloadPersonalData.cs new file mode 100755 index 0000000..bc0b934 --- /dev/null +++ b/src/ClientServer/Migrations/20190914132004_SystemRolePermissionCanDownloadPersonalData.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ClientServer.Migrations +{ + public partial class SystemRolePermissionCanDownloadPersonalData : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn<bool>( + name: "CanDownloadPersonalDataFromOthers", + table: "SystemRolePermissions", + nullable: false, + defaultValue: false); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CanDownloadPersonalDataFromOthers", + table: "SystemRolePermissions"); + } + } +} diff --git a/src/ClientServer/Migrations/YapexDbContextModelSnapshot.cs b/src/ClientServer/Migrations/YapexDbContextModelSnapshot.cs index aed1841..8b3d9b7 100755 --- a/src/ClientServer/Migrations/YapexDbContextModelSnapshot.cs +++ b/src/ClientServer/Migrations/YapexDbContextModelSnapshot.cs @@ -1496,6 +1496,8 @@ namespace ClientServer.Migrations b.Property<bool>("CanDeleteRoles"); + b.Property<bool>("CanDownloadPersonalDataFromOthers"); + b.Property<bool>("CanManageNewUsers"); b.Property<bool>("CanManageTags"); diff --git a/src/ClientServer/Models/CustomProjects/CustomProjectTestWithSolutionAsTestResult.cs b/src/ClientServer/Models/CustomProjects/CustomProjectTestWithSolutionAsTestResult.cs index 7e4fa7a..e2a7b18 100644 --- a/src/ClientServer/Models/CustomProjects/CustomProjectTestWithSolutionAsTestResult.cs +++ b/src/ClientServer/Models/CustomProjects/CustomProjectTestWithSolutionAsTestResult.cs @@ -80,12 +80,12 @@ namespace ClientServer.Models.CustomProjects public string TestServerVersion { get; set; } /// <summary> - /// true: the limit <see cref="YapexDbContext.TestProtocolMaxStringLength"/> was exceeded and + /// true: the limit <see cref="YapexDbContext.TestProtocolMaxStringLength"/> was exceeded for the <see cref="Protocol"/> and /// the <see cref="Protocol"/> was cut /// </summary> public bool CharacterLimitExceeded { get; set; } /// <summary> - /// the db will only store a max of characters <see cref="YapexDbContext.TestProtocolMaxStringLength"/> + /// the db will only store a max of characters <see cref="YapexDbContext.TestProtocolMaxStringLength"/> for the <see cref="Protocol"/> /// can be null for old results /// </summary> public int? CharacterLimitUsed { get; set; } diff --git a/src/ClientServer/Models/Exercises/AfterSolutions/AfterSolution.cs b/src/ClientServer/Models/Exercises/AfterSolutions/AfterSolution.cs index 56f26b5..53bf732 100644 --- a/src/ClientServer/Models/Exercises/AfterSolutions/AfterSolution.cs +++ b/src/ClientServer/Models/Exercises/AfterSolutions/AfterSolution.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using ClientServer.Models.Exercises.Solution; namespace ClientServer.Models.Exercises.AfterSolutions { diff --git a/src/ClientServer/Models/Exercises/AfterSolutions/CustomTestWithAfterSolutionAsTestResult.cs b/src/ClientServer/Models/Exercises/AfterSolutions/CustomTestWithAfterSolutionAsTestResult.cs index aba9c74..a52384d 100644 --- a/src/ClientServer/Models/Exercises/AfterSolutions/CustomTestWithAfterSolutionAsTestResult.cs +++ b/src/ClientServer/Models/Exercises/AfterSolutions/CustomTestWithAfterSolutionAsTestResult.cs @@ -91,7 +91,7 @@ namespace ClientServer.Models.Exercises.AfterSolutions /// </summary> public bool CharacterLimitExceeded { get; set; } /// <summary> - /// the db will only store a max of characters <see cref="YapexDbContext.TestProtocolMaxStringLength"/> + /// the db will only store a max of characters <see cref="YapexDbContext.TestProtocolMaxStringLength"/> for <see cref="Protocol"/> /// can be null for old results /// </summary> public int? CharacterLimitUsed { get; set; } diff --git a/src/ClientServer/Models/Exercises/AfterSolutions/TestWithAfterSolutionAsTestResult.cs b/src/ClientServer/Models/Exercises/AfterSolutions/TestWithAfterSolutionAsTestResult.cs index fae4b2b..c0d1823 100644 --- a/src/ClientServer/Models/Exercises/AfterSolutions/TestWithAfterSolutionAsTestResult.cs +++ b/src/ClientServer/Models/Exercises/AfterSolutions/TestWithAfterSolutionAsTestResult.cs @@ -93,7 +93,7 @@ namespace ClientServer.Models.Exercises.AfterSolutions /// </summary> public bool CharacterLimitExceeded { get; set; } /// <summary> - /// the db will only store a max of characters <see cref="YapexDbContext.TestProtocolMaxStringLength"/> + /// the db will only store a max of characters <see cref="YapexDbContext.TestProtocolMaxStringLength"/> or <see cref="Protocol"/> /// can be null for old results /// </summary> public int? CharacterLimitUsed { get; set; } diff --git a/src/ClientServer/Models/Exercises/Solution/CustomTestWithSingleSolutionAsTestResult.cs b/src/ClientServer/Models/Exercises/Solution/CustomTestWithSingleSolutionAsTestResult.cs index 089f479..a9edc08 100644 --- a/src/ClientServer/Models/Exercises/Solution/CustomTestWithSingleSolutionAsTestResult.cs +++ b/src/ClientServer/Models/Exercises/Solution/CustomTestWithSingleSolutionAsTestResult.cs @@ -101,7 +101,7 @@ namespace ClientServer.Models.Exercises.Solution /// </summary> public bool CharacterLimitExceeded { get; set; } /// <summary> - /// the db will only store a max of characters <see cref="YapexDbContext.TestProtocolMaxStringLength"/> + /// the db will only store a max of characters <see cref="YapexDbContext.TestProtocolMaxStringLength"/> for <see cref="Protocol"/> /// can be null for old results /// </summary> public int? CharacterLimitUsed { get; set; } diff --git a/src/ClientServer/Models/Exercises/Solution/ExerciseReleaseWithUserAsParticipation.cs b/src/ClientServer/Models/Exercises/Solution/ExerciseReleaseWithUserAsParticipation.cs index d621a2f..353b381 100644 --- a/src/ClientServer/Models/Exercises/Solution/ExerciseReleaseWithUserAsParticipation.cs +++ b/src/ClientServer/Models/Exercises/Solution/ExerciseReleaseWithUserAsParticipation.cs @@ -7,8 +7,8 @@ using ClientServer.Models.Users; namespace ClientServer.Models.Exercises.Solution { /// <summary> - /// a class to store wich user has entered wich code (which user can access which exercise) - /// and stores the solution + /// a class to store which user has entered which code (which user can access which exercise) + /// and stores the solution for an exercise /// </summary> public class ExerciseReleaseWithUserAsParticipation { diff --git a/src/ClientServer/Models/Exercises/Solution/Solution.cs b/src/ClientServer/Models/Exercises/Solution/Solution.cs index 92a308b..7d482fa 100644 --- a/src/ClientServer/Models/Exercises/Solution/Solution.cs +++ b/src/ClientServer/Models/Exercises/Solution/Solution.cs @@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; using ClientServer.Db; +using ClientServer.Helpers; using ClientServer.Models.Exercises.AfterSolutions; namespace ClientServer.Models.Exercises.Solution @@ -96,7 +97,7 @@ namespace ClientServer.Models.Exercises.Solution /// the connected files for this solution version /// NOTE that these contain contain copies of readonly files from the exercise /// DO replace readonly files in this list with the version from the exercise (because this is always up to date) - /// USE the helper <see cref="UserSUserSolutionHelper"/> to replace the readonly files + /// USE the helper <see cref="UserSolutionHelper"/> to replace the readonly files /// </summary> public List<SolutionFile> SolutionFiles { get; set; } diff --git a/src/ClientServer/Models/Exercises/Solution/TestWithSingleSolutionAsTestResult.cs b/src/ClientServer/Models/Exercises/Solution/TestWithSingleSolutionAsTestResult.cs index 3e1d5c1..b8c436b 100644 --- a/src/ClientServer/Models/Exercises/Solution/TestWithSingleSolutionAsTestResult.cs +++ b/src/ClientServer/Models/Exercises/Solution/TestWithSingleSolutionAsTestResult.cs @@ -103,7 +103,7 @@ namespace ClientServer.Models.Exercises.Solution /// </summary> public bool CharacterLimitExceeded { get; set; } /// <summary> - /// the db will only store a max of characters <see cref="YapexDbContext.TestProtocolMaxStringLength"/> + /// the db will only store a max of characters <see cref="YapexDbContext.TestProtocolMaxStringLength"/> for <see cref="Protocol"/> /// can be null for old results /// </summary> public int? CharacterLimitUsed { get; set; } diff --git a/src/ClientServer/Models/Exercises/Tests/Test.cs b/src/ClientServer/Models/Exercises/Tests/Test.cs index 70a9ec9..4ed91b2 100644 --- a/src/ClientServer/Models/Exercises/Tests/Test.cs +++ b/src/ClientServer/Models/Exercises/Tests/Test.cs @@ -76,8 +76,14 @@ namespace ClientServer.Models.Exercises.Tests //---added + /// <summary> + /// all results for this test + /// </summary> public List<TestWithSingleSolutionAsTestResult> TestResultsNew { get; set; } + /// <summary> + /// all results for this after test (after release has finished...) + /// </summary> public List<TestWithAfterSolutionAsTestResult> AfterTestResults { get; set; } } } diff --git a/src/ClientServer/Models/Files/FileReferenceMarkdownAsset.cs b/src/ClientServer/Models/Files/FileReferenceMarkdownAsset.cs index 575bd83..205097d 100644 --- a/src/ClientServer/Models/Files/FileReferenceMarkdownAsset.cs +++ b/src/ClientServer/Models/Files/FileReferenceMarkdownAsset.cs @@ -11,6 +11,8 @@ namespace ClientServer.Models.Files /// a reference to a file on the hard drive /// for exercise description (markdown) asset files /// files are stored without an extension because we don't need to know + /// + /// as file name we use the id in the db /// </summary> public class FileReferenceMarkdownAsset : IFileReference { @@ -28,7 +30,7 @@ namespace ClientServer.Models.Files public string Hash { get; set; } /// <summary> - /// original name of the file (at the time the file was uploaded) + /// original name of the file (at the time the file was uploaded) (might have no extension if uploaded without!) /// </summary> [MaxLength(YapexDbContext.DefaultMaxStringLength)] public string OriginalName { get; set; } diff --git a/src/ClientServer/Models/Files/FileReferenceTestAsset.cs b/src/ClientServer/Models/Files/FileReferenceTestAsset.cs index f728ece..f8964b3 100644 --- a/src/ClientServer/Models/Files/FileReferenceTestAsset.cs +++ b/src/ClientServer/Models/Files/FileReferenceTestAsset.cs @@ -9,8 +9,10 @@ namespace ClientServer.Models.Files { /// <summary> /// a reference to a file on the hard drive - /// for test asset files + /// for test asset files (normal, submit tests) /// files are stored without an extension because we don't need to know + /// + /// as file name we use the id in the db /// </summary> public class FileReferenceTestAsset : IFileReference { @@ -28,7 +30,7 @@ namespace ClientServer.Models.Files public string Hash { get; set; } /// <summary> - /// original name of the file (at the time the file was uploaded) + /// original name of the file (at the time the file was uploaded) (might have no extension if uploaded without!) /// </summary> [MaxLength(YapexDbContext.DefaultMaxStringLength)] public string OriginalName { get; set; } diff --git a/src/ClientServer/Models/Files/FileReferenceUserFileAsset.cs b/src/ClientServer/Models/Files/FileReferenceUserFileAsset.cs index dc2754c..0e1f6b0 100644 --- a/src/ClientServer/Models/Files/FileReferenceUserFileAsset.cs +++ b/src/ClientServer/Models/Files/FileReferenceUserFileAsset.cs @@ -12,6 +12,8 @@ namespace ClientServer.Models.Files /// a reference to a file on the hard drive /// for user files (may be a custom test file, custom project test file, custom project description markdown asset) /// files are stored without an extension because we don't need to know + /// + /// as file name we use the id in the db /// </summary> public class FileReferenceUserFileAsset : IFileReference { @@ -31,7 +33,7 @@ namespace ClientServer.Models.Files public string Hash { get; set; } /// <summary> - /// original name of the file (at the time the file was uploaded) + /// original name of the file (at the time the file was uploaded) (might have no extension if uploaded without!) /// </summary> [MaxLength(YapexDbContext.DefaultMaxStringLength)] public string OriginalName { get; set; } diff --git a/src/ClientServer/Models/Files/IFileReference.cs b/src/ClientServer/Models/Files/IFileReference.cs index 2da462f..960ee42 100644 --- a/src/ClientServer/Models/Files/IFileReference.cs +++ b/src/ClientServer/Models/Files/IFileReference.cs @@ -8,6 +8,9 @@ namespace ClientServer.Models.Files string Hash { get; set; } + /// <summary> + /// the original name when the file was uploaded (might have no extension if uploaded without!) + /// </summary> string OriginalName { get; set; } string MimeType { get; set; } diff --git a/src/ClientServer/Models/Users/SystemRolePermission.cs b/src/ClientServer/Models/Users/SystemRolePermission.cs index bee0b07..ced1fbc 100644 --- a/src/ClientServer/Models/Users/SystemRolePermission.cs +++ b/src/ClientServer/Models/Users/SystemRolePermission.cs @@ -77,6 +77,11 @@ namespace ClientServer.Models.Users /// </summary> public bool CanViewDashboard { get; set; } + /// <summary> + /// true: can download personal data from other users, false: not + /// </summary> + public bool CanDownloadPersonalDataFromOthers { get; set; } + //fk public int SystemRoleId { get; set; } public SystemRole SystemRole { get; set; } diff --git a/src/ClientServer/Models/Users/User.cs b/src/ClientServer/Models/Users/User.cs index d42b7b0..597b8ca 100644 --- a/src/ClientServer/Models/Users/User.cs +++ b/src/ClientServer/Models/Users/User.cs @@ -95,7 +95,7 @@ namespace ClientServer.Models.Users /// <summary> - /// the <see cref="Exercise"/> s where this user was the creator + /// the <see cref="Exercise"/> s where this user was the creator / is the owner /// </summary> public List<Exercise> Exercises { get; set; } -- GitLab