Quantcast
Channel: Sitecore – Horizontal Blog
Viewing all 88 articles
Browse latest View live

Creating a new Sitecore Rules Macro in Sitecore 8

$
0
0

A long time ago I wrote a post to help developers get started using the Sitecore rules engine.  I’d like to continue that conversation now by demonstrating how to create a new macro for use in your actions and conditions, if you haven’t read the previous entry I strongly recommend doing so now.  The macro used in a condition or rule will control the dialog presented to an end user that is configuring that rule.  There’s a good list and descriptions of them found here.  Macros are referenced in the text field of actions and conditions and will end up looking something like “Where blah blah blah [SearchTerm,NewMacro,/sitecore/content/home,value] blah.”  The […] will be presented to and end user as a link and the contents of the bracket control its action and display.  The first parameter is the property of the class that will be set when the action is executed.  The second parameter is the macro to use, in our case a newly created macro named “NewMacro”.  The third parameter will be passed into the macro and can be used to further control functionality.  These will be converted to a Sitecore Url string and allow you to vary the performance of the macro based on the condition or action it’s used on.  The final parameter is the default value displayed to the content author.

When creating a new macro for use in Sitecore rules the first thing you’ll want to do is create a new macro item in /sitecore/system/Settings/Rules/Definitions/Macros.  You’ll also want to create class in your solution and reference the class in the newly created marco’s Type field.

image

 

The class you create will need to implement Sitecore’s Sitecore.Rules.RuleMacros.IRuleMacro interface.  There’s a single method defined in this interface.  This method is called when a user clicks the macro’s link in the rule editor.  The parameter’s that are passed in provide the xml defining the condition or action, the property name that will be set, the url string specified as the 3rd parameter in the action or condition and finally the current value.

As a proof of concept I’ve created a simple implementation.

public void Execute(System.Xml.Linq.XElement element, string name, Sitecore.Text.UrlString parameters, string value)
{
var options = new SelectItemOptions()
{
Title = "Foo",
Text = "Bar",
Icon = "People/16x16/cube_blue.png",
ResultType = SelectItemOptions.DialogResultType.Id,
Root = Sitecore.Context.ContentDatabase.SelectSingleItem(parameters["root"]),
};

options.IncludeTemplatesForSelection = SelectItemOptions.GetTemplateList(Sitecore.Context.ContentDatabase, new[] { "{E6964C3E-D415-40C8-91D9-2BF90F6566E9}", "{1B6A3702-5694-4FC4-8366-989ECDCD7F1B}", "{826DC4A0-BEB8-4774-8FA7-791F1EC584B4}" });
options.IncludeTemplatesForSelection = SelectItemOptions.GetTemplateList(Sitecore.Context.ContentDatabase, new[] { "{E6964C3E-D415-40C8-91D9-2BF90F6566E9}", "{1B6A3702-5694-4FC4-8366-989ECDCD7F1B}" });
SheerResponse.ShowModalDialog(options.ToUrlString().ToString(), true);
}

 

This macro can now be set up for use on your conditions or actions.  I’ve added a new condition to an existing rule element folder, shown below.

image

 

Once we build our solution we’re all set to go.  Opening the rule editor now shows our new condition using our custom macro.

image

Clicking the “value” link will now execute our NewMacro.Execute(…) method.  Displaying a Sheer dialog using the parameters specified in the condition.  Test content from LaunchSitecore.

image



HI’s Clutch Performance

$
0
0

160125_Devconference-009.png

Imagine our surprise when business analyst/research company Clutch posted a rundown of client feedback regarding Horizontal Integration. It can be scary to know people are talking about you behind your back, but we dove into the detailed analysis with confidence. Here are some excerpts from the reviews that we are particularly fond of.

One of the companies interviewed is a global healthcare company that engaged with Horizontal Integration to tackle a large custom web development project. In response to Clutch’s question, “How do you think Horizontal Integration performed on the project?” the client answered:

There’s a very high bar to begin with, yet Horizontal Integration is one of my top two partners I’ve ever had the pleasure of working with. They bring more to the table than your run of the mill solution partner. They’re not just order takers. They bring extensive acumen and awareness of best practices, of strategy, of business, and of technology, to the forefront.

They’re also very forthright. They don’t nickel and dime you. They have always hit their timelines. If we’ve missed a deadline, it’s because we have done something. There are times that they hop on calls with systems that we’re integrating with merely to share their expertise. It’s not that they’re going to benefit from it and be able to charge us. They look at partnerships holistically rather than one-off transactions. I thoroughly enjoy working with them.

The second company interviewed is a large hearing aid manufacturer that sought to consolidate their many web properties onto a single Sitecore CMS instance. They were asked, “When working with Horizontal Integration, is there anything you would consider unique or special about them compared to some of their competitors?” The client responded:

I would say as a group, they are incredibly accountable. I’ve worked in the interactive world and digital space for a long time. They do what they say they’re going to do. They deliver. They will do absolutely whatever it takes without nickel and diming you to ensure your success. If we ran into a roadblock or if the project took a slight turn, they were very accommodating. They’re extremely accountable. They’re certainly not the least expensive in town, but they’re also not the most expensive. When you look at our investment with them compared to spinning our wheels with other partners in the past, it’s a relationship that we will absolutely continue to maintain.

we_deliver_blue_large
Both clients supplied numbers that quantified our success. The first noted that site traffic increased by 10 times. “It would be easy to say we increased 10 times if we only had 100 visitors,” they said, “but we’re talking about going from a million users to 10 million users.” The other client couldn’t be as specific, but they had this to say: “I can tell you that engagement with the site is up considerably. We have tested the site through focus groups and through third parties. We’ve received great user feedback at this point… When we launched[,] we saw a dramatic increase from the prior site in terms of people coming in, filling out a form, and requesting more information.”

We couldn’t ask for nicer compliments.

But don’t take our word for it. Read the whole thing here.


Congratulations to UnitedHealth Group for Winning Innovation Award

$
0
0

UHG screenshot.png

Horizontal Integration (HI) extends sincere congratulations to the Talent Acquisition team at UnitedHealth Group (UHG) for being awarded the prestigious Innovation Award for their mobile-first candidate experience advancement. The Innovation Award is the most prestigious internal award at UHG. HI is proud to have collaborated together on this project with UHG.

The mobile-first candidate experience project involved creating and implementing a responsive UHG careers site where candidates can learn about and apply for jobs on any device: https://careers.unitedhealthgroup.com/

“Congratulations to our partners and friends at UHG for your hard work, dedication and innovation,” says Chris Staley, EVP of Digital Solutions. “We are honored to work with such an advanced group, and we share in your delight for this prestigious award.”

HI and UHG have been partners for nearly a decade, engaging in complex design and development projects for UHG’s Sitecore-based website. In addition, HI provides staffing needs for Marketing and IT throughout the organization.


Sitecore 8 Content Testing : Start A/B Multivariate Tests using Workflow Commands

$
0
0

Greetings Readers,

I am going to write a quick blog post about starting A/B Test via workflow, In Sitecore CMS version 8.0 and up, Sitecore Content testing has changed quite a bit with array of new functionalities. Up until sitecore CMS version 7.2 you could create and start the test in page editor using a “START” button (See Image Below). But in version sitecore 8.0 you would not be able to do so from the Experience Editor. This post will go through the process of configuring the A/B test via workflow and hence allowing content editors to execute the test from experience editor.

ContentTesting

Create Workflow Command  : Start Multivariate Test

Create a Workflow Command named Start Multivariate Test using a command template (/sitecore/templates/System/Workflow/Command). This command will allow the user to create and start the tests on the selected item

On the newly created command , please fill in the “Appearance Evaluator Type” field with this value “Sitecore.ContentTesting.Workflows.TestCandidatesCommandStateEvaluator, Sitecore.ContentTesting”(See Below) . This value will allow sitecore to show /Hide this workflow steps based on multiple conditions such as Is Content Testing Enabled, Is Automatic Content Testing Enabled ,  Is the test already running, Is the test valid etc.

step2

Create Workflow Action: Launch Create Test Dialog

We will now create a action which will be executed as soon as the user executes the Start Multivariate Test command. Sitecore has a inbuilt wizard built in SPEAK for creating A/B test. Let us see how to configure that

create a action named “Launch Create Test Dialog” based of the a special template called “/sitecore/templates/System/Content Testing/Workflow/Launch Create Test Dialog Action

Type String : Sitecore.ContentTesting.Workflows.LaunchCreateTestDialogAction, Sitecore.ContentTesting

Also check the “Create Suggested Tests If No Wizard” which will create a suggested test if there are no test to be configured on the page by sitecore.

