Wednesday 1 December 2010

Updating indexes on a web farm

Well there is loads of information on this in other blogs. Most refer to the history engine. In principle this works well, but if you want all the servers to present the same content, then this is not always reliable. I have created a service on each server that is called on the publish event that will update the index. You will need to force the service to log in as an admin user.


[WebMethod(Description = "Rebuilds index on webserver", EnableSession = true)]
        public bool UpdateIndex(string itemUri)
        {
            try
            {
                Log.Warn("Update Indexes WS command reached.", this);

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

                Item item = webDB.GetItem(itemUri);
                if (item != null)
                {
                    string username = Sitecore.Configuration.Settings.GetSetting("admin");
                    string password = Sitecore.Configuration.Settings.GetSetting("b");
                    Sitecore.Security.Authentication.AuthenticationManager.Login(username, password);
                    for (int i = 0; i < webDB.Indexes.Count; i++)
                    {
                        //Sitecore.Data.Indexing.Index index = new Index(webDB.Indexes[i].Name);
                        webDB.Indexes[i].UpdateItem(item);
                        Log.Audit(this, "Update search index: {0}", new string[] { webDB.Indexes[i].Name });
                    }
                }

                Log.Warn("Update Indexes completed by WS", this);
                return true;
            }
            catch (Exception ex)
            {
                Log.Error(string.Format("Update Indexes Failed: {0}", ex.Message), this);
                return false;
            }
        }

Thursday 11 November 2010

Creating an application in for the ribbon

To add an application that will be initiated from the context menu in the Ribbon that will enable you to either make changes to the Sitecore item or to an external database, you will need to do the following:

1.       Add an item to the core database
a.       Add an item into /sitecore/content/Applications/Content Editor/Ribbons/Contextual Ribbons/xxxx in the core database
b.      In the Header field add the text that will appear in the ribbon.
c.       In the Icon, add the icon that will appear in the ribbon
d.      In the Click field add the name of the command that is defined in Commands.config e.g. custom:map
2.       Add a line into the commands.config that will initiate your command handler
a.  <command name="custom:map" type="TestProject.SitecoreExtensions.Media.Commands.EditImageAttributes,TestProject.SitecoreExtensions.Media" />
3.       Create a xaml control (see attached)
4.       Create a command
5.       Create the code behind.
Below is a link to sample code. Please note that this is not fully functional. It is just a framework to get you started.

Tuesday 9 November 2010

Programmatically Add/Edit Users

I always find it frustrating when looking at code samples that the references are not included :-). I have included the whole class on how to create, edit and delete users.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Security;

using Sitecore.Configuration;
using Sitecore.Security.Accounts;

namespace Client.Project.Security
{
    /// <summary>
    /// This class will be responsible for:
    /// 1. Adding new users
    /// 2. Editing existing users
    /// 3. Deleting users
    /// 4. Assigning roles
    /// </summary>
    public class UserMaintenance
    {
        /// <summary>
        /// Creates a new user and edits the profile custom fields
        /// </summary>
        /// <param name="domain"></param>
        /// <param name="firstName"></param>
        /// <param name="lastName"></param>
        /// <param name="email"></param>
        /// <param name="comment"></param>
        /// <param name="telephoneNumber"></param>
        /// <param name="jobTitle"></param>
        public void AddUser(string domain, string firstName, string lastName, string email, 
            string comment, string telephoneNumber, string jobTitle)
        {
            string userName = string.Concat(firstName, lastName);
            userName = string.Format(@"{0}\{1}", domain, userName);
            string newPassword = Membership.GeneratePassword(10, 3);
            try
            {
                if (!User.Exists(userName))
                {
                    Membership.CreateUser(userName, newPassword, email);

                    // Edit the profile information
                    Sitecore.Security.Accounts.User user = Sitecore.Security.Accounts.User.FromName(userName, true);
                    Sitecore.Security.UserProfile userProfile = user.Profile;
                    userProfile.FullName = string.Format("{0} {1}", firstName, lastName);
                    userProfile.Comment = comment;

                    // Assigning the user profile template
                    userProfile.SetPropertyValue("ProfileItemId", "{AE4C4969-5B7E-4B4E-9042-B2D8701CE214}");

                    // Have modified the user template to also contain telephone number and job title.
                    userProfile.SetCustomProperty("TelephoneNumber", telephoneNumber);
                    userProfile.SetCustomProperty("JobTitle", jobTitle);
                    userProfile.Save();
                }
            }
            catch (Exception ex)
            {
                Sitecore.Diagnostics.Log.Error(string.Format("Error in Client.Project.Security.UserMaintenance (AddUser): Message: {0}; Source:{1}", ex.Message, ex.Source), this);
            }
        }

