EditContext vs. Item.Editing in Sitecore

So, the other day a co-worker asked me about my thoughts on using the EditContext class as opposed to using item.Editing for editing an item using the API in Sitecore.  I did a lot of research on the 2 and what the diferences are and decided to put this up for either someone to correct me in my conclusion or at least to share my findings in a way that might simplify things for others.

Ok.. so to start, lets start with the basics.  In Sitecore, when you want to edit an item using the API, you first need to put the item in Editing mode. Make your edits and then remove the item from edit mode.  Editing an item, in the simplest possible code would look something like this:

item.Editing.BeginEdit();
item.Fields["Title"].Value = "My New Title";
item.Editing.EndEdit();

Now, as you can see, there’s a method that needs to place the item in the editing mode… plus you have to remember to remove the item from editing mode, plus make sure that the “user” that is doing so has permissions (and either use a securitydisabler or switch user context) as well as some other considerations less apparent than these.  Fortunately, Sitecore has included a class that provides a quick and easy way of doing this.  The above edit can be done instead with this:

using (new EditContext(item)) { item["Title"] = "My New Title"; }

The above statement does the same and more.  First of all, the EditContext constructor takes an item as a parameter and then does item.Editing.BeginEdit().  Also, since its declared in a using statement (since it inherits from IDisposable), the Dispose function is guaranteed to be called which automatically call the item.Editing.EndEdit() using optional boolean params (which I’ll talk about in a second) or the defaults (true, false). No muss, no fuss.

Now, as I mentioned.. there are some optional params.  One is that you can pass it the item with the SecurityCheck.Enable or SecurityCheck.Disable parameter.  Imagine if you wanted to update an item that is a dependency for another item that the user is editing that they do have access to but the dependency item is not one that the user has access to. This allows that to happen without the need to use a SecurityDisabler or switch user contexts.

More importantly, imho, is the other method signature which allows you to pass it 2 bool values that represent “UpdateStatistics” and “Silent”.

  • UpdateStatistics – This just lets you control whether or not the item statistics fields will be updated (when updated, who updated, etc.).  Among other scenarios, imagine a situation where you have a batch process that will reset a flag of some sort on your item, but want to leave the last edited by field to the person who actually did the edit.  Set this flag to false and it simply updates your items without the changing the statistics of the item.
  • Silent – This is a biggie! This lets you disable any of the Events that will be triggered by the edit… such as the Item:Saved event.  This is helpful in situations like the above mentioned batch job.  Say you wanted to update the flag, but don’t want to have that item now added to the publish queue.  Basically, setting this to true will simply update the value without triggering all the other stuff that happens next.

So basically, using the EditContext object does the same but it wraps everything up for you in a nice package.  Sitecore standards prefer the use of the EditContext class as a quick and easy way of editing an item. I think this is probably the case because it handles doing all the stuff that can be forgotten and lets the coder focus on what is to be edited without having to worry about all the other potentially bad things that can be forgotten.  In a nutshell, it still does all the same stuff as the item.Editing class but allows one to do so within a using context which wraps it all up nicely. It’s definitely my choice for making edits to an item using the API.

By the way, be careful with changing the Silent flag.  I recently saw an instance where a call that was being made in the custom method that was run when an item was created.  It checked for a certain value and if that field’s value matched the criteria, it updated another field’s value…. however, the silent flag was set to true with updateStatistics being false.  There were some odd things that happened.  Why they happened is a different story and unrelated, but what it amounted to was a mess where basically the item update triggered a new version to be created and then both item versions set to publish which was an even bigger mess.  You may see the logic in doing this, but as I’ve seen, it caused some unexpected results.

Advertisements

Referencing Code File in Sitecore Presentation

I started this a long time ago but never posted it.  Thought I would share it now…

Let me preface this by saying that this was work from an agency partner that did some work for a client I worked for.  The company had decided to do their site in house… and with good reason.  This was a 3rd party agency that created it originally.  This has allowed me some insight into some new practices that I cannot help but say to myself “You didn’t quite think that one through, did you?”.  I’d like to share a one of those things:

  • Sitecore layout type items should NOT… — EVER — point to the same code file.   For example:  (the following are SitecoreSublayout Items created in the Presentation area)
    • Generic Sublayout item  field:  ascx file =   “/layouts/sublayouts/genericSublayout.ascx”
    • Foo  item field:  ascx file =   “/layouts/sublayouts/genericSublayout.ascx”
    • Bar item  field:  ascx file =   “/layouts/sublayouts/genericSublayout.ascx”

While this may seem like a good idea, this makes it absolutely IMPOSSIBLE for you to know the consequences of making changes/updates to the genericSublayout.ascx file since you have no idea what items are using the code.  The only thing I can think of why they did this is to maybe change the data source.. in which case, I can think of many other ways that are better than having multiple Sitecore items point to the same code file.

BAD CODE MONKEY!  NO DONUT!!

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!

 

SQL 2012 and older versions of Sitecore

So I just wanted to share something.  I have a client who is using 6.2 and SQL 2005.   They want to upgrade to 6.5.  They have SQL 2005 and SQL 2012.  Sitecore officially says that SQL 2012 is not supported for versions less than 6.5.  The option of moving to SQL 2008 , performing the upgrade, and then moving to SQL 2012 was less than fun to consider.  Instead, I tried doing a Sitecore 6.2 install, attaching the 6.2 databases to my SQL 2012 server.  Worked like a charm.  I’d say that for a short term “need to upgrade and don’t want to waste time with 2008” scenario  you can go ahead and try attaching your databases to SQL 2012 and doing the upgrade.  I have yet to do the actual upgrade, but I suspect that it should go just fine.

 

Sitecore and Trusted Connections and AD

The Requirements

So I recently did an install with a client. They had some pretty specific needs when it comes to the security on their sites and I thought I would share a few of the things that I learned along the way.  In fact, much of this appeared to work one way in previous versions of Windows and no longer works the same.

