String Writer

my tech stuffs…

Posts Tagged ‘TOM

Delete keywords in Tridion

with 5 comments

As posted earlier, I completed the keyword deletion tool. I’ve tried both approaches (explained in my earlier post) to implement this keyword deletion. Finally I’ve decided to go with deleting keywords from bottom to top that’s 2nd approach.

How to call it

All you need is just to create class object with TCM Uri of a parent keyword whose child keywords are to be deleted and call its delete method.


//initialize the class with keyword TCM Uri value
TCMKeywords keywords = new TCMKeywords(ConfigurationManager.AppSettings["TCMURI"]);
//deletes keyword branch
keywords.Delete();

Here we don’t need to pass its publication TCM Uri since that can be retrieved from its keyword TCM Uri. I’ve given TCM Uri in my app.config file so that I can change it whenever I needed.

Call child keywords recursively

Tridion API does not provide direct method to get all child keywords in hierarchical order. But we can write a recursive call just like the way we do it for any other tree structure objects.


/// <summary>
/// Recursive method to navigate all keywords
/// </summary>
/// <param name="parentTCM">TCM Uri of parent keyword</param>
/// <param name="level">Hierarchy level of keyword</param>
private void NavigateKeywords(string parentTCM, int level)
{
   //gets all child keywords from given parent
   XmlNode keyNode = this.GetListChildKeywords(parentTCM);

   foreach (XmlNode key in keyNode.ChildNodes)
   {
      //loops through its next level
      NavigateKeywords(key.Attributes["ID"].Value, level + 1);

      //deletes keyword if it doesn't have any child
      this.DeleteKeyword((Keyword)this.GetObject(key.Attributes["ID"].Value), level);
   }
}

Obviously a keyword cannot be deleted if it is being used by other items. So all other associations should be removed before deleting it. That’s the reason DeleteKeyword(…) is being called after NavigateKeywords(…). That means if keyword (A) do not have any child, keyword (A) will be considered for deletion.

GetListChildKeywords(…) is a private method to retrieve all child keywords in Xml type of object.


/// <summary>
/// Gets all child keywords in xml object
/// </summary>
/// <param name="parentKeywordTCM">Parent <see cref="Keyword">Keyword</see></param>
/// <returns>Child keywords in <see cref="XmlNode">XmlNode</see> object</returns>
private XmlNode GetListChildKeywords(string parentKeywordTCM)
{
   //gets all child keywords
   string keyXML = ((Keyword)this.GetObject(parentKeywordTCM)).GetListChildKeywords(EnumKeywordSortType.SortTypeTitle,ListColumnFilter.XMLListID, (ListRowFilter)TObj.CreateListRowFilter());

   //returns output as xml object
   return this.PopulateXMLNode(keyXML);
}

ListColumnFilter –Other attributes also can be retrieved other than ListID. But we need only list id for this tool.

ListRowFilter – Here we are getting all rows so we are not setting any filter conditions. This can be set based upon our requirements. Let’s look at this code sample if filtering condition is needed.

Deleting a keyword

The keywords in Tridion can be used in following ways.

  1. Classified with other building blocks
  2. Used in component or metadata fields
  3. Localized in its child publications

A keyword should be released from the all above associations before deleting it.


//releases keyword from its all dependencies
if (this.ReleaseKeyword(keyword, keyLevel))
{
   //deletes keyword
   keyword.Delete();

   Console.WriteLine("{0}{1} deleted...", keyLevel, keyword.Title);
}

ReleaseKeyword(…) is a private method to unclassify and unlocalize the keywords.


/// <summary>
/// Releases the keyword from its dependencies
/// </summary>
/// <param name="keyword"><see cref="Keyword">Keyword</see> object to be released</param>
/// <param name="keyLevel">Hirarchical level of a keyword</param>
/// <returns>Returns <see cref="bool">bool</see> if released or not</returns>
private bool ReleaseKeyword(Keyword keyword, string keyLevel)
{
   //default status
   bool released = false;

   //unclassifies the keyword
   released = this.Unclassify(keyword, keyLevel);

   //unlocalizes the keyword from its all child publications
   if (released)
      released = UnLocalize(keyword, keyLevel);

   //returns released status
   return released;
}