        /// <summary>
        /// Edits the user profile and custom fields
        /// </summary>
        /// <param name="domain"></param>
        /// <param name="firstName"></param>
        /// <param name="lastName"></param>
        /// <param name="email"></param>
        /// <param name="comment"></param>
        /// <param name="telephoneNumber"></param>
        /// <param name="jobTitle"></param>
        public void EditUser(string domain, string firstName, string lastName, string email,
            string comment, string telephoneNumber, string jobTitle)
        {
            string userName = string.Concat(firstName, lastName);
            userName = string.Format(@"{0}\{1}", domain, userName);
            try
            {
                Sitecore.Security.Accounts.User user = Sitecore.Security.Accounts.User.FromName(userName, true);
                Sitecore.Security.UserProfile userProfile = user.Profile;
                if (!string.IsNullOrEmpty(email))
                {
                    userProfile.Email = email;
                }
                if (!string.IsNullOrEmpty(comment))
                {
                    userProfile.Comment = comment;
                }

                // Have modified the user template to also contain telephone number and job title.
                if (!string.IsNullOrEmpty(telephoneNumber))
                {
                    userProfile.SetCustomProperty("TelephoneNumber", telephoneNumber);
                }
                if (!string.IsNullOrEmpty(jobTitle))
                {
                    userProfile.SetCustomProperty("JobTitle", jobTitle);
                }
                userProfile.Save();
            }
            catch (Exception ex)
            {
                Sitecore.Diagnostics.Log.Error(string.Format("Error in Client.Project.Security.UserMaintenance (EditUser): Message: {0}; Source:{1}", ex.Message, ex.Source), this);
            }
        }

        /// <summary>
        /// Deletes a user for a particular domain
        /// </summary>
        /// <param name="userName"></param>
        public void DeleteUser(string userName)
        {
            try
            {
                Sitecore.Security.Accounts.User user = Sitecore.Security.Accounts.User.FromName(userName, true);
                user.Delete();
            }
            catch (Exception ex)
            {
                Sitecore.Diagnostics.Log.Error(string.Format("Error in Client.Project.Security.UserMaintenance (DeleteUser): Message: {0}; Source:{1}", ex.Message, ex.Source), this);
            }            
        }

        /// <summary>
        /// Assigns a user to either the User or Superuser role of that particular domain
        /// </summary>
        /// <param name="domain"></param>
        /// <param name="firstName"></param>
        /// <param name="lastName"></param>
        /// <param name="isSuperUser"></param>
        public void AssignUserToRole(string domain, string firstName, string lastName, bool isSuperUser)
        {
            try
            {
                ConfigStore userRolesConfig = Sitecore.Configuration.ConfigStore.Load("config");
                List<ConfigRecord> userRoles = userRolesConfig.RootRecord.GetChildRecords();

                string userName = string.Concat(firstName, lastName);
                userName = string.Format(@"{0}\{1}", domain, userName);
                string domainRole = string.Empty;
                if (isSuperUser)
                {
                    domainRole = string.Format("{0}\\{1}",
                        domain,
                        userRoles.SingleOrDefault(role => role.Attributes["IsSuperUser"] == "1").Attributes["name"]);
                }
                else
                {
                    domainRole = string.Format("{0}\\{1}",
                        domain,
                        userRoles.SingleOrDefault(role => role.Attributes["IsSuperUser"] == "0" && role.Attributes["Access"] == "Allow").Attributes["name"]);
                }
                UserRoles.FromUser(User.FromName(userName, true)).Add(Role.FromName(domainRole));
            }
            catch (Exception ex)
            {
                Sitecore.Diagnostics.Log.Error(string.Format("Error in Client.Project.Security.UserMaintenance (AssignUserToRole): Message: {0}; Source:{1}", ex.Message, ex.Source), this);
            }
        }
    }
}

