0 comments on “Advanced System Reporter Custom Reports Part 2”

Advanced System Reporter Custom Reports Part 2

Simple Sitecore Query based Report

For this example, we will build a report which displays a set of results based on a Sitecore Query.

Requirements:

The report will display all items under a fixed path in the content tree and only return items of predefined specific templates. The report query is fixed and doesn’t require any user input via report parameters.

Let’s build it

Step 1: Create a scanner called “Simple Sitecore Query” and add a query as per the image. The query below gets all items under the categories node where the template is one of the three listed types of templates.

ASRpart5.png

Step 2: Create a new report called “Simple Sitecore Query Report” and configure as per the image.

ASRpart6.png

Step 3: Run the report from ASR. Here you can see we have run the report to return all items of the specific templates within the categories node of the content tree.

ASRpart7.png

0 comments on “Advanced System Reporter Custom Reports Part 1”

Advanced System Reporter Custom Reports Part 1

Getting started

Firstly download the install package and the source code. https://marketplace.sitecore.net/en/Modules/A/Advanced_System_Reporter.aspx

ASR reports are easy to configure and most of the time require no custom code to be written.

When a custom report requires coding the easiest way is to follow the examples found in the source code. There are also some filters; scanners etc. which are not in use by the default reports that are very handy and you would not know about them unless you have a browse through the ASR projects.

Basics

ASRpart01

Report: The report item pulls together the configuration elements required for the ASR report: Scanners, Filters, Viewers, and Commands.
Viewer: The viewer item defines the report view, the columns to display etc.
Scanner: The scanner item defines the search query to populate the viewer.
Parameters: The parameter item defines a parameter input control to be used within a report. It defines what type of control and default values to display or select from depending on control type.
Parameter Type: The parameter type item defines a control type to be used by a parameter for a report. The default Parameter types, for example, are Date picker, Dropdown, Text etc.
Filters: The filter item adds filters to the scanner to provide filtered results for the report. Filters have an attribute field which maps a parameter item to a property within your filter class.
Command: The command item adds commands to the report ribbon. The commands can be used to execute functionality on items in the report.

All Items Scanner

The All items scanner is a great utility scanner. It references the QueryScanner which takes a Sitecore query from the attributes field. This allows you to create a report to output the results of any Sitecore query.

ASRpart.png

0 comments on “Missing Sitecore Commerce licenses message”

Missing Sitecore Commerce licenses message

In Sitecore 8.1 you will receive this content warning if you don’t have a valid license for Sitecore Commerce:

“Required license is missing.

You require one of the following licenses to use the Sitecore Commerce Connect module. ‘Sitecore.Commerce.Connect’ or ‘Sitecore.Commerce.ConnectPartner'”

Required license is missing.png

The warning is only useful if you’re intending to use the Sitecore Commerce module, otherwise you need to remove it.

I found this KB post to fix the issue: https://kb.sitecore.net/articles/852031

Here is the patch file you need to add to remove the warning rather than commenting out as suggested above:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>

<!--Remove this warning as commerce module not in use-->
<pipelines>
<getContentEditorWarnings>
<processor type="Sitecore.Commerce.Pipelines.ContentEditorLicenseWarning, Sitecore.Commerce">
<patch:delete />
</processor>
</getContentEditorWarnings>
</pipelines>

</sitecore>
</configuration>
0 comments on “Aceik’s Jason Horne Wins Sitecore “Most Valuable Professional” Award”

Aceik’s Jason Horne Wins Sitecore “Most Valuable Professional” Award

Technology2016Elite distinction awarded for exceptional contributions to the Sitecore community

Melbourne, Victoria, Australia — February 22, 2016 — Aceik, today announced that Jason Horne, Founder and Technical Lead has been named a “Most Valuable Professional (MVP)” by Sitecore, the global leader in experience management software. Jason Horne was one of only 177 people worldwide to be named a Sitecore Technology MVP this year. There are more than 10,000 certified developers in Sitecore’s global network.

Now in its tenth year, Sitecore’s MVP program recognizes individual technology, digital strategy, and commerce advocates who share their Sitecore passion and expertise to offer positive customer experiences that drive business results. The Sitecore Technology MVP Award recognizes the most active Sitecore experts from around the world who participate in online and offline communities to share their knowledge with other Sitecore partners and customers.

Aceik provides a complete range of Sitecore services; architecture, development, integration, support and maintenance, intranets, expert help, existing site audits and upgrades. As Sitecore specialists, Aceik strives to provide excellence in every Sitecore project we  undertake.

Aceik contributes to the Sitecore community through thought leadership content on our blog, the Sitecore community blog and stack overflow. We co-founded the Sitecore Melbourne meetup group, founded the Sitecore ANZ certified developers group and continue to be involved in the ongoing management of these groups with the goal to share knowledge, learn and build the Sitecore community within Australia and New Zealand.

“We are grateful for the leadership, expertise, and ongoing contributions that Jason Horne has made to the Sitecore community,” said Pieter Brinkman, Director of Developer and Platform Evangelism, Sitecore. “Jason Horne has demonstrated a mastery of our technology and exemplifies the spirit of Sitecore.”

Sitecore’s experience platform combines web content management, omnichannel digital delivery, customer insight and engagement, and strategic digital marketing tools into a single, unified platform. The platform is incredibly easy to use, capturing every minute interaction—and intention—that customers and prospects have with a brand, both on a website and across other digital channels. The end-to-end experience technology works behind the scenes to deliver context marketing to individual customers, so that they engage in relevant brand experiences that earn loyalty and achieve results.

Aceik is a boutique web development company specialising in the technical implementation and support of Sitecore solutions. 100% of our work is Sitecore related and we pride ourselves on being experts in our field.

Jason Horne, Founder and Technical Lead
Aceik
jasonhorne@aceik.com.au
0426971867

0 comments on “Using a bucket as a datasource”

Using a bucket as a datasource

download (1)A common scenario requirement these days is to enable content editors to select an item that resides in a Sitecore bucket. The simplest solution is to use a Sitecore multilist with search field.

Another common requirement is to only allow the editor to select one item. In this case we dont have a drop list with search field so again the simplest solution is to use the Sitecore multilist with search field.

First you need to set the source of your field to display items within your bucket of a specific template(s).

Example: StartSearchLocation={11111111-1111-1111-1111-111111111111}&Filter=+_templatename:sample item

Here is an article describing how to set the source of your field: Sitecore 7 field types

If you need to limit the selection to one item for example then you need to also apply some regex. To achieve this you need to enable standards values in the view tab so you can alter the data section.

In the data section add the following regex: ^({[^}]+}\|?){0,1}$ and add some validation text.

Example:

Lqmqh
This article provides additional information:Limit selected items on Sitecore multilist field

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));
}