December 20, 2017

Tutorial for Tridion Powershell commands

In my last blog post, i demonstrated the setup of Powershell for beginners. Now i will discuss more commands of Tridion Powershell.

List of commands: following Power shell command will return all the commands available in Tridion Powershell module.

PS C:\Windows\system32> Get-Command -Module Tridion-CoreService

  • Clear-TridionCoreServiceSettings
  • Close-TridionCoreServiceClient  
  • Convert-TridionApplicationData  
  • Get-TridionUser - there is also Get-TridionUsers which is not listed by get-command
  • Disable-TridionUser             
  • Enable-TridionUser              
  • Get-TridionApplicationData      
  • Get-TridionCoreServiceClient    
  • Get-TridionCoreServiceSettings  
  • Get-TridionGroup                
  • Get-TridionGroups               
  • Get-TridionItem                 
  • Get-TridionPublications         
  • Get-TridionPublicationTarget    
  • Get-TridionPublicationTargets   
  • Get-TridionPublishTransaction                
  • New-TridionGroup                
  • New-TridionItem                 
  • New-TridionPublication          
  • New-TridionUser                 
  • Publish-TridionItem      
Lets try few of the commands to unleash power of Powershell 

Get-TridionPublications:
Above command it will list all the publication details with lots of additional information.  You can also filter the the number of fields to show and in nice format. below example show just Title, Id and PublicationType.








User Based Operations:


To get all users those are not currently active. PS module make is very easy to fetch those details and take action if required. Firstly all the Users are fetched then filtered on rows, then on columns respectively.

PS C:\Windows\system32> Get-TridionUsers | where  {$_.IsEnabled -eq $false} | select Title, IsEnabled, Description

Title                                                                 IsEnabled Description
-----                                                                 --------- -----------
SDLDEVCMS\rkum                                                      False Raj Kumar

Enabling all the disabled users at once:

Get-TridionUsers | where  {$_.IsEnabled -eq $false} | Enable-TridionUser

otherwise if you just use Enable-TridionUser command, you have to pass one by one to enable/Disable the users


Publish Transactions Operations:
To get the status of transactions, you manipulate the results on various parameters

Following command with list out all the failed transactions in publishing queue. 

Get-TridionPublishTransaction | where {$_.State -eq "Failed"} | select Title, Id, State

List out tranactions grouped by State

PS C:\Windows\system32> Get-TridionPublishTransaction | Group-object State




December 19, 2017

Load balancing DXA and CD servers in Web 8

With new micro-services architecture its very efficient to distribution of workloads of different servers. Dxa servers aka Presentation servers can be load balanced separately.



Features of above architecture:
  • Works best in fail-over scenarios. if any service is down other available can take the load
  • Presentation machines can be added or removed on the go without worrying about setting up the CD servers.
  • Its cost efficient scenario.

I tested above above on Microsoft Azure, presentation servers were load balanced on Application gateway.
and CD servers were load balanced using Azure Load balanced.

How does it work?

Settings:
  • DXA web.config is configured with IP address of Load-balancer for Discovery service.  
  • Required capabilities listed in Discovery service has URLs of Load balancer. 
  • Discovery DB is shared among all the discovery servers.
  • Load Balancer is aware of available nodes of services.
when request comes to Application gateway, AG get it processed from one of the available DXA server. if Dxa app is already loaded and request pages is already processed and cached it can be processed completly on DXA machine itself.

otherwise Discovery Service URL is hit which goes to internal load balancer. Load balancer get this executed from one of CD server. AS discovery had configured address of Load-balancer for Content Service, request once again go to LB. it then redirects it to available Content service node. so a request is completed.

With use of Akamai and other caching strategies e.g page caching or donut caching and other tridion caching techniques CD servers might get reduced load after first hit and boot time. DXA servers are easy to Auto-scale.

December 12, 2017

Introduction to Tridion Powershell

