November 25, 2014

Tridion: Image handling in Responsive design.

In responsive websites, we usually keep different breakpoints for different devices & different breakpoints require different image sizes. There are different ways to use images in responsive layouts.
  • Same image on all Devices
  • Manual upload of device specific Image
  • Generate images on Fly.
  • Tridion CID
Same image on all devices: This is simplest of all as point of development, let user upload single image and same image is published on delivery. But there are few points which needs to be taken care for this.

UI Designer has to maintain the aspect ratio in his designs across Desktop, tablet, mobile or whatever breakpoints you have. e.g

Desktop:  200  : 100
Tablet:      100 :  50
Mobile:     50:    25

so if you see the aspect ration has been maintained at 2:1.  so Maintaining aspect-ratio is very important as it can go haywire if the objects start scaling based on the screen dimension. To maintain aspect ratio of the objects, select Auto for either width or height of the object in the Primary view

Tridion side there is no special change is required. normal image publishing.

Cons: Mobile has to download the same big image, which could be bandwidth issue, also in sites where lots of images are used, this approach on tablet/mobile would not be considered best

Manual upload of device specific Image:  There are different reasons where device specific images are required:
  • Designers are not totally versed with it. so ratios are not maintained  across devices
  • Performance and bandwidth issue
  • For mobile instead of whole image only part of image is required for more cleared and focused view.
these images could be manually-generated or from some DAM System.

Tridion Implementation:

Create a category with Keywords having device information. 
Category : Device Type
Keywords - large, medium, small

Create a embedded schema "Responsive Multimedia" as shown below
ScreenSize refer above category.

For this we created a generic helper class in razor, it generate the Data-Interchange tags in images. We are using Foundation Framework for handle responsiveness of site  here. 

<img data-interchange="[@Publication.MultimediaUrl/@mobile, (default)], [@Publication.MultimediaUrl/@tablet, (medium)], [@desktop, (large)]"></img>

Now you can read the component and read the values of uploaded images and generate the image tag with data-interchange as shown above. 

Generate images on Fly  Other approach is to upload a single image and generate the rest of the device specific images on Fly using custom code. But design should maintain aspect ratio of the images. 

Tridion code: we have written a c# tbb to handle it, here is the code for it. you can customize it 

[TcmTemplateTitle("AddAssets")]
    public class AddAssets : TemplateBase
    {
        
        public override void Transform(Engine engine, Package package)
        {
            Initialize(engine, package);
            var items = m_Package.GetEntries();
            var dynamicbinaries = new Dictionary<string, string[]>();
            foreach (var i in items)
            {
                Logger.Info(i.Key + ":" + i.Value);

                if (i.Key != "GenerateDynamic") continue;
                Logger.Info("Generate Dynamic parsing:");

                var arr = i.Value.GetAsString();
                var binaries = arr.Split(',');

                dynamicbinaries.Add(binaries[0], binaries);
           }

            var allowedContentTypes = new Dictionary<ContentType, ImageFormat>()
                                   {
                                       {ContentType.Bmp, ImageFormat.Bmp},
                                       {ContentType.Jpeg, ImageFormat.Jpeg},
                                       {ContentType.Png, ImageFormat.Png},
                                       {ContentType.Gif, ImageFormat.Gif}
                                   };
            foreach (var contentType in allowedContentTypes.Keys)
            {
                foreach (var item in package.GetAllByType(contentType))
                {
                    if (dynamicbinaries.ContainsKey(item.Properties[Item.ItemPropertyTcmUri]))
                    {
                        InsertRelatedBinaries(item, allowedContentTypes[contentType], dynamicbinaries[item.Properties[Item.ItemPropertyTcmUri]]);
                    }
                }

            }
        }

        private void GenerateDeviceSpecificBinaries(Item item, ImageFormat imageFormat, string[] binary)
        {

            var mmc = new Component(new TcmUri(item.Properties[Item.ItemPropertyTcmUri]), m_Engine.GetSession());

            var binaryContent = mmc.BinaryContent;
            var binaryBytes = binaryContent.GetByteArray();
            using (var bitmap = new Bitmap(new MemoryStream(binaryBytes)))
            {
                var tabPercentage = Int32.Parse(m_Package.GetValue("TabPercentage"));
                var mobilePercentage = Int32.Parse(m_Package.GetValue("MobilePercentage"));
                var resizedBitmap = ResizeBitmap(bitmap, tabPercentage);
                AddBitmapAsBinary(item, imageFormat, mmc, binaryContent, resizedBitmap, "2", binary[2]);//Tablet Images

                resizedBitmap = ResizeBitmap(bitmap, mobilePercentage);

                AddBitmapAsBinary(item, imageFormat, mmc, binaryContent, resizedBitmap, "3", binary[1]); // Mobile Images
                resizedBitmap.Dispose();
            }
        }

        private void AddBitmapAsBinary(Item item, ImageFormat format, Component mmc, BinaryContent binaryContent, Bitmap resizedBitmap, string variant, string filename)
        {
            var resizedStream = new MemoryStream();
            resizedBitmap.Save(resizedStream, format);
            var binary = this.m_Engine.PublishingContext.RenderedItem.AddBinary(
                resizedStream, filename, variant, mmc,
                binaryContent.MultimediaType.MimeType);
            resizedStream.Dispose();
        }

        private Bitmap ResizeBitmap(Bitmap sourceBitmap, int per)
        {
          
                var sourceWidth = sourceBitmap.Width;
                var sourceHeight = sourceBitmap.Height;

                float nPercentW = per;
                float nPercentH = per;

                var  destWidth = (int)(sourceWidth * (nPercentW / 100));
                var  destHeight = (int)(sourceHeight * (nPercentH / 100));
           

            var resultBitmap = new Bitmap(destWidth, destHeight);
            var graphicsd = Graphics.FromImage(resultBitmap);
            graphicsd.InterpolationMode = InterpolationMode.HighQualityBicubic;

            graphicsd.DrawImage(sourceBitmap, 0, 0, destWidth, destHeight);
            graphicsd.Dispose();

            return resultBitmap;
        }

    }
}

