How to properly add linked Items to a List of already existing linked items with Kentico Cloud Management API - kentico-kontent

In Kentico Cloud I got an Item which contains a list of linked items. However this list should be edited from outside with the Kentico Cloud Management API. Is there a way to simply add an Item to this list without updating the whole list?
I am working with the Kentico Cloud SDK in C# and what I have tried so far is upserting a language variant with a new Array of ContentItemIdentifier.byId but whenever I call this it overwrites my already existing list.
private async Task AddOrderToDay(string orderItemExternalId, Guid dayId)
{
ContentItemVariantIdentifier ident = new ContentItemVariantIdentifier(
ContentItemIdentifier.ById(dayId),
LanguageIdentifier.DEFAULT_LANGUAGE);
UpdateOrdersDay update = new UpdateOrdersDay
{
Orders =
new[]{ContentItemIdentifier.ByExternalId(orderItemExternalId)}
};
ContentItemVariantModel<UpdateOrdersDay> response =
await this._cmclient.UpsertContentItemVariantAsync(ident, update);
}
What I'd expect is that my new element is added to the existing list of elements in Cloud.
At the moment it simply overwrites them.
I tried a workaround: I call the delivery Api to receive the current Items and add them to new new [] {old1, old2, new ItemIdentifier}. However this solution is not very performant.

Currently, the CM API (including v2) does not support adding / removing of linked items one by one.
What you are currently doing is the most efficient way as you get items from Delivery API rather than CM API.
This might change in the future, but for now you have to make that additional request before saving the item.

Related

Programmatically replace one django CMS plugin with another

I have a website with many Filer Video plugins that point to YouTube. I've decided that the filer isn't very good for responsive sites (it demands fixed widths) so rather than fix it, I've written my own simple little plugin that does YouTube embeds very easily for variable-width layouts.
So now I have a ton of cmsplugin_filer_video.models.FilerVideo instances that I want to replace with my cmsyoutube.models.YtVideo instances. Looping them is fine but how do I programmatically take out one plugin and replace it (in place) with another?
Thinking out aloud, they're both [eventually] based on CMSPlugin. Can I transpose the parent items into my new class?
This is as far as I've gotten so far. Iterate over the old videos and try to create a new YtVideo (same thing) that points at the same cmsplugin_ptr before deleting the old FilerVideo v...
for v in FilerVideo.objects.all():
plugin = v.cmsplugin_ptr
v.cmsplugin_ptr_id = None
new = YtVideo()
new.cmsplugin_ptr = plugin
plugin.plugin_type = 'YoutubePlugin'
new.youtube_id = re.search(r'v=([^&]+)', v.movie_url).groups(0)[0]
new.thumbnail = v.image
new.save()
plugin.save()
v.delete()
But TreeBeard gets very upset with this:
NodeAlreadySaved: Attempted to add a tree node that is already in the database
Looks like there's some cascade save logic in there. Any idea how to hotwire this so I can replace the plugin instances without destroying the underlying CMSPlugin tree?
Had more luck with cms.api.add_plugin. Funny thing is I couldn't get the position to stick. I think it expects wafty "last-child"-style English rather than a numerical input. So I just nail it in a second time after add_plugin.
from cms import api
for v in FilerVideo.objects.all():
youtube_id = re.search(r'v=([^&]+)', v.movie_url).groups(0)[0]
position = v.position
v.delete()
new = api.add_plugin(v.placeholder, 'YoutubePlugin', v.language, position, v.parent, youtube_id=youtube_id, thumbnail=v.image)
new.position = position
new.save()

How to filter a Backbone collection on the server