What is PowerShell:
Windows PowerShell is a Windows command-line shell designed especially for system administrators. Windows PowerShell includes an interactive prompt and a scripting environment that can be used independently or in combination.
Windows PowerShell is built on top of the .NET Framework common language runtime (CLR) and the .NET Framework, and accepts and returns .NET Framework objects. Its also extensible.

Introduction to Tridion Powershell:


Peter Kjaer has developed Tridion Powershell Module  which contains cmdlets that allow you to talk to the Tridion Content Manager using the Core Service. This is very powerful and fast way to access Tridion system without opening CME. It Currently supported Tridion versions: 2011 SP1, 2013 GA, 2013 SP1 and Web 8.

Setting up Tridion Powershell:

Open Powershell windows as administrator. 
iwr "https://raw.githubusercontent.com/pkjaer/tridion-powershell-modules/master/CoreService/Installation/Install.ps1" | iex











Tridion Powershell module will be installed with above success message.

Verify Settings:
Run a command to test the environment. if there are issues in settings you may can following error



Get-TridionCoreServiceSettings will give you current settings its using. 
but ensure these settings are as per your environment

In my case, Coreservice was running at another port, so commands were failing. so you might have to update the setting as per need. 

Update Settings:

PS C:\Windows\system32> help set-TridionCoreServiceSettings

NAME
    Set-TridionCoreServiceSettings

SYNOPSIS
    Changes the settings used to connect to the Core Service.
when 
SYNTAX
    Set-TridionCoreServiceSettings [[-HostName] <String>] [[-Version] <String>] [[-Credential] <PSCredential>]
    [[-ConnectionType] <String>] [[-ConnectionSendTimeout] <String>] [-Persist] [<CommonParameters>]

Following command changes the hostName on my environment. You can update as per your enviornnment.

Set-TridionCoreServiceSettings -HostName localhost:91

after the setting are done, retry following command

Get-TridionPublications to get list of publications in the system.

December 08, 2017

Whats up SDL

SDL Web 8.5 was last major release by Sdl around start of this year. This year has been very challenging in IT for everyone. SDL ranked as Challenger in Gartner's magic quadrant. Sdl sold its  non-core products: “Social Intelligence, Campaigns, and SDL Fredhopper to focus on core.

SDL is working hard and there are number of changes happening under the hood, and impressive Roadmap.

Lets start with Major change:


SDL Tridion DX


SDL rebrands SDL Web to SDL Tridion Sites and SDL Knowledge Center to SDL Tridion Docs, and "Tridion" is back and i am so happy like any other community member.



SDL Tridion DX combines SDL Tridion Sites (WCM) and  DITA-based Structured Content Management (SDL Tridion Docs) and translation technology supported by artificial intelligence (AI)

Check Press release for more information on above

Graphene UI: is awesome, Check this Future UX & UI blogs to check the impressive work that is happening. I am impressed!!

GraphQL services: GraphQL will be replacing OData services.

Integration & connectors: As per connector strategy SDL is developing connectors for lots of products, tools and services.
e.g Sales force marketing cloud, Marketo, Microsoft dynamics, Experience Regions, SDL ETS on AWS and many more eg. Image Editing with in CMS

Salesforce Community Cloud integration: this is just released check out.(updated after release)

DXA Form Builder Modules:


Form builder is always requested by clients but SDL gets X in the capability matrix, , but i just to get know Niclas Cedermalm from SDL just released form builder DXA module with name "Alpaca Forms", Also its open source. I am writing it here for community awareness. i will write a separate blog on it. It currently supports DXA 1.7 more supporting version later.

HTML5 forms module is based on Twitter Bootstrap and Alpaca.JS which uses JSON and inturn used to generate form on Delivery side.

Forms are CMS component like any other component and can be added to pages. End Points can be configured to APIs. Customization is also possible as you can write your custom code to pass data to required ice. The Forms DXA module also comes with a built-in controller that can send form data to different endpoint such as DB, e-mail and CRM.