Tridion CID  Contextual Image delivery  is a product from SDL which helps in transforming the images in various ways such as
  • Resize with Cropping
  • Resize without Cropping
  • Trim image
  • Change Image format
Please read this very good article for further explanation of this module. What Does Contextual Image Delivery Mean for SDL Tridion Images? 

September 14, 2014

Single Sign On Integration with SDL Tridion and CA Siteminder


In today’s world most of the products, be it a financial product like credit  card, media or electronics item,  do not come from just one vendor but from collaboration between different partner companies. All partners play  a unique role in marketing of such product and need access to related data.

Practically, it is not possible to provide access to all the partners to the Active Directory or LDAP and here comes SSO for the rescue which enables all the partners to use a common policy server to authenticate themselves.
SDL Tridion can be easily integrated with SSO servers like CA Siteminder and IBM Tivoli.

Following figures describes SSO flow till authentication.
Fig: Flow Chart of SSO Request

Installation & Configuration: Setting up SSO requires lots of configurationThe first step for configuring SSO for Tridion is installing Site Minder agent on CM server which intercepts all the request coming to Tridion CMS and authorize the user
The web agent interacts with Policy Server in order to authorize user based on the credential provided by user. Once authenticated, unique user id is set in header variable of each request which is then used by Tridion to identify the user if SSO is configured.
Authorization can be done at Tridion CMS security level where based on business requirement different access and permissions can be given to users on CMS items like publication etc.

Installing CA Siteminder agent on CMS server 
  1. Execute ca-wa-12.5-cr02-win64.exe.
  2. Follow the instructions according to installation wizard
  3. In the Host Registration dialog box, select ‘Yes’ to register a host and click Next.
  4. Complete the following fields in the Admin Registration dialog box, then click Next- Provide the required admin credentials
  5. Admin User Name
  6. Admin Password
  7. Confirm Admin Password 
  8. Enabled Shared Secret Rollover – Unchecked
  9. In the Trusted Host Name and Configuration Object dialog box, enter webserver name as trusted host name. 
  10. In the Policy Server IP Address dialog box enter the IP or  policy server VIP:
  11. Choose FIPS Compatibility Mode (Default) and click Next.
  12. Accept the default location of the host configuration file, SmHost.conf or click Choose to select a different location. Click Next.
  13. In the Select Web server(s) dialog box, select the option for the Microsoft IIS 7.5 and click Next.
  14. Select the virtual sites(SDL Tridion 2011) that need to be configured with this web agent and click Next.
  15. Enter the ACO name <webserver>_agent_config and click Next:
  16. In the WebAgent Enable Option, check the YES box and click Next.
  17. In the Web Server Configuration Summary dialog box. Confirm that the configuration settings are correct, then click Install.
  18. Click done when the installation is complete. The system restarts
