Recommended Practices Part 1

New Beginnings!

So the first thing that I should share is that I recently left Sitecore to go back to a pure developer type of role.   While I am very sad to leave Sitecore, I really missed being a developer and didn’t want to lose that “code monkey” side of myself.  I found a new home and I couldn’t have asked for a better place to transition to.  I’m working now with XCentium.  It is a real privilege to be working with such a talented group.. and a little unnerving discussing Sitecore with these guys.  For example, my boss has been involved with Sitecore from its conception, another guy was at Sitecore for years until recently when he left Sitecore to go to XCentium, and one of my teammates just received the MVP distinction from Sitecore.  So with that being said, let me say that those discussions are what triggered this post.  I’m trying to learn some of the details about getting back into being part of a full-time development team and it got me thinking about a few things.   I’m passionate about what I do and somewhat anal about development practices and having seen enough bad development practices, I have a few things I’d like to share.

Minor Rant

So as I was going over some of the work that I did during my time with Sitecore Technical Services, one of my duties included doing site reviews.  In doing so, I would basically look at all aspects of a site and determine if the client was practicing the Sitecore recommended best practices in each area.  What I usually found was that there were lots of areas that were lacking and could easily be updated to get the site within standards.  Mostly, I think, this is just due to a lack of knowledge about what those recommended practices were.  There is a document on the SDN that covers the Sitecore Best Practices, however, I think it’s one of those things that unless you have a need to look at it, most people don’t.  I thought it would be helpful to cover some of the things that I often saw that can be implemented very easily, and in a lot of cases, can be applied on existing sites without disturbing the content in place.  Lets start with one of the biggies.

Templates and Standard Values

Inheritance: Now, templates is the one thing that while there are some things you can change (which I’ll get to), it’s very difficult to fix template problems that result from bad architecture.  One school of thought seems to say to keep the number of templates to a minimum, but I think that is mostly in terms of trying to encourage using inheritance.  Keep in mind that it’s perfectly acceptable to create templates that have NO fields and simply inherit from another template just so that a different set of standard values is implemented.  By the way, if you don’t know about template inheritance, let me just say, You’re doing it wrong!  PLEASE.. FOR THE LOVE OF ALL THAT IS GOOD.. learn about it before you build your next site!  Some benefits of using inheritance is that you have a lot less work, everything has much more flexibility and content is much easier to maintain.  Also, realize that your template standard values for base templates inherits down.  That’s a very powerful tool to have!  And if nothing else, you should ALWAYS have at least one base template that ALL templates inherit from. 

Template Folder Structure:  Of course its best to keep your templates in an ordered structure.  This isn’t usually a problem.  One thing that is helpful, however, is to keep templates organized in such a way that makes it clear the purpose.. specifically templates that are stand alone pages on the site vs. templates that are components used by other templates or metadata for the site, etc.  For example, one of the things I often had to do was help set workflow… but in a lot of cases, you don’t want workflow on templates that are just component templates.  It’s very helpful to know which templates need to have workflow and which aren’t necessary or may need a different workflow.  Given Sitecore’s ability to dynamically update links, simple drag and drop is completely doable (as long as you don’t have hard-coded paths to your templates in your code, of course… but we’ll get to coding standards later).  

Icons: Use UNIQUE ICONS!!! For fucks sake.  It’s so quick and easy to set an icon for each of your templates and makes such a difference!  If you are a new user tasked with anything that means having to know what content is what and where to find it,  giving your templates a UNIQUE icon is sooooo  helpful!!!   The poor marketing guy who just started a new job and has to go find all the pages that need to be connected to his new marketing campaign will love you for it.  This doesn’t stop at the marketing users either.  Think about the new developer who has to try to understand the structure of the existing site that they’ve just been hired to maintain.  They open up Sitecore, only to find a site full of white document icons that are all the generic and all the templates have names that may mean something to you but not a thing to them.  Those white document icon and with names that don’t mean anything to them but if you give your templates unique icons, just a glance suggests the structure.  This is soooo easily changed!!!  Just look:

I found this illustration on a Nick Allen’s blog where he illustrated this very nicely.  Which would you prefer?plain vs icons