Programmatically create and delete a domain

This is pretty easy and well documented, but I thought I would add it in anyway as I have added the user and role code. This will give you a full picture. You can add a domain that is administered locally. This means that you can have users that belong to a specific domain and can view and edit users in that domain only. This is particularly useful when you are dealing with lots of microsites.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Sitecore.Configuration;
using Sitecore.Security.Domains;

namespace Client.Project.Security
{
    /// <summary>
    /// This class will be responsible for:
    /// 1. Adding new domains
    /// 3. Deleting domains
    /// </summary>
    public class DomainMaintenance
    {
        public void AddDomain(string domain)
        {
            try
            {
                if (Sitecore.SecurityModel.DomainManager.GetDomain(domain) == null)
                {
                    Sitecore.Context.User.RuntimeSettings.IsAdministrator = true;
                    Sitecore.SecurityModel.DomainManager.AddDomain(domain, true);
                    Sitecore.Context.User.RuntimeSettings.IsAdministrator = false;
                }
            }
            catch (Exception ex)
            {
                Sitecore.Diagnostics.Log.Error(string.Format("Error in Client.Project.Security.DomainMaintenance (AddDomain): Message: {0}; Source:{1}", ex.Message, ex.Source), this);
            }
        }
        public void DeleteDomain(string domain)
        {
            try
            {
                if (Sitecore.SecurityModel.DomainManager.GetDomain(domain) != null)
                {
                    Sitecore.Context.User.RuntimeSettings.IsAdministrator = true;
                    Sitecore.SecurityModel.DomainManager.RemoveDomain(domain);
                    Sitecore.Context.User.RuntimeSettings.IsAdministrator = false;
                }
            }
            catch (Exception ex)
            {
                Sitecore.Diagnostics.Log.Error(string.Format("Error in Client.Project.Security.DomainMaintenance (DeleteDomain): Message: {0}; Source:{1}", ex.Message, ex.Source), this);
            }
        }
    }
}

Programmatically create a Sitecore Role

Again, I have included the whole class to make it easier for you. The config file that I read for contains a list of role types. The example below was used for a client that had multiple microsites. On creation of each microsite, 3 roles were created:
1. User
2. SuperUser (could manage users in their domain)
3. Deny - this role is assigned to all the other user and super user roles, so that only particular roles have access to each microsite.

Hope this helps.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Web.Security;

using Sitecore.Configuration;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Security.Accounts;
using Sitecore.Security.AccessControl;

namespace Client.Project.Security
{
    /// <summary>
    /// This class will be responsible for:
    /// 1. Adding new roles
    /// 2. Editing existing roles
    /// 3. Deleting roles
    /// 4. Assigning access to roles
    /// </summary>
    public class RoleMaintenance
    {
        /// <summary>
        /// Adds a user, superUser and deny role for the domain (these are defined in a configuration file)
        /// </summary>
        /// <param name="domain"></param>
        public void AddRole(string domain)
        {
            try
            {
                //read user roles from config file
                ConfigStore userRolesConfig = Sitecore.Configuration.ConfigStore.Load("Config");
                List<ConfigRecord> userRoles = userRolesConfig.RootRecord.GetChildRecords();        

                string domainRole = string.Empty;
                foreach (ConfigRecord userRole in userRoles)
                {
                    domainRole = string.Format("{0}\\{1}", domain, userRole.Attributes["name"]);
                    if (!Sitecore.Security.Accounts.Role.Exists(domainRole))
                    {
                        Roles.CreateRole(domainRole);
                    }
                }               
            }
            catch (Exception ex)
            {
                Sitecore.Diagnostics.Log.Error(string.Format("Error in Client.Project.Security.RoleMaintenance (AddRole): Message: {0}; Source:{1}", ex.Message, ex.Source), this);
            }
        }
       
