String Writer

my tech stuffs…

Posts Tagged ‘C#

Publication URL TBB

leave a comment »

In most cases, you will not be publishing the binary files (images/css/js) in the same location of where you publish pages. As a good practice, these external files will be stored in separate folders. In such cases, you will need to  specify path (relative) in the page DWT template.

Generally external css files will be declared like

<link href="/styles/main.css" rel="stylesheet" type="text/css"/>

This would work only if you have styles folder immediately after server path.

http://<server>/styles/main.css

In Tridion, default publication URL is “/” but if you have specified any path in the Publication,  you will need to append that path also. It should be accessed like.

http://<server>/<pub url>/styles/main.css

The below C# TBB will get the publication URL of the current publication.


//gets the current session object
 Session session = engine.GetSession();
 //gets page object from the package
 Item item = package.GetByType(ContentType.Page);
 //gets TcmUri object of Page
 TcmUri tcmObj = new TcmUri(item.GetValue("ID"));
 //generate tcm string format with publication id
 string publicationId = string.Format("tcm:0-{0}-1", tcmObj.PublicationId);
 //gets publication object from the current session
 Publication publication = new Publication(new TcmUri(publicationId), session);
 //writes the publication URL into the package
 package.PushItem("PublicationUrl", package.CreateStringItem(ContentType.Text, publication.PublicationUrl));

As you aware, variable “PublicationUrl” will be included in the package so this variable is accessible when you include this TBB on your page template.
Now you need to call this variable on the page DWT like below,

<link href="@@PublicationUrl@@/styles/main.css" rel="stylesheet" type="text/css"/>

Written by visvabalaji

November 22, 2011 at 3:49 pm

Posted in C#, SourceCode, Tridion

Tagged with , ,

Get object types in C#/Tridion 2009

leave a comment »

In C#, the general usage to identify the object type is using “is” keyword. This is okay to some extent but you have to write “switch” of “if”, for multiple types. If there are more object types, code will be lengthy. Recently I came across a easy way of checking the object’s type. This is helpful when the object is of COM type.

Just add the below reference in your project and call its TypeName method.

image


using Microsoft.VisualBasic;
.
.
.
.
.
public string GetTypeOf(object obj)
{
   return Microsoft.VisualBasic.Information.TypeName(obj);
}

Output:

image

This was very handy for me in my migration project where I needed to write all the object details in log file.

Written by visvabalaji

August 11, 2011 at 11:10 pm

Iterate all Inner Exceptions in C#

with 2 comments

In simple applications or pages, handling an exception will not be a difficult task. But when your application is using any web services or threading, most of the time, actual exception details will be stored in InnerException of the main Exception object. In such cases, we need to iterate all the inner exceptions to get the actual error message.

Here is a code snippet to iterate all inner exceptions from the root Exception object.


private List<string> GetAllErrMessages(Exception ex)
{
   List<string> messages = new List<string>();
   //assign the current exception as first object and then loop through its
   //inner exceptions till they are null
   for(Exception eCurrent = ex.Exception; eCurrent != null; eCurrent = eCurrent.InnerException)
   {
      messages.Add(eCurrent.Message);
   }
}

My team said they never saw any code like handling Exception objects using for loop. I don’t know but I learnt this from my lead when I was working on Adrenalin HRMS product. Just sharing the code for your review ;)

Written by visvabalaji

July 26, 2011 at 4:43 pm

Posted in C#

Tagged with , ,

Trigger mail from Tridion Workflow

with 2 comments

One of the most-wanted requirement from all customers is that notifying users through email. When they have some level of approvers to approve the content before they get published, the current user or next level approvers can be notified via mail.

I’ve earlier done this same implementation in ASP.Net & MOSS but Tridion is bit different. Sending mail is no different than traditional C# way. C# is to write mail related code and VB Script is to invoke it. Thats it…

Tridion allows this implementation in 3 simple steps. This simple example is just to understand its process… other business logics can be included later according to our requirements.

Implementation

1) Create custom dll using C#

2) Create workflows using Visio

3) Define workflow actions using VB Script

1) Create Custom dll using C#

Assuming that you now have fair idea about developing custom assemblies in Tridion. I’m starting directly with a code snippet.

Let’s create a custom class to define a C# method which has mailing piece of code.


namespace WorkFlow
{

  [System.Runtime.InteropServices.ComVisible(true)]
  [System.Runtime.InteropServices.ProgId("WCMReq.WorkflowSamples")]
  public class MailUtility
  {

