// ---------- async Task <ICommandResultBase> InstallInternalAsync(IShellFeature feature) { // Validate if (feature == null) { throw new ArgumentNullException(nameof(feature)); } if (string.IsNullOrEmpty(feature.ModuleId)) { throw new ArgumentNullException(nameof(feature.ModuleId)); } var user = await _contextFacade.GetAuthenticatedUserAsync(); // Get all feature tags var categories = await _categoryStore.QueryAsync() .Select <CategoryQueryParams>(Q => { Q.FeatureId.Equals(feature.Id); }) .ToList(); // Associate every category with at least 1 entity var output = new CommandResultBase(); if (categories != null) { var entityFeature = await GetEntityFeatureAsync(feature); if (entityFeature == null) { return(output.Failed($"A feature named {feature.ModuleId.Replace(".Categories", "")} is not enabled!")); } // Get entities for feature var entities = await _entityStore.QueryAsync() .Select <EntityQueryParams>(q => { q.FeatureId.Equals(entityFeature.Id); q.CategoryId.Equals(0); }) .ToList(); // Keeps track of entities already added to categories var alreadyAdded = new Dictionary <int, Entity>(); // Interate categories building random entities // not already added to a category and adding // those random entities to the current category foreach (var category in categories?.Data) { // Get random entities var randomEntities = GetRandomEntities(entities?.Data, alreadyAdded); // Ensure we have some random entities, they may have already al been added if (randomEntities == null) { return(output.Success()); } // Add random entities to category foreach (var randomEntity in randomEntities) { // Get the full entity var entity = await _entityStore.GetByIdAsync(randomEntity.Id); // Update entity.CategoryId = category.Id; entity.ModifiedUserId = user?.Id ?? 0; entity.ModifiedDate = DateTime.UtcNow; // Persist var entityResult = await _entityManager.UpdateAsync(entity); if (entityResult.Succeeded) { // Add entity / category relationship var result = await _entityCategoryManager.CreateAsync(new EntityCategory() { EntityId = entityResult.Response.Id, CategoryId = category.Id, CreatedUserId = user?.Id ?? 0, CreatedDate = DateTime.UtcNow }); if (!result.Succeeded) { return(output.Failed(result.Errors.ToArray())); } } else { return(output.Failed(entityResult.Errors.ToArray())); } } } } return(output.Success()); }
// ---------- async Task <ICommandResultBase> InstallInternalAsync(IShellFeature feature) { // Validate if (feature == null) { throw new ArgumentNullException(nameof(feature)); } if (string.IsNullOrEmpty(feature.ModuleId)) { throw new ArgumentNullException(nameof(feature.ModuleId)); } var user = await _contextFacade.GetAuthenticatedUserAsync(); // Get all tags var tags = await _tagStore.QueryAsync() .Select <TagQueryParams>(Q => { Q.FeatureId.Equals(feature.Id); }) .ToList(); // Associate every tag with at least 1 entity var output = new CommandResultBase(); if (tags != null) { // Get all entities var entities = await _entityStore.QueryAsync() .Select <EntityQueryParams>(q => { q.FeatureId.Equals(feature.Id); }) .ToList(); var alreadyAdded = new Dictionary <int, Entity>(); foreach (var tag in tags?.Data) { var randomEntities = GetRandomEntities(entities?.Data, alreadyAdded); if (randomEntities == null) { return(output.Success()); } foreach (var randomEntity in randomEntities) { var result = await _entityTagManager.CreateAsync(new EntityTag() { EntityId = randomEntity.Id, TagId = tag.Id, CreatedUserId = user?.Id ?? 0, CreatedDate = DateTime.UtcNow }); if (!result.Succeeded) { return(output.Failed(result.Errors.ToArray())); } } } } return(output.Success()); }
// -------------------- async Task <ICommandResultBase> ApplyLatestHistoryPoint(Idea entity, IdeaComment reply) { // Get current user var user = await _contextFacade.GetAuthenticatedUserAsync(); // We need to be authenticated to make changes if (user == null) { return(await ResetEditDetails(entity, reply, user)); } // Get newest / most recent history entry var histories = await _entityHistoryStore.QueryAsync() .Take(1, false) .Select <EntityHistoryQueryParams>(q => { q.EntityId.Equals(entity.Id); q.EntityReplyId.Equals(reply?.Id ?? 0); }) .OrderBy("Id", OrderBy.Desc) .ToList(); // No history point, return success if (histories == null) { return(await ResetEditDetails(entity, reply, user)); } // No history point, return success if (histories.Data == null) { return(await ResetEditDetails(entity, reply, user)); } // No history point, return success if (histories.Data.Count == 0) { return(await ResetEditDetails(entity, reply, user)); } var history = histories.Data[0]; // No history available reset edit details if (history == null) { return(await ResetEditDetails(entity, reply, user)); } // Update edit details based on latest history point var result = new CommandResultBase(); if (reply != null) { reply.ModifiedUserId = user.Id; reply.ModifiedDate = DateTimeOffset.UtcNow; reply.EditedUserId = history.CreatedUserId; reply.EditedDate = history.CreatedDate; // Update reply to history point var updateResult = await _entityReplyStore.UpdateAsync(reply); if (updateResult != null) { return(result.Success()); } } else { entity.ModifiedUserId = user.Id; entity.ModifiedDate = DateTimeOffset.UtcNow; entity.EditedUserId = history.CreatedUserId; entity.EditedDate = history.CreatedDate; // Update entity to history point var updateResult = await _entityStore.UpdateAsync(entity); if (updateResult != null) { return(result.Success()); } } return(result.Success()); }
async Task <ICommandResultBase> InstallEntityInternalAsync(SampleDataDescriptor descriptor, IList <User> users, int sortOrder = 0) { // Validate if (descriptor == null) { throw new ArgumentNullException(nameof(descriptor)); } if (string.IsNullOrEmpty(descriptor.ModuleId)) { throw new ArgumentNullException(nameof(descriptor.ModuleId)); } // Our result var result = new CommandResultBase(); // Ensure the feature is enabled var feature = await _featureFacade.GetFeatureByIdAsync(descriptor.ModuleId); if (feature == null) { return(result.Failed($"The feature {descriptor.ModuleId} is not enabled!")); } // Get a random user for the post var randomUser = users[_random.Next(0, users.Count)]; // Capitalize the first character of our entity type var entityTypeCapitalized = char.ToUpper(descriptor.EntityType[0]).ToString() + descriptor.EntityType.Substring(1); // Create the post var entity = new Entity() { Title = $"Example {entityTypeCapitalized} {_random.Next(0, 2000).ToString()}", Message = GetEntityText(descriptor), FeatureId = feature?.Id ?? 0, SortOrder = sortOrder + 1, CreatedUserId = randomUser?.Id ?? 0, CreatedDate = DateTimeOffset.UtcNow }; // Create entity var entityResult = await _entityManager.CreateAsync(entity); if (entityResult.Succeeded) { var lastReplyId = string.Empty; var lastReplyUserName = string.Empty; var lastReplyMessage = string.Empty; // Create entity replies for (var i = 0; i < descriptor.EntityRepliesToCreate; i++) { randomUser = users[_random.Next(0, users.Count - 1)]; var message = GetReplyText(descriptor); message = message.Replace("{replyUserName}", randomUser?.UserName ?? ""); message = message.Replace("{lastReplyId}", lastReplyId ?? ""); message = message.Replace("{lastReplyMessage}", lastReplyMessage ?? ""); message = message.Replace("{lastReplyQuotedMessage}", lastReplyMessage.Replace(System.Environment.NewLine, System.Environment.NewLine + "> ") ?? ""); message = message.Replace("{lastReplyUserName}", lastReplyUserName ?? ""); message = message.Replace("{entityId}", entityResult.Response.Id.ToString() ?? ""); message = message.Replace("{entityTitle}", entityResult.Response.Title ?? ""); message = message.Replace("{entityUserName}", entityResult.Response.CreatedBy.UserName); message = message.Replace("{mentionSample}", BuildMentionSampleMarkUp()); message = message.Replace("{lastReplyUrl}", _contextFacade.GetRouteUrl(new RouteValueDictionary() { ["area"] = descriptor.ModuleId, ["controller"] = "Home", ["action"] = "Reply", ["opts.id"] = entityResult.Response.Id.ToString() ?? "", ["opts.alias"] = entityResult.Response.Alias.ToString() ?? "", ["opts.replyId"] = lastReplyId ?? "" })); // Create reply var reply = new EntityReply() { EntityId = entityResult.Response.Id, Message = message, CreatedUserId = randomUser?.Id ?? 0, CreatedDate = DateTimeOffset.UtcNow }; // Add reply var replyResult = await _entityReplyManager.CreateAsync(reply); if (!replyResult.Succeeded) { return(result.Failed()); } lastReplyId = replyResult.Response.Id.ToString(); lastReplyMessage = replyResult.Response.Message; lastReplyUserName = replyResult.Response.CreatedBy.UserName; } } else { return(result.Failed(result.Errors.ToArray())); } return(result.Success()); }
public async Task <ICommandResultBase> UpdateAsync(string moduleId) { var output = new CommandResultBase(); // Get installed feature var feature = await _featureFacade.GetFeatureByIdAsync(moduleId); // Ensure we found the feature if (feature == null) { return(output.Failed( $"A feature named '{moduleId}' could not be found within the ShellFeatures table.")); } // Get available module var module = await _shellDescriptorManager.GetFeatureAsync(moduleId); // Ensure we found the module if (module == null) { return(output.Failed($"A module named '{moduleId}' could not be found on the file system. Please ensure the '{moduleId}' module exists within the root/modules folder.")); } // ------------------------------------------------------------------ // 1. Check to ensure we have a newer version of the module // ------------------------------------------------------------------ var from = feature.Version.ToVersion(); var to = module.Descriptor.Version.ToVersion(); if (from == null) { return(output.Failed( $"Could not convert version for feature {feature.ModuleId} of {feature.Version} to a valid version. Please check the version within the shell features database table.")); } if (to == null) { return(output.Failed( $"Could not convert version for module {module.Descriptor.Id} of {module.Descriptor.Version} to a valid version. Please check the version within the modules manifest file.")); } // No newer version simply return if (from >= to) { return(output.Success()); } // ------------------------------------------------------------------ // 2. Check to ensure the module we are updating is compatible // with the current version of Plato we are running // ------------------------------------------------------------------ var modulePlatoVersion = module.Descriptor.PlatoVersion.ToVersion(); // Does the module have a Plato version defined? if (modulePlatoVersion != null) { // Get current plato version var currentPlatoVersion = _platoOptions.Value.Version.ToVersion(); if (currentPlatoVersion != null) { // Does the module require a newer version of Plato? if (modulePlatoVersion > currentPlatoVersion) { return(output.Failed( $"{moduleId} {module.Descriptor.Version} requires Plato {modulePlatoVersion.ToString()} whilst you are running Plato {currentPlatoVersion.ToString()}. Please upgrade to Plato {modulePlatoVersion.ToString()} and try updating {moduleId} again.")); } } } // ------------------------------------------------------------------ // 3. Invoke FeatureEventHandlers & database migrations // ------------------------------------------------------------------ var results = await InvokeFeatureEventHandlersAsync( feature, async context => { // The result to return var result = new CommandResultBase(); // Perform migrations from current installed feature version // to latest available version defined via the modules IMigrationProvider // All versions between from and to var versions = from.GetVersionsBetween(to); // Compile versions to search var versionsToSearch = versions != null ? versions?.ToList().Select(v => v.ToString()) : new List <string>(); // Build migrations for feature & versions var migrations = _migrationBuilder.BuildMigrations(moduleId, versionsToSearch.ToArray()); // Apply migrations var migrationResults = await migrations.ApplyMigrationsAsync(); // We may not have migrations if (migrationResults != null) { // Did any errors occur whilst applying the migration? if (migrationResults.Errors.Any()) { var errors = new List <CommandError>(); foreach (var error in migrationResults.Errors) { errors.Add(new CommandError(error.Message)); } return(result.Failed(errors.ToArray())); } } // If we reach this point everything went OK, Migrations applied // successfully and no errors occurred within the features update handlers // finally update the features version within the ShellFeatures table to reflect // the new version of the module we've just updated to, also update // shell descriptor to reflect version changes within dictionary store var updateResult = await UpdateShellFeatureVersionAsync(feature, module.Descriptor.Version); if (updateResult.Errors.Any()) { return(result.Failed(updateResult.Errors.ToArray())); } // Return success return(result.Success()); }); // Did any errors occur? var handlerErrors = results .Where(c => c.Value.Errors.Any()) .SelectMany(h => h.Value.Errors) .ToList(); if (handlerErrors.Count > 0) { var errors = new List <CommandError>(); foreach (var error in handlerErrors) { errors.Add(new CommandError(error.Value)); } return(output.Failed(errors.ToArray())); } // No errors, recycle shell context to apply updates RecycleShell(); return(output.Success()); }
// ------------------------ async Task <IDictionary <string, IFeatureEventContext> > InvokeFeatureEventHandlersAsync( IShellFeature feature, Func <IFeatureEventContext, Task <CommandResultBase> > configure) { var output = new CommandResultBase(); // Get available module var module = await _shellDescriptorManager.GetFeatureAsync(feature.ModuleId); // Raise updating & updated event handlers for features return(await InvokeFeatureEvents(new[] { feature }, async (context, handler) => { var contexts = new ConcurrentDictionary <string, IFeatureEventContext>(); try { // Ensure we only invoke handlers for the feature we are updating if (context.Feature.ModuleId.Equals(feature.ModuleId, StringComparison.OrdinalIgnoreCase)) { await handler.UpdatingAsync(context); } contexts.AddOrUpdate(context.Feature.ModuleId, context, (k, v) => { foreach (var error in context.Errors) { v.Errors.Add(error.Key, error.Value); } return v; }); } catch (Exception e) { contexts.AddOrUpdate(context.Feature.ModuleId, context, (k, v) => { v.Errors.Add(context.Feature.ModuleId, e.Message); return v; }); } // Did any event encounter errors? var hasErrors = contexts .Where(c => c.Value.Errors.Any()); // No errors raise UpdatedAsync if (!hasErrors.Any()) { // Execute upgrade configuration var configureResult = await configure(context); if (!configureResult.Errors.Any()) { try { // Ensure we only invoke handlers for the feature we've updated if (context.Feature.ModuleId.Equals(feature.ModuleId, StringComparison.OrdinalIgnoreCase)) { await handler.UpdatedAsync(context); } contexts.AddOrUpdate(context.Feature.ModuleId, context, (k, v) => { foreach (var error in context.Errors) { v.Errors.Add(error.Key, error.Value); } return v; }); } catch (Exception e) { contexts.AddOrUpdate(context.Feature.ModuleId, context, (k, v) => { v.Errors.Add(context.Feature.ModuleId, e.Message); return v; }); } } else { foreach (var error in configureResult.Errors) { if (context.Errors == null) { context.Errors = new Dictionary <string, string>(); } if (!context.Errors.ContainsKey(error.Code)) { context.Errors.Add(error.Code, error.Description); } } contexts.AddOrUpdate(context.Feature.ModuleId, context, (k, v) => { foreach (var error in configureResult.Errors) { if (!v.Errors.ContainsKey(error.Code)) { v.Errors.Add(error.Code, error.Description); } } return v; }); } } return contexts; }, async context => { var contexts = new ConcurrentDictionary <string, IFeatureEventContext>(); // Execute upgrade configuration var configureResult = await configure(context); if (configureResult.Errors.Any()) { foreach (var error in configureResult.Errors) { if (context.Errors == null) { context.Errors = new Dictionary <string, string>(); } if (!context.Errors.ContainsKey(error.Code)) { context.Errors.Add(error.Code, error.Description); } } contexts.AddOrUpdate(context.Feature.ModuleId, context, (k, v) => { foreach (var error in configureResult.Errors) { if (!v.Errors.ContainsKey(error.Code)) { v.Errors.Add(error.Code, error.Description); } } return v; }); } return contexts; })); }