Is there a common pattern for using Backbone with a service that filters a collection on the server? I haven't been able to find anything in Google and Stack Overflow searches, which is surprising, given the number of Backbone apps in production.
Suppose I'm building a new front end for Stack Overflow using Backbone.
On the search screen, I need to pass the following information to the server and get back a page worth of results.
filter criteria
sort criteria
results per page
page number
Backbone doesn't seem to have much interest in offloading filtering to the server. It expects the server to return the entire list of questions and perform filtering on the client side.
I'm guessing that in order to make this work I need to subclass Collection and override the fetch method so that rather than always GETting data from the same RESTful URL, it passes the above parameters.
I don't want to reinvent the wheel. Am I missing a feature in Backbone that would make this process simpler or more compatible with existing components? Is there already a well-established pattern to solve this problem?
If you just want to pass GET parameters on a request, you should just be able to specify them in the fetch call itself.
collection.fetch( {
data: {
sortDir: "ASC",
totalResults: 100
}
} );
The options passed into fetch should directly translate to a jQuery.ajax call, and a data property should automatically get parsed. Of course overriding the fetch method is fine too, especially if you want to standardize portions of the logic.
You're right, creating your own Collection is the way to go, as there are not standards about server pagination except OData.
Instead of overriding 'fetch', what I usually do in these cases is create a collection.url property as a function, an return the proper URL based on the collection state.
In order to do pagination, however, the server must return to you the total number of items so you can calculate how many pages based on X items per page. Nowadays some APIs are using things like HAL or HATEOAS, which are basically HTTP response headers. To get that information, I normally add a listener to the sync event, which is raised after any AJAX operation. If you need to notify external components (normally the view) of the number of available items/pages, use an event.
Simple example: your server returns X-ItemTotalCount in the response headers, and expects parameters page and items in the request querystring.
var PagedCollection = Backbone.Collection.extend({
initialize: function(models,options){
this.listenTo(this, "sync", this._parseHeaders);
this.currentPage = 0;
this.pageSize = 10;
this.itemCount = 0;
},
url: function() {
return this.baseUrl + "?page=" + this.currentPage + "&items=" + this.pageSize;
},
_parseHeaders: function(collection,response){
var totalItems = response.xhr.getResponseHeader("X-ItemTotalCount");
if(totalItems){
this.itemCount = parseInt(totalItems);
//trigger an event with arguments (collection, totalItems)
this.trigger("pages:itemcount", this, this.itemCount);
}
}
});
var PostCollection = PagedCollection.extend({
baseUrl: "/posts"
});
Notice we use another own property, baseUrl to simplify extending the PagedCollection. If you need to add your own initialize, call the parent's prototype one like this, or you won't parse the headers:
PagedCollection.protoype.initialize.apply(this,arguments)
You can even add fetchNext and fetchPrevious methods to the collection, where you simply modify this.currentPage and fetch. Remember to add {reset:true} as fetch options if you want to replace one page with the other instead of appending.
Now if your backend for the project is consistent, any resource that allows pagination on the server may be represented using one PagedCollection-based collection on the client, given the same parameters/responses are used.

Google App Engine - Add & Reflect pattern, working around eventual consistency

I'm building a web application on app engine.
In my case, that's built on django-nonrel, but the key point is that it's using Google's datastore.
I love the fact that I don't need to deal with replication, sharding, backups and such, but one thing that is always getting in my way is the eventual consistency, which seems to get in the way of implementing a common web app pattern which I'm calling "Add & Reflect".
Let's say I have a project management app. The Project is its central model.
Now there's a web page page where I see a list of all projects, can add a project, and then I'll reflect back the list of all projects, which should include the project I just added (assuming no errors).
So the pattern goes like this:
Get and display list of existing projects
User adds new project (using a form on that page)
New project is created
As a response, get and display list of existing projects (now includes the new project)
Now the thing is, that due to eventual consistency, there is no guarantee whatsoever that I will get that new project when I get a list of all projects right after adding a new project.
Now that would be fine if this momentary inconsistency happened when another request (e.g. another user: user B) requested the list of projects one second after the project was added by the first user (user A), but it's really a problem when user A performs an operation, and does not see the results of his action, therefore does not get feedback.
I have gotten used to doing something like this to work around this problem:
def create_project(request):
response_context = {}
new_project = Project(name=request.POST['name'])
project.save()
response_context['projects'] = Project.get_serialized_projects()
# on GAE, eventual consistency means we are not guaranteed to see the
# new projects while querying for all projects, therefore we might need
# to add it manually...
if project.serialize() not in response_context['projects']:
response_context['projects'].append(project.serialize())
return render('projects.html', response_context)
The problem is that this happens in many places in my code, so I'm thinking maybe I'm missing something there, since this pattern is such a basic web app pattern.
Any suggestions for other ways to handle this?
Yes its a common issue. No theres no magic fix.
From client-side once you know the commit succeeded you can save the item locally (globals or storage) and then when querying from datastore merge your saved data. Put an expiration on it so its temporary. Its not trivial to make it work in all cases (say added an item then removed/renamed it so also update cache etc).
From server-side its common to cache recent saves in memcache and also merge with your queries.

Can we store Sitecore item in session