The first thing is to mention that although some of this is already documented in Alex Shyba’s blog post ,  there were gotchas that I didn’t realize and this actually addresses a situation in which your CD server doesn’t have access to the domain.  I thought I’d share this step by step for the people like me who are developers but aren’t network admins and want a straightforward guide.

First thing is to spell out is our specifications.

  • Need to set up both a CM and a CD server on different servers
  • CM server located in the domain and needs to use AD for Sitecore back-end access
  • CD server is going to reside in the DMZ outside the domain with NO POSSIBILITY of setting up a trust
  • We will have a couple of different web databases (one for a preview version of the site), but this is completely optional to configure
  • SQL server is the same for both servers and resides inside the domain network and will be accessible from DMZ on a non-traditional specific port only
  • Security is VERY tight and there is absolutely no possibility of having any connection strings in plain text anywhere in the solution code

Now, as part of the prep, a domain user was created for the purposes of the Sitecore sites.  Our approach was to set things up on the database first.   You can use the Sitecore installer for DB only, but the only access I had was via SQL Management studio remotely logged in with an SA account with anything I needed done directly on the server being done via my verbal instructions.  We opted to just do the install manually, so there may be some back-end sysadmin stuff that was done that I am not aware of, but I’ll try to outline this stuff, step by step.  Here’s what we did.

Set up Database Users

Since there are 2 different instances that will be accessing the database, 1 using Windows Authentication and 1 using SQL Authentication, I configured both users at the same time and with the same permission set (almost).  So the first thing is that the domain user (ad\CMUser) was added as a windows user with limited access to the box.  At the same time, a local windows login was created as well with limited rights, including that they could not change their password and that they didn’t have remote log in rights to the server.  We added both users as SQL logins and mapped out the DB privileges:

CMUser and CDUser –  Core  :   db_datareader, db_datawriter, public, aspnet_Membership_BasicAccess, aspnet_Membership_FullAccess, aspnet_Membership_ReportingAccess, aspnet_Profile_BasicAccess, aspnet_Profile_FullAccess, aspnet_Profile_ReportingAccess, aspnet_Roles_BasicAccess, aspnet_Roles_FullAccess,aspnet_Roles_ReportingAccess

CMUser only – Master  :   db_datareader, db_datawriter, public

CMUser only – Web (Preview) :  db_datareader, db_datawriter, public

CMUser and CDUser – Web (Production) : db_datareader, db_datawriter, public

CMUser and CDUser – Analytics  : db_datareader, db_datawriter, public

We also had to edit each of the databases to provide our users with Execute rights for the databases.

Configuring the Content Management Webserver

The first thing is that you’re going to need to set the ad user with the right privileges on the web server.  The first thing I did was to add this user to the IIS_WPG group.  Now this user group may vary depending on the version of windows and IIS that you’re using, so you may need to check the naming on this.

Next thing was to actually install Sitecore.  This is pretty straight forward and we actually used the installer for this.   Don’t worry too much about the database part of the installer since we’re going to change all of that anyway. I know that it appears to need a valid login, so you can put in the SA or whatever login you have to get passed this part and change it later.  Since this is a Sitecore 6.5 site, we went with setting the app pool to run in Integrated mode using .Net 4.0.

Once the install is done, we’ll need to configure things.  Now, there are 3 parts to why we’re using the AD user for everything.

  1. The first thing is that we want to run the AppPool as a specific user, which places all the security in the hands of IT, rather than having that information accessible to developers.
  2. In order to use a trusted connection when accessing the databases, it uses the app pool identity and windows authentication.
  3. Regardless of database connections, if using AD, you still need to access the LDAP and if the app pool user is an AD user, you don’t need to put user/pass in the provider declaration.

So, to start, in order to give a custom user the proper levels of access to run as the app pool user, we used the aspnet_regiis.exe tool to set the user as a system account.  This is accomplished by running the aspnet_regiis.exe command with the -ga switch and the fully qualified domain name like so:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_regiis.exe -ga ad\CMUser

You should see this where the blurry part is the domain\user name:

To change the app pool user, looking at the properties for the App Pool, click the Advanced Settings and click the button by next to the identity.  You would select the option for Custom and then put in the domain\user along with the  password.  Once you do that, this site will be running in the context of your AD user and since our AD user also has access to the SQL box and databases, we can change the connection strings as well.  They should look something like this given the above server configuration:

<add name="analytics" connectionString="Trusted_Connection=Yes;Data Source=MyDatabase;Database=Sitecore_analytics"/>
<add name="core" connectionString="Trusted_Connection=Yes;Data Source=MyDatabase;Database=Sitecore_Core"/>
<add name="master" connectionString="Trusted_Connection=Yes;Data Source=MyDatabase;Database=Sitecore_Master"/>
<add name="web" connectionString="Trusted_Connection=Yes;Data Source=MyDatabase;Database=Sitecore_Web"/>
<add name="web" connectionString="Trusted_Connection=Yes;Data Source=MyDatabase;Database=Sitecore_Web_Prod"/>

Configuring the Active Directory Module

If you have set things up right, you should now be able to browse the website and log in with the admin user.  Now, let’s get things set up to use the AD module.  The first part is to install the AD package and there’s nothing out of the ordinary about that.  That simply puts the files and items in place and configuring things is will be the next step and is actually very easy when the app pool user is an AD user.  Most of this stuff is covered very well in the AD documentation, but I’ll just touch on some things that aren’t covered and maybe just the steps I took.

The hardest part, I thought, is getting the right connection string.  It’s very helpful to download and install an LDAP browser.  I recommend Softerra.  They have a light weight, free version that actually has live LDAP servers pre-installed for you to browse and get a feel for what you’re looking for.  If you’re on a domain, it also has the ability of looking up what domain you’re on and helping you connect to it so you can see what your own connection string should be.  It’s also very good at helping to create your connection string because it actually maps it out for you and is especially helpful if you want to use custom filters, which is great when your users are in one area and groups are in another and you don’t want to have to pull in the whole directory.