step1

This is all you need to start the multivariate test on a custom workflow. If you need to clean up existing test or remove unwanted tests form you page you can do it as follows

Create Workflow Action: Remove Test

In order to clean up or remove Tests, create a action named “Remove Test” based of the a special template called “ /sitecore/templates/System/Content Testing/Workflow/Remove Tests Action

Type String: Sitecore.ContentTesting.Workflows.RemoveTestsAction, Sitecore.ContentTesting

Check the Delete Test Definition checkbox

step3

Executing this step will cleanup all the test definition which are stored here /sitecore/system/Marketing Control Panel/Test Lab

 

That’s all for now, please comment if you have further questions on the same.

 


Sitecore Dedicated Publishing Instance

$
0
0

Recently while working on a project we configured a dedicated Publishing Instance for Sitecore. The decision to configure it was made looking at the volume of the items that were supposed to be published which were in huge numbers and we do not wanted the content authors to feel the slowness on the content management server while publishing was taking place. Of course it adds up the license cost (good things are never for free).

Here I am sharing a step-by-step (precise) guide about how we configured the publishing instance. DEDICATED PUBLISHING INSTANCE CONFIGURATION has all the necessary information for configuring a dedicated publishing instance. The reason to share this blog post is to have all the necessary steps at one place. Few server roles (Content Management Server and Aggregation or Processing Server if they are defined separately in your environment) gets affected when a dedicated publishing instance is added to the Sitecore ecosystem.

1.Configuration on Publishing Instance

1.1 Ensure Sitecore is installed

1.2 Website/App_config/ConnectionStrings.config file of Content Management and Publishing Instance should be same.

1.3 Create a folder at Website/App_Config/Include/zzz/, copy zzz.DedicatedPublishing-PI.config file to it,It is important to note that all include config files are applied in alphabetical order. Therefore this include file cannot remove indexes that are defined by include files that are applied later. Hence moving the file under folder zzz is ncesessary. This file contains settings from below mentioned files,
– Sitecore.Publishing.DedicatedInstance.config.example
– Sitecore.Publishing.Optimizations.config.example
– Sitecore.Publishing.EventProvider.Async.config.disabled
– Sitecore.Publishing.Parallel.config.disabled

1.4 Make sure that the server time is in sync with the database server and content management server.

1.5 Make sure Sitecore’s keepalive agent is alive and kicking. Copy KeepAlive.config file under Website/App_Config/Include/RJ folder.Open the file and update the value “PI-WebsiteName” with the name of the publishing instance website name
http://PI-WebsiteName/sitecore/service/keepalive.aspx
1.6 Tweak IIS application initialization settings to make sure the publishing instance’s worker can start without the need of a first request. This is done in a two-step manner,

1.6.1 Start worker process without waiting for a request
Open IIS -> Select the Application Pool of sitecore publishing instance -> Open Advanced Settings and change the “Start Mode” value to “AlwaysRunning” as shown in below image.

PI-Image-1

This setting will ensure that a worker process is always running for the application pool. This means that IIS will spin up a worker process when the World Wide Web Service is started, and it will start a new worker process if the existing one is terminated.

1.6.2 Worker process to load application without waiting for a request
Select site-> select Advanced Settings and change the value of “Preload Enabled” to “True”.
Below image is for showing an example about how to set “Preload Enabled” setting the Application Pool will be different for your installation.

PI-Image-2

When a new worker process spins up, Application Initialization will enumerate all of the applications that it will host and checks for this property. For any application where preloadEnabled=”true”, it will build a URL corresponding to the default page for the application and run it through the pipeline. This request does not go through the network, and there is no client listening for a response (IIS discards any data that would have gone to the client.)

1.7 Tweak app pool idle timeout, as you wouldn’t want the publishing server to idle out and restart between publishing jobs and lose all cached data. Change the value of “Idle Time-out(minutes)” to 0 which means infinite.

PI-Image-3

1.8 Disable recycling of App Pool in IIS, Select the app pool for sitecore website-> Advance Settings and change the value of “Regular Time Interval(minutes)” to “0”

PI-Image-4

2.    Configuration on Content Management Server

Content Management server should be made aware about the dedicated publishing instance in order to do so,

2.1 Copy the attached zzz.DedicatedPublishing-CM.config file to Website/App_Config/Include/zzz/ folder,

2.2 Open the file and update “value” to point to the publishing server instance name.

The value should be the name of the IIS Instance and the Website Name of the publishing instance. For e.g the publishing instance value would be “BKR8D72-speakpublish” for a publishing instance shown in below image,

PI-Image-5

Below image is an example about how to determine publishing instance name, it will be different for your sitecore installation.

3.    Configuration on Processing / Aggregation Server

Processing / Aggregation server should also be made aware about a dedicated publishing instance in order to do so repeat the steps mentioned at section “2. Configuration on Content Management Server” of this post.


Sitecore URL Rewrite Backreferences

$
0
0

Last night, my colleague John Kurtis inquired about the Sitecore URL Rewrite module and asked:

Hey – I’m trying to redirect