Unclassifying keywords

In Tridion, the content or building blocks are generally classified with keywords.

Classified means that it can be in either way,

1) Keyword can be used in any of field values in metadata or components.

2) An Tridion object is classified against keyword.

These two associations should be removed as part of unclassification.

For example, if a keyword is classified with 10 Tridion objects, we need to retrieve those 10 objects and call them separately its own unclassify method.

The problem here is a keyword in one publication would be derived into its child publications. In child publications, these keywords might have classified with other objects. Hence the appropriate keyword from the same publication should be retrieved to unclassify it.


/// <summary>
/// Unclassifies the keyword
/// </summary>
/// <param name="keyword"><see cref="Keyword">Keyword</see> object to be released</param>
/// <param name="keyLevel">Hierarchical level of a keyword</param>
/// <returns>Returns <see cref="bool">bool</see> if unclassified or not</returns>
private bool Unclassify(Keyword keyword, string keyLevel)
{
   try
   {
      //gets all using items from all publications
      string classifiedItemsXML = keyword.Info.GetListUsingItems(ListColumnFilter.XMLListDefault,(ListRowFilter)TObj.CreateListRowFilter());

      //converts xml string to xml object
      XmlNode xmlDoc = this.PopulateXMLNode(classifiedItemsXML);

      string tcmChild = null;
      //default publication is set to current publication

      string pubURI = this.PublicationURI;

      foreach (XmlNode child in xmlDoc.ChildNodes)
      {
         //gets keyword tcm uri
         tcmChild = child.Attributes["ID"].Value;

         //checks if publication of child object is same as its parent keyword
         if (!this.IsSamePublication(tcmChild))
         {
            //gets publication tcm uri from given tcm uri
            pubURI = GeneratePublicationTCM(tcmChild);

            //gets the same keyword object from its child object publication
            keyword = (Keyword)this.GetObject(pubURI, ReplacePublicationID(keyword.ID, this.GetPublicationID(tcmChild)));
         }

         //checks its child object type and unclassify
         switch (child.Attributes["Type"].Value)
         {
            case "2":
               ((Folder)this.GetObject(pubURI, tcmChild)).UnClassify(keyword);
               break;
            case "4":
               ((StructureGroup)this.GetObject(pubURI, tcmChild)).UnClassify(keyword);
               break;
            case "16":
               ((Component)this.GetObject(pubURI, tcmChild)).UnClassify(keyword);
               break;
            case "32":
               ((ComponentTemplate)this.GetObject(pubURI, tcmChild)).UnClassify(keyword);
               break;
            case "64":
               ((Tridion.ContentManager.Interop.TDS.Page)this.GetObject(pubURI, tcmChild)).UnClassify(keyword);
               break;
            case "128":
               ((PageTemplate)this.GetObject(pubURI, tcmChild)).UnClassify(keyword);
               break;
         }
      }

      //display the output if any of child is unclassified
      if (xmlDoc.ChildNodes.Count != 0)
         Console.WriteLine("{0}{1} unclassified...", keyLevel, keyword.Title);

      return true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Unclassification Failed!");
      Console.WriteLine(ex.Message);
      return false;
   }
}

GetListUsingItems(…) is the common method in all Tridion objects to retrieve its associated items. In this case, this method will return all the objects wherever the keyword is being used.

Here important note is that if a keyword is unclassified, the parent object will not get republished automatically. If you need to republish the parent object while keyword unclassification, you need to set some code and trigger workflow events.

Unlocalizing keywords

Unlocalizing is not a straight forward approach unlike unclassification. There is no method to write all localized keywords.

If there is a keyword in publication, the same keyword has to be checked in its all child publications whether it is localized in child publication level.


