Crocodoc .NET API Out Of Beta

My colleague Frank Taylor and I are happy to announce that Crocodoc v1.0 for .NET is now out of Beta testing and can be downloaded from its Google Code site. Thanks to everyone who has participated in the 1.0 Beta testing and who has provided feedback or reported any issues.

And for those who may be reading this article wondering what this .NET library is for, check out my previous blog post about its beta release, Frank’s blog entry, or even the Crocodoc site itself.

Besides fixes, only one (overloaded) method has been added to this release. While the previous CrocodocDocument.Upload(string filePath) allowed you to specify a complete file path for you to upload, the method CrocodocDocument.Upload(string fileName, byte[] binary) allows you to upload a document’s byte array directly. This could be useful for when uploading files not from the file system, but say a database or content management system.

Please continue to either contact Frank or myself with any feedback, suggestions, or further issues found. And happy document embedding everyone!

Razor Mediator Version 1.3.2 Released

A new minor version of the Razor Mediator has been released and can be found at the Google Code site. For anyone who is interested in installing Razor Mediator on Tridion 2013, you will find this release especially important to you. Prior to this version, the Installer will throw an error and the installation will fail.

Besides being able to install without an error, this version also changes how the configuration is done slightly. Prior to 1.3.2, the template ID for Razor Mediator was generated during installation by selecting the highest free available ID. With an out of the box Tridion setup, this would normally have resulted in a template type of “8″. Tridion 2013 comes with a new XSLT Mediator, but they have left the ID’s of 8 and 9 empty. So, installation for 1.3.2 will attempt to use 8 if its available, otherwise it’ll pick the ID just like it use to do.

An important thing to note is that you may have to manually modify this ID if you are porting from another system that used a different ID for the Mediator. If the ID’s don’t match up, you will get an error during the content porter process.

Thanks to Nicholas Vander Ende, Frank Taylor, and Piti Itharat for reporting and troubleshooting the installation error in 2013. A special thanks to Nicholas for actually finding the fix to the problem as well.

ComponentPresentations and ComponentTemplateModel

Thanks to Chris Curry for spotting that ComponentPresentationModel’s Template property was not returning a ComponentTemplateModel type, but rather just the Tridion’s ComponentTemplate type. This means you would have to grab the ItemFields in order to fetch fields. This version fixes the CompoenntPresentationModel’s Template property.

@foreach (var cp in ComponentPresentations) {
    <div>cp.Template.Fields.FieldName</div>
}

Get Version From Template

Although you can get the Razor Mediator version by looking at the Tridion.ContentManager.config file, sometimes you may not have access to the server and need another quick way to get the version. 1.3.2 comes with a “Version” property in the base template that you can output to check the version.

    <div>Version: @Version</div>

Models.GetComponentTemplate

The ModelUtilities class now comes with a GetComponentTemplate method to easily pull out ComponentTemplateModels.

@{
    var ct = Models.GetComponentTemplate("tcm:1-2345-32");
}

Thanks again to everyone for your feedback and suggestions!

Changing Components’ Schemas With Core Service

Besides making new extensions and applications to extend the features of Tridion, sometimes the Core Service API is a great tool for scripting something that would otherwise take a long time to do manually. A colleague of mine is in need of such a script, and has never worked with the Core Service API before, so I thought I would write this tutorial to help him get on the right track.

The project he’s in has the need to change the schema for a bunch of multi-media components. The GUI does not provide a way to change the schema, as changing schemas for components could lead to data loss. For example, if your metadata values have different names than the schema that you are switching to, data in those fields would be gone. Fortunately for my colleague, there is no metadata on the old schemas.

As my colleague is somewhat new to the world of .NET as well, this tutorial may explain some of the basics of .NET and Visual Studio as well.

The Project Setup

To start, create a new .NET Console Application project in Visual Studio (for this tutorial, I am using Visual Studio 2010 Professional Edition). Lets call it “Examples.ComponentSchemaChanger”. Visual Studio will create a new project with a Program class that contains a static void Main(string[] args) method. This Main method is the entry point for your console application. The args parameter is arguments that you can pass to your application from the command line.

Next, create a folder in Windows in the Solution folder that was created called “Resources”. Here we’ll include all referenced assemblies that our project will use. Since this is a fairly simple script, only one will be needed.

On a Tridion server (or VM), from the /bin/client/ directory of the location where Tridion is installed. Copy “Tridion.ContentManager.CoreService.Client.dll.config” and “Tridion.ContentManager.CoreService.Client.dll” from this location to the Resources folder you created in the previous step. The DLL contains the Core Service API that you will use for this script, and the config contains the configuration bindings that your application will use to connect to Tridion.

