Friday, December 29, 2006

Cannot create new groups in the Forum module


The solution is simple. Check if you modified the IgnoreUrlPrefixes setting according to the SDN5 instructions:

Sitecore 5.1 instructions:

|/sitecore modules/forum/web/|/users/avatar.aspx|/rss.aspx

Sitecore 5.2 instructions:

|/sitecore modules/forum/web/|/users/avatar.aspx|/rss.aspx|/WebResource.axd The thing is that the GroupRepeater control relies on the ItemCommand event which is not simply fired unless you add your page to the IgnoreUrlPrefixes list. This is applied to 5.1.1.x. Not sure that same problems exist in 5.3 though.

Tuesday, December 12, 2006

Using the XSL helper class from your code


Here is an easy way to output an image tag from an image field with possibility to use some new enhancements provided in 5.3.

Sitecore.Xml.Xsl.XslHelper xslhelper = new Sitecore.Xml.Xsl.XslHelper();

string output = xslhelper.image("ImageField", Factory.CreateItemNavigator(Sitecore.Context.Item).Select("."));

Response.Write(output);

Understanding the Sort Order


1) In Sitecore 5.3 this code returns the home children in the same way as in Shell UI:

Item root = db.Items["/sitecore/content/home"];

ChildList list = root.GetChildren(ChildListOptions.IgnoreSecurity);

foreach (Item itm in list) { Response.Write(itm.Name); }

2) Sitecore sorts children according to their SortOrder field firstly.

3) If items do not have the SortOrder field filled or the values are equal Sitecore sorts it according to “Subitems Sorting” field value of the parent item.

