Custom xmlsitemapprovider can't set Title of sitemapnode? - sitemap

I created my custom XmlSiteMapProvider. Works out great but I want to be able to change the Title of the SiteMap also in there. However I get exceptions that Title is suddenly readonly while the property has a setter when viewing in the Object Browser. Anyone can explain this behavior?
I've been trying to create a new node, use the RemoveNode method to delete the current one and then use the AddNode method. However when I use the method with 1 parameter it starts to complain (exception that the parentNode can't be null). If I use the method overload with 2 parameters and want to pass in the parentNode of the node to delete it's also null. How to solve this?

Apparently it's not possible due to optimizations by Microsoft. The base class to be used instead is StaticSiteMapProvider.

Related

How are request parameters mapped into RenderingModel of magnolia?

Im using Magnolia RenderingModel in combination with Freemarker.
I have URLs like the following:
http://anyPath/context?productTypes=XXXXX&productTypes=YYYYY
my rendering model class looks like:
class MyModel extends RenderingModelImpl {
...
private String[] productTypes;
...
}
However the mentioned array contains only the first value, but not the second.
I checked the behaviour of template directives like ctx.getParameters(). This shows the same behaviour, I get only the first value returned. But if im using ctx.getParameterValues(paramName), it returns both values.
This leads me to following questions:
How would I go, if I want to lookup how the request parameters are mapped into the rendering model, or better:
How can i change the behaviour of that ?
Can anyone acknowledge, that this behaviour is wrong ?
It used to be mentioned in documentation and I believe it still is - if you use .getParameters() you get only first value for multivalue parameter. If you want to get all the values, you need to use .getParameterValues(String param).
From what I understand reasons for that were backward compatibility.
As for changing the behavior, you would need to write your own renderer (e.g. by extending default FreemarkerRenderer and override info.magnolia.rendering.renderer.AbstractRenderer.newModel(Class<T>, Node, RenderableDefinition, RenderingModel<?>) method which instantiates and populates the model class.
Alternatively you can provide fix for above set population method and submit it to Magnolia as a patch. While the .getParameters() behavior is iirc on purpose, the model param population might not be, so you have high chance of getting that changed.

best practice to get the default object wrapper?

when creating custom method, I implements TemplateMethodModelEx and returns SimpleSequence object.
according to the API, I should use this constructor:
SimpleSequence(ObjectWrapper wrapper)
since I am setting incompatibleImprovements as 2.3.24, the doc said I can simply use Configuration instance's getObjectWrapper(). My problem is when implementing TemplateMethodModelEx, I have no access to the current config unless I pass cfg to the method's constuctor. then the root.put would look like:
root.put("getMeList", new GetMeListMethod(cfg));
this looks odd to me, i wonder whats the right to construct this kind of SimpleSquence model and whats the right way to get the default object wrapper.
Thanks a lot
You should pass in the ObjectWrapper as the constructor parameter. (It's unrelated to incompatibleImprovements 2.3.24.) Any TemplateModel that creates other TemplateModel-s (like TemplteSequenceModel-s, TemplateHashModel-s, TemplateMethodModel-s) used to work like that. This is normally not apparent because they are created by an ObjectWrapper. If you do the TemplateModel-s manually however (which is fine), then you will face this fact.

Get Class of Map in FreeMarker

I want to get a variable's class type in freemarker, used var.class.simpleName;
but if var is a Map, freemarker will process class as a key to find value in var.
it throw exception. how can I do this ? thanks for any suggestion.
First I have to ask why do you need that, because FreeMarker templates aren't supposed to know even if var is Map at all. Maybe your data-model is not what the template needs.
Anyway, for now, I would write a custom TemplateMethodModelEx for this purpose, something that you can use like ${classOf(var)}. Inside the TemplateMethodModelEx implementation you will receive a TemplateModel as the argument value, and then you can check if it's an AdapterTemplateModel, and if so you can get back the original object and get its class. (If it's not a AdapterTemplateModel, then it perhaps isn't even a wrapped Java object, so it doesn't make sense to ask what the class of the original object is.) However, the DefaultObjectWrapper with incompatibleImprovements set to less than 2.3.22 doesn't give AdapterTemplateModel to wrapped Map-s... so in 2.3.21 you will still have to use BeansWrapper, but you can at least set simpleMapWrapper to true.
In 2.3.22 it will be actually possible to write ${var?api.class}... you might use the nightly build. Though it only supposed to solve the problem where you can't access business methods because the primary type of the business class is Map.

NSTreeController fetch predicate based on transient isRoot exceptions

My document-based Cocoa application uses a NSOutlineView/NSTreeController combo, bound to the document's Core Data store. My NSTreeController has the fetch predicate isRoot == YES. isRoot is a transient boolean attribute with a default value of NO. My root model's awakeFromInsert calls:
[self setIsRoot:[NSNumber numberWithBool:YES]];
I'm able to add objects to the hierarchy just fine, but when I try to load a document I just saved, I get an exception:
[<NSDictionaryMapNode 0x1001a8190> valueForUndefinedKey:]: this class is not key value coding-compliant for the key isRoot.
I can work around this exception and successfully load a newly-saved document if I change the isRoot attribute to non-transient in the xcdatamodel, but based on my understanding of the transient flag it should not cause a problem, and this really isn't the kind of data that should be persisted.
I have also tried implementing -isRoot in the NSManagedObject subclasses to return the appropriate fixed value, as well as making the same setIsRoot: call within awakeFromFetch, both to no avail.
Is there some other subtlety I'm missing? I can't imagine that fetch predicates don't support transient attributes. I don't know much about the inner workings of Core Data but it seems interesting that it's trying to look up isRoot on the store-specific class and not my NSManagedObject subclass.
I can't imagine that fetch predicates
don't support transient attributes.
After a bit of research, I can tell you that they don't. See this document. Quote:
You cannot fetch using a predicate
based on transient properties
(although you can use transient
properties to filter in memory
yourself).
I've put together a test project and can verify I get exactly the same error as you do.
When I need to filter out the root nodes in a tree, I use a fetch predicate of parent == nil instead of a transient attribute.
I understand your reaction - I too wanted way of having an attribute specifically called isRoot too. My guess is it's possible, but it'd take so much code it's just not worth the hassle.
Oh, and if you're dealing with core data any more than a little, mogenerator will make your life much easier.
Another option is to have a separate class for the top-level nodes, use that class name as "Entity Name" and leave "Fetch Predicate" blank. As long as the child nodes have the same values as the top-level node (I use a common superclass/entity inheritance), everything still works.
Have you made sure that the NSTreeController is set to control an entity rather than a class?
From your error, it looks like it might be set to a class with the default - NSMutableDictionary.
I'd also argue that maybe isRoot could be persisted. It depends on what you're trying to do with your app, of course, but if it's a tree view that gets loaded on app run I'd either make isRoot persist.

mvc 2.0 updatemodel and my ID Column

I have created a create view within my MVC 2.0 Application and by default it included a field for the integer ID Column.
This is definitely a field i do not need.
If i remove the field and use updatemodel when trying to create the object in code, will something break because it doesnt see my ID column data being passed in, even though it is auto increment?
Also, i noticed that in the NerdDinner example, updatemodel was used and after that the repository.save method was called.
I thought that updatemodel would save the object to the database..why then call the .save method afterwards? Or have i missed something?
Any help with this would be appreciated.
Cheers
As I understand it, the UpdateModel method will blow away all data in the object. This is because MVC is round-trip based. Anything that goes out should come back if you need to keep state. See this question for more details.
A better way to handle this scenario in my opinion is to have an input model class as an Action parameter which is passed to a service call to update the domain entity in the DB. (Or this mapping code could be right in the action method if you really want.)
Also please be aware of the security vulnerabilities that could be introduced by binding directly to your DB model.

Resources