Next, lets add the reference to the DLL. Right click the References folder in your Visual Studio project, and select “Add Reference”. Select the “Browse” tab, and then browse to your Resources folder. Select “Tridion.ContentManager.CoreService.Client.dll” and click “Ok”.

You’ll also need to add several of other references. Right click and add references, but this time select the “.NET” tab and add references to “System.Configuration”, “System.ServiceModel” and “System.Runtime.Serialization”.

Now that we have referenced the Core Service library, lets modify the configuration. Right click the project, and select “Add -> New Item”. Under the General section, select “Application Configuration File”. Next copy the contents of “Tridion.ContentManager.CoreService.Client.dll.config” to your “App.config” folder. Right underneath the <configuration> element in your App.config file, add the following:

  <appSettings>
    <add key="CoreServiceEndpoint" value="netTcp_2011" />
    <add key="TridionUsername" value="YOUR TRIDION USERNAME" />
    <add key="TridionPassword" value="YOUR TRIDION PASSWORD"/>
  </appSettings>

The above will let you set the username and password that your script will use to authenticate with the Core Service, as well as which endpoint name to use (more on that later).

The SchemaChanger Class

Let’s add a new class to the project called “SchemaChanger”. Right click the project, click “Add -> Class” and then give it the name “SchemaChanger”.

Now add the following field to your new class:

private SessionAwareCoreServiceClient _client;

You’ll see an error squiggly line and a message of “The type or namespace name ‘SessionAwareCoreServiceClient’ could not be found (are you missing a using directive or an assembly reference?)”. This is because we have not referenced the namespace in a using statement. Let’s let Visual Studio do this automatically for us. Click the squiggly lined “SessionAwareCoreServiceClient”, and when the down arrow appear, click that, and then “using Tridion.ContentManager.CoreService.Client;“. You’ll see the using statement automatically added to the top, and your error message has gone away. For the rest of this tutorial, do the same to any similar errors you encounter to automatically add the the namespaces with the using directive.

Next, let’s add a property to our class that will use a getter accessor for getting our client.

public SessionAwareCoreServiceClient Client
{
    get
    {
        if (_client == null)
        {
            string endpointName = ConfigurationManager.AppSettings["CoreServiceEndpoint"];
            if (String.IsNullOrEmpty(endpointName))
            {
                throw new ConfigurationErrorsException("CoreServiceEndpoint missing from appSettings");
            }

            _client = new SessionAwareCoreServiceClient(endpointName);

            string username = ConfigurationManager.AppSettings["TridionUsername"];
            string password = ConfigurationManager.AppSettings["TridionPassword"];

            if (!String.IsNullOrEmpty(username) && !String.IsNullOrEmpty(password))
            {
                _client.ClientCredentials.Windows.ClientCredential = new NetworkCredential(username, password);
            }
        }
        return _client;
    }
}

What the above is doing is, if our _client variable is null, to create a new instance of SessionAwareCoreSerivceClient using the endpoint that we supply in our appSettings. If _client has already been instanciated, it’ll return it without creating a new instance. We’ll also be using the TridionUsername and TridionPassword appSettings that we set up earlier. If the appConfig does not have these settings, or if they are empty, the application will attempt to use the user that will be running the script. It also checks to make sure the CoreServiceEndpoint appSetting exists, and throws an error if its missing.

Next, lets add a method that’s going to be doing the work for us. Let’s call this method “ChangeSchemasForComponentsInFolder”, and allow it to take 4 arguments: one for the TcmUri of the folder we want to search in, one to allow the operation to happen recursively, the third one for the TcmUri of the schema that we want to change from, and the forth one for the TcmUri of the schema that we want to change the components to.

public void ChangeSchemasForComponentsInFolder(string folderUri, bool recursive, string fromSchemaUri, string schemaUriToChangeTo)
{

}

We can only modify components that aren’t already checked out, so lets create a variable that will keep track of components that we are not able to edit.

    List<ComponentData> failedItems = new List<ComponentData>();

Next we’ll want to open the folder that we are going to search in. We can use the CoreService client’s Read method for this.

    FolderData folder = Client.Read(folderUri, null) as FolderData;

We’ll also want to grab the Schema that we’ll be switching to, and the namespace of that schema.

    SchemaData schema = Client.Read(schemaUriToChangeTo, null) as SchemaData;
    XNamespace ns = schema.NamespaceUri;

