Quantcast
Channel: Project Lifecycle
Viewing all 178 articles
Browse latest View live

Catching Exceptions Episode 6 : Stephen Pope


Catching Exceptions Episode 7 : Mike Edwards

Catching Exceptions Episode 8 : Adam Najmanowicz

Catching Exceptions Episode 9 : Martina Welander

Catching Exceptions Episode 10 : Mike Reynolds

Catching Exceptions SUGCON North America 2015

$
0
0

I put together a special episode of Catching Exceptions for SUGCON North America 2015 to try to capture some small part of what it was all about. It was a wild ride with a lot of great speakers (I may have been one of 'em) but i wanted to be able to let everyone else who couldn't make it see what was going on, what you missed and to encourage you NOT TO MISS IT EVAR AGAIN! 

I may have also contributed to the introduction video of the SUGCON speakers. It was a ton of work but I had so much talent to work with. LONG LIVE THE SITECORE COMMUNITY! 

Big respect to Hedgehog Development, BrainJocks, Akshay Sura and of course Mike Reynolds.

Catching Exceptions Episode 11 : John West

Catching Exceptions Episode 12 : Pavel Veller


Catching Exceptions Episode 13 : Brian Beckham

Catching Exceptions Episode 14 : Akshay Sura

Catching Exceptions Episode 15 : Corey Caplette

Catching Exceptions Episode 16 : Dan Solovay

Catching Exceptions Episode 17 : Martin Davies

Catching Exceptions Episode 18 : Mark Servais

Adding a button to Rich Text Editor in Sitecore

$
0
0

Sitecore is an incredibly good platform that provides a lot of functionality out of the box, but one of the great things about Sitecore is that if there is something custom that you'd like to add in, there's always a way to do it. I'm going to walk through the process of adding a custom piece of functionality to Sitecore's Rich Text Editor. There are a lot of reasons you would want to do this but for the sake of this example we'll build a simple text insertion demo that you can expand upon.

To start you'll need to create a button in the wysiwyg toolbar. You will need to switch over to the core database and browse to /sitecore/system/Settings/Html Editor Profiles. It's up to you where you end up creating the button but for now I'm just going to go into /sitecore/system/Settings/Html Editor Profiles/Rich Text Full/Toolbar 1 and create an "__Html Editor Button" and name it InsertText. Once you've got your button you're going to want to go to the "configure" tab at the top and change the icon to something recognizeable. Make sure that when you've selected the image it's 16x16 otherwise it won't look right in the wysiwyg. Next you'll want to add in a value for the "Click" field. This value will be used in javascript to call your function we're going to stay consistent and give it the value "InsertText". Now go back into the master database and open a Rich Text Editor field with the correct profile to view the button and ensure it's displaying.

Next thing to do is create the javascript method that will launch your modal window. In Visual Studio or whatever editor you're using open /sitecore/shell/controls/Rich Text Editor/RichText Commands.js. This file is included when the wysiwyg is launched and will need to contain your code or a reference to it to support your button. Add in the following code:

 

RadEditorCommandList["InsertText"] = function(commandName, editor, tool) {	var args = editor.GetDialogParameters(commandName);	var html = editor.GetSelectionHtml(); 	scEditor = editor;	editor.ShowDialog(		"/sitecore/shell/default.aspx?xmlcontrol=RichText.InsertText&la=" + scLanguage + "&selectedText=" + escape(html),		null, //argument		600, //width		500, //height		scInsertText,		null,		"Insert Text");};function scInsertText(returnValue) {	if (returnValue) {		scEditor.PasteHtml(returnValue.text);	}}

 

The first thing you'll notice is the "RadEditorCommandList["InsertText"] = function". This is where you're defining the function that is referenced in the "Click" field of your button in Sitecore. You'll also want to know that you have an editor object that will be giving you access to the selected text from the wysiwyg field. The most important thing you'll want to do in this function aside from gathering any data from the selected text (such as querystring params from a url) is to call your dialog window. In this case our dialog window will be leveraging Sitecore's sheer UI. If you haven't heard of this already, Sitecore has a tutorial here. It will help you understand the basics. The "editor.ShowDialog" function calls the /sitecore/shell/default.aspx file with the xmlcontrol RichText.InsertText and selected text from the wysiwyg as params. You also have the ability to set the height, width and callback method. Before we get to the xml control let's go over the callback method first. When you're leaving you're xml control you'll be calling another javascript method that will define your returnValue object. The returnValue object is passed to your callback method and you can then process the values. In this case I'm just interested in pasting the returned text back into the wysiwyg editor. In some cases you'll find that you're returning a value that is intended for the html view and needs to be converted to design view. You can use this callback function to handle converting your text before pasting it. A function to handle the conversion would look like this:

 

function scConvertText(html) {	try {		var win = window.dialogArguments[0];	}catch (e) {win = window;}	var form = win.scForm;	if (form != null) {		var request = new win.scRequest();		request.form = "html=" + encodeURIComponent(html);		request.build("", "", "", 'Convert("' + scMode + '")', true);		var url = "/sitecore/shell/Applications/Content Manager/Execute.aspx?cmd=Convert&mode=" + scMode;		request.url = url;		request.send();		var r = "";		if (request.httpRequest != null && request.httpRequest.status == "200") {			r = request.httpRequest.responseText;		}		return r;	} }

 

This is making a call to another utility that will do the conversion for you and return the text.

Now we need to create your dialog window. In the /sitecore/shell/controls/Rich Text Editor/ folder copy the existing InsertLink folder and name it InsertText. Inside there you'll see two files: InsertLink.js and InsertLink.xml. You'll want to rename both of them to InsertText.js and InsertText.xml respectively. Open the InsertText.js file and modify the "scClose" function to look like this:

 

function scClose(text) {	var returnValue = {		text:text	};	CloseDlg(returnValue);}

 

This is the function that you're xml control will call that defines you're return value which is passed to your callback function. Here we're just setting the text property on the returnValue. Now save and close that and open the InsertText.xml file. This is where we're using Sitecore's sheer UI. This xml file will be the designed layout of your dialog window. It will also reference a class file from a binary library that will be used to control the events. You can overwrite the existing content with this:

 

<control xmlns:def="Definition" xmlns="http://schemas.sitecore.net/Visual-Studio-Intellisense">	<richtext.inserttext>		<formdialog icon="Network/32x32/link.png" header="Insert Text" text="Insert the text you want." okbutton="Insert">			<script type="text/javascript" language="javascript" src="/sitecore/shell/RadControls/Editor/Scripts/7_2_0/RadWindow.js">.</script>			<script type="text/javascript" language="javascript" src="InsertCode/InsertText.js">.</script>			<codebeside type="Library.ClassName,BinaryFileName"></codebeside>			<gridpanel width="100%" height="100%" style="table-layout:fixed">      				<memo id="memoText" style="height:100%;width:100%;border-top:1px solid #919b9c"></memo>			</gridpanel>		</formdialog>	</richtext.inserttext></control>

 

You'll notice the "RichText.InsertText" tag wrapping most of the content. This will line up with the name of the xmlControl called in the javascript dialog function. Here we're just defining the class that handles the events, including some functional javascript, a submit button and a text (Memo) field to capture the text you want to enter.

Now that the UI of the dialog is setup you're going to have to create the class that is referenced in the UI. The class will look like this:

 

public class InsertTextForm : DialogForm { 	// Front End Object Reference  	protected Sitecore.Web.UI.HtmlControls.Memo memoText;  	protected override void OnLoad(EventArgs e) {		Assert.ArgumentNotNull(e, "e");		base.OnLoad(e);		if (!Context.ClientPage.IsEvent) {			this.Mode = WebUtil.GetQueryString("mo"); 			string text = WebUtil.GetQueryString("selectedText");			//set textbox text to selected text from the wysiwyg			memoText.Value = text;		}	}	protected override void OnOK(object sender, EventArgs args) {		Assert.ArgumentNotNull(sender, "sender");		Assert.ArgumentNotNull(args, "args");		//send it back		if (this.Mode == "webedit") {			SheerResponse.SetDialogValue(StringUtil.EscapeJavascriptString(memoText));			base.OnOK(sender, args);		} else {			//call javascript method in InsertText.js    			SheerResponse.Eval("scClose(" + StringUtil.EscapeJavascriptString(memoText) + ")");		}	}	// Local property accessor	protected string Mode {		get {			string str = StringUtil.GetString(base.ServerProperties["Mode"]);			if (!string.IsNullOrEmpty(str)) {				return str;			}			return "shell";		}		set {			Assert.ArgumentNotNull(value, "value");			base.ServerProperties["Mode"] = value;		}	} }

 

All right, now you're going to want to compile your class and restart your browser so that any javascript used isn't cached. Then open up you're wysiwyg editor and click the InsertText button. You'll see your dialog window popup and you can enter some text which, when you click the "Insert" button will insert the text back into the wysiwyg editor.

Now this example is pretty generic and by now you're asking yourself, "why would I ever do that?" In this particular instance I was demonstrating how I insert code blocks into my text. I use the Memo field to format the code and on the "OK" click I wrap it in a <pre> tag with a "Code" class. I realize that inserting text by means of another text field is entirely useless but as a demo it's as simple as you can get for something that requires so many moving parts. It should however show you how all the pieces are tied together and allow you customize the code as needed to insert information compiled by accessing the content tree and process selected text from the wysiwyg.

There will be times when your client/manager will want you to be able to insert some functional content inline on the body of the main text area. This will provide that capability. You could simply replace the Memo field in this instance with a treelist have a user select an item and then output a server control tag. If you're unfamiliary with a server control you can read on MSDN about it or go for a tutorial here first, but the basic premise is that you design a UI control like a Literal and display something yourself. The reason I would suggest that path is that the wysiwyg editor will display a server control like an object that you can select and change the attributes on. A very little known but powerful feature in Sitecore.


Sitecore Express

$
0
0

If you're a Sitecore developer and you love working on the platform but don't have the money to buy a personal license for your own site, you should check out Sitecore Express. Sitecore released a version of Sitecore 6.0 that directly marketed toward developers for non-commercial use. You'll need to login with your SDN credentials and you'll be mailed an express license. The license runs for a year but as long as you're still a registered developer you can continue to renew it. The system runs directly off the master database which for me is fine. It also mean you can't publish and you won't be using workflow but you still get the content management and template definition. It is a 6.0 version so there are a lot of new features you're not going to get access to but it's a great tool and it'll allow you to use all that high powered code you've been developing between projects.

Also if you're going to host it on shared hosting you will see some performance issues and limitations. My host allows multiple databases per site but if you're stuck in that unfortunate position then you'll likely need some guidance on setting up Sitecore on a single database.

Sitecore Layout Comparer Extension Classes

$
0
0

So during a project where I was converting a site to new templates for a rebuild, I was automating the creation of the new items and needed to know if the current page had any layout overrides on the item. It was necessary for me to compare each page's layout definition against the layout definition assigned to its standard value. If you've never ventured into the standard fields let alone the raw values of an item you might find this useful. The layout fields are stored as xml and keep references to all devices, sublayouts and layouts assigned to it. I ended up writing a few classes that handles the comparisons. It's not something that's entirely common but occassionally you need to know how to deal with the layouts programmatically. Hopefully someone else will find it useful too.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Sitecore.Layouts;
using Sitecore.Data.Items;
using Sitecore.Data;

namespace YourSite.Extensions
{
	public static class LayoutDefinitionExtensions
	{
		/// <summary>
		/// Is Equal To - this will determine if a layout definition is equal to another layout definition
		/// </summary>
		public static bool IsEqualTo(this LayoutDefinition layout1, LayoutDefinition layout2) {

			//get device lists
			ArrayList dd1 = layout1.Devices;

			//compare the device count
			if (dd1.Count.Equals(layout2.Devices.Count)) {
				foreach (DeviceDefinition d1 in dd1) {
					DeviceDefinition d2 = layout2.GetDevice(d1.ID);
					if (d2 != null) {
						if (!d1.IsEqualTo(d2)) {
							return false;
						}
					} else {
						return false;
					}
				}
			} else {
				return false;
			}
			return true;
		}
	}

	public static class DeviceDefinitionExtensions
	{
		/// <summary>
		/// Is Equal To - this will determine if a device definition is equal to another device definition
		/// </summary>
		public static bool IsEqualTo(this DeviceDefinition device1, DeviceDefinition device2) {

			//compare the layout on the device
			if (!device1.Layout.Equals(device2.Layout)) {
				return false;
			}
			ArrayList rd1 = device1.Renderings;

			//compare the rendering count
			if (rd1.Count.Equals(device2.Renderings.Count)) {
				foreach (RenderingDefinition r1 in rd1) {
					RenderingDefinition r2 = device2.GetRendering(r1.ItemID);
					if (r2 != null) {
						if (!r1.IsEqualTo(r2)) {
							return false;
						}
					} else {
						return false;
					}
				}
			} else {
				return false;
			}
			return true;
		}

		/// <summary>
		/// Inner Item - this will return an item reference of the device on a device definition
		/// </summary>
		public static Item InnerItem(this DeviceDefinition device) {
			return InnerItem(device, Sitecore.Context.Database);
		}

		public static Item InnerItem(this DeviceDefinition device, Database db) {
			return db.Items[device.ID];
		}

		/// <summary>
		/// Layout - this will return an item reference of the layout on a device definition
		/// </summary>
		public static Item Layout(this DeviceDefinition device) {
			return Layout(device, Sitecore.Context.Database);
		}

		public static Item Layout(this DeviceDefinition device, Database db) {
			return db.Items[device.Layout];
		}

		/// <summary>
		/// Sublayouts - this will return a list of rendering definitions on a device definition
		/// </summary>
		public static List<RenderingDefinition> Sublayouts(this DeviceDefinition device) {
			return Sublayouts(device, Sitecore.Context.Database);
		}

		public static List<RenderingDefinition> Sublayouts(this DeviceDefinition device, Database db) {
			List<RenderingDefinition> rdl = new List<RenderingDefinition>();
			ArrayList al = device.Renderings;
			foreach (RenderingDefinition rd in al) {
				Item rend = db.Items[rd.ItemID];
				if (rend.IsNotNull() && rend.TemplateName.Equals("Sublayout")) {
					rdl.Add(rd);
				}
			}
			return rdl;
		}

		/// <summary>
		/// XSL Renderings - this will return a list of Rendering Definitions on a Device Definition
		/// </summary>
		public static List<RenderingDefinition> XslRenderings(this DeviceDefinition device) {
			return XslRenderings(device, Sitecore.Context.Database);
		}

		public static List<RenderingDefinition> XslRenderings(this DeviceDefinition device, Database db) {
			List<RenderingDefinition> rdl = new List<RenderingDefinition>();
			ArrayList al = device.Renderings;
			foreach (RenderingDefinition rd in al) {
				Item rend = db.Items[rd.ItemID];
				if (rend.IsNotNull() && rend.TemplateName.Equals("Xsl Rendering")) {
					rdl.Add(rd);
				}
			}
			return rdl;
		}
	}

	public static class RenderingDefinitionExtensions
	{
		/// <summary>
		/// Is Equal To - this will determine if a Rendering Definition is equal to another Rendering Definition by comparing all placeholders
		/// </summary>
		public static bool IsEqualTo(this RenderingDefinition r1, RenderingDefinition r2) {

			//compare item id
			if (!r1.ItemID.Equals(r2.ItemID)) {
				return false;
			}
			//compare the placeholder
			if (!r1.Placeholder.Equals(r2.Placeholder)) {
				return false;
			}

			return true;
		}

		public static Item InnerItem(this RenderingDefinition r) {
			return InnerItem(r, Sitecore.Context.Database);
		}

		public static Item InnerItem(this RenderingDefinition r, Database db) {
			return db.GetItem(r.ItemID);
		}
	}

}

Here are also some Item extensions that work with the layouts/sublayouts

public static class ItemExtensions {
	#region LAYOUTS SUBLAYOUTS TEMPLATES

	/// <summary>
	/// Will return true if the item contains a sublayout path you've provided
	/// </summary>
	/// <param name="thisItem" />
	/// The item whose sublayouts are to be checked
	///
	/// <param name="sublayoutPath" />
	/// The path after "/sitecore/layout/Sublayouts/"
	///
	/// <returns></returns>
	public static bool HasSublayout(this Item thisItem, String sublayoutPath) {

		Sitecore.Data.Database db = Sitecore.Context.Database;

		// get the Renderings field of the item, which
		// corresponds to the Layout field in the Content Editor
		string rend = thisItem.Fields["__renderings"].Value;

		// Use the LayoutDefinition to parse the field value
		Sitecore.Layouts.LayoutDefinition layout = new LayoutDefinition();

		layout = LayoutDefinition.Parse(rend);

		// Get the current device from the Context
		Sitecore.Data.Items.DeviceItem dev = Sitecore.Context.Device;

		// Get the device definition for the current device from the
		// Test Item’s Layout field
		Sitecore.Layouts.DeviceDefinition device = layout.GetDevice(dev.ID.ToString());

		// we are looking for references to the Document rendering
		// so retrieve that rendering from the master database
		Item docRendering = db.Items["/sitecore/layout/Sublayouts/" + sublayoutPath];

		// Look for the document rendering in the definition of
		// the current device in the Test Item’s layout field
		Sitecore.Layouts.RenderingDefinition rendering = device.GetRendering(docRendering.ID.ToString());

		// If the rendering is not null, then it is referenced
		// on the current device in the layout field for the
		// Test Item
		return (rendering != null) ? true : false;
	}

	/// <summary>
	/// This will return a Device Definition for the item you've provided or null if none is found
	/// </summary>
	public static DeviceDefinition DefaultDevice(this Item thisItem) {
		return DefaultDevice(thisItem, Sitecore.Context.Database);
	}

	public static DeviceDefinition DefaultDevice(this Item thisItem, Database db) {
		List<devicedefinition> al = thisItem.Devices();
		foreach (DeviceDefinition dd in al) {
			Item device = db.GetItem(dd.ID);
			if (((CheckboxField)device.Fields["Default"]).Checked) {
				return dd;
			}
		}
		return null;
	}

	/// <summary>
	/// This will return a list of Device Definitions on the provided item
	/// </summary>
	public static List<devicedefinition> Devices(this Item thisItem) {

		List<devicedefinition> list = new List<devicedefinition>();

		Sitecore.Layouts.LayoutDefinition layout = LayoutDefinition.Parse(thisItem["__Renderings"]);
		ArrayList al = layout.Devices;
		foreach (DeviceDefinition dd in al) {
			list.Add(dd);
		}

		return list;
	}

	/// <summary>
	/// Check to see if an item contain a layout and returns true if there is a match
	/// </summary>
	public static bool HasLayout(this Item thisItem) {

		return (!thisItem["__Renderings"].Equals("") && thisItem.Devices().Count > 0) ? true : false;
	}

	/// <summary>
	/// Return true if the placeholder key is set on a sublyout or xsl rendering applied to the presentation settings of this item
	/// </summary>
	/// <param name="thisItem" />
	/// The item being queried
	///
	/// <param name="PlaceholderKey" />
	/// The key of the sitecore placeholder to search for
	///
	/// <returns></returns>
	public static bool HasPlaceholderApplied(this Sitecore.Data.Items.Item thisItem, string PlaceholderKey) {

		string rend = thisItem.Fields["__renderings"].Value;
		Sitecore.Layouts.LayoutDefinition layout = LayoutDefinition.Parse(rend);
		Sitecore.Data.Items.DeviceItem dev = Sitecore.Context.Device;
		Sitecore.Layouts.DeviceDefinition device = layout.GetDevice(dev.ID.ToString());
		foreach (Sitecore.Layouts.RenderingDefinition rd in device.Renderings) {
			if (rd.Placeholder.Equals(PlaceholderKey)) {
				return true;
			}
		}
			return false;
	}

	/// <summary>
	/// this will return a list of placeholder key names that are applied to a device definition on an item. if you specify a placeholder this will return those whose key matches. if you provide ignoreSublayout it will return all but that key.
	/// </summary>
	public static List<string> Placeholders(this Item item, string placeholder, string ignoreSublayout) {
		List<string> s = new List<string>();

		Sitecore.Data.Items.DeviceItem dev = Sitecore.Context.Device;
		string rend = item.Fields["__renderings"].Value;
		Sitecore.Layouts.LayoutDefinition layout = LayoutDefinition.Parse(rend);
		Sitecore.Layouts.DeviceDefinition device = layout.GetDevice(dev.ID.ToString());
		foreach (Sitecore.Layouts.RenderingDefinition rd in device.Renderings) {
			bool addIt = true;

			if (!ignoreSublayout.Equals("")) {
				Item i = Sitecore.Context.Database.Items[rd.ItemID];
				if (i.DisplayName.Equals(ignoreSublayout)) {
					addIt = false;
				}
			}
			if (!placeholder.Equals("") && !rd.Placeholder.Equals(placeholder)) {
				addIt = false;
			}

			if (addIt) {
				s.Add(rd.Placeholder);
			}
		}
		return s;
	}

	/// <summary>
	/// Checks to see if the item has the base template specified
	/// </summary>
	/// <param name="thisItem" />
	/// The item whose base templates will be checked
	///
	/// <param name="BaseTemplateName" />
	/// The base template name to check for
	///
	/// <returns></returns>
	public static bool HasBaseTemplate(this Sitecore.Data.Items.Item thisItem, string BaseTemplateName) {

		TemplateItem[] bases = thisItem.Template.BaseTemplates;
		bool found = false;
		foreach (TemplateItem temp in bases) {
			found = (temp.DisplayName.Equals(BaseTemplateName)) ? true : false;
		}

		return found;
	}

	#endregion
}

Editor Tabs in Sitecore

$
0
0

updated on 8/11/2011 regarding the order of the tabs. specifically to the position of the content tab .

So during the course of developing websites for Sitecore there are a lot of opportunities to create custom functionality. One feature that I like to use is a custom editor tab. If you're unfamiliar with the concept then you've seen them and just didn't notice. If you have a media folder selected you'll see options to "upload" or "upload advanced".

media tab

This is a custom editor tab. Sitecore provides this one out of the box along with many others but occasionally you'll find it advantageous to create your own.

To be clear there are two roads that you can go down with this kind of thing: the Sheer UI road and the aspx road. If you've never done either you may want to start with the aspx road. I say this because you're most likely a typical .NET developer who is accustomed to writing aspx page. To develop a Sheer UI page you'll have to read up on the language specifics that sitecore provides and how to wire it up. You can start in Sitecore's Developer Network (SDN), but you may need to login to view it. One distinct advantage of the Sheer UI approach is that you will be able to tie into the global editor events such as when a user clicks the save button. This can be important because it's more natural behavior for a user to expect to do that. If you have a custom form that will manipulate the selected items data you will notice that the other tabs do not refresh with the new information until you re-select the item in the content tree. This can be counter intuitive for a user and it's best if you keep it simple. When it's time to make your decision keep in mind that if all you're really looking to do is create a custom display of information then an aspx will do the job just fine otherwise if you need some real tie-ins to the event chain you're going to want to use the Sheer UI route. Also to note that an aspx page can be browsed to and if you do have a form on the page and don't handle the security aspect you may leave yourself open to problems. 

Alright back to the tabs. In my example I'm going to be taking the lesser of two roads which is to create an aspx page. I'm going to just display the values that are provided so that you know how to tie into it for whatever use you may have.

1. In Visual Studio you're going to want to create an aspx page that you'll be using in your project. In this case I'll be creating it at /sitecore modules/web/tabs/newtab.aspx. You can really put it anywhere but keep in mind that as you continue to add these pages you'll want to have some organized location for these things to go. Here's the code for the front end of my aspx file.

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="newTab.aspx.cs" Inherits="sitecore_modules.Web.Tabs.newTab" %><html xmlns="http://www.w3.org/1999/xhtml"></html>    <head runat="server"></head>        <form id="form1" runat="server">            <div style="padding:30px;">		            <asp:literal runat="server" id="ltlOut"></asp:literal>            </div>        </form></html>

Then here's the code behind:

using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.UI;using System.Web.UI.WebControls;namespace sitecore_modules.Web.Tabs {	public partial class newTab : System.Web.UI.Page {		protected void Page_Load(object sender, EventArgs e) {			//available querystring values for context info (id, language, version, database)			ltlOut.Text = "id: " + Request.QueryString["id"] + "<br/>";			ltlOut.Text += "language: " + Request.QueryString["language"] + "<br/>";			ltlOut.Text += "version: " + Request.QueryString["version"] + "<br/>";			ltlOut.Text += "database: " + Request.QueryString["database"] + "<br/>";		}	}}

2. Now you're going to need to log into Sitecore and change the database you're viewing to Core

3. Open the content editor and browse to /sitecore/content/applications/content editor/editors.

core tree

This is where you create the editor item. Once this is created and the fields are set you will be able to apply it to as many template types as you want.

4. Right-Click and add an item. You'll have to Insert from template. The path to the editor template is /templates/sitecore client/content editor/editor

new tab

5. Now you'll need to setup the fields. You'll need to choose a name, icon and the file path to the aspx or xml control (Sheer UI) you'll be using.

tab fields

6. Now hit save on the item editor and switch the Sitecore database back to master. Browse to your template that you want to set the tab on and if you don't already have a standard values item then create one and select it. You'll want to apply it to the standard values item not the template. Once the standard values item is selected choose the "configure" tab at the top of Sitecore's ribbon and click on the editors button.

add editors

7. Browse down the tree and select the editor from core that you just created.

add editor dialog

8. You've wired up all the relevant parts and now it's time to view what you've got. Browse into your content tree and select an item of the template type you added the editor tab to. You should see something like this:

 tab view

That's it. Not too painful really. So to recap you're going to have to make a choice about what level of functionality you're going to need. If you're just displaying content in a nice way for your editors then you'll be fine with the aspx approach but if you're looking for a complex solution you'll really want to read up on Sitecore's Sheer UI since this will really open up your options. Please let me know what if anything you end up using this for I'd be interested to know.

As a post script there were a few good questions about the order of the editor tabs and whether or not you could put the "Content" tab first. Cedric also mentions in the comments that there is a Sitecore forum post about it. To start, I found where the editor tabs are created by Sitecore. They're in the Sitecore.Client dll in the Sitecore.Shell.Applications.ContentManager.Editor class in the GetEditorTabs method. I've attached a screenshot so you can see what it's doing in that method.

EditorTabs.jpg

First it creates a list to hold the tabs. Next it gets the "Custom" editor tabs and then it gets the "Content" and "Dynamic" tabs last. If you're looking to be able to set the content tab first you're going to ahve to rewrite this class and figure out what's calling it. It's deep in the belly of the beast. Chait Suwannarat was nice enough to pass on the response from Sitecore's support site regarding a possible patch for the class.

Please perform the following steps:

1. Copy the attached Sitecore.Support.339051.dll file into the /bin folder.
2. Replace the following line in the /sitecore/shell/Applications/Content Manager/Default.aspx file:

<sc:CodeBeside runat="server" Type="Sitecore.Shell.Applications.ContentManager.ContentEditorForm,Sitecore.Client"/>

with the following one:

<sc:CodeBeside runat="server" Type="Sitecore.Support.Shell.Applications.ContentManager.CustomizedContentEditorForm,Sitecore.Support.339051"/>

I've looked over the support dll and noticed that it includes three classes including the change noted above for the Editor class. This means there are other dependancies for this class that if you're going to override this you'll need to look into. I'd strongly urge you NOT TO DO THIS unless you really know what you're doing and are willing to take those risks. I'd also hold off since Sitecore will likely release it as a patch in an upcoming release.

Sitecore Util Classes

$
0
0

If you've ever done a lot of Sitecore development you may have built a lot of tools only to find out that Sitecore itself has a utility that already does what you wanted. After a friend, Mark Ursino, joked about how ridiculous the Sitecore.MainUtil.True() method was I looked around and remarked how many times I bumped into a new found util class. I then asked myself just how many util classes Sitecore had so I popped open the object browser and did a quick search. The number: a staggering 31 utility classes. That's a ton of good code that you don't have to write. Some real nuggets too like the reflector utility. Here's what I found with some method highlights:

Sitecore.Data.Items.ItemUtil
AddFromTemplate(string itemName, string templateName, Item parent)
IsNull(ID id)
IsItemNameValid(string name)
ProposeValidItemName(string name, string defaultValue)

Sitecore.Data.Items.WebDAVItemUtil
IsMediaItem(Item item, string dataFieldName)
IsVersioned(Item item)

Sitecore.Data.Query.QueryUtil(I believe this is an XPath Query Util since it's trying to locate the "[" and "::" characters)
CanResolve(object obj, DataUri uri)
IsQueryPath(string path)

Sitecore.Data.Serialization.CommonUtils
IsDirectoryHidden(string path)
Uniq<t>(List<T> workset, IComparer<T> comparer) (gets the unique objects in a list)

Sitecore.Data.Serialization.ObjectModel.SerializationUtils
IsItemSerialization(string filePath)

Sitecore.Data.Serialization.PathUtils
CreateWatcher()
GetDatabaseRoot(string databaseName)
MakeItemPath(string path, string root)

Sitecore.Data.Sql.SqlUtil
ExecCommand(string sql, object[] parameters, string connection, Timespan timeout)
ExecuteScalar(SqlCommand cmd)
GetFullTextSearchSql(string search, string table, string displayFields, string searchFields)
ObfuscatePassword(string connection)

Sitecore.DateUtil
IsoDateToDateTime(string isoDate, DateTime defaultValue)
ToIsoDate(DateTime datetime, bool includeTicks)

Sitecore.Install.Files.FileKeyUtils (internal)

Sitecore.Install.Files.PathUtils
DemangleDirectoryPaths(string path)
MangleDirectoryPath(string path)
ReducePath(string root, string fullname)

Sitecore.Install.Items.ItemKeyUtils
GetKey(Item item)
Resolve(string key)

Sitecore.Install.PackageUtils
CleanupFileName(string fileName)
GetItemStream(Item item)
TryGetValue<K, V>(IDictionary<K, V> dictionary, K key)

Sitecore.Install.Serialization.IOUtils
LoadSolution(string stream)
StoreSolution(PackageProject solution)

Sitecore.Install.StreamUtil
LoadStream(Stream stream)
Copy(Stream from, Steam to, int bufferSize)

Sitecore.IO.FileUtil
AppendToFile(string filename, string text)
BackupFile(string filename)
IsAspxFile(string filename)
MatchPattern(string value, string pattern)
ReadBinaryFile(string filename)
ReadUTF8File(string filename)

Sitecore.MainUtil
AppendMissingIDBrace(string sPath)
ColorToString(Color color)
ConvertToHashTable(ArrayList list)
EncodeName(string name)
GetDotNetVersion()
GetSingleton<T>(ref T instance, Func<T> createInstance) where T: class
HexToColor(string value)
IsXPath(string path)
SendMail(MailMessage message)
SplitPath(string path, bool expand)
TerminateAspNet(string message)
True() (yeah i can't really believe it, False() either)

Sitecore.Reflection.ReflectionUtil
CallMethod(Type type, object obj, string methodsName,bool includeNonPublic, bool includeInherited, bool includeStatic, object[] parameters)
CreateObjectFromConfig(string configPath, string typeAttribute, object[] parameters)
LoadAssembly(string name)

Sitecore.Reflection.TypeUtil
SizeOfGuid()
SizeOfString(string value)

Sitecore.Security.SecurityUtil
GetUserDigestCredentials(string username, bool withoutDomain)

Sitecore.Security.SecurityUtility
GetAccount(string domainName)
GetAccountNameAndType(string domainName, out string accountName, out AccountType accountType)
GetDomainName()
IsRole(string accountName)
IsUser(string accountName)

Sitecore.Security.Serialization.SecuritySerializationUtils
CleanRoles(List<string> loadedRoles, string domainName)
CleanUsers(List<string loadedUsers, string domainName)

Sitecore.Shell.Applications.WebEdit.WebEditUtil
GetCurrentDate()
SetCurrentDate(DateTime value)

Sitecore.StringUtil
Capitalize(string text)
Combine(object part1, object part2, string divider)
EscapeJavascriptString(string text, bool surroundWithQuotes)
GetNameValues(string[] list)
Repeat(string s, int nCount)

Sitecore.Syndication.FeedUtil
GetFeedDevice()
GetFeedRendering(Itemitem)
IsConfiguredForFeed(Item item)

Sitecore.UIUtil
AddContentDatabaseParameter(UrlBuilder url)
BrowserHeightToDialogHeight(string height)
FindControlByType(Type type)
GetBrowserClassString()
GetItemFromQueryString(Database database)
IsFirefox()
IsIE()
SupportsInlineEditing
UseFlashUpload()

Sitecore.Web.HtmlUtil
CreateAnchor(string text, string href)
FindStartTag(string sTagName, string sText)
GetCheckBox(bool isChecked)
GetRootControl(Control control)
ParseTagAttributes(string tag)
RenderControl(Control ctl)

Sitecore.Web.UI.Grids.GridUtil
GetSelectedValue(string gridID)

Sitecore.Web.WebUtil
AddPageControl(Page page, Control control)
BuildQueryString(SafeDictionary<string> parameters, bool xhtml)
GetCookieDateTime(string key, DateTime defaultValue)
GetHostName()
GetHostIPAddress()
GetIISName()
IsOnAspxPage()
RedirectToErrorPage(string text)

Sitecore.WebDAVUtil
BasedOnFolderTemplate(Item item)
GetBrowseRootItem(Item item)
GetEditFileScript()
GetEditMediaScript(MediaItem item, ID optionsID)
GetWebDAVViewUrl(ID optionID)

Sitecore.WordOCX.WordOcxUtils
GetItemLink(Item item)

Sitecore.Xml.XmlUtil
AddAttribute(string name, string value, XmlNode node)
FindChildWithAttribute(string attribute, string value, XPathNavigator nav)
GetAttribute(int index, XmlNode node)
ReplaceWithChildren(XmlNode node)
TransferAttributes(XmlNode source, XmlNode target)

So there's what I got. It's certainly not an exhaustive list considering there are a whole lot more methods where those came from, but it'll give you a taste of what's out there. Let me know what you find.

Clean your XPath

$
0
0

While developing with Sitecore you'll probably end up using XPath query to return some results. Since a lot of people will be requiring you to create content items with dashes in the name, for SEO purposes, you're going to need to escape the dashes with "#" (pound) signs as detailed in this SDN Sitecore Query Syntax article. This means you'll need a method to clean your XPath queries. Having done this a few times and integrated mine with other developers versions, here's what I've come up with:

public static class StringExtensions {	public static string CleanXPath(this string s) {		string scQuery = s;		//loop through each match and replace it in the query with the escaped pattern		char[] splitArr = { '/' };		string[] strArr = scQuery.Split(splitArr);		//search for {6E729CE5-558A-4851-AA30-4BB019E5F9DB}		string re1 = ".*?";	// Non-greedy match on filler		string re2 = "([A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12})";	// GUID		Regex r = new Regex(re1 + re2, RegexOptions.IgnoreCase | RegexOptions.Singleline);		for (int z = 0; z <= strArr.Length - 1; z++) {			Match m = r.Match(strArr[z]);			//if it contains a dash and it's not a guid			if (strArr[z].Contains("-") && !m.Success) {				strArr[z] = "#" + strArr[z] + "#";			}		}		scQuery = string.Join("/", strArr);		return scQuery;	}}

Now let's wrap that all up into a single method for you to call using the Linq ToList extension.

public static class StringExtensions {	public static List<Item> RunXPath(this string s, Sitecore.Data.Database db) {		return db.SelectItems(s.CleanXPath()).ToList();	}}

And voila, you've got yourself a couple handy little extension methods that let you get to the work of Sitecore without the hassle.

Viewing all 178 articles
Browse latest View live