Downloads/autoupdate/* to the same path on another domain. 

Eg

Downloads/autoupdate/some folder/foo.xml would go to http://www.domain.com/downloads/autoupdate/some folder/foo.xml

How can I do that with the module?

There are 2 kinds of backreferences, each one with its own token format:

  • Rule – {R:n} – pattern matches in the rule
  • Condition – {C:n} – pattern matches in the condition

First, you need to understand regex capture groups.  A regex capture group is signified by a parenthesis in the regex.  If you head over to www.regexr.com and enter /foo(.*)bar/g as the expression, you can see what I’m talking about:

pic1

Here are the matches explained.

When it encounters “foo@demo.net     bar”, the capture group is “@demo.net    “:

pic2

When it encounters “foo.html?q=bar”, the capture group is “.html?q“:

pic3

In the URL Rewrite module, the {R:0} would signify use the rule pattern match’s first capture group.  Remember, this is the information age and things are of a zero-based index.

Using the URL Rewrite module, I can create the following rule to redirect everything to a different domain that is under a specific subpath:

In the rule, specify the pattern as downloads\/autoupdate\/foo(*).  This means to match on url’s where the path begins with downloads/autoupdate/foo and ends with anything.

pic4

In the redirect, specify the url to be http://mynewdomain/mynewpath/downloads/autoupdate/foo{R:0}.  This means to redirect to the new url, but replace the backreference token with the matched capture group from the rule.

pic5

Using my simlulation tool, you can see it work as follows:

pic6

Remember, my module was based on IIS Url Rewrite, so if you have any other questions you can use the IIS Url Rewrite documentation.  Here is the specific link to IIS URL Rewrite back reference configuration.  Alternatively, you can contact me directly.

 


Dynamic Placeholders

$
0
0

Have you ever been asked to create a website that uses completely dynamic content?  Meaning, the same style of content doesn’t always appear in the same place the same way.  There’s one big issue with using a standard Sitecore placeholder.  There’s no way to have multiple renderings that happen to have the same Placeholder Key without it causing issues.  If there are multiple places with the same Key, the datasource will be rendered in the first instance of the Placeholder Key.

I’ve ran into this situation with a few websites within the last couple years.  The clients have wanted a dynamic way to build out a webpage rather than have the layout be dictated by the type of page.  The time that this issue became really apparent was when the client asked for the ability to allow multiple “Multi Column” renderings to a page, but be able to vary where they went and vary what content went into them.  I created a proof of concept for the client with the normal Sitecore Placeholders.  However, once I tried to duplicate the content on the page with the a design file we had for demo purposes, I found out that there were some Placeholder Key collisions since I had multiple “Three Column” renderings.  This pointed me in the direction of needing to implement Dynamic Placeholders.  Dynamic Placeholders are not supported by Sitecore, but there have been a couple places where I’ve implemented other developers’ code and they seem to be working very well in both MVC and Web Forms.  Of course, as with any solution, the logic to get the Dynamic Placeholders to work will vary depending on if you have a Web Forms solution or an MVC solution.  So, click on either link for your solution.

dynamicplaceholderkeyWhat this solution does is take the Placeholder Key (eg. “onecolumncontent”) and append a GUID, usually the GUID of the current Rendering ID, to the end of the Key making it a unique key while not having to create multiple renderings with varying Placeholder Keys.  As of my recent solution, there’s not a way to overwrite the display of the name in Experience Editor for the Web Forms solution, so you’ll end up with toolbars that look like the one I have pictured here.  However, in the MVC solution, the GetDynamicPlaceholderChromeData() strips off the GUID and leaves the display name as your normal Placeholder Key.  If you click into the Presentation Details of a page, you’ll see that these Dynamic Placeholders are also shown in here.

dynamicplaceholderspresentationdetails

One big perk with using both of these solutions is that you don’t need to create new Placeholder Settings for each Dynamic Placeholder.  The code will resolve to the correct Placeholder Setting by stripping off the GUID from the Keys.  Now, in this screenshot, you’ll see that I have both MVC and Web Forms as solutions.  The main reason for this is this client has both MVC and Web Forms in the solution depending on which site is being rendered.  So, the main reasoning is for the Allowed Controls, not necessarily the Dynamic Placeholders.

dynamicplaceholderplaceholdersettings


Page Template vs. Component Site Architecture

$
0
0

In the last couple projects that I’ve worked on, I’ve been faced with a very common question.  How should we architect the solution, using Components or Page Templates?  Components meaning a piece of data that can be used in multiple places and can be rendered anywhere on the page.  Essentially a free way of designing a web page without needing a developer’s intervention.  Page Templates meaning a much more rigid design of the pages that will prevent possible design complications.  There are major pros and cons to both of these options, and most people will end up wanting a combination of the two.

Page Template Architecture

pagetemplatepresentationdetailsThis is one of the more common ways to architect the Sitecore content tree.  Essentially, you create numerous page types depending on what data you want to display on the page.  For example, you’d have a Homepage, a Products page, a Product Detail page, a 404 page, etc.  The perk of the Page Template architecture is that all the fields that are on the Page Template are relevant to the page and only that page.  There should not be any fields that crossover between the different pages that aren’t relevant to that page, assuming you inherited properly.  This also allows for all your data to be entered in one place.  All you’d need to do is create the page in the content tree and enter all your data in that item.  However, a perk and downfall is that a content author does not have control of the look of the page.  That is controlled by the template and layouts associated with that template.  The only choice the content author has is which template to create.

However, there are few downfalls to this architecture.  If you wanted to allow for any additional data to be added to the page, it would require some developer intervention to not only add the field in Sitecore, but to add the ability for the code to render that data.  This architecture also doesn’t make any repeated data easy to content manage.  Each time you created a page, you’d need to re-enter any repeated data.  Unless, of course, it is set in the Standard Values of that template.

Component Based Architecture

componentspresentationdetailsThe biggest perk to having a Component based architecture is the flexibility it provides the content editor.  You can take a blank page, and with the power of a handful of components, you can build a webpage multiple different ways.  Do you want a page that has 2 columns at the top with 1 column below that with a YouTube video in the 1 column and a rich text component in the left column and an image in the right column the 2 column one?  How about on the next page, you want that layout to change up a bit.  You can put 1 column at the top of the page with an image in that while the 2 column can go below it with a YouTube video in the left column and the rich text in the right.  Having a component based architecture allows for that.  It also allows for one page type to be created.  This is also depending on if all pages will have the same basic structure and data.  There may be some exceptions to this if there’s additional data that needs to be added specifically for certain page types.  The content author would create a page in the Sitecore content tree, then would start building out that page in the Experience Editor.  In the Experience Editor, there will be one placeholder that would allow for the structure of content (eg. one column, two column, three column, etc.).  Within that content, there will be additional placeholders that would allow for all the different component types to be inserted into them (eg. image, rich text, YouTube video).

There are also some very noticeable disadvantages for this architecture.  One of the biggest ones is that the content you enter is separate from the page itself.  So, you’ll need to not only create the page, but you’ll need to create the datasource as well.  Another disadvantage is the default Sitecore Placeholders don’t take into account adding multiple of the same placeholder (eg. adding two 2 column renderings that have “leftcolumn” and “rightcolumn” placeholders).  This can be solved by the use of Dynamic Placeholders.  However, Dynamic Placeholders aren’t an out-of-the-box solution from Sitecore.  So, there will need to be some custom work added to enable that ability.

 

hybridpresentationdetailsWhat a lot of people settle on is a hybrid of the two of these architectures.  For example, you have a News Feed page.  This page pulls in a feed of any articles you have published.  The layout of that page shouldn’t change.  However, there can be placeholders placed at the top or bottom of the page to allow for flexible content to be added.  There are even more options that aren’t as obvious.  It could be as subtle as the HTML structure being slightly different between two fully component based pages.  For example, one renders slightly wider than the other.  This would require a different sublayout or rendering which would mean that a developer would need to create a new template in order to add that wider sublayout or rendering to the Standard Values as opposed to the narrower initial sublayout or rendering.



Azure Automation and Sitecore EventQueue

$
0
0

As you may or may not know, SQL Azure (previously known as Azure SQL) is a trimmed down feature set of a full installation of SQL Server.  Two of the features that you will miss out on are Full Text Search and the ability to run SQL Agent jobs.  Nonetheless, SQL Azure is a phenomenal solution for a Platform-as-a-Service (PaaS) SQL.

Sitecore maintains it’s own task scheduling solutions.  However, even if you wanted to run a SQL job instead, you wouldn’t have the ability if you were running SQL Azure.  Recently, we found that we were having performance problems and found that clearing the event queue was our solution.  While a default installation of Sitecore has it’s own agent to cleanup the event queue (Sitecore.Tasks.CleanupEventQueue), we found that this was not sufficient due to the number of events that our particular implementation created.

2016-12-05_1355

 

This default agent runs every 4 hours and leaves events in the event queue that are less than 1 day old.  For our application, 1 day is a very long time and we needed the ability to clear out events at a shorter interval and leave only events from the last hour.

Enter Azure Automation.

We needed the following script to run hourly:

DELETE TOP (500)
FROM eventqueue
WHERE
created < dateadd(hh, -1, getdate()) OR
instancedata like '%virtualindexcrawler%'

Note* the above code loops through a delete operation 500 rows at a time, instead of running a truncate.

Azure Automation allows you to create a Runbook which allows you to execute Powershell scripts in the cloud.  Here is the scripts we came up with:


workflow ProdClearEventQueue
{
inlinescript
{

function EQOpenConnection() {
# Create connection to Core DB
$CoreDatabaseConnection = New-Object System.Data.SqlClient.SqlConnection
$CoreDatabaseConnection.ConnectionString = "Data Source=tcp:SERVER;Initial Catalog=DATABASE;Integrated Security=False;User ID=USER;Password=PASSWORD;Encrypt=True;"
$CoreDatabaseConnection.Open();

return $CoreDatabaseConnection
}

$CoreConnection = EQOpenConnection

function EQSelect($connection) {
# Select the number of remaining records in the event queue
$selectCommand = New-Object System.Data.SqlClient.SqlCommand
$selectCommand.Connection = $connection
$selectCommand.CommandTimeout = 540
$selectCommand.CommandText =
"
SELECT COUNT (1)
FROM eventqueue
WHERE
created < dateadd(hh, -1, getdate()) OR
instancedata like '%virtualindexcrawler%'
"
$selectResult = $selectCommand.ExecuteReader()

while($selectResult.Read()) {
$numRecords = $selectResult.GetValue($1)
}

$selectResult.Close()

return $numRecords
}

# put the number of event queue records into a variable for later use
$selectCount = EQSelect -connection $CoreConnection

function EQDelete($connection) {
# delete 500 rows of event queue rows, leaving those from the last hour
$deleteCommand = New-Object System.Data.SqlClient.SqlCommand
$deleteCommand.Connection = $connection
$deleteCommand.CommandTimeout = 540
$deleteCommand.CommandText =
"
DELETE TOP (500)
FROM eventqueue
WHERE
created < dateadd(hh, -1, getdate()) OR
instancedata like '%virtualindexcrawler%'
"

$deleteResult = $deleteCommand.ExecuteScalar()

}

# execute the deletion in a loop
do
{

EQDelete -connection $CoreConnection
$selectCount = EQSelect -connection $CoreConnection
# output the number of rows
Write-Output $selectCount

} while ($selectCount -gt 0)
# Close connection
$CoreConnection.Close()

}

}

1. To get this to run in Azure, you first need to create an Azure Automation account within your Azure portal.

Create Azure Automation Dialog

2. Once you have done that, you then need to create a Runbook.

runbooks_settings

Create A Runbook part 1

Create a Runbook part 2

Create a Runbook

3. After that, paste the Powershell script previously mentioned.

Edit Runbook

Edit Runbook

 

4. After you Save and then Publish, click on the Schedules.

Click Schedules

Click Schedules

5. Add a Schedule.

6. Link a schedule to your runbook.

7. Create a new schedule.

Create a Schedule

Create a Schedule

New Schedule

New Schedule

After you have added a Schedule for your Runbook, you can look at the status of the runs in the Jobs section.

Jobs

Jobs

You can then click on a job and look at the output.

Output of a Job

Output of a Job

If you wanted to get fancy – and I recommend you do, you can store your credentials and connection string in an Azure Automation Connection asset.


Mongo Backup and Restore

$
0
0

While working on Sitecore at times one can come across a simple task for backing up and restoring mongo databases. Did that made you think? Why me? It should have been done by a DBA. Let’s ignore Mr. Anger from your Inside Out and learn how to do that.

Mad at Mongo

To me the task seemed simple and my initial thoughts were let’s get connected to RoboMongo or Mongo Management Studio, visit any one menu or right click on any one of the mongo database and I will find an option to backup my databases. Well reality is always bitter and I did not have those easy options. At this point of time I was Sad from Inside Out.

Do not know Mongo Backup & Restore

I learned some commands from “Back Up and Restore with MongoDB Tools” and would like to share.

Run the command prompt as administrator and navigate to Mongo/Bin folder in my case the path was C:\Databases\Mongo26\bin. From the command prompt issue below commands,

Backup

1) Backup all databases on the Mongo Instance

mongodump

mongodump

This command will be backup all the databases available on the mongo instance. The backup files will be created under \bin\dump folder

Explorer

2) Specific Database

mongodump --db speakplay_analytics --out c:/Backup/dump/speakplay_analytics
Parameter Description
–db Path of the database for which you want to create a backup
–out Destination path of the backup


Mongo specific database

3) Specific Database behind authentication

mongodump.exe --db sc8prod_analytics --authenticationDatabase admin --username xxxx --password xxxx --out F:\MongoBackup\sc8prod_analytics

In case if authentication is enabled extra parameters needs to be passed on to mongodump command,

Parameter Description
–authenticationDatabase DB in which the user is defined, mostly it would be “admin” database
–username Self explanatory
–password Self explanatory


Authentication
4) Specific Collection

mongodump  --db speakplay_analytics --collection Interactions

If you want to restore specific collection (table) from a mongo db use –collection parameter to do so.

Restore

1) Restore a database

mongorestore --db speakplay_analytics dump/speakplay_analytics/Interactions.bson

2) Restore a collection

mongorestore --collection Interactions --db speakplay_analytics dump/speakplay_analytics/Interactions.bson

This now makes me Joy from Inside Out as I now know Mongo basics.

Happy now


Sitecore Sitemap XML

$
0
0

Sitecore Sitemap XML module is one of the brilliant Sitecore Marketplace module. We have been using it frequently in many of our Sitecore implementations and it works seamlessly. Here I would like to share a side effect of this module which we encountered today. We all have lower environments other than PRODUCTION i.e DEV, QA, STAGING and would wish that the content from the lower environments should not be indexed by any bots. Restricting bots to crawl a website content is all together a different topic and that is not what I wanted to write in this post.

The word of caution is if you are using this module and have not created a robot.txt on your website root directory. Sitecore Sitemap XML will autogenerate robot.txt and place a reference of sitemap.xml into it. It updates robot.txt for every publishing you do as the code is hooked into publish:end event of Sitecore.

There might be a thought in your mind as to why lower environment is accessible over the internet or publicly? Well the environments might be hosted on cloud and in this global village where people work from different part of the worlds the lower environments are required to be accessible from anywhere.

The code / package on Sitecore Marketplace is not updated so would recommend to visit GitHub repository at SitecoreSitemapXML. It also contains a fix made by hloken in his commit, where he introduces a setting in the config file specifying whether to generate / update the robot.txt.

So proud to be part of Sitecore Community where you always find solution to your problems.


Query Sitecore xDB MongoDatabases Part 1

$
0
0

Mongo databases are integral part of Sitecore xDB and they are the main data stores for the Sitecore Experience Platform. Experience Analytics, Experience Profile and Experience Optimization all in a way or other depends on Mongo databases. It always good to have some hands-on queries related to mongo in case troubleshooting or understanding of sitecore analytics data is required. In this blog post I am sharing some ground up work that needs to be done before firing some queries to mongo. It is definitely on my plan to write another part of this blog post to list down basic mongo queries.

Sitecore sends a persistent session cookie “SC_ANALYTICS_GLOBAL_COOKIE” to identify a potential contact or repeated visits from a single user. The value of the cookie is a GUID and can be found into “Contacts” collection of sitecore_analytics mongo database. RoboMongo can be used to connect and fire up a query to filter data. Mongo stores GUID as a value and not string so executing below query in RoboMongo will not fetch any results.

db.Contacts.find({_id:'{3b4b5488-d3a0-40b1-9099-9b896c55abf0}’})

or

db.getCollection(‘Contacts’).find({_id:'{3b4b5488-d3a0-40b1-9099-9b896c55abf0}’})

Mongo has provided javascript helper functions for parsing and displaying UUIDs. CSUUID is one of them which is used to query mongo db for finding a contact. So, your query would look like

db.Contacts.find({_id:CSUUID(‘{3b4b5488-d3a0-40b1-9099-9b896c55abf0}’)})

Below are the steps that needs to be followed for making CSUUID available,

  1. Download uuidhelpers.js.
  2. If .mongorc.js does not exists on your user profile create one.
  3. Update .mongorc.js file by adding load(“C:\\Users\\bpatel\\uuidhelpers.js”); to it. In my case I have placed .mongorc.js and uuidhelpers.js at the same path.
  4. Load .mongorc.js in Robomongo.

    Load Mongorc

  5. Make sure to change Legacy UUID Encoding to Use .Net Encoding.netuuid

    Now firing the query db.Contacts.find({_id:CSUUID(‘{3b4b5488-d3a0-40b1-9099-9b896c55abf0}’)}) will fetch a result. RoboMongo dispalys the type of encoding that is being used,

    luuid_diff

    After the Encoding is switched it displays NUUID aka .NET UUID.

    nuuid_diff
    Point to notice is the GUID too changes as for Legacy encoding it was 88544b3b-a0d3-b140-9099-9b896c55abf0 and for .NET Encoding it is 3b4b5488-d3a0-40b1-9099-9b896c55abf0

Stay tuned for next part of this blog post for some basics of mongo queries.


Query Sitecore xDB MongoDatabases Part 2

$
0
0

Let’s query Mongo Database and fire some queries on collections to see what all Sitecore stores in the collections. We will also try to learn the syntax of mongo queries something similar that we use to do in our academic’s days trying to remember SQL queries as

create table tablename

OR

select * from tablename where fieldname=value orderby fieldname.

This post is a continuation of my previous blog post Query Sitecore xDB MongoDatabases Part 1 where I shared information about how to set up RoboMongo so that it can understand GUID. Here I would be sharing few queries that can be executed from mongo shell but nothing about executing queries from RoboMongo, well this allows me to write a 3rd post in the series where I will show how to query mongo db via RoboMongo.

A point worth noting is Mongo is case sensitive so Find() and find() are different and the former will throw an error saying it is not a function.

Of course, the mongo shell needs to be running from where one can query mongo databases. So how do I run mongo shell?

  1. Open command prompt and change the directory to /mongo/bin
  2. Execute “mongo” and you will be connected to test db.
  3. Execute “use admin”. Mongo shell will switch database to admin and display message “switched to db admin”. “admin” is the database where all information related to security is stored.
  4. Execute db.auth(“username”,”password”) command. Once successfully authenticated mongo shell will display “1”
  5. Now for connecting to the desired mongo db execute “use databasename” statement for e.g use sc82up1play_analytics

mongo_shell

If authentication is not enabled on mongo instance you can skip #3 and #4 steps. Why would one leave a mongo instance vulnerable and be hacked so as a best practice authentication, should always be enabled and if you want to learn how to enable it follow below links,

Let’s start! We now have enough background

find()

To query data from MongoDB collection, you need to use MongoDB’s find() method. It would list all the records from a collection and is equivalent to SELECT statement in SQL.

Syntax of find() method is

db.COLLECTION_NAME.find()

For listing all records from Contacts or Interactions collections of sitecore_analytics database you will fire up,

  • db.Contacts.find()
  • db.Interactions.find()

For getting a record to match a field value syntax would be

db.COLLECTION_NAME.find({“key:value”}) 

Or

db.COLLECTION_NAME.find({“key.key:value”})

if the fields are nested we use dot notation to separate out top-level fields.

find

Examples

db.Contacts.find({“System.VisitCount”:”5″}) where value of “VisitCount” is 5 and it falls under a top level field “System”

db.Interactions.find({“_t”:”VisitData”} ) is an example of top level field.

and in MongoDB

Syntax for and is,

db.collection.find({$and: [{key1: value1}, {key2:value2}]})

Key refers to as fieldname and value is what you would have the record for.

and

Example

db.Contacts.find({$and:[{“System.VisitCount”:5},{“System.Value”:10}]})

or in MongoDB

Syntax for or is,

db.collection.find({$or: [{key1: value1}, {key2:value2}]})

or
Example

db.Contacts.find({$or:[{“System. Value “:5},{“System.Value”:10}]})

sort()

Syntax for sort() is,

db.COLLECTION_NAME.find().sort({KEY:1})

To specify sorting order 1 and -1 are used. 1 is used for ascending order while -1 is used for descending order. If the sorting preference is not specified, then sort() method will display the documents in ascending order. sort() is equivalent to OrderBy in SQL.

sort_ascending

sort_descending

Examples

db.Contacts.find({}).sort({“System.Value”:-1})

db.Contacts.find({}).sort({“System.Value”:1})

If we want to find out the latest record in Interactions collection there are two fields “StartDateTime” and “SaveDateTime” which can be queried,

db.Interactions.find().sort({“StartDateTime”:-1}).pretty()

db.Interactions.find().sort({“SaveDateTime”:-1}).pretty()

limit()

As the name says limit() can be used to limit the number of records to be displayed on mongo shell

Syntax for limit() is

db.COLLECTION_NAME.find().limit(NUMBER)

sort() and limit() can be used to match TOP statement in SQL.

limit-2

limitsort

Examples

db.Contacts.find().limit(2)

db.Contacts.find().sort({“System.Value”:1}).limit(2)

db.Contacts.find().sort({“System.Value”:-1}).limit(2)

db.Interactions.find().sort({“SavedDateTime”:-1}).limit(1)

Here‘s the end of the post and all I have to share on Mongo for now. I will soon be writing another post to talk on the above basic mongo queries in RoboMongo.


Sitecore Email Experience Manager – Installation & Configuration

$
0
0

Let’s get Sitecore Email Experience Manager(EXM) to work. By work I mean let’s install, configure and send some emails from EXM. When I landed on this territory of Sitecore ecosystem I had no idea what it all takes to get EXM working but finally playing with around for few hours I got a hold of it. So, if you are looking to start with EXM with no background knowledge of how to install & configure it this blog post would help you get started.

Download

Latest version of Email Experience Manager can be downloaded from dev.sitecore.net. EXM is bundled up as a Sitecore package which can be installed on any Sitecore instance using Installation Wizard from Sitecore. Once the zip is downloaded point to note here is it is not a Sitecore package, the file name too has a disclaimer attached mentioning (not sc package). It contains many other packages too.

 Email Experience Manager Package

Installation

EXM Installation guide has all the details that are required to install EXM. Here I am going to mention an abstract of what is available in the guide. As a prerequisite before installing EXM module two steps needs to be completed,

1) Add two empty connection strings with the names master and exm.web. For example:

<add name="exm.master" connectionString="" />

<add name="exm.web" connectionString="" />

2) Add the two connection strings CryptographicKey and EXM.AuthenticationKey. The keys must be represented in hexadecimal format by 64 characters, where you can use the symbols 0-9 and A-F. For example:

<add name="EXM.CryptographicKey" connectionString= "E040C938FC9E4EBC3E93330B0F7837F284207B8180DB64CB5B6ABEB1AFBF6F5B" />

&nbsp;

<add name="EXM.AuthenticationKey" connectionString= "9D80B4E56AEE694058567BD89C936FB88F2DB1272A4E88F419B6501919E0BB25" />

Installation guide does mentions a note that should not be ignored,

Note

For security reasons, do not use the example key provided above.

How to generate these keys? Well I used Random.Org.

EXM Keys

EXM Keys 1

Copy the string generated in some text editor, remove all the white spaces and update connectionString value for both EXM.CryptographicKey and EXM.AuthenticationKey. As an example my string now looked like

<add name="EXM.CryptographicKey" connectionString="1acda6a3dd6178045eb13b28e4c5dac2cde2339ded292205743fc495d5edba84" />

<add name="EXM.AuthenticationKey" connectionString="ceb73b557b29f685d4b0ef6b2baeaba78ef539a9903d00adb9ad3783d60d4c7a" />

Let’s swing back to some action now! EXM installation is pretty simple and works like a charm as any other Sitecore package works. Install Email Experience Manager 3.4.1 rev. 170105.zip using Installation Wizard.

Installation will also dump EXM SQL database files so to complete the installation, move the following database files to the /Databases folder and attach them to a SQL Server instance.

o Sitecore.Exm.ldf

o Sitecore.Exm.mdf

o Sitecore.Exm_Web.ldf

o Sitecore.Exm_Web.mdf

I was eager to know what are the visual changes that were made to the content tree after the installation. Hope you too will be looking out for it, Are you?

After successful installation, the first visual change you will notice is on Launchpad which now have Email Experience Manager link under the Marketing Applications.

EXM Launchpad

Content Tree will have a new node “/sitecore/content/Email Campaign” with Message Types and Messages. The module is installed at “/sitecore/system/Modules/E-mail Campaign Manager”

EXM Tree

Configuration

There are few configuration changes that needs to happen before we can start the actual usage of EXM and send out some test emails.

1) Update connectionstrings.config file to have below exm.master and exm.web connection strings. As an example,

<add name="exm.master" connectionString="user id=user;password=password;Data Source=(server);Database=Sitecore_EXM" />

<add name="exm.web" connectionString="user id=user;password=password;Data Source=(server);Database=Sitecore_EXM.WEB"/>

2) In the /App_Config/Include/Sitecore.Analytics.Tracking.config file, ensure the value of the ClusterName setting is set to your instance host name. For example

<setting name="Analytics.ClusterName" value="sc82up2exmplay"/>

3) Lastly setup the message transfer agent. EXM has made two options available for setting up an agent based on client’s need.

  1. Sitecore Email Cloud
  2. Custom SMTP

Sitecore has already provided “The Sitecore Email Cloud compared to the custom SMTP” compassion that will help you to make a decision.

As a playground, it’s better to use Custom SMTP which will leave out licensing issues and subscription to App Centre. On a production environment, you might of course want to configure Sitecore Email Cloud.

Configuring EXM for Custom SMTP

To start using the Custom SMTP:

1) In the Website\App_Config\Include\EmailExperiencefolder, add the suffix .disabled to the end of the following file names:

  • EDS.Providers.Sparkpost.config
  • EDS.Providers.Sparkpost.Sync.config

2) In the Website\App_Config\Include\EmailExperiencefolder, remove the suffix .disabled from the following file names:

  • EDS.Providers.CustomSmtp.config.disabled
  • EDS.Providers.CustomSmtp.Sync.config.disabled

3) Update pop3Settings in EDS.Providers.CustomSmtp.config

4) Update smtpSettings in Sitecore.EDS.Providers.CustomSmtp.Sync.config

Making life better for a developer I have created xml patch files, ready for use that can be dumped into Sitecore instance at path Website\App_Config\Include\zzz

I used Gmail SMTP configuration to send out emails hence these files have all the details related to Gmail Pop3 and SMTP except username and password.

Lastly you might want to put on verbose logging to troubleshoot EXM issues if any faced after installation and while sending emails. To enable it update below setting to true in file Website\App_Config\Include\EmailExperience\Sitecore.ExM.Framework.config

<setting name="EXM.Debug" value="true" />

EXM Now Do Some Work!

Opening Email Experience Manager from Launchpad will take you to below screen where there is not data yet to report.

EXM Report

1) Update Default settings, more information can be found at The EXM default settings

EXM Default Settings

2) Create an email campaign. As can be seen from screen shot there are two types of it. For simplicity, I went ahead creating a Regular email campaign. Looking for more information see Types of email campaigns.

EXM Email Types

EXM has some inbuilt template that can be used while creating a campaign.

EXM default templates

I selected “Modern” as my campaign’s template. Are you wondering how do I get it? It is available in the EXM package as “Email Experience Manager Sample Newsletter 3.4.1 rev. 170105.zip” hence installing this Sitecore package will get a new template into EXM.

EXM templates

3) Creating a campaign is a five-step process. First one is to specify General information about the campaign.

EXM General

EXM reports From Email field to be mandatory if it is not specified

EXM Mandatory Fields

4) Select Recipients, in order to create a list of recipients check Creating A Contact List section of this blog post

EXM Recipients

5) Message Details

EXM Message Details

6) Review: Before sending out actual emails we can review the email by sending it out to our self

EXM Review

I placed myself as the reviewer and was able to get an email as below,

EXM Inbox

7) Delivery: Final step for creating an email campaign. Hitting Send Message starts sending out emails and keeps posted about the activities.

EXM Delivery

Moving back to dashboard now will have has some data to be reported

EXM Dashboard

Creating a Contact List

1) Open List Manager for Sitecore Launchpad

List Manager

2) Hit Create, different options are available to make it quick and easy for me I used Empty Contact List.

List Manager Create

3) Enter information related to Contact List and Hit Save

List Manager Save

4) Click on Contact Drop Down and click on Create and add new contact

List Manager Contact Dropdown

5) Enter Email address, First name and Last name values and Save it.

New Contact

6) Few warnings appears as contact is being created and indexed. Have patience I understand the eagerness to send out emails from EXM.

Contact List Index

7) On refreshing the page added contact will appear on the list

Contact Added

Tips for Troubleshooting Email Sending

1) EXM records all activities in two log files, so checkout for any errors into these log files.

  • Eds.log
  • Exm.log

2) Gmail blocks signing in from unsecure apps which it did for me too. Turning “On” Allow less Secure App setting will allow EXM to send email message vis Gmail SMTP. I received and email from Gmail stating they have blocked access for an unsecure app.

Google Unsecure

Enable App

Other way to enable this setting in Gmail is to,

  • Login into Gmail
  • Go to settings -> Account and Import
  • Click on Other Google Account Settings

Other Account Settings

  • Under Sign-in & Security click Connected apps & sites

Sign-In & Security

  • Enable Allow less secure apps. You may want to turn this setting off once playing around with EXM is completed.

Enable APP Other

Finally EXM is done and dusted. There is more to learn on EXM but whatever is noted down in the blog post helped me get started.


Query Sitecore xDB MongoDatabases via RoboMongo Part 3

$
0
0

In my previous two blog post Query Sitecore xDB MongoDatabases Part 1, I shared information about configuring RoboMongo for making it to understand CSUUID and the second post Query Sitecore xDB MongoDatabases Part 2 informing about executing queries from Mongo Shell. This post would be the last in the series where I would share queries that would be handy to be executed from RoboMongo. One would wonder as to why mentioning queries if already there is a blog post out there for Mongo Shell. There are few differences in the syntax for RoboMongo and Mongo Shell and hence this post.

Differences

The differences that stands out for queries for Mongo Shell vs RoboMongo are,

  • Instead of referring to the collection name COLLECTION_NAME.find() as compared to Mongo Shell, RoboMongo uses getCollection method.
  • GUIDS would have to be enclosed in the call for CSUUID for e.g CSUUID(“7801dccd-6d8c-4c6e-8327-f66a7c773698”)

Similarities

  • Similarity is both Mongo Shell and RoboMongo are case sensitive so .find({}) and .Find({}) are interpreted differently where former is correct to search for a record and the later would result in an error. Case sensitivity holds true for fields names too so finding a record for “ContactID” would get you results but not for “contactid”
  • find, and, or, sort and limit syntax is similar for Mongo Shell and RoboMongo.
  • Fields hierarchy separated by “.”

find()


db.getCollection('Contacts').find({})
Query for GUIDS
 db.getCollection('Contacts').find({"_id" : CSUUID("7801dccd-6d8c-4c6e-8327-f66a7c773698")})

RoboMongo-Guid

Query for data without GUIDS. Fields hierarchy separated by “.”

 db.getCollection('Contacts').find({"System.VisitCount" : 6})

RoboMongo-Field

Note: _id from Contacts collections maps back to ContactId in Interactions collection


db.getCollection('Interactions').find({"ContactId" : CSUUID("7801dccd-6d8c-4c6e-8327-f66a7c773698")})

and


db.getCollection('Contacts').find({$and:[{"System.Value":0},{"System.VisitCount":10}]})

RoboMongo-and

or


db.getCollection('Contacts').find({$or:[{"System.VisitCount":6},{"System.VisitCount":10}]})

RoboMongo-or

sort()

--For descending order.
db.getCollection('Interactions').find({"ContactId" : CSUUID("7801dccd-6d8c-4c6e-8327-f66a7c773698")}).sort({"SaveDateTime":-1})
--For ascending order.
db.getCollection('Interactions').find({"ContactId" : CSUUID("7801dccd-6d8c-4c6e-8327-f66a7c773698")}).sort({"SaveDateTime":1})

limit()


db.getCollection('Interactions').find({"ContactId" : CSUUID("7801dccd-6d8c-4c6e-8327-f66a7c773698")}).sort({"SaveDateTime":-1}).limit(3) can be used for fetching top 3 records.


Sitecore UserName & Password Hardening

$
0
0

All organizations have processes & procedures in place to reduce the surface of vulnerability. One of the way to reduce attacks typically includes having a password policy and Sitecore Experience Platform is no exception to it. The way by which one installs Sitecore and configure has a significant effect on the security of a website. Sitecore has in place a fair amount of documentation related to Security Hardening and Sitecore Security Hardening Guide. Recently I was working on a Request For Proposal (RFP) which had lot of questionnaire related to password policy, I was aware that password policy can be enforced for Sitecore but was not satisfied that I have complete details and specifics to implement the password policy.

This blog post is about a voyage “Sitecore Username & Password Hardening” during which I drilled down the information for various settings and options available in Sitecore related to username and password policy implementations. If you are interested to harden a Sitecore implementation join me on this voyage and keeping reading the blog post further.

There are good number of documents & blogs, see “Nice Reads” section of this post for all such details.

Let’s start with some fundamentals to be aware about,

  1. Sitecore uses .NET security engine and is built on top of .Net Membership Provider
  2. All information related to roles, users, passwords and access is stored into “core” database. Hah! Why am I writing this should this not be known? Well trying to share information and making sure I cover all audiences for the post from including starters, mid-level and experienced Sitecore geeks.
  3. It is possible to replace or extend default configurations by writing a custom provider.

Let’s start with some specifics now,

UserName

Jason’s post USING EMAIL ADDRESSES AS YOUR SITECORE USERNAME is almost a clone of Brian’s post Sitecore allow email adress as user name

Rhys Godfrey’s post Sitecore–Changing the Username validation rule is all you have to understand for setting by UserName Validation.

I do not want to bombard duplicated information.

Password

Q. Have you been asked to setup and enforce password policy?

Q. How to configure it for Sitecore?

Q. Where to start?

A. Here’s the answer, as said earlier Sitecore is built on .Net Membership provider start by looking <configuration>/ <system.web>/<membership> section of a web.config file on your Sitecore installation.

Q. What I need to look for?

A. The “sql” provider and the attributes of the provider.

Attributes Value Type
minRequiredPasswordLength int
minRequiredNonalphanumericCharacters int
requiresQuestionAndAnswer bool
requiresUniqueEmail bool
maxInvalidPasswordAttempts int
 passwordAttemptWindow int
enablePasswordRetrieval bool
passwordFormat string
passwordStrengthRegularExpression string

 

  • minRequiredPasswordLength : The attribute name itself is self-explanatory, it allows to specific the minimum number of character a password should have. The default value is 1 and of-course it cannot be 0 otherwise you will see below error,

1) minRequiredPasswordLength

What happens when some tries to setup password where length is less than minimum required?

Sitecore displays message and makes you aware of possible reasons,

Reasons

  • minRequiredNonalphanumericCharacters: Specify minimum number of non-alphanumeric characters a password must contain.
  • requiresQuestionAndAnswer: Sitecore do not support this attribute and setting it to true will not allow for creation of a new user.

3) requiresQuestionAndAnswer

  • requiresUniqueEmail : Validates if there is already another user with same email address or not. If found New User will not be created and a message will be displayed.

4) requiresUniqueEmail

  • maxInvalidPasswordAttempts: User will be locked out after sur-passing the number of times an incorrect password is specified for login to Sitecore.

Sitecore login page do not specifically mentions about the user being locked out.

maxInvalidPasswordAttempts

The information for a “Locked Out” user is available in User Manager,

locked out

  • passwordAttemptWindow


The PasswordAttemptWindow property works in conjunction with the MaxInvalidPasswordAttempts property to help guard against an unwanted source guessing the password or password answer of a membership user through repeated attempts. When a user attempts to log in with, change, or reset his or her password, only a certain number of consecutive attempts are allowed within a specified time window. The length of this time window is specified in the PasswordAttemptWindow property, which identifies the number of minutes allowed between invalid attempts.

If the number of consecutive failed attempts that a user makes to reset his or her password equals the value stored in the MaxInvalidPasswordAttempts property, and the time elapsed since the last invalid attempt is less than the number of minutes specified in the PasswordAttemptWindow property, then the membership user is locked out. The user is locked out by setting the IsLockedOut property to true until the user is unlocked by a call to the UnlockUser method.

If the interval between the current failed attempt and the last failed attempt is greater than the PasswordAttemptWindow property setting, the current invalid attempt is counted as the first. If a valid password answer is supplied before the maximum number of allowed invalid attempts is reached, the count of invalid password-answer attempts is set to 0 (zero). If a valid password is supplied before the maximum number of allowed invalid attempts is reached, the count of invalid password attempts and the count of invalid password-answer attempts are set to 0 (zero).

So for example if the values set for the attributes are maxInvalidPasswordAttempts=”5″ passwordAttemptWindow=”1”. A user will be locked out after he makes 5 unsuccessful attempts. If the user has made less 4 unsuccessful attempts and then he/she tries after “passwordAttemptWindow” (in our example 1 minute) the user will not be locked out as the attempt would have been set to 0 counter.

  • enablePasswordRetrieval
  • This attribute holds a value indicating whether the current membership provider is configured to allow users to retrieve their password. Default value is false and if you turn it on you might see below error as this attribute works in conjunction with passwordFormat.
  • enablePasswordRetrieval
  • passwordFormat
  • This attribute describes the encryption format for storing passwords for users. The values can be,
    Attribute Value Description
    Clear Not secure, do not use. Passwords are not encrypted.
    Encrypted Not secure, do not use. Passwords are encrypted using the encryption settings determined by the machineKey Element (ASP.NET Settings Schema) element configuration.
    Hashed Passwords are encrypted one-way using the SHA1 hashing algorithm.

    Hashed is the default one!

    How does it looks in Membership database (MS SQL) when these formats are changed?

    Below image is for “Clear” format

  • Clear
  • Below image is for “Encrypted” format
  • Encrypted
  • passwordStrengthRegularExpression
  • The PasswordStrengthRegularExpression property gets the regular expression used to evaluate password complexity from the provider specified in the Provider property. This is one of the most important attribute that would allow to implement strict password policy and set various rules related to passwords like,
    • Is greater than seven characters.
    • Contains at least one digit.
    • Contains at least one special (non-alphanumeric) character.

    All the above attributes are considered while a user is being setup into Sitecore from User Manager. Sitecore Login page ignores these attributes and just validates if the username and password is correct which make sense as otherwise consider a case that users were already setup and later on password policy was enforced to have at least one non-aplhanumeric character. Such users will not be able to login even if their password matches to whatever it’s is stored in database.

    I collected information related to sql provider from Membership Class and if you are looking for more information here is the place where you should watch out for.

    Nice Reads


Keeping Item and Field Names Unique

$
0
0

There are many things that can help with confusion as a developer and as a content author.  One of the biggest confusions I’ve come across has been duplicate item names, fields, and placeholders.  Sitecore will render whatever the first instance of that name is.  So, if you have an item with the same name as another, it will render the one higher up in the tree.  The same idea expands to placeholders and field names as well.  There’s a way to work with the placeholders and not be required to create multiple placeholders that have the same function called “Dynamic Placeholders“. However, the other two require some discipline as a developer and a content author.

As a content author, to avoid general confusion, giving unique names to items will avoid a possible situation where content you’ve entered is not showing up properly or not at all.  I’ve run into a few situations where the client has asked me to troubleshoot why the content they’ve updated was not displaying like they were expecting.  When I started troubleshooting, I noticed that there were items that had the same name.  It turned out they were editing the lower of the items which meant the rendered content wasn’t actually updating.  In addition, I had also noticed that other renderings were missing content entirely.  After looking into this situation, I found out that there were different templates that were utilizing the same name as well.  So, the content author added a Rich Text block to the page and correctly pointed the Datasource.  However, the item above it was an Accordion template that shared the same name.  Since Sitecore uses the path to get to the Datasource as opposed to a GUID, the rendered page was trying to render the Accordion template in the Rich Text rendering, which didn’t render properly.  One of the easiest ways to do this is to either put all items that belong together in a folder or name each one differently but name them in a way that a content author will understand that they go together (see green highlighted area).

As a developer, however, one of the things that will avoid general confusion for content authors is to name your template fields well.  Naming something “Content” can work if you’re creating a template that’s going to be inherited in to multiple places.  However, if you are creating a field specifically for a template type, it’s worth naming your fields more specifically.  As you see in the screenshot, there are two fields named “Page Title”.  One field is being inherited while the other field was added to the template itself with the same name.  This means, the first one will be the field that gets rendered while the second one is about as useless as it can be.  To avoid these types of situations, it’s good to plan out your templates before you create them to see what fields are being inherited and what fields you’ll need to create.


Keeping Your Sitecore Tree Organized

$
0
0

There’s one big thing that should be on everyone’s mind when working with a lot of content.  That big thing is organization of the Sitecore content tree.  With a single site solution, organization can be a little less serious.  But, once you start to deal with a multi-site solution, organization is key.  There are so many different ways for a content tree to go from being very cleanly organized to being one of the most confusing things to try and navigate.  Admittedly, I like my content tree like how I have my folders on my computer, very organized.  That way, when you need to find something quickly, it is very easy.  If it’s disorganized, then it ends up being a longer process to find the item.  Which, multiplied by however many components you have on a page, could really affect how quickly you can create a site.  So, when I see a Sitecore tree that seems to have items spread out all over the place, I want nothing more than to help the client clean it up and give them the tools to keep it clean.

Component Tree 1

Let’s take a look at this screenshot.  In this multi-site solution, we have created components (or modules) to be added separately to the page allowing the content editor to essentially be able to build out a webpage from a blank slate.  So, due to the number of components that will be created, it is important to keep the components folder organized in order to quickly find the datasource you’re looking for.  As a developer, when setting up a components folder, one of the easiest, but more restrictive, ways is to create folders yourself and only allow the content editor to insert that type of component in that folder (eg. Creating a Rich Text folder and only allow a Rich Text component to be inserted below it).  This isn’t the way I wanted to implement this organization strategy since I didn’t exactly know if the client wanted them to be organized by site, by component type, by both, etc.  This way also doesn’t easily lend itself to growth since it’s more restrictive for the content editor.  So, a more flexible way to implement this is to create a new folder template and give all your components as an insert option to this folder.  But, the key step here is to also add the same folder template as an insert option as well.  This will allow the content editor to make folders as they please and organize the Sitecore tree to how it’ll make sense to them.

Component Folder Insert Options

Now, these were all ways as a developer to help the content editor.  But, a content editor should utilize these tools that the developer has implemented.  If a developer hasn’t implemented a good way to organize the Sitecore tree, it is absolutely worth asking.  This will only help out with confusion in similarly named components, identically named components that are different templates, etc (as mentioned in Keeping Item and Field Names Unique).  As a content editor, it is also worth thinking about how you want your data structured in Sitecore before you start building out your content.  This way, you could build out your folder structure logically before adding any sort of content.  If you don’t want the ability to create folders of your own, but want the restriction of only being able to add a specific component(s) under a certain folder, work with your developer and implement it together.  This way, you’ll have a way to organize your data that you’ll be happy with.  Personally, I’m a fan of giving the content editor the ability to structure their data as they please.  This allows the solution to easily expand if additional sites are added.  This also gives the ability for the content editor to change their mind about how the tree is structured.  Let’s say the content editor just wanted all the components to be grouped together regardless of site.  Well, after the content editor starts to get hundreds of components, that organization process may not be sustainable anymore.  Instead of needing to go to the developer and paying to have an additional ability to change how the content tree is organized, that ability is already there and the content author can alter that item structure without any negative effects to the site.

Component Tree 2

In the end, as a developer, the most important thing is to deliver a solution that the client is happy with.  Knowing full well that there will be some complex data that will need to be made easier for a content editor to work with, organization is key.  As you can see in the “Component Tree 2” screenshot, I have organized the components from the “Component Tree 1” screenshot into easier to understand folders by using the same insert options as shown in the “Component Folder Insert Options” screenshot.  If there are even more components, and there still are too many items under each folder, you can easily add another level of the site that they’re created for.  But, for this situation, just doing the one level is enough.  It is all dependent on how much content is going to be going into each site and how easily it is to understand where to find those items.

 

 

 


How to create an Experience Editor default rendering button

$
0
0

I recently had a client request to speed up the process of adding a component to multiple language version of an item. Rather than going to each language version and adding a component to a placeholder, I created a default rendering button to set up the component on one language version of the item and copy it to all the other language versions.

The first thing I did was navigate to /sitecore/content/Applications/WebEdit/Default Rendering Buttons in the core database and added an item based off the /sitecore/templates/System/WebEdit/WebEdit Button template called Copy to other language versions.

Copy to other language versions

In order to get the click I had to do two more steps. First, I added a command to call into my custom code.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <commands>
      <command name="webedit:CopyToOtherLanguageVersions" type="Hi.Shared.BrentsCoolCopyRendLangVersModule.CopyToOtherLanguageVersions, Hi.Shared.BrentsCoolCopyRendLangVersModule"/>
    </commands>
  </sitecore>
</configuration>

Next, I changed the /sitecore/shell/Applications/Page Modes/ChromeTypes/RenderingChromeType.js and added a new runcommand so I can pass get the renderings unique id in my code. I first changed the javascript here:

handleMessage: function(message, params, sender) {
    switch (message) {
      case "chrome:rendering:sort":
        this.sort();
        break;
      case "chrome:rendering:properties":
        this.editProperties();
        break;
      case "chrome:rendering:propertiescompleted":
        this.editPropertiesCompleted();
        break;
      case "chrome:rendering:delete":
        this.deleteControl();
        break;
      case "chrome:rendering:morph":
        this.morph(params);
        break;
      case "chrome:rendering:morphcompleted":
        this.morphCompleted(params.id, params.openProperties);
        break;
      case "chrome:rendering:personalize":
        if (Sitecore.PageModes.Personalization) {
          this.personalize(params.command);
        }
        break;
      case "chrome:rendering:personalizationcompleted":
        if (Sitecore.PageModes.Personalization) {
          this.presonalizationCompleted(params, sender);
        }
        break;
      case "chrome:personalization:conditionchange":
        if (Sitecore.PageModes.Personalization) {
          this.changeCondition(params.id, sender);
        }
        break;
      case "chrome:rendering:editvariations":
        if (Sitecore.PageModes.Testing) {
          this.editVariations(params.command, sender);
        }
        break;
      case "chrome:rendering:editvariationscompleted":
        if (Sitecore.PageModes.Testing) {
          this.editVariationsCompleted(params, sender);
        }
        break;
      case "chrome:testing:variationchange":
        if (Sitecore.PageModes.Testing) {
          this.changeVariation(params.id, sender);
        }
        break;
	case "chrome:rendering:runcommand":
		this.runcommand(params.command, sender);
		break;
    }
  },

Lines 51-53 are the added code and next I added the runcommand function to RenderingChromeType.js:

runcommand: function(commandName, sender) {
    var ribbon = Sitecore.PageModes.PageEditor.ribbon();
    Sitecore.PageModes.PageEditor.layoutDefinitionControl().value = Sitecore.PageModes.PageEditor.layout().val();
    var controlId = this.controlId();
    if (sender) {
      controlId = sender.controlId();
    }

    Sitecore.PageModes.PageEditor.postRequest(commandName + "(uniqueId=" + this.uniqueId() + ",controlId=" + controlId + ")");
  },

Next, I opened up dotPeek to look at how webedit:personalize at Sitecore.Shell.Applications.WebEdit.Commands.Personalize, Sitecore.ExperienceEditor was written so I could follow that pattern. I came up with this class.

using Sitecore;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Globalization;
using Sitecore.Layouts;
using Sitecore.Shell.Applications.WebEdit.Commands;
using Sitecore.Shell.Framework.Commands;
using Sitecore.Web;
using Sitecore.Web.UI.Sheer;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Hi.Shared.BrentsCoolCopyRendLangVersModule
{
    [Serializable]
    public class CopyToOtherLanguageVersions : WebEditCommand
    {
        public CopyToOtherLanguageVersions()
        {
        }

        protected virtual string ConvertToXml(string layout)
        {
            Assert.ArgumentNotNull(layout, "layout");
            return WebEditUtil.ConvertJSONLayoutToXML(layout);
        }

        public override void Execute(CommandContext context)
        {
            Assert.ArgumentNotNull(context, "context");
            ItemUri itemUri = ItemUri.ParseQueryString();
            if (itemUri != null)
            {
                Item item = Database.GetItem(itemUri);
                if (item != null && !WebEditUtil.CanDesignItem(item))
                {
                    SheerResponse.Alert("The action cannot be executed because of security restrictions.", new string[0]);
                    return;
                }
            }

            string formValue = WebUtil.GetFormValue("scLayout");
            Assert.IsNotNullOrEmpty(formValue, "Layout Definition");
            string str = ShortID.Decode(WebUtil.GetFormValue("scDeviceID"));
            Assert.IsNotNullOrEmpty(str, "device ID");
            string str1 = ShortID.Decode(context.Parameters["uniqueId"]);
            Assert.IsNotNullOrEmpty(str1, "Unique ID");
            string xml = this.ConvertToXml(formValue);
            Assert.IsNotNullOrEmpty(xml, "convertedLayoutDefition");

            NameValueCollection nameValueCollection = new NameValueCollection();
            nameValueCollection["deviceId"] = str;
            nameValueCollection["uniqueId"] = str1;
            nameValueCollection["contextItemUri"] = itemUri != null ? itemUri.ToString() : string.Empty;

            Context.ClientPage.Start(this, "Run", nameValueCollection);
        }

        [UsedImplicitly]
        protected void Run(ClientPipelineArgs args)
        {
            string deviceId;
            string uniqueId;
            string contextItemUri;

            Assert.ArgumentNotNull(args, "args");
            try
            {
                deviceId = args.Parameters["deviceId"];
                Assert.IsNotNull(deviceId, "deviceId");
                uniqueId = args.Parameters["uniqueId"];
                Assert.IsNotNull(uniqueId, "uniqueId");
                contextItemUri = args.Parameters["contextItemUri"];
                Assert.IsNotNull(contextItemUri, "contextItemUri");
            }
            catch
            {
                throw;
            }

            if (args.IsPostBack)
            {
                if (args.Result == "yes")
                {
                    if (!RunCopy(deviceId, uniqueId, contextItemUri))
                    {
                        SheerResponse.Alert("Cannont copy this rendering. Please save first.");
                    }
                }
            }
            else
            {
                try
                {
                    SheerResponse.Confirm("Are you sure you want to copy renderings to the most recent language versions?");
                    args.WaitForPostBack();
                }
                catch
                {
                    throw;
                }
            }
        }

        public bool RunCopy(string deviceId, string uniqueId, string contextItemUri)
        {
            Item i = Database.GetItem(new ItemUri(contextItemUri));
            RenderingDefinition rd = i.GetRenderingDefinitionByUniqueId(uniqueId, deviceId);
            if (i != null && rd != null)
            {
                List<Language> langList = i.Languages.Where(e => !e.CultureInfo.Name.Equals(i.Language.CultureInfo.Name)).ToList();
                foreach (Language l in langList)
                {
                    Item li = i.Database.GetItem(i.ID, l);
                    if (li != null && li.Versions.Count > 0)
                    {
                        li.CopyRenderingReference(rd, deviceId);
                    }
                }
                return true;
            }
            return false;
        }
    }
}

The difference between my code and Sitecore.Shell.Applications.WebEdit.Commands.Personalize is that I have removed the session code to keep track of the dialog. The RunCopy method is what does the bulk of the work to copy rendering references to the other language versions. This is done with a help of a few extension methods located here.

When it’s all done, you will have a nice rendering button to help content authors.

copy rendering button


Sitecore Glass Mapper Regenerate Code not working for Color (or custom) Field

$
0
0

If we are using Glass Mapper ORM to convert the Sitecore template to modal properties, some of the custom fields (like color field) downloaded from Sitecore market place or custom field types are not able to retrieve the content from the Sitecore Item.

For example,

We have color field in Sitecore which will be converted into c# property by Glass mapper will return null values in the view like shown below.

Resolution –

The code written in this blog was tested for Sitecore 8.0, 8.2. Other versions of Sitecore May have different API code.

The TDS regenerate code of the Sitecore templates will create template attributes for all the fields defined in the templates. The data types like int or string will be retrieved according to the field types defined in the template. For example, rich area text field will be converted into string in the template. For the fields like color field which is downloaded from market place, Glass mapper will regenerate the items as “object” and not “string”.

When Glass mapper cannot detect the type of the Sitecore field, it will simply create that field as “object” in the public partial interface.

The solution is to convert the object into string which will retrieve the color value from Sitecore color field like shown below.

For Code generation templates to convert object to string  , Navigate to the CTABackgroundColor field in Visual studio and navigate to properties and add type=string in Custom Data field of Sitecore field.

 

Now In Visual Studio, Click on regenerate code for all items and it should be added as string in auto generated c# template

 

 

Viewing all 88 articles
Browse latest View live