Blog

0 comments on “Resolve RTE responsive image issue”

Resolve RTE responsive image issue

PIC-Responsive_Web_DesignRecently I found an issue with the Sitecores rich text editor causing responsive images to not size correctly. Whenever an image is edited in Sitecores Rich Text Editor (Even if no height or width has been touched) then it adds:

style="height: 123px; width: 123px;"

Before edit:

<img alt="" height="123" width="123" src="~/media/123" />p>

After edit:

<img alt=""  
src="~/media/123;h=123&w=123" style="height: 123px; width: 123px;" />p>

The problem is that the style height and width added by Sitecore messes with the responsiveness of the image.

Solution:

The best way to remove those attributes in a consistent fashion is to strip the attributes on render of the image by overriding this default pipeline: Sitecore.Pipelines.RenderField.GetImageFieldValue.

Implementation:

First add the following class to your solution:

namespace Custom.Business.SC.Extensions.Pipelines.RenderField
{
    using System;

    using HtmlAgilityPack;

    using Sitecore;
    using Sitecore.Diagnostics;
    using Sitecore.Pipelines.RenderField;

    /// <summary>Class that renders a rich text field with removed image dimensions.</summary>
    public class GetFieldValue : Sitecore.Pipelines.RenderField.GetFieldValue
    {
        /// <summary>The process method.</summary>
        /// <param name="args">The render field arguments.</param>
        public new void Process(RenderFieldArgs args)
        {
            base.Process(args);

            // Do not modify output if the field is not a rich text field,
            // or if the page is in page editor mode
            if (args.FieldTypeKey != "rich text" || string.IsNullOrEmpty(args.FieldValue) || Context.PageMode.IsPageEditorEditing)
            {
                return;
            }

            // stripping dimension attributes from images
            Profiler.StartOperation("Stripping dimension attributes from image field: " + args.FieldName);
            args.Result.FirstPart = this.StripImageDimensions(args.Result.FirstPart);
            Profiler.EndOperation();
        }

        /// <summary>The strip image dimensions.</summary>
        /// <param name="text">The text.</param>
        /// <returns>The <see cref="string"/>.</returns>
        private string StripImageDimensions(string text)
        {
            if (string.IsNullOrWhiteSpace(text))
            {
                return text;
            }

            var outText = text;
            try
            {
                var doc = new HtmlDocument();
                doc.LoadHtml(outText);
                this.StripAttribute(doc, "width");
                this.StripAttribute(doc, "height");
                this.StripAttribute(doc, "style");
                outText = doc.DocumentNode.WriteContentTo();
            }
            catch (Exception)
            {
            }

            return outText;
        }

        /// <summary>The strip attribute.</summary>
        /// <param name="doc">The doc.</param>
        /// <param name="attribute">The attribute.</param>
        private void StripAttribute(HtmlDocument doc, string attribute)
        {
            // HtmlAgilityPack returns null instead of an empty collection when the query finds no results.  
            var nodes = doc.DocumentNode.SelectNodes(string.Format("//img[@{0}]", attribute));
            if (nodes == null || nodes.Count.Equals(0))
            {
                return;
            }

            foreach (var node in nodes)
            {
                node.Attributes[attribute].Remove();
            }
        }
    }
}

Patch in the following config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
            <sitecore>
              <pipelines>
                <renderField>
                  <processor type="Custom.Business.SC.Extensions.Pipelines.RenderField.GetFieldValue,     Custom.Business"
                    patch:instead="*[@type='Sitecore.Pipelines.RenderField.GetImageFieldValue, Sitecore.Kernel']" />
                </renderField>
              </pipelines>
            </sitecore>
    </configuration> 
0 comments on “Automate friendly field descriptions”

Automate friendly field descriptions

58845601

As you may well know when creating and naming fields in Sitecore we also need to create a friendly title for the content editors. By default Sitecore doesn’t make this as easy as it could be and therefore there are a number of naming conventions and solutions for this simple but fundamental issue.

I have always taken the approach of camel casing my field names. It makes sense from a coding perspective. It is a good practice as most developers understand this convention and its benefits. Also when integrating with ORMs such as glass a camel case field name will map directly to the property name of the class without additional mapping attributes.

The obvious issue with using camel case field names is that the default value the content editors see is the field name. Camel case is great for developers but not as good for content editors, so we need to manually write a friendly display description for the editors. This leads to double entry of field name and field description and then keeping this consistent.

Solution:

Auto generate a friendly field description based on the field name by splitting the camel case and adding spaces. For additional information sometimes required we use the help text option.

Example:

Field Name:

PageTitle

Generated Field Description:

PageTitle1

Addition information:

PageTitle2

Implementation:

Tap into the item on saving event and add some basic code to auto generate a display title based on the field name.

