episerver / netcore-preview

This repository is a preview providing early access to the latest Optimizely (formerly Episerver) product packages targeting .NET 5.
43 stars 9 forks source link

A module without module.config #17

Closed marisks closed 3 years ago

marisks commented 3 years ago

Is there a way to configure a module without module.config and without module directory under modules/_protected?

As of now, you can create your module as a Razor Class Library that has all views compiled and resources embedded, there is no need for the configuration in a config file and no need for an empty folder under modules/_protected. It would be nice if we could configure modules in a Startup.

I have tried this way:

public static IServiceCollection AddMyModule(this IServiceCollection services)
{
    services.AddCmsUI();
    services.Configure<ProtectedModuleOptions>(
        pm =>
        {
            if (!pm.Items.Any(i => i.Name.Equals(Constants.ModuleName, StringComparison.OrdinalIgnoreCase)))
            {
                pm.Items.Add(new ModuleDetails
                {
                    Name = Constants.ModuleName,
                    Assemblies = { Constants.ModuleName }
                });
            }
        });
    return services;
}

Tried the same but without providing assemblies too but I still got an exception on runtime when getting the URL of a module Paths.ToResource(GetType(), "container"); for the menu provider:

ArgumentException: Unable to find a module by assembly 'MyModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' (Parameter 'moduleAssembly')
EPiServer.Shell.Paths.ToResource(Assembly moduleAssembly, string moduleRelativeResourcePath)
EPiServer.Shell.Paths.ToResource(Type typeInModuleAssembly, string moduleRelativeResourcePath)
MyModule.MenuProvider.GetMenuItems() in MenuProvider.cs
+
            var url = Paths.ToResource(GetType(), "container");
EPiServer.Shell.Navigation.MenuAssembler+<>c.<GetMenuItems>b__8_1(IMenuProvider m)
System.Linq.Enumerable+SelectManySingleSelectorIterator<TSource, TResult>.MoveNext()
EPiServer.Shell.Navigation.MenuAssembler.GetMenuItems(string parentPath, int relativeDepth)

And in the log I see this:

2021-02-24 10:00:53.216 +02:00 [WRN] The modules finder couldn't find a directory at '~/EPiServer/MyModule'
lunchin commented 3 years ago

Thanks for the report, seems reasonable to not have to have a module.config if using the options class. I will add a bug to our internal tracking

lunchin commented 3 years ago

We have added the feature request but will not be prioritized until after GA. In the meantime you can register module provider to get around. Here is the one for find that pullls from embedded resource.

using EPiServer.Find.Cms;
using EPiServer.Find.Framework;
using EPiServer.Shell.Configuration;
using EPiServer.Shell.Modules;
using EPiServer.Web;
using System.Collections.Generic;
using System.Reflection;

namespace EPiServer.Find.UI
{
    /// <summary>
    /// Find module provider
    /// </summary>
    [ModuleProvider]
    public class FindModuleProvider : IModuleProvider
    {
        private readonly ProtectedModuleOptions _protectedModuleOptions;

        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="protectedModuleOptions"></param>
        public FindModuleProvider(ProtectedModuleOptions protectedModuleOptions)
        {
            _protectedModuleOptions = protectedModuleOptions;
        }

        public IEnumerable<ShellModule> GetModules()
        {
            return new List<ShellModule>() { GetFindModule() };
        }

        private ShellModule GetFindModule()
        {
            var moduleConfig = GetType().Assembly.GetManifestResourceStream("EPiServer.Find.UI.module.config");
            if (moduleConfig == null)
            {
                return null;
            }
            var manifest = ShellModuleManifest.Deserialize(moduleConfig);
            var rootPath = VirtualPathUtilityEx.AppendTrailingSlash(_protectedModuleOptions.RootPath);
            var normalizedRouteBase = rootPath.TrimStart('~', '/');
            var moduleName = "Find";

            var module = new ShellModule(moduleName, normalizedRouteBase, "");
            module.Manifest = manifest;
            module.Assemblies = new List<Assembly>()
            {
                GetType().Assembly,
                typeof(FindCmsOptions).Assembly,
                typeof(SearchClient).Assembly
            };

            return module;
        }
    }
}
lunchin commented 3 years ago

We have this prioritized after GA