Permission to user to modify encrypted configuration sections
  1. Open command prompt and go to where PSTools is located
  2. Run:    psexec -i -s cmd.exe
  3. This should open another new command prompt window
  4. In the new command prompt window, navigate to where aspnet_regiis is (should be in c:\Windows\Microsoft.Net\Framework64\v4.0.30319  
  5. Run:   aspnet_regiis -pa "TridionRsaKeyContainer" "Domain\UserName"
  6. Follow the same steps for user NT AUTHORITY\IUSR
Configuration Console:

Fig: SSO Configuration in CMS
Set header name which will be used to retrieve user name after authentication.

Some Useful links:

  • http://docs.sdl.com/LiveContent/content/en-US/SDL%20Tridion%202011%20SP1%20full%20documentation-v1/GUID-407B4F89-B8D0-4799-A16B-147D89B044F6
  • https://support.ca.com/cadocs/0/CA%20SiteMinder%20r6%200%20SP6-ENU/Bookshelf_Files/PDF/siteminder_wa_config_enu.pdf
Issues and Trouble shooting

In my previous experience with SSO the unique user GUID that is set by Siteminder in the header of response was not getting persisted and hence SSO was not working fine with Tridion. To resolve this issue one can try putting a custom HTTP Module in request pipeline and check if the GUID is getting persisted, and in case it is not you will have to explicitly set the header variable.
NOTE: One important point to take care of (especially in case you are making entries manually in web.config)  make sure that HTTP module entry in web.config for your custom http module comes after the entry of CA Siteminder.

<add name="CASiteMinderWebagentModule" preCondition="integratedMode,bitness64" />
<add name="customhttpmodule" type="<>, Version=1.1.0.0, Culture=neutral, PublicKeyToken=<>" />
Some point worth noting is that we were not able to run the Tridion Client applications like Template Builder and Content porting using SSO and hence it was explicitly set on windows authentication.

July 24, 2014

Tridion CMIS connector for aDAM - External content Library

We had a requirement to connect aDam with Tridion 2013. I started the POC after reading docs, posts       ( from Eric & Bart).

External Content Library aka ECL was introduced in Tridion 2013 to connect external content/multimedia sources with Tridion Interface e.g youtube, vimeo, flicker, facebook, aDam, Media Manager. Its a great feature.
When content starts displaying in the CME, it looks like native components.

Prerequisite

Tridion: 
  • Assembly: Tridion.ExternalContentLibrary.V2
  • Admin Privilege
Terminologies:

Mount Point: Tridion folder which you mount to access external content. all external content is listed under this folder. 

ECL Stub Schema: When a provider is mounted in Tridion, it generates a Stub schema , and all stub components (from external sources) displayed in this mountPoint are type of this stub schema.(because you need a schema to represent a component in Tridion)

Interface: Interfaces in following figure are used in development.








aDam
  • aDam Studio installed
  • Rest Service to access its content - in aDam 5.0 there was no open API/oData etc were available. In current 5.0 version there is no official API comes with aDam installer. but aDam guys provided this Sample Rest Service which can be extended, as i discussed with them, API will be released in next versions.
Few Terminologies
  • Classification: its basically taxonomy of aDam, and content in CME would also categorized by classification. 
  • Record: Assets are called Record in aDam. 
Development.  
Lets move to development side, i will keep code short as possible with a view to brevity and clarity.

Here i am declaring that my connector will show two types of item in the interface, first one is a folder, which is collection of items, in aDam case its Classification. Rest methods are standard implementations.

AdamEclProvider : IContentLibrary 
it initialize the AddIn, this is the class which is configured in ExternalContentLibrary.xml.

[AddIn(
    "AdamProvider",
    Description = "Adam External Content Library Provider",
    Publisher = "Raj Future Technologies",
    Version = "1.0.0.0")]
    public class AdamEclProvider : IContentLibrary

    {
public IList<IDisplayType> DisplayTypes
        {
            get
            {
                return new[]
                    {
                        HostServices.CreateDisplayType( "clf", "Classification", EclItemTypes.Folder),
                        HostServices.CreateDisplayType( "ast", "Assets", EclItemTypes.File),
                    };
            }

        }

     }//Class end


AdamContext : IContentLibraryContext:

This call has code for connecting to external sources and retrieval of the content. 

internal class AdamContext : IContentLibraryContext

    {

/*this method is triggered when user click on MountPoint, its fetches the classifications from the Adam and show there
now when that particular folder is clicked, sub classicifations and assest are displayed. */

public IFolderContent GetFolderContent(IEclUri parentFolderUri, int pageIndex, EclItemTypes itemTypes)
        {
                       
            List<IContentLibraryListItem> result = new List<IContentLibraryListItem>();
            if (parentFolderUri.ItemType == EclItemTypes.MountPoint && itemTypes.HasFlag(EclItemTypes.Folder))
            {
//AdamHelper is kind of facade to get the information from service
                            var classifications = AdamHelper.GetClassifications();
                foreach (var classification in classifications)
                {
                    result.Add(new AdamListClassification(parentFolderUri.PublicationId, classification.id.ToString(), classification.Name));                
                
                }
                
           canSearch = true;
            }
            else if (parentFolderUri.ItemType == EclItemTypes.Folder && parentFolderUri.SubType == "clf" && itemTypes.HasFlag(EclItemTypes.File))
            {
               // throw new Exception("iam here");
                var records = AdamHelper.GetRecords(parentFolderUri.ItemId);
                
                result.AddRange()//
            }
                return AdamEclProvider.HostServices.CreateFolderContent(parentFolderUri, result, false, canSearch);


        }
//fetch particular item. 
public IContentLibraryItem GetItem(IEclUri eclUri)
        {
                       if (eclUri.ItemType == EclItemTypes.File && eclUri.SubType == "ast")
            {
                var record = AdamHelper.GetRecord(eclUri.ItemId);
                return new AdamAsset(eclUri.PublicationId, record, eclUri.ItemId);
            }
            throw new NotSupportedException();

        }
//fetch item list
 public IList<IContentLibraryItem> GetItems(IList<IEclUri> eclUris)
        {
///code goes here to call the aDam helper
}

} //Class end

//This class represent one type of asset, so if you have image, video you have to implement it for  each of the asset.

public class AdamAsset : IContentLibraryMultimediaItem
    {
public AdamAsset(int publicationId, RecordData record, string parentId)
        {
            _parentId = parentId;
            _record = record;
            _id = AdamEclProvider.HostServices.CreateEclUri(publicationId, AdamEclProvider.MountPointId, record.id.ToString(), "ast", EclItemTypes.File);

        }

public string DisplayTypeId
        {
            get { return "ast"; }

        }

}
}//Class end