namespace Aceik.Framework.SC.Extensions.Events
{
    using System;
    using System.Text.RegularExpressions;

    using Sitecore.Data;
    using Sitecore.Data.Items;
    using Sitecore.Events;

    /// <summary>The item on saving events.</summary>
    public class ItemOnSavingEvents
    {
        /// <summary>The field title name.</summary>
        private const string FieldNameTitle = "Title";

        /// <summary>The field template id.</summary>
        private readonly ID fieldTemplateId = new ID("{455A3E98-A627-4B40-8035-E683A0331AC7}");

        /// <summary>The on item save.</summary>
        /// <param name="sender">The sender.</param>
        /// <param name="args">The args.</param>
        public void OnItemSaving(object sender, EventArgs args)
        {
            var contextItem = Event.ExtractParameter(args, 0) as Item;

            if (contextItem == null)
            {
                return;
            }

            if (contextItem.TemplateID == this.fieldTemplateId && contextItem.Fields[FieldNameTitle] != null)
            {
                contextItem.Fields[FieldNameTitle].SetValue(SplitCamelCase(contextItem.Name), false);
            }
        }

        /// <summary>The split camel case.</summary>
        /// <param name="input">The input.</param>
        /// <returns>The <see cref="string"/>.</returns>
        private static string SplitCamelCase(string input)
        {
            return Regex.Replace(input, "([A-Z])", " $1", RegexOptions.Compiled).Trim();
        }
    }
}

Patch in the following config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
  <events timingLevel="custom">
      <event name="item:saving">
        <handler patch:instead="*[@type='Sitecore.Tasks.ItemEventHandler, Sitecore.Kernel']" type="Aceik.Framework.SC.Extensions.Events.ItemOnSavingEvents, Aceik.Framework.SC.Extensions" method="OnItemSaving"/>
      </event>
    </events>
  </sitecore>
</configuration>

As the description is being auto generated it restricts your ability to add a custom description, however you can add additional information to the display title by adding help text. This blog explains it well: http://firebreaksice.com/sitecore-item-and-field-names/

0 comments on “Sitecore 7.5 highlights”

Sitecore 7.5 highlights

MongoMemeHi, welcome to my Sitecore 7.5 highlights.

This blog post is intended for those of you who would like a high level overview of Sitecore 7.5, need a reminder or have not had the time to investigate this release. At the bottom of this post is a list of useful resources related to Sitecore 7.5.

Highlights

  1. Sitecore 7.5 = Sitecore 7.2 + xDB.
  2. xDB (Experience Database) is the new name for the Analytics database.
  3. New Contact entity allows tracking an individual across devices, visits and cookies.
    • Requires some sort of mechanism of identifying the user( login, form submission, checkout etc)
  4. New scalable analytics data architecture
    • Session DB (private and shared) (inProc, Sql or Mongo)
    • Collection DB i.e. xDB (Mongo)
    • Reporting DB (SQL)
  5. Experience profile (xFile) i.e the contact
  6. xDB Cloud
    • Sitecores cloud hosting offering for xDB
    • Pricing based on numbers of stored contacts.
  7. Mongo DB
    • Data stored in documents not tables
    • Collections of documents rather than tables with rows.
    • Scales horizontally not vertically
    • Data stored in BSON binary Json
  8. Upgrade key points
    • Analytics API has many small changes
    • Migration of data from Analytics DB to xDB i.e. SQL to Mongo
    • Review of hosting structure to suit xDB.
  9. Other name changes:
    • Customer Engagement Platform = Sitecore Experience Platform (Sitecore XP)
    • Digital Marketing System (DMS) = Experience Marketing
    • Email Campaign Manger = Email Experience Manager
    • Engagement Analytics = Experience Analytics
    • Adaptive Print Studio = Print Experience Manger
    • Engagement Automation = Experience Automation

Sitecore 7.5 Resources

0 comments on “Sitecore membership guide”

Sitecore membership guide

join1Below is a core list of commonly used authentication and authorization methods to act like a quick reference guide for people building membership based Sitecore solutions.

This reference is intended to get you on the right track immediately from the one source.

If anyone has anything they would like me to add to the list please comment below and I’ll update it accordingly.

Using

These are the required references for the following examples:

using System;
using System.Collections.Generic;
using System.Linq;
using Sitecore;
using Sitecore.Security;
using Sitecore.Security.Accounts;
using System.Web.Security;
using Sitecore.Security.Authentication;

Retrieving Users

//Get all users regardless of domain, Sitecore, extranet, custom etc:
public IEnumerable<User> GetUsers()
{
return UserManager.GetUsers();
}

//Get all users for a particular domain, in most situations this will be of more use then getting all users:
public IEnumerable<User> GetUsers(string domainName)
{
return UserManager.GetUsers().Where(user => user.Domain != null && user.Domain.Name == domainName);
}

