1.1 Requirements
To generate email notifications for the expired content
1.2 Implementation
A windows service is created to implement above requirement. This service uses Tridion Coreservice API to fetch all the components based on schema “AllFields” and reads “ExpiryDate” metadata of these fetched components. The code checks if the date is within 30 days and sends emails to the creator of this component. The email also contains link to the content.
The email is sent to the user who had created the content. And email id is fetched from FullName of Tridion User seperated in brackets () Tridion User (tridionuser@sdl.com)
Creation of Windows Service
To interact with Tridion Coreservice Client http://cms.electridion.com/webservices/CoreService2011.svc create a new Windows Service.
From the New Project Dialog Box, choose the Windows service template project and name it ContentExpiryNotification.Service like shown below:
References
To Access CoreService API the following jars need to be referred in the ContentExpiryNotification.Service Project.
The project template automatically adds a component class that is called Service
1
by default and inherits fromSystem.
ServiceProcess.
ServiceBase
.
Click the designer. Then, in the Properties window, set the Service
Name
property for Service1
to ContentExpiryNotification
.
Set the
Name
property to ContentExpiryNotification
. Set the AutoLog
property to true
.
In the Program.cs, edit the
Main
method to create an instance of ContentExpiryNotification
.
namespace ContentExpiryNotification.Service
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new ContentExpiryNotification()
};
ServiceBase.Run(ServicesToRun);
}
}
}
In the next section, you will add a custom event log to your Windows service. Event logs are not associated in any way with Windows services. Here
EventLog
component is used as an example of the type of components you could add to a Windows service.
To add custom event log functionality to service
1. In the Solution Explorer, right-click ContentExpiryNotification.cs and select View Designer.
2. From the Components tab of the Toolbox, drag an
EventLog
component to the designer.
3. In the Solution Explorer, right-click ContentExpiryNotification.cs and select View Code.
4. Edit the constructor to define a custom event log.
To access the constructor in Visual C#, expand the Component Designer generated code region.
public partial class ContentExpiryNotification : ServiceBase
{
public ContentExpiryNotification()
{
InitializeComponent();
// Turn off autologging
this.AutoLog = false;
// create an event source, specifying the name of a log that
// does not currently exist to create a new, custom log
if (!System.Diagnostics.EventLog.SourceExists("ContentExpiryNotification"))
{
System.Diagnostics.EventLog.CreateEventSource(
"ContentExpiryNotification", "ContentExpiryNotificationLog");
}
// configure the event log instance to use this source name
eventLog1.Source = "ContentExpiryNotification";
1.3 Implementation Code
OnStart() of Service Execute code
To define what happens when the windows service starts, in the code editor, locate the
OnStart
method that was automatically overridden when the project is created, and write code to send out email notifications.
protected override void OnStart(string[] args)
{
if (args.Length > 0)
{
for (int i = 0; i < args.Length; i++)
{
eventLog1.WriteEntry(args[i]);
bool success = SendExpiryNotifications(args[i]);
if (success)
eventLog1.WriteEntry("Expiry Notifications Sent");
else
eventLog1.WriteEntry("Expiry Notifications Not Sent");
}
}
else
eventLog1.WriteEntry("No Args specified");
}
}
Tridion Core service API to generate emails
private SessionAwareCoreServiceClient m_CoreServiceClient = null;
public SessionAwareCoreServiceClient CoreServiceClient
//private static CoreServiceClient m_CoreServiceClient = null;
// public static CoreServiceClient CoreServiceClient
{
get
{
if (m_CoreServiceClient == null)
{
var netTcpBinding = new NetTcpBinding
{
MaxReceivedMessageSize = 2147483647,
ReaderQuotas = new XmlDictionaryReaderQuotas
{
MaxStringContentLength = 2147483647,
MaxArrayLength = 2147483647
}
};
var remoteAddress = new EndpointAddress("net.tcp://localhost:2660/CoreService/2011/netTcp");
m_CoreServiceClient = new SessionAwareCoreServiceClient(netTcpBinding, remoteAddress);
}
return m_CoreServiceClient;
}
set { m_CoreServiceClient = value; }
}
ReadOptions options = new ReadOptions();
MailMessage _mail = new MailMessage();
public bool SendExpiryNotifications(string schemaURI)
{
try
{
UsingItemsFilterData componentsFilter = new UsingItemsFilterData();
ItemType[] ItemTypes = new ItemType[1];
ItemTypes[0] = ItemType.Component;
componentsFilter.ItemTypes = ItemTypes;
XElement componentListXML = CoreServiceClient.GetListXml(schemaURI, componentsFilter);
XmlDocument componentList = new XmlDocument();
componentList.Load(componentListXML.CreateReader());
XmlNodeList ComponentNodeList = componentList.SelectNodes("//tcm:Item", NSManager);
foreach (XmlNode componentNode in ComponentNodeList)
{
//do stuff get metdatada
String componentURI = "";
if (componentNode.Attributes["ID"] != null)
{
componentURI = componentNode.Attributes["ID"].Value;
ComponentData componentData = (ComponentData)CoreServiceClient.Read(componentURI, options);
if (componentData.Metadata != "" || componentData.Metadata != null)
{
XmlDocument componentMetadataDoc = new XmlDocument();
componentMetadataDoc.LoadXml(componentData.Metadata);
XmlNode metadataNode = componentMetadataDoc.ChildNodes[0];
foreach (XmlNode fieldNode in metadataNode.ChildNodes)
{
if (fieldNode.Name == "ExpiryDate")
{
String expirydate = fieldNode.InnerText;
DateTime dt = Convert.ToDateTime(expirydate);
int noOfDays = GetDaysBetweenDates(dt, System.DateTime.Now);
if (noOfDays < 30)
{
eventLog1.WriteEntry("Mail is being generated for " + componentData.Title);
FullVersionInfo componentVersion = componentData.VersionInfo as FullVersionInfo;
LinkToUserData componentCreator = componentVersion.Creator;
string emailTo = TridionUsers[componentCreator.Title];
//send email notification
_mail.From = new MailAddress(System.Configuration.ConfigurationManager.AppSettings["MAIL_SENDER"]);
_mail.To.Add(emailTo);
_mail.Subject = System.Configuration.ConfigurationManager.AppSettings["MAIL_SUBJECT"];
_mail.SubjectEncoding = Encoding.UTF8;
String url = String.Format(System.Configuration.ConfigurationManager.AppSettings["CMS_URL"], componentData.Id);
string htmlbody = string.Format(System.Configuration.ConfigurationManager.AppSettings["MAIL_BODY"], componentData.Title, noOfDays, url);
_mail.Body = htmlbody;
_mail.BodyEncoding = Encoding.UTF8;
_mail.IsBodyHtml = true;
SmtpClient client;
String smtpHost = System.Configuration.ConfigurationManager.AppSettings["MAIL_SMTPHOST"];
int smtpPort = Int32.Parse(System.Configuration.ConfigurationManager.AppSettings["MAIL_SMTPPORT"]);
if (smtpPort < 1)
{
client = new SmtpClient(smtpHost);
}
else
{
client = new SmtpClient(smtpHost, smtpPort);
}
String enableSSL = System.Configuration.ConfigurationManager.AppSettings["MAIL_ENABLESSL"];
if (!String.IsNullOrEmpty(enableSSL))
client.EnableSsl = Boolean.Parse(enableSSL);
client.SendCompleted += SendCompletedCallback;
String smtpUserName = System.Configuration.ConfigurationManager.AppSettings["MAIL_SMTPUSERNAME"];
String smtpPassword = System.Configuration.ConfigurationManager.AppSettings["MAIL_SMTPPASSWORD"];
if (!String.IsNullOrEmpty(smtpUserName) && !String.IsNullOrEmpty(smtpPassword))
{
client.Credentials = new NetworkCredential(smtpUserName, smtpPassword);
}
try
{
client.Send(_mail);
//Logger.Debug("Email queued for sending to " + _mail.To);
}
catch (SmtpException ex)
{
//log exception
eventLog1.WriteEntry("Expiry Notifications MailServer Exception" + ex.StackTrace);
eventLog1.WriteEntry("Expiry Notifications MailServer Exception Message" + ex.Message);
}
}
}
}
}
}
}
CoreServiceClient.Close();
}
catch (Exception e)
{
//Exception Occured
eventLog1.WriteEntry("Expiry Notifications Exception" + e.StackTrace);
eventLog1.WriteEntry("Expiry Notifications Exception Message" + e.Message);
eventLog1.WriteEntry("Expiry Notifications InnerException Message" + e.InnerException.Message);
return false;
}
return true;
}
/// <summary>
/// method that is called when the Send email is completed
/// </summary>
/// <param name="sender"></param>
/// <param name="aceArgs"></param>
private void SendCompletedCallback(Object sender, System.ComponentModel.AsyncCompletedEventArgs aceArgs)
{
String token = (String)aceArgs.UserState;
if (aceArgs.Cancelled)
{
eventLog1.WriteEntry("EmailHandler.SendCompletedCallback: Send cancelled"+ token);
}
if (aceArgs.Error != null)
{
eventLog1.WriteEntry("EmailHandler.SendCompletedCallback: Error while sending email"+token+" "+aceArgs.Error.ToString());
}
else
{
if (_mail == null)
{
eventLog1.WriteEntry("EmailHandler.SendCompletedCallback: Email [{0}] successfully sent (but mail is null)"+ token);
}
else
{
eventLog1.WriteEntry("EmailHandler.SendCompletedCallback: Email [{0}] successfully sent to {1}"+ token+ _mail.To.ToString());
}
}
}
private int GetDaysBetweenDates(DateTime firstDate, DateTime secondDate)
{
return secondDate.Subtract(firstDate).Days;
}
private XmlNamespaceManager m_NSM;
public XmlNamespaceManager NSManager
{
get
{
if (m_NSM == null)
{
m_NSM = new XmlNamespaceManager(new NameTable());
m_NSM.AddNamespace("tcm", "http://www.tridion.com/ContentManager/5.0");
m_NSM.AddNamespace("xlink", "http://www.w3.org/1999/xlink");
m_NSM.AddNamespace("xhtml", "http://www.w3.org/1999/xhtml");
m_NSM.AddNamespace("xsd", "http://www.w3.org/2001/XMLSchema");
m_NSM.AddNamespace("AllFields", "http://cms.electridion.com/schemas/AllFields");
}
return m_NSM;
}
}
private static readonly Regex UserEmailRegex = new Regex(@"\(.*\)", RegexOptions.Compiled | RegexOptions.Singleline);
/// <summary>
/// Gets the name of the user emails by.
/// </summary>
/// <returns></returns>
public Dictionary<string, string> GetUserEmailsByName()
{
Dictionary<string, string> result = new Dictionary<string, string>();
UsersFilterData usersFilterData = new UsersFilterData { BaseColumns = ListBaseColumns.IdAndTitle };
IdentifiableObjectData[] datas = CoreServiceClient.GetSystemWideList(usersFilterData);
foreach (IdentifiableObjectData data in datas)
{
if (data is UserData)
{
UserData user = (UserData)data;
if (user.Description.Contains("@"))
{
string userDescription = user.Description;
string userName = user.Title;
string userEmail = UserEmailRegex.Match(userDescription).ToString();
if (userEmail != "")
{
// userDescription = userDescription.Replace(userEmail, "").TrimEnd();
userEmail = userEmail.Replace("(", "").Replace(")", "");
if (!result.ContainsKey(userName))
{
result.Add(userName, userEmail);
}
}
}
}
}
return result;
}
private Dictionary<string, string> _tridionUsers = new Dictionary<string, string>();
/// <summary>
/// Gets a Tridion User dictionary with key = user name and value = user email.
/// </summary>
public Dictionary<string, string> TridionUsers
{
get
{
if (_tridionUsers.Count == 0)
{
_tridionUsers = GetUserEmailsByName();
}
else
{
//"Tridion Users called, returning list from Cache.");
}
return _tridionUsers;
}
}
1.4 Creation of Installer
To run any Tridion Service, A valid Tridion user should exist. All tridion services shipped with product installation runs with “Local System” account.
To create the installers for service in 4. 2
· Return to design view for
ContentExpiryNotification
.
· Click the background of the designer to select the service itself, rather than any of its contents.
· In the Properties window, click the Add Installer link in the gray area beneath the list of properties. By default, a component class containing two installers is added to your project. The component is named
ProjectInstaller
, and the installers it contains are the installer for your service and the installer for the service's associated process.
· Access design view for
ProjectInstaller
, and click ServiceInstaller1
.
· In the Properties window, set the Service
Name
property to ContentExpiryNotification
.
· Set the
StartType
property to Automatic
.
· Access design view for
ProjectInstaller
, and click ServiceProcessInstaller1
.
· Set the
Account
property to LocalSystem
.
1.5 Configuration
In Solution Explorer, right-click project and Add new item of type “Application configuration File”. Here the constants are defined.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="CMS_URL" value="https://cms.electridion.com/WebUI/item.aspx?tcm=16#id={0}" />
<add key="MAIL_SENDER" value="Tridion Email Notification Agent <tridion@myagent.com>" />
<add key="MAIL_SUBJECT" value="Component Expiry Notification" />
<add key="MAIL_BODY" value="The component <b> {0} </b> is about to expire in {1} days. <a href={2}>Open Component</a>" />
<add key="MAIL_SMTPHOST" value="smtp.gmail.com" />
<add key="MAIL_SMTPPORT" value="542" />
<add key="MAIL_SMTPUSERNAME" value="" />
<add key="MAIL_SMTPPASSWORD" value="" />
<add key="MAIL_ENABLESSL" value="true" />
</appSettings>
</configuration>
1.6 To build ContentExpiryNotification
Service project
· In Solution Explorer, right-click your project and select Properties from the shortcut menu. The project's Property Pages dialog box appears.
· In the left pane, select the General tab in the Common Properties folder.
· From the
Startup object
list, choose ContentExpiryNotification.ServiceProgram. Click OK.
· Press Ctrl+Shift+B to build the project.
Now that the project is built, it can be deployed. The deployed folder consists of Config file with constants needed by the program and exe files.
1.7 To create a setup project for service built in 1.6
· On the File menu, point to Add Project, and then choose New Project.
· In the Project Types pane, select the Setup and Deployment Projects folder.
· In the Templates pane, select Setup Project. Name the project
ServiceSetup
.
A setup project is added to the solution. Next you will add the output from the Windows service project, ContentExpiryNotification.exe, to the setup.
· In Solution Explorer, right-click
ServiceSetup
, point to Add, then choose Project Output. The Add Project Output Group dialog box appears.
· ContentExpiryNotification is selected in the Project box.
· From the list box, select Primary Output, and click OK.
· Build the project
Browse to the directory where the setup project was saved, and run the
ServiceSetup.msi
Execution of Code (Windows Service)
· Open the Services Control Manager In Windows 2000 Professional
· You should see
ContentExpiryNotification
listed in the Services section of the window after performing all the steps below
· Select your service in the list, right-click it, and then click Properties. In “Start Parameters” pass the schema id and without closing window click start.
Once the above service is started. The emails will be sent to the respective authors. The email content generated for POC is as below
1.8 Logging
Open Server Explorer and access the Event Logs node.
Note: Logs are written under Application
1.9 To uninstall the service
On the Start menu, open Control Panel and click Add/Remove Programs, and then locate your service and click Uninstall.
You can also uninstall the program by right-clicking the program icon for the .msi file and selecting Uninstall.
No comments:
Post a Comment