Base plugin structure

For creating a plugin, firstly open Grandnode in your IDE and create a new project (class library) inside the “Plugins” directory.

Next open .csproj file. Paste the code visible below and replace the “Misc.MyPlugin” with your plugin name. This will add to your project required GrandNode libraries references and specific compilation output path.

<Project Sdk="Microsoft.NET.Sdk.Razor">
  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
    <AddRazorSupportForMvc>true</AddRazorSupportForMvc>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <OutputPath>..\..\Web\Grand.Web\Plugins\Misc.MyPlugin\</OutputPath>
    <OutDir>$(OutputPath)</OutDir>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
    <OutputPath>..\..\Web\Grand.Web\Plugins\ Misc.MyPlugin \</OutputPath>
    <OutDir>$(OutputPath)</OutDir>
  </PropertyGroup>
  <Target Name="PostBuild" AfterTargets="PostBuildEvent">
    <RemoveDir Directories="..\..\Web\Grand.Web\Plugins\Misc.MyPlugin\refs" />
    <RemoveDir Directories="..\..\Web\Grand.Web\Plugins\Misc.MyPlugin \ref" />
  </Target>
  <ItemGroup>
    <ProjectReference Include="..\..\Business\Grand.Business.Authentication\Grand.Business.Authentication.csproj">
      <Private>false</Private>
    </ProjectReference>
    <ProjectReference Include="..\..\Business\Grand.Business.Catalog\Grand.Business.Catalog.csproj">
      <Private>false</Private>
    </ProjectReference>
    <ProjectReference Include="..\..\Business\Grand.Business.Checkout\Grand.Business.Checkout.csproj">
      <Private>false</Private>
    </ProjectReference>
    <ProjectReference Include="..\..\Business\Grand.Business.Cms\Grand.Business.Cms.csproj">
      <Private>false</Private>
    </ProjectReference>
    <ProjectReference Include="..\..\Business\Grand.Business.Common\Grand.Business.Common.csproj">
      <Private>false</Private>
    </ProjectReference>
    <ProjectReference Include="..\..\Business\Grand.Business.Customers\Grand.Business.Customers.csproj">
      <Private>false</Private>
    </ProjectReference>
    <ProjectReference Include="..\..\Business\Grand.Business.Marketing\Grand.Business.Marketing.csproj">
      <Private>false</Private>
    </ProjectReference>
    <ProjectReference Include="..\..\Business\Grand.Business.Messages\Grand.Business.Messages.csproj">
      <Private>false</Private>
    </ProjectReference>
    <ProjectReference Include="..\..\Business\Grand.Business.Storage\Grand.Business.Storage.csproj">
      <Private>false</Private>
    </ProjectReference>
    <ProjectReference Include="..\..\Business\Grand.Business.System\Grand.Business.System.csproj">
      <Private>false</Private>
    </ProjectReference>
    <ProjectReference Include="..\..\Core\Grand.Domain\Grand.Domain.csproj">
      <Private>false</Private>
    </ProjectReference>
    <ProjectReference Include="..\..\Core\Grand.Infrastructure\Grand.Infrastructure.csproj">
      <Private>false</Private>
    </ProjectReference>
    <ProjectReference Include="..\..\Core\Grand.SharedKernel\Grand.SharedKernel.csproj">
      <Private>false</Private>
    </ProjectReference>
    <ProjectReference Include="..\..\Web\Grand.Web.Common\Grand.Web.Common.csproj">
      <Private>false</Private>
    </ProjectReference>
  </ItemGroup>
</Project>

Besides, you must create a Manifest.cs file that includes basic information about the plugin, like system name, group, friendly name, author, etc..

using Grand.Infrastructure;
using Grand.Infrastructure.Plugins;

[assembly: PluginInfo(
    FriendlyName = "My Plugin",
    Group = "Misc",
    SystemName = "My Plugin",
    SupportedVersion = GrandVersion.SupportedPluginVersion,
    Author = "grandnode team",
    Version = GrandVersion.SupportedPluginVersion + ".001"
)]

Grandnode plugin also required logo in jpg format, so copy image file to main directory of plugin. Logo file must be in output plugin directory (after compilation) ,so  in visual studio right click on file -> properties -> set build action to “Copy if newer”. This add code to your .csproj file :

  <ItemGroup>
    <None Update="logo.jpg">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>

Note: Is important to emphasize that all static files (like HTML, cshtml, js, CSS etc.)  that your plugin use should have set build action as “Copy if newer”

Lastly, we crate c# class that will be representing our plugin. This class must inherit from BasePlugin :

public class MyPlugin : BasePlugin
    {
        private readonly ITranslationService _translationService;
        private readonly ILanguageService _languageService;

        public MyPlugin(ITranslationService translationService,ILanguageService languageService)
        {
            _translationService = translationService;
            _languageService = languageService;
        }
		
        public override string ConfigurationUrl()
        {
            return "/Admin/MyPlugin/Configure";
        }

        public async override Task Install()
        {
            await this.AddOrUpdatePluginTranslateResource(_translationService,     _languageService, "Plugins.Misc.MyPlugin.Value", "Sample value");
            await base.Install();
        }

        public async override Task Uninstall()
        {
            await this.DeletePluginTranslationResource(_translationService, _languageService, "Plugins.Misc.MyPlugin.Value");
            await base.Uninstall();
        }
    }

BasePlugin class has a few methods that we can override and add additional functionality.

The most common use cases for install/uninstall are:

-add/delete translation resources

-Initialize/remove settings for our application 

If we specify the configuration URL (override ConfigurationUrl method and not return null) we will see the button in the plugin list that redirects us to the configuration page of our plugin. Obviously, we must create a controller that will be equivalent to our URL.

Note: in the MyPlugin class code example you can see that GrandNode uses dependency injection, you specific required service in the constructor, and DI container will inject this. Obviously, if you want to inject your custom service, firstly it should be registered.

In your base plugin class, you can't use your own services, because they are not registered in DI

Finally, build your plugin project, run GrandNode and you will see your newly created plugin in the plugin list.