//Get a user from a custom property. This can be useful to find a user by some unique value.
public User GetUserFromCustomField(string fieldName, string fieldValue)
{
return UserManager.GetUsers().FirstOrDefault(user => user.Profile.GetCustomProperty(fieldName) == fieldValue);
}

//Get a collection of users from a custom property. An example might be to get all male users.
public IEnumerable<User> GetUsersFromCustomField(string fieldName, string fieldValue)
{
return UserManager.GetUsers().Where(user => user.Profile.GetCustomProperty(fieldName) == fieldValue);
}

//Again with queries like this it is most likely that filtering by domain name will be more useful then searching all users.
public User GetUserFromCustomField(string fieldName, string fieldValue, string domainName)
{
return UserManager.GetUsers().FirstOrDefault(user => user.Profile.GetCustomProperty(fieldName) == fieldValue && user.Domain.Name == domainName);
}

public IEnumerable<User> GetUsersFromCustomField(string fieldName, string fieldValue, string domainName)
{
return UserManager.GetUsers().Where(user => user.Profile.GetCustomProperty(fieldName) == fieldValue && user.Domain.Name == domainName);
}

//Get user by user name, it must include the domain.
public User GetUserByUserName(string domainName, string userName)
{
return User.FromName(String.Format(@"{0}\{1}", domainName, userName), false);
}

//Get user by email, first get the username for the email then get user by username.
public User GetUserByEmail(string domainName, string email)
{
var userName = Membership.GetUserNameByEmail(email);
return !String.IsNullOrEmpty(userName) ? GetUserByUserName(userName, domainName) : null;
}

//Check if a user exists
public bool DoesUserExist(string domainName, string userName)
{
return User.Exists(String.Format(@"{0}\{1}", domainName, userName));
}

//Get the current context user. This will by default be extranet\Anonymous if no one is logged in.
public User GetCurrentUser()
{
return Context.User;
}

Users Roles

public  IEnumerable<Role> GetUsersRoles(UserProfile profile)
{
return profile.ProfileUser.Roles;
}

public void AddRoleToUser(UserProfile profile, string roleName, string domainName)
{
profile.ProfileUser.Roles.Add(RoleHelper.GetRole(domainName, roleName));
profile.Save();
}

public void RemoveRoleFromUser(UserProfile profile, string roleName, string domainName)
{
profile.ProfileUser.Roles.Remove(RoleHelper.GetRole(domainName, roleName));
profile.Save();
}

public bool IsUserInRole(User user, string role)
{
return user != null && user.IsInRole(role);
}

User CRUD

public void AddUser(string domainName, string userName, string password, string email)
{
Membership.CreateUser(String.Format(@"{0}\{1}", domainName, userName), password, email);
}

public string GetUsersCutomProperty(User user, string fieldName)
{
return user.Profile.GetCustomProperty(fieldName);
}

//Update the Users default sitecore and custom fields
public void UpdateUser(UserProfile profile, string email, string fullName, string customFieldName, string customFieldValue)
{
profile.Email = email;
profile.FullName = fullName;
profile.SetCustomProperty(customFieldName, customFieldValue);
profile.Save();
}

public void DeleteUser(User user)
{
user.Delete();
}

Is authenticated, is administrator

//Method not required but here for completeness
public bool IsUserAuthenticated(User user)
{
return user.IsAuthenticated;
}

//Method not required but here for completeness
public bool IsUserAdministrator(User user)
{
return user.IsAdministrator;
}

Change and reset users password

public void ChangeUsersPassword(string userName, string currentPassword, string newPassword)
{
Membership.Provider.ChangePassword(userName, currentPassword, newPassword);
}

public static string ResetUsersPassword(string userName)
{
var membershipUser = Membership.GetUser(userName);
return membershipUser != null ? membershipUser.ResetPassword() : string.Empty;
}

Authentication

//login without password
public void LoginUser(string domainName, string userName)
{
AuthenticationManager.Login(string.Format(@"{0}\{1}", domainName, userName), false);
}

//default login
public void LoginUser(string domainName, string userName, string password)
{
AuthenticationManager.Login(string.Format(@"{0}\{1}", domainName, userName), password, false);
}

public void LogOutUser()
{
AuthenticationManager.Logout();
}

Roles

//Roles are seperate per domain
public Role GetRole(string domainName, string roleName)
{
return Role.FromName(string.Format(@"{0}\{1}", domainName, roleName));
}

public IEnumerable<string> GetRoles()
{
return Roles.GetAllRoles();
}

public IEnumerable<string> GetRoles(string domainName)
{
return Roles.GetAllRoles().Where(r => r.Contains(domainName));
}