     public bool NotifyUser(object workItem)
     {

        try
        {
           If(workItem is Component)
           {
               Component comp = (Component)workItem;
               String message = “Current approval level of “ + comp.Title + “ is “ + comp.ApprovalStatus.Position;
               return SendMail(message);
           }
           return false;
        }
        catch (Exception ex)
        {
           WriteLog(ex.Message);
        }
     }

     private bool SendMail(string message)
     {
       …
     }
  }
}

2) Create workflows using Visio

Assuming that you are already defined workflows using Visio Tridion extention.

3) Define workflow actions using VB Script

Open any workflow automatic action and include the following VB Script code.


Dim workobj
Set workobj= CreateObject("WCMReq.WorkflowSamples")
workobj.NotifyUser(CurrentWorkItem.GetItem())
Set workobj= Nothing

Written by visvabalaji

March 9, 2011 at 7:30 am

Posted in Tridion

Tagged with , , , ,

Source Code for COM Deployer

with one comment

In my earlier post, I had published COM Deployer which is just to copy the dlls into Tridion System. Here I’m briefing the source code of the tool …

Namespaces:

using System;
using System.Configuration;
using System.IO;
using COMAdmin;

All these are common .Net namespaces excep COMAdmin. For this , you need to include Interop.COMAdmin.dll reference. This is common dll which is provided by Microsoft to access COM services on Windows OS. More details on this can be found in MSDN.

But if you don’t have time to go through all these stuffs, you can simply copy the file from the following directory on any Windows OS J

%windir%/System32/com/comadmin.dll

Configuration Entries:

I’ve stored these entries app.config file. Hope I don’t need to explain more about these configuration entries.


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <!--COM Service Name ex) "Tridion Content Manager" -->
    <add key="application" value="Tridion Content Manager"/>
    <!--Server name or IP where COM services are installed -->
    <add key="server" value="192.168.0.2"/>
    <!--Source file location (local/remote shared path), including file name & use ; to include multiple files-->
    <add key="sourceFiles" value="D:\Training\SDL Tridion\wkflw.dll;D:\Training\SDL Tridion\evntSystem.dll "/>
    <!--Target file location (local), without file name-->
    <add key="targetLocation" value="C:\Program Files\Tridion\bin"/>
  </appSettings>
</configuration>

Source Code:

//read config entries to local variables
string server = ConfigurationManager.AppSettings["server"];
string application = ConfigurationManager.AppSettings["application"];
string sourceFiles = ConfigurationManager.AppSettings["sourceFiles"];
string targetLocation = ConfigurationManager.AppSettings["targetLocation"];

COMAdminCatalog is the root object to access the sevice. Please note that the user must be having admin access to run this tool otherwise you will get Access Denied error.

COMAdminCatalog catalog = new COMAdminCatalogClass();
//connecting to server to access COM services
catalog.Connect(server);
//stopping service
catalog.ShutdownApplication(application);

COMAdminCatalog is an interface so we need to instantiate with COMAdminCatalogClass object.

//copy file to target location
File.Copy(file, targetLocation + "\\" + file.GetFileName(), true);

Just copying the source files to target location.

//restarting service
catalog.StartApplication(application);

After copied all the files, the services will be restarted by calling StartApplication method.

Sub methods:

Other private methods used in this utility…


/// <summary>
/// Method to write the output to the user
/// </summary>
/// <param name="message"></param>
private static void WriteMessage(string message)
{
    Console.WriteLine(Environment.NewLine + message);
}

public static class FileExtentionClass
{
  /// <summary>
  /// This extention method will be accessible from any string object.
  /// Intention is to retrieve the file name alone from the file path
  /// </summary>
  /// <param name="filePath">file path of string type</param>
  /// <returns>file name of string type</returns>
  public static string GetFileName(this string filePath)
  {
      return filePath.Substring(filePath.LastIndexOf("\\") + 1);
  }
}

Written by visvabalaji

February 3, 2011 at 6:36 am

Posted in Tridion

Tagged with , , ,

Get CData from XML using C#

with 2 comments

