Admin plugin / tool in CMS 12?

Vote:
 

Hi!

We have a plugin that show in the CMS 11 admin menu, I guess this should be moved to the Tools tab in admin for CMS 12 but I cannot figure out how to do that?
Has anyone added a link in the Tool menu in CMS 12?

Thanks!

/Kristoffer

#267983
Dec 07, 2021 16:43
Vote:
 

Yes, you need to create a menu provider

    [MenuProvider]
    public class CustomAdminMenuProvider : IMenuProvider
    {
        public CustomAdminMenuProvider()
        {
        }

        public IEnumerable<MenuItem> GetMenuItems()
        {
            var urlMenuItem1 = new UrlMenuItem("Custom Admin Page 1", MenuPaths.Global + "/cms/admin/examplesub1", "/CustomAdminPage/FunctionOne");
            urlMenuItem1.IsAvailable = context => true;
            urlMenuItem1.SortIndex = SortIndex.First + 100;
            urlMenuItem1.AuthorizationPolicy = CmsPolicyNames.CmsAdmin;

            var urlMenuItem2 = new UrlMenuItem("Custom Admin Page 2", MenuPaths.Global + "/cms/admin/examplesub2", "/CustomAdminPage/FunctionTwo");
            urlMenuItem1.IsAvailable = context => true;
            urlMenuItem1.SortIndex = SortIndex.First + 101;
            urlMenuItem1.AuthorizationPolicy = CmsPolicyNames.CmsAdmin;

            return new List<MenuItem>(3)
            {
                urlMenuItem1,
                urlMenuItem2
            };
        }
    }

Then hook it in to the controller path you defined in the UrlMenuItem above

[Authorize(Roles = "CmsAdmin,WebAdmins,Administrators")]
    [Route("[controller]")]
    public class CustomAdminPageController : Controller
    {
        // GET
        [Route("[action]")]
        public IActionResult FunctionOne()
        {
            return View("~/Views/CustomAdminPage/FunctionOne.cshtml", model);
        }
    }
#267984
Edited, Dec 07, 2021 16:55
Vote:
 

However this example just added them as a sub item under the admin menu

It doesn't add them to tools. That's the core tools items from Optimizely, I would personally create them in this way as tabs under admin as that's more how the UI had been built

#267986
Dec 07, 2021 17:00
Vote:
 

Now that worked as a charm, thanks Scott!

/Kristoffer

#268032
Dec 08, 2021 8:12
Vote:
 

Hi Scott,

It works for me when I add these directly to the CMS project. But I need to get this done by Add-on. Tried to build and copy DLLs and added controllers and views to the module folder as well.

But it is not rendering.

I have separate CMS instance project and Addon project in solution and i m coping DLLs from Addon project bin folder after building.

Do i need to zip. the module?

can you please point me to a single explanation or article on how to add the admin menu and page by add-on?

#271639
Edited, Feb 13, 2022 15:18
Vote:
 

Hi Nimesh,

Yes, best approach would be to create this as a zipped module. 

I recommend reviewing source code of Geta Notfoundhandler for an example of this can be done: https://github.com/Geta/geta-notfoundhandler

#272230
Feb 15, 2022 16:39
Vote:
 
#272237
Feb 15, 2022 22:22
Vote:
 

Hi thanks Guys,

I manage to get the menu item rendered in the admin area. but I'm having issues with routing. even though I have set the path in the menu item URL attribute, It does not work to render the view form my plugin. how can I set URLs to controller/action? I have index action in the AddonSettings controller.

 [MenuItem("/global/cms/admin/addon", Text = "Addon Settings", SortIndex = 60, Url = "/EPiServer/AddonSettings/index")]

Can anyone please mention a good article or source where these plugin-related routings are explained? I see different ways in documentation as well so those are a bit unclear. I'm looking for a clear explanation about these areas

  1. Configuring routing for an addon in CMS 12
  2. how the controller files should place inside the module folder?
  3. How we can include views in a module.
  4. How the view rendering happens(Virtual paths??)
  5. Do we need to Zip the module and Why is that?

Anyone who has experience in these areas please point me to somewhere I can refer and understands things.

Many Thanks to all.

#272650
Edited, Feb 21, 2022 13:40
Vote:
 

