Showing posts with label Sitecore 6. Show all posts
Showing posts with label Sitecore 6. Show all posts

Thursday, April 01, 2010

God Mode for Sitecore


While playing with Sitecore .NET CMS today, I discovered a hidden feature that was slipped into the product just recently in the latest update for 6.2. It is called God Mode and is pretty much the same as in Windows 7.

Just download this package and install on your local Sitecore instance.

***WARNING***
Please don’t try it in production environment since it is not meant for this.
***WARNING***

Expected to work with 6.1/6.2 releases.

Let me know what you think!

Tuesday, March 09, 2010

Read only view of Rich Text field for Sitecore editor


Today I want to share an idea of a lightweight customization for Sitecore CMS that meant to increase usability of the rich text field for your editors. The idea is inspired by great feedback I received recently while being in the field.

The basic problem reported by one of the editors who uses Sitecore on the daily basis, is that when a content item is approved in workflow there is no way to see what’s inside of a rich text field easily. All field buttons are grayed out thus disabled:
disabled rich text field
…so the only way is to click “Lock and Edit” which creates a new version:
lock-and-edit

As you can imagine, this is not generally acceptable. If your non-admin editor user simply wants to see what’s in there, you don’t want a new version to be created.

Now it is worth mentioning that the item which is approved, unlocked is disabled for editing only if “RequireLockBeforeEditing” setting is set to true which is often the case, since you would want to have “Lock and Edit” in place so new version of the approved content is created.

Same goes along with the items that are locked. If there is a locked item, I would still want to see the content of the rich text field in read-only mode.

So one of the easiest ways I could think of to address this was enable “preview” of the rich text field in a modal dialog on double click.

Tech support provided the following solution which enabled the double click on disabled rich text field and showed the “preview” version of the field in a modal dialog:image

To make it work, follow these steps:
1. Open Preview.aspx under /sitecore/shell/Controls/Rich Text Editor.
2. Locate the scEdit() function and comment out the first IF statement:

function scEdit() {
        //if (scDisabled == "1") {
         // return;
       // }
        
var form = scGetForm();
...

3. Compile the following code:

namespace Company.Web.Fields
{
   using Sitecore.Diagnostics;
   using Sitecore.Shell.Applications.ContentEditor.RichTextEditor;
   using Sitecore.Web.UI.Sheer;

   public class RichText : Sitecore.Shell.Applications.ContentEditor.RichText
   {
      protected new void EditText(ClientPipelineArgs args)
      {
         Assert.ArgumentNotNull(args, "args");
         if (Disabled && !args.IsPostBack)
         {
            var url = new RichTextEditorUrl
                         {
                            Conversion = RichTextEditorUrl.HtmlConversion.Runtime,
                            Disabled = Disabled,
                            FieldID = FieldID,
                            ID = ID,
                            ItemID = ItemID,
                            Language = ItemLanguage,
                            Mode = string.Empty,
                            Source = Source,
                            Url = "/sitecore/shell/Controls/Rich Text Editor/Preview.aspx",
                            Value = Value,
                            Version = ItemVersion
                         };

            var str = url.GetUrl();
            SheerResponse.ShowModalDialog(str.ToString(), "800px", "500px", string.Empty, true);
            args.WaitForPostBack();
         }

         base.EditText(args);
      }
   }
}

4. In web.config, add your own controlSource reference to the namespace above:

<controlSources>
      <source mode="on" namespace="Sitecore.Web.UI.XmlControls" folder="/sitecore/shell/override" deep="true" />
...
       <source mode="on" namespace="Company.Web.Fields" assembly=" Company.Web" prefix="custom" />
</controlSources>

5. In the core database, locate the Rich Text field item (/sitecore/system/Field types/Simple Types/Rich Text) and adjust the value of the “control” field:
custom:RichText
Note that the prefix is used from the controlSource reference above.

Simple and lightweight customization that should bring some value. Sitecore rocks!

Tested on 6.1, expected to work on 6.0.x and 6.2.

Friday, February 05, 2010

Sitecore 6 quick tip. Email as user name for authentication


It is pretty common to have email served as username for authentication in web systems for both visitors and internal users. Sitecore Content Management System is no exception.

With 5.3 it was pretty easy since users were just items – adjust the regex of the “ItemNameValidation” setting, make sure you don’t have @ and dots in the “InvalidItemNameChars” setting and you are pretty much set.

Now as you know, Sitecore 6 has different rules for security so you will need to do the following to make it work:
1. Make sure the membership provider treats email as a unique attribute for users so you don’t end up with more than one user attached to the same email:
<add name="sql" type="System.Web.Security.SqlMembershipProvider" connectionStringName="core" applicationName="sitecore" minRequiredPasswordLength="1" minRequiredNonalphanumericCharacters="0" requiresQuestionAndAnswer="false" requiresUniqueEmail="true" maxInvalidPasswordAttempts="256" />

2. Put email into both username and email properties (fields) of a user during the registration (can be handled via code).

3. Introduce the following entry into the web.config’s <settings> section. The “value” attribute parameter contains the regular expression used in the Create User dialog within User Manager. This regex should allow emails, otherwise Sitecore will fallback on a default regex that does not allow it.
<setting name="AccountNameValidation" value=".+" />

4. If you want to handle the case when email needs to be changed, either provide an extranet form for the profile section on your website or you can even take it one step forward – modify the EditUser dialog within User Manager to have this ability.

Happy coding!

Friday, January 29, 2010

Better Know Sitecore: Preventing multiple messages of the same type in the log


I would like to start a series of blog posts titled “Better Know Sitecore” where I will cover those areas of the product that are on on the surface, for example, useful utility methods that can save you time, efficient development techniques, etc.

Today we are covering the “Sitecore.Diagnostics” namespace, specifically, the “Log” class which most of you used for logging.
Recently discovered method of this class “SingleError” can be used when you would like to log only one instance of a specific error, not more than that. For example, we used it when trying to get a template for an item. If a template is not found or invalid, we don’t want to output it more than once. Under the hood, there is a caching mechanism in place that checks if the same error message is already in the collection – skip it, otherwise call the “Error” method.

Thanks goes to Sergey for clarifying it.

Happy logging!

Tuesday, December 22, 2009

Publish to pre-production web database


In my experience with enterprise level implementations, there is often a need for a separate “stage environment” for final content preview or a pre-production phase of workflow.

It is also often the case that the environmental limitations or restrictions make the content delivery part of the authoring environment suitable for such purpose. For example, your e-commerce infrastructure could not be available from the authoring instance. One of the possible ways to approach such a requirement is to configure a stand-alone staging instance of Sitecore which will be used for content delivery instance and is commonly configured exactly like one of the servers in the web farm. There is one exception though – the database that is used for content delivery is generally different from the one that is used in production content delivery.

With this approach, now the question is how to plug in workflow in a way that content could go via this “stage” environment and then only upon final approval will be able to get to “production”.

Now there is a problem that you cannot get get items published until they are in final workflow state. You could have two workflow states “Staged” and “Published”, both marked as final and with auto publish actions connected.
I don’t like this approach since it goes against the workflow nature – only one state should be marked as final.

Alternatively, you can plug in a “copy item” workflow action to the “Staged” workflow state where you can programmatically copy an item from master database to stage web.

While this approach does seem legitimate, it works around the need of publishing thus any additional processing you may be having in publish(item) pipeline would not work.

I looked into the option of having publishing process ignore workflow when publishing to stage. While this seemed like a dangerous path to go, I have soon discovered that the PublishHelper’s GetVersionToPublish method already accepts this notion of passing a “requireapproval” flag to the underlying publishing mechanism. Since it is always passes “true”, I started looking into ways to make this flag dynamic and figured that setting it on the level of PublishOptions could be a good idea. For example, from the code of my workflow action I would be able to define whether I want to have workflow respected or not.

Here is what you will need to do to make it work.

First, override the default PipelinePublishProvider and plug it into the web.config:

<publishManager defaultProvider="default" enabled="true">
    <providers>
        <clear />
         <!--<add name="default" type="Sitecore.Publishing.PipelinePublishProvider, Sitecore.Kernel" />-->
         <add name="default" type="SCUSAINC.Publishing.ExtendedPublishProvider, SCUSAINC.Publishing" />
     </providers>
</publishManager>

Secondly, you will need to override the CreatePublishHelper class:

public class ExtendedPublishProvider : PipelinePublishProvider
    {
        public override PublishHelper CreatePublishHelper(PublishOptions options)
        {
            Assert.ArgumentNotNull(options, "options");
            if (options is ExtendedPublishOptions)
                return new ExtendedPublishHelper(options as ExtendedPublishOptions);

            return new PublishHelper(options);
        }
    }

After that, introduce two more classes – ExtendedPublishHelper and ExtendedPublishOptions:

public class ExtendedPublishHelper : PublishHelper
    {
        private readonly ExtendedPublishOptions _options;

        public ExtendedPublishHelper(ExtendedPublishOptions options)
            : base(options)
        {
            _options = options;
        }

        public override Item GetVersionToPublish(Item sourceItem)
        {
            Assert.ArgumentNotNull(sourceItem, "sourceItem");
            if (Options is ExtendedPublishOptions)
            {
                return sourceItem.Publishing.GetValidVersion(Options.PublishDate, _options.RequireApproval);
            }

            return sourceItem.Publishing.GetValidVersion(Options.PublishDate, true);
        }
    }

public class ExtendedPublishOptions : PublishOptions
    {
        public ExtendedPublishOptions(Database sourceDatabase, Database targetDatabase, PublishMode mode, Language language, DateTime publishDate, bool requireApproval)
            : base(sourceDatabase, targetDatabase, mode, language, publishDate)
        {
            RequireApproval = requireApproval;
        }

        public bool RequireApproval { get; set; }
    }

Now you are ready to launch publishing with workflow settings completely ignored. For example, this is how you can do it from the workflow action:

var database = Factory.GetDatabase(databaseName);

var options = new ExtendedPublishOptions(dataItem.Database, database, PublishMode.SingleItem, dataItem.Language, DateTime.Now, true)
{
         RootItem = dataItem,
         Deep = true;
};

new Publisher(options).PublishAsync();

That’s it!

Tested on Sitecore 6.1. Expected to work with 6.2.

Monday, December 14, 2009

Get all published items in Sitecore 6.1/6.2


You may need to get all items that were processes by a publishing operation for different reasons, for example, pull certain information for each item and notify CDN of a change so cache could be purged. AKAMAI can be configured to behave that way.
Currently we have two articles on the subject. This one describes how to use the PublishEngine, second article suggests approach with custom PublishLog. Both are not compatible with 6.x. So I’ve looked into the options for the latest Sitecore with tech support. Our findings included introduction of a new PublishProcessor which examines the Queue property of PublishContext, but this did not work properly for smart publishing and I wanted a bulletproof yet simple approach applicable to all possible publishing modes.
Then I remembered that we are already doing similar things for indexing. The update index job processes only changed items and gets that information from the History storage.

Thursday, December 10, 2009

Quick tip: resolving section sort order problem with inheritance


There is a known issue currently with the sort order set on the standard values for the “/sitecore/templates/System/Templates/Template section” template and not being picked correctly and always treated as zero until you set the sort order specifically for the section thus overriding the standard value.

This has impact on section inheritance, producing unexpected behavior in the following case:
1. You have an abstract “base product” template with the following sections:
image

2. You have a derivative template “monitor” with its own domain specific data section:
image

3. You want to have certain sections from the “base product” template show up first, then the “data” section from the “monitor” template itself, then the rest of sections from base:
image 

Since the default inherited sort order for a section is 100 (coming from __Standard values), however you won’t be able to accomplish this by setting the sort order of “descriptors” and “product data” section to 80 and 90 correspondingly.
This is because the value for the section sort order that is coming from the __Standard values will always be considered as zero.

There is an easy and simple way to solve this by simply setting the sort order of “descriptors” and “product data” sections to negative values (-2 and -1) for example.

Enjoy!

Friday, October 30, 2009

Quick tip – remove “Edit HTML” option for the rich text field


A while ago  (in 2005 actually), I blogged about how to disable HTML tab in the HTML Editor. How it is almost 2010 and it is time to post an update :-)
Well, first thing I would like to point out is that after 4+ years, the approach pretty much has not changed. We changed the rich text editor from CuteEditor to Telerik’s RAD Editor, released a great deal of new functionality, but still preserved the idea of backward compatibility even in such small things as rich text editor configuration!
Isn’t it amazing?