Getting your connection string is as simple as creating a new Profile.  This brings up a wizard.  If you click the ‘Lookup Servers…’  button, it’ll go find the servers that are available to you… which, if you’re logged in to the domain, finds your domain controllers.

Lookup Servers

You might have several to choose from, so you may need to check with a sysadmin, but in my personal experiences, any of them worked.  Once you choose one, you will have to set the base DN.   Click the “Fetch Base DN’s” and it’ll create some strings for you to choose from.  I’d suggest that if you’re not sure which to use, just go with using the one that looks something like your domain with a  DC= before each part of your domain name.  For example,   DC=mydomain,DC=com if your domain is mydomain.com.  After selecting your credentials (you can start off with just choosing the option for the currently logged in user), you can now browse your LDAP and work out the exact connection string.  Now, as I mentioned above, the AD module uses groups and users and if they’re in 2 different areas, just find the highest common denominator for your connections. We can user Custom Filters for filtering the proper groups and users; this is only for a connection to the LDAP.  You can then, if you’re using Softerra, simply right click on the folder for where you want to get the connection string from and choose properties.  At the bottom, you’ll see URL and you can copy that whole part as the connection string.  The key things to remember are that you need to start the connection string with ldap:// , you should also use a fully qualified domain name and port followed by a slash and the rest will be the full path to a location where the users AND groups are at.  It should look something like this where the OU entries are before the DC and are in order that go up the hierarchy.  You would then need to add a connection string for the LDAP server in your connection strings file like this (name is whatever you want to name it and can be your domain or another name of your choice):

<add name="mydomainConnectionName" connectionString="ldap://domaincontroller.mydomain.com:389/OU=MyRegion,OU=Locations,DC=mydomain,DC=com" />

More information on this is available in the AD documentation, but the other relevant bits are the other parts I want to touch on are the membership providers.  Since we’re using the AD user, we can avoid the need to use the user/pass and simply place our declarations in the web.config.  Now there are 2 parts to this and the first is the system.web area starting in the membership area around line 3300 (in a more or less clean config file) and should look something like this.  Notice that my connectionStringName is the name I gave it in the connectionString name declaration.  The provider name does not match and does not have to.  Notice also that in my provider declaration for the Sitecore item, the realProviderName has been changed to be “switcher” rather than SQL.  Notice also that I’m not putting in the full string as defined in the AD documentation as some of that refer to options for resetting passwords and such and I choose to put only the essentials:

<membership defaultProvider="sitecore" hashAlgorithmType="SHA1">
 <providers>
  <clear/>
  <add name="sitecore" type="Sitecore.Security.SitecoreMembershipProvider, Sitecore.Kernel" realProviderName="switcher" providerWildcard="%" raiseEvents="true"/>
  <add name="sql" type="System.Web.Security.SqlMembershipProvider" connectionStringName="core" applicationName="sitecore" minRequiredPasswordLength="1" minRequiredNonalphanumericCharacters="0" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="256"/>
  <add name="switcher" type="Sitecore.Security.SwitchingMembershipProvider, Sitecore.Kernel" applicationName="sitecore" mappings="switchingProviders/membership"/>
  <add name="ad" type="LightLDAP.SitecoreADMembershipProvider" connectionStringName="mydomainConnectionName" applicationName="sitecore" connectionProtection="Secure" attributeMapUsername="sAMAccountName" enableSearchMethods="true" />
 </providers>
</membership>
<roleManager defaultProvider="sitecore" enabled="true">
 <providers>
  <clear/>
  <add name="sitecore" type="Sitecore.Security.SitecoreRoleProvider, Sitecore.Kernel" realProviderName="switcher" raiseEvents="true"/>
  <add name="sql" type="System.Web.Security.SqlRoleProvider" connectionStringName="core" applicationName="sitecore"/>
  <add name="switcher" type="Sitecore.Security.SwitchingRoleProvider, Sitecore.Kernel" applicationName="sitecore" mappings="switchingProviders/roleManager"/>
 </providers>
</roleManager>

Don’t forget this part per the AD module documentation:

The connectionProtection attribute set to Secure requires that you add one more element to the <system.web> section. You can place it anywhere inside this section:

<!-- Machine key attributes -->
<machineKey validationKey="BDDFE367CD36AAA81E195761BEFB073839549FF7B8E34E42C0DEA4600851B0065856B211719ADEFC76F3F3A556BC61A5FC8C9F28F958CB1D3BD8EF9518143DB6" decryptionKey="0DAC68D020B8193DF0FCEE1BAF7A07B4B0D40DCD3E5BA90D" validation="SHA1" />

Configuring the Content Delivery Server

This used to be very similar to setting up the content management server.  This is absolutely NOT the case any longer.  To start off, being outside the domain, per a LOT of research, unless you have some way of setting up a trust relationship (which most domain admins are not willing to do), the only option is a local windows user that has access to the SQL box and then you have to obfuscate your connection information.  There may be some tricks out there or this may work a different way in older versions of Windows, but per Microsoft, if using Windows Server 2008 and SQL Server 2008, the only way recommended approach is the following and I couldn’t get anything else to work.

First, we’re going to create a mirrored local account that matches what we used on the SQL box.  This may or may not be necessary, but we did it this way because in connecting to the SQL box at all via TCP/IP connection on a specific port, we couldn’t seem to get this to work until we used our local SQL account.  If anyone knows why this is, please let me know.  Anyway, we made our 2 SQL accounts the same on the CD and SQL box.  Then, as above, we added this user to the IIS group and ran the aspnet_regiis.exe with the -ga switch for the local SQL user to give it system account permissions.

Next thing was to actually install Sitecore and this is the same as the content manager server.   Then we set the app pool to run as this local identity.  The next thing is that because we had to use a specific port (such as 1436 instead of 1433) and we don’t need to access the master db, in the connection strings, we edited our connection strings so that the port is specified with a comma behind the server name.  You can use an IP address instead of a domain name, by the way, but we went with specifying the domain name and adding an entry in the hosts file so that we could easily change the database that it pointed to on the fly without the need to decrypt the connection string.  It looked like this this to start:

<add name="analytics" connectionString="user id=CDUser;password=myPassword;Data Source=myDatabase.myDomain.com, 1436;Database=Sitecore_analytics"/>
<add name="core" connectionString="user id=CDUser;password=myPassword;Data Source=myDatabase.myDomain.com, 1436;Database=Sitecore_Core"/>
<add name="web" connectionString="user id=CDUser;password=myPassword;Data Source=myDatabase.myDomain.com, 1436;Database=Sitecore_Web_Prod"/>

Next, we run a command that encrypts and decrypts the connection string file with the aspnet_regiis.exe tool.  The -pe and -pd switch or used for encrypting and decrypting, but we also add the additional f to signify that the encryption should be on a file and then the path to the ROOT of the website (do not put the path to the file itself.. just the path to the “website” folder is required).  Now, one thing to mention is that anyone who has access to the file system and knows how to decrypt can run this and see your credentials.  I’m sure I don’t have to tell anyone that it’s best to have a method in place to push files without the need to have developers on the box.

Encrypt

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_regiis.exe -pef "connectionStrings" C:\inetpub\wwwroot\Sitecore\Website

Decrypt

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_regiis.exe -pdf "connectionStrings" C:\inetpub\wwwroot\Sitecore\Website

When it’s encrypted it should look something like this:

<?xml version="1.0" encoding="utf-8"?>
 <connectionStrings configProtectionProvider="RsaProtectedConfigurationProvider">
  <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns="http://www.w3.org/2001/04/xmlenc#">
   <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
   <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
    <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
     <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
     <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
      <KeyName>Rsa Key</KeyName>
     </KeyInfo>
     <CipherData>
      <CipherValue>sDZPMPxsAFllxt35B0vw4a1QqtmO58/HaWIA4BHoPoSY+IYv3XhJ7jy/bgXWy9aOEhUikcWt05m9IJKe3cQu8eqbzVLYIcHx95ZKcKN/RPkY+1Juph3qOcbIAIQQALDDvmHVtoBgeDuB6/ucWK+NiNIqy8bgwNjLPkjI0Skhdn8=</CipherValue>
     </CipherData>
    </EncryptedKey>
   </KeyInfo>
  <CipherData>
   <CipherValue>QBX1tSYhKL3uibyDPCFFKixBxde4btjzu5gR5HdSj+C7riScRA+GmUBsAA/+Ij9Xt13E7Ju7wYpvTilP7akK19Gp3zd4uQX4S1Cbv/nI8yCo7GTjoBLhLTdVBh4ojEoK7PoFJ+7KJQ+0JtD6x9fXlR9r+Cqqv1A6pzOny/1yj6H6YJXgWHlJuuqEFtRyZibmC8sHdFITkTCw97vSNbMLFR2jQfnHAjfsnREh2b/imxCkCuQNh8OP4kJbnLkF2QIGHZQr4CyCodsj40dpGvh96/Wvj4SGuVadinDAbARQlJ0l1mU/9EjpnKoJjF9Uy8yoDJUoyTjYoj2fNI/kxtnR3sLL3TC/a30swPjNX7NstPQpQyfaDFJ2eNtX8JU/xDpMqQlQXkXli5b8W2/lUHd5hFC7ixfb9q6ihQuQYTe4W36eC/KDbHzZLtKdEyr+Fewz5rdLllRpmo/m1KwbHB/CzEF78m1Os1kmD5Dlj6Di0Gw9+GzxhjxywOsonUUp+79eImbE/5F6C2ZHOatTVbTobpOT9izg7Z7C41dC7ch8k/JW8UHRipnfkAmV9YmPa4XvB1QO8L8nK7rDs2cXHbLuKb3Y33w5iFOh+LLtDTogIphQLBjNQFcmuraUu52Rv+YFP0LHp646tnVwBwKGPwl+17yPXCB+gQVYTjsI7LQYcV4Nnv7EZWbxHmqvy/A+pE/y5TJ45yHBWmqIJa3TCn8kf33vvVaLhmgFpJZjXyUww0U=</CipherValue>
  </CipherData>
 </EncryptedData>
</connectionStrings>

Now, all that is left is to test things out and to finish up the process to secure the CD server via the scaling guide.  There’s a lot to that and this article won’t cover it.

Also, as I pointed out before, I did things here based on my own experiences.  There may have been some configuration that was done on the server/network side that I wasn’t aware of, but to the best of my knowledge, this is, step by step, what I did and what worked.  Please feel free to correct something I’ve done or said that should have been done another way.   I’m still learning and just simply wanted to share my experiences.

Happy Sitecoreing!

Advanced System Reporter Customization

As a Sitecore employee, I spend a lot of time doing site reviews.  One of the best ways to get a quick snapshot of things is via the Advanced System Reporter module.  I’ve learned quite a bit about modifying the reports and creating my own using this module.

Adding Fields to Results

The first thing that I found that I didn’t realize was possible is to add in additional fields to a result set.  For example, say you wanted to view the Not Recently modified report. Your base page template has a custom field called “Region” that is used to categorize the which region the content applies to.  You want to follow-up on some of this old content and so you want to be able to see the value in the Region field.  If you edit the report, you can see that it uses the Item Viewer as the viewer for the report.  In the configuration area for the ASR module, under Viewers, if you locate the Item Viewer, you can view that to see the fields it renders.  Opening that, you can see a default list of fields that it renders, but of course, it doesn’t include the Region field. If you click the Edit button in the “Viewer” section to add new fields, the field options available do not include the Region field. As it turns out, you can add this field.. very easily!

