Easily Publish Website Design - How to publish the whole design

The Dreamweaver integration for Modular Templating enables you to rapidly upload HTML designs into SDL Tridion and publish them onto your website. However there are some common stumbling blocks related to how 2nd level dependencies are managed (for example the relationship between CSS and images), and the structure of the published files on your website. This article outlines these issues and illustrates a simple approach to overcome them.

Easily Publish Website Design

Article Sections

The Get Design Elements Template Building Block described in this article can be downloaded here .

How to do preserve structure and publish all design assets with a .NET TBB

The assumptions that we make are:

 

  1. That we decide to publish all multimedia components that are uploaded into the CMS as part of the design and not worry about the 2nd or 3rd level dependencies between items.
  2. The we have created structure groups (SGs) that mirror the structure of the HTML prototype (they start from a sub structure group, provided the HTML prototype will also work in a sub folder of the website) - in our example this means we create css and img SGs

Then it is a case of creating a .NET TBB to do the processing:

 

First we implement the Transform method. This will:

 

  1. Calculate the root SG to publish into (we assume here it is relative to the current (master) page we are publishing, but you could make this constant, or add your own logic)
  2. Read the root folder from which to read the MM components from a parameter (use a default as fallback) - we assume this is passed in as a webdav url, relative to the root folder.
  3. Recursively add all binaries from this folder and its subfolders using a separate method AddBinariesFromFolder

 

Note that in the example shown, it is assumed that we are using a TemplateBase class such as can be found here, with helper methods for getting references to the current page etc.

 

        private string _rootSGWebDavUrl;

 

        public override void Transform(Engine engine, Package package)

        {

            this.Initialize(engine, package);

            Page page = this.GetPage();

 

            //Set the root SG based on the current page

            _rootSGWebDavUrl = page.OrganizationalItem.WebDavUrl;

 

            //Locate the root folder to find images

            Folder folder = null;

            string sysfolderuri = package.GetValue("SystemComponentFolderWebdavUrl");

            if (sysfolderuri == null)

            {

                sysfolderuri = "/System/Designs/Page%20Designs";

            }

            sysfolderuri = this.GetPublication().RootFolder.WebDavUrl + sysfolderuri;

            folder = engine.GetObject(sysfolderuri) as Folder;

            if (folder==null)

            {

                throw new Exception(String.Format("System folder {0} does not exist", sysfolderuri));

            }

            //Recursively add binaries from the folder and its subfolders

            AddBinariesFromFolder(_rootSGWebDavUrl, folder, "");

        }

 

The separate method to add binaries from a folder will do the following:

 

  1. Get a reference to the corresponding structure group
  2. Loop through all binaries found in the folder and add them to the SG using a separate method AddBinary

 

        public void AddBinariesFromFolder(string rootSGWebDavUrl, Folder folder, string path)

        {

            //Try to get a reference to the sub Structure Group based on the folder title

            StructureGroup sg;

            sg = m_Engine.GetObject(rootSGWebDavUrl + path) as StructureGroup;

            if (sg == null)

            {

                throw new Exception(String.Format("Could not find Structure Group {0}. Please create this and republish", rootSGWebDavUrl + path));

            }

 

            //Loop through all components in the folder

            foreach (KeyValuePair<TcmUri, string> item in GetOrganizationalItemContents(folder, ItemType.Component, false))

            {

                Component mmComp;

                mmComp = m_Engine.GetObject(item.Key) as Component;

                if (mmComp.ComponentType == ComponentType.Multimedia)

                {

                    //if its a MM comp then add it to the package, and publish it

                    AddBinary(mmComp, sg);

                }

 

            }

            //Loop through all subfolders to recurse

            foreach (KeyValuePair<TcmUri, string> item in GetOrganizationalItemContents(folder, ItemType.Folder, false))

            {

                Folder subFolder = m_Engine.GetObject(item.Key) as Folder;

                AddBinariesFromFolder(rootSGWebDavUrl, subFolder, path + "/" + subFolder.Title);

            }

        }

The separate method to add a binary to a Structure Group does the following:

  1. Check if the binary is already in the package - which will be the case for CSS, JS etc linked from the DWT.
  2. If it is not in the package, we add it
  3. If an item is in the package but it has a different tcm uri, we replace it with the current item (this is to avoid conflicts with 2 different binaries with the same filename)
  4. We ensure the item will be published to the correct SG by calling the TOM.NET AddBinary method with the appropriate parameters
  5. We write the path of the published item back into the package item (so the LinkResolver will update links to this item in our output to use the correct path)

        public void AddBinary(Component mmComp, StructureGroup sg)

        {

            Item packageItem = null;

            string filename = Utilities.GetFilename(mmComp.BinaryContent.Filename);

 

            //Check if the binary is already in the package (for example, if the DWT already added it

            packageItem = m_Package.GetByName(filename);

            if (packageItem != null)

            {

                KeyValuePair<string, string> pair = new KeyValuePair<string, string>("TCMURI", mmComp.Id.ToString());

                if (!packageItem.Properties.Contains(pair))

                {

                    //its a different item so we should push our item in the package

                    packageItem = null;

                }

            }

 

            //if its not in the package, add it

            if (packageItem == null)

            {

                packageItem = m_Package.CreateMultimediaItem(mmComp.Id);

                m_Package.PushItem(filename, packageItem);

            }

 

            //Publish the binary into the appropriate SG

            using (Stream itemStream = packageItem.GetAsStream())

            {

                try

                {

                    byte[] data = new byte[itemStream.Length];

                    itemStream.Read(data, 0, data.Length);

                    string publishedPath = m_Engine.AddBinary(mmComp.Id, null, sg.Id, data, filename);

                    packageItem.Properties[Item.ItemPropertyPublishedPath] = publishedPath;

                }

                finally

                {

                    itemStream.Close();

                }

            }

        }

We add this TBB into the modular template between the DWT and Default Finish Actions, and when we publish, we see that

1)      The HTML prototype structure is preserved

2)      All binaries are published, not just the ones directly referenced in the DWT

You will notice also that if you un-publish your page, it will remove the CSS and image files from the webserver file system, because using AddBinary means that on the Content Delivery side, the binary files are managed correctly when content is unpublished.

This solution is a great first step, some limitations and further ideas are discussed in the final section .