4) You can apply your own sorting by writing your own IComparer. Please add your own
/sitecore/system/Settings/Subitems Sorting/* comparer with such code structure:

public class CreatedComparer : Comparer

{

protected override int DoCompare(Item item1, Item item2)

{

// return item1.Statistics.Created.CompareTo

// (item2.Statistics.Created);

return {0 or 1 or -1};

}

}

5) Note: you can apply this to all the items based on a certain template by setting it via “Standard Values”.

Uploading media items from the front end


Why not? What if you want the extranet users to upload media to the master database? No problem with that! Just follow these easy steps to accomplish this.

Step 1: add a new pipeline to the <processors> section in the web.config:

<frontendUpload>

<processor mode="on" type="Sitecore.Pipelines.Upload.Save, Sitecore.Kernel" />

</frontendUpload>

Step 2: Add the FileUpload control to your layout (web form):

<asp:FileUpload ID="FileUpload1" runat="server" />

This control will be used to select a image for uploading.

Step3: in the code behind of a layout you should add the following code for example in the button click handler.

// defining if a file is being uploaded

if (FileUpload1.PostedFile != null)

{

// disabling security in order to create a media item

using (new Sitecore.SecurityModel.SecurityDisabler())

{

// creating necessary arguments to be passed to the processor

UploadArgs args = new UploadArgs();

// adding http files collection

args.Files = base.Request.Files;

// a media path where the media item will be created

args.Folder = "/sitecore/media library/Files";

// we may want to override existing media items

args.Overwrite = true;

// we do not need to choose this option since we are uploading images, not archives

args.Unpack = false;

// turning on versioning for the uploaded item

args.Versioned = true;

// selecting a language in which the media item will be created

args.Language = Sitecore.Globalization.Language.Parse("en");

// we are uploading to the database

args.Destination = UploadDestination.Database;

// if we are uploading to the master database, we need to change the active site

Sitecore.Context.SetActiveSite("shell");

// starting the pipeline previously added to web.config

PipelineFactory.GetPipeline("frontendUpload").Start(args);

// the media item is created. Now we can do whatever we want with the uploaded items

// for exampe, programmatically populate the Alt fields

foreach (Item itm in args.UploadedItems)

{

itm.Editing.BeginEdit();

itm.Fields["Alt"].Value = "Set from API";

itm.Editing.EndEdit();

}

// setting the active site back

Sitecore.Context.SetActiveSite("website");

}

}

else

{

Response.Write("No file is posted.");

}

Thursday, November 30, 2006

Workflow with unpublishing


Nothing difficult, but I thought this might be interesting anyway.

So we need to implement a workflow which provides us with a possibility to schedule item deletion and finally unpublish it from the front end upon confirmation.

The key point is additional workflow state named "Deletion Pending" which is referred by the "Schedule for Deletion" command.It has two commands: "Confirm Deletion" and "Reject Deletion". The first command unpublishes item and the second returns it to the "Published" state.

The "Process" method that is invoked by the "Unpublish" workflow action looks like this:

public void Process(WorkflowPipelineArgs args) { Error.AssertObject(args.DataItem, "args.DataItem"); Item item = args.DataItem;

Database web = Factory.GetDatabase("web"); Database master = Factory.GetDatabase("master");

using (new Sitecore.SecurityModel.SecurityDisabler()) { DeleteItem(web, item); DeleteItem(master, item);

// RemoveLinks(item); LinkManager.UpdateLink(item); }

Sitecore.Context.ClientPage.SendMessage(this, String.Format("item:refresh(id={0})", item.ID)); }

private void DeleteItem(Database db, Item item) { if (db.Items[item.ID] != null) { db.Items[item.ID].Delete(); Log.Info(String.Format("Item {0} was deleted from the {1} database using the workflow command.", item.Paths.FullPath, db), this); } else { Log.Info(String.Format("Deletion for the item {0} failed for the {1} database. Item not found.", item.Paths.FullPath, db), this); } }

I've found two cons so far:

  1. multiple final states: "Published" and "Unpublished"
  2. the solution is not a substitute of the "Delete" which may be misleading.

Good luck.

Friday, August 18, 2006

5.3: Enabling Edit chunk in Content Editor


You may not see the Edit chunk/button while being logged in as a newly created user. Complete these steps to make it visible: 1. Switch to the core database; 2. Open the Security Editor; 3. Locate this item /sitecore/system/ribbons/chunks/workflow edit/edit; 4. Toggle the inherit checkbox as shown below:

Speeding up File Explorer


On installations with a huge amount of media in the upload folder, you may experience serious delay when opening the File Explorer. If you feel that the upload folder’s existence doesn’t make much sense in File Explorer, you can make this folder invisible; this should speed up the performance. Here are the steps: 1. Locate the File Explorer’s form under \sitecore\shell\Applications\Files\File explorer\ and copy it to \sitecore\shell\Override. 2. In the newly copied file make the following changes: <DataContext ID="FileExplorerDataContext" DataContext="FilteredFileExplorerDataContext" DefaultItem="/" ShowRoot="true" Filter="Not(@@name='upload')"/> <DataContext ID="FilteredFileExplorerDataContext" DataContext="FileExplorerDataContext" DefaultItem="/" Filter="Not(@@name='upload')"/> As can be seen, I assigned a special folder name filter for the DataContext objects. Now the contents of the upload folder should not be loaded to File Explorer.

Sitecore taskbar at the top?


For some inexplicable reason I thought that it is not possible :-) My doubts were destroyed by Jacob. Just move the <startbar> tag above the <border> tag in the /sitecore/shell/Applications/shell.xml file <GridPanel Class="scFill scFixed"> <Startbar GridPanel.Class="scStartbar"/> <Border ID="Desktop" GridPanel.Class="scFill" ContextMenu="ShowContextMenu"> <Border ID="Links" DblClick="Launch"/> </Border> </GridPanel> Result: It turned out that nothing is impossible in Sitecore.

Tuesday, August 08, 2006

Sending data to event handler


You can pass any string/list of strings to your event handler by completing these two steps: 1. Web.config definition: <handler type="Custom.TestEvent, PublishEvents" method="OnPublishBegin"> <mystring>teststring</mystring> </handler> 2. In the code you should define a field named mystring and a property with get and set accessors: private string mystring; public string MyString { set { this.mystring = value; } get { return this.mystring; } }

Accessing the App_Code folder


Since all the classes under the App_Code folder are compiled to an assembly with random name placed under the Temporary ASP.NET Files folder, you are not able to use these classes from an assembly compiled to the bin folder. Let’s assume that we need to create a workflow action class that will communicate with the classes from the App_Code folder. The solution here is to create a workflow action code that will iterate through all the assemblies loaded to the current application domain and find the instance of the necessary class from the App_Code folder and call the Process method. public class CustomAction { static Hashtable table = new Hashtable(); static Type LookupType(string name) { Type result = null; lock (table) { result = table[name] as Type; } if (result == null) { foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { result = assembly.GetType(name); if (result != null) { lock (table) { table[name] = result; } break; } } } return result; } public void Process(WorkflowPipelineArgs args) { ProcessorItem processor = args.ProcessorItem; object instance = ReflectionUtil.CreateObject(LookupType(processor.InnerItem.Fields["dynamictype"].Value), new object[0]); ReflectionUtil.CallMethod(instance, "Process", new object[] { args }); } } As can be seen from the code above, the name of the type that we search for is retrieved from the Action item (dynamictype field): The Type string field contains the reference to the action class compiled to the bin folder. In order to add the DynamicType field to the Action template, you should create a new template e.g. named Dynamic Type by inheriting from the existing Action template and add this new field. You may also want to create a master from this template.
Update: Thanks to Ole, starting with 5.3 BETA 060825 we have made reflection work with App_Code. Therefore, custom processors in App_Code simply work out of the box. In the processor definition, simply leave out the assembly part of the type string:

Wednesday, July 12, 2006

Changing a workflow state from API


Changing a workflow state from API Version: Sitecore 5.1.1.18/5.2.0.9 Include these using directives: using Sitecore; using Sitecore.Data; using Sitecore.Data.Items; using Sitecore.Workflows; using Sitecore.Configuration; using Sitecore.SecurityModel; Here is the code snippet with appropriate comments: // getting the master database Database master = Factory.GetDatabase("master"); // getting a sample item Item itm = master.Items["/sitecore/content/home/news"]; // getting the item's workflow reference IWorkflow wf = master.WorkflowProvider.GetWorkflow(itm); // here we need either to login as a user with appropriate permissions // to have access to workflow states/commands or disable security using(new SecurityDisabler()) { // executing the workflow command by calling the Execute method. // it receives the ID of the command being invoked, // the item that is in the workflow. // Note that the command ID that we passed as the first parameter, // should be a valid ID of any workflow command under the current workflow state // that the item belongs to. WorkflowResult result = wf.Execute("{0FB92663-F44B-4E24-9CF2-B6D6CE786505}", itm, "my comments", false); // Retrieving the result of the workflow command execution Response.Write("Succeeded: " + result.Succeeded + " Message: " + result.Message); } After the code above is succeeded, all actions that are tied to the new workflow state will be executed.

Wednesday, July 05, 2006

HTML Editor's context menu items


In order to disable some items in the Context Menu of the HTML Editor, you should modify the file \sitecore\shell\Editor\Configuration\AutoConfigure\Full.config Please have a look at the contextmenu section. <contextmenu> <item name="Editing" value="False" /> <item name="Format" value="True" /> <item name="Insert" value="True" /> … If you want to disable an item, you should set appropriate value to False.

Thursday, June 29, 2006

Extending the Preview flyout panel


The steps below show how to extend the preview flyout panel by adding a space to display the current workflow state of the context item. 1. Locate the Portal item under content/Applications/Preview item in the core database. 2. Add an item from the Portlet template:
3. Fill the necessary fields such as it is shown above.
4. Create a folder under sitecore\shell\Applications\Preview\Portlets\ 5. Create an XML file with the following source code:

<?xml version="1.0" encoding="utf-8" ?>

<control xmlns:def="Definition" xmlns="http://schemas.sitecore.net/Visual-Studio-Intellisense">

<PreviewCurrentWorkflowPortlet

def:inherits="Custom.Portlets.PreviewCurrentWorkflowPortletXmlControl,CurrentWorkflowPortlet">

<Border def:ID="Portlet">

<DefaultPortletWindow def:ID="Window" Header="Workflow Details" Icon="Network/16x16/inbox.png">

<Border def:ID="Body"/>

<Literal def:ID="Workflow" Text="Workflow: " />

<br />

<Literal def:ID="WorkflowState" Text="Workflow State" />

</DefaultPortletWindow>

</Border>

</PreviewCurrentWorkflowPortlet>

</control>

6. Create a class that will stand for this XML control. Compile it and place into the bin folder. using System; using Sitecore; using Sitecore.Data.Items; using Sitecore.Web.UI.XmlControls; using Sitecore.Web.UI.HtmlControls; namespace Custom.Portlets { public class PreviewCurrentWorkflowPortletXmlControl : XmlControl { protected Border Body; protected Border Portlet; protected XmlControl Window; protected Literal WorkflowState; protected Literal Workflow; protected override void OnLoad(EventArgs e) { base.OnLoad(e); if (!Sitecore.Context.ClientPage.IsEvent) { this.Window.ID = this.ID + "_window"; this.Portlet.Attributes["id"] = this.ID; Item item = UIUtil.GetItemFromQueryString(Sitecore.Context.ContentDatabase); string workflowID = item.Fields["__Workflow"].Value; string stateName = "none"; string workflowName = "none"; if(workflowID != "") { stateName = GetState(item, Sitecore.Context.Database, workflowID).DisplayName; workflowName = Sitecore.Context.Database.Items[new Sitecore.Data.ID(workflowID)].Name; } this.WorkflowState.Text += stateName; this.Workflow.Text += workflowName; } } private Sitecore.Workflows.WorkflowState GetState(Sitecore.Data.Items.Item item, Sitecore.Data.Database database, string workflowID) { // getting the workflow provider for the master database Sitecore.Workflows.IWorkflowProvider provider = database.WorkflowProvider; // getting the Simple workflow through the IWorkflow interface Sitecore.Workflows.IWorkflow iWorkflow = provider.GetWorkflow(workflowID); return iWorkflow.GetState(item); } } } 7. Add the reference to this dll into the web.config (UI » References section): /bin/CurrentWorkflowPortlet.dll The result of these actions is depicted below:

How to make lookup field to retrieve the field value rather than item name


How to make lookup field to retrieve the field value rather than item name For example, you want to refer to the Country items from your custom lookup field: content ---home -------countries ----------------Ukraine ----------------Denmark ----------------Germany ----------------USA ----------------Spain If you specify just a source field of your template field as /content/countries, you will get this field populated with the item names. But there is also a possibility to retrieve the field values of these items rather that the names. Let’s assume that these items are based on the Country template that defines a single field named ISO: If you want to have a custom lookup field populated with the ISO field values instead of the item’s name, the following steps should be completed: Add the custom lookup field named List to e.g. Document template: Extend the Template field template by adding a field named Field Name which will define the name of the field to fill into the lookup dropdown list: Fill the newly created field with the ISO value – name of the field to retrieve the values from: Now you have to customize the source code of your custom lookup field. Here is the full source code of the custom lookup field. The instructions about how to add a custom field to Sitecore shell can be found here: http://sdn5.sitecore.net/Articles/API/ Creating%20a%20Composite%20Custom%20Field/ Adding%20a%20Custom%20Field%20to%20Sitecore%20Client.aspx using System; using System.Text; using Sitecore; using Sitecore.Data; using Sitecore.Data.Items; using Sitecore.Shell.Applications.ContentEditor; using Sitecore.Web.UI.Sheer; using Sitecore.Web.UI.HtmlControls; using Sitecore.Web.UI.HtmlControls.Data; namespace Sitecore.Shell.Applications.ContentEditor { public class CustomLookup : Lookup { protected override void OnLoad(EventArgs args) { if (!Sitecore.Context.ClientPage.IsEvent) { Item contextItem = Sitecore.Context.ContentDatabase.Items[this.ItemID]; foreach(TemplateFieldItem tfItem in contextItem.Template.OwnFields) { if(tfItem.Source == this.Source) { FieldName = tfItem.InnerItem.Fields["Field Name"].Value; } } } base.OnLoad(args); } } } Here we have overridden the OnLoad method with the code that sets the FieldName property to the value of the field. There are some tricks made here since we do not know the name of the current field (List in our case), we had to iterate thorough the template’s own field of the context item and check the Source property. If the sources match, the FieldName property is set to the value of the template field (ISO in our case). The result of the actions above are depicted below:

Exporting the broken link report


This is some instructions on how to add an Export button to the Broken Links application. Upon clicking, you can export the results of the broken link search to any location, e.g. Excel worksheet, XML file, etc. The sample code below outputs the results to the log file. 1. Add a command under /content/applications/tools/broken links/commands: 2. Fill the fields as it is shown above. 3. Find the XML control that stands for the Broken Link dialog. It can be found here: \sitecore\shell\Applications\Tools\Broken links\Broken links.xml 4. Substitute the code beside definition with your custom class as it is shown below: <CodeBeside Type="Custom.CustomBrokenLinksForm,BrokenLinksForm"/> where Custom.CustomBrokenLinksForm is the full class name with namespace, BrokenLinksForm is the name of the assembly. 5. Create and compile the following sample code into the bin directory. This class outputs the broken link results into the log file. You can implement your own logic here to export it to any file. using System; using Sitecore; using Sitecore.Jobs; using Sitecore.Links; using Sitecore.Web.UI.Sheer; using Sitecore.Resources; using Sitecore.Diagnostics; namespace Custom { public class CustomBrokenLinksForm : Sitecore.Shell.Applications.Tools.BrokenLinks.BrokenLinksForm { [HandleMessage("brokenlinks:export")] protected void Export(Message message) { Context.ClientPage.ClientResponse.Timer("StartExport", 10); } public void StartExport() { Context.ClientPage.ServerProperties["handle"] = LinkDatabaseProxy.GetBrokenLinks(Context.ContentDatabase).ToString(); CheckExportStatus(); } protected void CheckExportStatus() { Handle handle = Handle.Parse(Context.ClientPage.ServerProperties["handle"] as string); JobStatus status = LinkDatabaseProxy.GetStatus(handle); if (status.Failed) { Sitecore.Context.ClientPage.ClientResponse.ShowError("An error occured.", StringUtil.StringCollectionToString(status.Messages)); } else if (status.State == JobState.Finished) { ExportReport(status); } else { Context.ClientPage.ClientResponse.SetInnerHtml("Report", "
" + Images.GetImage("Applications/48x48/exchange.png", 0x30, 0x30) + " Processed: " + status.Processed.ToString() + "
"); Context.ClientPage.ClientResponse.Timer("CheckExportStatus", 500); } } protected void ExportReport(JobStatus status) { ItemLink[] linkArray = status.Result as ItemLink[]; Log.Info("Exporting the broken link results into Excel worksheet...", this); if(linkArray.Length == 0) { Log.Info("There were no broken links found. ", this); } else { foreach(ItemLink itemLink in linkArray) { Log.Info(String.Format("Broken link found. SourceDatabaseName: {0}, SourceFieldID: {1}, SourceItemID: {2}, TargetDatabaseName: {3}, TargetItemID: {4}, TargetPath: {5}.", itemLink.SourceDatabaseName, itemLink.SourceFieldID, itemLink.SourceItemID, itemLink.TargetDatabaseName, itemLink.TargetItemID, itemLink.TargetPath), this); } } Context.ClientPage.ClientResponse.SetInnerHtml("Report", "The export process is finished."); } } }

Adding the Save As button to Image Editor


You can customize Image Editor and add "Save As" button. Here are steps: 1) Copy Image Editor XAML application from "\sitecore\shell\Applications\Media\Imager\Imager.xml" to "\sitecore\shell\Override\" folder. 2) Change CodeBeside e.g.: <CodeBeside Type="CustomImagerForm.CustomImagerForm, CustomImagerForm"/> 3) Create a custom code beside class that inherits from Sitecore.Shell.Applications.Media.Imager.ImagerForm 4) Here is the example code: using System; using System.IO; using Sitecore; using Sitecore.Text; using Sitecore.Data; using Sitecore.Data.Items; using Sitecore.Shell; using Sitecore.Shell.Applications.Media.Imager; using Sitecore.Web.UI.Sheer; namespace CustomImagerForm { public class CustomImagerForm: ImagerForm { [HandleMessage("imager:saveas", true)] protected void SaveAs(ClientPipelineArgs args) { if (args.IsPostBack) { if (((args.Result == null) || (args.Result.Length <= 0)) || (args.Result == "undefined")) { return; } string lastFile = this.GetWorkFile(this.Work); SaveAsNewImage(lastFile, args.Result); } else { Context.ClientPage.ClientResponse.Input("Please input new name without extension? ", "NewName"); args.WaitForPostBack(); } } #region Private Method private void SaveAsNewImage(string oldName, string newImage) { oldName = MainUtil.MapPath(oldName); newImage = String.Concat(Path.GetDirectoryName(MainUtil.MapPath(this.File)), @"\" , newImage , Path.GetExtension(oldName)); System.IO.File.Copy(oldName, newImage, true); } private string GetWorkFile(int index) { ListString listFiles = new ListString(this.UndoList, '|'); string lastFile = listFiles[index]; return lastFile.Substring(0, lastFile.IndexOf("*")); } #endregion Private Method } } I’ve attached this class. 5) Go to Sitecore client and switch to Core database 6) Duplicate node /content/Applications/Media/Imager/Toolbar/Resize to /content/Applications/Media/Imager/Toolbar/SaveAs and place a message “imager:saveas” to the Click field. In the Icon filed you can place your the appropriate icon. So, when you click SaveAs, the new file name without extension will be requested and aftewards Image Editor saves edited image to new one.

Media Library: problem with uploading


If you experience that after uploading media items are not showing in the Media Library while physical files were copied into the upload folder, the following instructions should fix the problem. 1. Open the Access Viewer. 2. Ensure that the Inherit checkbox is set for the Masters node for the sitecore/anonymous user. 3. The sitecore/anonymous user should have all allowed security rights for the whole media library. 4. Check also that the Everyone group has the same security setup. I.e. this user (sitecore/anonymous) should have rights for the Media Library where the items are created and for the masters the media items are created from since these actions are performed under this account.

Creating Debug Item button


You may consider adding a button to the Content Editor’s toolbar and context menu that will start the debugging of the selected item. Here are the steps: 1. Create a new command named Debug under /Content/System/Commands in the core database. 2. Fill all necessary fields in the newly created command. Pay attention to the following ones: • Click – you can set some arbitrary value here, for example item:debug. • Type – specify namespace.class_name,assembly_name here of the class which is shown below: using System; using Sitecore; using Sitecore.Data.Items; namespace Sitecore.Shell.Commands { public class DebugItem : CommandBase { public override bool Execute(Item[] itemArray) { Context.ClientPage.ClientResponse.Eval("window.open('/?sc_itemid=" + itemArray[0].ID.ToString() + "&sc_debug=1', '_blank')"); return true; } public override CommandStatus QueryStatus(Item[] itemArray2) { return CommandStatus.Normal; } } } As can be seen, we inherited this class from the CommandBase class. The key method is named Execute where we are opening the selected item in debug mode in new window. The QueryStatus method is obligatory to be overridden. 3. Navigate to System/Shell/__Default/Commands and Add “Debug” to the Commands multilist field That’s all, the command should be present in the context menu. After clicking, you should get the selected item in the debug mode. Note that the selected item should be present in the web database, otherwise, the home node will be shown upon clicking on the Debug command.

Thursday, March 23, 2006

Accessing the AD server over Windows Firewall using the LDAP module


The following steps should be done in order to let the LDAP module access the AD server when Windows Firewall is switched on. 1. Open the Windows Firewall. 2. If it is turned off, activate it. Note: Exceptions should be allowed. 3. Switch to the Exceptions tab. 4. Click the Add Port button. 5. Specify name of the exception (e.g. LDAP), port number (389) and protocol to be used (TCP). 6. Click OK. 7. The newly created exception must be activated by default. After this, the LDAP module can access the AD server and query the directory for users and roles. Related reading.

Sunday, March 12, 2006

Inserting a field value into the title tag of the layout


If you want to modify the contents of the < title/> tag in the layout dynamically and populate it with the value from a current item’s field, the following steps should be completed. 1. Create a rendering that will simply output the field value of the current item: <sc:text field="title" /> 2. Add the rendering definition inside of the < title/> tag: <title>
<sc:xslfile ID="pageTitle" runat="server" renderingid="{C954895B-655A-4C6C-8641-3B199CCDC2D9}" path="/xsl/Page Title.xslt" />
</title>
To make it more easy you can drop this rendering on your layout in the Layout Studio, then cut-paste it into the < title/> tag.

Saturday, March 11, 2006

Items that are Done but not published


private void Page_Load(object sender, System.EventArgs e) { Sitecore.Data.Database master = Sitecore.Configuration.Factory.GetDatabase("master"); Sitecore.Data.Items.Item rootItem = master.Items.GetItem("/sitecore/content/home/"); // the ID of the "Simple" workflow string workflowID = "{A5BC37E7-ED96-4C1E-8590-A26E64DB55EA}"; // checking all children of the rootItem foreach (Sitecore.Data.Items.Item item in rootItem.Children) { // getting the current workflow state of the item string stateName = GetState(item, master, workflowID).DisplayName; // if the item is in the "Done" state, outputting its name if (stateName == "Done") { Response.Write(item.Name); } } } private Sitecore.Workflows.WorkflowState GetState(Sitecore.Data.Items.Item item, Sitecore.Data.Database database, string workflowID) { // getting the workflow provider for the master database Sitecore.Workflows.IWorkflowProvider provider = database.WorkflowProvider; // getting the Simple workflow through the IWorkflow interface Sitecore.Workflows.IWorkflow iWorkflow = provider.GetWorkflow(workflowID); return iWorkflow.GetState(item); }

Working with Sitecore in VS .NET 2005


Due to the different approach of creating a new web project in VS 2005, some tricks should be made to create a Sitecore project. Firstly, you need to download and install Visual Studio 2005 Web Application Projects (Beta V2 Preview) Then you need to perform the following steps in order to get a similar functionality for working with Web Project like it was in VS 2003: 1. Start VS 2005 and create ASPNET Web Application: Visual Studio creates solution and project only in subfolder now: /Test5203/Test5203/ 2. Close project 3. Move project files (*.csproj.user and *.csproj, Properties folder) to the Sitecore distributive root 4. Open the project (*.csproj file ) from the Sitecore distributive root. 5. Exclude the default.aspx page from the project. Now you can add necessary assemblies, include some layouts and sublayouts in project as it was before: 5. Make sure that Project Properties -> Ouput Type is Class Library like it is shown here. 6. In order to work with code behind class you should resort to some trick.
  • Create a layout in Sitecore. Do not include one in the VS project
  • Create WebForm in VS with the same name and overwrite the existing layout:
  • Add missing directives at the top of the layout: <%@ OutputCache VaryByParam="none" Duration="100" %>
<%@ register TagPrefix="sc" Namespace="Sitecore.Web.UI.WebControls" Assembly="Sitecore.Kernel" %>
  • If you want to work with code-behind class, you should replace the CodeBehind page attribute with the CodeFile attribute:
<%@ Page language="c#" Inherits="Viewstate.layouts.Main_Layout" Codepage="65001" CodeFile="Main Layout.aspx.cs"%> Well, that’s all. Now you can work in VS 2005 like it was in VS 2003 - compile, attach to ASPNET process, debug code etc. Related reading:
  1. http://webproject.scottgu.com/
  2. http://webproject.scottgu.com/CSharp/Default.aspx
  3. http://webproject.scottgu.com/CSharp/HelloWorld/Helloworld.aspx
P.S. Great thanks to Alexander Tsvirchkov for the complete step-by-step instructions.

Wednesday, January 18, 2006

Firebird problem continued


If you are experiencing the following screen after launching the newly installed Sitecore with Firebird database enabled and the application pool problem is not the case, please follow the steps below in order to make it work. Please note that the Firebird server is not required to be installed on the web server at all. If restarting IIS doesn’t help in your case, I suggest you to ensure that the following security permissions are set for the Firebird databases. • ASPNET for XP or NETWORK SERVICE for 2003 Server – full control on the dist folder • IUSR – full control on the dist folder In addition, grant write rights for the data folder in IIS. Make appropriate security changes and reset the IIS.

Unable to load DLL (fbembed)


Sometimes the Firebird runtime couldn’t be loaded from the bin folder. The following error may appear: This should be cured by copying fbembed.dll to your system directory (c:\windows\system32).

Tuesday, January 10, 2006

Layout Caching


In order to turn on layout caching, the following should be done:
  1. Ensure that the following web.config setting is set to false. Note that layout caching is disabled by default: <setting name="DisableBrowserCaching" value="true">
  2. Add “OutputCache” page directive with appropriate parameters to the layout (ASPX page) that you would like to cache.