This should be what you are looking for: https://getadigital.com/blog/optimizely-12-ui-plugin

The zip part is for packaging - if you are creating a module that will be re-used across several project.

#272651
Feb 21, 2022 13:56
Vote:
 

It was a great article Mari, Thanks!

I was able to get some things fixed in my project and organize it n the correct way. but still, I was able to get the menu rendered in the Admin menu But I can't render the addon view UI on the admin page. I guess this is a small thing but can't find it. May be this is now a issue with my project structure. 

MyModule name is: QbankModule and I have the following project in the solution.

1. Cms12-New2 - the CMS project where we need to run the addon.

2. Qbank.Connector.Core - core + other functionalities

3. Qbank.Connector.EpiCore - other functionalities

4. Qbank.Connector.EpiCore11 - includes initialization module and MenuProvider(MenuProvider Is working correctly)

5. Qbank.Connector.EpiServerModule5 - includes the MVC Controllers, Models,Views (Views are in QbankModule.Views/Views folder.  _Layout _ViewImports _ViewStart files are also placed as needed)

6. We have this routing setup as well in the module.config.

  <route  url="{controller}/{action}">
    <defaults>
      <add key="controller" value="QbankSettings" />
      <add key="action" value="Index" />
    </defaults>
  </route>

Do we need Area part also? Somthing like : <add key="moduleArea" value="Admin" />

7. Here is my MenuProvider:

[MenuProvider]
    sealed  public class MenuProvider : IMenuProvider
    {
        public IEnumerable<MenuItem> GetMenuItems()
        {
            var name = "Qbank";
            var path = MenuPaths.Global + "/cms/admin/qbank";
            var url = Paths.ToResource(GetType(), "QbankSettings");

            var link = new UrlMenuItem(name, path, url)
            {
                SortIndex = 200,
                AuthorizationPolicy = CmsPolicyNames.CmsAdmin
            };

            return new List<MenuItem>
            {
                link
            };
        }

    }
  1. it is creating Qbank.Connector.EpiServerModule5.Views.dll as well and we have included it in the module.config as an assembly.
    but Our module name (QbankModule) and the project name( Qbank.Connector.EpiServerModule5) are different. will that be an issue for this setup?
  2. The module.config files we have  in the module folder(QbankModule) and in the project name( Qbank.Connector.EpiServerModule5) are must be exact same?

Would be great if someone can help with this as well

Thanks!

#272732
Edited, Feb 22, 2022 14:27
Vote:
 

Hi All, I was able to fix the rendering issue, and thank you a lot for the support, the issue with the Routing was attributes for routes.

 [Route("[controller]")] 
 [Route("[action]")]

And the set the Build Action in properties is set to C# compiler for MenuProvider, Controller, Model.

And set the Build Action in properties is set to Content for Views.

Now I need help with Client resource importing,  According to the documentation I have created ClientResourceProvider and QbankClientResourceRegister. Kindly check the folllowings.

QbankClientResourceRegister :

 [ClientResourceRegistrator]
    public class QbankClientResourceRegister : IClientResourceRegistrator
    {
        public void RegisterResources(IRequiredClientResourceList requiredResources)
        {
            requiredResources.Require("epi-cms.widgets.base").AtHeader();
            requiredResources.Require("qbank.picker").AtFooter();
        }
    }

ClientResourceProvider :

[ClientResourceProvider]
    public class ClientResourceProvider : IClientResourceProvider
    {
        public IEnumerable<ClientResource> GetClientResources()
        {
            return new[]
            {
                  new ClientResource
                  {
                    Name = "epi-cms.widgets.base",
                    Path = Paths.ToClientResource("QbankModule", "ClientResources/Scripts/Editors/QBankMediaPicker.js"),
                    ResourceType = ClientResourceType.Script
                    
                  },
                  new ClientResource
                  {
                    Name = "qbank.picker",
                    Path = Paths.ToClientResource("QbankModule", "ClientResources/Scripts/qbank-connector.js"),
                    ResourceType = ClientResourceType.Script
                  }
            };
        }
    }

module.config file in the moduel folder :