Now we’ll create a filter to actually query for multimedia items. We’ll want to make sure to only grab Components, and to only grab components of a Multimedia type. We’ll also want to check for components recursively if we’ve supplied to do so in the passed argument.

    OrganizationalItemItemsFilterData filter = new OrganizationalItemItemsFilterData();
    filter.ItemTypes = new ItemType[] { ItemType.Component };
    filter.ComponentTypes = new ComponentType[] { ComponentType.Multimedia };
    filter.Recursive = recursive;

And finally the actual work of switching the schema of the component. We’ll open up the component in read mode first, and only check it out to modify if the component’s current schema ID matches the schema we want to change from. If the component doesn’t match, we’ll attempt to check it out. If we are successful on checking it out, we’ll change its schema, save, and check back in. If we weren’t successful on checking the item out, we’ll make note of that item to report at the end.

    XElement items = Client.GetListXml(folder.Id, filter);
    foreach (XElement item in items.Elements())
    {
        ComponentData component = Client.Read(item.Attribute("ID").Value, null) as ComponentData;

        if (!component.Schema.IdRef.Equals(fromSchemaUri))
        {
            // If the component is not of the schmea that we want to change from, do nothing...
            continue;
        }

        component = Client.TryCheckOut(component.Id, new ReadOptions()) as ComponentData;

        if (component.IsEditable.Value)
        {
            component.Schema.IdRef = schemaUriToChangeTo;
            component.Metadata = new XElement(ns + "Metadata").ToString();
            Client.Save(component, null);
            Client.CheckIn(component.Id, null);
            Console.WriteLine(String.Format(" - changed schema for {0} ({1}) ", component.Title, component.Id));
        }
        else
        {
            failedItems.Add(component);
        }
    }

This is where the namespace of the schema comes into play with the component.Metadata = new XElement(ns + "Metadata").ToString() Here we are setting up an empty metadata section using the namespace from the Schema that we are switching to. Without this line, you might see an error like “Root element must be in namespace”.

What about that failedItems variable that we were keeping track of? Good catch. Let’s go ahead and report the items that we weren’t able to change by adding the following to the end of our method.

    if (failedItems.Count > 0)
    {
        Console.WriteLine();
        Console.WriteLine("The following items could not be converted. Please have them checked in and try again.");
        foreach (ComponentData component in failedItems)
        {
            Console.WriteLine(component.Id + " :" + component.LocationInfo.WebDavUrl);
        }
    }

Our SchemaChanger class is now complete, and should look something like the following:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Net;
using System.Xml.Linq;
using Tridion.ContentManager.CoreService.Client;

namespace Examples.ComponentSchemaChanger
{
    /// <summary>
    /// Script example of changing schemas for multi media components.
    /// </summary>
    class SchemaChanger
    {
        /// <summary>
        /// The session aware core service client.
        /// </summary>
        private SessionAwareCoreServiceClient _client;

        /// <summary>
        /// Gets the SessionAware Core Service client.
        /// </summary>
        public SessionAwareCoreServiceClient Client
        {
            get
            {
                if (_client == null)
                {
                    string endpointName = ConfigurationManager.AppSettings["CoreServiceEndpoint"];
                    if (String.IsNullOrEmpty(endpointName))
                    {
                        throw new ConfigurationErrorsException("CoreServiceEndpoint missing from appSettings");
                    }

                    _client = new SessionAwareCoreServiceClient(endpointName);

                    string username = ConfigurationManager.AppSettings["TridionUsername"];
                    string password = ConfigurationManager.AppSettings["TridionPassword"];

                    if (!String.IsNullOrEmpty(username) && !String.IsNullOrEmpty(password))
                    {
                        _client.ClientCredentials.Windows.ClientCredential = new NetworkCredential(username, password);
                    }
                }
                return _client;
            }
        }