/// <summary>
/// Unlocalizes keyword
/// </summary>
/// <param name="keyword"><see cref="Keyword">Keyword</see> object to be released</param>
/// <param name="keyLevel">Hirarchical level of a keyword</param>
/// <returns>Returns <see cref="bool">bool</see> if unlozalized or not</returns>
private bool UnLocalize(Keyword keyword, string keyLevel)
{
   try
   {
      //gets all child publications
      List<string> childPublications = this.GetChildPublications(keyword.Publication.ID);

      foreach (string cPub in childPublications)
      {
         //gets the keyword in each child publication
         Keyword childKey = (Keyword)this.GetObject(cPub, ReplacePublicationID(keyword.ID, this.SplitURI(cPub, 1)));

         //checks if it is localized
         if (childKey.Info.IsLocalized)
         {
             childKey.UnLocalize();
             Console.WriteLine("{0}{1} unlocalized from {2}...", keyLevel, childKey.Title, childKey.Publication.Title);
         }
      }
      return true;
   }
   catch (Exception ex)
   {
      Console.WriteLine(ex.Message);
      return false;
   }
}

GetChildPublications(…) posted earlier (Get Child publications in Tridion)

Output

I created these keywords for my testing. All keywords are classified against a component and two of them are localized in child publications.

clip_image001[1]

I ran the tool and got the following output.

clip_image002[1]

The source code can be copied from this link.

Written by visvabalaji

April 12, 2011 at 10:53 pm

Get Child publications in Tridion

leave a comment »

This code sample will give you all the derived child publications from the specific parent publication. There is no direct method to get all child publications but can be easily retrieved from GetListUsingItems(…).

I needed this method to get a status of a component in all child levels.

/// <summary>
/// Gets all child publications
/// </summary>
/// <param name="parent">TCM Uri of parent publication</param>
/// <returns>Collection of child publications</returns>
private void GetChildPublications(string parentTCM)
{
  //gets current publication object
  Publication pub = tdse.GetPublication(parentTCM);
  //gets row filter object
  ListRowFilter filter = tdse.CreateListRowFilter();
  //filter only publication type
  filter.SetCondition("ItemType", 1);
  //returns using publications as xml
  string itemsXML = pub.Info.GetListUsingItems(ListColumnFilter.XMLListID, filter);

  //initialize xml document object
  XmlDocument xmlDoc = new XmlDocument();
  //loads items xml
  xmlDoc.LoadXml(itemsXML);

  foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes)
  {
    //loops id attribute in each child node
    Console.WriteLine(node.Attributes["ID"].Value);
  }
}

The below code returns all derived publications and remember that this will return all using items if ItemType is not specified.

  //filter only publication type
  filter.SetCondition("ItemType", 1);
  //returns using publications as xml
  string itemsXML = pub.Info.GetListUsingItems(ListColumnFilter.XMLListID, filter);

Output of above line is,

<?xml version="1.0" encoding="utf-8"?>
<tcm:ListUsingItems xmlns:tcm="http://www.tridion.com/ContentManager/5.0">
  <tcm:Item ID="tcm:0-123-1" />
  <tcm:Item ID="tcm:0-124-1" />
  <tcm:Item ID="tcm:0-125-1" />
  <tcm:Item ID="tcm:0-142-1" />
  <tcm:Item ID="tcm:0-143-1" />
  <tcm:Item ID="tcm:0-144-1" />
</tcm:ListUsingItems>

Written by visvabalaji

April 11, 2011 at 11:38 pm

Blockers for deleting keywords in Tridion

leave a comment »

Some months back I had written a custom tool to import all keywords from XML to Tridion. I thought it was a one time activity, so I didn’t care much about the tool. Completed the tool in 3 days and imported all keywords. But last week there was a change in imported keywords. That was a not major change but we had a very very tough time just to implement the changes.

Problem:

Initially we were thinking of deleting all the keywords manually and run the tool again. But there comes a difficult part, we had around 5000 keywords and at least 30% of them have been used by other items. so we couldn’t just delete manually.

After few trail runs, identified that most of the keywords

  1. Classified by other building blocks
  2. Localized in any of child