//Represent a classification

public class AdamClassification : IContentLibraryListItem, IContentLibraryItem 

    {

    }

Note: You have to set WebReady property to "Yes" from Files tab to view the asset in Tridion.

with above code you will able to display the asset in CMS and will able to include it in the native Tridion components, for publishing those there would be additional template code. which i did not do as scope of my POC and different direction of the project.


Official Adam Connector: Official aDam connector is also available from SDL which is licensed Separately. You can check with SDL for it.

Let me know if you have any questions or need any help, feel free to comment below.

July 09, 2014

Content Porter - How it works

Content Porter(CP)  is integral part of SDL Tridion development to transfer content in DTAP environments. I have used it in various releases 5.3 sp1, 2009, 2011 etc. Its has been improved a lot in each of the releases. 2013 sp1 claims to be fastest CP ever.

Next set of lines i will explain some internals of content porter. 

Following tools/services are behind the import/export of a Content Porter package.
  • Content Porter Client
  • Import Export Service
  • Core Service
 1) Content Porter Client - this tools is generally downloaded on the client machine from the CM GUI interface. 
It acts as thin client for user, which send user selections/settings to server and show the progress of import/export.

 2) Import Export Service - It is installed after running Content Porter server setup. Here import/export happens. It basically groups and wraps different method of Core Service.

if you open: http://CMSURL/ImportExportService/ImportExportService.asmx. You will see some endpoints. and from their names you can figure out the use.

Another URL http://CMSURL/ImportExportService/UploadPackage.aspx:  its used to upload package from client to Server side.

 3) Core Service - Its is installed as part of Tridion CM installation. It is used by Import/Export service APIs to facilitate the import/export of content.

Export Process: 

  1. User selects content to be exported.
  2. and that selections are sent to Import/Export Service
  3. Import export service calls Core service APIs and complete Package is generated at server file-system
  4. Progress of that is shown to user on CP client
  5. at last exported package is sent back to User Machine via CP client.
Fig-1: Export Process

                                                               


Import Process

  1. User selects package to be imported
  2. Package is uploaded at server with help of client & import/export service
  3. Service extract the package to a temp folder
  4. Service read the package
  5. Then items are updated in the CM DB using coreservice



Fig-2: Import Process

So in both cases(export/import) most of the work is performed at server side,  so it does not matter much from where you actually trigger content porter.

 

Copyright 2010 All Rights Reserved