Sitecore Update Placeholder Key (Programming) - sitecore

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

Related

CTabCtrl DeleteItem does not work in every case

i have a function which loops through map (the map contains the index of my CTabCtrl-Tab and the ID of the Document which is shown in the Tab) and if the ID of the current selected Tab is not similar to the ID of the current looped tab, the tab should be removed.
int deleteTab = -1;
for (auto i : tabIndexToFBNR)
{
deleteTab = i.first;
if (i.second == m_pDlgSubFBs.at(m_AktTab)->m_pRecFB->m_ID)
deleteTab = -1;
if (deleteTab != -1)
m_tabSubFB.DeleteItem(deleteTab);
}
Problem is: Some tabs are removed, some are not. The return code of DeleteItem is always "1".
Any ideas?

How to access childWidgets in a QTreeView using QModelIndex?

I'm working on an application using the Qt library (version 4.8).
I have a QTreeView with a QStandardItemModel. My widget looks like that:
Item1
subitem11
subitem12
Item2
subitem21
subitem22
Item3
subitem31
subitem32
Here is how I add the items to my QTreeView:
model->setItem(0, 0, item1);
item1->setChild(0, 0, subitem12);
I want to take an action only when the user double cliked an item (and do nothing when he clicked a subitem). So I use the doubleClicked(const QModelIndex & index) signal.
I want to process the information about the item/subitem which was double cliked by the user. So I get the row of my item/subitem:
index.row();
But every time I try to reference to the item/subitem to display its name or check if it has children, I can only access the items:
index.model()->item(row)->text();
My question is: how can I acces the subitems (vbetween items abd subitems) in my slot? Or how can I prevent them from emitting the signal? I can't disable them - it would be too confusing for the user.
Edit: The problem is that every time I click on an item or subitem and execute:
index.model()->item(row)->hasChildren();
or:
index.model()->item(row)->parent() == 0;
I get true as result. So I can reference only the items.
My question is: What is the correct way to reference to subitems?
When you are trying to access model items by row index, the model returns top-level item at that row. Use itemFromIndex instead:
auto item = index.model()->itemFromIndex(index);
if (item && item->hasChildren()){
// item is not a leaf
}
EDIT index.model() returns QAbstractItemModel*, so a cast is also necessary here (or, better, storing a pointer to the standard model somehwere in the code).
I would do it like this:
// Define your custom role to store item type.
enum MyRoles
{
ItemTypeRole = Qt::UserRole + 1
};
// Define item types.
enum ItemType
{
Primary,
Secondary
};
Than set the proper item type for all your items:
QStandardItem* item = new QStandardItem("Item1");
item->setData(Primary, ItemTypeRole);
QStandardItem* subItem = new QStandardItem("SubItem1");
subItem->setData(Secondary, ItemTypeRole);
And in your slot connected to the doubleClicked signal acces the type like this:
ItemType type = static_cast<ItemType>(index.data(ItemTypeRole).toInt());
if (type == Primary)
std::cout << "It's a Primary item!" << std::endl;
else if (type == Secondary)
std::cout << "It's a Secondary item!" << std::endl;
You can't disable the signal for the subitems. However, you can check if an item has a parent. It it doesn't, it is an item and if it does, it's a subitem.
if (item->parent() != 0)
.. //subitem
else
.. //item
An alternative would be to use the data() function to set some special value to distinguish between the two.
item1->setData(QVariant("item"));
subitem1->setData(QVariant("subitem"));
Then query the value in your doubleclick handler:
QVariant var = item->data();
if (var.toString() == "item")
...
else if (var.toString() == "subitem")
...

Ember context passed to a view

I'm trying to figure out what's wrong about sending action from parentView to one of its children view:
I've made a PhotoUploadView used to resize and then upload images; it adds canvas drawing the resized image in a div inside its template:
<div class="img-list">
//here the canvas will be added
</div>
The action "saveImages" in this view, acts like this:
var list = this.$('.img-list');
$.each(list.children(), function(index, value) {
//here the code to save
}
But this action is not called directly; because I need to save not only the images but also some records (an offer record and many products records, children of the offer; the images are associated to the product record);
So I have the action "save" on the parentView that is like this:
//save records
offer.save().then(function(savedOffer) {
newProducts.forEach(function(product, indexP) {
product.set('offer', savedOffer);
product.save().then(function(savedProduct) {
}
}
}
//save photos by cycling the PhotoUploadViews that are inside ProductViews that are inside the mainView
this.get('childViews').forEach(function(view, index) {
if (index >= 4) { //the childView from the 4th are the ProductViews
var productId = view.get('product').get('id');
var folder = 'offer-' + controller.get('model').get('id') + '/product-' + productId + '/';
view.get('childViews')[0].send('saveImages', folder, productId); //the first childView of a ProductView is the UploadView
}
});
Well, this works and save the images correctly when you add images to an existing offer with existing product; but when you are creating a new offer, it fails since the folder will becone "offer-undefined/product-undefined" because of course you must wait the records to be saved in order to get their ID;
So I'm trying now to move the send action into the .then callback of product save:
var childViews = this.get('childViews'); //the childView starting from the 4th are the productsViews
offer.save().then(function(savedOffer) {
newProducts.forEach(function(product, indexP) {
product.set('offer', savedOffer);
product.save().then(function(savedProduct) {
var currentPview = (childViews[4 + indexP]); //get the productView associated with the current product
var productId = savedProduct.get('id');
var folder = 'offer-' + savedOffer.get('id') + '/product-' + productId + '/';
currentPview.get('_childViews')[2].send('saveImages', folder, productId); //the object at index 2 of _childViews array is the photoUploadView
Here, the folder is built correctly but after sending action, the saveImages action crashes saying that list is undefined; trying to log the value of "this" inside "saveImages" I can see that also its value is undefined; Someone can please explain why calling the action from one point, it works, and calling it inside the .then callback of product save it doesn't?
I also would like to understand why in the first case I can do
.get('childViews')[0]
to get the PhotoUploadView, but in the second I must do
.get('_childViews')[2]
since using get('childViews) it doesn't work anymore; What is the difference between childViews and _childViews? And why _childViews has more elements than childView?

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.

How do I trigger a profile in Sitecore DMS?

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.