I understand that its ridiculous to do it manually. But as part of testing itself, we have partially done it manually :(

As I was held up in various activities, I couldn’t develop the tool immediately but thinking of developing it for future purpose.

Solutions:

Soon after I realized that there is a need for a tool, I began thinking a way of writing it. I didn’t write up anything but two approaches came into my mind immediately.

  1. Simple and Easy
    1. Get category object and retrieve all keywords in xml
    2. Loop though each keyword
      1. unclassify if classified
      2. unlocalize if localized
    3. Find root keyword and delete it.
  2. Nice Programming
    1. Get root keyword and navigate through its child recursively
    2. If the child is
      1. classified then unclassify
      2. localized then unlocalize
    3. Once the focus is back to its parent object while looping, delete the child keyword. It is something like deleting the nodes from its bottom.

I like the 2nd approach but eager to finish up the tool quickly so I’m going to try the 1st one now. But 2nd is the nice approach to do, just becoz of its algorithmic approach :)

Will share the source code for both approaches soon.

Written by visvabalaji

April 6, 2011 at 10:01 pm

Posted in Tridion

Tagged with , ,

Finding IsPublished status in Tridion

with 3 comments

Sometimes back we faced a difficulty in identifying whether a component is published or not. I don’t think there is a direct method or property to check its published status. I was expecting something similar to IsPublished property in TOM.Net but I couldn’t find anything like that so one of my teammate found a workaround to check its status. We were sure that we could find this status somewhere in component object. Finally component object has its all details in its xmldata.

XML data has IsPublished node which tells you whether it is published or not. So we have written a below utility method to get you the component’s publish status.

The same can also be applied to other building blocks like page. All you need to do is, replace page instead of component object in below method.

private bool IsPublished(Component component)
{
string xmlData = component.GetXML(XMLReadFilter.XMLReadPublishInfo);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlData);
string isPublished = xmlDoc.SelectSingleNode("//*[local-name()= 'IsPublished']").InnerText;

return Convert.ToBoolean(isPublished);
}

Component.GetXML(XMLReadFilter.XMLReadPublishInfo) -> returns you the below xmldata. Then its all about getting the value of its IsPublished node. That’s it.


<tcm:Component ID="tcm:21-26941" IsEditable="false" xmlns:tcm="http://www.tridion.com/ContentManager/5.0" xmlns:xlink="http://www.w3.org/1999/xlink">
<tcm:Info>
<tcm:PublishInfo xmlns:tcm="http://www.tridion.com/ContentManager/5.0" xmlns:xlink="http://www.w3.org/1999/xlink">
<tcm:IsPublished>false</tcm:IsPublished>
</tcm:PublishInfo>
</tcm:Info>
</tcm:Component>

If you are a .Net developer, you would realize that Tridion 2009 is not so developer friendly. I’m hoping that all these will be resolved in Tridion 2011 version ;)

I would appreciate if anyone suggest better solution ;)

Written by visvabalaji

January 13, 2011 at 7:59 am

Posted in Tridion

Tagged with ,

RenderMe in Tridion

leave a comment »

As part of my templating requirements, I wanted to add few statements only while previewing the current version of the component. I was sure that this could be achievable in TOM. After few mins of searching found out that engine object provide RenderMode enum to differentiate between current and last checked-in version.

RenderMode enum  - Identifies the type of render mode of the current publishing action.

It provides three options

· PreviewDynamic – while previewing current version of an object

· PreviewStatic –while previewing last checked-in object

· Publish – while publishing any object

 


public class SampleTemplate : ITemplate
{

 public void Transform(Engine engine, Package package)
 {

// statements will run only during previewing current version
    if (engine.RenderMode == RenderMode.PreviewDynamic)
    {
      // implementation goes here
      …
    }
  }
}

Written by visvabalaji

November 23, 2010 at 7:59 am

Posted in Tridion

Tagged with , ,

Identifying component presentation objects

leave a comment »

As you aware, Component Presentation is not a static object in Tridion. Component and Component Template will be combined (during runtime) while rendering on a page. During customization, you must have a situation where you need to retrieve the component presentation objects.

