How do I trigger a profile in Sitecore DMS? - sitecore

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.

Related

Personalization in SItecore

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

Sitecore dictionary items Editable via page editor

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 ?

Sitecore Update Placeholder Key (Programming)

I'd like to update the value of placeholder(PH) key assigned into each page item.
The problem is I changed the value of PH key in master template (actually combined two templates to make only one template) and a number of pages should be updated with new assigned PH key.
How to update placeholder key without clicking each item and changing the value in presentation? If I do like this, it takes a lot of time.
What I want to do in program is:
Set initial path (/sitecore/home/robot/)
Check each item (with each item's sub-item) in initial path
Retrieve each item's assigned controls in presentation
If there is "Breadcrumbs" control with "breadcrumbs" key name
Then, change the value to "/template/dynamic/breadcrumbs"
Do until it retrives all items in the initial path
See the code below. What it does, it gets rendering references for the selected items, checks their placeholders and rendering names and updates xml value of the __Renderings field of selected item, based on the unique id of selected renderings. Then it fires same code for all descendants recursively.
This code
does not update placeholders for components which are inherited from __Standard Values
does not publish changed items automatically.
is case sensitive
requires that user has write access for the items that you want to change
public void Start()
{
string initialPath = "/sitecore/home/robot";
Item root = Database.GetDatabase("master").GetItem(initialPath);
UpdatePlaceholderName(root, "Breadcrumbs", "breadcrumbs", "/template/dynamic/breadcrumbs");
}
private void UpdatePlaceholderName(Item item, string componentName, string placeholderName, string newPlaceholderName)
{
if (item != null)
{
List<RenderingReference> renderings = item.Visualization.GetRenderings(Sitecore.Context.Device, false)
.Where(r => r.Placeholder == placeholderName && r.RenderingItem.Name == componentName).ToList();
if (renderings.Any())
{
string renderingsXml = item["__Renderings"];
item.Editing.BeginEdit();
foreach (RenderingReference rendering in renderings)
{
string[] strings = renderingsXml.Split(new [] {"<r"}, StringSplitOptions.None);
foreach (string renderingXml in strings)
{
if (renderingXml.Contains("s:ph=\"" + placeholderName + "\"") && renderingXml.Contains("uid=\"" + rendering.UniqueId + "\""))
{
renderingsXml = renderingsXml.Replace(renderingXml, renderingXml.Replace("s:ph=\"" + placeholderName + "\"", "s:ph=\"" + newPlaceholderName + "\""));
}
}
}
item["__Renderings"] = renderingsXml;
item.Editing.EndEdit();
}
foreach (Item child in item.GetChildren())
{
UpdatePlaceholderName(child, componentName, placeholderName, newPlaceholderName);
}
}
}

Google Glass Mirror API - Is there a way to delete a bundle of cards?

I'm sending a bundle of cards to Glass with the Mirror API (c# library)
I know that you can use the default delete menu item on single cards, but is there a way to provide delete functionality for an entire bundle, ideally the result of one action by the users?
I have successfully used the DELETE action on a menu item with the code below
MenuItem mi = new MenuItem();
mi.Action = "DELETE";
TimelineItem tli = new TimelineItem()
{
Html = itemHtml.ToString(),
Notification = new NotificationConfig() { Level = "DEFAULT" },
MenuItems = new List<MenuItem>() { mi }
};
Is there a way to add this delete menu item to a bundle cover? I know this may be tricky because clicking the bundle cover causes you to navigate into the child cards thus no menu is present like on single cards. I'm looking for something (which I did try but it just ignored the menu item) like this:
MenuItem mi = new MenuItem();
mi.Action = "DELETE";
TimelineItem tli = new TimelineItem()
{
Html = itemHtml.ToString(),
Notification = new NotificationConfig() { Level = "DEFAULT" },
IsBundleCover = true,
BundleId = bundleId,
MenuItems = new List<MenuItem>() { mi }
};
If not possible on a cover card, is there a way to do this for a bundle by adding delete menu items to the child cards?
Any suggestions would be appreciated
You can use customized menu to do this. The code below is using Java but C# should be similar:
Add customized menu item to the card:
List<MenuValue> menuValueList = new ArrayList<MenuValue>();
menuValueList.add(new MenuValue().setIconUrl(iconUrl).setDisplayName("Delete All"));
MenuItem menuItem = new MenuItem();
menuItem.setValues(menuValueList).setId("delete_bundle_A").setAction("CUSTOM");
List<MenuItem> menuItemList = new ArrayList<MenuItem>();
menuItemList.add(menuItem);
timelineItem.setMenuItems(menuItemList);
Define the controller which handles the callback request of Mirror server notification:
if (notification.getCollection().equals("timeline") && notification.getUserActions().contains(new UserAction().setType("CUSTOM").setPayload("delete_bundle_A"))) {
deleteCards(credential, bundleId);
}
The delete card function:
// if bundleId is null or "", delete all cards
public static void deleteCards(Credential credential, String bundleId) throws IOException {
if (bundleId == null) {
bundleId = "";
}
Mirror.Timeline timelineItems = MirrorClient.getMirror(credential).timeline();
Mirror.Timeline.List list = timelineItems.list();
List<TimelineItem> timelineItemList = null;
do {
TimelineListResponse response = list.execute();
timelineItemList = response.getItems();
if (timelineItemList != null && timelineItemList.size() > 0) {
for (TimelineItem item : timelineItemList) {
if (bundleId == "" || bundleId.equalsIgnoreCase(item.getBundleId())) {
LOG.info("Deleting card " + item.getId());
MirrorClient.deleteTimelineItem(credential, item.getId());
}
}
list.setPageToken(response.getNextPageToken());
} else {
break;
}
} while (list.getPageToken() != null && list.getPageToken().length() > 0);
}
Finally, don't forget to subscribe timeline notification when application starts up:
String notifyUrl = "https://mirrornotifications.appspot.com/forward?url=" + "http://yourServer.com/notify";
Subscription subscription = MirrorClient.insertSubscription(credential, notifyUrl, userId, "timeline");
It isn't clear if you're asking how to create the menu items to delete the entire bundle at once, or if you're looking for code to do the actual delete.
Yuan provides some very good answers to both (not least of which because he actually provides code, which I won't), but there are three things you might also want to consider.
1) You can't have a menu on the bundle cover, but if you don't explicitly specify a bundle cover, then the most recent card will be shown as the cover and will also be shown as the first card in the bundle. You'd be able to get to the menu this way. (The default messaging app works this way, for example, but the first card has the same menu as the rest.)
2) You don't need to create a new menu item. You can leverage the DELETE menu item, if you wish. You'll get a delete notification for one of the cards in the bundle and you can then read the bundleId and delete the rest.
3) You don't need to loop through all the cards you've inserted just to find ones that have that bundleId. That is horribly inefficient. I am not fluent in C#, but from reading the documentation at https://developers.google.com/resources/api-libraries/documentation/mirror/v1/csharp/latest/classGoogle_1_1Apis_1_1Mirror_1_1v1_1_1TimelineResource_1_1ListRequest.html, I get the sense that you can create a ListRequest and then set the bundleId before executing the query and get the results.
So I think you can change Yuan's code to something like:
Mirror.Timeline.List list = timelineItems.list();
list.BundleId = bundleId;
List<TimelineItem> timelineItemList = null;
do {
TimelineListResponse response = list.execute();
timelineItemList = response.getItems();
if (timelineItemList != null && timelineItemList.size() > 0) {
for (TimelineItem item : timelineItemList) {
LOG.info("Deleting card " + item.getId());
MirrorClient.deleteTimelineItem(credential, item.getId());
}
list.setPageToken(response.getNextPageToken());
} else {
break;
}
} while (list.getPageToken() != null && list.getPageToken().length() > 0);
(this should be treated as pseudo-code, at best)
If you're confident how many items you've put into a bundle, you might also be able to just set list.MaxResults and not have to iterate over the pages of results. So perhaps something more like
Mirror.Timeline.List list = timelineItems.list();
list.BundleId = bundleId;
list.MaxResults = 20; // Set to more than the max number of items in a bundle
TimelineListResponse response = list.execute();
List<TimelineItem> timelineItemList = response.getItems();
if (timelineItemList != null && timelineItemList.size() > 0) {
for (TimelineItem item : timelineItemList) {
LOG.info("Deleting card " + item.getId());
MirrorClient.deleteTimelineItem(credential, item.getId());
}
}
There doesn't appear to be a way to delete a bundle in one step but it's still possible...
You can do a GET on /Timeline to get a list of items your app has pushed to the users timeline. Filter that out to find the entries with the bundleId you want to delete. For each of those items, call DELETE /Timeline/{itemid}

Sitecore workbox change sort order

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.