        /// <summary>
        /// Changes schemas for multimedia components located in a specific folder.
        /// </summary>
        /// <param name="folderUri">The tcm uri of the folder to change the items in.</param>
        /// <param name="recursive">Whether or not to perform this recursively on child folders.</param>
        /// <param name="fromSchemaUri">The tcm uri of the schema that we are changing from.</param>
        /// <param name="schemaUriToChangeTo">The tcm uri of the schema to change the components to.</param>
        public void ChangeSchemasForComponentsInFolder(string folderUri, bool recursive, string fromSchemaUri, string schemaUriToChangeTo)
        {
            // Let's keep track of items that couldn't be checked out and report at the end.
            List<ComponentData> failedItems = new List<ComponentData>();

            // Open the folder to check
            FolderData folder = Client.Read(folderUri, null) as FolderData;

            // Open up the schema that we will be changing to.
            SchemaData schema = Client.Read(schemaUriToChangeTo, null) as SchemaData;
            XNamespace ns = schema.NamespaceUri;

            // Create a filter to get all multi-media components.
            OrganizationalItemItemsFilterData filter = new OrganizationalItemItemsFilterData();
            filter.ItemTypes = new ItemType[] { ItemType.Component };
            filter.ComponentTypes = new ComponentType[] { ComponentType.Multimedia };
            filter.Recursive = recursive;

            XElement items = Client.GetListXml(folder.Id, filter);
            foreach (XElement item in items.Elements())
            {
                ComponentData component = Client.Read(item.Attribute("ID").Value, null) as ComponentData;

                if (!component.Schema.IdRef.Equals(fromSchemaUri))
                {
                    // If the component is not of the schmea that we want to change from, do nothing...
                    continue;
                }

                if (component.Schema.IdRef.Equals(schema.Id))
                {
                    // If the component already has this schema, don't do anything.
                    continue;
                }

                component = Client.TryCheckOut(component.Id, new ReadOptions()) as ComponentData;

                if (component.IsEditable.Value)
                {
                    component.Schema.IdRef = schemaUriToChangeTo;
                    component.Metadata = new XElement(ns + "Metadata").ToString();
                    Client.Save(component, null);
                    Client.CheckIn(component.Id, null);
                    Console.WriteLine(String.Format(" - changed schema for {0} ({1}) ", component.Title, component.Id));
                }
                else
                {
                    failedItems.Add(component);
                }
            }

            if (failedItems.Count > 0)
            {
                Console.WriteLine();
                Console.WriteLine("The following items could not be converted. Please have them checked in and try again.");
                foreach (ComponentData component in failedItems)
                {
                    Console.WriteLine(component.Id + " :" + component.LocationInfo.WebDavUrl);
                }
            }
        }
    }
}

The Program Class

Now lets go back to the Program class’ Main method and put our code to work. We’ll allow the user to pass into the command line the arguments for the folder’s tcm uri, whether or not to perform this recursively, the schema uri of the components we want to change from, and the schema uri of the schema to change the components to. We’ll also display some a usage message when an incorrect # of arguments is supplied, a message showing the error if one pops up, and finally a message letting the user know that the script has completed.

static void Main(string[] args)
{
    if (args.Length < 4)
    {
        Console.WriteLine("Usage: Examples.ComponentSchemaChanger  <y/n for recursive>  ");
        Console.WriteLine("Example:");
        Console.WriteLine("Examples.ComponentSchemaChanger tcm:100-12345 y tcm:100-987-8 tcm:100-1234-8");
    }
    else
    {
        string folderUri = args[0];
        string recursive = args[1].ToLower();
        string schemaUriFrom = args[2];
        string schemaUriTo = args[3];

        try
        {
            SchemaChanger changer = new SchemaChanger();
            changer.ChangeSchemasForComponentsInFolder(folderUri, recursive.Equals("y"), schemaUriFrom, schemaUriTo);
        }
        catch (Exception ex)
        {
            Console.WriteLine("There was an error:");
            Console.WriteLine(ex);
        }
    }

    Console.WriteLine();
    Console.WriteLine(" press <ENTER> to continue");
    Console.ReadLine();
}

Building and Deploying

Right click the project and select “Build”. This will compile and build the project for you, putting the files you need in %Project Directory%\bin\Debug\ (or %Project Directory%\bin\Release\ if you are targeting Release). The 3 files that you’ll need are Examples.ComponentSchemaChanger.exe, Examples.ComponentSchemaChanger.exe.config (this is the file containing your app settings), and Tridion.ContentManager.CoreService.Client.dll.

Executing Our Script

Our script is setup to work either on the CMS Server or remotely with a simple configuration change. Remember that CoreServiceEndpoint application config setting that we added? When this setting is “wsHttp_2011″, you’ll be able to run your script remotely (as long as you have access to contact the CMS server from your location), and when this setting is “netTcp_2011″, you’ll be able to run locally on the CMS Server. You can actually run the wsHttp_2011 binding from the CMS Server, but the netTcp binding will perform faster for you.

Whether deployed locally or remotely, open up the command prompt, navigate to the folder you deployed to, and enter the following command:
Examples.ComponentSchemaChanger tcm:12-3456-2 y tcm:12-1000-8 tcm:12-1234

Sit back, and watch magic happen. :)

Again a Warning