If you have component and component template object, you can create component presentation object.

 

ComponentPresentation cp = new ComponentPresentation(component, componentTemplate);

If you want to retrieve only its key, there is an alternate way, by adding TCMURI of component and template.


public string GetComponentPresentationKey(TcmUri componentUri, TcmUri templateUri)
{
	return componentUri.ToString() + "_" + templateUri.ToString();
}

Calling method like…


string key = GetComponentPresentationKey(component.Id, componentTemplate.Id);

 

Written by visvabalaji

November 13, 2010 at 7:48 am

Posted in Tridion

Tagged with ,

Get Components from specific folder

leave a comment »

In my earlier Tridion code sample, we have seen about retrieving components in XML format. In some cases, we may need to have component objects instead of XML string.

Below code snippet will help you to retrieve component objects from a specific folder.

private void GetComponents(string publicationURI, string folderURI)
{
    //initialize TDSE object    TDSE tObj = new TDSE();
    //get folder object from where the components have to be retrieved
    Folder folder = (Folder)tObj.GetObject(folderURI, EnumOpenMode.OpenModeEdit, publicationURI, XMLReadFilter.XMLReadAll);
    //get all components from folder
    TDSItems components = folder.GetItems(ItemType.ItemTypeComponent);
    //navigate through all components
     foreach(Component comp in components)
    {
        //do something here.
        Console.WriteLine(comp.Title);
    }
}

Written by visvabalaji

July 17, 2010 at 7:40 am

Posted in Tridion

Tagged with , ,

Retrieving all Components (that are based on a certain Schema) in XML

leave a comment »

I had this code snippet in scripting but presenting it here in C# format. When I was working on a custom migration tool, this was very handy.

The below code snippet will be useful if you want to retrieve specific type of an objects from TOM layer.

To test it clearly, let’s try a sample piece of code.

TDSE is the basic object should be initialized first by which you can access any objects from Tridion.

TDSE tdse = new TDSE();
ListRowFilter rowFilter = tdse.CreateListRowFilter();
string result = tdse.GetListItems(ListColumnFilter.XMLListID, rowFilter);

GetListItems() returns TcmUri of all objects in XML format. So the output variable result has the following xml data.

<?xml version="1.0" ?>
<tcm:ListItems xmlns:tcm="http://www.tridion.com/ContentManager/5.0" ID="tcm:89-3-2" Managed="10682">
<tcm:Item ID="tcm:89-145-16" />
<tcm:Item ID="tcm:89-146-16" />
<tcm:Item ID="tcm:89-147-16" />
<tcm:Item ID="tcm:89-148-16" />
<tcm:Item ID="tcm:89-8756-8" />
<tcm:Item ID="tcm:89-6545" />
<tcm:Item ID="tcm:89-34" />
<tcm:Item ID="tcm:89-2332" />
</tcm:ListItems>

Now I’m adding few conditions to retrieve only component objects.

rowFilter = tdse.CreateListRowFilter();
rowFilter.SetCondition("ItemType", ItemType.ItemTypeComponent);
rowFilter.SetCondition("LockUser", tdse.User);
result = tdse.GetListItems(ListColumnFilter.XMLListID, rowFilter);

Finally we will get the following output in XML format.

<?xml version="1.0" ?>
<tcm:ListItems xmlns:tcm="http://www.tridion.com/ContentManager/5.0" ID="tcm:89-3-2" Managed="10682">
<tcm:Item ID="tcm:89-145-16" />
<tcm:Item ID="tcm:89-146-16" />
<tcm:Item ID="tcm:89-147-16" />
<tcm:Item ID="tcm:89-148-16" />
</tcm:ListItems>

Now you can add one more condition to retrieve the components based on specific schema.

rowFilter.SetCondition("BasedOnSchema", schemaUri);

Written by visvabalaji

July 13, 2010 at 7:06 am

Posted in Tridion

Tagged with , ,

Follow

Get every new post delivered to your Inbox.