How to pack a nuget addon for #Episerver, Example FotoWare Plugin
Demo how we are working with the FotoWare AddOn and automatically nuget package it from Visual Studio.
Published 20th august 2019
Episerver CMS version 11.15 and Commerce > 11
Had the opportunity to work on this addon together with Epinova, which is publically available for all FotoWare customer in the new Market Place https://marketplace.episerver.com/apps/fotoware/fotoware-connector/
About Fotoware
That was the selling part
… aaaand the link www.fotoware.com
About the addon/plugin
The addon integrates the Episerver Media Property and Rich Text Editor with FotoWare DAM, available from Episerver Nuget Feed. Both for CMS and Commerce.
Example Rich Text Editor (TinyMce):
Example Episerver Media Property CMS:
Example Episerver Media Property Commerce Asset List:
How To package a zip with build tasks in visual studio
Episerver has this cool feature where you can install a zip under modules/_protected and it unzips and loads the modules/route the files.
End product files looks like this (after installation):
Nuget package content example:
But in working project, it is unpacked:
We do use an Alloy template as host web project, also the client side files resides in the host project, why? easier to work/debug and change while runing the application.
Solution overview:
Using Build Tasks
Create a new files named whatever “buildtasks/makeapackage.targets”
throw it in your csproj:
<Import Project=“$(MSBuildToolsPath)\buildtasks\makeapackage.targets” />
<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <!-- content goes here --> </Project>
Set parameters
These will be used in the targets files (SolutionDir, NugetExe, TmpOutDir, NuspecFile)
The main point of doing all this is to build up your package structure in “TmpOutDir” before building the nuget package
Example:
<PropertyGroup> <SolutionDir Condition="$(SolutionDir) == ''">$(MSBuildProjectDirectory)\..\</SolutionDir> <NuGetExe>$(SolutionDir)FotoWare.PlugIns.Episerver\.nuget\NuGet.exe</NuGetExe> <TmpOutDir>$(SolutionDir)FotoWare.PlugIns.Episerver\tmp</TmpOutDir> <NuspecFile>$(SolutionDir)FotoWare.PlugIns.Episerver\FotoWare.PlugIns.Episerver.nuspec</NuspecFile> </PropertyGroup>
Versioning
We will be using the versioning in AssemblyInfo.cs
[assembly: AssemblyVersion("0.9.0.5")] //IMPORTANT! if you change this version no, this will change the nuget packet version. //Also change the path version no to module in alloyTemplates project //eg. modules/_protected/FotoWare.PlugIns.Episerver/1.x.x.x/ and commit.
so in makeapackage.targets do a “function” GetVersion:
<UsingTask TaskName="GetVersion" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll"> <ParameterGroup> <AssemblyPath ParameterType="System.String" Required="true" /> <Version ParameterType="System.String" Output="true" /> </ParameterGroup> <Task> <Using Namespace="System" /> <Using Namespace="System.Diagnostics" /> <Code Type="Fragment" Language="cs"> <![CDATA[ Log.LogMessage("Getting version details of assembly at: " + this.AssemblyPath, MessageImportance.High); this.Version = FileVersionInfo.GetVersionInfo(this.AssemblyPath).FileVersion; ]]> </Code> </Task> </UsingTask>
Using the GetVersion
<Target Name="CreateNugetPackage" AfterTargets="Build"> <GetVersion AssemblyPath="$(SolutionDir)FotoWare.PlugIns.Episerver\bin\$(Configuration)\FotoWare.PlugIns.Episerver.dll"> <Output TaskParameter="Version" PropertyName="Version" /> </GetVersion> </Target>
Updating modules.config with version
<Target Name="CreateNugetPackage" AfterTargets="Build"> <!-- Update the module config with the version information --> <XmlPoke XmlInputPath="$(SolutionDir)Alloytemplates\modules\_protected\FotoWare.PlugIns.Episerver\module.config" Query="/module/@clientResourceRelativePath" Value="$(Version)" /> </Target>
zipping files
<UsingTask TaskName="ZipDirectory" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll"> <ParameterGroup> <InputPath ParameterType="System.String" Required="true" /> <OutputFileName ParameterType="System.String" Required="true" /> <OverwriteExistingFile ParameterType="System.Boolean" Required="false" /> </ParameterGroup> <Task> <Reference Include=" System.IO.Compression.FileSystem" /> <Using Namespace="System.IO" /> <Using Namespace="System.IO.Compression" /> <Code Type="Fragment" Language="cs"> <![CDATA[ if(this.OverwriteExistingFile) { File.Delete(this.OutputFileName); } ZipFile.CreateFromDirectory(this.InputPath, this.OutputFileName); ]]> </Code> </Task> </UsingTask>
Using the ZipDirectory
<Target Name="CreateNugetPackage" AfterTargets="Build"> <!--Create the Zip file--> <ZipDirectory InputPath="$(SolutionDir)Alloytemplates\modules\_protected\FotoWare.PlugIns.Episerver\" OutputFileName="$(TmpOutDir)\content\modules\_protected\FotoWare.PlugIns.Episerver\FotoWare.PlugIns.Episerver.zip" OverwriteExistingFile="true" /> </Target>
MakeDir
<!-- Create the Versioned out dir for the client resources by using $(Version)--> <MakeDir Directories="$(TmpOutDir)\content\modules\_protected\" />
RemoveDir
<!-- Cleanup at end --> <RemoveDir Directories="$(TmpOutDir)" />
Moving/Copying a bunch of files
Copying all clientside files at once
<ItemGroup> <ClientResources Include="$(SolutionDir)Alloytemplates\modules\_protected\FotoWare.PlugIns.Episerver\**\*" /> </ItemGroup> <!-- Copy --> <Copy SourceFiles="@(ClientResources)" DestinationFiles="@(ClientResources -> '$(TmpOutDir)\content\modules\_protected\FotoWare.PlugIns.Episerver\%(RecursiveDir)%(Filename)%(Extension)')"/>
Adding and removing stuff in web.config
By applying XmlTransforms in web.config.install.xdt and web.config.uninstall.xdt, it can be accomplished.
<Copy SourceFiles="$(SolutionDir)FotoWare.PlugIns.Episerver\MSBuild\web.config.install.xdt" DestinationFolder="$(TmpOutDir)\content" /> <Copy SourceFiles="$(SolutionDir)FotoWare.PlugIns.Episerver\MSBuild\web.config.uninstall.xdt" DestinationFolder="$(TmpOutDir)\content" />
Renaming files
Copy the readme.md and change name to readme.txt
<Copy SourceFiles="$(SolutionDir)FotoWare.Plugins.Episerver\Documentation\readme.md" DestinationFiles="$(TmpOutDir)\readme.txt" />
Using .nuspec to build the nuget package
Executing .nuspec:
<!-- Create the package --> <PropertyGroup> <NugetCommand> "$(NuGetExe)" pack "$(NuspecFile)" -OutputDirectory "$(OutDir.TrimEnd('\\'))" -Version "$(Version)" -Properties Configuration=$(Configuration) </NugetCommand> </PropertyGroup> <Exec Command="$(NugetCommand)"/>
Example .nuspec
<?xml version="1.0"?> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <metadata> <id>FotoWare.PlugIns.Episerver</id> <version>$version$</version> <title>Episerver Addon integrates Fotoware DAM with Episerver CMS and Commerce</title> <authors>FotoWare</authors> <owners>FotoWare</owners> <iconUrl>https://www.fotoware.com/hubfs/FotoWare_January2018/image/favicon.ico</iconUrl> <projectUrl>https://learn.fotoware.com/02_FotoWeb_8.0/Integrating_FotoWeb_with_third-party_systems/Episerver_plugin_documentation</projectUrl> <requireLicenseAcceptance>false</requireLicenseAcceptance> <description>This Episerver Addon integrates Fotoware DAM with Episerver CMS and Commerce</description> <releaseNotes>More information check FotoWare website </releaseNotes> <copyright>MIT</copyright> <tags>EPiServerModulePackage EPiServer-UI EPiServerAddOn ThirdPartyAddon FotoWare FotoWare Imagebank Mediabank Integration CMS11 Epinova TinyMCE</tags> <dependencies> <dependency id="EPiServer.CMS.UI" version="[11.15,12.0)" /> <dependency id="EPiServer.CMS.TinyMce" version="[2.0,3.0)" /> <dependency id="System.Json" version="[4.5,6.0)" /> </dependencies> </metadata> <files> <file src="bin\$configuration$\FotoWare.PlugIns.Episerver.dll" target="lib\net45\" /> <file src="tmp\content\**\*" target="content" /> <file src="..\AlloyTemplates\Views\Shared\DisplayTemplates\FotoWareImageUrl.cshtml" target="content\views\shared\displaytemplates\" /> <file src="..\AlloyTemplates\Views\Shared\DisplayTemplates\FotoWareImage.cshtml" target="content\views\shared\displaytemplates\" /> <file src="msbuild\readme.txt" target="" /> <file src="..\AlloyTemplates\fotoware\*" target="tools\extras" /> <file src="..\FotoWare.PlugIns.Episerver.ArchiveImport\fotowareImport*" target="tools\extras" /> </files> </package>
Conclutions
- Build Task are used to build up a folder structure and zip files, you can use code and set parameters.
- Nuspec is used to build a nuget package, including files from the Build tasks.
- Versioning is working automatically.
- Store your Clientside files in host project, easiest way to work, debug at runtime.
Find the complete code here: [Gist Link]
Further reading:
- https://docs.microsoft.com/en-us/visualstudio/msbuild/walkthrough-using-msbuild?view=vs-2019
- https://world.episerver.com/documentation/developer-guides/CMS/add-ons/Developing-Add-ons/
About the author
Luc Gosso
– Independent Senior Web Developer
working with Azure and Episerver
Twitter: @LucGosso
LinkedIn: linkedin.com/in/luc-gosso/
Github: github.com/lucgosso