<?xml version="1.0" encoding="utf-8"?>
<module name="QbankModule" loadFromBin="false" clientResourceRelativePath="" viewEngine="Razor" authorizationPolicy="episerver:cmsadmin"
        moduleJsonSerializerType="None" prefferedUiJsonSerializerType="Net">
  <assemblies>
    <add assembly="Qbank.Connector.EpiServerModule5" />
    <add assembly="Qbank.Connector.EpiCore11" />
    <add assembly="Qbank.Connector.EpiCore" />
    <add assembly="Qbank.Connector.Core" />
  </assemblies>
  
  <clientResources>
    <add name="epi-cms.widgets.base" path="ClientResources/Styles/qbankplugin.css" resourceType="Style"/>
    <add name="qbank.picker" path="ClientResources/scripts/editors/QbankMediaPicker.js" resourceType="Script"/>
  </clientResources>
  
  <dojo>
    <paths>
      <add name="qbank" path="ClientResources/scripts" />
    </paths>
  </dojo>
</module>

I need to load QbankMediaPicker.js when loading the following specific block :

 [ContentType(
    DisplayName = "Banner",
    GUID = "0d1c8743-4d48-470f-8afb-7e62d84f6092",
    GroupName = SystemTabNames.Content)]
    public class BannerBlock : BlockData
    {
        [Display(
           Name = "Header",
           Description = "Enter a header for the block",
           GroupName = SystemTabNames.Content,
           Order = 1
           )]
        [Required]
        public virtual string Header { get; set; }

        [Display(
            Name = "Description",
            Description = "Enter a Description for the block",
            GroupName = SystemTabNames.Content,
            Order = 2
            )]
        [Required]
        public virtual XhtmlString Description { get; set; }

        [Display(
             Name = "QBankSampleImage",
            Description = "QBankSampleImage for the block",
            GroupName = SystemTabNames.Content,
            Order = 3)]
        //[Qbank.Connector.EpiCore.DataAnnotations.MediaFormat("#310")]
        [UIHint(Qbank.Connector.Core.Web.UIHint.QBankMedia)]
        public virtual ContentReference QBankSampleImage { get; set; }

Do I need to create a virtual path provider? If yes what is it and why do we need that? 

Anyone who has experience in these areas please point me to somewhere I can refer and understands things.

Thank You!

#275605
Mar 03, 2022 19:30
Vote:
 

Hi All, I was able to fix the rendering issue, and thank you a lot for the support, the issue with the Routing was attributes for routes.

 [Route("[controller]")] 
 [Route("[action]")]

And the set the Build Action in properties is set to C# compiler for MenuProvider, Controller, Model.

And set the Build Action in properties is set to Content for Views.

Now I need help with Client resource importing,  According to the documentation I have created ClientResourceProvider and QbankClientResourceRegister. Kindly check the folllowings.

QbankClientResourceRegister :

 [ClientResourceRegistrator]
    public class QbankClientResourceRegister : IClientResourceRegistrator
    {
        public void RegisterResources(IRequiredClientResourceList requiredResources)
        {
            requiredResources.Require("epi-cms.widgets.base").AtHeader();
            requiredResources.Require("qbank.picker").AtFooter();
        }
    }

ClientResourceProvider :

[ClientResourceProvider]
    public class ClientResourceProvider : IClientResourceProvider
    {
        public IEnumerable<ClientResource> GetClientResources()
        {
            return new[]
            {
                  new ClientResource
                  {
                    Name = "epi-cms.widgets.base",
                    Path = Paths.ToClientResource("QbankModule", "ClientResources/Scripts/Editors/QBankMediaPicker.js"),
                    ResourceType = ClientResourceType.Script
                    
                  },
                  new ClientResource
                  {
                    Name = "qbank.picker",
                    Path = Paths.ToClientResource("QbankModule", "ClientResources/Scripts/qbank-connector.js"),
                    ResourceType = ClientResourceType.Script
                  }
            };
        }
    }

module.config file in the moduel folder :

<?xml version="1.0" encoding="utf-8"?>
<module name="QbankModule" loadFromBin="false" clientResourceRelativePath="" viewEngine="Razor" authorizationPolicy="episerver:cmsadmin"
        moduleJsonSerializerType="None" prefferedUiJsonSerializerType="Net">
  <assemblies>
    <add assembly="Qbank.Connector.EpiServerModule5" />
    <add assembly="Qbank.Connector.EpiCore11" />
    <add assembly="Qbank.Connector.EpiCore" />
    <add assembly="Qbank.Connector.Core" />
  </assemblies>
  
  <clientResources>
    <add name="epi-cms.widgets.base" path="ClientResources/Styles/qbankplugin.css" resourceType="Style"/>
    <add name="qbank.picker" path="ClientResources/scripts/editors/QbankMediaPicker.js" resourceType="Script"/>
  </clientResources>
  
  <dojo>
    <paths>
      <add name="qbank" path="ClientResources/scripts" />
    </paths>
  </dojo>
</module>

I need to load QbankMediaPicker.js when loading the following specific block :

 [ContentType(
    DisplayName = "Banner",
    GUID = "0d1c8743-4d48-470f-8afb-7e62d84f6092",
    GroupName = SystemTabNames.Content)]
    public class BannerBlock : BlockData
    {
        [Display(
           Name = "Header",
           Description = "Enter a header for the block",
           GroupName = SystemTabNames.Content,
           Order = 1
           )]
        [Required]
        public virtual string Header { get; set; }

        [Display(
            Name = "Description",
            Description = "Enter a Description for the block",
            GroupName = SystemTabNames.Content,
            Order = 2
            )]
        [Required]
        public virtual XhtmlString Description { get; set; }

        [Display(
             Name = "QBankSampleImage",
            Description = "QBankSampleImage for the block",
            GroupName = SystemTabNames.Content,
            Order = 3)]
        //[Qbank.Connector.EpiCore.DataAnnotations.MediaFormat("#310")]
        [UIHint(Qbank.Connector.Core.Web.UIHint.QBankMedia)]
        public virtual ContentReference QBankSampleImage { get; set; }

Do I need to create a virtual path provider? If yes what is it and why do we need that? 

Anyone who has experience in these areas please point me to somewhere I can refer and understands things.

Thank You!

#275606
Mar 03, 2022 19:30
Vote:
 

What version are you running? 

Requiring resources from a partial view/compontent has stopped working in CMS12. The fix will be released in 12.4.0
See forum thread here: https://world.optimizely.com/forum/developer-forum/CMS/Thread-Container/2022/2/require-client-resources-only-when-specific-block-type-is-rendered/#271292

#275647
Mar 04, 2022 9:26
Vote:
 

Hi Mari,

Thanks for the input,

Actually, I upgraded  EPiServer.Framework and EPiServer.CMS.Core packages to the latest version (12.4.0). But it seems the EPiServer.CMS package does not have version 12.4.* yet. But still, I can't get the client resources imported when I'm creating my custom block. 

I'm getting following errors:

GET http://localhost:8000/EPiServer/QbankModule/ClientResources/scripts/editors/QBankMediaPicker.js net::ERR_ABORTED 404 (Not Found)
GET http://localhost:8000/EPiServer/QbankModule/ClientResources/Styles/qbankplugin.css net::ERR_ABORTED 404 (Not Found)
GET http://localhost:8000/ClientResources/Styles/qbankplugin.css net::ERR_ABORTED 404 (Not Found)

Regarding the versions, you have mentioned that fixes will be released on version 12.4.0, so is it on EPiServer.CMS  package? or in EPiServer.FrameworkEPiServer.CMS.Core packages? 

if it is EPiServer.CMS  package, does it means that we need to wait for the next release (12.4.0) with the fix? Or do you know any workaround for this?

#275914
Edited, Mar 08, 2022 17:57
Vote:
 

Hi guys,

I was able to fix the version-related issues I had in the project. But now I'm getting the following error when going to edit mode.

 Error: multipleDefine                                                            dojo.js:15                                                                 
    at _f (dojo.js:15:436)
    at _f6 (dojo.js:15:15989)
    at dojo.js:15:16572
    at _9 (dojo.js:15:328)
    at _7b (dojo.js:15:16543)
    at _f0 (dojo.js:15:14659)
    at HTMLScriptElement._10a (dojo.js:15:17619)

According to the community forums, this is happening because of conflict between Jquery-related libraries when trying to register some function twice. 

I have dojo.js and jquery.js file which is used by the plugging I'm developing.

How we can easily find the exact library or point this is happening? Any way to get detailed error messages about what is happening?

#276794
Mar 21, 2022 10:49
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.