// ----------

        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());
        }
Example #2
0
        // ----------

        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());
        }
Example #3
0
        // --------------------

        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());
        }
Example #4
0
        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());
        }
Example #5
0
        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());
        }
Example #6
0
        // ------------------------

        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;
            }));
        }