cofoundry icon indicating copy to clipboard operation
cofoundry copied to clipboard

Admin Section: Creating a new custom module/section

Open mcockrellsana opened this issue 6 years ago • 6 comments

Hey Joel,

Sorry if this question appears elsewhere, I searched through issues and couldn't find something specifically related. My question/issue revolves around implementing a brand new admin section/module. I've used the Cofoundry.Web.Admin project and Modules folder as references but have a few questions related to the JS side of things.

C#

  • We have created a new ModuleRegistration class inheriting from IStandardAngularModuleResgistration In our own Core project under /Admin/Modules/SchoolData/Bootstrap
public class SchoolDataImportModuleRegistration : IStandardAngularModuleRegistration
    {
        private readonly IAdminRouteLibrary _adminRouteLibrary;


        public SchoolDataImportModuleRegistration(IAdminRouteLibrary adminRouteLibrary)
        {
            _adminRouteLibrary = adminRouteLibrary;
        }
        public AdminModule GetModule()
        {
            var module = new AdminModule()
            {
                AdminModuleCode = "COFSCHDI",
                Title = "School Data Import",
                Description = "Import Schools Data .",
                MenuCategory = AdminModuleMenuCategory.Settings,
                PrimaryOrdering = AdminModuleMenuPrimaryOrdering.Last,
                Url = new SchoolDataImportRouteLibrary(new AdminSettings()).Details(),
                RestrictedToPermission = new SettingsAdminModulePermission()
            };

            return module;
        }

        public string RoutePrefix => SchoolDataImportRouteLibrary.RoutePrefix;
    }
  • Created a RouteLibrary for use in the URL property of the AdminModule above (not sure if this is correct convention here, but seemed the only way to create the correct routes) created under Core project Admin/Modules/SchoolData/Constants
public class SchoolDataImportRouteLibrary : ModuleRouteLibrary
    {
        public const string RoutePrefix = "schooldataimport";

        #region constructor

        public SchoolDataImportRouteLibrary(AdminSettings adminSettings)
            : base(adminSettings, RoutePrefix,RouteConstants.InternalModuleResourcePathPrefix)
        {
        }

        #endregion

        #region routes

        public string Details()
        {
            return AngularRoute();
        }

        #endregion
    }

Is there anything else C# wise we would need to do to register this module, it seems to be showing up in the correct place within settings, and allows us to click on it, obviously showing nothing because angular errors, because we don't have the proper JS framework set up yet.

JS

Here we have started to create some JS files from examples within the Cofoundry.Web.Admin project, they live within our Core project in Admin/Modules/SchoolData/Content and /Js (mirroring the structure used in Cofoundry Admin project)

The things we've noticed are:

  • In the Cofoundry project, there's compiled JS for _templates.js, the controllers, etc - is this something we will need to do as well, is there a more simplified way of doing all this?
  • My colleague mentioned that he thinks we may need to copy down the Shared folder in the Admin.Web project as he thinks some of the modules we are using as reference to write our own are dependent on that for loading.

I know there isn't documentation yet on how to do all this, but was hoping we might be overthinking this and re-working the wheel here when there are already things in place to take care of most of this or a different way to do this then you have done in the Admin.Web project for the Cofoundry admin modules.

mcockrellsana avatar May 14 '19 22:05 mcockrellsana

Another related question being, if we do create these JS files, without having to manually create the compiled minified output that gets embedded as a resource, how does one "build the js" for this? I see the cake script, and powershell script, etc in the Admin Web project, but i'm unsure how to utilize this in my own project which includes our custom admin modules, looking through the code for these, I don't see where it compiles the angular js and templates and embeds them... maybe I've missed something important in my search. I do apologize for the extensive questions, but hoping that as this will help me, might help others in the future.

mcockrellsana avatar May 14 '19 23:05 mcockrellsana

I see looking at the youtube plugin you've authored, we need to build ourselves a grunt file and build our files as such. This explains some things that were confusing me that I didn't notice were in the Build folder of the Admin.Web project, yikes, my brain is on fire today lol apologies. Let me know if i'm on the right track and if there's anything else i might need to be aware of.

mcockrellsana avatar May 15 '19 00:05 mcockrellsana

So as an update, i've been able to recreate everything down to a T from the module definitions provided in Cofoundry.Web.Admin. However, when clicking the link to my module, i'm getting errors stating it can't find my js files (they were built using grunt and the methods provided, the files look fine according to convention). However it's trying to load my module js from https://localhost:44352/admin/Cofoundry/Admin/Modules/Schooldataimportexport/Content/js/schooldataimportexport_templates.js

I'm unsure why it's adding CoFoundry to the url, and also, i can't seem to figure out where the files exist anyway.

I've made them embedded resources in my project, i've also included a class that inherits from IAssemblyResourceRegistration to let my embedded resources be picked up. I'm not sure what the issue is here, any thoughts?

mcockrellsana avatar May 15 '19 02:05 mcockrellsana

I'm happy to report that after a long battle, i've figured everything out that was needed, my main issue from before was casing in some of my module names, also adding a CoFoundry folder as top level to my Admin Modules as well as creating a IEmbeddedResourceRouteRegistration class.

I'll be happy to provide all the necessary code to make this happen as a "sort of" tutorial for those who might look to do the same once I have this fully working as an example, however, now i run into a new issue, understanding the angular components which you have built and used throughout the admin section. an Interesting problem i'm encountering is this described below with a screenshot and the template code:

Why are the p tag text not showing up here, also the buttons that are supposed to be below the Import File control?

Capture

And the supporting code:

