diff --git a/Config/MiscConfig.cs b/Config/MiscConfig.cs index 965eb041..a8f4f7f0 100644 --- a/Config/MiscConfig.cs +++ b/Config/MiscConfig.cs @@ -44,5 +44,11 @@ public class MiscConfig /// true if Legal Notice Support should be active /// public bool UseLegalNotice { get; set; } + + /// + /// CSV Delimiter + /// + /// + public string CsvDelimiter { get; set; } } } \ No newline at end of file diff --git a/Controllers/Api/EvneApiController.cs b/Controllers/Api/EvneApiController.cs index e50085d5..ea66c08a 100644 --- a/Controllers/Api/EvneApiController.cs +++ b/Controllers/Api/EvneApiController.cs @@ -21,6 +21,7 @@ using GoNorth.Services.Security; using System.Globalization; using Microsoft.AspNetCore.Http; +using GoNorth.Services.CsvHandling; namespace GoNorth.Controllers.Api { @@ -115,6 +116,12 @@ public class EvneApiController : FlexFieldBaseApiController protected override TimelineEvent ObjectMovedToRootEvent { get { return TimelineEvent.EvneSkillMovedToRoot; } } + /// + /// Event used for the value file import event + /// + protected override TimelineEvent ValueFileImportEvent { get { return TimelineEvent.EvneValueFileImport; } } + + /// /// Aika Quest Db Access /// @@ -145,6 +152,7 @@ public class EvneApiController : FlexFieldBaseApiController /// User Db Access /// Tag Db Access /// Export Template Db Access + /// Import field values log Db Access /// Language Key Db Access /// Export Function Id Db Access /// Object export snippet Db Access @@ -155,18 +163,20 @@ public class EvneApiController : FlexFieldBaseApiController /// Kirja Page Db Access /// Tale Db Access /// Kortisto Npc Db Access + /// CSV Generator + /// CSV Reader /// User Manager /// Implementation Status Comparer /// Timeline Service /// Xss Checker /// Logger /// Localizer Factory - public EvneApiController(IEvneFolderDbAccess folderDbAccess, IEvneSkillTemplateDbAccess templateDbAccess, IEvneSkillDbAccess skillDbAccess, IProjectDbAccess projectDbAccess, IEvneSkillTagDbAccess tagDbAccess, IExportTemplateDbAccess exportTemplateDbAccess, ILanguageKeyDbAccess languageKeyDbAccess, + public EvneApiController(IEvneFolderDbAccess folderDbAccess, IEvneSkillTemplateDbAccess templateDbAccess, IEvneSkillDbAccess skillDbAccess, IProjectDbAccess projectDbAccess, IEvneSkillTagDbAccess tagDbAccess, IExportTemplateDbAccess exportTemplateDbAccess, ILanguageKeyDbAccess languageKeyDbAccess, IEvneImportFieldValuesLogDbAccess importFieldValuesLogDbAccess, IExportFunctionIdDbAccess exportFunctionIdDbAccess, IObjectExportSnippetDbAccess objectExportSnippetDbAccess, IObjectExportSnippetSnapshotDbAccess objectExportSnippetSnapshotDbAccess, IEvneSkillImageAccess imageAccess, IEvneThumbnailService thumbnailService, IAikaQuestDbAccess aikaQuestDbAccess, - ITaleDbAccess taleDbAccess, IKirjaPageDbAccess kirjaPageDbAccess, IKortistoNpcDbAccess kortistoNpcDbAccess, UserManager userManager, IImplementationStatusComparer implementationStatusComparer, ITimelineService timelineService, IXssChecker xssChecker, ILogger logger, - IStringLocalizerFactory localizerFactory) - : base(folderDbAccess, templateDbAccess, skillDbAccess, projectDbAccess, tagDbAccess, exportTemplateDbAccess, languageKeyDbAccess, exportFunctionIdDbAccess, objectExportSnippetDbAccess, objectExportSnippetSnapshotDbAccess, imageAccess, thumbnailService, userManager, - implementationStatusComparer, timelineService, xssChecker, logger, localizerFactory) + ITaleDbAccess taleDbAccess, IKirjaPageDbAccess kirjaPageDbAccess, IKortistoNpcDbAccess kortistoNpcDbAccess, ICsvGenerator csvGenerator, ICsvParser csvReader, UserManager userManager, IImplementationStatusComparer implementationStatusComparer, ITimelineService timelineService, + IXssChecker xssChecker, ILogger logger, IStringLocalizerFactory localizerFactory) + : base(folderDbAccess, templateDbAccess, skillDbAccess, projectDbAccess, tagDbAccess, exportTemplateDbAccess, importFieldValuesLogDbAccess, languageKeyDbAccess, exportFunctionIdDbAccess, objectExportSnippetDbAccess, objectExportSnippetSnapshotDbAccess, imageAccess, thumbnailService, csvGenerator, csvReader, + userManager, implementationStatusComparer, timelineService, xssChecker, logger, localizerFactory) { _aikaQuestDbAccess = aikaQuestDbAccess; _taleDbAccess = taleDbAccess; diff --git a/Controllers/Api/FlexFieldBaseApiController.cs b/Controllers/Api/FlexFieldBaseApiController.cs index 7651587d..9d8f8718 100644 --- a/Controllers/Api/FlexFieldBaseApiController.cs +++ b/Controllers/Api/FlexFieldBaseApiController.cs @@ -19,14 +19,27 @@ using GoNorth.Data.Exporting; using GoNorth.Services.Security; using System.Globalization; +using System.Text; +using GoNorth.Services.CsvHandling; +using System.Text.Json; namespace GoNorth.Controllers.Api { /// /// Flex Field Base Api Controller Api controller /// - public abstract class FlexFieldBaseApiController : ControllerBase where T:FlexFieldObject + public abstract class FlexFieldBaseApiController : ControllerBase where T:FlexFieldObject,ICloneable { + /// + /// Number Flex Field Type + /// + public const int FlexFieldType_Number = 2; + + /// + /// Option Flex Field Type + /// + public const int FlexFieldType_Option = 3; + /// /// Folder Request data /// @@ -92,6 +105,81 @@ public class FlexFieldObjectQueryResult } + /// + /// Request to export field values + /// + public class ExportFieldValuesRequest + { + /// + /// Selected template + /// + public string SelectedTemplate { get; set; } + + /// + /// Selected fields + /// + public List SelectedFields { get; set; } + + /// + /// Id of the folder + /// + public string FolderId { get; set; } + } + + + /// + /// Query result for querying field values + /// + public class ImportFieldValuesLogQueryResult + { + /// + /// true if there are more objects to query, else false + /// + public bool HasMore { get; set; } + + /// + /// Logs + /// + public List Logs { get; set; } + } + + + /// + /// Csv Content Type + /// + private const string CsvContentType = "text/csv"; + + /// + /// Excel Csv Content Type + /// + private const string ExcelCsvContentType = "application/vnd.ms-excel"; + + /// + /// CSV Header Id + /// + private const string CsvHeaderId = "Id"; + + /// + /// CSV Header Id + /// + private const string CsvHeaderName = "Name"; + + /// + /// CSV Header Result + /// + private const string CsvHeaderResult = "Result"; + + /// + /// CSV Header Error Message + /// + private const string CsvHeaderErrorMessage = "Error Message"; + + /// + /// Prefix for CSV Header Template Id + /// + private const string CsvHeaderTemplateIdPrefix = "TemplateId:"; + + /// /// Event used for the folder created event /// @@ -172,7 +260,13 @@ public class FlexFieldObjectQueryResult /// /// Event used for the object moved to root level event /// - protected abstract TimelineEvent ObjectMovedToRootEvent { get; } + protected abstract TimelineEvent ObjectMovedToRootEvent { get; } + + + /// + /// Event used for the value file import event + /// + protected abstract TimelineEvent ValueFileImportEvent { get; } /// @@ -205,6 +299,11 @@ public class FlexFieldObjectQueryResult /// private readonly IExportTemplateDbAccess _exportTemplateDbAccess; + /// + /// Import field values log Db Access + /// + private readonly IFlexFieldImportFieldValuesLogDbAccess _importFieldValuesLogDbAccess; + /// /// Language Key Db Access /// @@ -245,6 +344,16 @@ public class FlexFieldObjectQueryResult /// protected readonly ITimelineService _timelineService; + /// + /// CSV Generator + /// + protected readonly ICsvGenerator _csvGenerator; + + /// + /// CSV Parser + /// + protected readonly ICsvParser _csvParser; + /// /// User Manager /// @@ -274,12 +383,15 @@ public class FlexFieldObjectQueryResult /// Project Db Access /// Tag Db Access /// Export Template Db Access + /// Import field values log Db Access /// Language Key Db Access /// Export Function Id Db Access /// Object export snippet Db Access /// Object export snippet snapshot Db Access /// Image Access /// Thumbnail Service + /// CSV Generator + /// CSV Parser /// User Manager /// Implementation Status Comparer /// Timeline Service @@ -287,9 +399,9 @@ public class FlexFieldObjectQueryResult /// Logger /// Localizer Factory public FlexFieldBaseApiController(IFlexFieldFolderDbAccess folderDbAccess, IFlexFieldObjectDbAccess templateDbAccess, IFlexFieldObjectDbAccess objectDbAccess, IProjectDbAccess projectDbAccess, IFlexFieldObjectTagDbAccess tagDbAccess, IExportTemplateDbAccess exportTemplateDbAccess, - ILanguageKeyDbAccess languageKeyDbAccess, IExportFunctionIdDbAccess exportFunctionIdDbAccess, IObjectExportSnippetDbAccess objectExportSnippetDbAccess, IObjectExportSnippetSnapshotDbAccess objectExportSnippetSnapshotDbAccess, - IFlexFieldObjectImageAccess imageAccess, IFlexFieldThumbnailService thumbnailService, UserManager userManager, IImplementationStatusComparer implementationStatusComparer, ITimelineService timelineService, - IXssChecker xssChecker, ILogger> logger, IStringLocalizerFactory localizerFactory) + IFlexFieldImportFieldValuesLogDbAccess importFieldValuesLogDbAccess, ILanguageKeyDbAccess languageKeyDbAccess, IExportFunctionIdDbAccess exportFunctionIdDbAccess, IObjectExportSnippetDbAccess objectExportSnippetDbAccess, + IObjectExportSnippetSnapshotDbAccess objectExportSnippetSnapshotDbAccess, IFlexFieldObjectImageAccess imageAccess, IFlexFieldThumbnailService thumbnailService, ICsvGenerator csvGenerator, ICsvParser csvParser, UserManager userManager, + IImplementationStatusComparer implementationStatusComparer, ITimelineService timelineService, IXssChecker xssChecker, ILogger> logger, IStringLocalizerFactory localizerFactory) { _folderDbAccess = folderDbAccess; _templateDbAccess = templateDbAccess; @@ -297,12 +409,15 @@ public FlexFieldBaseApiController(IFlexFieldFolderDbAccess folderDbAccess, IFlex _projectDbAccess = projectDbAccess; _tagDbAccess = tagDbAccess; _exportTemplateDbAccess = exportTemplateDbAccess; + _importFieldValuesLogDbAccess = importFieldValuesLogDbAccess; _languageKeyDbAccess = languageKeyDbAccess; _exportFunctionIdDbAccess = exportFunctionIdDbAccess; _objectExportSnippetDbAccess = objectExportSnippetDbAccess; _objectExportSnippetSnapshotDbAccess = objectExportSnippetSnapshotDbAccess; _imageAccess = imageAccess; _thumbnailService = thumbnailService; + _csvGenerator = csvGenerator; + _csvParser = csvParser; _userManager = userManager; _implementationStatusComparer = implementationStatusComparer; _timelineService = timelineService; @@ -1402,5 +1517,709 @@ private async Task RemoveUnusedTags(List tagsToCheck) } } + /// + /// Exports a list of field values for a list of templates + /// + /// Export request + /// File with field values + [ValidateAntiForgeryToken] + [ProducesResponseType(typeof(byte[]), StatusCodes.Status200OK)] + [HttpPost] + public async Task ExportFieldValues([FromForm]ExportFieldValuesRequest exportRequest) + { + if(exportRequest.SelectedFields == null) + { + exportRequest.SelectedFields = new List(); + } + + T template = await _templateDbAccess.GetFlexFieldObjectById(exportRequest.SelectedTemplate); + if (template == null) + { + return NotFound(); + } + + List objects = await _objectDbAccess.GetFlexFieldObjectsByTemplate(exportRequest.SelectedTemplate); + if(!string.IsNullOrEmpty(exportRequest.FolderId)) + { + GoNorthProject curProject = await _projectDbAccess.GetDefaultProject(); + List folders = await _folderDbAccess.GetFoldersForHierarchy(curProject.Id); + objects = FilterObjectsByFolder(objects, folders, exportRequest.FolderId); + } + + List> rowValues = objects.Select(o => GetFieldValuesForObject(exportRequest.SelectedFields, o)).ToList(); + + List headerValues = new List { CsvHeaderId, CsvHeaderName }; + headerValues.AddRange(exportRequest.SelectedFields); + headerValues.Add(CsvHeaderTemplateIdPrefix + template.Id); + + string csvValues = await _csvGenerator.GenerateCsvFile(headerValues, rowValues); + + return File(Encoding.UTF8.GetBytes(csvValues), CsvContentType, template.Name + "_FieldValues.csv"); + } + + /// + /// Filters an object by folder hierarchy + /// + /// Objects to filter + /// Folder + /// Id of the folder from which to filter + /// List of objects + private List FilterObjectsByFolder(List objects, List folders, string folderId) + { + List filteredFolders = folders.Where(f => IsFolderChildOfFolder(folderId, f, folders)).ToList(); + return objects.Where(o => filteredFolders.Any(f => f.Id == o.ParentFolderId)).ToList(); + } + + /// + /// Checks if a folder is a child of a folder + /// + /// Id of the folder to check + /// Folder + /// Folders + /// true if the folder is a child + private bool IsFolderChildOfFolder(string folderId, FlexFieldFolder folder, List folders) + { + if(folder.Id == folderId) + { + return true; + } + + FlexFieldFolder parent = folder; + do + { + parent = folders.FirstOrDefault(f => f.Id == parent.ParentFolderId); + if(parent == null) + { + break; + } + + if(parent.Id == folderId) + { + return true; + } + } + while(parent != null); + + return false; + } + + /// + /// Returns the field values for an object + /// + /// Selected fields for exporting + /// Object to export + /// Field values for the object + private Dictionary GetFieldValuesForObject(List selectedFields, T obj) + { + Dictionary fieldValues = new Dictionary(); + fieldValues.Add(CsvHeaderId, obj.Id); + fieldValues.Add(CsvHeaderName, obj.Name); + + List fieldsToExport = obj.Fields.Where(f => selectedFields.Contains(f.Name) && f.Name != CsvHeaderId && f.Name != CsvHeaderName).ToList(); + foreach(FlexField curField in fieldsToExport) + { + if(!fieldValues.ContainsKey(curField.Name)) + { + fieldValues.Add(curField.Name, curField.Value); + } + } + + return fieldValues; + } + + /// + /// Runs a field value import pre check + /// + /// Prcheck Result + [ValidateAntiForgeryToken] + [ProducesResponseType(typeof(FlexFieldImportValuePreCheckResult), StatusCodes.Status200OK)] + [HttpPost] + public async Task ImportFieldValuesPreCheck() + { + if (!Request.Form.Files.Any()) + { + return BadRequest(); + } + + IFormFile uploadFile = Request.Form.Files[0]; + if (uploadFile.ContentType != CsvContentType && uploadFile.ContentType != ExcelCsvContentType) + { + return BadRequest(); + } + + CsvReadResult csvReadResult; + using (Stream csvStream = uploadFile.OpenReadStream()) + { + csvReadResult = await _csvParser.ReadCsvFile(csvStream); + } + + if (csvReadResult == null) + { + return BadRequest(); + } + + if(!csvReadResult.Columns.Any()) + { + return BadRequest(_localizer["ImportFieldValuesNoValidRows"]); + } + + List objectsToUpdate = await LoadObjectsFromCsv(csvReadResult); + FlexFieldImportValuePreCheckResult preCheckResult = await BuildCsvPreCheckResult(uploadFile.FileName, csvReadResult, objectsToUpdate); + if(preCheckResult == null) + { + return BadRequest(_localizer["ImportFieldValuesNoValidTemplateSpecified"]); + } + return Ok(preCheckResult); + } + + /// + /// Loads the objects which need to be updated for a CSV + /// + /// CSV Read result + /// Loaded objects + private async Task> LoadObjectsFromCsv(CsvReadResult csvReadResult) + { + List ids = new List(); + foreach (Dictionary curRow in csvReadResult.Rows) + { + if (curRow.ContainsKey(CsvHeaderId)) + { + ids.Add(curRow[CsvHeaderId]); + } + } + List objectsToUpdate = await _objectDbAccess.GetFlexFieldObjectsByIds(ids); + return objectsToUpdate; + } + + /// + /// Builds the CSV PreCheck result + /// + /// Filename + /// CSV Read Result + /// Loaded objects + /// Pre Check result + private async Task BuildCsvPreCheckResult(string filename, CsvReadResult csvReadResult, List objectsToUpdate) + { + FlexFieldImportValuePreCheckResult preCheckResult = new FlexFieldImportValuePreCheckResult(); + preCheckResult.Filename = filename; + + preCheckResult.Columns = csvReadResult.Columns.Where(c => c != CsvHeaderId && !c.StartsWith(CsvHeaderTemplateIdPrefix)).ToList(); + + string headerTemplateValue = csvReadResult.Columns.FirstOrDefault(c => c.StartsWith(CsvHeaderTemplateIdPrefix)); + if(string.IsNullOrEmpty(headerTemplateValue)) + { + return null; + } + + string templateId = headerTemplateValue.Replace(CsvHeaderTemplateIdPrefix, string.Empty); + T template = await _templateDbAccess.GetFlexFieldObjectById(templateId); + if(template == null) + { + return null; + } + + preCheckResult.TemplateId = template.Id; + + foreach(Dictionary curRow in csvReadResult.Rows) + { + string id = string.Empty; + if(curRow.ContainsKey(CsvHeaderId)) + { + id = curRow[CsvHeaderId]; + } + + FlexFieldImportValuePreCheckRow preCheckRow = new FlexFieldImportValuePreCheckRow(); + preCheckRow.Id = id; + + T existingObject = objectsToUpdate.FirstOrDefault(o => o.Id == id); + foreach(string curCol in csvReadResult.Columns) + { + if(curCol == CsvHeaderId || curCol.StartsWith(CsvHeaderTemplateIdPrefix)) + { + continue; + } + + FlexFieldImportValuePreCheckCell preCheckCell = new FlexFieldImportValuePreCheckCell(); + preCheckCell.Name = curCol; + preCheckCell.NewValue = string.Empty; + if(curRow.ContainsKey(curCol)) + { + preCheckCell.NewValue = curRow[curCol]; + } + + preCheckCell.OldValue = string.Empty; + if(curCol == CsvHeaderName && existingObject != null) + { + preCheckCell.OldValue = existingObject.Name; + } + else if(existingObject != null && existingObject.Fields != null) + { + FlexField field = existingObject.Fields.FirstOrDefault(f => f.Name == curCol); + if(field != null) + { + preCheckCell.OldValue = field.Value; + } + } + preCheckRow.ColumnValues.Add(preCheckCell); + } + + if(existingObject != null) + { + preCheckResult.ExistingRows.Add(preCheckRow); + } + else + { + preCheckResult.NewRows.Add(preCheckRow); + } + } + + return preCheckResult; + } + + /// + /// Runs a field value import + /// + /// Rows to import + /// Import Result + [ValidateAntiForgeryToken] + [ProducesResponseType(typeof(FlexFieldImportFieldValuesResultLog), StatusCodes.Status200OK)] + [HttpPost] + public async Task ImportFieldValues([FromBody]FlexFieldImportValueImportRequest importRows) + { + if(string.IsNullOrEmpty(importRows.TemplateId)) + { + return BadRequest(); + } + + if(string.IsNullOrEmpty(importRows.TargetFolderId)) + { + importRows.TargetFolderId = string.Empty; + } + + T template = await _templateDbAccess.GetFlexFieldObjectById(importRows.TemplateId); + if(template == null) + { + return BadRequest(); + } + + GoNorthProject curProject = await _projectDbAccess.GetDefaultProject(); + + FlexFieldImportFieldValuesResultLog importResult = new FlexFieldImportFieldValuesResultLog(); + importResult.FileName = importRows.Filename; + importResult.ProjectId = curProject.Id; + importResult.Columns = importRows.Columns; + + List existingIds = importRows.ExistingRows.Select(e => e.Id).ToList(); + List existingObjectsToUpdate = await _objectDbAccess.GetFlexFieldObjectsByIds(existingIds); + + await ImportFieldValuesExistingRows(template, importRows.ExistingRows, importResult, existingObjectsToUpdate); + await ImportFieldValuesNewRows(template, importRows.TargetFolderId, importRows.NewRows, importResult); + + if(importResult.ExistingRows.Any(e => e.Result == FlexFieldImportValueRowResult.Success) || importResult.NewRows.Any(e => e.Result == FlexFieldImportValueRowResult.Success)) + { + await _timelineService.AddTimelineEntry(ValueFileImportEvent, importResult.ExistingRows.Where(e => e.Result == FlexFieldImportValueRowResult.Success).Count().ToString(), + importResult.NewRows.Where(e => e.Result == FlexFieldImportValueRowResult.Success).Count().ToString()); + } + + await this.SetModifiedData(_userManager, importResult); + + importResult = await _importFieldValuesLogDbAccess.CreateImportLog(importResult); + + return Ok(importResult); + } + + /// + /// Imports the field values for existing rows + /// + /// Template to which the rows belong + /// Existing rows that are being imported + /// Import result to populate + /// Already existing objects + /// Task + private async Task ImportFieldValuesExistingRows(T template, List existingRows, FlexFieldImportFieldValuesResultLog importResult, List existingObjectsToUpdate) + { + foreach (FlexFieldImportValuePreCheckRow curExistingRow in existingRows) + { + FlexFieldImportValueRow importRow = new FlexFieldImportValueRow(); + importRow.Id = curExistingRow.Id; + importRow.ColumnValues = curExistingRow.ColumnValues; + + T existingObject = existingObjectsToUpdate.FirstOrDefault(e => e.Id == curExistingRow.Id); + if (existingObject == null) + { + importRow.Result = FlexFieldImportValueRowResult.Failed; + importRow.ErrorMessage = _localizer["ImportFieldValuesObjectNotFound"]; + importResult.ExistingRows.Add(importRow); + continue; + } + + string invalidField = string.Empty; + bool unknownFieldForTemplate = false; + bool fieldValueInvalid = false; + bool anyChange = false; + foreach (FlexFieldImportValuePreCheckCell curColumn in curExistingRow.ColumnValues) + { + if(curColumn.Name.StartsWith(CsvHeaderTemplateIdPrefix)) + { + continue; + } + + if (curColumn.Name == CsvHeaderName) + { + if (existingObject.Name != curColumn.NewValue) + { + existingObject.Name = curColumn.NewValue; + anyChange = true; + } + } + else + { + bool isNewField = false; + string columnName = curColumn.Name.ToLowerInvariant(); + FlexField updatedField = existingObject.Fields.FirstOrDefault(f => f.Name.ToLowerInvariant() == columnName); + if (updatedField == null) + { + isNewField = true; + updatedField = GetFlexFieldFromTemplate(template, columnName); + if(updatedField == null) + { + invalidField = curColumn.Name; + unknownFieldForTemplate = true; + continue; + } + } + + if (updatedField.Value == curColumn.NewValue && (!isNewField || string.IsNullOrEmpty(curColumn.NewValue))) + { + continue; + } + + if (!ValidateFieldValue(updatedField, curColumn.NewValue)) + { + invalidField = curColumn.Name; + fieldValueInvalid = true; + break; + } + + updatedField.Value = curColumn.NewValue; + if(isNewField) + { + existingObject.Fields.Add(updatedField); + } + anyChange = true; + } + } + + if(string.IsNullOrEmpty(existingObject.Name)) + { + importRow.Result = FlexFieldImportValueRowResult.Failed; + importRow.ErrorMessage = _localizer["ImportFieldValuesNameIsEmpty"]; + importResult.ExistingRows.Add(importRow); + continue; + } + + if (fieldValueInvalid || unknownFieldForTemplate) + { + importRow.Result = FlexFieldImportValueRowResult.Failed; + importRow.ErrorMessage = _localizer[unknownFieldForTemplate ? "ImportFieldValuesValueNotValid" : "ImportFieldValuesValueNotValid", invalidField]; + importResult.ExistingRows.Add(importRow); + continue; + } + + if (!anyChange) + { + importRow.Result = FlexFieldImportValueRowResult.NoChange; + importResult.ExistingRows.Add(importRow); + continue; + } + + try + { + await SetNotImplementedFlagOnChange(existingObject); + + existingObject = await RunAdditionalUpdates(existingObject, existingObject); + + await this.SetModifiedData(_userManager, existingObject); + + await _objectDbAccess.UpdateFlexFieldObject(existingObject); + importRow.Result = FlexFieldImportValueRowResult.Success; + } + catch (Exception ex) + { + importRow.Result = FlexFieldImportValueRowResult.Failed; + importRow.ErrorMessage = _localizer["ImportFieldValuesObjectCouldNotBeUpdated"]; + _logger.LogError(ex, string.Format("Could not import object {0}", existingObject.Id)); + } + + importResult.ExistingRows.Add(importRow); + } + } + + /// + /// Returns a flex field from a template + /// + /// Template + /// Name of the field + /// Flex Field + private FlexField GetFlexFieldFromTemplate(T template, string name) + { + FlexField templateField = template.Fields.FirstOrDefault(f => f.Name.ToLowerInvariant() == name); + if(templateField == null) + { + return null; + } + + templateField = (FlexField)templateField.Clone(); + templateField.Id = Guid.NewGuid().ToString(); + templateField.CreatedFromTemplate = true; + return templateField; + } + + /// + /// Imports the new rows for a field value + /// + /// Template to use + /// Target folder id + /// New Rows to import + /// Import result to fill + /// Task + private async Task ImportFieldValuesNewRows(T template, string targetFolderId, List newRows, FlexFieldImportFieldValuesResultLog importResult) + { + if(!newRows.Any()) + { + return; + } + + foreach (FlexFieldImportValuePreCheckRow curNewRow in newRows) + { + T newObject = (T)template.Clone(); + newObject.TemplateId = template.Id; + newObject.ParentFolderId = targetFolderId; + + string invalidField = string.Empty; + bool unknownFieldForTemplate = false; + bool fieldValueInvalid = false; + + FlexFieldImportValueRow importRow = new FlexFieldImportValueRow(); + importRow.ColumnValues = curNewRow.ColumnValues; + + foreach (FlexFieldImportValuePreCheckCell curColumn in curNewRow.ColumnValues) + { + if(curColumn.Name.StartsWith(CsvHeaderTemplateIdPrefix)) + { + continue; + } + + if (curColumn.Name == CsvHeaderName) + { + newObject.Name = curColumn.NewValue; + } + else + { + string columnName = curColumn.Name.ToLowerInvariant(); + FlexField updatedField = newObject.Fields.FirstOrDefault(f => f.Name.ToLowerInvariant() == columnName); + if (updatedField == null) + { + invalidField = curColumn.Name; + unknownFieldForTemplate = true; + continue; + } + + if (!ValidateFieldValue(updatedField, curColumn.NewValue)) + { + invalidField = curColumn.Name; + fieldValueInvalid = true; + break; + } + + updatedField.Value = curColumn.NewValue; + } + } + + if(string.IsNullOrEmpty(newObject.Name)) + { + importRow.Result = FlexFieldImportValueRowResult.Failed; + importRow.ErrorMessage = _localizer["ImportFieldValuesNameIsEmpty"]; + importResult.NewRows.Add(importRow); + continue; + } + + if (fieldValueInvalid || unknownFieldForTemplate) + { + importRow.Result = FlexFieldImportValueRowResult.Failed; + importRow.ErrorMessage = _localizer[unknownFieldForTemplate ? "ImportFieldValuesValueNotValid" : "ImportFieldValuesValueNotValid", invalidField]; + importResult.NewRows.Add(importRow); + continue; + } + + try + { + newObject.IsImplemented = false; + + newObject = await RunAdditionalUpdates(newObject, newObject); + + await this.SetModifiedData(_userManager, newObject); + + newObject = await _objectDbAccess.CreateFlexFieldObject(newObject); + importRow.Result = FlexFieldImportValueRowResult.Success; + importRow.Id = newObject.Id; + } + catch (Exception ex) + { + importRow.Result = FlexFieldImportValueRowResult.Failed; + importRow.ErrorMessage = _localizer["ImportFieldValuesObjectCouldNotBeCreated"]; + _logger.LogError(ex, string.Format("Could not import new object {0}", newObject.Name)); + } + + importResult.NewRows.Add(importRow); + } + } + + /// + /// Validates a field value + /// + /// Target field + /// New value + /// True if the field is valid, else false + private bool ValidateFieldValue(FlexField field, string newValue) + { + if(field.FieldType == FlexFieldType_Number) + { + float tempVal; + return float.TryParse(newValue, out tempVal); + } + else if(field.FieldType == FlexFieldType_Option) + { + try + { + List allowedOptions = JsonSerializer.Deserialize>(field.AdditionalConfiguration); + return allowedOptions.Any(a => a == newValue); + } + catch(Exception ex) + { + _logger.LogError(ex, string.Format("Could not parse options for field {0}", field.Name)); + return false; + } + } + + return true; + } + + + /// + /// Exports the result of an import for field values + /// + /// Id of the log + /// Export file + [ProducesResponseType(typeof(byte[]), StatusCodes.Status200OK)] + [HttpGet] + public async Task ExportFieldValueImportResult(string id) + { + FlexFieldImportFieldValuesResultLog importLog = await _importFieldValuesLogDbAccess.GetImportLogById(id); + if(importLog == null) + { + return NotFound(); + } + + List columns = new List { CsvHeaderId, CsvHeaderResult, CsvHeaderErrorMessage }; + columns.AddRange(importLog.Columns); + + List> rowValues = importLog.ExistingRows.Select(o => columns.ToDictionary(c => c, c => GetExportFieldValueLogValue(c, o, false))).ToList(); + rowValues.AddRange(importLog.NewRows.Select(o => columns.ToDictionary(c => c, c => GetExportFieldValueLogValue(c, o, true))).ToList()); + + string csvValues = await _csvGenerator.GenerateCsvFile(columns, rowValues); + + return File(Encoding.UTF8.GetBytes(csvValues), CsvContentType, Path.GetFileNameWithoutExtension(importLog.FileName) + "_Result.csv"); + } + + /// + /// Returns the column value for a log result + /// + /// Column + /// Value row + /// True if the row is a new row + /// Value + private string GetExportFieldValueLogValue(string column, FlexFieldImportValueRow valueRow, bool isNewRow) + { + if(column == CsvHeaderId) + { + if(isNewRow) + { + return !string.IsNullOrEmpty(valueRow.Id) ? string.Format("NEW: {0}", valueRow.Id) : ""; + } + else + { + return valueRow.Id; + } + } + else if(column == CsvHeaderResult) + { + return valueRow.Result.ToString(); + } + else if(column == CsvHeaderErrorMessage) + { + return valueRow.ErrorMessage; + } + + if(valueRow.ColumnValues == null) + { + return string.Empty; + } + + FlexFieldImportValuePreCheckCell value = valueRow.ColumnValues.FirstOrDefault(c => c.Name == column); + if(value == null) + { + return string.Empty; + } + + if(value.NewValue == value.OldValue) + { + return value.NewValue; + } + + if(isNewRow) + { + return value.NewValue; + } + + return string.Format("{0} -> {1}", !string.IsNullOrEmpty(value.OldValue) ? value.OldValue : "", !string.IsNullOrEmpty(value.NewValue) ? value.NewValue : ""); + } + + /// + /// Returns a list of import logs + /// + /// Start of the page + /// Size of the page + /// List of import logs without details + [ProducesResponseType(StatusCodes.Status200OK)] + [HttpGet] + public async Task> GetFlexFieldValueImportLogs(int start, int pageSize) + { + GoNorthProject project = await _projectDbAccess.GetDefaultProject(); + Task> queryTask; + Task countTask; + queryTask = _importFieldValuesLogDbAccess.GetImportLogsByProject(project.Id, start, pageSize); + countTask = _importFieldValuesLogDbAccess.GetImportLogsByProjectCount(project.Id); + Task.WaitAll(queryTask, countTask); + + ImportFieldValuesLogQueryResult queryResult = new ImportFieldValuesLogQueryResult(); + queryResult.Logs = queryTask.Result; + queryResult.HasMore = start + queryResult.Logs.Count < countTask.Result; + return Ok(queryResult); + } + + + /// + /// Returns a flex field value import log + /// + /// Id of the log + /// Import log + [ProducesResponseType(typeof(FlexFieldImportFieldValuesResultLog), StatusCodes.Status200OK)] + [HttpGet] + public async Task> GetFlexFieldValueImportLog(string id) + { + FlexFieldImportFieldValuesResultLog importLog = await _importFieldValuesLogDbAccess.GetImportLogById(id); + return Ok(importLog); + } } } \ No newline at end of file diff --git a/Controllers/Api/KirjaApiController.cs b/Controllers/Api/KirjaApiController.cs index 8f3b636b..9a09150e 100644 --- a/Controllers/Api/KirjaApiController.cs +++ b/Controllers/Api/KirjaApiController.cs @@ -267,11 +267,26 @@ public KirjaApiController(IKirjaPageDbAccess pageDbAccess, IKirjaPageVersionDbAc _xssChecker = xssChecker; _logger = logger; _localizer = localizerFactory.Create(typeof(KirjaApiController)); - _allowedAttachmentMimeTypes = configuration.Value.Misc.KirjaAllowedAttachmentMimeTypes.Split(",").Select(s => "^" + Regex.Escape(s).Replace("\\*", ".*") + "$").ToList(); + _allowedAttachmentMimeTypes = configuration.Value.Misc.KirjaAllowedAttachmentMimeTypes.Split(",").Select(s => ConvertMimeTypeToRegex(s)).ToList(); _versionMergeTimeSpan = configuration.Value.Misc.KirjaVersionMergeTimeSpan; _maxVersionCount = configuration.Value.Misc.KirjaMaxVersionCount; } + /// + /// Converts a mime type to a regex. Does not change file endings + /// + /// Mime type + /// Converted mime type + private string ConvertMimeTypeToRegex(string mimeType) + { + if (!mimeType.StartsWith(".")) + { + return "^" + Regex.Escape(mimeType).Replace("\\*", ".*") + "$"; + } + + return mimeType; + } + /// /// Returns a page by its id /// @@ -864,11 +879,12 @@ public async Task UploadPageAttachment(string id) } IFormFile uploadFile = Request.Form.Files[0]; + string fileEnding = Path.GetExtension(uploadFile.FileName).ToLowerInvariant(); string fileContentType = uploadFile.ContentType; bool mimeTypeAllowed = false; foreach(string curAllowedMimeType in _allowedAttachmentMimeTypes) { - if(Regex.IsMatch(fileContentType, curAllowedMimeType)) + if(Regex.IsMatch(fileContentType, curAllowedMimeType) || (curAllowedMimeType.StartsWith(".") && curAllowedMimeType.ToLowerInvariant() == fileEnding)) { mimeTypeAllowed = true; break; diff --git a/Controllers/Api/KortistoApiController.cs b/Controllers/Api/KortistoApiController.cs index 769de0e0..418e2a6d 100644 --- a/Controllers/Api/KortistoApiController.cs +++ b/Controllers/Api/KortistoApiController.cs @@ -25,6 +25,7 @@ using GoNorth.Data.ProjectConfig; using Microsoft.AspNetCore.Http; using System.Globalization; +using GoNorth.Services.CsvHandling; namespace GoNorth.Controllers.Api { @@ -119,6 +120,12 @@ public class KortistoApiController : FlexFieldBaseApiController protected override TimelineEvent ObjectMovedToRootEvent { get { return TimelineEvent.KortistoNpcMovedToRoot; } } + /// + /// Event used for the value file import event + /// + protected override TimelineEvent ValueFileImportEvent { get { return TimelineEvent.KortistoValueFileImport; } } + + /// /// Aika Quest DB Access /// @@ -153,6 +160,7 @@ public class KortistoApiController : FlexFieldBaseApiController /// User Db Access /// Tag Db Access /// Export Template Db Access + /// Import field values log Db Access /// Language Key Db Access /// Export Function Id Db Access /// Object export snippet Db Access @@ -164,18 +172,20 @@ public class KortistoApiController : FlexFieldBaseApiController /// Kirja Page Db Access /// Karta Map Db Access /// Project config provider + /// CSV Generator + /// CSV Reader /// User Manager /// Implementation Status Comparer /// Timeline Service /// Xss Checker /// Logger /// Localizer Factory - public KortistoApiController(IKortistoFolderDbAccess folderDbAccess, IKortistoNpcTemplateDbAccess templateDbAccess, IKortistoNpcDbAccess npcDbAccess, IProjectDbAccess projectDbAccess, IKortistoNpcTagDbAccess tagDbAccess, IExportTemplateDbAccess exportTemplateDbAccess, + public KortistoApiController(IKortistoFolderDbAccess folderDbAccess, IKortistoNpcTemplateDbAccess templateDbAccess, IKortistoNpcDbAccess npcDbAccess, IProjectDbAccess projectDbAccess, IKortistoNpcTagDbAccess tagDbAccess, IExportTemplateDbAccess exportTemplateDbAccess, IKortistoImportFieldValuesLogDbAccess importFieldValuesLogDbAccess, ILanguageKeyDbAccess languageKeyDbAccess, IExportFunctionIdDbAccess exportFunctionIdDbAccess, IObjectExportSnippetDbAccess objectExportSnippetDbAccess, IObjectExportSnippetSnapshotDbAccess objectExportSnippetSnapshotDbAccess, IKortistoNpcImageAccess imageAccess, - IKortistoThumbnailService thumbnailService, IAikaQuestDbAccess aikaQuestDbAccess, ITaleDbAccess taleDbAccess, IKirjaPageDbAccess kirjaPageDbAccess, IKartaMapDbAccess kartaMapDbAccess, IProjectConfigProvider projectConfigProvider, UserManager userManager, - IImplementationStatusComparer implementationStatusComparer, ITimelineService timelineService, IXssChecker xssChecker, ILogger logger, IStringLocalizerFactory localizerFactory) - : base(folderDbAccess, templateDbAccess, npcDbAccess, projectDbAccess, tagDbAccess, exportTemplateDbAccess, languageKeyDbAccess, exportFunctionIdDbAccess, objectExportSnippetDbAccess, objectExportSnippetSnapshotDbAccess, imageAccess, thumbnailService, userManager, - implementationStatusComparer, timelineService, xssChecker, logger, localizerFactory) + IKortistoThumbnailService thumbnailService, IAikaQuestDbAccess aikaQuestDbAccess, ITaleDbAccess taleDbAccess, IKirjaPageDbAccess kirjaPageDbAccess, IKartaMapDbAccess kartaMapDbAccess, IProjectConfigProvider projectConfigProvider, ICsvGenerator csvGenerator, + ICsvParser csvReader, UserManager userManager, IImplementationStatusComparer implementationStatusComparer, ITimelineService timelineService, IXssChecker xssChecker, ILogger logger, IStringLocalizerFactory localizerFactory) + : base(folderDbAccess, templateDbAccess, npcDbAccess, projectDbAccess, tagDbAccess, exportTemplateDbAccess, importFieldValuesLogDbAccess, languageKeyDbAccess, exportFunctionIdDbAccess, objectExportSnippetDbAccess, objectExportSnippetSnapshotDbAccess, imageAccess, thumbnailService, csvGenerator, + csvReader, userManager, implementationStatusComparer, timelineService, xssChecker, logger, localizerFactory) { _aikaQuestDbAccess = aikaQuestDbAccess; _taleDbAccess = taleDbAccess; diff --git a/Controllers/Api/PersonalDataApiController.cs b/Controllers/Api/PersonalDataApiController.cs index 1e9d2c17..e8d736ad 100644 --- a/Controllers/Api/PersonalDataApiController.cs +++ b/Controllers/Api/PersonalDataApiController.cs @@ -9,6 +9,7 @@ using GoNorth.Data.Aika; using GoNorth.Data.Evne; using GoNorth.Data.Exporting; +using GoNorth.Data.FlexFieldDatabase; using GoNorth.Data.Karta; using GoNorth.Data.Kirja; using GoNorth.Data.Kortisto; @@ -244,6 +245,11 @@ public class PersonalDataResponse /// private readonly IEvneSkillImplementationSnapshotDbAccess _skillImplementationSnapshotDbAccess; + /// + /// Skill import field values log Db access + /// + private readonly IEvneImportFieldValuesLogDbAccess _skillImportFieldValuesLogDbAccess; + /// /// Npc Db Access /// @@ -259,6 +265,11 @@ public class PersonalDataResponse /// private readonly IKortistoNpcImplementationSnapshotDbAccess _npcImplementationSnapshotDbAccess; + /// + /// Npc import field values log Db access + /// + private readonly IKortistoImportFieldValuesLogDbAccess _npcImportFieldValuesLogDbAccess; + /// /// Item Db Access /// @@ -274,6 +285,11 @@ public class PersonalDataResponse /// private readonly IStyrItemImplementationSnapshotDbAccess _itemImplementationSnapshotDbAccess; + /// + /// Item import field values log Db access + /// + private readonly IStyrImportFieldValuesLogDbAccess _itemImportFieldValuesLogDbAccess; + /// /// Export Template Db Access /// @@ -379,12 +395,15 @@ public class PersonalDataResponse /// Skill Db Access /// Skill Template Db Access /// Skill Implementation Snapshot Db Access + /// Skill import field values log Db access /// Npc Db Access /// Npc Template Db Access /// Npc Implementation Snapshot Db Access + /// Npc import field values log Db access /// Item Db Access /// Item Template Db Access /// Item Implementation Snapshot Db Access + /// Item import field values log Db access /// Export template Db access /// Include export template Db Access /// Object Export snippet Db Access @@ -405,8 +424,9 @@ public class PersonalDataResponse /// Signin Manager /// User Deleter public PersonalDataApiController(IAikaQuestDbAccess questDbAccess, IAikaQuestImplementationSnapshotDbAccess questImplementationSnapshotDbAccess, IAikaChapterDetailDbAccess chapterDetailDbAccess, IAikaChapterOverviewDbAccess chapterOverviewDbAccess, IEvneSkillDbAccess skillDbAccess, - IEvneSkillTemplateDbAccess skillTemplateDbAccess, IEvneSkillImplementationSnapshotDbAccess skillImplementationSnapshotDbAccess, IKortistoNpcDbAccess npcDbAccess, IKortistoNpcTemplateDbAccess npcTemplateDbAccess, IKortistoNpcImplementationSnapshotDbAccess npcImplementationSnapshotDbAccess, - IStyrItemDbAccess itemDbAccess, IStyrItemTemplateDbAccess itemTemplateDbAccess, IStyrItemImplementationSnapshotDbAccess itemImplementationSnapshotDbAccess, IExportTemplateDbAccess exportTemplateDbAccess, IIncludeExportTemplateDbAccess includeExportTemplateDbAccess, + IEvneSkillTemplateDbAccess skillTemplateDbAccess, IEvneSkillImplementationSnapshotDbAccess skillImplementationSnapshotDbAccess, IEvneImportFieldValuesLogDbAccess skillImportFieldValuesLogDbAccess, IKortistoNpcDbAccess npcDbAccess, IKortistoNpcTemplateDbAccess npcTemplateDbAccess, + IKortistoNpcImplementationSnapshotDbAccess npcImplementationSnapshotDbAccess, IKortistoImportFieldValuesLogDbAccess npcImportFieldValuesLogDbAccess, IStyrItemDbAccess itemDbAccess, IStyrItemTemplateDbAccess itemTemplateDbAccess, + IStyrItemImplementationSnapshotDbAccess itemImplementationSnapshotDbAccess, IStyrImportFieldValuesLogDbAccess itemImportFieldValuesLogDbAccess, IExportTemplateDbAccess exportTemplateDbAccess, IIncludeExportTemplateDbAccess includeExportTemplateDbAccess, IObjectExportSnippetDbAccess objectExportSnippetDbAccess, IKartaMapDbAccess mapDbAccess, IKirjaPageDbAccess pageDbAccess, IKirjaPageVersionDbAccess pageVersionDbAccess, ITaleDbAccess taleDbAccess, ITaleDialogImplementationSnapshotDbAccess taleImplementationSnapshotDbAccess, IProjectConfigDbAccess projectConfigDbAccess, ITaskBoardDbAccess taskBoardDbAccess, ITaskGroupTypeDbAccess taskGroupTypeDbAccess, ITaskTypeDbAccess taskTypeDbAccess, IUserTaskBoardHistoryDbAccess userTaskBoardHistoryDbAccess, IProjectDbAccess projectDbAccess, ITimelineDbAccess timelineDbAccess, ILockServiceDbAccess lockDbAccess, UserManager userManager, SignInManager signInManager, IUserDeleter userDeleter) @@ -418,12 +438,15 @@ public PersonalDataApiController(IAikaQuestDbAccess questDbAccess, IAikaQuestImp _skillDbAccess = skillDbAccess; _skillTemplateDbAccess = skillTemplateDbAccess; _skillImplementationSnapshotDbAccess = skillImplementationSnapshotDbAccess; + _skillImportFieldValuesLogDbAccess = skillImportFieldValuesLogDbAccess; _npcDbAccess = npcDbAccess; _npcTemplateDbAccess = npcTemplateDbAccess; _npcImplementationSnapshotDbAccess = npcImplementationSnapshotDbAccess; + _npcImportFieldValuesLogDbAccess = npcImportFieldValuesLogDbAccess; _itemDbAccess = itemDbAccess; _itemTemplateDbAccess = itemTemplateDbAccess; _itemImplementationSnapshotDbAccess = itemImplementationSnapshotDbAccess; + _itemImportFieldValuesLogDbAccess = itemImportFieldValuesLogDbAccess; _exportTemplateDbAccess = exportTemplateDbAccess; _includeExportTemplateDbAccess = includeExportTemplateDbAccess; _objectExportSnippetDbAccess = objectExportSnippetDbAccess; @@ -579,6 +602,13 @@ private async Task FillModifiedData(PersonalDataResponse response, GoNorthUser c ModifiedDate = p.ModifiedOn })); + List skillsFieldValuesLogs = await _skillImportFieldValuesLogDbAccess.GetImportLogsByModifiedUser(currentUser.Id); + response.ModifiedData.AddRange(skillsFieldValuesLogs.Select(p => new TrimmedModifiedData { + ObjectType = "SkillFieldValueImportLog", + Name = p.FileName, + ModifiedDate = p.ModifiedOn + })); + List skillTemplates = await _skillTemplateDbAccess.GetFlexFieldObjectsByModifiedUser(currentUser.Id); response.ModifiedData.AddRange(skillTemplates.Select(p => new TrimmedModifiedData { ObjectType = "SkillTemplate", @@ -613,6 +643,13 @@ private async Task FillModifiedData(PersonalDataResponse response, GoNorthUser c Name = p.Name, ModifiedDate = p.ModifiedOn })); + + List npcsFieldValuesLogs = await _npcImportFieldValuesLogDbAccess.GetImportLogsByModifiedUser(currentUser.Id); + response.ModifiedData.AddRange(npcsFieldValuesLogs.Select(p => new TrimmedModifiedData { + ObjectType = "NpcFieldValueImportLog", + Name = p.FileName, + ModifiedDate = p.ModifiedOn + })); List npcTemplates = await _npcTemplateDbAccess.GetFlexFieldObjectsByModifiedUser(currentUser.Id); response.ModifiedData.AddRange(npcTemplates.Select(p => new TrimmedModifiedData { @@ -648,6 +685,13 @@ private async Task FillModifiedData(PersonalDataResponse response, GoNorthUser c Name = p.Name, ModifiedDate = p.ModifiedOn })); + + List itemsFieldValuesLogs = await _itemImportFieldValuesLogDbAccess.GetImportLogsByModifiedUser(currentUser.Id); + response.ModifiedData.AddRange(itemsFieldValuesLogs.Select(p => new TrimmedModifiedData { + ObjectType = "ItemFieldValueImportLog", + Name = p.FileName, + ModifiedDate = p.ModifiedOn + })); List itemTemplates = await _itemTemplateDbAccess.GetFlexFieldObjectsByModifiedUser(currentUser.Id); response.ModifiedData.AddRange(itemTemplates.Select(p => new TrimmedModifiedData { diff --git a/Controllers/Api/StyrApiController.cs b/Controllers/Api/StyrApiController.cs index 6e042931..c9de2ac3 100644 --- a/Controllers/Api/StyrApiController.cs +++ b/Controllers/Api/StyrApiController.cs @@ -22,6 +22,7 @@ using GoNorth.Services.Security; using Microsoft.AspNetCore.Http; using System.Globalization; +using GoNorth.Services.CsvHandling; namespace GoNorth.Controllers.Api { @@ -116,6 +117,12 @@ public class StyrApiController : FlexFieldBaseApiController protected override TimelineEvent ObjectMovedToRootEvent { get { return TimelineEvent.StyrItemMovedToRoot; } } + /// + /// Event used for the value file import event + /// + protected override TimelineEvent ValueFileImportEvent { get { return TimelineEvent.StyrValueFileImport; } } + + /// /// Aika Quest DB Access /// @@ -150,6 +157,7 @@ public class StyrApiController : FlexFieldBaseApiController /// User Db Access /// Tag Db Access /// Export Template Db Access + /// Import field values log Db Access /// Language Key Db Access /// Export Function Id Db Access /// Object export snippet Db Access @@ -161,18 +169,20 @@ public class StyrApiController : FlexFieldBaseApiController /// Kirja Page Db Access /// Karta Map Db Access /// Kortisto Npc Db Access + /// CSV Generator + /// CSV Reader /// User Manager /// Implementation Status Comparer /// Timeline Service /// Xss Checker /// Logger /// Localizer Factory - public StyrApiController(IStyrFolderDbAccess folderDbAccess, IStyrItemTemplateDbAccess templateDbAccess, IStyrItemDbAccess itemDbAccess, IProjectDbAccess projectDbAccess, IStyrItemTagDbAccess tagDbAccess, IExportTemplateDbAccess exportTemplateDbAccess, + public StyrApiController(IStyrFolderDbAccess folderDbAccess, IStyrItemTemplateDbAccess templateDbAccess, IStyrItemDbAccess itemDbAccess, IProjectDbAccess projectDbAccess, IStyrItemTagDbAccess tagDbAccess, IExportTemplateDbAccess exportTemplateDbAccess, IStyrImportFieldValuesLogDbAccess importFieldValuesLogDbAccess, ILanguageKeyDbAccess languageKeyDbAccess, IExportFunctionIdDbAccess exportFunctionIdDbAccess, IObjectExportSnippetDbAccess objectExportSnippetDbAccess, IObjectExportSnippetSnapshotDbAccess objectExportSnippetSnapshotDbAccess, IStyrItemImageAccess imageAccess, - IStyrThumbnailService thumbnailService, IAikaQuestDbAccess aikaQuestDbAccess, ITaleDbAccess taleDbAccess, IKirjaPageDbAccess kirjaPageDbAccess, IKartaMapDbAccess kartaMapDbAccess, IKortistoNpcDbAccess kortistoNpcDbAccess, UserManager userManager, - IImplementationStatusComparer implementationStatusComparer, ITimelineService timelineService, IXssChecker xssChecker, ILogger logger, IStringLocalizerFactory localizerFactory) - : base(folderDbAccess, templateDbAccess, itemDbAccess, projectDbAccess, tagDbAccess, exportTemplateDbAccess, languageKeyDbAccess, exportFunctionIdDbAccess, objectExportSnippetDbAccess, objectExportSnippetSnapshotDbAccess, imageAccess, thumbnailService, userManager, - implementationStatusComparer, timelineService, xssChecker, logger, localizerFactory) + IStyrThumbnailService thumbnailService, IAikaQuestDbAccess aikaQuestDbAccess, ITaleDbAccess taleDbAccess, IKirjaPageDbAccess kirjaPageDbAccess, IKartaMapDbAccess kartaMapDbAccess, IKortistoNpcDbAccess kortistoNpcDbAccess, ICsvGenerator csvGenerator, + ICsvParser csvReader, UserManager userManager, IImplementationStatusComparer implementationStatusComparer, ITimelineService timelineService, IXssChecker xssChecker, ILogger logger, IStringLocalizerFactory localizerFactory) + : base(folderDbAccess, templateDbAccess, itemDbAccess, projectDbAccess, tagDbAccess, exportTemplateDbAccess, importFieldValuesLogDbAccess, languageKeyDbAccess, exportFunctionIdDbAccess, objectExportSnippetDbAccess, objectExportSnippetSnapshotDbAccess, imageAccess, thumbnailService, csvGenerator, + csvReader, userManager, implementationStatusComparer, timelineService, xssChecker, logger, localizerFactory) { _aikaQuestDbAccess = aikaQuestDbAccess; _taleDbAccess = taleDbAccess; diff --git a/Data/Evne/EvneImportFieldValuesLogMongoDbAccess.cs b/Data/Evne/EvneImportFieldValuesLogMongoDbAccess.cs new file mode 100644 index 00000000..70075e8b --- /dev/null +++ b/Data/Evne/EvneImportFieldValuesLogMongoDbAccess.cs @@ -0,0 +1,25 @@ +using GoNorth.Config; +using GoNorth.Data.FlexFieldDatabase; +using Microsoft.Extensions.Options; + +namespace GoNorth.Data.Evne +{ + /// + /// Evne import field values Mongo Db Access + /// + public class EvneImportFieldValuesLogMongoDbAccess : FlexFieldImportFieldValuesLogMongoDbAccess, IEvneImportFieldValuesLogDbAccess + { + /// + /// Collection Name of the evne import field values log + /// + public const string EvneImportFieldValuesLogCollectionName = "EvneImportFieldValuesLog"; + + /// + /// Constructor + /// + /// Configuration + public EvneImportFieldValuesLogMongoDbAccess(IOptions configuration) : base(EvneImportFieldValuesLogCollectionName, configuration) + { + } + } +} diff --git a/Data/Evne/IEvneImportFieldValuesLogDbAccess.cs b/Data/Evne/IEvneImportFieldValuesLogDbAccess.cs new file mode 100644 index 00000000..dd03ce0c --- /dev/null +++ b/Data/Evne/IEvneImportFieldValuesLogDbAccess.cs @@ -0,0 +1,11 @@ +using GoNorth.Data.FlexFieldDatabase; + +namespace GoNorth.Data.Evne +{ + /// + /// Interface for Database Access for Evne field values log db access + /// + public interface IEvneImportFieldValuesLogDbAccess : IFlexFieldImportFieldValuesLogDbAccess + { + } +} \ No newline at end of file diff --git a/Data/Evne/IEvneSkillDbAccess.cs b/Data/Evne/IEvneSkillDbAccess.cs index db8f456a..360b75a3 100644 --- a/Data/Evne/IEvneSkillDbAccess.cs +++ b/Data/Evne/IEvneSkillDbAccess.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.Threading.Tasks; using GoNorth.Data.FlexFieldDatabase; namespace GoNorth.Data.Evne diff --git a/Data/FlexFieldDatabase/FlexFieldFolderBaseMongoDbAccess.cs b/Data/FlexFieldDatabase/FlexFieldFolderBaseMongoDbAccess.cs index fc8b6ddd..ffbfd903 100644 --- a/Data/FlexFieldDatabase/FlexFieldFolderBaseMongoDbAccess.cs +++ b/Data/FlexFieldDatabase/FlexFieldFolderBaseMongoDbAccess.cs @@ -129,6 +129,19 @@ public async Task GetChildFolderCount(string folderId, string locale) return count; } + /// + /// Returns all folders to build a hierarchy of folders + /// + /// Project Id + /// Folders with simple information + public async Task> GetFoldersForHierarchy(string projectId) + { + return await _FolderCollection.Find(f => f.ProjectId == projectId).Project(f => new FlexFieldFolder { + Id = f.Id, + ParentFolderId = f.ParentFolderId + }).ToListAsync(); + } + /// /// Updates a folder /// diff --git a/Data/FlexFieldDatabase/FlexFieldImportFieldValuesLogMongoDbAccess.cs b/Data/FlexFieldDatabase/FlexFieldImportFieldValuesLogMongoDbAccess.cs new file mode 100644 index 00000000..c59c0f0d --- /dev/null +++ b/Data/FlexFieldDatabase/FlexFieldImportFieldValuesLogMongoDbAccess.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using GoNorth.Config; +using Microsoft.Extensions.Options; +using MongoDB.Driver; +using MongoDB.Driver.Linq; + +namespace GoNorth.Data.FlexFieldDatabase +{ + /// + /// Flex Field field value log Mongo DB Access + /// + public class FlexFieldImportFieldValuesLogMongoDbAccess : BaseMongoDbAccess, IFlexFieldImportFieldValuesLogDbAccess + { + /// + /// Object Collection + /// + protected IMongoCollection _LogCollection; + + /// + /// Constructor + /// + /// Name of the object collection + /// Configuration + public FlexFieldImportFieldValuesLogMongoDbAccess(string collectionName, IOptions configuration) : base(configuration) + { + _LogCollection = _Database.GetCollection(collectionName); + } + + /// + /// Creates an export log + /// + /// Import log to save + /// Created log, with filled id + public async Task CreateImportLog(FlexFieldImportFieldValuesResultLog importLog) + { + importLog.Id = Guid.NewGuid().ToString(); + await _LogCollection.InsertOneAsync(importLog); + + return importLog; + } + + /// + /// Returns an import log by id + /// + /// Log id + /// Import log + public async Task GetImportLogById(string id) + { + FlexFieldImportFieldValuesResultLog importLog = await _LogCollection.Find(n => n.Id == id).FirstOrDefaultAsync(); + return importLog; + } + + /// + /// Returns the import logs for a project without details + /// + /// Project Id + /// Start of the query + /// Page Size + /// Import logs + public async Task> GetImportLogsByProject(string projectId, int start, int pageSize) + { + List logs = await _LogCollection.Find(n => n.ProjectId == projectId).SortByDescending(n => n.ModifiedOn).Skip(start).Limit(pageSize).Project(l => new FlexFieldImportFieldValuesResultLog{ + Id = l.Id, + ProjectId = l.ProjectId, + ModifiedOn = l.ModifiedOn, + FileName = l.FileName + }).ToListAsync(); + return logs; + } + + /// + /// Returns the count of import logs for a project + /// + /// Project Id + /// Import logs Count + public async Task GetImportLogsByProjectCount(string projectId) + { + return (int)(await _LogCollection.Find(n => n.ProjectId == projectId).CountDocumentsAsync()); + } + + + /// + /// Returns all log that were last modified by a given user + /// + /// Id of the user + /// Objects + public async Task> GetImportLogsByModifiedUser(string userId) + { + return await _LogCollection.AsQueryable().Where(n => n.ModifiedBy == userId).ToListAsync(); + } + + /// + /// Resets all logs that were modified by a user + /// + /// Id of the user + /// Task + public async Task ResetImportLogsByModifiedUser(string userId) + { + await _LogCollection.UpdateManyAsync(n => n.ModifiedBy == userId, Builders.Update.Set(n => n.ModifiedBy, Guid.Empty.ToString()).Set(n => n.ModifiedOn, DateTimeOffset.UtcNow)); + } + } +} \ No newline at end of file diff --git a/Data/FlexFieldDatabase/FlexFieldImportFieldValuesResultLog.cs b/Data/FlexFieldDatabase/FlexFieldImportFieldValuesResultLog.cs new file mode 100644 index 00000000..9a520453 --- /dev/null +++ b/Data/FlexFieldDatabase/FlexFieldImportFieldValuesResultLog.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; + +namespace GoNorth.Data.FlexFieldDatabase +{ + /// + /// Flex Field import field values result log + /// + public class FlexFieldImportFieldValuesResultLog : IHasModifiedData + { + /// + /// Id + /// + public string Id { get; set; } + + /// + /// Project Id + /// + public string ProjectId { get; set; } + + /// + /// Filename of the import file + /// + public string FileName { get; set; } + + /// + /// Columns + /// + public List Columns { get; set; } + + /// + /// Rows that exist + /// + public List ExistingRows { get; set; } + + /// + /// Rows that are new + /// + public List NewRows { get; set; } + + /// + /// Last modify Date + /// + public DateTimeOffset ModifiedOn { get; set; } + + /// + /// Id of the user who last modified the object + /// + public string ModifiedBy { get; set; } + + /// + /// Constructor + /// + public FlexFieldImportFieldValuesResultLog() + { + ExistingRows = new List(); + NewRows = new List(); + } + } +} \ No newline at end of file diff --git a/Data/FlexFieldDatabase/FlexFieldImportValueImportRequest.cs b/Data/FlexFieldDatabase/FlexFieldImportValueImportRequest.cs new file mode 100644 index 00000000..1df3fc32 --- /dev/null +++ b/Data/FlexFieldDatabase/FlexFieldImportValueImportRequest.cs @@ -0,0 +1,13 @@ +namespace GoNorth.Data.FlexFieldDatabase +{ + /// + /// Flex Field Import value import request + /// + public class FlexFieldImportValueImportRequest : FlexFieldImportValuePreCheckResult + { + /// + /// Target Folder Id + /// + public string TargetFolderId { get; set; } + } +} \ No newline at end of file diff --git a/Data/FlexFieldDatabase/FlexFieldImportValuePreCheckCell.cs b/Data/FlexFieldDatabase/FlexFieldImportValuePreCheckCell.cs new file mode 100644 index 00000000..17ad582e --- /dev/null +++ b/Data/FlexFieldDatabase/FlexFieldImportValuePreCheckCell.cs @@ -0,0 +1,23 @@ +namespace GoNorth.Data.FlexFieldDatabase +{ + /// + /// Flex Field pre check cell + /// + public class FlexFieldImportValuePreCheckCell + { + /// + /// Name of the column + /// + public string Name { get; set; } + + /// + /// Old value + /// + public string OldValue { get; set; } + + /// + /// New value + /// + public string NewValue { get; set; } + } +} \ No newline at end of file diff --git a/Data/FlexFieldDatabase/FlexFieldImportValuePreCheckResult.cs b/Data/FlexFieldDatabase/FlexFieldImportValuePreCheckResult.cs new file mode 100644 index 00000000..826d8f2d --- /dev/null +++ b/Data/FlexFieldDatabase/FlexFieldImportValuePreCheckResult.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; + +namespace GoNorth.Data.FlexFieldDatabase +{ + /// + /// Flex Field Import value precheck result + /// + public class FlexFieldImportValuePreCheckResult + { + /// + /// Import Filename + /// + public string Filename { get; set; } + + /// + /// Template Id + /// + public string TemplateId { get; set; } + + /// + /// Columns + /// + public List Columns { get; set; } + + /// + /// Rows that exist + /// + public List ExistingRows { get; set; } + + /// + /// Rows that are new + /// + public List NewRows { get; set; } + + /// + /// Constructor + /// + public FlexFieldImportValuePreCheckResult() + { + Columns = new List(); + ExistingRows = new List(); + NewRows = new List(); + } + } +} \ No newline at end of file diff --git a/Data/FlexFieldDatabase/FlexFieldImportValuePreCheckRow.cs b/Data/FlexFieldDatabase/FlexFieldImportValuePreCheckRow.cs new file mode 100644 index 00000000..9f5f26e6 --- /dev/null +++ b/Data/FlexFieldDatabase/FlexFieldImportValuePreCheckRow.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace GoNorth.Data.FlexFieldDatabase +{ + /// + /// Flex Field pre check row + /// + public class FlexFieldImportValuePreCheckRow + { + /// + /// Id of the row + /// + public string Id { get; set; } + + /// + /// Column values + /// + public List ColumnValues { get; set; } + + /// + /// Constructor + /// + public FlexFieldImportValuePreCheckRow() + { + ColumnValues = new List(); + } + } +} \ No newline at end of file diff --git a/Data/FlexFieldDatabase/FlexFieldImportValueRow.cs b/Data/FlexFieldDatabase/FlexFieldImportValueRow.cs new file mode 100644 index 00000000..3cd50023 --- /dev/null +++ b/Data/FlexFieldDatabase/FlexFieldImportValueRow.cs @@ -0,0 +1,18 @@ +namespace GoNorth.Data.FlexFieldDatabase +{ + /// + /// Flex Field pre check row + /// + public class FlexFieldImportValueRow : FlexFieldImportValuePreCheckRow + { + /// + /// Result of the import + /// + public FlexFieldImportValueRowResult Result { get; set; } + + /// + /// Error message + /// + public string ErrorMessage { get; set; } + } +} \ No newline at end of file diff --git a/Data/FlexFieldDatabase/FlexFieldImportValueRowResult.cs b/Data/FlexFieldDatabase/FlexFieldImportValueRowResult.cs new file mode 100644 index 00000000..ad37ac87 --- /dev/null +++ b/Data/FlexFieldDatabase/FlexFieldImportValueRowResult.cs @@ -0,0 +1,23 @@ +namespace GoNorth.Data.FlexFieldDatabase +{ + /// + /// Result of a row import + /// + public enum FlexFieldImportValueRowResult + { + /// + /// Successful import + /// + Success = 0, + + /// + /// Failed import + /// + Failed = 1, + + /// + /// No change + /// + NoChange = 2 + } +} \ No newline at end of file diff --git a/Data/FlexFieldDatabase/FlexFieldObjectBaseMongoDbAccess.cs b/Data/FlexFieldDatabase/FlexFieldObjectBaseMongoDbAccess.cs index a0123004..a3c5faa0 100644 --- a/Data/FlexFieldDatabase/FlexFieldObjectBaseMongoDbAccess.cs +++ b/Data/FlexFieldDatabase/FlexFieldObjectBaseMongoDbAccess.cs @@ -61,6 +61,17 @@ public async Task GetFlexFieldObjectById(string id) return flexFieldObject; } + /// + /// Returns a list Flex Field Objects by id with full data + /// + /// Id + /// Flex Field Objects + public async Task> GetFlexFieldObjectsByIds(List id) + { + List flexFieldObjects = await _ObjectCollection.Find(n => id.Contains(n.Id)).ToListAsync(); + return flexFieldObjects; + } + /// /// Builds a flex field object queryable for root folder objects /// diff --git a/Data/FlexFieldDatabase/IFlexFieldFolderDbAccess.cs b/Data/FlexFieldDatabase/IFlexFieldFolderDbAccess.cs index d7662205..cf4a4d5e 100644 --- a/Data/FlexFieldDatabase/IFlexFieldFolderDbAccess.cs +++ b/Data/FlexFieldDatabase/IFlexFieldFolderDbAccess.cs @@ -58,6 +58,13 @@ public interface IFlexFieldFolderDbAccess /// Count of child folders Task GetChildFolderCount(string folderId, string locale); + /// + /// Returns all folders to build a hierarchy of folders + /// + /// Project Id + /// Folders with simple information + Task> GetFoldersForHierarchy(string projectId); + /// /// Updates a folder /// diff --git a/Data/FlexFieldDatabase/IFlexFieldImportFieldValuesLogDbAccess.cs b/Data/FlexFieldDatabase/IFlexFieldImportFieldValuesLogDbAccess.cs new file mode 100644 index 00000000..01f35c4d --- /dev/null +++ b/Data/FlexFieldDatabase/IFlexFieldImportFieldValuesLogDbAccess.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace GoNorth.Data.FlexFieldDatabase +{ + /// + /// Interface for Database Access for an flex field import log + /// + public interface IFlexFieldImportFieldValuesLogDbAccess + { + /// + /// Creates an export log + /// + /// Import log to save + /// Created log, with filled id + Task CreateImportLog(FlexFieldImportFieldValuesResultLog importLog); + + /// + /// Returns an import log by id + /// + /// Log id + /// Import log + Task GetImportLogById(string id); + + /// + /// Returns the import logs for a project without details + /// + /// Project Id + /// Start of the query + /// Page Size + /// Import logs + Task> GetImportLogsByProject(string projectId, int start, int pageSize); + + /// + /// Returns the count of import logs for a project + /// + /// Project Id + /// Import logs Count + Task GetImportLogsByProjectCount(string projectId); + + /// + /// Returns all log that were last modified by a given user + /// + /// Id of the user + /// Objects + Task> GetImportLogsByModifiedUser(string userId); + + /// + /// Resets all logs that were modified by a user + /// + /// Id of the user + /// Task + Task ResetImportLogsByModifiedUser(string userId); + } +} \ No newline at end of file diff --git a/Data/FlexFieldDatabase/IFlexFieldObjectDbAccess.cs b/Data/FlexFieldDatabase/IFlexFieldObjectDbAccess.cs index cc1e166f..f9c9447b 100644 --- a/Data/FlexFieldDatabase/IFlexFieldObjectDbAccess.cs +++ b/Data/FlexFieldDatabase/IFlexFieldObjectDbAccess.cs @@ -22,6 +22,13 @@ public interface IFlexFieldObjectDbAccess where T: FlexFieldObject /// Flex Field Object Task GetFlexFieldObjectById(string id); + /// + /// Returns a list Flex Field Objects by id with full data + /// + /// Id + /// Flex Field Objects + Task> GetFlexFieldObjectsByIds(List id); + /// /// Returns the Flex Field Objects in the root folder /// diff --git a/Data/Kortisto/IKortistoFolderDbAccess.cs b/Data/Kortisto/IKortistoFolderDbAccess.cs index 5ca58fd8..4e1f8932 100644 --- a/Data/Kortisto/IKortistoFolderDbAccess.cs +++ b/Data/Kortisto/IKortistoFolderDbAccess.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.Threading.Tasks; using GoNorth.Data.FlexFieldDatabase; namespace GoNorth.Data.Kortisto diff --git a/Data/Kortisto/IKortistoImportFieldValuesLogDbAccess.cs b/Data/Kortisto/IKortistoImportFieldValuesLogDbAccess.cs new file mode 100644 index 00000000..058fa6ee --- /dev/null +++ b/Data/Kortisto/IKortistoImportFieldValuesLogDbAccess.cs @@ -0,0 +1,11 @@ +using GoNorth.Data.FlexFieldDatabase; + +namespace GoNorth.Data.Kortisto +{ + /// + /// Interface for Database Access for Kortisto field values log db access + /// + public interface IKortistoImportFieldValuesLogDbAccess : IFlexFieldImportFieldValuesLogDbAccess + { + } +} \ No newline at end of file diff --git a/Data/Kortisto/KortistoImportFieldValuesLogMongoDbAccess.cs b/Data/Kortisto/KortistoImportFieldValuesLogMongoDbAccess.cs new file mode 100644 index 00000000..11ab8a61 --- /dev/null +++ b/Data/Kortisto/KortistoImportFieldValuesLogMongoDbAccess.cs @@ -0,0 +1,25 @@ +using GoNorth.Config; +using GoNorth.Data.FlexFieldDatabase; +using Microsoft.Extensions.Options; + +namespace GoNorth.Data.Kortisto +{ + /// + /// Kortisto import field values Mongo Db Access + /// + public class KortistoImportFieldValuesLogMongoDbAccess : FlexFieldImportFieldValuesLogMongoDbAccess, IKortistoImportFieldValuesLogDbAccess + { + /// + /// Collection Name of the kortisto import field values log + /// + public const string KortistoImportFieldValuesLogCollectionName = "KortistoImportFieldValuesLog"; + + /// + /// Constructor + /// + /// Configuration + public KortistoImportFieldValuesLogMongoDbAccess(IOptions configuration) : base(KortistoImportFieldValuesLogCollectionName, configuration) + { + } + } +} diff --git a/Data/MongoDbSetup.cs b/Data/MongoDbSetup.cs index 1489fd21..74d5bc62 100644 --- a/Data/MongoDbSetup.cs +++ b/Data/MongoDbSetup.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Threading.Tasks; using GoNorth.Config; @@ -82,6 +81,7 @@ public async Task SetupDatabaseAsync() await CreateCollectionIfNotExists(KortistoNpcMongoDbAccess.KortistoNpcRecyclingBinCollectionName, collectionNames); await CreateCollectionIfNotExists(KortistoNpcTagMongoDbAccess.KortistoNpcTagCollectionName, collectionNames); await CreateCollectionIfNotExists(KortistoNpcImplementationSnapshotMongoDbAccess.KortistoNpcImplementationSnapshotCollectionName, collectionNames); + await CreateCollectionIfNotExists(KortistoImportFieldValuesLogMongoDbAccess.KortistoImportFieldValuesLogCollectionName, collectionNames); await CreateCollectionIfNotExists(StyrFolderMongoDbAccess.StyrFolderCollectionName, collectionNames); await CreateCollectionIfNotExists(StyrItemTemplateMongoDbAccess.StyrItemTemplateCollectionName, collectionNames); @@ -90,6 +90,7 @@ public async Task SetupDatabaseAsync() await CreateCollectionIfNotExists(StyrItemMongoDbAccess.StyrItemRecyclingBinCollectionName, collectionNames); await CreateCollectionIfNotExists(StyrItemTagMongoDbAccess.StyrItemTagCollectionName, collectionNames); await CreateCollectionIfNotExists(StyrItemImplementationSnapshotMongoDbAccess.StyrItemImplementationSnapshotCollectionName, collectionNames); + await CreateCollectionIfNotExists(StyrImportFieldValuesLogMongoDbAccess.StyrImportFieldValuesLogCollectionName, collectionNames); await CreateCollectionIfNotExists(EvneFolderMongoDbAccess.EvneFolderCollectionName, collectionNames); await CreateCollectionIfNotExists(EvneSkillTemplateMongoDbAccess.EvneSkillTemplateCollectionName, collectionNames); @@ -98,6 +99,7 @@ public async Task SetupDatabaseAsync() await CreateCollectionIfNotExists(EvneSkillMongoDbAccess.EvneSkillRecyclingBinCollectionName, collectionNames); await CreateCollectionIfNotExists(EvneSkillTagMongoDbAccess.EvneSkillTagCollectionName, collectionNames); await CreateCollectionIfNotExists(EvneSkillImplementationSnapshotMongoDbAccess.EvneSkillImplementationSnapshotCollectionName, collectionNames); + await CreateCollectionIfNotExists(EvneImportFieldValuesLogMongoDbAccess.EvneImportFieldValuesLogCollectionName, collectionNames); await CreateCollectionIfNotExists(KirjaPageMongoDbAccess.KirjaPageCollectionName, collectionNames); await CreateCollectionIfNotExists(KirjaPageMongoDbAccess.KirjaPageRecyclingBinCollectionName, collectionNames); diff --git a/Data/Styr/IStyrImportFieldValuesLogDbAccess.cs b/Data/Styr/IStyrImportFieldValuesLogDbAccess.cs new file mode 100644 index 00000000..abb9b26d --- /dev/null +++ b/Data/Styr/IStyrImportFieldValuesLogDbAccess.cs @@ -0,0 +1,11 @@ +using GoNorth.Data.FlexFieldDatabase; + +namespace GoNorth.Data.Styr +{ + /// + /// Interface for Database Access for Styr field values log db access + /// + public interface IStyrImportFieldValuesLogDbAccess : IFlexFieldImportFieldValuesLogDbAccess + { + } +} \ No newline at end of file diff --git a/Data/Styr/IStyrItemDbAccess.cs b/Data/Styr/IStyrItemDbAccess.cs index 8cdb7905..e1bacd77 100644 --- a/Data/Styr/IStyrItemDbAccess.cs +++ b/Data/Styr/IStyrItemDbAccess.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.Threading.Tasks; using GoNorth.Data.FlexFieldDatabase; namespace GoNorth.Data.Styr diff --git a/Data/Styr/StyrImportFieldValuesLogMongoDbAccess.cs b/Data/Styr/StyrImportFieldValuesLogMongoDbAccess.cs new file mode 100644 index 00000000..2b599809 --- /dev/null +++ b/Data/Styr/StyrImportFieldValuesLogMongoDbAccess.cs @@ -0,0 +1,25 @@ +using GoNorth.Config; +using GoNorth.Data.FlexFieldDatabase; +using Microsoft.Extensions.Options; + +namespace GoNorth.Data.Styr +{ + /// + /// Styr import field values Mongo Db Access + /// + public class StyrImportFieldValuesLogMongoDbAccess : FlexFieldImportFieldValuesLogMongoDbAccess, IStyrImportFieldValuesLogDbAccess + { + /// + /// Collection Name of the styr import field values log + /// + public const string StyrImportFieldValuesLogCollectionName = "StyrImportFieldValuesLog"; + + /// + /// Constructor + /// + /// Configuration + public StyrImportFieldValuesLogMongoDbAccess(IOptions configuration) : base(StyrImportFieldValuesLogCollectionName, configuration) + { + } + } +} diff --git a/GoNorth.csproj b/GoNorth.csproj index 38abd99b..bf246fbf 100644 --- a/GoNorth.csproj +++ b/GoNorth.csproj @@ -10,13 +10,14 @@ - - - - - - - + + + + + + + + diff --git a/GoNorthVersion.cs b/GoNorthVersion.cs index f2f5b556..0e50f642 100644 --- a/GoNorthVersion.cs +++ b/GoNorthVersion.cs @@ -10,6 +10,6 @@ public class GoNorthVersion /// /// Current GoNorth Version /// - public static readonly Version CurrentVersion = new Version(1, 8, 0, 0); + public static readonly Version CurrentVersion = new Version(1, 8, 0, 5); }; } \ No newline at end of file diff --git a/README.md b/README.md index 143b4184..dc11575c 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ It offers modules for: * Planning Items and their different values * Planning Skills and their different values * Exporting of Npcs, Dialogs, Items and skills + * Bulk exporting / importing of Npc, Item and skill values * Wiki component with versioning and tight integration into the other modules * Map component to position Quest marker, Npcs, Items, Wiki pages and map changes * Tracking your implementation status and showing changed values after change to already implemented object @@ -40,6 +41,7 @@ The web application provides functionality for planning Npcs, Items and Skills w ![Npc planning](https://github.com/steffendx/GoNorth/blob/master/TeaserImages/Kortisto.PNG?raw=true) Each object will display the different maps in which the Npc or Item is marked, the Wiki Pages in which its mentioned and all other connections. This way the user has a good overview what he or she has to consider when building the character or item. +To help with balancing and moving data, GoNorth supports bulk exporting and importing of values and objects. More details can be found in the wiki for [npcs](https://github.com/steffendx/GoNorth/wiki/Kortisto), [items](https://github.com/steffendx/GoNorth/wiki/Styr) and [skills](https://github.com/steffendx/GoNorth/wiki/Evne). ### Dialog Planning (Tale) @@ -149,4 +151,5 @@ GoNorth uses the following libraries: * [htmldiff.js](https://github.com/tnwinc/htmldiff.js) licensed under MIT * [mocha](https://github.com/mochajs/mocha) licensed under MIT * [Puppeteer](https://github.com/GoogleChrome/puppeteer) licensed under Apache License 2.0 - * [Scriban](https://github.com/lunet-io/scriban) licensed under BSD-2 \ No newline at end of file + * [Scriban](https://github.com/lunet-io/scriban) licensed under BSD-2 + * [CsvHelper](https://github.com/JoshClose/CsvHelper) licensed under Microsoft Public License (Ms-PL) \ No newline at end of file diff --git a/Resources/Controllers/Api/EvneApiController.de.json b/Resources/Controllers/Api/EvneApiController.de.json index 4b5551fc..9e13fa1d 100644 --- a/Resources/Controllers/Api/EvneApiController.de.json +++ b/Resources/Controllers/Api/EvneApiController.de.json @@ -11,5 +11,14 @@ "CouldNotUploadImage": "Bild konnte nicht hochgeladen werden.", "ImageFormatNotSupported": "Das Bildformat wird nicht unterstützt.", - "DuplicateFieldNameExist": "Es existitieren mehrere Felder mit gleichem Namen." + "DuplicateFieldNameExist": "Es existitieren mehrere Felder mit gleichem Namen.", + + "ImportFieldValuesObjectNotFound": "Die Fähigkeit wurde nicht gefunden.", + "ImportFieldValuesFieldNotFoundInTemplate": "Das Feld {0} wurde weder im Template noch in der Fähigkeit gefunden. Der Wert kann nicht importiert werden.", + "ImportFieldValuesNameIsEmpty": "Der Name darf nicht leer sein.", + "ImportFieldValuesValueNotValid": "Der Feldwert für das Feld {0} ist nicht gültig für den Feldtypen.", + "ImportFieldValuesObjectCouldNotBeUpdated": "Fähigkeit konnte nicht aktualisiert werden.", + "ImportFieldValuesObjectCouldNotBeCreated": "Fähigkeit konnte nicht angelegt werden.", + "ImportFieldValuesNoValidTemplateSpecified": "Es ist keine gültige Template Id in der CSV-Datei angegeben. Bitte exportier eine Vorlagen CSV und nutze diese für den Import.", + "ImportFieldValuesNoValidRows": "Keine gültige Zeilen gefunden. Bitte trag Zeilen für den Import ein." } \ No newline at end of file diff --git a/Resources/Controllers/Api/EvneApiController.en.json b/Resources/Controllers/Api/EvneApiController.en.json index 151e2c3d..7419ba4d 100644 --- a/Resources/Controllers/Api/EvneApiController.en.json +++ b/Resources/Controllers/Api/EvneApiController.en.json @@ -11,5 +11,14 @@ "CouldNotUploadImage": "Image could not be uploaded.", "ImageFormatNotSupported": "The image format is not supported.", - "DuplicateFieldNameExist": "Multiple fields with the same name exist." + "DuplicateFieldNameExist": "Multiple fields with the same name exist.", + + "ImportFieldValuesObjectNotFound": "Skill not found.", + "ImportFieldValuesFieldNotFoundInTemplate": "The field {0} was not found in the template nor in the skill. The value cannot be imported.", + "ImportFieldValuesNameIsEmpty": "The name must not be empty.", + "ImportFieldValuesValueNotValid": "The value of the field {0} is not valid for the field type.", + "ImportFieldValuesObjectCouldNotBeUpdated": "Skill could not be updated.", + "ImportFieldValuesObjectCouldNotBeCreated": "Skill could not be created.", + "ImportFieldValuesNoValidTemplateSpecified": "No valid template id was specified in the CSV file. Please export a CSV template using the CSV export functionality for an import.", + "ImportFieldValuesNoValidRows": "No valid rows to import. Please enter some rows to import." } \ No newline at end of file diff --git a/Resources/Controllers/Api/KortistoApiController.de.json b/Resources/Controllers/Api/KortistoApiController.de.json index 9f4a43ab..ee1adb96 100644 --- a/Resources/Controllers/Api/KortistoApiController.de.json +++ b/Resources/Controllers/Api/KortistoApiController.de.json @@ -15,5 +15,14 @@ "CouldNotUploadImage": "Bild konnte nicht hochgeladen werden.", "ImageFormatNotSupported": "Das Bildformat wird nicht unterstützt.", - "DuplicateFieldNameExist": "Es existitieren mehrere Felder mit gleichem Namen." + "DuplicateFieldNameExist": "Es existitieren mehrere Felder mit gleichem Namen.", + + "ImportFieldValuesObjectNotFound": "Der Npc wurde nicht gefunden.", + "ImportFieldValuesFieldNotFoundInTemplate": "Das Feld {0} wurde weder im Template noch im Npc gefunden. Der Wert kann nicht importiert werden.", + "ImportFieldValuesNameIsEmpty": "Der Name darf nicht leer sein.", + "ImportFieldValuesValueNotValid": "Der Feldwert für das Feld {0} ist nicht gültig für den Feldtypen.", + "ImportFieldValuesObjectCouldNotBeUpdated": "Npc konnte nicht aktualisiert werden.", + "ImportFieldValuesObjectCouldNotBeCreated": "Npc konnte nicht angelegt werden.", + "ImportFieldValuesNoValidTemplateSpecified": "Es ist keine gültige Template Id in der CSV-Datei angegeben. Bitte exportier eine Vorlagen CSV und nutze diese für den Import.", + "ImportFieldValuesNoValidRows": "Keine gültige Zeilen gefunden. Bitte trag Zeilen für den Import ein." } \ No newline at end of file diff --git a/Resources/Controllers/Api/KortistoApiController.en.json b/Resources/Controllers/Api/KortistoApiController.en.json index 4eec91e6..6333223d 100644 --- a/Resources/Controllers/Api/KortistoApiController.en.json +++ b/Resources/Controllers/Api/KortistoApiController.en.json @@ -15,5 +15,14 @@ "CouldNotUploadImage": "Image could not be uploaded.", "ImageFormatNotSupported": "The image format is not supported.", - "DuplicateFieldNameExist": "Multiple fields with the same name exist." + "DuplicateFieldNameExist": "Multiple fields with the same name exist.", + + "ImportFieldValuesObjectNotFound": "Npc not found.", + "ImportFieldValuesFieldNotFoundInTemplate": "The field {0} was not found in the template nor in the npc. The value cannot be imported.", + "ImportFieldValuesNameIsEmpty": "The name must not be empty.", + "ImportFieldValuesValueNotValid": "The value of the field {0} is not valid for the field type.", + "ImportFieldValuesObjectCouldNotBeUpdated": "Npc could not be updated.", + "ImportFieldValuesObjectCouldNotBeCreated": "Npc could not be created.", + "ImportFieldValuesNoValidTemplateSpecified": "No valid template id was specified in the CSV file. Please export a CSV template using the CSV export functionality for an import.", + "ImportFieldValuesNoValidRows": "No valid rows to import. Please enter some rows to import." } \ No newline at end of file diff --git a/Resources/Controllers/Api/StyrApiController.de.json b/Resources/Controllers/Api/StyrApiController.de.json index 21f42548..47c9590c 100644 --- a/Resources/Controllers/Api/StyrApiController.de.json +++ b/Resources/Controllers/Api/StyrApiController.de.json @@ -12,5 +12,14 @@ "CouldNotUploadImage": "Bild konnte nicht hochgeladen werden.", "ImageFormatNotSupported": "Das Bildformat wird nicht unterstützt.", - "DuplicateFieldNameExist": "Es existitieren mehrere Felder mit gleichem Namen." + "DuplicateFieldNameExist": "Es existitieren mehrere Felder mit gleichem Namen.", + + "ImportFieldValuesObjectNotFound": "Das Item wurde nicht gefunden.", + "ImportFieldValuesFieldNotFoundInTemplate": "Das Feld {0} wurde weder im Template noch im Item gefunden. Der Wert kann nicht importiert werden.", + "ImportFieldValuesNameIsEmpty": "Der Name darf nicht leer sein.", + "ImportFieldValuesValueNotValid": "Der Feldwert für das Feld {0} ist nicht gültig für den Feldtypen.", + "ImportFieldValuesObjectCouldNotBeUpdated": "Item konnte nicht aktualisiert werden.", + "ImportFieldValuesObjectCouldNotBeCreated": "Item konnte nicht angelegt werden.", + "ImportFieldValuesNoValidTemplateSpecified": "Es ist keine gültige Template Id in der CSV-Datei angegeben. Bitte exportier eine Vorlagen CSV und nutze diese für den Import.", + "ImportFieldValuesNoValidRows": "Keine gültige Zeilen gefunden. Bitte trag Zeilen für den Import ein." } \ No newline at end of file diff --git a/Resources/Controllers/Api/StyrApiController.en.json b/Resources/Controllers/Api/StyrApiController.en.json index c21601ec..45e971c2 100644 --- a/Resources/Controllers/Api/StyrApiController.en.json +++ b/Resources/Controllers/Api/StyrApiController.en.json @@ -12,5 +12,14 @@ "CouldNotUploadImage": "Image could not be uploaded.", "ImageFormatNotSupported": "The image format is not supported.", - "DuplicateFieldNameExist": "Multiple fields with the same name exist." + "DuplicateFieldNameExist": "Multiple fields with the same name exist.", + + "ImportFieldValuesObjectNotFound": "Item not found.", + "ImportFieldValuesFieldNotFoundInTemplate": "The field {0} was not found in the template nor in the item. The value cannot be imported.", + "ImportFieldValuesNameIsEmpty": "The name must not be empty.", + "ImportFieldValuesValueNotValid": "The value of the field {0} is not valid for the field type.", + "ImportFieldValuesObjectCouldNotBeUpdated": "Item could not be updated.", + "ImportFieldValuesObjectCouldNotBeCreated": "Item could not be created.", + "ImportFieldValuesNoValidTemplateSpecified": "No valid template id was specified in the CSV file. Please export a CSV template using the CSV export functionality for an import.", + "ImportFieldValuesNoValidRows": "No valid rows to import. Please enter some rows to import." } \ No newline at end of file diff --git a/Resources/Services/Export/Placeholder/ScribanRenderingEngine/RenderingObjects/ScribanExportSnippet.de.json b/Resources/Services/Export/Placeholder/ScribanRenderingEngine/RenderingObjects/ScribanExportSnippet.de.json index 8008ecce..68314cbf 100644 --- a/Resources/Services/Export/Placeholder/ScribanRenderingEngine/RenderingObjects/ScribanExportSnippet.de.json +++ b/Resources/Services/Export/Placeholder/ScribanRenderingEngine/RenderingObjects/ScribanExportSnippet.de.json @@ -1,4 +1,7 @@ { "PlaceholderDesc_Name": "Name des Snippets", - "PlaceholderDesc_Exists": "true wenn das Snippet existiert, sonst false" + "PlaceholderDesc_Exists": "true wenn das Snippet existiert, sonst false", + "PlaceholderDesc_AdditionalFunctions": "Alle Funktionen des Snippets ohne die initiale Funktion.", + "PlaceholderDesc_AllFunctions": "Alle Funktionen des Snippet", + "PlaceholderDesc_InitialFunction": "Initiale Funktion für das Snippet." } \ No newline at end of file diff --git a/Resources/Services/Export/Placeholder/ScribanRenderingEngine/RenderingObjects/ScribanExportSnippet.en.json b/Resources/Services/Export/Placeholder/ScribanRenderingEngine/RenderingObjects/ScribanExportSnippet.en.json index 0098df49..ecba99be 100644 --- a/Resources/Services/Export/Placeholder/ScribanRenderingEngine/RenderingObjects/ScribanExportSnippet.en.json +++ b/Resources/Services/Export/Placeholder/ScribanRenderingEngine/RenderingObjects/ScribanExportSnippet.en.json @@ -1,4 +1,7 @@ { "PlaceholderDesc_Name": "Name of the snippet", - "PlaceholderDesc_Exists": "true if the snippet exists, else false" + "PlaceholderDesc_Exists": "true if the snippet exists, else false", + "PlaceholderDesc_AdditionalFunctions": "All functions of the snippet without the initial one.", + "PlaceholderDesc_AllFunctions": "All functions of the snippet", + "PlaceholderDesc_InitialFunction": "Initial function of the snippet." } \ No newline at end of file diff --git a/Resources/Services/Timeline/HtmlTimelineTemplateService.de.json b/Resources/Services/Timeline/HtmlTimelineTemplateService.de.json index 77e2932a..4327fb55 100644 --- a/Resources/Services/Timeline/HtmlTimelineTemplateService.de.json +++ b/Resources/Services/Timeline/HtmlTimelineTemplateService.de.json @@ -28,6 +28,8 @@ "KortistoNpcExportSnippetChanged": "Das Export Snippet {0} des Npc {1} wurde geändert.", "KortistoNpcExportSnippetDeleted": "Das Export Snippet {0} des Npc {1} wurde gelöscht.", + "KortistoValueFileImport": "Eine Npc Wertedatei wurde importiert. Es wurden {0} Npcs aktualisiert und {1} Npcs neu angelegt.", + "StyrFolderCreated": "Ein neue Kategorie {0} wurde in Styr angelegt.", "StyrFolderDeleted": "Die Kategorie {0} wurde in Styr gelöscht.", "StyrFolderUpdated": "Die Kategorie {0} wurde in Styr aktualisiert.", @@ -49,6 +51,8 @@ "StyrItemExportSnippetChanged": "Das Export Snippet {0} des Item {1} wurde geändert.", "StyrItemExportSnippetDeleted": "Das Export Snippet {0} des Item {1} wurde gelöscht.", + "StyrValueFileImport": "Eine Item Wertedatei wurde importiert. Es wurden {0} Items aktualisiert und {1} Items neu angelegt.", + "EvneFolderCreated": "Ein neue Kategorie {0} wurde in Evne angelegt.", "EvneFolderDeleted": "Die Kategorie {0} wurde in Evne gelöscht.", "EvneFolderUpdated": "Die Kategorie {0} wurde in Evne aktualisiert.", @@ -70,6 +74,8 @@ "EvneSkillExportSnippetChanged": "Das Export Snippet {0} der Fähigkeit {1} wurde geändert.", "EvneSkillExportSnippetDeleted": "Das Export Snippet {0} der Fähigkeit {1} wurde gelöscht.", + "EvneValueFileImport": "Eine Fähigkeiten Wertedatei wurde importiert. Es wurden {0} Fähigkeiten aktualisiert und {1} Fähigkeiten neu angelegt.", + "KirjaPageCreated": "Eine neue Seite {0} wurde in Kirja angelegt.", "KirjaPageDeleted": "Die Seite {0} wurde in Kirja gelöscht.", "KirjaPageUpdated": "Die Seite {0} wurde in Kirja aktualisiert.", diff --git a/Resources/Services/Timeline/HtmlTimelineTemplateService.en.json b/Resources/Services/Timeline/HtmlTimelineTemplateService.en.json index 727a0768..4814f97d 100644 --- a/Resources/Services/Timeline/HtmlTimelineTemplateService.en.json +++ b/Resources/Services/Timeline/HtmlTimelineTemplateService.en.json @@ -28,6 +28,8 @@ "KortistoNpcExportSnippetChanged": "The export snippet {0} of the npc {1} was updated.", "KortistoNpcExportSnippetDeleted": "The export snippet {0} of the npc {1} was deleted.", + "KortistoValueFileImport": "A npc value file was imported. {0} npcs were updated and {1} new npcs were created.", + "StyrFolderCreated": "A new category {0} was created in Styr.", "StyrFolderDeleted": "The category {0} was deleted in Styr.", "StyrFolderUpdated": "The category {0} was updated in Styr.", @@ -49,6 +51,8 @@ "StyrItemExportSnippetChanged": "The export snippet {0} of the item {1} was updated.", "StyrItemExportSnippetDeleted": "The export snippet {0} of the item {1} was deleted.", + "StyrValueFileImport": "An item value file was imported. {0} items were updated and {1} new items were created.", + "EvneFolderCreated": "A new category {0} was created in Evne.", "EvneFolderDeleted": "The category {0} was deleted in Evne.", "EvneFolderUpdated": "The category {0} was updated in Evne.", @@ -70,6 +74,8 @@ "EvneSkillExportSnippetChanged": "The export snippet {0} of the skill {1} was updated.", "EvneSkillExportSnippetDeleted": "The export snippet {0} of the skill {1} was deleted.", + "EvneValueFileImport": "A skill value file was imported. {0} skills were updated and {1} new skills were created.", + "KirjaPageCreated": "A new page {0} was created in Kirja.", "KirjaPageDeleted": "The page {0} was deleted in Kirja.", "KirjaPageUpdated": "The page {0} was updated in Kirja.", diff --git a/Resources/Views/Evne/Index.de.json b/Resources/Views/Evne/Index.de.json index 4d97b9cf..5484e936 100644 --- a/Resources/Views/Evne/Index.de.json +++ b/Resources/Views/Evne/Index.de.json @@ -19,6 +19,9 @@ "ManageTemplates": "Templates verwalten", + "FieldValueExport": "Feldwerte exportieren", + "FieldValueImport": "Feldwerte importieren", + "DropHereToMoveToParent": "Objekt hier ablegen um es nach oben zu bewegen", "SelectTargetCategory": "Kategorie auswählen", "RootLevel": "Oberste Ebene", diff --git a/Resources/Views/Evne/Index.en.json b/Resources/Views/Evne/Index.en.json index fea3261d..4806d1aa 100644 --- a/Resources/Views/Evne/Index.en.json +++ b/Resources/Views/Evne/Index.en.json @@ -18,6 +18,9 @@ "Cancel": "Cancel", "ManageTemplates": "Manage templates", + + "FieldValueExport": "Export field values", + "FieldValueImport": "Import field values", "DropHereToMoveToParent": "Drop object here to move to parent folder", "SelectTargetCategory": "Select target category", diff --git a/Resources/Views/FlexFieldDatabase/ValueExportDialog.de.json b/Resources/Views/FlexFieldDatabase/ValueExportDialog.de.json new file mode 100644 index 00000000..30c439b7 --- /dev/null +++ b/Resources/Views/FlexFieldDatabase/ValueExportDialog.de.json @@ -0,0 +1,25 @@ +{ + "ExportFieldValues": "Feldwerte exportieren", + + "AllObjectsWillBeExported": "Alle Objekte des ausgewählten Templates werden exportiert.", + "OnlyObjectsUnderneathFolderPrefix": "Alle Objekte des ausgewählten Templates inner- und unterhalb des Ordners ", + "OnlyObjectsUnderneathFolderPostfix": " werden exportiert.", + + "Template": "Template", + + "Fields": "Felder", + + "NoFieldsFound": "Es wurden keine Felder gefunden.", + + "SingleLineField": " (Textfeld)", + "MultiLineField": " (Mehrzeiliges Textfeld)", + "NumberField": " (Zahlenfeld)", + "OptionField": " (Auswahlfeld)", + + "YouHaveToSelectATemplate": "Du musst ein Template auswählen.", + + "Export": "Exportieren", + "Cancel": "Abbrechen", + + "ErrorOccured": "Ein Fehler ist aufgetreten." +} \ No newline at end of file diff --git a/Resources/Views/FlexFieldDatabase/ValueExportDialog.en.json b/Resources/Views/FlexFieldDatabase/ValueExportDialog.en.json new file mode 100644 index 00000000..227beca0 --- /dev/null +++ b/Resources/Views/FlexFieldDatabase/ValueExportDialog.en.json @@ -0,0 +1,25 @@ +{ + "ExportFieldValues": "Export field values", + + "AllObjectsWillBeExported": "All objects of the selected template will be exported.", + "OnlyObjectsUnderneathFolderPrefix": "Alle objects of the selected template insde and underneath of the folder ", + "OnlyObjectsUnderneathFolderPostfix": " will be exported.", + + "Template": "Template", + + "Fields": "Fields", + + "NoFieldsFound": "No fields were found.", + + "SingleLineField": " (Text field)", + "MultiLineField": " (Multiline text field)", + "NumberField": " (Number field)", + "OptionField": " (Option field)", + + "YouHaveToSelectATemplate": "You must select a template.", + + "Export": "Export", + "Cancel": "Cancel", + + "ErrorOccured": "An error occured." +} \ No newline at end of file diff --git a/Resources/Views/FlexFieldDatabase/ValueImportDialog.de.json b/Resources/Views/FlexFieldDatabase/ValueImportDialog.de.json new file mode 100644 index 00000000..57cac6f6 --- /dev/null +++ b/Resources/Views/FlexFieldDatabase/ValueImportDialog.de.json @@ -0,0 +1,35 @@ +{ + "ImportFieldValues": "Feldwerte importieren", + + "DropValueFileHere": "Wertedatei hier ablegen...", + + "ExistingRows": "Bestehende Zeilen:", + "NewRows": "Neue Zeilen:", + "ObjectsWillBeImportedToRoot": "Objekte werden auf oberster Ebene importiert.", + "ObjectsWillBeImportedIntoFolderPrefix": "Objekte werden in den Ordner ", + "ObjectsWillBeImportedIntoFolderPostfix": " importiert.", + + "ShowPreviousImports": "Vorherige Imports anzeigen", + "BackToImport": "Zurück zum Import", + "ShowImportLogList": "Log Liste anzeigen", + + "PreviousImports": "Vorherige Imports", + "ImportedOn": "Importiert am", + "Filename": "Dateiname", + + "PreviousPage": "Vorherige Seite", + "NextPage": "Nächste Seite", + + "Import": "Importieren", + "ExportResults": "Ergebnisse exportieren", + "Close": "Schließen", + "Cancel": "Abbrechen", + + "ImportResultSuccess": "Die Werte wurden erfolgreich importiert.", + "ImportResultFailed": "Es ist ein Fehler beim Import aufgetreten.", + "ImportResultNoChange": "Es liegen keine Änderungen vor. Die Zeile wurde nicht importiert.", + "DetailHeader": "Details", + + "Error": "Fehler:", + "ErrorOccured": "Es ist ein Fehler aufgetreten." +} \ No newline at end of file diff --git a/Resources/Views/FlexFieldDatabase/ValueImportDialog.en.json b/Resources/Views/FlexFieldDatabase/ValueImportDialog.en.json new file mode 100644 index 00000000..a965d51d --- /dev/null +++ b/Resources/Views/FlexFieldDatabase/ValueImportDialog.en.json @@ -0,0 +1,35 @@ +{ + "ImportFieldValues": "Import field values", + + "DropValueFileHere": "Drop value file here...", + + "ExistingRows": "Existing rows:", + "NewRows": "New rows:", + "ObjectsWillBeImportedToRoot": "Objects will be imported at root level.", + "ObjectsWillBeImportedIntoFolderPrefix": "Objects will be imported into the folder ", + "ObjectsWillBeImportedIntoFolderPostfix": ".", + + "ShowPreviousImports": "Show previous imports", + "BackToImport": "Back to import", + "ShowImportLogList": "Show import log list", + + "PreviousImports": "Previous imports", + "ImportedOn": "Imported on", + "Filename": "Filename", + + "PreviousPage": "Previous page", + "NextPage": "Next page", + + "Import": "Import", + "ExportResults": "Export results", + "Close": "Close", + "Cancel": "Cancel", + + "ImportResultSuccess": "The values were successfully imported.", + "ImportResultFailed": "An error occured during the import of the row.", + "ImportResultNoChange": "No changes exist. The row was ignored.", + "DetailHeader": "Details", + + "Error": "Error:", + "ErrorOccured": "An error occured." +} \ No newline at end of file diff --git a/Resources/Views/Home/Index.de.json b/Resources/Views/Home/Index.de.json index 7686d226..58c4d1f3 100644 --- a/Resources/Views/Home/Index.de.json +++ b/Resources/Views/Home/Index.de.json @@ -6,22 +6,22 @@ "Tasks": "Aufgaben", "TaskManagement": "Aufgaben Verwaltung", - "Aika": "Aika", + "Aika": "Quests", "AikaQuestDatabase": "Quest Datenbank", - "Kortisto": "Kortisto", + "Kortisto": "Npcs", "KortistoNpcDatabase": "Npc Datenbank", - "Styr": "Styr", + "Styr": "Items", "StyrItemDatabase": "Item Datenbank", - "Evne": "Evne", - "EvneSkillDatabase": "Skill Datenbank", + "Evne": "Fähigkeiten", + "EvneSkillDatabase": "Fähigkeiten Datenbank", - "Kirja": "Kirja", + "Kirja": "Wiki", "KirjaWiki": "Wiki Komponente", - "Karta": "Karta", + "Karta": "Karten", "KartaMap": "Karten Komponente", "ImplementationStatus": "Implementierung", diff --git a/Resources/Views/Home/Index.en.json b/Resources/Views/Home/Index.en.json index c3d17028..1a234f42 100644 --- a/Resources/Views/Home/Index.en.json +++ b/Resources/Views/Home/Index.en.json @@ -6,22 +6,22 @@ "Tasks": "Tasks", "TaskManagement": "Task management", - "Aika": "Aika", + "Aika": "Quests", "AikaQuestDatabase": "Quest database", - "Kortisto": "Kortisto", + "Kortisto": "Npcs", "KortistoNpcDatabase": "Npc database", - "Styr": "Styr", + "Styr": "Items", "StyrItemDatabase": "Item database", - "Evne": "Evne", + "Evne": "Skills", "EvneSkillDatabase": "Skill database", - "Kirja": "Kirja", + "Kirja": "Wiki", "KirjaWiki": "Wiki component", - "Karta": "Karta", + "Karta": "Maps", "KartaMap": "Map component", "ImplementationStatus": "Implementation", diff --git a/Resources/Views/Karta/Index.de.json b/Resources/Views/Karta/Index.de.json index ca59932e..11beea0d 100644 --- a/Resources/Views/Karta/Index.de.json +++ b/Resources/Views/Karta/Index.de.json @@ -9,12 +9,12 @@ "Yes": "Ja", "No": "Nein", - "Kirja": "Kirja", + "Kirja": "Wiki", "NewKirjaPage": "", - "ShowKirjaMarker": "Kirja Markierungen anzeigen", + "ShowKirjaMarker": "Wiki Markierungen anzeigen", - "Kortisto": "Kortisto", - "ShowKortistoMarker": "Kortisto Markierungen anzeigen", + "Kortisto": "Npcs", + "ShowKortistoMarker": "Npc Markierungen anzeigen", "KortistoEditDailyRoutineTooltip": "Tagesablauf bearbeiten", "EditingDailyRoutinePrefix": "Du bearbeitest den Tagesablauf von ", @@ -48,13 +48,13 @@ "ChapterAreNotSupportedForDailyRoutines": "Kapiteländerungen für Tagesabläufe werden nicht unterstützt. Änderungen am Tagesablauf wirken sich auf alle Kapitel aus. Bitte benutze einen Aktionsknoten in einem Dialog oder Quest um den Tagesablauf anzupassen.", "Ok": "Ok", - "Styr": "Styr", - "ShowStyrMarker": "Styr Markierungen anzeigen", + "Styr": "Items", + "ShowStyrMarker": "Item Markierungen anzeigen", - "Aika": "Aika", - "ShowAikaMarker": "Aika Markierungen anzeigen", + "Aika": "Quests", + "ShowAikaMarker": "Quest Markierungen anzeigen", - "ShowKartaMarker": "Karta Markierungen anzeigen", + "ShowKartaMarker": "Karten Markierungen anzeigen", "Notes": "Notizen", "NoteMarkerNewNote": "", diff --git a/Resources/Views/Karta/Index.en.json b/Resources/Views/Karta/Index.en.json index 481bc35c..56ae8576 100644 --- a/Resources/Views/Karta/Index.en.json +++ b/Resources/Views/Karta/Index.en.json @@ -9,12 +9,12 @@ "Yes": "Yes", "No": "No", - "Kirja": "Kirja", + "Kirja": "Wiki", "NewKirjaPage": "", - "ShowKirjaMarker": "Show Kirja markers", + "ShowKirjaMarker": "Show wiki markers", - "Kortisto": "Kortisto", - "ShowKortistoMarker": "Show Kortisto markers", + "Kortisto": "Npcs", + "ShowKortistoMarker": "Show npc markers", "KortistoEditDailyRoutineTooltip": "Edit daily routine", "EditingDailyRoutinePrefix": "You are editing the daily routine of ", @@ -48,13 +48,13 @@ "ChapterAreNotSupportedForDailyRoutines": "Chapter editing for daily routines is not supported. Changes to the daily routine will affect all chapters. Instead you should use one of an action node in a dialog or quest to manipulate the daily routine of npcs.", "Ok": "Ok", - "Styr": "Styr", - "ShowStyrMarker": "Show Styr markers", + "Styr": "Items", + "ShowStyrMarker": "Show item markers", - "Aika": "Aika", - "ShowAikaMarker": "Show Aika markers", + "Aika": "Quests", + "ShowAikaMarker": "Show quest markers", - "ShowKartaMarker": "Show Karta markers", + "ShowKartaMarker": "Show map markers", "Notes": "Notes", "NoteMarkerNewNote": "", diff --git a/Resources/Views/Kortisto/Index.de.json b/Resources/Views/Kortisto/Index.de.json index 1b4ad7f8..b687218d 100644 --- a/Resources/Views/Kortisto/Index.de.json +++ b/Resources/Views/Kortisto/Index.de.json @@ -18,6 +18,9 @@ "Cancel": "Abbrechen", "ManageTemplates": "Templates verwalten", + + "FieldValueExport": "Feldwerte exportieren", + "FieldValueImport": "Feldwerte importieren", "DropHereToMoveToParent": "Objekt hier ablegen um es nach oben zu bewegen", "SelectTargetCategory": "Kategorie auswählen", diff --git a/Resources/Views/Kortisto/Index.en.json b/Resources/Views/Kortisto/Index.en.json index aadaa605..ea77d260 100644 --- a/Resources/Views/Kortisto/Index.en.json +++ b/Resources/Views/Kortisto/Index.en.json @@ -18,6 +18,9 @@ "Cancel": "Cancel", "ManageTemplates": "Manage templates", + + "FieldValueExport": "Export field values", + "FieldValueImport": "Import field values", "DropHereToMoveToParent": "Drop object here to move to parent folder", "SelectTargetCategory": "Select target category", diff --git a/Resources/Views/Styr/Index.de.json b/Resources/Views/Styr/Index.de.json index 776a6074..2cad68bc 100644 --- a/Resources/Views/Styr/Index.de.json +++ b/Resources/Views/Styr/Index.de.json @@ -19,6 +19,9 @@ "ManageTemplates": "Templates verwalten", + "FieldValueExport": "Feldwerte exportieren", + "FieldValueImport": "Feldwerte importieren", + "DropHereToMoveToParent": "Objekt hier ablegen um es nach oben zu bewegen", "SelectTargetCategory": "Kategorie auswählen", "RootLevel": "Oberste Ebene", diff --git a/Resources/Views/Styr/Index.en.json b/Resources/Views/Styr/Index.en.json index 3e0e43b5..e51e4edb 100644 --- a/Resources/Views/Styr/Index.en.json +++ b/Resources/Views/Styr/Index.en.json @@ -19,6 +19,9 @@ "ManageTemplates": "Manage templates", + "FieldValueExport": "Export field values", + "FieldValueImport": "Import field values", + "DropHereToMoveToParent": "Drop object here to move to parent folder", "SelectTargetCategory": "Select target category", "RootLevel": "Root level", diff --git a/Services/CsvHandling/CsvGenerator.cs b/Services/CsvHandling/CsvGenerator.cs new file mode 100644 index 00000000..57eba7ca --- /dev/null +++ b/Services/CsvHandling/CsvGenerator.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Threading.Tasks; +using CsvHelper; +using CsvHelper.Configuration; +using GoNorth.Config; +using Microsoft.Extensions.Options; + +namespace GoNorth.Services.CsvHandling +{ + /// + /// Interface for generating CSV-File + /// + public class CsvGenerator : ICsvGenerator + { + /// + /// Delimiter + /// + private readonly string _delimiter; + + /// + /// Constructor + /// + /// Configuration Data + public CsvGenerator(IOptions configuration) + { + _delimiter = configuration.Value.Misc.CsvDelimiter; + } + + /// + /// Generates a CSV-File + /// + /// Header names + /// Field values + /// CSV-Content + public async Task GenerateCsvFile(List headerNames, List> fieldValues) + { + using(TextWriter writer = new StringWriter()) + { + CsvConfiguration configuration = new CsvConfiguration(CultureInfo.CurrentUICulture); + configuration.Delimiter = _delimiter; + using(CsvWriter csvWriter = new CsvWriter(writer, configuration)) + { + await WriteHeader(csvWriter, headerNames); + foreach(Dictionary curRow in fieldValues) + { + foreach(string curHeader in headerNames) + { + string fieldValue = string.Empty; + if(curRow.ContainsKey(curHeader)) + { + fieldValue = curRow[curHeader]; + } + + csvWriter.WriteField(fieldValue); + } + await csvWriter.NextRecordAsync(); + } + } + + return writer.ToString(); + } + } + + /// + /// Builds the CSV Header row + /// + /// CSV Writer + /// Header names + /// CSV Header row + private async Task WriteHeader(CsvWriter csvWriter, List headerNames) + { + foreach(string curHeader in headerNames) + { + csvWriter.WriteField(curHeader); + } + + await csvWriter.NextRecordAsync(); + } + + } +} diff --git a/Services/CsvHandling/CsvParser.cs b/Services/CsvHandling/CsvParser.cs new file mode 100644 index 00000000..7dbae8a0 --- /dev/null +++ b/Services/CsvHandling/CsvParser.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using CsvHelper; +using CsvHelper.Configuration; + +namespace GoNorth.Services.CsvHandling +{ + /// + /// Interface for reading CSV-File + /// + public class CsvParser : ICsvParser + { + /// + /// Possible CSV Delimiters + /// + private readonly List PossibleDelimiters = new List { ",", ";", "\t", "|" }; + + /// + /// Reads a CSV-File + /// + /// CSV Stream Reader + /// CSV-Content + public async Task ReadCsvFile(Stream csvStream) + { + CsvReadResult readResult = new CsvReadResult(); + + using(StreamReader streamReader = new StreamReader(csvStream)) + { + string delimiter = await DetectDelimiter(csvStream, streamReader); + + CsvConfiguration configuration = new CsvConfiguration(CultureInfo.CurrentUICulture); + configuration.Delimiter = delimiter; + using (CsvReader csvReader = new CsvReader(streamReader, configuration)) + { + while (await csvReader.ReadAsync()) + { + IDictionary rowValues = csvReader.GetRecord() as IDictionary; + if (rowValues == null) + { + continue; + } + + AddMissingHeaders(readResult, rowValues); + + readResult.Rows.Add(ReadRow(readResult.Columns, rowValues)); + } + } + } + + return readResult; + } + + /// + /// Detects the delimiter of a CSV File + /// + /// CSV Stream + /// Stream Reader + /// Delimiter + private async Task DetectDelimiter(Stream csvStream, StreamReader streamReader) + { + string headerLine = await streamReader.ReadLineAsync(); + csvStream.Seek(0, SeekOrigin.Begin); + streamReader.DiscardBufferedData(); + + foreach (var possibleDelimiter in PossibleDelimiters) + { + if (headerLine.Contains(possibleDelimiter)) + { + return possibleDelimiter; + } + } + + return PossibleDelimiters[0]; + } + + /// + /// Adds missing headers to a read result + /// + /// Read result + /// Row values + private void AddMissingHeaders(CsvReadResult readResult, IDictionary rowValues) + { + List missingHeaders = rowValues.Keys.Where(k => !readResult.Columns.Contains(k)).ToList(); + if(!missingHeaders.Any()) + { + return; + } + + readResult.Columns.AddRange(missingHeaders); + readResult.Rows.ForEach(r => missingHeaders.ForEach(h => r.Add(h, string.Empty))); + } + + /// + /// Reads a row + /// + /// Columns to read + /// Row values + /// Read row + private Dictionary ReadRow(List columns, IDictionary rowValues) + { + Dictionary row = new Dictionary(); + foreach(string curCol in columns) + { + string fieldValue = string.Empty; + if(rowValues.ContainsKey(curCol) && rowValues[curCol] != null) + { + fieldValue = rowValues[curCol].ToString(); + } + + row.Add(curCol, fieldValue); + } + + return row; + } + } +} diff --git a/Services/CsvHandling/CsvReadResult.cs b/Services/CsvHandling/CsvReadResult.cs new file mode 100644 index 00000000..60bb98a8 --- /dev/null +++ b/Services/CsvHandling/CsvReadResult.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; + +namespace GoNorth.Services.CsvHandling +{ + /// + /// Reading result for a csv + /// + public class CsvReadResult + { + /// + /// Read Columns + /// + public List Columns { get; set; } + + /// + /// Read Rows + /// + public List> Rows { get; set; } + + /// + /// Constructor + /// + public CsvReadResult() + { + Columns = new List(); + Rows = new List>(); + } + } +} diff --git a/Services/CsvHandling/ICsvGenerator.cs b/Services/CsvHandling/ICsvGenerator.cs new file mode 100644 index 00000000..5da2b5aa --- /dev/null +++ b/Services/CsvHandling/ICsvGenerator.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace GoNorth.Services.CsvHandling +{ + /// + /// Interface for generating CSV-File + /// + public interface ICsvGenerator + { + /// + /// Generates a CSV-File + /// + /// Header names + /// Field values + /// CSV-Content + Task GenerateCsvFile(List headerNames, List> fieldValues); + } +} diff --git a/Services/CsvHandling/ICsvParser.cs b/Services/CsvHandling/ICsvParser.cs new file mode 100644 index 00000000..039ba12e --- /dev/null +++ b/Services/CsvHandling/ICsvParser.cs @@ -0,0 +1,18 @@ +using System.IO; +using System.Threading.Tasks; + +namespace GoNorth.Services.CsvHandling +{ + /// + /// Interface for reading CSV-File + /// + public interface ICsvParser + { + /// + /// Reads a CSV-File + /// + /// CSV Stream Reader + /// CSV-Content + Task ReadCsvFile(Stream csvStream); + } +} diff --git a/Services/FlexFieldThumbnail/ImageSharpFlexFieldThumbnailService.cs b/Services/FlexFieldThumbnail/ImageSharpFlexFieldThumbnailService.cs index f769dd2f..34975dae 100644 --- a/Services/FlexFieldThumbnail/ImageSharpFlexFieldThumbnailService.cs +++ b/Services/FlexFieldThumbnail/ImageSharpFlexFieldThumbnailService.cs @@ -81,7 +81,7 @@ private void ResizeImage(Image image) ResizeOptions fillOptions = new ResizeOptions(); fillOptions.Mode = ResizeMode.Max; fillOptions.Position = AnchorPositionMode.TopLeft; - fillOptions.Size = new SixLabors.Primitives.Size(ThumbnailSize, ThumbnailSize); + fillOptions.Size = new Size(ThumbnailSize, ThumbnailSize); image.Mutate(i => i.Resize(fillOptions)); } diff --git a/Services/Karta/ImageSharpKartaImageProcessor.cs b/Services/Karta/ImageSharpKartaImageProcessor.cs index 3959961b..2207e1a1 100644 --- a/Services/Karta/ImageSharpKartaImageProcessor.cs +++ b/Services/Karta/ImageSharpKartaImageProcessor.cs @@ -7,7 +7,6 @@ using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SixLabors.Primitives; namespace GoNorth.Services.Karta { @@ -125,7 +124,7 @@ private void EnsurePowerOfTwo(KartaMap map, Image image) ResizeOptions fillOptions = new ResizeOptions(); fillOptions.Mode = ResizeMode.BoxPad; fillOptions.Position = AnchorPositionMode.TopLeft; - fillOptions.Size = new SixLabors.Primitives.Size(targetSize, targetSize); + fillOptions.Size = new Size(targetSize, targetSize); image.Mutate(i => i.Resize(fillOptions)); } diff --git a/Services/Timeline/TimelineEvent.cs b/Services/Timeline/TimelineEvent.cs index ab719b3a..c00c7436 100644 --- a/Services/Timeline/TimelineEvent.cs +++ b/Services/Timeline/TimelineEvent.cs @@ -129,6 +129,11 @@ public enum TimelineEvent /// KortistoNpcExportSnippetDeleted = 109, + /// + /// A Kortisto value file was imported + /// + KortistoValueFileImport = 115, + /// /// A new Styr Folder was created @@ -222,6 +227,11 @@ public enum TimelineEvent /// StyrItemExportSnippetDeleted = 110, + /// + /// A Styr value file was imported + /// + StyrValueFileImport = 116, + /// /// A new Evne Folder was created @@ -315,6 +325,11 @@ public enum TimelineEvent /// EvneSkillExportSnippetDeleted = 111, + /// + /// An evne value file was imported + /// + EvneValueFileImport = 117, + /// /// A Kirja page was created diff --git a/Services/Timeline/TimelineService.cs b/Services/Timeline/TimelineService.cs index a42a4b94..36b0f9e7 100644 --- a/Services/Timeline/TimelineService.cs +++ b/Services/Timeline/TimelineService.cs @@ -111,6 +111,8 @@ private void SetupFilters() _entryRoleFilter.Add(TimelineEvent.KortistoNpcExportSnippetChanged, new List() { RoleNames.Kortisto, RoleNames.ExportObjects }); _entryRoleFilter.Add(TimelineEvent.KortistoNpcExportSnippetDeleted, new List() { RoleNames.Kortisto, RoleNames.ExportObjects }); + _entryRoleFilter.Add(TimelineEvent.KortistoValueFileImport, new List() { RoleNames.Kortisto }); + // Styr _entryRoleFilter.Add(TimelineEvent.StyrFolderCreated, new List() { RoleNames.Styr }); _entryRoleFilter.Add(TimelineEvent.StyrFolderDeleted, new List() { RoleNames.Styr }); @@ -132,6 +134,8 @@ private void SetupFilters() _entryRoleFilter.Add(TimelineEvent.StyrItemMovedToRoot, new List() { RoleNames.Styr }); _entryRoleFilter.Add(TimelineEvent.StyrItemExportSnippetChanged, new List() { RoleNames.Styr, RoleNames.ExportObjects }); _entryRoleFilter.Add(TimelineEvent.StyrItemExportSnippetDeleted, new List() { RoleNames.Styr, RoleNames.ExportObjects }); + + _entryRoleFilter.Add(TimelineEvent.StyrValueFileImport, new List() { RoleNames.Styr }); // Evne _entryRoleFilter.Add(TimelineEvent.EvneFolderCreated, new List() { RoleNames.Evne }); @@ -155,6 +159,8 @@ private void SetupFilters() _entryRoleFilter.Add(TimelineEvent.EvneSkillExportSnippetChanged, new List() { RoleNames.Evne, RoleNames.ExportObjects }); _entryRoleFilter.Add(TimelineEvent.EvneSkillExportSnippetDeleted, new List() { RoleNames.Evne, RoleNames.ExportObjects }); + _entryRoleFilter.Add(TimelineEvent.EvneValueFileImport, new List() { RoleNames.Evne }); + // Kirja _entryRoleFilter.Add(TimelineEvent.KirjaPageCreated, new List() { RoleNames.Kirja }); _entryRoleFilter.Add(TimelineEvent.KirjaPageDeleted, new List() { RoleNames.Kirja }); diff --git a/Services/User/UserDeleter.cs b/Services/User/UserDeleter.cs index 9a2133b8..3e10d120 100644 --- a/Services/User/UserDeleter.cs +++ b/Services/User/UserDeleter.cs @@ -59,6 +59,11 @@ public class UserDeleter : IUserDeleter /// private readonly IEvneSkillImplementationSnapshotDbAccess _skillImplementationSnapshotDbAccess; + /// + /// Skill import field values log Db access + /// + private readonly IEvneImportFieldValuesLogDbAccess _skillImportFieldValuesLogDbAccess; + /// /// Npc Db Access /// @@ -74,6 +79,11 @@ public class UserDeleter : IUserDeleter /// private readonly IKortistoNpcImplementationSnapshotDbAccess _npcImplementationSnapshotDbAccess; + /// + /// Npc import field values log Db access + /// + private readonly IKortistoImportFieldValuesLogDbAccess _npcImportFieldValuesLogDbAccess; + /// /// Item Db Access /// @@ -89,6 +99,11 @@ public class UserDeleter : IUserDeleter /// private readonly IStyrItemImplementationSnapshotDbAccess _itemImplementationSnapshotDbAccess; + /// + /// Item import field values log Db access + /// + private readonly IStyrImportFieldValuesLogDbAccess _itemImportFieldValuesLogDbAccess; + /// /// Export Template Db Access /// @@ -179,12 +194,15 @@ public class UserDeleter : IUserDeleter /// Skill Db Access /// Skill Template Db Access /// Skill Implementation Snapshot Db Access + /// Skill import field values log Db access /// Npc Db Access /// Npc Template Db Access /// Npc Implementation Snapshot Db Access + /// Npc import field values log Db access /// Item Db Access /// Item Template Db Access /// Item Implementation Snapshot Db Access + /// Item import field values log Db access /// Export template Db access /// Include export template Db access /// Object Export snippet Db Access @@ -202,8 +220,9 @@ public class UserDeleter : IUserDeleter /// Timeline Db Access /// User manager public UserDeleter(IAikaQuestDbAccess questDbAccess, IAikaQuestImplementationSnapshotDbAccess questImplementationSnapshotDbAccess, IAikaChapterDetailDbAccess chapterDetailDbAccess, IAikaChapterOverviewDbAccess chapterOverviewDbAccess, IEvneSkillDbAccess skillDbAccess, - IEvneSkillTemplateDbAccess skillTemplateDbAccess, IEvneSkillImplementationSnapshotDbAccess skillImplementationSnapshotDbAccess, IKortistoNpcDbAccess npcDbAccess, IKortistoNpcTemplateDbAccess npcTemplateDbAccess, IKortistoNpcImplementationSnapshotDbAccess npcImplementationSnapshotDbAccess, - IStyrItemDbAccess itemDbAccess, IStyrItemTemplateDbAccess itemTemplateDbAccess, IStyrItemImplementationSnapshotDbAccess itemImplementationSnapshotDbAccess, IExportTemplateDbAccess exportTemplateDbAccess, IIncludeExportTemplateDbAccess includeExportTemplateDbAccess, + IEvneSkillTemplateDbAccess skillTemplateDbAccess, IEvneSkillImplementationSnapshotDbAccess skillImplementationSnapshotDbAccess, IEvneImportFieldValuesLogDbAccess skillImportFieldValuesLogDbAccess, IKortistoNpcDbAccess npcDbAccess, IKortistoNpcTemplateDbAccess npcTemplateDbAccess, + IKortistoNpcImplementationSnapshotDbAccess npcImplementationSnapshotDbAccess, IKortistoImportFieldValuesLogDbAccess npcImportFieldValuesLogDbAccess, IStyrItemDbAccess itemDbAccess, IStyrItemTemplateDbAccess itemTemplateDbAccess, + IStyrItemImplementationSnapshotDbAccess itemImplementationSnapshotDbAccess, IStyrImportFieldValuesLogDbAccess itemImportFieldValuesLogDbAccess, IExportTemplateDbAccess exportTemplateDbAccess, IIncludeExportTemplateDbAccess includeExportTemplateDbAccess, IObjectExportSnippetDbAccess objectExportSnippetDbAccess, IKartaMapDbAccess mapDbAccess, IKirjaPageDbAccess pageDbAccess, IKirjaPageVersionDbAccess pageVersionDbAccess, ITaleDbAccess taleDbAccess, ITaleDialogImplementationSnapshotDbAccess taleImplementationSnapshotDbAccess, IProjectConfigDbAccess projectConfigDbAccess, ITaskBoardDbAccess taskBoardDbAccess, ITaskGroupTypeDbAccess taskGroupTypeDbAccess, ITaskTypeDbAccess taskTypeDbAccess, IUserTaskBoardHistoryDbAccess userTaskBoardHistoryDbAccess, ILockServiceDbAccess lockDbService, ITimelineDbAccess timelineDbAccess, UserManager userManager) @@ -215,12 +234,15 @@ public UserDeleter(IAikaQuestDbAccess questDbAccess, IAikaQuestImplementationSna _skillDbAccess = skillDbAccess; _skillTemplateDbAccess = skillTemplateDbAccess; _skillImplementationSnapshotDbAccess = skillImplementationSnapshotDbAccess; + _skillImportFieldValuesLogDbAccess = skillImportFieldValuesLogDbAccess; _npcDbAccess = npcDbAccess; _npcTemplateDbAccess = npcTemplateDbAccess; _npcImplementationSnapshotDbAccess = npcImplementationSnapshotDbAccess; + _npcImportFieldValuesLogDbAccess = npcImportFieldValuesLogDbAccess; _itemDbAccess = itemDbAccess; _itemTemplateDbAccess = itemTemplateDbAccess; _itemImplementationSnapshotDbAccess = itemImplementationSnapshotDbAccess; + _itemImportFieldValuesLogDbAccess = itemImportFieldValuesLogDbAccess; _exportTemplateDbAccess = exportTemplateDbAccess; _includeExportTemplateDbAccess = includeExportTemplateDbAccess; _objectExportSnippetDbAccess = objectExportSnippetDbAccess; @@ -307,6 +329,8 @@ private async Task DeleteModifiedData(GoNorthUser user) await _skillImplementationSnapshotDbAccess.ResetSnapshotsByModifiedUser(user.Id); + await _skillImportFieldValuesLogDbAccess.ResetImportLogsByModifiedUser(user.Id); + List skillTemplates = await _skillTemplateDbAccess.GetFlexFieldObjectsByModifiedUser(user.Id); foreach(EvneSkill curSkill in skillTemplates) { @@ -328,6 +352,8 @@ private async Task DeleteModifiedData(GoNorthUser user) await _npcDbAccess.ResetRecycleBinFlexFieldObjectsByModifiedUser(user.Id); await _npcImplementationSnapshotDbAccess.ResetSnapshotsByModifiedUser(user.Id); + + await _npcImportFieldValuesLogDbAccess.ResetImportLogsByModifiedUser(user.Id); List npcTemplates = await _npcTemplateDbAccess.GetFlexFieldObjectsByModifiedUser(user.Id); foreach(KortistoNpc curNpc in npcTemplates) @@ -351,6 +377,8 @@ private async Task DeleteModifiedData(GoNorthUser user) await _itemImplementationSnapshotDbAccess.ResetSnapshotsByModifiedUser(user.Id); + await _itemImportFieldValuesLogDbAccess.ResetImportLogsByModifiedUser(user.Id); + List itemTemplates = await _itemTemplateDbAccess.GetFlexFieldObjectsByModifiedUser(user.Id); foreach(StyrItem curItem in itemTemplates) { diff --git a/Startup.cs b/Startup.cs index 66912895..0517b2cd 100644 --- a/Startup.cs +++ b/Startup.cs @@ -60,6 +60,7 @@ using GoNorth.Services.Export.Dialog.ActionRendering.Localization; using GoNorth.Services.Export.Dialog.ConditionRendering.Localization; using GoNorth.Services.Export.DailyRoutine; +using GoNorth.Services.CsvHandling; namespace GoNorth { @@ -227,6 +228,9 @@ public void ConfigureServices(IServiceCollection services) services.AddScoped(); services.AddScoped, GoNorthUserClaimsPrincipalFactory>(); + + services.AddTransient(); + services.AddTransient(); // Database services.AddScoped(); @@ -243,19 +247,22 @@ public void ConfigureServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); - + services.AddScoped(); + services.AddScoped(); services.AddScoped(); diff --git a/UITests/package-lock.json b/UITests/package-lock.json index d572ba41..ac9d2205 100644 --- a/UITests/package-lock.json +++ b/UITests/package-lock.json @@ -4,10 +4,25 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@types/mime-types": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.0.tgz", - "integrity": "sha1-nKUs2jY/aZxpRmwqbM2q2RPqenM=" + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" + }, + "@types/node": { + "version": "14.11.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz", + "integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==", + "optional": true + }, + "@types/yauzl": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", + "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", + "optional": true, + "requires": { + "@types/node": "*" + } }, "agent-base": { "version": "5.1.1", @@ -15,9 +30,9 @@ "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==" }, "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==" + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" }, "ansi-gray": { "version": "0.1.1", @@ -34,11 +49,12 @@ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "requires": { - "color-convert": "^1.9.0" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, "ansi-wrap": { @@ -188,6 +204,17 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "array.prototype.map": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz", + "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.4" + } + }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -212,11 +239,6 @@ "integrity": "sha1-tyfb+H12UWAvBvTUrDh/R9kbDL8=", "dev": true }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, "async-settle": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", @@ -309,12 +331,39 @@ } } }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, "binary-extensions": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", "integrity": "sha1-WYr+VHVbKGilMw0q/51Ou1Mgm2U=", "dev": true }, + "bl": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", + "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -358,6 +407,15 @@ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" }, + "buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -372,7 +430,8 @@ "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=" + "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=", + "dev": true }, "cache-base": { "version": "1.0.1", @@ -397,23 +456,12 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, "charenc": { @@ -449,6 +497,11 @@ } } }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -487,6 +540,27 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -574,17 +648,17 @@ } }, "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "requires": { - "color-name": "1.1.3" + "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "color-support": { "version": "1.1.3", @@ -607,6 +681,7 @@ "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", "integrity": "sha1-kEvfGUzTEi/Gdcd/xKw9T/D9GjQ=", + "dev": true, "requires": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", @@ -642,7 +717,8 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true }, "crypt": { "version": "0.0.2", @@ -756,10 +832,15 @@ "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", "dev": true }, + "devtools-protocol": { + "version": "0.0.799653", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.799653.tgz", + "integrity": "sha512-t1CcaZbvm8pOlikqrsIM9GOa7Ipp07+4h/q9u0JXBWjPCjHdBl9KkddX87Vv9vBHoBGtwV79sYQNGnQM6iS5gg==" + }, "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" }, "duplexify": { "version": "3.7.1", @@ -792,7 +873,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha1-7SljTRm6ukY7bOa4CjchPqtx7EM=", - "dev": true, "requires": { "once": "^1.4.0" } @@ -807,27 +887,58 @@ } }, "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", "object-inspect": "^1.7.0", "object-keys": "^1.1.1", "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + } + } + }, + "es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" + }, + "es-get-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", + "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", + "requires": { + "es-abstract": "^1.17.4", + "has-symbols": "^1.0.1", + "is-arguments": "^1.0.4", + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-string": "^1.0.5", + "isarray": "^2.0.5" }, "dependencies": { "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" } } }, @@ -886,9 +997,9 @@ } }, "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, "esprima": { "version": "4.0.1", @@ -1047,36 +1158,28 @@ } }, "extract-zip": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", - "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "requires": { - "concat-stream": "^1.6.2", - "debug": "^2.6.9", - "mkdirp": "^0.5.4", + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "dependencies": { "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "requires": { - "minimist": "^1.2.5" + "ms": "2.1.2" } }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, @@ -1124,11 +1227,12 @@ } }, "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "requires": { - "locate-path": "^3.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" } }, "findup-sync": { @@ -1204,6 +1308,11 @@ "map-cache": "^0.2.2" } }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "fs-mkdirp-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", @@ -1781,7 +1890,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "dev": true, "requires": { "pump": "^3.0.0" } @@ -2082,9 +2190,9 @@ } }, "gulp-zip": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/gulp-zip/-/gulp-zip-5.0.1.tgz", - "integrity": "sha512-M/IWLh9RvOpuofDZkgDirtiyz9J3yIqnDOJ3muzk2D/XnZ1ruqPlPLRIpXnl/aZU+xXwKPdOIxjRzkUcVEQyZQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/gulp-zip/-/gulp-zip-5.0.2.tgz", + "integrity": "sha512-rZd0Ppuc8Bf7J2/WzcdNaeb+lcEXf1R8mV/PJ9Kdu7PmnInWVeLSmiXIka/2QSe6uhAsGVFAMffWSaMzAPGTBg==", "dev": true, "requires": { "get-stream": "^5.1.0", @@ -2095,11 +2203,12 @@ }, "dependencies": { "through2": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", "dev": true, "requires": { + "inherits": "^2.0.4", "readable-stream": "2 || 3" } } @@ -2123,9 +2232,9 @@ } }, "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "has-symbols": { "version": "1.0.0", @@ -2200,15 +2309,25 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -2271,6 +2390,11 @@ } } }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -2292,9 +2416,9 @@ "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" }, "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" }, "is-data-descriptor": { "version": "0.1.4", @@ -2370,6 +2494,11 @@ "is-extglob": "^2.1.1" } }, + "is-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", + "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==" + }, "is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", @@ -2402,6 +2531,11 @@ } } }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -2412,11 +2546,18 @@ } }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "requires": { - "has": "^1.0.3" + "has-symbols": "^1.0.1" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + } } }, "is-relative": { @@ -2428,6 +2569,16 @@ "is-unc-path": "^1.0.0" } }, + "is-set": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", + "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==" + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==" + }, "is-symbol": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", @@ -2473,7 +2624,8 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true }, "isexe": { "version": "2.0.0", @@ -2486,10 +2638,24 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, + "iterate-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz", + "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==" + }, + "iterate-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", + "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", + "requires": { + "es-get-iterator": "^1.0.2", + "iterate-iterator": "^1.0.1" + } + }, "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -2571,12 +2737,11 @@ } }, "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "^5.0.0" } }, "lodash": { @@ -2585,11 +2750,11 @@ "integrity": "sha512-+CiwtLnsJhX03p20mwXuvhoebatoh5B3tt+VvYlrPgZC1g36y+RRbkufX95Xa+X4I59aWEacDFYwnJZiyBh9gA==" }, "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "requires": { - "chalk": "^2.4.2" + "chalk": "^4.0.0" } }, "make-iterator": { @@ -2652,19 +2817,19 @@ } }, "md5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", - "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", "requires": { - "charenc": "~0.0.1", - "crypt": "~0.0.1", - "is-buffer": "~1.1.1" + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" }, "dependencies": { "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=" } } }, @@ -2689,24 +2854,6 @@ "to-regex": "^3.0.2" } }, - "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" - }, - "mime-db": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", - "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" - }, - "mime-types": { - "version": "2.1.26", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", - "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", - "requires": { - "mime-db": "1.43.0" - } - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -2742,42 +2889,48 @@ } }, "mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "requires": { "minimist": "^1.2.5" } }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, "mocha": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.1.tgz", - "integrity": "sha512-3qQsu3ijNS3GkWcccT5Zw0hf/rWvu1fTN9sPvEd81hlwsr30GX2GcDSSoBxo24IR8FelmrAydGC6/1J5QQP4WA==", + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.3.tgz", + "integrity": "sha512-ZbaYib4hT4PpF4bdSO2DohooKXIn4lDeiYqB+vTmCdr6l2woW0b6H3pf5x4sM5nwQMru9RvjjHYWVGltR50ZBw==", "requires": { - "ansi-colors": "3.2.3", + "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.3.0", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", + "chokidar": "3.4.2", + "debug": "4.1.1", + "diff": "4.0.2", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", "growl": "1.10.5", "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", + "js-yaml": "3.14.0", + "log-symbols": "4.0.0", "minimatch": "3.0.4", - "mkdirp": "0.5.3", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", + "ms": "2.1.2", "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", + "promise.allsettled": "1.0.2", + "serialize-javascript": "4.0.0", + "strip-json-comments": "3.0.1", + "supports-color": "7.1.0", + "which": "2.0.2", "wide-align": "1.1.3", + "workerpool": "6.0.0", "yargs": "13.3.2", "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" + "yargs-unparser": "1.6.1" }, "dependencies": { "anymatch": { @@ -2790,9 +2943,9 @@ } }, "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==" }, "braces": { "version": "3.0.2", @@ -2803,18 +2956,26 @@ } }, "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", + "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", "requires": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.1.1", + "fsevents": "~2.1.2", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" + "readdirp": "~3.4.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" } }, "fill-range": { @@ -2826,11 +2987,24 @@ } }, "fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", "optional": true }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "glob-parent": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", @@ -2852,17 +3026,22 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, "readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", "requires": { - "picomatch": "^2.0.4" + "picomatch": "^2.2.1" } }, "to-regex-range": { @@ -2872,13 +3051,21 @@ "requires": { "is-number": "^7.0.0" } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } } } }, "mocha-junit-reporter": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-1.23.3.tgz", - "integrity": "sha512-ed8LqbRj1RxZfjt/oC9t12sfrWsjZ3gNnbhV1nuj9R/Jb5/P3Xb4duv2eCfCDMYH+fEu0mqca7m4wsiVjsxsvA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-2.0.0.tgz", + "integrity": "sha512-20HoWh2HEfhqmigfXOKUhZQyX23JImskc37ZOhIjBKoBEsb+4cAFRJpAVhFpnvsztLklW/gFVzsrobjLwmX4lA==", "requires": { "debug": "^2.2.0", "md5": "^2.1.0", @@ -2890,7 +3077,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", "requires": { "ms": "2.0.0" } @@ -2954,15 +3141,6 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, - "node-environment-flags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", - "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", - "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - } - }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -3037,9 +3215,9 @@ } }, "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" }, "object-keys": { "version": "1.1.1", @@ -3078,15 +3256,6 @@ "isobject": "^3.0.0" } }, - "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, "object.map": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", @@ -3134,19 +3303,19 @@ } }, "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", + "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", "requires": { "p-try": "^2.0.0" } }, "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "requires": { - "p-limit": "^2.0.0" + "p-limit": "^3.0.2" } }, "p-try": { @@ -3199,9 +3368,9 @@ "dev": true }, "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" }, "path-is-absolute": { "version": "1.0.1", @@ -3271,10 +3440,53 @@ "pinkie": "^2.0.0" } }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, "plugin-error": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", - "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "integrity": "sha1-dwFr2JGdCsN3/c3QMiMolTyleBw=", "dev": true, "requires": { "ansi-colors": "^1.0.1", @@ -3286,7 +3498,7 @@ "ansi-colors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", - "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "integrity": "sha1-Y3S03V1HGP884npnGjscrQdxMqk=", "dev": true, "requires": { "ansi-wrap": "^0.1.0" @@ -3309,13 +3521,26 @@ "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha1-eCDZsWEgzFXKmud5JoCufbptf+I=" + "integrity": "sha1-eCDZsWEgzFXKmud5JoCufbptf+I=", + "dev": true }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" }, + "promise.allsettled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz", + "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==", + "requires": { + "array.prototype.map": "^1.0.1", + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "iterate-value": "^1.0.0" + } + }, "proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -3324,8 +3549,7 @@ "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, + "integrity": "sha1-tKIRaBW94vTh6mAjVOjHVWUQemQ=", "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -3355,32 +3579,46 @@ } }, "puppeteer": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-2.1.1.tgz", - "integrity": "sha512-LWzaDVQkk1EPiuYeTOj+CZRIjda4k2s5w4MK4xoH2+kgWV/SDlkYHmxatDdtYrciHUKSXTsGgPgPP8ILVdBsxg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-5.3.1.tgz", + "integrity": "sha512-YTM1RaBeYrj6n7IlRXRYLqJHF+GM7tasbvrNFx6w1S16G76NrPq7oYFKLDO+BQsXNtS8kW2GxWCXjIMPvfDyaQ==", "requires": { - "@types/mime-types": "^2.1.0", "debug": "^4.1.0", - "extract-zip": "^1.6.6", + "devtools-protocol": "0.0.799653", + "extract-zip": "^2.0.0", "https-proxy-agent": "^4.0.0", - "mime": "^2.0.3", - "mime-types": "^2.1.25", + "pkg-dir": "^4.2.0", "progress": "^2.0.1", "proxy-from-env": "^1.0.0", - "rimraf": "^2.6.1", - "ws": "^6.1.0" + "rimraf": "^3.0.2", + "tar-fs": "^2.0.0", + "unbzip2-stream": "^1.3.3", + "ws": "^7.2.3" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", @@ -3427,6 +3665,7 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3582,9 +3821,9 @@ "dev": true }, "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "requires": { "glob": "^7.1.3" } @@ -3606,7 +3845,8 @@ "semver": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha1-eQp89v6lRZuslhELKbYEEtyP+Ws=" + "integrity": "sha1-eQp89v6lRZuslhELKbYEEtyP+Ws=", + "dev": true }, "semver-greatest-satisfied-range": { "version": "1.1.0", @@ -3617,6 +3857,14 @@ "sver-compat": "^1.5.0" } }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "requires": { + "randombytes": "^2.1.0" + } + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -3907,26 +4155,6 @@ "es-abstract": "^1.17.5" } }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - } - }, "string.prototype.trimstart": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", @@ -3962,16 +4190,16 @@ } }, "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==" }, "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } }, "sver-compat": { @@ -3984,6 +4212,46 @@ "es6-symbol": "^3.1.1" } }, + "tar-fs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.0.tgz", + "integrity": "sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg==", + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.0.0" + } + }, + "tar-stream": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.4.tgz", + "integrity": "sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw==", + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -4086,7 +4354,17 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } }, "unc-path-regex": { "version": "0.1.2", @@ -4285,6 +4563,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", + "dev": true, "requires": { "isexe": "^2.0.0" } @@ -4302,6 +4581,11 @@ "string-width": "^1.0.2 || 2" } }, + "workerpool": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz", + "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==" + }, "wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", @@ -4355,12 +4639,9 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "requires": { - "async-limiter": "~1.0.0" - } + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" }, "xml": { "version": "1.0.1", @@ -4400,6 +4681,44 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -4430,19 +4749,104 @@ } }, "yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz", + "integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==", "requires": { + "camelcase": "^5.3.1", + "decamelize": "^1.2.0", "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" + "is-plain-obj": "^1.1.0", + "yargs": "^14.2.3" }, "dependencies": { - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "yargs": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", + "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.1" + } + }, + "yargs-parser": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", + "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } }, @@ -4458,7 +4862,7 @@ "yazl": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", - "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "integrity": "sha1-o9ZdPdZZpbCTeFDoYJ8i//orXDU=", "dev": true, "requires": { "buffer-crc32": "~0.2.3" diff --git a/UITests/package.json b/UITests/package.json index 20430142..c957f7fa 100644 --- a/UITests/package.json +++ b/UITests/package.json @@ -14,14 +14,14 @@ "author": "Steffen Nörtershäuser", "license": "MIT", "dependencies": { - "mocha": "^7.1.1", - "mocha-junit-reporter": "^1.23.3", + "mocha": "^8.1.3", + "mocha-junit-reporter": "^2.0.0", "mocha-multi-reporters": "^1.1.7", - "puppeteer": "^2.1.1" + "puppeteer": "^5.3.1" }, "devDependencies": { "gulp": "^4.0.2", - "gulp-zip": "^5.0.1", + "gulp-zip": "^5.0.2", "minimist": "^1.2.5" } } diff --git a/UITests/test/basicTests/evneTest.js b/UITests/test/basicTests/evneTest.js index 20442140..fc576e7f 100644 --- a/UITests/test/basicTests/evneTest.js +++ b/UITests/test/basicTests/evneTest.js @@ -25,7 +25,7 @@ describe("Evne", function () { testBed.assertNoErrorsOccured(); }); - it("should be able to use command buttons", async function() { + it("should be able to use paging buttons", async function() { var testBed = this.test.testBed; await testBed.navigateByUrl("/Evne"); @@ -42,6 +42,14 @@ describe("Evne", function () { assert.notStrictEqual(oldContent, newContent, "Content not changed after loading next overview page"); } + testBed.assertNoErrorsOccured(); + }); + + it("should be able to use command buttons", async function() { + var testBed = this.test.testBed; + + await testBed.navigateByUrl("/Evne"); + await testBed.clickElement("#gn-flexFieldOverviewCreateObjectButton"); await testBed.checkElementVisibleOnPage("#gn-flexFieldOverviewCreateObjectDropdown"); @@ -50,6 +58,16 @@ describe("Evne", function () { await testBed.clickElement("#gn-flexFieldFolderCreateEditDialogCancel"); await testBed.checkElementHiddenOnPage("#gn-flexFieldFolderCreateEditDialog"); + await testBed.clickElement("#gn-flexFieldValueExportButton"); + await testBed.checkElementVisibleOnPage("#gn-flexFieldValueExportDialog"); + await testBed.clickElement("#gn-flexFieldValueExportDialogCancelButton"); + await testBed.checkElementHiddenOnPage("#gn-flexFieldValueExportDialog"); + + await testBed.clickElement("#gn-flexFieldValueImportButton"); + await testBed.checkElementVisibleOnPage("#gn-flexFieldValueImportDialog"); + await testBed.clickElement("#gn-flexFieldValueImportDialogCancelButton"); + await testBed.checkElementHiddenOnPage("#gn-flexFieldValueImportDialog"); + testBed.assertNoErrorsOccured(); }); diff --git a/UITests/test/basicTests/kortistoTest.js b/UITests/test/basicTests/kortistoTest.js index 365389ff..4c6d9e5b 100644 --- a/UITests/test/basicTests/kortistoTest.js +++ b/UITests/test/basicTests/kortistoTest.js @@ -25,7 +25,7 @@ describe("Kortisto", function () { testBed.assertNoErrorsOccured(); }); - it("should be able to use command buttons", async function() { + it("should be able to use paging buttons", async function() { var testBed = this.test.testBed; await testBed.navigateByUrl("/Kortisto"); @@ -42,6 +42,14 @@ describe("Kortisto", function () { assert.notStrictEqual(oldContent, newContent, "Content not changed after loading next overview page"); } + testBed.assertNoErrorsOccured(); + }); + + it("should be able to use command buttons", async function() { + var testBed = this.test.testBed; + + await testBed.navigateByUrl("/Kortisto"); + await testBed.clickElement("#gn-flexFieldOverviewCreateObjectButton"); await testBed.checkElementVisibleOnPage("#gn-flexFieldOverviewCreateObjectDropdown"); @@ -49,6 +57,16 @@ describe("Kortisto", function () { await testBed.checkElementVisibleOnPage("#gn-flexFieldFolderCreateEditDialog"); await testBed.clickElement("#gn-flexFieldFolderCreateEditDialogCancel"); await testBed.checkElementHiddenOnPage("#gn-flexFieldFolderCreateEditDialog"); + + await testBed.clickElement("#gn-flexFieldValueExportButton"); + await testBed.checkElementVisibleOnPage("#gn-flexFieldValueExportDialog"); + await testBed.clickElement("#gn-flexFieldValueExportDialogCancelButton"); + await testBed.checkElementHiddenOnPage("#gn-flexFieldValueExportDialog"); + + await testBed.clickElement("#gn-flexFieldValueImportButton"); + await testBed.checkElementVisibleOnPage("#gn-flexFieldValueImportDialog"); + await testBed.clickElement("#gn-flexFieldValueImportDialogCancelButton"); + await testBed.checkElementHiddenOnPage("#gn-flexFieldValueImportDialog"); testBed.assertNoErrorsOccured(); }); diff --git a/UITests/test/basicTests/styrTest.js b/UITests/test/basicTests/styrTest.js index 92b3c363..b32ba564 100644 --- a/UITests/test/basicTests/styrTest.js +++ b/UITests/test/basicTests/styrTest.js @@ -25,7 +25,7 @@ describe("Styr", function () { testBed.assertNoErrorsOccured(); }); - it("should be able to use command buttons", async function() { + it("should be able to use paging buttons", async function() { var testBed = this.test.testBed; await testBed.navigateByUrl("/Styr"); @@ -42,6 +42,14 @@ describe("Styr", function () { assert.notStrictEqual(oldContent, newContent, "Content not changed after loading next overview page"); } + testBed.assertNoErrorsOccured(); + }); + + it("should be able to use command buttons", async function() { + var testBed = this.test.testBed; + + await testBed.navigateByUrl("/Styr"); + await testBed.clickElement("#gn-flexFieldOverviewCreateObjectButton"); await testBed.checkElementVisibleOnPage("#gn-flexFieldOverviewCreateObjectDropdown"); @@ -50,6 +58,16 @@ describe("Styr", function () { await testBed.clickElement("#gn-flexFieldFolderCreateEditDialogCancel"); await testBed.checkElementHiddenOnPage("#gn-flexFieldFolderCreateEditDialog"); + await testBed.clickElement("#gn-flexFieldValueExportButton"); + await testBed.checkElementVisibleOnPage("#gn-flexFieldValueExportDialog"); + await testBed.clickElement("#gn-flexFieldValueExportDialogCancelButton"); + await testBed.checkElementHiddenOnPage("#gn-flexFieldValueExportDialog"); + + await testBed.clickElement("#gn-flexFieldValueImportButton"); + await testBed.checkElementVisibleOnPage("#gn-flexFieldValueImportDialog"); + await testBed.clickElement("#gn-flexFieldValueImportDialogCancelButton"); + await testBed.checkElementHiddenOnPage("#gn-flexFieldValueImportDialog"); + testBed.assertNoErrorsOccured(); }); diff --git a/Views/FlexFieldDatabase/Overview.cshtml b/Views/FlexFieldDatabase/Overview.cshtml index afaba1ae..1359d4d5 100644 --- a/Views/FlexFieldDatabase/Overview.cshtml +++ b/Views/FlexFieldDatabase/Overview.cshtml @@ -122,6 +122,14 @@ + + @await Html.PartialAsync("~/Views/FlexFieldDatabase/ValueExportDialog.cshtml") + + + + @await Html.PartialAsync("~/Views/FlexFieldDatabase/ValueImportDialog.cshtml") + + @@ -148,6 +156,9 @@ @Model.Localizer["LevelBack"] + @Model.Localizer["FieldValueExport"] + @Model.Localizer["FieldValueImport"] + diff --git a/Views/FlexFieldDatabase/ValueExportDialog.cshtml b/Views/FlexFieldDatabase/ValueExportDialog.cshtml new file mode 100644 index 00000000..0e35020b --- /dev/null +++ b/Views/FlexFieldDatabase/ValueExportDialog.cshtml @@ -0,0 +1,49 @@ +@using Microsoft.AspNetCore.Mvc.Localization + +@inject IViewLocalizer Localizer + + + + + + × + @Localizer["ExportFieldValues"] + + + + @Localizer["AllObjectsWillBeExported"] + @Localizer["OnlyObjectsUnderneathFolderPrefix"]@Localizer["OnlyObjectsUnderneathFolderPostfix"] + + + @Localizer["Template"] + + + + + + + + + @Localizer["Fields"] + @Localizer["NoFieldsFound"] + + + + + @Localizer["SingleLineField"] + @Localizer["MultiLineField"] + @Localizer["NumberField"] + @Localizer["OptionField"] + + + + + + + + + + \ No newline at end of file diff --git a/Views/FlexFieldDatabase/ValueImportDialog.cshtml b/Views/FlexFieldDatabase/ValueImportDialog.cshtml new file mode 100644 index 00000000..28f2e8fb --- /dev/null +++ b/Views/FlexFieldDatabase/ValueImportDialog.cshtml @@ -0,0 +1,143 @@ +@using Microsoft.AspNetCore.Mvc.Localization + +@inject IViewLocalizer Localizer + + + + + + + + × + @Localizer["ImportFieldValues"] + + + + @Localizer["Error"] @Localizer["ErrorOccured"] + + @Localizer["DropValueFileHere"] + + + + @Localizer["ExistingRows"] + + + + + @Localizer["NewRows"] + + @Localizer["ObjectsWillBeImportedToRoot"] + @Localizer["ObjectsWillBeImportedIntoFolderPrefix"]@Localizer["ObjectsWillBeImportedIntoFolderPostfix"] + + + + + + + + @Localizer["ExistingRows"] + + + + + @Localizer["NewRows"] + + + + + + @Localizer["PreviousImports"] + + + + + @Localizer["ImportedOn"] + @Localizer["Filename"] + + + + + + + + + + + + + + + + + + + + + + + + + @Localizer["ExistingRows"] + + + + + @Localizer["NewRows"] + + + + + + + + + + + + \ No newline at end of file diff --git a/Views/Shared/_Layout.cshtml b/Views/Shared/_Layout.cshtml index bfbc9ef7..89484d5a 100644 --- a/Views/Shared/_Layout.cshtml +++ b/Views/Shared/_Layout.cshtml @@ -144,11 +144,11 @@ - - diff --git a/Views/Shared/_ValidationScriptsPartial.cshtml b/Views/Shared/_ValidationScriptsPartial.cshtml index 08010e6b..f354b9a6 100644 --- a/Views/Shared/_ValidationScriptsPartial.cshtml +++ b/Views/Shared/_ValidationScriptsPartial.cshtml @@ -8,11 +8,11 @@ -
+ @Localizer["AllObjectsWillBeExported"] + @Localizer["OnlyObjectsUnderneathFolderPrefix"]@Localizer["OnlyObjectsUnderneathFolderPostfix"] +
+ @Localizer["ObjectsWillBeImportedToRoot"] + @Localizer["ObjectsWillBeImportedIntoFolderPrefix"]@Localizer["ObjectsWillBeImportedIntoFolderPostfix"] +