In my recent work, I wrote a custom tool in C# to import xml data into Tridion. With some trails, I found out that error handling in Tridion Object Model (TOM) is bit different than modern APIs. If there is an API, I would always expect to have custom error objects to handle the exceptions in a better and easy way.
But Tridion doesn’t provide any custom error objects in its 2009 version. Since all are COM based components, we shouldn’t expect the benefits of .Net L. Tridion 2011 is completely based on .Net so I believe we will not have any such problems in its future versions.
Generally TOM throws exceptions as xml string. In my case, below is the message.
<?xml version="1.0" ?>
<tcm:Error xmlns:tcm="http://www.tridion.com/ContentManager/5.0" ErrorCode="80040327" Category="19" Source="Kernel" Severity="2">
<tcm:Line ErrorCode="80040327" Cause="false" MessageID="16137">
<![CDATA[
Unable to save Keyword (tcm:0-0-0).
]]>
<tcm:Token>tcm:0-0-0</tcm:Token>
</tcm:Line>
<tcm:Line ErrorCode="80040327" Cause="true" MessageID="15176">
<![CDATA[
XML validation error. Reason: enumeration constraint failed. The element: '{uuid:FAC1CA6A-C5BC-4CEA-AA98-D11E5EE4CD3C}State'  has an invalid value according to its data type.";  Source:        <State>TAMILNADU</State>;
]]>
<tcm:Token>enumeration constraint failed." The element: '{uuid:FAC1CA6A-C5BC-4CEA-AA98-D11E5EE4CD3C}State' has an invalid value according to its data type. "</tcm:Token>
<tcm:Token><State>TAMILNADU</State></tcm:Token>
</tcm:Line>
<tcm:Details>
<tcm:CallStack>
<tcm:Location>XMLValidation.ValidateXML</tcm:Location>
<tcm:Location>XMLValidation.ValidateXML</tcm:Location>
<tcm:Location>XMLValidation.ValidateCustom</tcm:Location>
<tcm:Location>KeywordBL.Create</tcm:Location>
<tcm:Location>XMLState.Save</tcm:Location>
<tcm:Location>Keyword.Save</tcm:Location>
</tcm:CallStack>
</tcm:Details>
</tcm:Error>

Well as you can see this message, I can’t display the entire message to user so I wanted to display only error code and CData which will help them to trace the exception.
Firstly, I tried to come up with a generic method to extract all CData from XML. XLINQ is my obvious choice for these sorts of generic methods.  Finally below is the method which will give you all CData in a List<string> object.
public List<string> GetCDataFromXML(string xmlFilePath)
{
//initialize output object
List<string> result = new List<string>();
//initialize XElement object with xml file path
XElement nodes = XElement.Load(xmlFilePath);
// In case of xml string instead of file path
//XElement nodes = XElement.Parse(xmlFilePath);
//navigates only CData elements
foreach (var cData in nodes.DescendantNodes().OfType<XCData>())
{
result.Add(cData.Value);
}
//returns collection of CData<string>
return result;
}

When I tried this code on another machine, unfortunately the machine had only .Net 2.0. So this time, I had to change code for .Net that is without XLINQ.
public List<string> GetCDataFromXML(string xmlFilePath)
{
//initialize output object
List<string> result = new List<string>();
//initialize xml reader object with xml file path
XmlReader reader = new XmlTextReader(xmlFilePath);
// In case of xml string instead of file path
// XmlReader reader = XmlReader.Create(new StringReader(xmlFilePath));
reader.MoveToContent();

//navigates only CData elements
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.CDATA:
result.Add(reader.Value);
break;
}
}

//returns collection of CData<string>
return result;
}

Both returns the same output as below,
Now I’m able to get all CData in an XML so let’s make more generic so that I can use it wherever I’m handling exceptions. Here’s an implementation of simplified custom exception class using above logic.
public class TCMError
{
List<string> _messages = null;
public string Message
{
get
{
//this is as per my requirement
return this._messages[this._messages.Count - 1];
}
}

public TCMError(string exception)
{
this._messages = new List<string>();
XmlReader reader = new XmlTextReader(exception);
reader.MoveToContent();

while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.CDATA:
this._messages.Add(reader.Value);
break;
}
}
}
}

Here’s the method calling…
TCMError tcmError = new TCMError(exception);
Console.WriteLine("Message : {0}", tcmError.Message);

Later I’ve modified my custom class according to my requirements but this is only to understand the pattern.

Written by visvabalaji

December 28, 2010 at 4:11 am

Posted in C#, Tridion

Tagged with ,

Getting keywords from category in Tridion

leave a comment »

As part of my requirements, I needed to navigate all the keywords from all Tridion categories. So I have written a utility class which will loop through all the keywords in a publication. Here I have given a part of it which will explain about keyword retrieval part.

In Tridion, a category will contain all the keywords so in order to retrieve all the keywords, we need to have category object first. Category object does provide a method, GetKeywordByTitle, to retrieve a keyword by passing its title as parameter. But the problem is when you pass a wrong name to this method, this will throw an exception instead of returning null.