<cms-page-actions>

    <cms-button class="main-cta"
                cms-text="Import"
                ng-click="vm.showForm(1)"
                ng-disabled="vm.showMode == 1"></cms-button>

    <cms-button class="main-cta"
                     cms-text="Export"
                     ng-click="vm.showForm(2)"
                     ng-disabled="vm.showMode == 2"></cms-button>

</cms-page-actions>


<cms-page-body cms-content-type="form" ng-show="vm.showMode == 1">
    <cms-form cms-name="importForm" ng-submit="">
        <cms-form-section cms-title="Import School Data">
            <p>
                THIS IS A TEST FOR IMPORT<br/>
                TODO: FILL THIS IN WITH VALID FORM FIELD FOR SELECTING A FILE (USE EXAMPLES FROM OTHER ADMIN SECTIONS THAT LET YOU SELECT A FILE)<br/>
                BELOW IS A TEST USING THE DOCUMENT UPLOAD CONTROL PROVIDED BY COFOUNDRY
            </p>
            <cms-form-field-document-upload cms-title="Import File"
                                            cms-model=""
                                            cms-load-state=""
                                            cms-change=""
                                            ng-required="true"></cms-form-field-document-upload>

            <div>
                <cms-button-submit class="main-cta" cms-text="Import Data"></cms-button-submit>
                <cms-button cms-text="Cancel"></cms-button>
            </div>
        </cms-form-section>
    </cms-form>
</cms-page-body>

<cms-page-body cms-content-type="form" ng-show="vm.showMode == 2">
    <cms-form-section cms-title="Export School Data">
        <p>
            THIS IS A TEST FOR EXPORT<br/>
            TODO: PERHAPS JUST HAVE A BUTTON HERE THAT THEN EXPORTS ALL DATA FROM THE SCHOOL STUFF
        </p>
    </cms-form-section>
</cms-page-body>

mcockrellsana avatar May 15 '19 05:05 mcockrellsana

Hi @mcockrellsana,

As you've probably worked out already, the admin panel is not well documented but is quite extensible. As a general rule the parts of Cofoundry that are documented should be relatively stable and will be well supported in terms of a migration path for future updates, whereas un-documented parts tend to be liable to change and may not have a documented migration path when we finalize the API.

With that warning out of the way, if you are a little adventurous and don't mind digging into the code to work things out then that's cool, I'll see if I can point you in the right direction!

I see you've worked out quite a lot of this but I'll just add some clarification to your questions for anyone who stumbles on this trying to do the same thing:

IStandardAngularModuleRegistration

You didn't necessarily need to create a ModuleRouteLibrary here, and instead you could hard code the route if you prefer to avoid creating the extra class. One thing to note here though is that you've used RouteConstants.InternalModuleResourcePathPrefix which is reserved for internal (Cofoundry) modules.

Because embedded resources are all consumed using relative paths from your application, we try and avoid conflicts by dividing module code into three categories:

  • Internal: Cofoundry modules, these use RouteConstants.InternalModuleResourcePathPrefix and are located in /Admin/Modules/
  • Plugins: Cofoundry plugin modules, these use RouteConstants.PluginModuleResourcePathPrefix and are located in /Plugins/Admin/Modules/
  • Local: Your own modules in your local project, these use RouteConstants.ModuleResourcePathPrefix and are located in /Cofoundry/Admin/Modules/

This means that, for example, if we later introduce a Cofoundry module with the same name as one of your local modules, then we don't conflict.

IAssemblyResourceRegistration

If you use RouteConstants.ModuleResourcePathPrefix and locate your module files in /Cofoundry/Admin/Modules/ in your local project then you shouldn't need to add a IAssemblyResourceRegistration. This is only necessary if you're using them in a separate project/assembly e.g. if you're creating a plugin.

Compiled files

The admin panel was original developed in .NET framework and made use of .NET bundling and minification, which made it really easy to drop in new source files and dynamically bundle them. When we ported to .NET Core this wasn't available and we switched to using a grunt build task to pre-compile the files.

I'm not really happy with how non-intuitive all this is to set up so we'd definitely be looking to improve this in the future.

For now, the error logging plugin is the best example to work to, although if you're creating a local project module you'd need to update the paths as you should be working from /Cofoundry/Admin/Modules/.

Missing elements

You'll need to wrap any ad-code html in your form into a cms-form-field-container control:

<cms-form-section cms-title="Import School Data">

    <cms-form-field-container>
        <p>
            THIS IS A TEST FOR IMPORT<br />
            TODO: FILL THIS IN WITH VALID FORM FIELD FOR SELECTING A FILE (USE EXAMPLES FROM OTHER ADMIN SECTIONS THAT LET YOU SELECT A FILE)<br />
            BELOW IS A TEST USING THE DOCUMENT UPLOAD CONTROL PROVIDED BY COFOUNDRY
        </p>
    </cms-form-field-container>

    <cms-form-field-document-upload cms-title="Import File"
                                    cms-model=""
                                    cms-load-state=""
                                    cms-change=""
                                    ng-required="true"></cms-form-field-document-upload>

    <cms-form-field-container cms-title="Titled content">
        <p>
            Ad-hoc content can have a title too
        </p>
    </cms-form-field-container>

</cms-form-section>

Working example

It would be great if you wanted to put together a working example. We've just not had the resource yet to focus on making the extensibility here as straightforward as it should be and it's likely we'd be looking at that further down road alongside a rebuild of the admin panel in a more up-to-date framework.

HeyJoel avatar May 15 '19 09:05 HeyJoel

TODO: Add documentation and samples for creating custom admin section.

HeyJoel avatar May 20 '19 07:05 HeyJoel