        /// <summary>
        /// deletes the user, superUser and deny role for the domain (these are defined in a configuration file)
        /// </summary>
        /// <param name="domain"></param>
        public void DeleteRole(string domain)
        {
            try
            {
                //read user roles from config file
                ConfigStore userRolesConfig = Sitecore.Configuration.ConfigStore.Load("Config");
                List<ConfigRecord> userRoles = userRolesConfig.RootRecord.GetChildRecords();        

                string domainRole = string.Empty;
                foreach (ConfigRecord userRole in userRoles)
                {
                    domainRole = string.Format("{0}\\{1}", domain, userRole.Attributes["name"]);
                    if (Sitecore.Security.Accounts.Role.Exists(domainRole))
                    {
                        Roles.DeleteRole(domainRole);
                    }
                }                         
            }
            catch (Exception ex)
            {
                Sitecore.Diagnostics.Log.Error(string.Format("Error in Client.Project.Security.RoleMaintenance (DeleteRole): Message: {0}; Source:{1}", ex.Message, ex.Source), this);
            }
        }

        /// <summary>
        /// Assign the  roles 
        /// </summary>
        /// <param name="domain"></param>
        public void AssignRoles(string domain, string userType)
        {
            try
            {               

                string domainUserRole= "sitecore\MyRole";
                string parentRole= "sitecore\Author";

               AssignRolesInRoles(parentRole, domainUserRole);
            }
            catch (Exception ex)
            {
                Sitecore.Diagnostics.Log.Error(string.Format("Error in Client.Project.Security.RoleMaintenance (AssignRoles): Message: {0}; Source:{1}", ex.Message, ex.Source), this);
            }
        }


        /// <summary>
        /// Make a role a member of another role
        /// </summary>
        /// <param name="parentRole"></param>
        /// <param name="userRole"></param>
        private void AssignRolesInRoles(string parentRole, string userRole)
        {
            try
            {
                List<string> names = new List<string>();
                if (!RolesInRolesManager.IsRoleInRole(Role.FromName(parentRole), Role.FromName(userRole), false))
                {
                    names.Add(userRole);
                }
                if (names.Count > 0)
                {
                    RolesInRolesManager.AddRolesToRole(RoleList.FromNames(names), Role.FromName(parentRole));
                }
            }
            catch (Exception ex)
            {
                Sitecore.Diagnostics.Log.Error(string.Format("Error in Client.Project.Security.RoleMaintenance (AssignRolesInRoles): Message: {0}; Source:{1}", ex.Message, ex.Source), this);
            }
        }

        /// <summary>
        /// This method allocates access to a particular item and its decendants for the user and super user of that domain
        /// </summary>
        /// <param name="domain"></param>
        /// <param name="roleName"></param>
        /// <param name="itemUri"></param>
        public void EditRoleAccess(string domain, string itemUri)
        {
            try
            {
                ConfigStore userRolesConfig = Sitecore.Configuration.ConfigStore.Load("Config");
                List<ConfigRecord> userRoles = userRolesConfig.RootRecord.GetChildRecords();

                string MyRole= string.Format(@"{0}\{1}",
                    domain,"MyRole");

                Database database = Factory.GetDatabase("master");
                Item item = database.GetItem(itemUri);

                AccessRuleCollection accessRules = item.Security.GetAccessRules();
                Account userAccount = Account.FromName(MyRole, AccountType.Role);

                AccessRight right = AccessRight.FromName("item:read");
                accessRules.Helper.RemoveExactMatches(userAccount, right, PropagationType.Any);
                accessRules.Helper.AddAccessPermission(userAccount, right, PropagationType.Any, AccessPermission.Allow);

                right = AccessRight.FromName("item:write");
                accessRules.Helper.RemoveExactMatches(userAccount, right, PropagationType.Any);
                accessRules.Helper.AddAccessPermission(userAccount, right, PropagationType.Any, AccessPermission.Allow);

                // commit changes
                item.Security.SetAccessRules(accessRules);
            }
            catch (Exception ex)
            {
                Sitecore.Diagnostics.Log.Error(string.Format("Error in Client.Project.Security.RoleMaintenance (EditRoleAccess): Message: {0}; Source:{1}", ex.Message, ex.Source), this);
            }
        }       
    }
}