Archive for the ‘Tridion’ Category
Get TcmId from web dav url
Many of tridion developer would have tried to get tcm id from web dav. This might be one of the common scenarios where we are using web dav when we are rendering embedded components (in another component template).
I think this is simple scenario so I’m just sharing C# code snippet for this. I’m posting this snippet becoz somebody was googling this yesterday
))
IdentifiableObject identifiableObject = engine.GetObject("<<Web dav url>>");
package.PushItem("TCMID", package.CreateStringItem(ContentType.Text, identifiableObject.Id));
Dreaweaver templating in Tridion
Unless we are familiar with XSLT, I believe, most of us would prefer developing Dreamweaver TBB (DWT) for CT/PT layout!
So just thought of sharing basic Dreamweaver syntax which are commonly used anyway. There are few Dreamweaver extensions are available, developed by Tridion Community, to simplify and enhance DWT templating. But I didn’t get a chance to work on these extensions. So below code syntax would work without any DWT extensions.
I’ve created few simple building blocks for this DWT example.
Schema & Component
Below is the sample content created based on a simple schema. So assume that component will have this below content.
<Blog xmlns="uuid:18db19ea-b584-4f97-9ac6-8a6e269c9f60"> <Author> <Name>Thotta</Name> <TwitterHandle>thotta</TwitterHandle> <Genere>Tamil</Genere> </Author> <Title>TwiTamils</Title> <Body>This post is to discuss about active twitters.</Body> <Tweeters> <Name>Sathish</Name> <TwitterHandle>sathm</TwitterHandle> <Genere>Tamil</Genere> </Tweeters> <Tweeters> <Name>Nuno</Name> <TwitterHandle>nunolinhares</TwitterHandle> <Genere>Tridion</Genere> </Tweeters> <Tweeters> <Name>Sowbarnika</Name> <TwitterHandle>sowmi_</TwitterHandle> <Genere>Tamil</Genere> </Tweeters> <Tweeters> <Name>Srinivasan</Name> <TwitterHandle>DKCBE</TwitterHandle> <Genere>Tamil</Genere> </Tweeters> <Tweeters> <Name>Alvin Reyes</Name> <TwitterHandle>Nivlong</TwitterHandle> <Genere>Tridion</Genere> </Tweeters> </Blog>
And the component layout is defined in DWT as follows
<div>
<h1>@@Component.Fields.Title@@</h1>
by <a href="twitter.com/@@Component.Fields.Author.TwitterHandle@@">@@Component.Fields.Author.Name@@</a>
@@Component.Fields.Body@@
<hr />
<table border="1">
<tr>
<td><strong><span style="text-decoration: underline;">Tweeters</span></strong></td>
<td><strong><span style="text-decoration: underline;">Tamil Tweeters</span></strong></td>
</tr>
<tr>
<td>
<ul>
<li><strong><a href="twitter.com/@@TwitterHandle@@">@@Name@@</a></strong></li>
</ul>
<ul>
<li><em><a href="twitter.com/@@TwitterHandle@@">@@Name@@</a></em></li>
</ul>
</td>
<td>
<ul>
<li><a href="twitter.com/@@TwitterHandle@@">@@Name@@</a></li>
</ul>
</td>
</tr>
</table>
</div>
Using template builder, we would need to execute this component template against the above component and that would render the output as,
TwiTamils
by
Thotta
This post is to discuss about active twitters.
| Tweeters | Tamil Tweeters |
Accessing a field value
There are many ways to retrieve the field of a component : @@Component.Fields.<field name>@@ or @@<field name>@@ or ${field name}.
Calling simply the field name, like @@Title@@, would work but we should be clear that the same field name was not used on CT/PT level. Otherwise it would return title of the component template since Title is the default attribute for Component Template also.
It’s always better to use full path like @@Component.Fields.Title@@.
Accessing sub field’s value
If embedded schema’s field needs to be accessed, we can extend the call by adding the sub field name.
As you can see in the above component xml source of a component, Name is the sub field of Author. So it can be accessed like
@@Component.Fields.Author.Name@@
Accessing a value from Collection
If there are any collection of fields, that can be looped using TemplateBeginRepeat. In the above content, Tweeters is the collection , the sub fields can be accessed inside the loop.
<!-- TemplateBeginRepeat name="Component.Fields.Tweeters" --> Name: @@Name@@ Handle : @@TwitterHandle@@ <!-- TemplateEndRepeat -->
Please note that the sub fields cannot be called like Component.Fields.Tweeters.Name as it cannot navigate right through the Component.
I wish to have something like Component.Fields.Tweeters[0].Name … Component.Fields.Tweeters[3].Name… but unfortunately, it won’t work
Getting loop index
When we are iterating a collection, obviously we would require to have an index of the loop. Unlike other languages, we can’t declare any variables here but Dreamweaver provides a default variable (TemplateRepeatIndex) which holds the index value of the loop.
@@ TemplateRepeatIndex@@ -> this will print the index value on the page.
Conditional loop
Dreamweaver also provides conditional loop which is similar to If conditions in other languages.
<!-- TemplateBeginIf cond="conditions" --> …. <!-- TemplateEndIf -->
For example,
If I want to render a footer component on bottom of the page, we can try something like
<!-- TemplateBeginRepeat name="Components" -->
<!-- TemplateBeginIf cond="ComponentTemplate.Metadata.Position = 'Footer'" -->
@@RenderComponentPresentation(Component.ID, ComponentTemplate.ID)@@
<!-- TemplateEndIf -->
<!-- TemplateEndRepeat -->
Hope this helps for beginners!
Publication URL TBB
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"/>
Get object types in C#/Tridion 2009
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.
using Microsoft.VisualBasic;
.
.
.
.
.
public string GetTypeOf(object obj)
{
return Microsoft.VisualBasic.Information.TypeName(obj);
}
Output:
This was very handy for me in my migration project where I needed to write all the object details in log file.
Tridion .Net Template Building Blocks (package) – 2
As part of previous post, this will just explain about package object in .Net Templating.
Just to understand what is package in the context of Tridion…During publishing Content Manager provides the publishable contents to Content Distributor. That is the publisher packages all the contents, metadata and instructions for publishing and deploying, in the form of XML. The transport service will send this package to a receiver in the Content Deployer and the Content Deployer will process the content according to the instruction given in the package. (Can refer the process here)
This package can be accessed through .Net templating(TBB) using C# fragments or .Net assemblies. Actually TBB will be executed in the context of given package. In order to customize the contents/instructions, package (typeof Package) object must be edited during templating process. TBB retrieves the relevant items from the package and store the modified contents back into the package object.
Both .Net templating approaches transforms the package object.
Using C# fragments
C# fragments will be a part of Transform(…) in a predefined class so the package variable is accessible by default.
Using .Net Assemblies
A class must inherit ITemplate interface in order to make it as TBB, which will have Transform(…) as part of interface implementation.
public class SampleTemplate : ITemplate
{
public void Transform(Engine engine, Package package)
{
}
}
Execute using Template Builder
TBB can be executed using Template Builder, which is an add-on tool in Tridion. After including TBB for compilation, Default Finish Actions will be included as last TBB. During this process, the package contains only the contents before processing Default Finish Actions; and after Default Finish Actions has been executed, all the publishable contents will be stored in the Output of package object.
package object:
This is a stack type of object and gives access to the contents of the package being processed by the current template (Component or Page). Any ContentType items can be pushed in or popped out from the package.
Package class is defined in Tridion.ContentManager.Templating namespace.
When the package is being processed against Component Template, the package will contain Component Item, through which the Component details can be accessed.
Example,
package.PushItem("Title", package.CreateStringItem(ContentType.Text, package.EvaluateExpression("Component.Title")));
In case of Page Template, the package will contain Page Item.
Example,
package.PushItem("Title", package.CreateStringItem(ContentType.Text, package.EvaluateExpression("Page.Title")));
Finally the Output Item of Component or Page Template must contain the contents to publish.
Package variables
To access a value in the package, the following string format should be used.
[Item name].([Item name]).[value selector]
Example,
To access Component title => Component.Title
To access CreatedBy from metadata => Component.MetaData.CreatedBy
Here’s some predefined/reserved item names in Templating which are constants of Package class.
|
Names |
Details |
Example |
| Component | Contains the component currently being processed | Component.Title |
| Components | Contains the list of components associated with the page currently being processed | |
| Page | Contains the page currently being processed | Page.Title |
| ComponentTemplate | Contains the current component template being processed against the Component | |
| Field | Contains the Field of the Component that is being processed | Component.MetaData.CreatedBy |
| Output | Contains the content to publish, in XML format | |
| TemplateRepeatIndex | Used to looping through the collections in a Dreamweaver template | <!– TemplateBeginRepeat name=”Fields.locations” –>${RenderComponentField(“Fields.locations”, TemplateRepeatIndex)}<!– TemplateEndRepeat –> |
Some frequently used methods in package:
PushItem(…) – Items will be added in the package using this method, LIFO (Last In, First Out) approach. Each item should be defined with a name in order to insert it in the package but multiple items can have same name.
package.PushItem("PageTitle", package.CreateStringItem(ContentType.Text, package.EvaluateExpression("Page.Title")));
GetByType(…) – Retrieves an item based the specific content type
Item item = package.GetByType(ContentType.Component);
GetAllByType(…) – Retrieves all items based on the specific content type
Items items = package.GetAllByType(ContentType.Component);
GetByName(…) – Retrieves an item by its name
package.GetByName(Package.OutputName);
GetValue(…) – Retrieves the value of an item
Component component = engine.GetObject(package.GetValue("Component.ID")) as Component;
Remove(…) – Removes an item from the package
Item componentsItem = package.GetByName(Package.ComponentsName); package.Remove(componentsItem); //it will remove only the first item if many items used same name
CreateStringItem(…) – creates string type of an item
package.PushItem("PageTitle", package.CreateStringItem(ContentType.Text, “Sample Page")));
CreateHtmlItem(…) – creates html type of an item
String marketUrl = package.EvaluateExpression("Components.Fields.MarketUrl");
package.PushItem("MarketLink", package.CreateHtmlItem(marketUrl));
EvaluateExpression(…) – evaluates an expression (predefined variables or user defined)
package.PushItem(package.CreateStringItem(ContentType.Text, package.EvaluateExpression("3>=3"))); //user defined
package.PushItem("PageTitle", package.CreateStringItem(ContentType.Text, package.EvaluateExpression("Page.Title"))); //predefined variables
Tridion .Net Template Building Blocks – 1
During my internal training to freshers, most of the folks raised questions about template building blocks. I’m glad that they understood Tridion basics quickly and done few hands-ons successfully. But I had to organize another quick discussion to clarify their doubts on TBBs and I’m just documenting our discussions in this blog.
We are yet to get Tridion 2011 access so these are all based on Tridion 2009
Generally SDL Tridion templating helps to transform the content from Content Manager and publish them to target location (or presentation server).
Few points about Template Building Blocks (TBB)
- It transforms the content from Content Manager into publishable content to target location (or presentation server), in other words, it just transforms the content from XML to (X)HTML.
- Template building blocks are inserted in a component template (or) page template in an order.
- Collections of building blocks can be grouped together to form a Compound Template using the Template Builder.
- Each TBB can be used in multiple Compound Templates.
Tridion supports following .Net template building blocks,
- C# fragments
- .Net assembly
Here are few details about these types,
C# fragments
We can simply write C# code fragments in the Content Manager Explorer interface (in the Source tab of TBB). These code snippets will be a part of ITemplate.Transform(…) in a predefined class. Hence package, engine and log objects are accessible without any additional reference and all the items in the Content Manager can be accessible through C# code.
Usual practice is to use when you are writing small junk of codes on a package.
Limitations:
- Cannot create classes as these codes are already part of another class.
- Cannot debug the code as we are writing in Content Manager Explorer interface.
.NET assembly
This is the typical way of creating C# class library projects which will return dll file. Here a class should implement ITemplate interface to create a TBB. If there are more classes that implements ITemplate, then TBB will be created for each classes.
using Tridion.ContentManager.ContentManagement;
using Tridion.ContentManager.ContentManagement.Fields;
using Tridion.ContentManager.Publishing;
using Tridion.ContentManager.Templating;
using Tridion.ContentManager.Templating.Assembly;
namespace TridionSamples
{
public class SampleTemplate : ITemplate
{
/// <summary>
/// Transform as defined by ITemplate.
/// </summary>
/// <param name="engine">Templating engine</param>
/// <param name="package">Package to process</param>
public void Transform(Engine engine, Package package)
{
}
}
}
If we are making further changes in the .Net assembly, the latest dll can be uploaded to Tridion Content Manager using TCMUploadAssembly.exe command-line utility. Hope this can be posted later.
In both cases, the following objects are important to write TBBs
- package – contains the content to be published.
- engine – gives you the access to Content Manager in the current context.
- log – writes log messages through template logger.
I believe these 3 objects should be detailed, to understand better.
SDL Tridion Classroom sessions
Before I started working on Tridion projects, almost nobody in my DC was aware of Tridion CMS. When we got a project from one of our client, we didn’t want to miss that opportunity. So we had to arrange a professional training from SDL Tridion. Those two weeks sessions was good enough to get started on our project.
Now I’m on the verge of signing another Tridion project and also we are expecting few more opportunities. We realized that its good time to train others and, of course its always good to have additional hands. So we randomly selected 10 peoples from a bunch of .Net developers and discussed with them on their interest in learning Tridion. When everything was set, we prepared a schedule for two weeks. Theories in the first week, 2 hours daily, and lab sessions in the second week.
This is our second week so team is fully involved in doing lab exercises. I can say that people are more interested in learning Tridion than we thought. Good to know that bunch of Tridion developers are coming out. Now its all rely on getting into right projects
Since we are concentrating primarily on technical front, we focused only on core topics.
| Topics | Details |
| Basics of Content Management Systems & SDL Tridion | 1) Introduction of CMS, WCM, ECM 2) Basics of SDL Tridion 3) Demo on creating building blocks |
| Template Building Blocks | 1) Basics of TBB 2) Template Builder 3) Demo of creating TBBs in all types |
| Event Systems | 1) Basics of Tridion Events 2) Customizing Event Systems 3) Deploying, Enabling and Debugging Event Systems |
| Workflows | 1) Basics of Tridion Workflows 2) Linking workflows with Tridion 3) Services related to Workflows |
| Content Broker | 1) Basics of Content Broker 2) Dynamic Publishing |
This schedule was based on SDL Tridion 2009 and we are trying to setup a lab on Tridion 2011, which would definitely need another week session
Delete keywords in Tridion
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.
- Classified with other building blocks
- Used in component or metadata fields
- 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.
I ran the tool and got the following output.
The source code can be copied from this link.
Get Child publications in Tridion
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>
Blockers for deleting keywords in Tridion
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
- Classified by other building blocks
- 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.
- Simple and Easy
- Get category object and retrieve all keywords in xml
- Loop though each keyword
- unclassify if classified
- unlocalize if localized
- Find root keyword and delete it.
- Nice Programming
- Get root keyword and navigate through its child recursively
- If the child is
- classified then unclassify
- localized then unlocalize
- 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.