If you are familiar with .Net/JAVA programming, while you are retrieving an item from a collection, you will always expect null to be returned if an item does not exists. Hope this will be solved in Tridion 2011 J

So I’ve added a utility method which would return null even if the keyword doesn’t exists in the category.

 

 

private Keyword GetKeywordByTitle(Category catObj , string keyword)
{
try
{
return catObj.GetKeywordByTitle(keyword);
}
catch
{}
return null;
}


The above method can be called when an keyword object is going to retrieved as shown below.

 

private void TestMethod()
{
TDSE tObj = new TDSE();
Category catObj = (Category)tObj.GetObject("tcm:50-51-512", EnumOpenMode.OpenModeEdit, PublicationTCMURI, XMLReadFilter.XMLReadAll);
Keyword kObj = this.GetKeywordByTitle(catObj, “Chennai”);
if (kObj == null)
   Console.WriteLine(“{0} does not exists!”, keyword);
else
   Console.WriteLine(“{0} exists!”, keyword);
}

 

This is not a solution but just a tip for Tridion beginners J

 

Written by visvabalaji

June 2, 2010 at 2:11 am

Posted in Tridion

Tagged with , ,

Create/Import child keywords in Tridion

with 2 comments

 

In my earlier post, we have seen about adding keywords. After that I was trying to add child keywords in the same code sample. But things didn’t work out when it comes to add parent details. Because you can’t assign multiple parents to a keyword if you use Tridion GUI  but it is possible if you use Tridion Object Model. Finally after some trial and errors, I got the working piece of code.

If you are trying to import large data into Tridion, you can modify this code using recursion. I assume that you are familiar with .Net programming so I don’t want to confuse this code sample by adding recursion. This simple code is about adding child keyword to a parent.

When you are adding the child keywords, you need to make four changes in my previous code.

1) Instantiate Category object to fetch parent key object

2) Get Parent keyword object

3) IsRoot – should be set to false as this is not a root keyword

4) Should include <tcm:ParentKeyword> within <tcm:ParentKeywords> as shown below

 


//initializing tridion base object

TDSE TObj = new TDSE();

//creating category object

Category catObj = (Category)TObj.GetObject(CategoryTCMURI, EnumOpenMode.OpenModeEdit, PublicationTCMURI, XMLReadFilter.XMLReadAll);

Keyword parentKey = catObj.GetKeywordByTitle("India");

//getting new Tridion keyword object

//CategoryTCMURI - TCM uri of category object

//PublicationTCMURI - TCM uri of publication

Keyword KObj = (Keyword)TObj.GetNewObject(ItemType.ItemTypeKeyword, CategoryTCMURI, PublicationTCMURI);

string title = "Chennai";

string description = "Chennai is the 4th largest metro";

string key = "Chennai";

bool isAbstract = false;

bool isRoot = false;

StringBuilder keyXML = new StringBuilder();

keyXML.Append("<tcm:Keyword xmlns:tcm='http://www.tridion.com/ContentManager/5.0'>");

keyXML.Append("<tcm:Data xmlns:tcm='http://www.tridion.com/ContentManager/5.0'>");

keyXML.AppendFormat("<tcm:Title>{0}</tcm:Title>", title);

keyXML.AppendFormat("<tcm:Description>{0}</tcm:Description>", description);

keyXML.AppendFormat("<tcm:Key>{0}</tcm:Key>", key);

keyXML.AppendFormat("<tcm:IsAbstract>{0}</tcm:IsAbstract>", isAbstract.ToString().ToLower());

keyXML.AppendFormat("<tcm:IsRoot>{0}</tcm:IsRoot>", isRoot.ToString().ToLower());

keyXML.Append("<tcm:ParentKeywords xmlns:tcm='http://www.tridion.com/ContentManager/5.0' xmlns:xlink='http://www.w3.org/1999/xlink' >");

if (parentKey != null)

{

string info = parentKey.Info.Path;

info = info.Replace(" ", "%20");

info = info.Replace("\\", @"/");

keyXML.Append("<tcm:ParentKeyword xlink:type='simple' xlink:title='" + parentKey.Title + "' xlink:href='/webdav" + parentKey.Info.Path + "/" + parentKey.Title + ".tkw' /> ");

}

keyXML.Append("</tcm:ParentKeywords>");

