I want to display some different sublayout if user has visit some page more than 2 times so I want to use this rules "where the visit no. compares to number" but I do not have idea how I can use it? I tried to add this rule and replace "number" to 2 but it is not working.
As Marek said this is not possible with the condition you are using. However you could adjust the Rule condition to achieve this by looking at the VisitPageIndex for the page.
public class ContactVisitPageIndexCondition<T> : OperatorCondition<T> where T : RuleContext
{
public int No
{
get;
set;
}
public ID PageGUID
{
get;
set;
}
public ContactVisitPageIndexCondition()
{
}
protected override bool Execute(T ruleContext)
{
Assert.ArgumentNotNull(ruleContext, "ruleContext");
Assert.IsNotNull(Tracker.Current, "Tracker.Current is not initialized");
Assert.IsNotNull(Tracker.Current.Session, "Tracker.Current.Session is not initialized");
Assert.IsNotNull(Tracker.Current.Session.Interaction, "Tracker.Current.Session.Interaction is not initialized");
int contactVisitPageIndex = Tracker.Current.Session.Interaction.Pages.SingleOrDefault(p => p.Item.Id == PageGUID).VisitPageIndex;
switch (base.GetOperator())
{
case ConditionOperator.Equal:
{
return contactVisitPageIndex == this.No;
}
case ConditionOperator.GreaterThanOrEqual:
{
return contactVisitPageIndex >= this.No;
}
case ConditionOperator.GreaterThan:
{
return contactVisitPageIndex > this.No;
}
case ConditionOperator.LessThanOrEqual:
{
return contactVisitPageIndex <= this.No;
}
case ConditionOperator.LessThan:
{
return contactVisitPageIndex < this.No;
}
case ConditionOperator.NotEqual:
{
return contactVisitPageIndex != this.No;
}
}
return false;
}
}
As Marek Musielak said, Where the visit no. compares to .. related to visit to the site and not an individual page.
I had a look in the Sitecore API, its Tracker namespace and the closest property I can find to individual page view count is VisitPageIndex but decompiling the code and checking in MongoDB shows that is just the index of the page was viewed for that visit to the site so this won't work for you.
Looking in MongoDB there are no properties to store page views but it does store the Pages viewed for Interactions so you could write a custom rule counting the number of times that page is in the Pages array
e.g.
int pageViewed = Tracker.Current.Session.Interaction.Pages.Count(p => p.Item.Id.Equals(yourPageId))
An alternative if you don't want to write custom is to change your approach a bit inline with how Sitecore personalisation scan work out of the box.
You'll want to use or create profile keys in the Marketing Centre e.g. 'Brand Aware'. Assign your new profile key to the page in question and assign it a score e.g. 10. This means that each time a user visits this page they will be given a 10 points in 'Brand Aware'.
Now for the personalisation bit. Create a new personalisation rule on the existing sublayout using 'where the value of the specific profile key compares to specific value' set it to hide if the score is greater than or equal to 20. Create another to display your new sublayout if the value is greater than or equal to 20.
I've wrote a blog about this if you need more info
Related
I am wondering if any of the built in rules for personalization within Sitecore 8.2 fit the requirements of what I am looking for. Using personalization I am trying to make a module on a page show up the first time you visit the page. Any subsequent visits to the page within the current session would not render the module.
I thought the built in rule "where the [specific page] has been visited during the current visit" would work but it doesn't in my scenario. It works if the [specific page] parameter is not the current page but that's not what I need.
It seems as if the visit is recorded before the rule is validated so when the rule is eventually validated it thinks the page has already been visited before when in actuality this may be the first page visit.
Any thoughts other than creating a custom rule? Thanks in advance.
I don't think there is anything OOTB in Sitecore. You're right - Sitecore first counts the page visit and then executes the rule.
I've created a blog post describing what you need: https://www.skillcore.net/sitecore/sitecore-rules-engine-has-visited-certain-page-given-number-of-times-condition
In shortcut:
Create a new condition item:
Text: where the [PageId,Tree,root=/sitecore/content,specific] page has been visited [OperatorId,Operator,,compares to] [Index,Integer,,number] times during the current visit
Type: YourAssembly.YourNamespace.HasVisitedCertainPageGivenNumberOfTimesCondition,YourAssembly
Use it to personalize your component with values:
where the [YOUR_PAGE] page has been visited [IS EQUAL TO] [1] times during the current visit
Create the code:
public class HasVisitedCertainPageGivenNumberOfTimesCondition<T> : OperatorCondition<T> where T : RuleContext
{
public string PageId { get; set; }
public int Index { get; set; }
protected override bool Execute(T ruleContext)
{
Assert.ArgumentNotNull(ruleContext, "ruleContext");
Assert.IsNotNull(Tracker.Current, "Tracker.Current is not initialized");
Assert.IsNotNull(Tracker.Current.Session, "Tracker.Current.Session is not initialized");
Assert.IsNotNull(Tracker.Current.Session.Interaction, "Tracker.Current.Session.Interaction is not initialized");
Guid pageGuid;
try
{
pageGuid = new Guid(PageId);
}
catch
{
Log.Warn(string.Format("Could not convert value to guid: {0}", PageId), GetType());
return false;
}
var pageVisits = Tracker.Current.Session.Interaction.GetPages().Count(row => row.Item.Id == pageGuid);
switch (GetOperator())
{
case ConditionOperator.Equal:
return pageVisits == Index;
case ConditionOperator.GreaterThanOrEqual:
return pageVisits >= Index;
case ConditionOperator.GreaterThan:
return pageVisits > Index;
case ConditionOperator.LessThanOrEqual:
return pageVisits <= Index;
case ConditionOperator.LessThan:
return pageVisits < Index;
case ConditionOperator.NotEqual:
return pageVisits != Index;
default:
return false;
}
}
}
Is it possible to set the number of components in placeholder?
We can add as many as components in placeholder by using "Add to here" in gray box even the component has been already added.
I'd like to say that
In plcaceholder named 'bodyArea', you can set only one component in 'bodyArea' placeholder and you will not add any other component additionally.
Is there anyway how to do this??
There could be many ways, but this is what I used before.
// Check the number of renderings in placeholder
public static bool numberOfRenderings(string placeholderName)
{
bool rendering = true;
var renderingReferences = Sitecore.Context.Item.Visualization.GetRenderings(Sitecore.Context.Device, true);
int renderingsInPlaceholder = renderingReferences.Where(r => r.Placeholder.EndsWith('/' + placeholderName, StringComparison.OrdinalIgnoreCase)).Count();
if (renderingsInPlaceholder > 1)
{
return rendering = false;
}
return rendering;
}
In View.cshtml
if (#yourObject.numberOfRenderings("your-placeholder-key")) {
#Html.Sitecore().Placeholder("your-placeholder-key")
}
else
{
#Html.Raw("<div>Only one rendering item is available in this placeholder.</div>")
}
here is a blog where is describing how to restrict number of allowed controls :
http://www.newguid.net/sitecore/2014/restricting-the-number-of-components-in-the-sitecore-page-editor/
Other solution is using rules :
http://dotnetmafia.com/blogs/kevin/archive/2013/07/10/placeholder-settings-rules.aspx
I am trying to implement Sitecore Dictionary items to be edited via PageEditor.
This is my approach.. Just need your thoughts and suggestions.
To make it simple and not to mess up with the pipelines, here is a simple way of what I am doing.
Normally you do a sitecore translate for example,
#Sitecore.Globalization.Translate.Text("SomeKey")
You can encapsulate the Translate to a custom class which might look like
public static class CustomTranslate
{
public static string Text(string key)
{
if (Sitecore.Context.PageMode.IsPageEditorEditing)
{
string val = String.Empty;
Item currentItem = Context.Database.GetItem(ResourcesController.ItemLookUp()[key]);
if (currentItem != null)
{
val = FieldRenderer.Render(currentItem, "Phrase");
}
return val;
}
else
return Sitecore.Globalization.Translate.Text(key);
}
}
The CustomTranslate.Text returns a FieldRenderer in PageEdit mode else returns the Sitecore.Globalization.Translate.Text
Then in your code you can refer the translations as
#CustomTranslate.Text("SomeKey")
The Lookup can be a dictionary of Key and Item ID as shown in below code,
public static class ResourceController
{
public static Dictionary ItemLookUp()
{
///get dictionary path..etc.. code not included
//read all sitecore dictionary Items
Sitecore.Data.Items.Item[] items =
Sitecore.Context.Database.SelectItems("fast:" + dictionaryPath +
"//*[##templateid='{6D1CD897-1936-4A3A-A511-289A94C2A7B1}']");
//build a Dictionary<string,string> using sitecore item key and Guid.
items.All(y => { resourceDictionary.Add(y["Key"], y.ID.Guid.ToString()); return true;}
// Key,Guid dictionary
return resourceDictionary;
}
}
A Simpler and much easier approach ! Thoughts, Comments ?
By default Sitecore workbox displays the Item name, and sort the item list by Item name.
In one of my previous posts regarding this I managed to change the item name to a custom field.
Now I need to sort workbox by this field. How can I do this?
Assuming that you already have your own implementation of the WorkboxForm as described in the post you linked in your question, you need to change the code of the DisplayState method.
The DataUri[] items inflow parameter of this method gives you the list of all items which are in given state of the workflows. You need to retrieve all the Sitecore items from this parameter and sort them:
DataUri[] items = new DataUri[0];
List<Item> sitecoreItems = items
.Select(uri => Context.ContentDatabase.Items[uri])
.OrderBy(item => item["YourCustomField"])
.ToList();
And use the new list for selecting the current page items. This solution is not optimized for the performance - you need to get every item in given state from database so you can access the custom field.
After studying Sitecore workbox modifications, I came across with following solution.
Step 1 - Modify the GetItems method as follows,
private DataUri[] GetItems(WorkflowState state, IWorkflow workflow)
{
if (workflow != null)
{
var items = workflow.GetItems(state.StateID);
Array.Sort(items, new Comparison<DataUri>(CompareDataUri));
return items;
}
return new DataUri[] { };
}
Here comes the "CompareDataUri" method,
private int CompareDataUri(DataUri x, DataUri y)
{
//Custom method written to compare two values - Dhanuka
Item itemX = Sitecore.Context.ContentDatabase.GetItem(x);
Item itemY = Sitecore.Context.ContentDatabase.GetItem(y);
string m_sortField = "__Updated";
bool m_descSort = false;
var res = 0;
res = string.Compare(itemX[m_sortField], itemY[m_sortField]);
if (m_descSort)
{
if (res > 0)
return -1;
if (res < 0)
return 1;
}
return res;
}
This approach is optimized for performance.
I am looking for a way to allow visitors to select what content they want displayed on the site.
Is there a way to programatically trigger a profile in Sitecore DMS?
I've looked at relevant documentation on SDN (http://sdn.sitecore.net/Reference/Sitecore 6/DMS Documentation.aspx), but so far haven't found a way.
EDIT: Raised this on Sitecore Support Portal - will post an answer once I find out more.
I have done something similar on my project. Check out this code sample and let me know if you have any questions. Also, make sure you add profiles to content items too. Call FilterItemByBehavior on a collection of items and it will filter them based on user's past browsing behavior.
private static Dictionary<string, List<string>> AnalyticsFilter()
{
Dictionary<string, List<string>> filter = new Dictionary<string, List<string>>();
if (Tracker.CurrentVisit.Profiles.Count() > 0)
{
foreach (VisitorDataSet.ProfilesRow row in Tracker.CurrentVisit.Profiles)
{
List<string> keys = new List<string>();
foreach (var key in row.Values)
{
if (key.Value >= ResourceHelper.GetInt(new ID(Resources.Settings.AnalyticsProfileSetMinValGuid)))
keys.Add(key.Key);
}
filter.Add(row.ProfileName, keys);
}
}
if(ResourceHelper.IsTurnedOn(new ID(Resources.Settings.AnalyticsUserProfileEnableSwitch)))
filter = ApplyUserProfile(filter);
return filter;
}
public static List<Item> FilterItemByBehavior(List<Item> items, int count)
{
try
{
var filter = AnalyticsFilter();
foreach (var profile in filter)
{
int counter = ResourceHelper.GetInt(new ID(Resources.Settings.AnalyticsProfileTagsFilterMaxGuid));
if (items.Count <= count) break;
foreach (string key in profile.Value)
{
if (items.Count <= count || counter == 0) break;
items = items.Where(i => (((MultilistField)i.Fields[profile.Key]).GetItems().ToList().Select(x => x.Name).Contains(key))).ToList();
counter--;
}
}
return items.Count <= count ? items : items.Take(count).ToList();
}
catch (System.Exception ex)
{
Sitecore.Diagnostics.Log.Error(ex.Message, ex, new AnalyticsHelper());
return items.Count <= count ? items : items.Take(count).ToList();
}
}
I have received a response from Sitecore support on this question. Here it is:
"If you are using pattern cards for personalzation, then you can use the following code as the event handler for "item selected" event for the dropdown list:"
var profile = Sitecore.Analytics.Tracker.CurrentVisit.GetOrCreateProfile("<Profile Name>");
profile.BeginEdit();
profile.Score("<profile key>",<profile key value you want to set>);
profile.Score("<profile key>",<profile key value you want to set>);
profile.UpdatePattern(); //sets the appropriate pattern based on the current profile keys values you have just set.
profile.EndEdit();
This interferes with automatic profile matching, so I am not sure I want to use this approach.