Personalization:
SDL is redesigning the whole experience for personalization and it looks very promising.

Prescriptive Personalization:  this is based on predefined  rules by author.
Adaptive Personalization: Machine language based personalization. 

November 30, 2017

Content migration from Gather Content to SDL Tridion

Content Migration Automation in SDL requires use of custom code written using Sdl Core services. Core services are set of APIs which helps in ingestion of data in to CMS system. But i have found people writing repetitive code to ingest data into CM.

Its not as complex as Elon Musk reusable Rockets. I mean its not rocket Science.

So write code to interact with SDL once, and use that to deploy the payload into required system.

Here is the process:


Gather Content:

Instead of getting content into excel or world, having content in Gather Content gives lots of advantages.

Gather content tool provides very simple and intuitive interface to enter the content and images. it allow collaboration, simple workflow, activity trails and CMS like interface. Collecting data from various users becomes very simple.

GC comes with direct import feature for various CMS, but Tridion is not yet supported.

Content Export:

After getting content into GC, its must be exported, there are multiple ways you can export the content,
- API
- Export to CSV

Adapter:

Adapter is system which converts above exported content into required format, this would be typically Schema format of  SDL component. This is little tricky but not that complex, Little  complexity is involved when dealing with linked component is required. Output of adapter is set of Xml files and config files which provides other required information for component to ingest.

Migration Tool:

Your organization should have this tool which accepts Content as XML and config files and ingest the content into system. As this tool is already tested and proven, so ingestion should be smooth if your adapter has created files well formatted.

In this way you can speed up the content migration part

November 26, 2017

SDL dxa compatibility table



Compatibility Chart 


DXA
1.5
DXA
1.6
DXA 1.7 DXA 1.8 DXA
2.0
Tridion 2013 - - - -  -
Tridion 2013 SP1 HR1  ✓d - -  -
SDL  8.1.1  ✓
SDL 8.5 - -  ✓
SDL Cloud - -
-
Content Interaction Library
CIL 8.2
CIL 8.3
CIL 8.5

CIL 10.1

 ✓
.net and Java versions
Java 7+
Java 8
.net 4.5.2

November 05, 2017

Compatibility Chart of SDL Tridion and DXA.



Compatibility Chart 


Tridion 2011 Tridion 2011 SP1 Tridion 2013 Tridion 2013 SP1 HR1 SDL  8 SDL 8.5
Microsoft Edge - - - - - -
Internet Explorer 11 - - -
Internet Explorer 10 - -
Internet Explorer 9 - - -
Internet Explorer 8 - - - -
Internet Explorer 7 - - - -
Internet Explorer 6 - - - - - -
Mozilla Firefox
Google Chrome
Safari for Mac
Operating System
Microsoft Windows Server 2012 R2 with Update x64  Y
Microsoft Windows Server 2012 R2 x64  ✓  ✓
Microsoft Windows Server 2012 x64  ✓  ✓Dr
MS Windows 2008 R2 SP1 x64  ✓  ✓  ✓  ✓
Microsoft Windows Server 2008 R2 x64  ✓D
Microsoft Windows Server 2008 SP2 x86/x64  ✓D
Windows Server 2003 x86, x64
Windows 10 x86, x64
Windows 8.1 x86, x64  ✓  ✓
Windows 8 x86, x64  ✓  ✓  ✓  ✓Dr
Windows 7 Sp1 x86, x64  ✓  ✓  ✓  ✓  ✓
Database Server
AWS RDS-MSSQL 2016 -
AWS RDS-MSSQL 2012 ✓P
Microsoft Azure SQL database - - - - Y
MS SQL 2016 SP1 - - - - -
MS SQL 2014 SP2 ✓P
MS SQL 2014 SP1 - - - - -
MS SQL 2012 SP3  -  -  -  - ✓P
MS SQL 2012 SP2  -  -  -
MS SQL 2012 SP1  - ✓dr  - -
MS SQL 2008 R2 SP3  -  -  ✓  -  -
MS SQL 2008 R2 SP2  ✓ ✓dr  -  -
MS SQL 2008 R2  ✓dr  - - - -
MS SQL 2008 SP3  - - - - -
MS SQL 2008 SP2  - - - - -
MS SQL 2008 SP1  - - - - -
Microsoft SQL Server 2005 SP4  ✓P  - - - -
Microsoft SQL Server 2005 SP3  ✓Dr  -  -  -  -
Oracle Database 12c patch set 12.1.0.2  -  -  ✓  ✓  ✓
Oracle Database 12c patch set 12.1.0.1  -  -  ✓dr -  -
Oracle Database 11g Release 2 patch set 11.2.0.4  -  -  ✓  ✓  ✓
Oracle Database 11g Release 2 patch set 11.2.0.3  -  ✓  ✓dr  -  -
Oracle Database 11g Release 2 patch set 11.2.0.2  ✓  -  -  -  -
Oracle Database 11g Release 2 patch set 11.2.0.1  ✓dr  -  -  -  -
Oracle Database 10g Release 2 patch set 11.1.0.7  - - - - -
Oracle Database 10g Release 2 patch set 10.2.0.5  ✓  ✓dr  -  -
Oracle Database 10g Release 2 patch set 10.2.0.4  - - - - -