Remember as mentioned, changing a component’s schema can lead you to lose data from fields if the field definitions are different. Before trying out the following code, make sure the components you are changing doesn’t contain any such fields that will be lost.

Stupid Mistakes: Tridion Event System and Async Subscriptions

It’s easy to make one of those simple and stupid mistakes when developing with Tridion.  You know, one of those mistakes that make you lose an hour or more to debugging.  One of those mistakes that, after realizing what you did wrong, you are extremely happy you found the issue, but at the same time still just a bit embarrassed that you missed it in the first place.  I thought I’d share one that I made over the weekend in hopes that perhaps if someone else accidently went down the wrong path they’d quickly be able to correct themselves.

Over the weekend I was working on some Event System code that worked with Multimedia Components.  I’ll spare most of the details, but the code had to do two main things: 1.) Get the binary data out of the Multimedia Component and 2.) Set a Metadata Field to a value if the field was empty.  Pretty easy right?  We’ve all done these things before and this kind of functionality should be smooth sailing.  Until its not…

Coded. Deployed. Created a new Multimedia Component.  Save and Closed.  And… wth? The Event Log showed the following error.

A database error occurred while executing Stored Procedure "EDA_ITEMS.GETBINARYCONTENT".
ORA-01403: no data found
ORA-06512: at "TRIDION_CM.EDA_ITEMS", line 4100
ORA-06512: at line 1

Looks like my code didn’t like my call to the GetByteArray() method… weird. This portion of the Event System was using the Processed event phase. Interestingly, switching it to Initiated made the error go away. Shouldn’t be a solution, but hey, it was working now. I’ll move on and figure out the root issue later.

Next I added code to set the Metadata Field when it was null. Coded. Deployed. Saved and Closed the modified MM component. And… wth again? The Event Log now showed the following error.

The item tcm:123-4567-16-v0 does not exist.

Another piece of code I’ve done countless times… why is it failing now?  Tridion, why have you forsaken me!?!

The Issue

After much debugging and even coming up with a workaround to get my code working, I looked up at the piece that was subscribing my event and immediately face palmed.

EventSystem.SubscribeAsync<Component, SaveEventArgs>(OnComponentSaveInitiated, EventPhases.Initiated);

Can you see the issue above? Yep, I had subscribed my event asynchronously, and those weird issues I was seeing was the price I paid for doing it incorrectly.  For those of you who are not familiar with the Tridion Event System, you should only subscribe events asynchronously via the SubscribeAsync method with a TransactionCommitted phase.

What had happened was, when I started on this Event Handler, I had a different set of requirements. I was originally going to use a TrasnactionCommitted phase and a Check In event, and started coding it, but switched to the Initiated phase with a Save event once I got the updated requirements. Unfortunately, I forgot to change the subscription method to not subscribe asynchronously.

EventSystem.Subscribe<Component, SaveEventArgs>(OnComponentSaveInitiated, EventPhases.Initiated);

Ah, all better now…

Another Clue…

Another clue that I had used the wrong subscription method should have been the errors themselves. Normally when an Exception is thrown in the Initiated or Processed phase, that error prevents the save, and the error message is displayed to the user. With my code, the component was saving just fine, and the error message was only getting logged and not communicated to the user.

Checking If An Item Is In Workflow With Core Service

During your quest of making custom applications using Tridion’s Core Service API, you may have come across the need to check whether or not an item is currently in a Workflow Process. Luckily, unlike some other tasks you may need to do with the Core Service API, this task is easily done by checking the WorkflowInfo property that is included in the PageData and ComponentData classes.

// Open a page in Core Service.
PageData page = client.Read(yourPageUri, new ReadOptions()) as PageData;

if (page.WorkflowInfo != null)
{
    // Ladies and gentlemen, WorkflowInfo property was not null, this page is in workflow
}
else
{
    // WorkflowInfo property is null, the page is not in workflow
}

Great, but now that you know that your item is in workflow, you’ll probably be wanting to grab some Workflow related items. The good news is, the WorkflowInfo class contains most of the data you’ll need to interact with the Workflow for that item. Here are some common things you’ll probably do with this property:

// Get the ProcessInstanceData from the WorkflowInfo property
ProcessInstanceData processInstance = 
    (ProcessInstanceData)client.Read(page.WorkflowInfo.ProcessInstance.IdRef, null);

// Get the ProcessDefinitionData from the process instnace
ProcessDefinitionData processDefinition =
    (ProcessDefinitionData)client.Read(processInstance.ProcessDefinition.IdRef, null);