keyXML.Append("<tcm:RelatedKeywords xmlns:tcm='http://www.tridion.com/ContentManager/5.0' xmlns:xlink='http://www.w3.org/1999/xlink' />");

keyXML.Append("<tcm:MetadataSchema xmlns:xlink='http://www.w3.org/1999/xlink' xlink:type='simple' xlink:title='' xlink:href='tcm:0-0-0' />");

keyXML.Append("<tcm:Metadata />");

keyXML.Append("</tcm:Data>");

keyXML.Append("</tcm:Keyword>");

KObj.UpdateXML(keyXML.ToString());

KObj.Save(true);

Here’s the output :)

 

image

Written by visvabalaji

April 7, 2010 at 7:25 pm

Posted in Tridion

Tagged with , ,

Create/Import keywords in Tridion

leave a comment »

Last week I had a chance to work on a custom tool to create Tridion keywords. I started doing some POCs using Tridion business connecter but it failed so I tried again with Object Model (TOM). I was sure that this could be done through object model but there were no much details available in Tridion materials. We had tried various options but finally support team have helped us to find a quick way to add keywords.

Solution:

Keywords can be created/edited by updating its intermediate xml. But I don’t have a complete knowledge on this intermediate xml format as Tridion is using this for internal processing. But it worked for creating keywords.

Here’s a sample C# code to create a keyword in Tridion…


 

//initializing tridion base object

TDSE TObj = new TDSE();

//getting new Tridion keyword object

//CategoryTCMURI - TCM uri of category object

//PublicationTCMURI - TCM uri of publication

Keyword KObj = (Keyword)TObj.GetNewObject(ItemType.ItemTypeKeyword, CategoryTCMURI, PublicationTCMURI);

string title = "Books";

string description = "Tamil Books";

string key = "Books";

bool isAbstract = false;

bool isRoot = true;

StringBuilder keyXML = new StringBuilder();

keyXML.Append("<tcm:Keyword xmlns:tcm='http://www.tridion.com/ContentManager/5.0'>");

keyXML.Append("<tcm:Data xmlns:tcm='http://www.tridion.com/ContentManager/5.0'>");

keyXML.AppendFormat("<tcm:Title>{0}</tcm:Title>", title);

keyXML.AppendFormat("<tcm:Description>{0}</tcm:Description>", description);

keyXML.AppendFormat("<tcm:Key>{0}</tcm:Key>", key);

keyXML.AppendFormat("<tcm:IsAbstract>{0}</tcm:IsAbstract>", isAbstract.ToString().ToLower());

keyXML.AppendFormat("<tcm:IsRoot>{0}</tcm:IsRoot>", isRoot.ToString().ToLower());

keyXML.Append("<tcm:ParentKeywords xmlns:tcm='http://www.tridion.com/ContentManager/5.0' xmlns:xlink='http://www.w3.org/1999/xlink' />");

keyXML.Append("<tcm:RelatedKeywords xmlns:tcm='http://www.tridion.com/ContentManager/5.0' xmlns:xlink='http://www.w3.org/1999/xlink' />");

keyXML.Append("<tcm:MetadataSchema xmlns:xlink='http://www.w3.org/1999/xlink' xlink:type='simple' xlink:title='' xlink:href='tcm:0-0-0' />");

keyXML.Append("<tcm:Metadata />");

keyXML.Append("</tcm:Data>");

keyXML.Append("</tcm:Keyword>");

KObj.UpdateXML(keyXML.ToString());

KObj.Save(true);

 

Note:

Lowercase values should be assigned isRoot & isAbstract.

 


Written by visvabalaji

April 2, 2010 at 7:11 pm

Posted in Tridion

Tagged with , ,

Get SID from LDAP Account Name

leave a comment »

During my migration we came across a situation where we need SID but all we had only LDAP user name. So after few trail and error googling, below code snippet helped us to get SID from LDAP user name.

/// <summary>
/// Gets the name of the SID by account.
/// <summary>
/// <param name="accountName"> Name of the account.</param>
/// <returns>SID</returns>
public string GetSIDByAccountName(string accountName)
{
   //name should be in the format of domain\username
   WindowsIdentity userIdentity = new WindowsIdentity(accountName);
   if (userIdentity == null)
      throw new Exception(string.Format("{0} : User does not exists", accountName));

   return userIdentity.User.AccountDomainSid.ToString();
}

Output : S-1-5-21-789336058-507921405-854245398-9938

Written by visvabalaji

August 13, 2009 at 7:02 am

Posted in LDAP

Tagged with , ,

Follow

Get every new post delivered to your Inbox.