October 30, 2017

SDL MVP retreat 2017

MVPs are few chosen Tridion professional who are selected based on yearly contribution to SDL community. SDL pampers those professional with work, sea food, music and unlimited Daaru in retreat. SDL retreat provides chance to visit Portugal again if you miss any place during last years.

SDL retreat is one of the most awaited event of the year for MVPs. It motivates for next year.

"This is always 5 days event, SDL treats you like Damad except few Fatwas:

  • From the moment you arrive to your departure airport until you fly back home, nearly all expenses are covered. If you decide to get drunk in a bar at the airport, I expect that you earn enough to pay for it yourself and not present me with an expense receipt for it, or I’ll remember next year.
  •  If you lose your flight due to above mentioned state of drunkenness, I also expect you to be man enough to realize it’s your damn problem to fix.

Few tips from one of the Permanent and Veteran Member

  1. Keep on the right side of Carla
  2. Don't try to out-drink Nuno 
  3. Don't expect to go home thinner
  4. If you at all musical (even slightly), we will expect you to enztertain us, and yourself. Even long-buried musical talents have been unearthed at the retreat. But there are no rules. And of course, bring your instrument(s).
  5. Expect the unexpected from your fellow-MVPs and yourself
My Biggest worry:

"What kind of Veggy food, No eggs" will be served to me in. *T*: Veggy food in Portugal?

Few Testimonials 

Carla:
"Well I'm actually thinking of doing all vegetarian meals FOR EVERYBODY and alcohol free retreats from now on"
Curlette:
"Great to have you back and finally someone more picky than me with food :) Already making me look better Haha ;)"
But to my surprise food was really good this time with few exceptions. My biggest helper was Garlic butter their which provided taste to food.


Day 1:
People gathers at Airport to meet and greet, then drivers drove to the Hotel Pousada de Ourem in beautiful city of Ourem. People drinks, eats and sleep to get early for the big day.

Day 2:

MVPs gathers at conference room for connect on upcoming changes in SDL product and road-map. Nuno and other Sdl teams showcase ongoing work. Some goodies to collect on EOD.

Day 3 & 4

Time to code and shocase next day. I teamed up with The Nuno and Sayantan to develop "Poor Man's AWS Cloudwatch Monitor" We successfully created & scheduled it on  Windows Scheduler to send queue status every 2-3 minutes to cloud watch. Night is full of fun, drinks and special musical performances by Quirijn, Raimond on Piano, Guitar..  give them any Instrument, these guys are exceptionally good. Rick and Johnathan also surprised with their Piano and singing skills respectively. Dominic also tried something with his mouth instruments before sleeping. 