Can we store Sitecore item in session.
Below are the example. Here this item [ "/sitecore/content/Vitality/Data/my_points/mypoints" ]
I have to use in many functions because. I want to avoid to use static path so want to store this in session. Is there any way to handle this.
Sitecore.Data.Items.Item item = contextDB.GetItem("/sitecore/content/Vitality/Data/my_points/mypoints");
In my opinion you should avoid storing Sitecore items in session. This can lead to potential memory leaks and those items will be stored for every session which is not yet expired on the server side for every new session so your application memory use might grow without any way of handling this.
In theory Sitecore built-in cache should work fine but if it's not enough for you may try to cache only some field values that you need to use for the item in session instead of storing the whole Item object. Still you need to remember that you need to handle item changes in a custom way so every time the item is changed, cached values are cleared or renewed.
Agree with Maras Musielak. Storing the item in session is not the way to go.
This actually seems like one of the few occasions where a simple static class would be useful:
static class CommonItems
{
public static Item MyPoints
{
get
{
return Sitecore.Context.Database.GetItem("{110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}");
}
}
}
Sitecore.Data.Items.Item item = CommonItems.MyPoints;
On a side note, it's almost always better to instantiate an item from its GUID rather than its path as this means your code its not as dependant on the structure of the sitecore tree.
I agree with the others in that you should not store the item in session. The Sitecore item cache will help you from a memory usage perspective.
TwentyGotoTen's idea of using a static getter is a good way to go to allow you to centralize your retrieval of the item so that your code doesn't have to be copy-pasted around.
Another extension to that idea is to not reference the content itself in the code, but instead load a configuration item from the content tree which can be edited to point at different content items. In that way, your authors can change which data item is being referenced without updating the static getter.
This involves creating some sort of item in your tree to use for configurations, and having a Generic Link Field item on it that can be used to link to your my points data item.
This is a more complex solution, but allows for complete separation of the content that authors might have access to from the code. If your myPoints information is not going to be accessed by authors, and is essentially a 'developer' item, then this extra layer of separation would not be needed.
Example (based on TwentyGotoTen's):
static class CommonItems
{
public static Item MyPoints
{
get
{
var referenceItem = Sitecore.Context.Database.GetItem("{12345678-ABCD-12AB-1A1A-1234567890}");
var myPointsField = referenceItem.Fields["myPoints"];
if(myPointsField != null)
{
LinkField linkField = (LinkField)myPointsField;
if(linkField.IsInternal && linkField.TargetItem != null){
return linkField.TargetItem;
}
}
return null;
}
}
}
}
Just from my practical experience you can save sitecore item to session if you are using InProc session. If you are using any option other than InProc, you cannot save it session as the Item class is not serializable. Had some issues with Glass mapper classes where I explicitly stored the item inside the model class and added it to session.
Sitecore itself stores specific items in static variables through its API. One example is the FieldIDs class. So yes it's totally valid to do the same in your libraries.
Drazen
Be especially aware of load-balanced solutions.
You might lose your session, or need to make all things stored in the session serializable.
That said, take a look at CompiledDomainModel on a neat way to handle your configuration items.

Sitecore: Seemingly Random errors appear

we are experiencing some very odd errors in our installation.
Some times out of nowhere Sitecore throws an error:
Assert: Value Cannot be null. Parameter: Item.
The closest i have come to identifying the problem is narrowing it down to either an index or the web database.
Anyway, if I log into sitecore the Item is just missing, i can fix it in 3 ways:
Rebuild the index.
Recycle app pool
iisreset
Does any of you have an idea why this might be happening? We are running Sitecore.NET 6.5.0 (rev. 120706). Any help will be deeply appreciated.
You are describing a system stability issue, so I recommend opening a ticket with Sitecore support (http://support.sitecore.net). This sort of issue is difficult to troubleshoot over Stack Overflow, since we do not have access to your logs and configuration.
When opening the ticket, I recommend using the Support Package Generator which bundles up all the information (Web.config, App_Config files, IIS settings, Sitecore log files) that Sitecore Support needs to troubleshoot the issue. It's a pretty nifty tool.
That said, from what you describe, it sounds like the issue is related to caching. The fact that restarting IIS resolves the issue indicates that the item is in the Web database, but the runtime doesn't see it. You can prove out whether this is the issue by clearing cache using the /sitecore/admin/cache.aspx screen. If your cache is not getting updated properly, you should review your configuration against the guidelines in the SDN Scaling Guide.
Based on knowing you're using the Advanced Database Crawler, your issue may be how you're converting a SkinnyItem to an Item. I've had this issue before. If you look at the SkinnyItem.cs class, there's a GetItem() method to convert it into an Item. You can see it uses the database to get the item by its ID, language, and version number. Its possible that when you publish from master to web, you are publishing a new version # of an existing item and thus the new version exists in the web DB, but the index is not updated and references the old version. So, this GetItem() call would use the previous version # and the item would be null. One way to fix this is instead of calling that GetItem() method, just use your own code to get the latest version of that item from Sitecore, e.g.
Item item = Sitecore.Context.Database.GetItem(someSkinnyItem.ItemID);
Instead of
Item item = someSkinnyItem.GetItem();
Here's an example flow:
Foo item created in master DB as version 1.
Publish Foo to web
Index will pick up version 1 in web DB and put in index.
Any querying code against index will convert the SkinnyItem to an Item via that GetItem() method and will pass 1 as the version #.
Page will load, no error in log
Back in master, create version 2 of Foo and publish.
Index may not get updated right away or even if configured wrong.
Code that looks against index will call GetItem() and still call with version 1 since that's in the index
But when you published, web no longer has version 1, it now has version 2, and thus that specific version of that item Foo is null
Error shows in log
On a similar note, here's a blog post by Alex Shyba (creator of the ADC) on how to sync HTML cache clearing with the index updates. That may help.