Template Standard Values: Another thing to point out is to make sure that you are filling in values on the template standard values.  Now, to be clear, icons can be set on the template itself since this should be a property of the icon item itself. A good thing to keep in mind is that if the field value belongs to the template, then it should go on the template.  If the value belongs to the items that are created from it, then add the value to the template standard values.  There are a few template standard values that are important to fill in and are often times neglected:

  • Insert Options: Some really, REALLY important things to fill in  are the Insert Options field.  Keep in mind that you, as a developer, have the right to use the Insert from Template.  Marketing users do not.  In many cases, if you use the placeholders as you should, then unless something is in the Insert Options, a regular user can’t even choose the content type to insert.
  •  Workflow:  The way that workflow is implemented is that the Default Workflow field is going to be the one that new items inherit from.  You’ll want to make sure that this is the ONLY value you set on a template standard values.  DO NOT set the Workflow field itself.
  • Layout values.  Lots of people fail to set the layout standard values and while it’s not the end of the world, think about the hassle of realizing it after the fact, updating it and suddenly remembering that you have just changed the banner on a bunch of pages that inherited from a template that inherits from the template you just changed.  In fact, in 90+% of all the time, there is NO reason for content to ever be outside of the template standard values.

Standard Fields: Be aware of the standard fields for templates.  For example, take some time to get familiar with what all those fields do.  Many of them are very helpful and are blank more often than they’re used.  For example, if there is a field that the end users need a bit of explanation to understand its purpose, you can use Help fields and includes the ability to add images to give it context.  Which brings up that image fields should ALWAYS have a media folder path set.  One thing that I see happen a LOT is that a media library is a jumbled mess of random images scattered here and there because image fields do not have a source defined.  Without this, when a user goes to find an image to place in an image field, there is no standard location for where it’s at and they end up uploading the image they need to random places.  The media library tends to get really messy, really fast.  I’ll touch more on this later though.  Setting a source also works for a Rich Text field.  You can set the default rich text in the web.config, but consider that you want to give users a certain font or snippet set to use in some templates but not others.  This is very rarely used and can have such an impact on how business are allowed to input their content.   Finally, most people know that the Display Name field can be used for item names that are not SEO friendly.  Remember, however, that this field exists on the standard template, so you can use it for template field names that aren’t user-friendly too!

So I had planned on making this a post to touch on each of the areas, but we’ll touch on the other areas next.

Next:  Information Architecture.  Now that you know how to create your templates, lets look at some ways to improve the structure of content.

Anyway, I’ll wrap this up for now.  Look for the next installment.  And as always, happy Sitecoring!

 

ECM Users and Roles

Let me state that one of the things I have to do is help clients come up with a solution for some situation that they find themselves in and Sitecore doesn’t have an out of the box solution for.  Tonight I had a request from a co-worker to see if I had any ideas on how to clean up a mess they made trying to implement Email Campaign Manager without any real plan for what they doing or the impact that it would have on the site.  Essentially, what happened is that a young, eager beaver developer read through what ECM did, decided it was the perfect tool to handle the all the correspondence that they sent to their users and promptly installed the module for a closer look.  After a quick investigation, he found the import function, quickly downloaded their userbase into CSV and started the import process happily on his way to sending glorious emails.  At some point he either decided it was taking too long or something.. but he managed to make a huge mess out of the user base that they had.  I’m not sure the whole extent since this was a co-worker, but now they needed to know how to get rid of all those users out of their database.  Sitecore, by default, allows you to delete users.. one at a time.  This is just not feasible if you mistakenly import your whole user base in and decide you don’t want it.  I’ve done that before and ended up just starting with a fresh Core database so then used serialization to get my data back. I happened to be working on another thing for another client that was actually the exact opposite of this situation and decided that if I could use the API to import users.. I could do it to get rid of them.   I built out a page that handles this and thought it might come in handy to someone else out there.   I’ve tested this briefly with test content and it worked just fine.  Your results may vary.. so please, please, if you use this, back up your CORE database first.  It’s only something I wrote for a quick fix that might be easier than starting with a fresh Core DB.

This is a simple .aspx page that I would suggest placing in your /sitecore/admin folder.  That ensures that only an administrator level user can access it at all.  Once logged in, it will allow you to choose a domain.. which auto populates all the roles for that domain.  The domain selection wasn’t really necessary, but I also had the idea that you could edit the selection of domains to restrict someone from accidentally deleting something like your whole extranet user base by accident. You then choose the role that you want to remove and presto.. any user that belongs to that role will be deleted along with the role.  Again, this is NOT really meant for anything other than cleaning up an ECM install or some other situation that you need to get rid of a lot of users in one fell swoop that are stored in the Core database so don’t leave nasty grams complaining that it didn’t work for you if you are doing anything other than using the out of the box Sitecore ASP.NET membership provider.   Also, if you simply want to remove users from roles, this can easiliy be edited to do that.. but at this time, it is NOT.  This deletes the user account and the role completely.

Finally, I have, by default, disabled this page by default.  One must edit the page, similar to how some other admin type of pages that come with Sitecore are configured to work. Enabling or disabling it means manually editing the .aspx page and setting a property on the page to true.. which enables and disabled the button that submits things.  Enjoy and again, please note that it’s very late and I’m very tired and if you find obvoius flaws. please share them politely so that I may correct my code.