also we saw some nazare @  Nazaré

Day 5

Time to say Good Bye & promises to meet again.

October 21, 2017

Publishing queue status on AWS Cloud-watch

In this years MVP retreat we did small Proof of concept to show the Publishing queue status on AWS cloud-watch in nice format. There are various ways to accomplish it.

- Using CoreService API to access the Queue.
- Directly querying the DB (not very recommended)
- PowerShell to read the queue (Yes its possible)

We choose last option as some Tridion Powershell Module created by SDL MVP Pkjaer makes it very easy.

Steps:

- Install Tridion Powershell module, details in above link.
- Install AWS Powershell module(s) on machine to work.
- Here is code snippet which do following

  • Import AWS Powershell 
  • Create Cloudwatch matrices based on two states "Waiting for Publish" and Waiting for Deployment"
  • fill the matrices with count of above two states
  • Write the data to cloudwatch


 Import-Module "C:\Program Files (x86)\AWS Tools\PowerShell\AWSPowerShell\AWSPowerShell.psd1"  
 $dat = New-Object Amazon.CloudWatch.Model.MetricDatum  
 $dat.Timestamp = (Get-Date).ToUniversalTime()  
 $dat.MetricName = "Waiting For Publish"  
 $dat.Unit = "Count"  
 $dat.Value = (Get-TridionPublishTransaction | Where-Object {$_.State -eq "WaitingForPublish"}).Count  
 $dat1 = New-Object Amazon.CloudWatch.Model.MetricDatum  
 $dat1.Timestamp = (Get-Date).ToUniversalTime()  
 $dat1.MetricName = "Waiting For Deployment"  
 $dat1.Unit = "Count"  
 $dat1.Value = (Get-TridionPublishTransaction | Where-Object {$_.State -eq "WaitingForDeployment"}).Count  
 set-AWSCredentials -AccessKey -SecretKey FT1nwN74NHkGtlJ -StoreAs "MVP17"  
 Set-AWSCredentials -ProfileName "MVP17"  
 Write-CWMetricData -Namespace "Usage Metrics" -MetricData $dat  
 Write-CWMetricData -Namespace "Usage Metrics" -MetricData $dat1  

Auto-scaling:

One of the purpose to push the queue data to AWS Cloudwatch is to configure the auto-scaling of instances.

if there are lots of items in queue, auto-scaling of publishing system can be configured on AWS infrastructure.



September 30, 2017

Migrate Images in Media Manager automation

Consider the use-case of uploading 7500 images manually and linking each image with component in SDL  Tridion. Its very tedious tasks to do and if any change in images and need to redo 2-3 times. you need hell of a time and dedication.

So Best approach is to Automate set of tasks so that it can be imported into Media Manager and Linking can also happen side by side.

So need following set of services to accomplish this task:

  1. SDL Tridion
  2. Media Manager Cloud instance
  3. STS Service
  4. SDL Media manager Client
  5. ECL Service Client
Part 1

Connecting to Media manager

STS - Security token service (STS) is a cross-platform open standard core component of the OASIS group's WS-Trust web services single sign-on specs.

STS is used to authenticate between Migration Tool and SDL media manager. setting up it required configuring public key of the  Media manager. It was the hardest part for this whole stuff.

- Generate proxy from the Media Manager Service
-  STS service should be in the running state.
- Migration tool will connect to MM using Federated Security with STS service providing the encoded MM's public certificate.
- Once connection is established the upload process triggers.

There would be two bindings in app.config  one for connecting to mediaManager another to connect to STS service running on Url like following : http://localhost:89/IWSTrust13

Note: STS service is not provide OOTB by SDL. You have to code is based on STS standard or talk to SDL if they have ready to use such service which can be leverages.

Part 2
This section is involved in ingesting assets into DAM.