// Getting the ActivityInstanceData from the WorkflowInfo property...
ActivityInstanceData activityInstance = 
    (ActivityInstanceData)client.Read(page.WorkflowInfo.ActivityInstance.IdRef, null);

// Get the ActivityDefinitionData from the activity instance
ActivityDefinitionData activityDefinition =
    (ActivityDefinitionData)client.Read(activityInstance.ActivityDefinition.IdRef, null);

// Note that if you only wanted to get the ActivityDefinition's Description, you can get that from the WorkflowInfo's ActivityDefinitionDescription property.
if (page.WorkflowInfo.ActivityDefinitionDescription.Equals(activityDefinition.Description))
{
    // true of course...
}

// Check how long its been since the activity was started...
if (page.WorkflowInfo.StartDate.HasValue)
{
    TimeSpan timeSinceStart = DateTime.Now - page.WorkflowInfo.StartDate.Value;
}

Other Useful Properties of WorkflowInfo

Here are the other properties of WorkflowInfo that wasn’t included in the samples above:

ActivityState: The enum value of the activity’s current state.
Assignee: The Link<TrusteeData> of the assignee.
CreationDate: The Nullable<DateTime> that the activity was created.
FinishDate: The Nullable<DateTime> that the previsou activity instance was finished.
Performer: The Link<UserData> of the activity’s performer.
PreviousMessage: The FinishMessage of the previous Activity.

Crocodoc .NET API Library Released

For those of you reading the title and asking, “What is this Crocodoc???”, Crocodoc is a tool that “converts Microsoft Office and PDF documents to HTML5 so your users can view and collaborate right in your web app.” What that means is, you can present your customers documents such as Word docs, PDFs, and Power Point presentations… embedded and viewable within your web pages. Long are the days where your customers must download your PDFs in order to view them.

That being said, my colleague and friend, Frank Taylor, needed to interact with the Crocodoc v2 REST API, but discovered Crocodoc was missing the .NET API. So after working together, we are now happy to announce that the Beta version of 1.0 for a .NET Wrapper has been released on its Google Code site. If you just want to jump straight into the action, you can download the binaries here. For those of you who might have worked with the Crocodoc Java or PHP API’s, you should feel comfortable with the .NET API.

Although the project’s Google Code site has plenty of samples (and there’s even an Examples project you can download and run as well), here’s a quick sample of its usage.

Crocodoc.ApiToken = "your-unique-api-token-right-here";
string sessionID;

try
{
    // Here, documentUUID is the uuid for the Document that you want to create a session for.
    sessionID = CrocodocSession.Create(documentUUID);
}
catch (CrocodocException ex)
{
    // Log or show error message if the Crocodoc server threw a web exception.
}

The above creates a unique session for a given document so that you can then embed it onto your webpage. The session allows you to set properties such as copyrighted, admin privies, or even allow the writing of annotations for your editors.

Here’s an example of uploading a new document to crocodoc.

try
{
    string newDocUUID = CrocodocDocument.Upload(@"C:\Documents\YourPDF.pdf");
}
catch (CrocodocException ex)
{

}

Your Help?

We have released it as version 1.0 Beta as we would like some of your help and feedback with the library. If you use Crocodoc and have been looking for a .NET API, try it out, and let us know any issues that you run into, or if you have any feature requests.

Quick Links

Crocodoc API Reference
Crocodoc .NET
Crocodoc .NET Binaries

Tridion Area51 Notifications

If you haven’t yet signed up to commit to the dedicated Tridion StackExchange site and are interested in supporting it, you should do that now. You can read more about the goals we need to reach in my previous post.

If you are interested in getting notifications and updates regarding the status of the dedicated Tridion site, a fellow Tridionaught, Chris Summers, is putting together an e-mail list. You can sign up for it over on the Tridion Developer site.

We are getting closer! In my last post, we were at 75% of the commitment score goal and needed 4 more people committed who had 200 rep on any of the sites. Today as of this post, we have the 100 people with 200 rep required, and are at 84% of reaching the commitment goal. Nice work everyone!

Tridion Area 51 Site Proposal

When I first started with Tridion, I remember wishing that there was more places than just the forums to look for information and help on Tridion (and maybe it was just my own greenness that overlooked anything that was actually out there beyond the forums during that time). Today its a different case, do a quick Google search for what you are looking for and you should come across Tridion related blogs and Q&A sites. One of those Q&A sites you’ve most likely stumbled upon is the Stack Overflow site and the Tridion tag. And if you haven’t heard, there’s a proposal for a dedicated Tridion site in the StackExchange system (you know, that Area 51 banner you see on most of the Tridion blogs and various posts and comments asking for your help by committing to the site?). That’s right, a dedicated StackExchange just for SDL Tridion where you can go to get help on your Tridion related questions. At the time of this writing, our commitment stats to reach our goals for this site look like:


Commitment Goals for Tridion Area 51

Currently we’re in our Commitment goal, which means before the site is even created, StackExchange needs to know that the site will have enough people that will be using it. The first goal, having 200 committers in total, has been reached (with a total of 251 so far!). The second goal, having 100 of those committers that have a total reputation score of 200 or more on any Stack Exchange site, we are just around the corner of reaching with a total of 96 (just 4 more to go!). The third goal is the tricky one, the committment score. This score is calculated based on all the users’ reputation and activeness on other sites. There’s also a decay factor involved, meaning that you should revisit the Area 51 page once in awhile to renew your vote.

What can I do to help?

  • If you have not done so already, commit to the Tridion Area 51 Site Proposal.
  • If you do not have a reputation of 200 on any of the Stack Exchange sites, try to achieve this. You can gain rep not only by answering questions, but by asking them as well! Start asking, answering and voting on questions and answers on the StackOverflow Tridion Tag.
  • Even if you don’t have anything you want to ask or answer, start voting for the questions and answers that you found useful. This may not increase your rep score, but could push another member’s score and help raise the Commitment Score.
  • Having a rep of 200 or more on multiple sites also helps! Start asking and answering away on other sites that interest you.
  • Don’t let your vote decay! If you haven’t visited the Area 51′s Tridion page in awhile, make sure to login and visit the page!

As a reminder, visit this page to see how the Commitment Score is calculated.

Thanks everyone for your help, and I hope to be seeing you on our own dedicated Tridion site soon!

Razor Mediator Version 1.3.1 Released

The next version of the Razor Mediator, version 1.3.1, is now up and ready to be downloaded at its Google Code site, and its updated documentation can be found here. This update includes mainly fixes for issues reported, though it does include a couple of new (or features that were missing) features as well. If after upgrading to version 1.3 and you have been receiving errors while attempting to do imports from the configuration, this may be the release for you!

Fixes for Imports

As mentioned, version 1.3 caused some issues when trying to use the new Where Used functionality along with global imports from the configuration. Errors included messages similar to “tcm:0-234-2048 does not exist”.

Fixes to Documentation

Thanks to Robert Curlette for supplying documentation that removed the smart quotes from the code samples in the documentation. The new version of the documentation and forward will be based off of his ascii documentation.

Non-Cache DynamicPackage

Thanks to Dominic Cronin for supplying a patch for the DynamicPackage to make it not cache the package’s values. This will ensure you don’t run into issues if the context of those package items gets changed during the razor’s scope.

Indexes for DynamicItemFields and DynamicPackage

You can now access fields for ItemFields and for the Package using indexers. This can help when creating generic templates, or when working with package items that contain dots in the item names.

@Fields["FieldName"]
@Package["SomeName"]
@Package["Some.Name.With.Dots"]

GetFields and GetFieldNames

Also to assist with the creation of making generic templates, DynamicItemFields now has a GetFields() and a GetFieldNames() method. GetFieldNames() returns an array of strings containing the field names, while GetFields() returns the underlying Dictionary<string, object> that represents the ItemFields.

@foreach (string name in @Fields.GetFieldNames()) {
    <span><strong>@name</strong>: @Fields[name]</span>
}

// var field is of type KeyValuePair<string, object>
@foreach (var field in @Fields.GetFields()) {
    <span><strong>@field.Key</strong>: @field.Value</span>
}

Fix to IsSiteEditEnabled

The IsSiteEditEnabled property would throw an error when working with a Publication Target that never had its Site Edit enabled or disabled yet. This is now fixed.

ParentKeywords and RelatedKeywords

These properties have been added to the KeywordModel, and both return a List of KeywordModel’s.

@foreach (var kw in @Fields.SomeKeyword.ParentKeywords) {
    <span>@kw.Title</span>
}

Thanks again to everyone who has been posting suggestions, issues, and fixes. Special thanks to Robert Curlette who has been the guinea pig for most of these updates!

Getting Using and Used Items With Core Services

A while back I posted about how to get Using and Used Items from Tridion using the TOM.NET API. Today I thought I would share again those same examples, only this time using the Core Service API. For those of you who may not know, the TOM.NET API is read only when used in Template Building Blocks, and read/write when used in the Tridion Event System. But, when you want to create custom applications or processes, you’ll want to create them using the Core Service API.

