| Alenka's profileSitecore ExperienceBlogLists | Help |
Sitecore Experience |
|||||
|
|
June 06 Mandatory workflow commentsIn the previous post I described how you can enhance your workflows with dialogs which appear after a command is executed. This time I’ll explain how you can make workflow comments mandatory. While we can’t ensure our users will actually enter a meaningful comment, we can certainly force them to enter one. This can be useful for ‘reject’ commands where a user should specify the reason for rejection. Instead of modifying the default Sitecore’s template for a workflow command, let’s start by creating a custom template which inherits from the default one. The template will inherit all fields from the base template, so our job is to add a checkbox field with which we can specify that the comment is mandatory.
So now you’re ready to create your workflow. If you need to add a command which requires a mandatory comment to your state, simply use the enhanced command template we just created and tick the ‘Mandatory’ checkbox. Of course, next step is to customize how workflow commands work. If you have a closer look at /App_Config/Commands.config file, you’ll find this line: <command name="item:workflow" type="Sitecore.Shell.Framework.Commands.Workflow,Sitecore.Kernel" /> So what we need to do is implement a custom class, which does everything that the default class does and additionally to that, checks if the comment should be mandatory. Here’s a hint, use Reflector and an add-in called File Disassembler to create a source copy of the above class. In the Run method, find a line of code, that look something like that (P.S. it’s not a big class, so it’s easy to find): if ((!isPostBack && ui) && !suppresscomment) { SheerResponse.Input("Enter a comment:", ""); args.WaitForPostBack(); } and replace it by something like that: if ((!isPostBack && ui) && !suppresscomment) { string commandID = args.Parameters["commandid"]; Item workflowCommand = Client.ContentDatabase.Items[ID.Parse(commandID)]; //check if comment is mandatory CheckboxField mandatoryField = workflowCommand.Fields["mandatory"]; if (mandatoryField != null && mandatoryField.Checked) { SheerResponse.Input("Enter a comment:",
The above described approach will work when a workflow command is executed from Content Editor application. However, commands in the Workbox application work a bit differently because there are more options available. User can execute commands on individual items, on selected items or all items in a certain state. Therefore you’ll have to have a look into WorkboxForm and customise it with a similar approach. Enjoy and check back regularly to read more tips from the Sitecore kitchen. May 26 Workflow commands with dialogsYou can implement virtually any workflow requirement with Sitecore’s workflow engine. It’s simple, easy to understand, easy to set up and comes with a set of common out-of-the box actions such as send email, auto-publish, perform validation and so on. If these don’t suffice and you need to implement an even more sophisticated workflow, you can of course do so. For example, a pretty common requirement is to set certain field values as part of the workflow command. I have content management fields in mind. I would like to demonstrate how you can display intuitive dialog to your users and allow them to set a field value. For this demo, let’s assume we want to pop up a dialog that prompts a user to choose a review date when approving an item. What you need to do is implement a custom workflow action and add it to the desired command on which you want the dialog box to appear. I want Set review date dialog to appear when user approves content item. On the action item, specify C# class which implements the logic. The class needs to implement Process method which is called when workflow command is executed. This method should contain logic that will pop up a modal dialog. public class SetReviewDateCommand : DialogForm
Okay, so now we need to create SetReviewDateDialog. Easiest way of doing it is to open Developer Center and create new XML Control. By default the new XML control will be placed under /layouts folder, so you might want to move it into the same folder where your class resides. It’s a good idea to place XML controls under one of the folder listed under the following setting in web.config: <controlSources> If the XML control resides in a folder which is not listed in the above setting, you will need to add a control source setting. Next step is to define our control. To create a dialog with a date time picker we’d do something like that: <control xmlns:def="Definition" xmlns="http://schemas.sitecore.net/Visual-Studio-Intellisense"> Okay, so now all we need to do is save the data from the dialog, when user clicks OK button: protected override void OnOK(object sender, EventArgs args)
Sample C# project: May 21 Making your business users really happySitecore’s media library has some very nice features. For example for image files it attempts to automatically import file metadata. Our editors were impressed by it and have quickly tried the same trick with PDF and Word documents. Hmm… why doesn’t this work they asked. It would be really nice if it did. A bit of investigation how this feature works for images and some help from Sitecore’s excellent support team gave me an inspiration to implement a prototype for file documents as well. First on my list was tackling PDF documents. Rather than re-inventing the wheel I decided to use an open source .NET library which allows developers to work with PDF documents - iTextSharp. Then I simply had to implement a class which inherits from Sitecore.Resources.Media.Media and override the following two methods: 1: protected override void UpdateMetaData(MediaStream mediaStream) 2: {3: base.UpdateMetaData(mediaStream); 4: if (!mediaStream.AllowMemoryLoading) 5: {6: Tracer.Error("Could not update PDF meta data as the PDF is larger than the maximum size allowed for memory processing. Media item: {0}", mediaStream.MediaItem.Path); 7: }8: else 9: {10: PdfReader reader = null; 11: try 12: {13: reader = new PdfReader(mediaStream.Stream); 14: if (reader == null) 15: {16: return; 17: }18: Item innerItem = this.MediaData.MediaItem.InnerItem; 19: using (new EditContext(innerItem, SecurityCheck.Disable)) 20: {21: innerItem["title"] = reader.Info["Title"].ToString(); 22: innerItem["description"] = reader.Info["Description"].ToString(); 23: innerItem["keywords"] = reader.Info["Keywords"].ToString(); 24: foreach (object key in reader.Info.Keys) 25: { 26: Log.Info(27: string.Format("PDF file metadata: {0} = {1}", 28: key, 29: reader.Info[key]),30: this); 31: } 32: } 33: }34: catch (Exception ex) 35: {36: Log.Warn("Exceptions occured while reading uploaded PDF file metadata.", ex, this); 37: } 38: } 39: }
Next step is of course configuring Sitecore to use this class when uploading PDFs. We could either do that directly in web.config but there is a much better way. Since Sitecore 6 we have configuration include files and they are a very convenient way of altering or enhancing Sitecore’s default configuration and providing you with a peace of mind when it comes to future upgrades. Something like that… 1: <?xml version="1.0" encoding="utf-8" ?> 2: <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> 3: <sitecore> 4: <mediaLibrary>5: <mediaType name="PDF file" extensions="pdf"> 6: <prototypes>7: <media type="Kaalen.Extras.PdfMedia, MRInternet" /> 8: </prototypes> 9: </mediaType> 10: </mediaLibrary> 11: </sitecore> 12: </configuration>
Sample PdfMedia class implementation: PdfMedia.cs If you’re simply looking for a quick solution, include the above file into your solution, put the configuration include file into /App_Config/Includes folder, download the iTextSharp library and place the dll file into /bin directory. Then compile and enjoy… until the users ask you to implement something similar for Word documents :-) May 17 The pain of pasting Word documentsContent migration on our project has started and we ran into a nasty issue with pasting Word documents into Rich Text Editor. Sure enough, there are special buttons in RTE which partially clean pasted text but in my opinion they’re not nearly good enough. Namely, these buttons are Paste from Word and Paste from Word cleaning fonts and styles. I’m happy that headings are transformed into HTML equivalents, e.g. H1, H2 etc. But bulleted and numbered lists are a major issue for our project. I have decided to come up with a decent solution for these problems and have started investigating. HTML Tidy was the first thing that popped into my mind. I quickly downloaded the necessary assemblies and I had to be a bit picky in doing that. At home I’m working on a 64-bit system and it seems that registering COM components in that case is a bit trickier. To avoid the unnecessary mangling I used my best friend Google to find a ready-to-use .NET wrapper. To my disappointment, HTML Tidy does a good job at cleaning Word documents but again it does not really transform it properly. I continued by trying a few other tools and even online services and none was up to my standards. So I’m back to square zero and I decided to tackle javascripts for RTE. RTE used in Sitecore is Telerik’s RADEditor by the way. This is where I am fully aware that the lack of this particular functionality isn’t Sitecore’s fault but I think Sitecore could do a bit more to push Telerik to improve in this area. Or maybe even consider replacing RADEditor with a different control. I can whinge on Telerik’s forums but even if my cries have an effect, solution is months away for sure. So the plan is to write additional Javascript functions to get the desired result when it comes to bulleted and numbered lists. I already shiver at the thought of writing nasty regular expressions but heck, if that’s the way it has to be, I’ll do it. In the mean time… hint hint… I have actually found a little bug in Telerik’s javascript. I’ll publish my work once I’m done as I’m 100% sure lots of others will find it extremely useful. May 14 Mysterious duplicated languagesA while ago I implemented a custom data provider for Sitecore. We wanted our content editors to be able to choose a content owner from a dropdown or other similar control. We already had standard ASP.NET membership provider tables stored in the core database so I only had to get them displayed in content tree. After that was achieved, I could simply set the tree node which held the user items as a source on the desired template field. It was quite easy to implement the provider and configure that. I only implemented a read-only provider since I didn’t want content editors to be able to modify user information. Later on in the project we plan to improve that data provider and add a custom field control which will enable content editors to search for users by their name, department etc. Since my provider seemed to work well I haven’t paid much attention to it afterwards, until we started showing off our work to business analysts and content migration team. They quickly noticed we had a “duplicate” languages problem in the content editor. Hmm… I was scratching my head and trying to figure out where those duplicate languages came from. I have also played a bit with the languages and added a custom one, then deleted it and so on. Initially I thought my language problem was somehow caused by that. I used reflector.net to explore what could possibly be happening and I quickly found out that DataProvider implementations return a collection of 2 default languages (en and en-US) if language provider isn’t disabled in the implementation of the provider. So if you’re planning on implementing a custom data provider you can learn a bit from my lesson. Read the documentation and instructions on SDN and explore the DataProvider class thoroughly to avoid such mysteries. |
||||
|
|