cofoundry-cms / cofoundry

Cofoundry is an extensible and flexible .NET Core CMS & application framework focusing on code first development
https://www.cofoundry.org
MIT License
834 stars 145 forks source link

Admin Section: Creating a new custom module/section #317

Open mcockrellsana opened 5 years ago

mcockrellsana commented 5 years ago

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

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

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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>
HeyJoel commented 5 years ago

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:

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 commented 5 years ago

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