Enough reminiscing, let me cut to the chase! Today with 6.x, you can still disable HTML tab for the RTE by modifying the security permissions on the “HTML View” item in one of the rich text profiles, for example for the “Rich Text Full”: /sitecore/system/Settings/Html Editor Profiles/Rich Text Full/Buttons/HTML View

Two things changed here. All profile definitions migrated to the “core” database and now the container names are a bit different.

In addition to this, as one of our partners noticed, there is another button in Content Editor above the RTE field called “Edit HTML” which needs to have the same rules applied to.
Capture

For that, follow the following steps:
1. Go to the core database, locate the field definition item:
/sitecore/system/Field types/Simple Types/Rich Text/Menu/Edit Html
2. On the Security Tab, click “Assign” and select the role you don’t want this option to be available to. Deny inheritance of this item or specify other security settings that make sense for your needs.
inheritance

That’s it, Folks! Enjoy Sitecore!

Friday, May 08, 2009

Altering the behavior of the tree view control


You may have noticed this too. When you create a template, the Base Template dropdown may be tricky to operate with. If you are trying to find a template in the subfolders to inherit from and miss the expansion plus, the tree will collapse and you will have to start over. This has been bugging me for a while so I asked the tech support if there is an easy way to fix this and not have the tree to collapse.
Luckily, there is an easy fix, but you will have to mess with the Sitecore.js file located under /sitecore/shell/controls.

