By Karl-Johan Sjögren, Content Studio Linkvalidator developer

Content Studio Linkvalidator is a new product in the Content Studio product family. The goal of Content Studio Linkvalidator is to crawl a specified Content Studio site looking for broken hyperlinks and report them to a specified administrator.

Requirements for Content Studio Linkvalidator

Installation steps

Creating a serverside eventhandler for notifying someone when a broken link is found

The easiest way to notify someone when a broken link is found is to create a serverside eventhandler for the OnDocumentApprove event. Below is an example of such a eventhandler written for Content Studio Linkvalidator. For more information about serverside eventhandlers see this article.

using System;
using System.Net.Mail;
using System.Xml;
using ContentStudio.Document.EPT;
using ContentStudio.Document;
using ContentStudio.EventActions;

namespace ExampleHandler
{
    /// <summary>
    /// Example handler that sends an email when a document is approved in a category.
    /// This was built as an example for Content Studio Linkvalidator and won't work
    /// with EPT-categories that doesn't have the specific fields that Content Studio
    /// Linkvalidator creates.
    /// </summary>
    public class SendEmailOnApprove : ICSAsyncEventHandler
    {
        public void EventHandler(int csEvent,
                                 int connectionId,
                                 string eventXMLArguments,
                                 string customData,
                                 ICSCredentialsContainer credentials,
                                 int timeOut,
                                 out string statusText)
        {
            try
            {
                statusText = "Failure";

                if (string.IsNullOrEmpty(eventXMLArguments))
                {
                    statusText = "Failed to load eventXMLArguments";
                    return;
                }

                XmlDocument Xml = new XmlDocument();
                Xml.LoadXml(eventXMLArguments);

                Int32 SessionId;
                if (!Int32.TryParse(Xml.DocumentElement.SelectSingleNode("sessionid").InnerText, out SessionId))
                {
                    statusText = "Failed to read SessionId";
                    return;
                }

                Int32 DocumentId;
                if (!Int32.TryParse(Xml.DocumentElement.SelectSingleNode("documentid").InnerText, out DocumentId))
                {
                    statusText = "Failed to read DocumentId";
                    return;
                }

                SyncronizedEPTDocument Ept = new SyncronizedEPTDocument(connectionId, SessionId, DocumentId, false);

                if (Ept == null)
                {
                    statusText = "Failed to load SynchronizedEPTDocument";
                    return;
                }

                if (Ept.ExistsField("Document") == false || Ept.ExistsField("Query") == false || Ept.ExistsField("BrokenSublinks") == false)
                {
                    statusText = "Failed to load EPT-fields from SynchronizedEPTDocument (Document, Query, BrokenSublinks)" + Environment.NewLine + "EptData for document #" + DocumentId.ToString() + ": " + Ept.ToString();
                    return;
                }

                string Body = string.Format(@"A broken link was discovered by Content Studio Linkvalidator!
The the page not responding is located at {0} and is linked from {1} pages.
These are the pages that link to the broken one:
", Ept["Document"] + Ept["Query"], Ept["BrokenSublinks"]);

                CSSyncronizedDocument Doc = new CSSyncronizedDocument(connectionId, SessionId, DocumentId, CSDocumentInformation.ContentToLoad.DraftOrApproved);

                if (Doc == null)
                {
                    statusText = "Failed to load SynchronizedDocument";
                    return;
                }

                if (Doc.Content == null)
                {
                    statusText = "Failed to load content from SynchronizedDocument";
                    return;
                }

                Xml.LoadXml(Doc.Content);
                XmlNodeList nodeCol = Xml.DocumentElement.SelectNodes("CSRecord/LinkingPages/Page");
                foreach (XmlNode Node in nodeCol)
                {
                    Body += Node.InnerText + Environment.NewLine;
                }

                ContentStudio.SettingManager Mgr = new ContentStudio.SettingManager();

                if (Mgr == null)
                {
                    statusText = "Failed to load SettingManager";
                    return;
                }

                string SmtpHost = Mgr.SettingValue(connectionId, SessionId, "Default_SMTP_Server");

                if (string.IsNullOrEmpty(SmtpHost))
                {
                    statusText = "Failed to load Default_SMTP_Server, call failed or value is empty";
                    return;
                }

                SmtpClient Smtp = new SmtpClient();
                Smtp.Host = SmtpHost;

                MailMessage Message = new MailMessage();
                Message.Subject = "A broken link was discovered by Content Studio Linkvalidator";

                if (string.IsNullOrEmpty(customData))
                {
                    statusText = "Failed to load customData (should be a valid email address)";
                    return;
                }

                Message.To.Add(customData);

                Message.Body = Body;
                Message.From = new MailAddress("linkvalidator@contentstudio.se");
                Smtp.Send(Message);

                statusText = "Success";
            }
            catch (NullReferenceException E)
            {
                statusText = E.Message + Environment.NewLine + E.StackTrace;
            }
        }
    }
}