Following are code snippet for digital assets migration/ingestion. code. first section create a program id and upload image

second code snipped create a distribution associated with program id.

 public string UploadFile(long folderId, long assetTypeId, long[] tags, string keywords, string fullPathToFileToUpload)  
     {  
       MediaManager2011Client mediaManagerService = null;  
       try  
       {  
         Logger.Info("Uploading Image");  
         UploadInfoData data = new UploadInfoData();  
         data.DistributionCreation=DistributionCreationOptions.OneDistributionPerItem;  
         data.IsIncludedInGalleries = true;  
         data.MakeAvailableForDownload = true;  
         data.MakeAvailableForWebPublishing = true;  
         data.ProgramCreation = ProgramCreationOptions.OneProgramPerItem;  
         mediaManagerService = new MediaManager2011Client("FederationEndpointHttps");  
         string uploadUrl = mediaManagerService.GetUploadUrl(folderId, assetTypeId, tags, keywords, data);  
         WebClient webClient = new WebClient();  
         var response= webClient.UploadFile(uploadUrl, fullPathToFileToUpload);  
         WebHeaderCollection headers = webClient.ResponseHeaders;  
         string assetIdAsString = headers["AssetId"];  
         string programIdAsString = headers["ProgramId"];  
         Logger.Debug(string.Format("Asset Id:{0} & ProgramId:{1} For Uploaded Image", assetIdAsString, programIdAsString));  
         return programIdAsString;  
       }  
       catch (Exception ex)  
       {  
         Logger.Error(string.Format("Exception caught while uploading image-{0}", ex.Message));  
         return null;  
       }  
       finally  
       {  
         if (mediaManagerService != null)  
         {  
           if (mediaManagerService.State == System.ServiceModel.CommunicationState.Faulted)  
           {  
             mediaManagerService.Abort();  
           }  
           else  
           {  
             mediaManagerService.Close();  
           }  
         }  
       }  
}

  public ItemData CreateNewDistributionData(long programId,long folderId,string distributionName, long outletId, IMediaManager2011 client)  
     {  
       var distributionData = new DistributionData();  
       distributionData.Name = distributionName;  
       distributionData.OutletId = outletId;  
       distributionData.OutletType = OutletClassifier.Image;  
       distributionData.ProgramIds = new[] { programId };  
       distributionData.IsLive = true;  
       distributionData.FolderId = folderId;  
       return client.AddItem(distributionData);  
     }  

Part 3
after images are ingested into Media Manager DAM, those needs to be linked to required components in the Tridion. so for External Content Library has to be used. Installation and configuration of ECL are very well documented on SDL Docs.

ECL Service client - Generating ECL stub is quite a task, it involves getting Media manager id and generate a stub in the CMS which actually create a component in CMS with Tcm-id. 

Stub Uri has following format: "ecl:{publicationId}-mm-{dist-Id}-dist-file"
ECL client is used to create ECL stubs in CMS. Check Detailed Article

September 27, 2017

Automation of ECL stubs creation

To generate a Stub for the image from external sources using External Content library,  its tricky to generate stub. if you have 100s of images to be imported and then putting those into SDL Tridion using ECL, its very time consuming

so we used following way to generate stub at run-time


Code Snippet:

This code create the ECL compatible URI which will be later used to generate the actual stub

 public string GetUris(string publicationId, CustomImage image)  
 {  
             if (image.d != null)  
             {  
               return string.Format("ecl:{0}-mm-{1}-dist-file", publicationId, image.Id);  
             }  
  }  


Following code is baseclass and instantiate the client to create the stub using following settings
    <endpoint name="EclBinaryEndpoint" address="net.tcp://localhost:2660/ExternalContentLibrary/2012/netTcp" binding="netTcpBinding" bindingConfiguration="EclNetTcpBinding" contract="Tridion.ExternalContentLibrary.Service.Client.ISessionAwareEclService"/>  


 public abstract class TridionEclBase  
   {  
     protected static Client tridionClient { get; set; }  
     public TridionEclBase()  
     {  
       tridionClient = Client.GetClientInstance();  
     }  
   }  