Thank you and happy Sitecoreing!

<%@ Page Language="C#" AutoEventWireup="true" %>
<DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
// TODO: to enable the page, set enableUnlockButton = true;
private bool enableUnlockButton = false;

protected void Page_Load(object sender, EventArgs e)
{
 this.SubmitButton.Enabled = this.enableUnlockButton;
 if (!Page.IsPostBack)
 {
 this.Domain.DataSource = Sitecore.Configuration.Factory.GetDomainNames();
 this.Domain.DataBind();
 this.Domain.Items.Insert(0,new ListItem("Please select a domain",""));
 SubmitButton.OnClientClick = "return confirm('Are you sure you wish to delete the users in this role and the role?');";
 }
}

protected void SubmitButton_Click(object sender, EventArgs e)
{
 try
 {
 if (Roles.SelectedValue.Length > 0)
 {
 List<string> users = new List<string>(System.Web.Security.Roles.GetUsersInRole(Domain.SelectedValue + '\\' + Roles.SelectedValue));
 if (users.Count > 0)
 {

foreach (string s in users)
 {
 System.Web.Security.Membership.DeleteUser(s);
 }
 }
 System.Web.Security.Roles.DeleteRole(Domain.SelectedValue + '\\' + Roles.SelectedValue);
 Domain.SelectedIndex = 0;
 Roles.Items.Clear();
 }
 }
 catch (Exception)
 {

throw;
 }
}

protected void Domain_SelectedIndexChanged(object sender, EventArgs e)
{
 try
 {
   if (Domain.SelectedValue.Length > 0)
   {
      List<string> roles = new List<string>();
      foreach (string s in System.Web.Security.Roles.GetAllRoles())
      {
        if (s.Contains(Domain.SelectedValue))
           roles.Add(s.Remove(0, s.LastIndexOf('\\') + 1));
      }
      Roles.DataSource = roles;
      Roles.DataBind();
   }
  else
   Roles.Items.Clear();
 }
 catch (Exception)
 {
    throw;
 }
}

protected void descriptionLiteral_PreRender(object sender, EventArgs e)
{
   this.descriptionLiteral.Visible = !this.enableUnlockButton;
}
</script>
<head runat="server">
 <title>Remove Users in Sitecore Role</title>
<style type="text/css">
body
 {
   font-family: normal 11pt "Times New Roman", Serif;
 }

.Warning
 {
   color: red;
 }

</style>
</head>
<body>
 <form id="form1" runat="server">
 <asp:ScriptManager ID="ScriptManager1" runat="server" />
 <div>
    <asp:Literal runat="server" ID="descriptionLiteral" EnableViewState="false" OnPreRender="descriptionLiteral_PreRender">
      <p>This page is currently disabled.</p></pre>
To enable the page, modify the ASPX page and set enableUnlockButton = true.
    </asp:Literal>
    <h1>Remove All Sitecore Users in a Sitecore Role </h1>
    <h2>Use this form to remove all users permanently that are in the selected Domain and Role.</pre>
    <h2>In order for this form to work, it must be placed in the /sitecore/admin folder.&nbsp;</h2>
    <h2 class="Warning">PLEASE BACK UP YOUR CORE DATABASE! </h2>
    <h2 class="Warning">This is NOT reversible!! Please make sure you know what you're doing!!!</h2>
      <table>
          <tr>
           <td style="width:200px;">
             <asp:Label ID="DomainLabel" runat="server" AssociatedControlID="Domain">
              <asp:Literal text="Domain:" runat="server" />
             </asp:Label>
           </td>
           <td>
           <asp:DropDownList AutoPostBack="true" ID="Domain" runat="server" style="width:300;" onselectedindexchanged="Domain_SelectedIndexChanged" />
           </td>
         </tr>
          <tr>
            <td align="right">
             <asp:Label ID="RolesLabel" runat="server" AssociatedControlID="Roles">
             <asp:Literal Text="Roles:" runat="server" />
             </asp:Label>
          </td>
           <td>
             <asp:UpdatePanel ID="UpdatePanel1" runat="server">
              <ContentTemplate>
                <asp:DropDownList ID="Roles" runat="server" />
              </ContentTemplate>
              <Triggers>
                <asp:PostBackTrigger ControlID="Domain" />
              </Triggers>
             </asp:UpdatePanel>
            </td>
          </tr>
          <tr>
            <td>&nbsp;</td>
            <td><asp:Button ID="SubmitButton" runat="server" Text="Submit" OnClick="SubmitButton_Click" /></td>
          </tr>
        </table>
       </div>
      </form>
   </body>
</html>