Please see this blog post which explains in detail how to package modules for CMS 12.
https://blog.tech-fellow.net/2021/09/03/how-to-pack-your-shell-module-for-optimizely-content-cloud/
I guess it is by design, the files are referenced in your projects after nuget file installation and that's it, no need for file copy.
The files will be copied at build/publish time anyway.
I'm not sure what the exact issue is here, but I'd like to point out a few things;
<Project>
<ItemGroup>
<ModuleFiles Include="$(MSBuildThisFileDirectory)..\..\contentFiles\any\any\modules\_protected\**\*.*"/>
</ItemGroup>
<Target
Name="CopyModuleFiles"
BeforeTargets="Build"
Inputs="@(ModuleFiles)"
Outputs="@(ModuleFiles -> '$(MSBuildProjectDirectory)\modules\_protected\%(RecursiveDir)%(Filename)%(extension)')">
<Copy
SourceFiles="@(ModuleFiles)"
DestinationFiles="@(ModuleFiles -> '$(MSBuildProjectDirectory)\modules\_protected\%(RecursiveDir)%(Filename)%(extension)')" />
</Target>
</Project>
Internally we're using somthing similar to this target file to build shell modules including zipping them. Import this file into your project:
<Project>
<PropertyGroup>
<!-- Where the targets files are located -->
<BuildDirectory Condition=" '$(BuildDirectory)' == '' ">$(MSBuildThisFileDirectory)</BuildDirectory>
<!-- Where the artifacts should be produced -->
<ArtifactsDirectory Condition=" '$(ArtifactsDirectory)' == '' ">$(SolutionDir)artifacts\</ArtifactsDirectory>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<ModuleName>$(MSBuildProjectName)</ModuleName>
<ModuleFile>$(ArtifactsDirectory)$(ModuleName).zip</ModuleFile>
<ModuleTempDirectory>$(ArtifactsDirectory)$(ModuleName)\</ModuleTempDirectory>
<ClientResourcesDirectory>clientResources\</ClientResourcesDirectory>
<ClientResourcesOutputDirectory>$(ClientResourcesDirectory)dist\</ClientResourcesOutputDirectory>
<!-- This property needs to be set for the module to be created -->
<ClientResourcesBuildCommand></ClientResourcesBuildCommand>
</PropertyGroup>
<ItemGroup>
<!-- These items do not apply when packing with nuspec files -->
<Content Remove="module.config" />
<None Include="module.config" />
<None
Include="$(BuildDirectory)copyModuleFiles.targets"
Visible="false"
Pack="true"
PackagePath="build\$(TargetFramework)\$(ModuleName).targets" />
<None
Include="$(ModuleFile)"
Visible="false"
Pack="true"
PackagePath="contentFiles\any\any\modules\_protected\$(ModuleName)\" />
</ItemGroup>
<!--
Note: 'DispatchToInnerBuilds' only works in multi-target projects.
We only want to compile the client resources once, even though the
project has multiple targets. Change to 'Build' if multi-targeting
is removed.
-->
<Target Name="BuildClientResources"
BeforeTargets="DispatchToInnerBuilds"
Condition=" '$(ClientResourcesBuildCommand)' != '' ">
<Message Text="Restoring dependencies and building client resources using 'npm'. This may take several minutes..." Importance="high" />
<Exec WorkingDirectory="$(ClientResourcesDirectory)" Command="npm install" />
<Exec WorkingDirectory="$(ClientResourcesDirectory)" Command="$(ClientResourcesBuildCommand)" />
</Target>
<Target Name="CreateModule"
AfterTargets="BuildClientResources"
Inputs="module.config;@(ModuleClientResources)"
Outputs="$(ModuleFile)">
<ItemGroup>
<ModuleClientResources Include="$(ClientResourcesOutputDirectory)\**\*"></ModuleClientResources>
</ItemGroup>
<Copy
SourceFiles="module.config"
DestinationFolder="$(ModuleTempDirectory)" />
<XmlPoke
XmlInputPath="$(ModuleTempDirectory)module.config"
Query="/module/@clientResourceRelativePath"
Value="$(Version)" />
<Copy
SourceFiles="@(ModuleClientResources)"
DestinationFiles="@(ModuleClientResources -> '$(ModuleTempDirectory)$(Version)\$(ClientResourcesOutputDirectory)%(RecursiveDir)%(Filename)%(Extension)')" />
<ZipDirectory
SourceDirectory="$(ModuleTempDirectory)"
DestinationFile="$(ModuleFile)"
Overwrite="true" />
<RemoveDir Directories="$(ModuleTempDirectory)" />
</Target>
</Project>
The project file...
<Project Sdk="Microsoft.NET.Sdk.Razor">
<Import Project="createModule.targets" />
<PropertyGroup>
<TargetFrameworks>net5.0</TargetFrameworks>
...
<ClientResourcesBuildCommand Condition=" '$(Configuration)' == 'Release' ">npm run build</ClientResourcesBuildCommand>
</PropertyGroup>
...
</Project>
Hi All, we managed to get this to pack and it's working fine to use the module. However, when adding all content into a zip file and then creating the package leads us to get an error. It says
Unable to find a module by assembly "Qbank.Connector.EpiCore11"
We have used the following menu provider and we notice this error occurs because of the "GetType()" method.
[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
};
}
}
it seems like when we zip the content of the module even though we can install it when using it can't identify the module by the assembly file. we have following folder structure
_protected
| - QBankModule
| - QBankModule.zip
|- 0.8.7
|- module.config
module.config
<?xml version="1.0" encoding="utf-8"?>
<module name="QbankModule" clientResourceRelativePath="0.8.7" loadFromBin="false" moduleJsonSerializerType="None" prefferedUiJsonSerializerType="Net">
<assemblies>
<add assembly="Qbank.Connector.EpiServerModule5.Views" />
<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="qbankmoduleResources" path="ClientResources/scripts/jquery.js" resourceType="Script"/>
</clientResources>
<dojo>
<paths>
<add name="qbank" path="ClientResources/scripts" />
</paths>
</dojo>
<clientModule>
<moduleDependencies>
<add dependency="Shell"/>
<add dependency="Cms" type="RunAfter"/>
</moduleDependencies>
<requiredResources>
<add name="qbankmoduleResources"/>
</requiredResources>
</clientModule>
</module>
Can anyone help with this? Is there anything that we missing here related to this zipping things and packing things?
You have two options, create an initialization module or add a extension method to call in startup.
[InitializableModule]
[ModuleDependency(typeof(InitializationModule))]
public class InitializeQBank : IConfigurableModule
{
private static ILogger _log = LogManager.GetLogger(typeof(InitializeAvaTaxUI));
/// <summary>
/// Configure container
/// </summary>
/// <param name="context"></param>
public void ConfigureContainer(ServiceConfigurationContext context)
{
context.Services.Configure<ProtectedModuleOptions>(x =>
{
if (!x.Items.Any(x => x.Name.Equals("QbankModule")))
{
x.Items.Add(new ModuleDetails
{
Name = "QbankModule"
});
}
});
}
/// <summary>
/// Initialize
/// </summary>
/// <param name="context">The context.</param>
public void Initialize(InitializationEngine context)
{
}
/// <summary>
/// Uninitialize
/// </summary>
/// <param name="context">The context.</param>
public void Uninitialize(InitializationEngine context)
{
}
}
public static class ServiceCollectionExtensions
{
/// <summary>
/// Convenient method to add CMS with both core services, Ui and runtime hosting.
/// </summary>
/// <param name="services">the services.</param>
/// <returns>The configured service container</returns>
public static IServiceCollection AddQBank(this IServiceCollection services)
{
services.Configure<ProtectedModuleOptions>(x =>
{
if (!x.Items.Any(x => x.Name.Equals("QbankModule")))
{
x.Items.Add(new ModuleDetails
{
Name = "QbankModule"
});
}
});
return services;
}
I'm working on the packaging part of module development and I was able to successfully create the package and install it on the destination site.
But I have an issue with copying files to the module folder in the destination project when installing. It creates some references for folders, not real folders. There are no physical files created in the modules folder in the destination project. But we noticed that it was created in the global NuGet folder on my computer (C:\Users<User>.nuget\packages).
I'm using Package.nuspec file and .targets file as follows:
Package.nuspec
QBankModule6.targets
Also, I found following post an implemented solutions mentioned in the post. But ended up with the same result.
https://stackoverflow.com/questions/65218578/why-are-content-files-from-nuget-packages-added-as-links-in-net-core-projects-a
Basically, I need to understand how to copy some files from the NuGet package to the destination project when installing the NuGet package.
Any input from someone who has experience on this would be great!
Thanks in advance!