following snippet was used to create the actual stub in the CMS

 public class CreateImageStub : TridionEclBase  
   {  
     private SessionAwareEclServiceClient eclServiceClient;  
     public CreateImageStub()  
       : base()  
     { }  
     public Dictionary<string, string> CreateImageStubByEclUir(List<string> eclUrl)  
     {  
       return eclServiceClient.CreateOrGetStubUris(eclUrl);  
     }  
   }  

it returns key value pair

ECLID as key
TCMID as Value

July 26, 2017

Localization Best practices for pages and pagemeta

In multilingual websites, localization of Page-Meta is something which is tricky as metadata is either part of structure group if you want to apply for set of pages or part of page.

take the scenario of 10 countries , two language each, so you have 20 websites where you have to localize the pages(50 pages per site) of your website. if you go by flat meta data schema for pages. 

or you create a language master. where you localize the pages for the metadata. 



Some issues:
  • Localization needs to be done in multiple publications for each Language.
  • Translation Manager : Multiple publication needs to be localized for each Language e.g. for French both “Content Translation Fr” and “500 Website Parent French”.
  • Priority conflict between Localized Page in “500 Website Parent French” and “500 Belgium Website English”.
  • Blueprinting Complexity Increases.
Approach we took:

- Keep metadata in Component















- In Metadata schema create a component link for that.













Approach Benefits:
  • Only one Publication needs to be localized i.e. “300 Translation Fr”
  • Translation Manager : Metadata for Page gets localized with Content.
  • Priority conflict resolved. Country Master Website  takes priority as there is no Language Website Master.
  • Blueprinting Complexity is reduced.

Any Cons:

As we were using page types to create page similar pages. As page types do not clone the component links, so every pages started having same component which we added to sample 
page

Solution: in next post.

May 29, 2017

Unpublish Dynamic Component when Page is unpublished

We have a setup where component is dynamic and its also used on Page. But by-design in Tridion if you un-publish a Page which is using a dynamic component, it does not un-publish the component.


We have to fetch the component based on the metadata at various places and attached on page where component was also required on page for following requirements.
  • Author driven Page name
  • Author driven page Url
  • Author driven page metadata.
so following event code was written to handle the scenario.  in our scenario 

 [TcmExtension("UnpublishEventHandler")]  
   public class UnpublishEventHandler : TcmExtension  
   {  
     public UnpublishEventHandler()  
     {  
       EventSystem.Subscribe<Page, UnPublishEventArgs>(UnpublishComponent, EventPhases.TransactionCommitted);  
     }  
     public void UnpublishComponent(Page page, UnPublishEventArgs mUnPublishEventArgs, EventPhases mEventPhases)  
     {  
       var appConfig = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location);  
       string cts = appConfig.AppSettings.Settings["UnpublishPageComponentTemplates"].Value;  
       IList<ComponentPresentation> cpList = page.ComponentPresentations;  
       List<IdentifiableObject> items = new List<IdentifiableObject>();  
       if (cpList != null)  
       {  
         foreach (ComponentPresentation cp in cpList)  
         {  
           if (cts.ToLowerInvariant().Contains(cp.ComponentTemplate.Title.ToLowerInvariant()))  
                        items.Add(cp.Component);  
         }  
         IEnumerable<IdentifiableObject> itemsenum = items;  
          List<TargetType> targets = new List<TargetType> { };  
          foreach (var pubTarget in mUnPublishEventArgs.Targets)  
          {  
            targets.Add((TargetType)pubTarget);  
          }  
          var transaction = PublishEngine.UnPublish(itemsenum, mUnPublishEventArgs.UnPublishInstruction, targets);  
       }  
     }  
   }