One of the major differences if you’re comparing this article with the TOM.NET one, is that the Core Service API doesn’t actually have a GetWhereUsed() or GetWhereUsing() method on object classes. Instead, we’ll be retrieving XML using the Core Service Client’s GetListXml(string identifier, SubjectRelatedListFileterData filter) method.

You may notice there’s a GetList(id, filter) method too and may be tempted to bypass working with the XML returned from GetListXml… however, as of Tridion 2011 SP1, if you look at the API’s documentation, currently the only filter supported by this method is the OrganizationalItemAncestorsFilterData, so trying to use that method with our UsingItemsFilterData or UsedItemsFilterData from the examples below will fail, and you’ll probably get an error stating “Unexpected List Type”.

Get Using Items (UsingItemsFilterData)

To get using items with Core Services, you just need to pass the GetListXml(id, filter) a filter of type UsingItemsFilterData, and the tcm uri of the item that you want to get the using items for.

GETTING ALL PAGES USED BY A COMPONENT:

SessionAwareCoreServiceClient client = new SessionAwareCoreServiceClient("netTcp_2011");

UsingItemsFilterData filter = new UsingItemsFilterData();
filter.ItemTypes = new [] { ItemType.Page };
filter.IncludedVersions = VersionCondition.OnlyLatestVersions;

XElement pages = client.GetListXml("tcm:12-3456", filter); // Pass in a Component ID

XML Sample Returned By GetListXml:

<tcm:ListUsingItems xmlns:tcm="http://www.tridion.com/ContentManager/5.0">
    <tcm:Item ID="tcm:17-384-64" Title="Some Page Title" Type="64" OrgItemID="tcm:17-107-4" Path="\040 Web Publication\Root\030 - Work" Icon="T64L1P0" Publication="040 Web Publication"></tcm:Item>
    <tcm:Item ID="tcm:31-871-64" Title="Another Page Title" Type="64" OrgItemID="tcm:31-206-4" Path="\050 Another Web\Root\030 - Work" Icon="T64L1P1" Publication="050 Another Web"></tcm:Item>
</tcm:ListUsingItems>

GETTING ALL COMPONENTS USED BY A SCHEMA:

SessionAwareCoreServiceClient client = new SessionAwareCoreServiceClient("netTcp_2011");
                
UsingItemsFilterData filter = new UsingItemsFilterData();
filter.ItemTypes = new ItemType[] { ItemType.Component };
filter.IncludedVersions = VersionCondition.OnlyLatestVersions;
filter.InRepository = new LinkToRepositoryData { IdRef = "tcm:0-31-1" };

XElement components = client.GetListXml("tcm:2-572-8", filter); // Pass in a Schema ID

GETTING ALL SCHEMAS THAT USED A GIVEN EMBEDDED SCHEMA:

UsingItemsFilterData filter = new UsingItemsFilterData
{
    ItemTypes = new ItemType[] { ItemType.Schema },
    IncludedVersions = VersionCondition.OnlyLatestVersions
};

XElement schemas = client.GetListXml("tcm:2-345-8", filter); // Pass in a Schema ID

GETTING ALL COMPONENTS USING A GIVEN KEYWORD:

UsingItemsFilterData filter = new UsingItemsFilterData
{
    ItemTypes = new ItemType[] { ItemType.Component },
    IncludedVersions = VersionCondition.OnlyLatestVersions
};

XElement components = client.GetListXml("tcm:2-213-1024", filter); // Pass in a Keyword ID

Note that the above could have just been retrieved using ClassifiedItemsFilterData.

XElement components = client.GetListXml("tcm:2-213-1024", new ClassifiedItemsFilterData());

Get Used Items (UsedItemsDataFilter)

Getting Used items from Core Service works in the same manner as getting Using items, except that you pass in a UsedItemsDataFilter as the filter instance.

GET THE EMBEDDED SCHEMAS USED IN A GIVEN SCHEMA:

SessionAwareCoreServiceClient client = new SessionAwareCoreServiceClient("netTcp_2011");
XElement schemas = client.GetListXml("tcm:2-184-8", new UsedItemsFilterData { ItemTypes = new[] { ItemType.Schema } });

GET COMPONENTS WITHIN COMPONENTLINKS OF A GIVEN COMPONENT:

UsedItemsFilterData filter = new UsedItemsFilterData
{
    ItemTypes = new ItemType[] { ItemType.Component }
};

XElement components = client.GetListXml("tcm:21-543", filter);

Happy coding everyone!