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.

icon of user profile

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

FotoWare is an enterprise digital asset management (DAM) system for your image, video, audio, graphics and documents archives. Founded in 1997, it was one of the first in the world to offer a DAM system. In the space of 20 years, has become a world-leading provider of DAM with more than 200,000 users and 4,000 customers in a wide range of industries worldwide.

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:

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