What you need to do is to add a global variable in Sitecore.js:

var iAmTree = null;

Then straight after line 563 within scSitecore.prototype.postEvent = function(tag, evt, parameters) add the following:

iAmTree = false;                                                                                      
if (ctl.className.toString() == "scTreeItem") iAmTree = true;  

And finally within scRequest.prototype.resume = function() alter this IF statement:

if (this.closePopups && typeof(scForm) != "undefined") {
    scForm.browser.closePopups("Request");
  }

to this:

if (this.closePopups && typeof(scForm) != "undefined" && iAmTree != true) {
    scForm.browser.closePopups("Request");
  }

This will also affect the Droptree field’s behavior and possibly more areas.

And you may have guessed – create a backup of Sitecore.js file and use the solution on your own risk ;-)

Special thanks goes to Ruslan for providing the fix.

Wednesday, March 25, 2009

Latest updates for version 6 and 5.3


We pushed two updates yesterday for both major releases of CMS product.

The list of issues addressed in the latest update for version 6 (6.0.1) is impressive. A couple of them related to proxies and the one for CryptographicException seems to be addressing the need for step #7 in my guideline for Windows Authentication.

The update for 5.3.2 release shows the continuous support of the installations that were not upgraded yet ;-)

Thursday, January 08, 2009

jQuery conflicts with PageEditor


There is such problem, indeed. It is caused by the conflict with Prototype.js that most of Page Editor, including designer and debugger uses. It can be simply worked around by overriding the $-function by calling "jQuery.noConflict()".
<script type="text/javascript" src="/js/jquery-1.2.6.js"></script>
 
<script type="text/javascript">
 
var $j = jQuery.noConflict();
 
      $j(document).ready(function() {
 
      });
 
</script>
More about that on the jQuery website.

Thursday, December 18, 2008

Who moved the item to the workflow state?


Applies to Sitecore 6.0.
In the workflow scenarios with "rejection" commands when an item is moved to a previous workflow state, sometimes necessary to notify the user who submitted the item in the first place.
Since Sitecore keeps track of workflow activities and writes everything into WorkflowHistory table, there are also APIs to retrieve such information.
The code snippet below shows how it can be done.

protected override string GetRecipient(WorkflowPipelineArgs args)
 {
    IWorkflowProvider workflowProvider = Context.ContentDatabase.WorkflowProvider;
 
    if (workflowProvider != null)
    {
       IWorkflow workflow = workflowProvider.GetWorkflow(args.DataItem[FieldIDs.Workflow]);
 
       if (workflow != null)
       {
          WorkflowEvent[] history = workflow.GetHistory(args.DataItem);
          if (history.Length > 0)
          {
             WorkflowEvent wfEvent = history[history.Length - 1];
             string fullUserName = wfEvent.User;
 
             if (User.Exists(fullUserName))
             {
                System.Web.Security.MembershipUser mUser = Membership.GetUser(fullUserName);
 
                if (mUser.Email != string.Empty)
                {
                   return mUser.Email;
                }
             }
          }
       }
    }
 
    return string.Empty;
 }

This method can be a part of workflow email action that is associated with the "Reject" command.
Related links:

Don't you just love Sitecore workflow?