Any of the viewer items can be edited to add or remove fields that it shows.  To do this, you can either click the drop-down next to the Column Name and choose a field that represents what you want and then click the Column Header box and give the header a name.  Clicking Add, it’ll add that and you should now see it on the results page.  You can, however, click the drop-down box and making sure that the top value is selected (this is configurable, by the way), you can simply type the name of the field, a name of the Column Header and then click the Add button.  If you need to change or correct it, or add another custom field, after you add the field, click it again and hit delete to clear the value and type a new field name.

A useful thing about this is that you can create your own custom viewer items for viewing specific fields that may be useful in multiple reports.  Then in those reports, you would simply add the new Viewer to the existing viewer on the report.  This will create a combined view of all the column headers in a single report.

Updated Filter

Here’s some simple code I adapted that is similar to the “Created Between” field, but instead allows you to filter by the Updated dates.  I put this in the Filter folder in the ASR.Reports solution.

class UpdatedBetween : BaseFilter
 {
 /// <summary>
 /// Gets from date.
 /// </summary>
 /// <value>From date.</value>
 public DateTime FromDate { get; set; }
/// <summary>
 /// Gets to date.
 /// </summary>
 /// <value>To date.</value>
 public DateTime ToDate { get; set; } /// <summary>
 /// Whether to use the first version
 /// </summary>
 /// <value>Use first version.</value>
 public bool UseFirstVersion { get; set; }
public override bool Filter(object element)
 {
 Item item = null;
 if (element is Item)
 {
 item = element as Item;
 }
 else if (element is ItemWorkflowEvent)
 {
 item = (element as ItemWorkflowEvent).Item;
 }
 if (item != null)
 {
 if (UseFirstVersion)
 {
 var versions = item.Versions.GetVersionNumbers();
 var minVersion = versions.Min(v => v.Number);
 item = item.Database.GetItem(item.ID, item.Language, new Version(minVersion)); 
 }
 DateTime dateUpdated = item.Statistics.Updated;
 if (FromDate <= dateUpdated && dateUpdated < ToDate)
 {
 return true;
 }
 }
 return false;
 }

Items with Template Inheritance

I thought I’d share a couple other things I created.  The first is that none of the parameters allow for choosing items that might use a certain template either as the type or as a base template.  This is a little resource intense, but I had a client who needed this and creating this made them very happy.. so I figure someone else will probably need this too.

The first thing you have to do is create a Template parameter.  This is basically an item selector that will only allow you to choose from templates.  Create a new item under System > Modules > ASR > Configuration > Parameters.  I called mine Template.  The type is going to be Item Selector since we’re selecting a TemplateItem.  I set my default value to the Standard template.  Next, the filter value I set to this so that it will start with the Template root item and then return back a tree for the templates.

displayresult=Name|valueresult=ID|root={3C1715FE-6A13-4FCF-845F-DE308BA9741D}|folder={3C1715FE-6A13-4FCF-845F-DE308BA9741D}|filter=@@templatename='Template' or @@templatename='Folder' or @@templatename='Template Folder'

The first thing we need is to create a custom class that does this.  Here’s the code for my class that I adapted from one of the other scanner classes:

class ContentWithInheritance : BaseScanner
 {
 public readonly static string DB_PARAMETER = "db";
 public readonly static string ROOT_PARAMETER = "root";
 public readonly static string CASCADE_PARAMETER = "search";
 public readonly static string TEMPLATE_PARAMETER = "template";
// This initializes the scan
public override ICollection Scan()
 {
 var databasename = getParameter(DB_PARAMETER);
 var db = !string.IsNullOrEmpty(databasename) ? Sitecore.Configuration.Factory.GetDatabase(databasename) ?? Sitecore.Context.ContentDatabase : Sitecore.Context.ContentDatabase;
// This grabs the item that is chosen as the root item for the items to scan. 
var rootpath = getParameter(ROOT_PARAMETER);
var rootitem = !string.IsNullOrEmpty(rootpath) ? db.GetItem(rootpath) ?? db.GetRootItem() : db.GetRootItem();
// This grabs the template that is selected by the Template parameter in our Scanner Item.
var template = getParameter(TEMPLATE_PARAMETER);
TemplateItem templateItem = !string.IsNullOrEmpty(template) ? db.GetTemplate(template) : rootitem.Template;
// This selects the scope for the scan
Item[] items;
 switch (getParameter(CASCADE_PARAMETER))
 {
 case "0": //children
 items = rootitem.Children.InnerChildren.ToArray();
 break;
 case "1": //descendants 
 items = rootitem.Axes.SelectItems("descendant-or-self::*");
 break;
 default:
 //case "-1": //item
 items = new[] { rootitem };
 break;
 }
var results = new ArrayList();

foreach (Item item in items)
 {
 if (IsTemplateDescendant(item, templateItem))
 results.Add(item);
 }
 return results;
 }
// Here's a simple check that looks at the template and then all the base templates until it finds a match. 
private static bool IsTemplateDescendant(Item item, TemplateItem template)
 {
 return ((item.TemplateID == template.ID) || IsTemplateDescendant(item.Template, template.ID));
}
private static bool IsTemplateDescendant(TemplateItem templateItem, ID itemTemplate)
 {
 if (templateItem == null || ID.IsNullOrEmpty(itemTemplate))
 {
 return false;
 }
 return ((templateItem.ID == itemTemplate) || templateItem.BaseTemplates.Any(baseTemplate => IsTemplateDescendant(baseTemplate, itemTemplate)));
 }
 }

As you might imagine, this is fairly resource intensive, so you may want to limit the items scanned rather than scanning the whole “Content” tree to start.

Next, you’ll need to create a scanner item.  I created one called Items with Template Inheritance.   This will take an Item selector parameter and a Template parameter as we saw in the code.  So the fields look like this:

Assembly:  ASR.Reports

Class:  ASR.Reports.Scanners.ContentWithInheritance

Attributes:  root={Root}|search={Search}|template={Template}

Now you can use this scanner in a report to return back only items that inherit from your chosen template. Now, for those times when you need to find all the items of a certain type, you’re all set.

I hope this was useful for someone.   Happy Sitecoreing!

Custom Access Rights and Workflow Commands

 

So one of my clients had a specific need to allow a specific subset of users to be able to publish stuff directly to the web, bypassing the workflow.  Sounds easy?  Here’s the specifics of their needs:

  • They only have 1 workflow in place for all site(s).
  • Content templates are varied and do not necessarily inherit from a specific template
  • They need to have only some users bypass the workflow only on some items while other items they should only have their regular permissions.
  • The workflow in place is a simple “Editing –  Approval  – Published” 3 step workflow.
  • There is a very large amount of existing content all over a very large site which any major change could potentially have an impact on.
  • The developers for the site are in China and the client is a site admin out of New York with no access at all to the file system or server.

So… here’s my thought process:

This was actually a lot trickier than I had first expected and in digging in to things, I found that there were hidden levels of complexity with the particulars of the solution that I didn’t expect.  Basically I tried to find a way to do this strictly on the front end without the need to add any custom code.  There was just no way that I could think of to do this on the front end only without a risk to the current solution.   I actually tried multiple approaches to find a way to implement this that wouldn’t require access to the backend.

Second Workflow?

This is complex.  There is existing content already published using their main workflow.  New versions of this content would need to use the new workflow.  New versions of the content would ONLY need to use this workflow for some users and not others.  The content template was not the same across the board for their needs. Not only would we need to get the existing content moved to the new workflow, but we’d need to work out some logic to assure that a subset of all newly created items would use the new workflow and override standard values for only a specific group.  This would work, but it’s a potentially complex solution to implement, would require a lot of custom coding and would potentially be difficult to maintain.

Create New Templates?

This assumes that the content in question lives in a single folder.  Another option would be to create a template that inherits from the current template(s) and set the workflow on standard values.  This could mean multiple new templates that need to be created (the folder would have to be inventoried to make sure which ones), but more importantly, it would require changing the template on existing content, potentially breaking it in the process.  This approach, I think is very clunky and would also be difficult to maintain.

Custom Access Right!! 

What I came up with instead is a way to alter the existing workflow to simply add another command that in addition to the “Submit” the appropriate users would also see a “Direct Publish” command along with the “Submit” command.  The new command sets the next state to be “Approved” instead of “Waiting Approval” and proceeds through the workflow as usual to the normal “approved” state.  Since the Approved state has an autopublish action on it, they simply need to get it to the approved state and the workflow does it’s thing.

To control access to this command, we would create a role(s) and then grant only that role the Workflow Command Execute right.  The tricky part of that, however, is to limit when that command is available since that should only be an option on certain items.  The easiest way to do that is to create a new access right, configure which items would have the option to set the right on it (such as by setting a “root” item and then it inherits down to its descendants allowing that right in the security editor for the specific role.  This result is that the only the items specified can have the right set and any other items that it’s not set on or not allowed, will not give access to this additional command.  This approach is completely flexible, it’s extendable to add other subsets of items to have this same permission, and its controlled via standard Sitecore security.

Code now please?

First.. you need to have the access right.  It must inherit from AccessRight

public class DirectPublishAccessRight : AccessRight
 {
public DirectPublishAccessRight(string name) : base(name) { }
public static AccessRight DirectPublish { get { return FromName("direct:publish"); } }
 }

Next, I need to hijack the authorization helper for items so that Sitecore knows to check my access right.  Here’s how I did that:

public class DirectPublishAuthorizationHelper : ItemAuthorizationHelper
    {
        protected override AccessResult GetItemAccess(Item item, Account account, AccessRight accessRight, PropagationType propagationType)
        {
            //This method applies the specified AccessRight.           
            var result = base.GetItemAccess(item, account, accessRight, propagationType);

            // First, lets check to see if the result returns back anything or if it's denied, return since we don't need to do anything
            if (result == null || result.Permission != AccessPermission.Allow)
            {
                return result;
            } 

           //  If the access right isn't our new access right, pass it on to be handled by the base object methods.
           //  From here on out, only checks against the direct publish access will get through.
            if (accessRight.Name != "direct:publish")
            {
                return result;
            }

            //  Now, if the access right is not even applicable to the item for this user, return it as not set.  
            if (!AccessRightManager.IsApplicable(accessRight,item))
            {
                return new AccessResult(AccessPermission.NotSet, new AccessExplanation("Access right not applicable", null));
            }

            //  if the access right gets this far, the permission is going to be allowed. 
            var ex = new AccessExplanation("This item can apply the Direct Publish command");
            return new AccessResult(AccessPermission.Allow, ex); 

        }
    }

Now that we have that done, we have to work out exactly how we’re going to get Sitecore to use the helper class we just created. Here’s how we did that:

public class DirectPublishAuthorizationProvider : SqlServerAuthorizationProvider
    {
        public DirectPublishAuthorizationProvider()
        {
            _itemHelper = new DirectPublishAuthorizationHelper();
        }        
        private ItemAuthorizationHelper _itemHelper;
        protected override ItemAuthorizationHelper ItemHelper
        {
            get { return _itemHelper; }
            set { _itemHelper = value; }
        }
    }

Here’s the access right.  Next I’ll add in the Workflow code that I used to be able to take control of the commands that were visible.  Look for the next installment on that soon!

Please let me know if you have any pointers.  This was a real challenge for me and the lack of documentation on this made it a challenge to implement.

 

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>

HTML5 and Video in Sitecore

Not long ago, a client came to us and asked what sort of support Sitecore had for videos and HTML5.  Now let me preface this by saying that I am NOT a front end developer.  Given the choice, I’d rather pull my toenails out with pliers over writing javascript and doing front end development.  So when a client asked about the support Sitecore had for HTML5, I didn’t have a clue what they were referring to.  That being said, I set out to learn all I could about video and HTML 5.  First impression is that I like it.  In looking over the basics and how it works via the W3 schools HTML5 tutorial, it feels like this is how html should work!.  Now.. that being said, how can/does this fit in with Sitecore?

What is HTML5?

Before we get into that.. lets first define exactly what makes HTML 5 unique when it comes to video.   The first thing to note is that this is NOT like a YouTube embedded video.  It will look like a YouTube style video possibly, however, the major difference here is that you are linking to the direct videos.. not linking to a 3rd party hosted video.  The second thing to note is that because HTML is so new, not all browsers support all video codecs.  So you’re going to have to make some decisions about the videos you’re linking to.  The third thing is that the codecs that are supported by HTML are VERY specific.     Now.. that being said.. what codecs are supported?

  • MP4–  MPEG 4 files with H264 video codec and  AAC audio codec
    •  Support:  
  • WebM –  WebM files with VP8 video codec and Vorbis audio codec
    • Support:  
  • Ogg= Ogg files with Theora video codec and Vorbis audio codec
    • Support:  

So now that we know what formats are supported and by what browsers, the next step for you will probably be to convert the your video. As you can see.. there is no advantage to adding the webm format, so I didn’t include it in my code samples.  I did a search out on the internet and the general consensus seems to be that for a free converter, your best bet would be to go with the Miro Video Converter.  It’s free, easy to use, just pay attention to the fact that not all conversions can be done from all formats.  For instance, mp4 to webm does not work.. but doesn’t warn you.  Once you have your new files.. we’re ready to go! Here’s the basic syntax for the tag:

<video width="320" height="240" controls="controls">
  <source src="movie.mp4" type="video/mp4" />
  <source src="movie.ogg" type="video/ogg" />
  <source src="movie.webm" type="video/webm" />
  Your browser does not support the video tag.
</video>

Integrating this with Sitecore

Looking at the Sitecore modules out there, there isn’t one specifically for this, so I’ve taken it up to write a module to do this and update this when I’m done.  However, until I iron out all the details, here’s how you can do this manually.  The first thing I did was create a new Sitecore template.  I added a File field for each of the file types supported.  Secondly, there are several attributes that you can add to the <video> tag and I added a field for each of these.  Here’s how my Sitecore template is set up.  These are not necessary, but allow for the most freedom in choosing how your video player will display.

Bind to Sitecore Presentation Layer

How we bind to presentation is accomplished any number of ways, but for the sake of showing how it works, I’m going to stick to the easiest and most basic way of doing this. So, we want to bind the new template item with a Sitecore rendering or sublayout. You can do this dynamically by setting the “DataSource” on a sublayout when you select it in the Presentation dialog for your item. You can find more here on how to get the Datasource for a sublayout here. From there, it’s just a matter of binding the fields to your tags. In my example, I created a literal for each of the source tags as well as one for all the attributes that you can use on the

if (this.DataSource != null && !this.DataSource.Empty)
 {
   int h, w;
   StringBuilder sb = new StringBuilder();
   if(Int32.TryParse(DataSource["Height"], out h))
     sb.Append(" height=" + h);
   if (Int32.TryParse(DataSource["Width"], out w))
     sb.Append(" width=" + w);
   if (Helper.GetCheckbox("Controls",DataSource))
     sb.Append(" controls='controls'");
   if (Helper.GetCheckbox("Muting", DataSource))
     sb.Append(" muting='muting'");
   if (MediaManager.HasMediaContent(Helper.GetFileMediaItem(DataSource, "Thumbnail")))
     sb.Append(" poster='" + Helper.GetImageMediaUrl(DataSource, "Thumbnail") + "'");

   attributes.Text = sb.ToString();

    mp4Source.Text = (MediaManager.HasMediaContent(Helper.GetFileMediaItem(DataSource,"mp4Source"))) ?
       "<source src="&quot; + Helper.GetMediaFileLink(<span class=" span="" class="hiddenSpellError" pre="class " />hiddenspellerror="" pre="" />DataSource, "mp4Source") + "" type="video/mp4" />;" : string.Empty;
    oggSource.Text = (MediaManager.HasMediaContent(Helper.GetFileMediaItem(DataSource, "oggSource"))) ?
       "<source src="&quot; + Helper.GetMediaFileLink(DataSource, &quot;oggSource&quot;) + &quot;" type="video/ogg" />;" : string.Empty;
 }
 else
   this.videoPlayer.Visible = false;

Now.. the beautiful part is that Sitecore will actually handle these for your just fine as media library items. You can simply upload your files to Sitecore’s media library and Sitecore will handle the .ashx links (p.s. The “Helper” class is just a personal Sitecore specific helper class to handle common tasks). The last part that is essential to making this work is that you’re going to need to tell Sitecore how to handle these new file types. You can add this to your mimetypes.config file:

  <mediaType extensions="mp4">video/mp4
  <mediaType extensions="ogg,ogv">video/ogg

I’ve tested this and it works great in all the browsers I tried (although you’ll have to use IE9 mode to make it work in IE9). Look for my module here soon. Also, there are quite a few video players out there. I have chosen not to cover those because I found that they vary a lot and the point was to show how Sitecore and HTML 5 video can be integrated. The players offer a lot of flexibility, but at a cost either financially or in a large learning curve. I’ll leave it to you to explore those options if you desire.. but for some simple inline videos.. I find that these work just fine. 🙂

Happy Sitecoring!

WFFM Custom Field Type made easy!

Anyone who has used the Sitecore module “Web Forms for Marketers” knows that although the module is absolutely the coolest thing ever to let marketing people create all their own forms, if you’re a developer and you need to make a form that has a form field with any bit of back-end logic to it, there’s just no way to do any of that with the out of the box form fields.  There is some excellent documentation out on the SDN for creating custom user fields.. however, it’s not complete in that there are 2 types of forms fields – A User control field type (basically an ascx type of file) and a Web Control that you would create from scratch.  The documentation covers creating the web control type of field.  However, I think most people would find it much more useful to create a simple ascx type file to extend a normal field type to allow for back-end business logic to be performed.  This is my purpose for this post.

So basically what I was needing was to be able to take a drop down list, populate it with Sitecore type data..which you can do.. except that this data required a complex query to filter items.. and then the items needed to be sorted.  Sitecore query handles that query very well.  Anyone who has built a template has gotten familiar with this and the module handles that quite well.  The problem is that there just isn’t any way to put an inline command for sorting and WFFM doesn’t have any out of the box way to sort data dynamically generated date in the drop down field type.

The thing to realize is that user control field types really are just a basic user control that extends the WFFM base user control – Sitecore.Form.Web.UI.Controls.BaseUserControl.  Also, in order to use the “Title” that is specified in the form designer, you’ll need to implement the  Sitecore.Form.Web.UI.Controls.IHasTitle interface.

Oh and lemme add this because you’ll need it for the sorting:


public class SimpleComparer : Comparer

{<br />
   // This just compares the items and sorts them by their name.<br />
   protected override int DoCompare(Item item1, Item item2)

   {<br />
      string x = item1.Name;<br />
      string y = item2.Name;<br />
      return x.CompareTo(y);<br />
   }<br />
}</p></pre>

This is easy enough to do.. but since it took me a bit to sort through the WFFM assembly to find out what it was doing, I’ll save you guys some of the trouble.   I tried this in a 6.5 instance with WFFM 2.3 and my control worked beautifully.   Basically your control needs to inherit a few classes and then create your control like you would any other control. And no comments about my choice of coding techniques! I don’t care.. that’s not my point.


    // The control must inherit either &quot;BaseUserControl&quot; for user controls or &quot;BaseControl&quot; for web controls.  I also<br />
    // implement the IHasTitle interface to allow the user to set the title for the field in the form designer diaglog.

    public partial class CustomFormDropdown : Sitecore.Form.Web.UI.Controls.BaseUserControl, Sitecore.Form.Web.UI.Controls.IHasTitle

    {<br />
        // This sets the value of the label to be the value set by the user in the form designer.<br />
        public string Title<br />
        {<br />
            get<br />
            {<br />
                return this.DropLabel.Text;<br />
            }<br />
            set<br />
            {<br />
                this.DropLabel.Text = value;<br />
            }<br />
        }</p></pre>
protected void Page_Load(object sender, EventArgs e)
<pre>
        {<br />
            //create my collection of Sitecore items with a Sitecore query.  This can be done however works best for you.<br />
            //This list, actually, is collecting all the Workflow action items.. for no other reason than that I didn't want to bother creating it<br />
            List selectionItems = new List();

            selectionItems.AddRange(Sitecore.Context.Database.SelectItems(&quot;/sitecore/system/Workflows//*[@@templatename='Command']&quot;));</p></pre>
//Sending item collection off to a comparer class.
<pre>
            SimpleComparer com = new SimpleComparer();

            selectionItems.Sort(com);</p>
<p>            //binding the data to my drop down list item.<br />
            ActionTitle.DataSource = selectionItems;<br />
            ActionTitle.DataValueField = &quot;ID&quot;;<br />
            ActionTitle.DataTextField = &quot;Name&quot;;<br />
            ActionTitle.DataBind();<br />
            ControlContainer.CssClass = this.CssClass;<br />
        }</p></pre>

Now that you have created your user control, the last step is to implement the code that WFFM will need to be able to get the value from our form field. You can also implement some of our custom properties as well. Depending on what you put in this area, you can add additional fields to your form designer.

</p>
<p>        // Properties that are added by the form designer.  These are purely optional and are here to show you that you can do this if you want<br />
        // Notice there are properties such as "DefaultValue" that can be left blank.

        [VisualProperty("Css Class:", 600), DefaultValue ("scfDroplistBorder"), VisualFieldType(typeof(CssClassField))]

        public string CssClass { get; set; }</pre>
&nbsp;
<pre>
<p>        // Properties<br />
        [VisualProperty("Help:", 500), VisualFieldType(typeof(TextAreaField)), Localize, VisualCategory("Appearance")]

        public string Information { get; set; }</p></pre>
// Take note!!  This is the mucho important part!!!
<pre>
        // This is required by WFFM to get the value from the drop down when the user submits the form.

        // This is all the code you need!<br />
        public override Sitecore.Form.Core.Controls.Data.ControlResult Result<br />
        {<br />
            get<br />
            {<br />
                //ControlName is automatically set by WFFM.  Don't set this.

                //The second part is for a string to represent the value of your drop down.<br />
                //The 3rd one is for optional parameters and is completely optional.

                return new Sitecore.Form.Core.Controls.Data.ControlResult(this.ControlName, this.ActionTitle.SelectedValue, null);<br />
            }<br />
        }<br />

Here’s my simple html ascx page.

<p>
<asp:Panel ID="ControlContainer" runat="server">
  <asp:Label ID="DropLabel" runat="server" CssClass="scfDropListLabel" AssociatedControlID="ActionTitle" />
  <asp:Panel ID="DropDownPanel" CssClass="scfDropListGeneralPanel" runat="server">
    <asp:<span class="hiddenSpellError">DropDownList runat="server" id="ActionTitle" CssClass="scDropList" />
    <asp:Label ID="dropDownHelp" Style="display:none" runat="server" />
  </asp:Panel>
</asp:Panel>
<br />
</p>


Now as I said, there were some optional fields that you can set up to show in the form designer. If you don’t do that.. then your form field will have no fields other than the title and analytics which is handled within Sitecore anyway. Once you have your user control saved, go into the /sitecore/Settings/Modules/Web Forms for Marketers/Settings/Field Types/Custom , create a new item using the Field Type template. Then down in the “code” area of the item, put in the link to your control “/controls/CustomFormDropdown.ascx” (without the quotes).

Now, when you create or edit forms, you’ll have your new user control available and it should return its value without incident and you now have a snazzy new form field type for future use.

Please let me know if I’m forgetting anything here..