Access to a property in a specific resource via sightly - aem

I want to access to a specific property in resource.
The main resource hat two children and the app is in the first one. I want to get a property from the second child.
Can i find something like :
${resource.parent.child[1].valueMap.title}
Thanks!

To start - note that the order of the children may not be guaranteed, unless you're using sling:OrderedFolder or some other ordered type. So trying to get the "second" child may not even make sense.
Having said that, there may some valid use cases that I am not thinking of for needing to get the second child -- as far as I can tell you will need to create a Java or JS object and make use of the Use Api.
Simple Example Java object
package apps.your_app.components.yourComponent;
import com.adobe.cq.sightly.WCMUsePojo;
import org.apache.sling.api.resource.Resource;
import java.util.Iterator;
public class Model extends WCMUsePojo {
#Override
public void activate() throws Exception {
//do some stuff if needed
}
public Resource getSecondSibling() {
Resource parent = getResource().getParent();
Resource secondSib = null;
Iterator<Resource> children = parent.listChildren();
//find the second child
for (int i = 0; i < 2; i++)
secondSib = children.next();
return secondSib;
}
}
Using it in the sightly:
<sly data-sly-use.model="Model">${model.secondSibling.propertyName}</sly>

Here's another example that i used with converting the content to JSON. The contents of the JSON as parsed Objects and each Object has Attributes.
<div data-sly-use.jsonHelper="${'com.service.helpers.JSONHelper'
#json=model.getRawJson}">
${jsonHelper.parsedJSON[item].commodityList[subitem].name}
...
</div>

Related

Umbraco 7 generic node class

With the help of other Stackoverflow users, I have gone some way to my solution but have come to a halt.
I would like to build some generic classes in an app_code .cshtml file eg one would be to return property values from documents from a function eg
public static string docFieldValue(int docID,string strPropertyName){
var umbracoHelper = new Umbraco.Web.UmbracoHelper(Umbraco.Web.UmbracoContext.Current);
var strValue = "";
try{
strValue = umbracoHelper.Content(docID).GetPropertyValue(strPropertyName).ToString();
}
catch(Exception ex){
strValue = "Error - invalid document field name (" + strPropertyName + ")";
}
var nContent = new HtmlString(strValue);
return nContent;
}
This works ok for returning one field (ie property) from a document. However, if I wanted to return 2 or more, ideally, I would store the returned node in a variable or class and then be able to fetch property values repeatedly without having to look up the document with each call
ie without calling
umbracoHelper.Content(docID).GetPropertyValue(strPropertyName).ToString();
with different strPropertyName parameters each time, as I assume that will mean multiple reads from the database).
I tried to build a class, with its properties to hold the returned node
using Umbraco.Web;
using Umbraco.Core.Models;
...
public static Umbraco.Web.UmbracoHelper umbracoHelper = new Umbraco.Web.UmbracoHelper(Umbraco.Web.UmbracoContext.Current);
public static IPublishedContent docNode;
...
docNode = umbracoHelper.Content(docID);
but this crashed the code. Can I store the node in a property on a class, and if so, what type is it?
First of all, using a .cshtml file is unnecessary, use a .cs file instead :-) CSHTML files are for Razor code and HTML and stuff, CS files are for "pure" C#. That might also explain why your last idea crashes.
Second of all, UmbracoHelper uses Umbracos own cache, which means that the database is NOT touched with every request. I would at least define the umbracoHelper object outside of the method (so it gets reused every time the method is called instead of reinitialised).
Also, beware that property values can contain all kinds of other object types than strings.
EDIT
This is an example of the entire class file - my example namespace is Umbraco7 and my example class name is Helpers:
using Umbraco.Web;
namespace Umbraco7
{
public class Helpers
{
private static UmbracoHelper umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
private static dynamic docNode;
public static string docFieldValue(int docID, string strPropertyName)
{
docNode = umbracoHelper.Content(docID);
return docNode.GetPropertyValue(strPropertyName).ToString();
}
}
}
This is an example how the function is called inside a View (.cshtml file inside Views folder):
#Helpers.docFieldValue(1076, "introduction")
Helpers, again, is the class name I chose. It can be "anything" you want. I've just tested this and it works.
I suggest you read up on general ASP.NET MVC and Razor development, since this is not very Umbraco specific.

How to restrict a component to add only once per page

How to restrict a CQ5/Custom component to add only once per page.? I want to restrict the drag and drop of component into the page when the author is going to add the same component for the second time into the same page.
One option is to include the component directly in the JSP of the template and exclude it from the list of available components in the sidekick. To do so, add the component directly to your JSP (foundation carousel in this example):
<cq:include path="carousel" resourceType="foundation/components/carousel" />
To hide the component from the sidekick, either set:
componentGroup: .hidden
or exclude it from the list of "Allowed Components" using design mode.
If you need to allow users to create a page without this component you can provide a second template with the cq:include omitted.
Thanks Rampant, I have followed your method and link stated.
Posting link again : please follow this blog
It was really helpful. I am posting the implementation whatever I have done.
It worked fine for me. One can definitely improve the code quality, this is raw code and is just for reference.
1.Servlet Filter
Keep this in mind that,if any resource gets refereshed, this filter will execute. So you need to filter the contents at your end for further processing.
P.S. chain.doFilter(request,response); is must. or cq will get hanged and nothing will be displayed.
#SlingFilter(generateComponent = false, generateService = true, order = -700,
scope = SlingFilterScope.REQUEST)
#Component(immediate = true, metatype = false)
public class ComponentRestrictorFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {}
#Reference
private ResourceResolverFactory resolverFactory;
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
WCMMode mode = WCMMode.fromRequest(request);
if (mode == WCMMode.EDIT) {
SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request;
PageManager pageManager = slingRequest.getResource().getResourceResolver().adaptTo(PageManager.class);
Page currentPage = pageManager.getContainingPage(slingRequest.getResource());
logger.error("***mode" + mode);
if (currentPage != null )) {
ComponentRestrictor restrictor = new ComponentRestrictor(currentPage.getPath(), RESTRICTED_COMPONENT);
restrictor.removeDuplicateEntry(resolverFactory,pageManager);
}
chain.doFilter(request, response);
}
}
public void destroy() {}
}
2.ComponentRestrictor class
public class ComponentRestrictor {
private String targetPage;
private String component;
private Pattern pattern;
private Set<Resource> duplicateResource = new HashSet<Resource>();
private Logger logger = LoggerFactory.getLogger(ComponentRestrictor.class);
private Resource resource = null;
private ResourceResolver resourceResolver = null;
private ComponentRestrictorHelper helper = new ComponentRestrictorHelper();
public ComponentRestrictor(String targetPage_, String component_){
targetPage = targetPage_ + "/jcr:content";
component = component_;
}
public void removeDuplicateEntry(ResourceResolverFactory resolverFactory, PageManager pageManager) {
pattern = Pattern.compile("([\"']|^)(" + component + ")(\\S|$)");
findReference(resolverFactory, pageManager);
}
private void findReference(ResourceResolverFactory resolverFactory, PageManager pageManager) {
try {
resourceResolver = resolverFactory.getAdministrativeResourceResolver(null);
resource = resourceResolver.getResource(this.targetPage);
if (resource == null)
return;
search(resource);
helper.removeDuplicateResource(pageManager,duplicateResource);
} catch (LoginException e) {
logger.error("Exception while getting the ResourceResolver " + e.getMessage());
}
resourceResolver.close();
}
private void search(Resource parentResource) {
searchReferencesInContent(parentResource);
for (Iterator<Resource> iter = parentResource.listChildren(); iter.hasNext();) {
Resource child = iter.next();
search(child);
}
}
private void searchReferencesInContent(Resource resource) {
ValueMap map = ResourceUtil.getValueMap(resource);
for (String key : map.keySet()) {
if (!helper.checkKey(key)) {
continue;
}
String[] values = map.get(key, new String[0]);
for (String value : values) {
if (pattern.matcher(value).find()) {
logger.error("resource**" + resource.getPath());
duplicateResource.add(resource);
}
}
}
}
}
3.To remove the node/ resource
Whichever resource you want to remove/delete just use PageManager api
pageManeger.delete(resource,false);
That's it !!! You are good to go.
None of the options looks easy to implement. The best approach I found is to use the ACS Commons Implementation which is very easy and can be adopted into any project.
Here is the link and how to configure it:
https://github.com/Adobe-Consulting-Services/acs-aem-commons/pull/639
Enjoy coding !!!
you can't prevent that without doing some massive hacking to the ui code, and even then, you've only prevented it from one aspect of the ui. there's still crxde, and then the ability to POST content.
if this is truly a requirement, the best approach might be the following:
have the component check for a special value in the pageContext object (use REQUEST_SCOPE)
if value is not found, render component and set value
otherwise, print out a message that component can only be used once
note that you can't prevent a dialog from showing, but at the very least the author has an indication that that particular component can only be used once.
It sounds like there needs to be clarification of requirements (and understanding why).
If the authors can be trained, let them manage limits of components through authoring and review workflows.
If there is just 1 fixed location the component can appear, then the page component should include the content component, and let the component have an "enable" toggle property to determine if it should render anything. The component's group should be .hidden to prevent dragging from the sidekick.
If there is a fixed set of locations for the component, the page component can have a dropdown of the list of locations (including "none"). The page render component would then conditionally include the component in the correct location. Again, prevent dragging the component from the sidekick.
In the "hard to imagine" case that the component can appear anywhere on the page, added by authors, but limited to only 1 instance - use a wrapper component to manage including the (undraggable) component. Let the authors drag the wrapper on the page as many times as they want, but the wrapper should query the page's resources and determine if it is the first instance, and if so, include the end component. Otherwise, the wrapper does nothing.
In our experience (>2years on CQ), implementing this type of business rules via code creates a brittle solution. Also, requirements have a habit of changing. If enforced via code, development work is required instead of letting authors make changes faster & elegantly.
None of these options are that great. If you truly want a robust solution to this problem (limit the number of items on the page without hardcoding location) then the best way is with a servlet filter chain OSGI service where you can administer the number of instances and then use a resource resolver to remove offending instances.
The basic gist is:
Refresh the page on edit using cq:editConfig
Create an OSGI service implementing javax.servlet.Filter that encapsulates your business rules.
Use the filter to remove excess components according to business rules
Continue page processing.
For more details see here:
Using a servlet filter to limit the number of instances of a component per page or parsys
This approach will let you administer the number of items per page or per parsys and apply other possibly complex business rules in a way that the other offered solutions simply cannot.

Wicket - Wrapped collection Model "transformation"

I have a domain object which has a collection of primitive values, which represent the primary keys of another domain object ("Person").
I have a Wicket component that takes IModel<List<Person>>, and allows you to view, remove, and add Persons to the list.
I would like to write a wrapper which implements IModel<List<Person>>, but which is backed by a PropertyModel<List<Long>> from the original domain object.
View-only is easy (Scala syntax for brevity):
class PersonModel(wrappedModel: IModel[List[Long]]) extends LoadableDetachableModel[List[Person]] {
#SpringBean dao: PersonDao =_
def load: List[Person] = {
// Returns a collection of Persons for each id
wrappedModel.getObject().map { id: Long =>
dao.getPerson(id)
}
}
}
But how might I write this to allow for adding and removing from the original List of Longs?
Or is a Model not the best place to do this translation?
Thanks!
You can do something like this:
class PersonModel extends Model<List<Person>> {
private transient List<Person> cache;
private IModel<List<String>> idModel;
public PersonModel( IModel<List<String>> idModel ) {
this.idModel = idModel;
}
public List<Person> getObject() {
if ( cache == null ) {
cache = convertIdsToPersons( idModel.getObject() );
return cache;
}
public void setObject( List<Person> ob ) {
cache = null;
idModel.setObject( convertPersonsToIds( ob ) );
}
}
This isn't very good code but it shows the general idea. One thing you need to consider is how this whole thing will be serialised between requests, you might be better off extending LoadableDetachableModel instead.
Another thing is the cache: it's there to avoid having to convert the list every time getObject() is called within a request. You may or may not need it in practice (depends on a lot of factors, including the speed of the conversion), but if you use it, it means that if something else is modifying the underlying collection, the changes may not be picked up by this model.
I'm not quite sure I understand your question and I don't understand the syntax of Scala.
But, to remove an entity from a list, you can provide a link that simply removes it using your dao. You must be using a repeater to populate your Person list so each repeater entry will have its own Model which can be passed to the deletion link.
Take a look at this Wicket example that uses a link with a repeater to select a contact. You just need to adapt it to delete your Person instead of selecting it.
As for modifying the original list of Longs, you can use the ListView.removeLink() method to get a link component that removes an entry from the backing list.

Why does getting the nth child of a Node fail in an ExplorerManager listener?

I'm having problems with the NetBeans Nodes API.
I have this line of code:
Node n = (new MyNode(X)).getChildren().getNodeAt(Y);
The call to new MyNode(X) with the same X always initializes a MyNode the same way, independent of the context.
When I place it by itself (say, in an menu action), it successfully gets the Yth child, but if I put it in an event where other Node/Children stuff happens, it returns null.
MyNode's Children implementation is a trivial subclass of Children.Keys, which is approximately:
// Node
import org.openide.nodes.AbstractNode;
class MyNode extends AbstractNode {
MyNode(MyKey key) {
super(new MyNodeChildren(key));
}
}
// Children
import java.util.Collections;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
public class MyNodeChildren extends Children.Keys<MyKey> {
MyKey parentKey;
MyNodeChildren(MyKey parentKey) {
super(true); // use lazy behavior
this.parentKey = parentKey;
}
#Override
protected Node[] createNodes(MyKey key) {
return new Node[] {new MyNode(key)};
}
#Override
protected void addNotify() {
setKeys(this.parentKey.getChildrenKeys());
}
#Override
protected void removeNotify() {
setKeys(Collections.EMPTY_SET);
}
}
// MyKey is trivial.
I assume this has something to do with the lazy behavior of Children.Keys. I have the sources for the API, and I've tried stepping through it, but they're so confusing that I haven't figured anything out yet.
NetBeans IDE 7.0.1 (Build 201107282000) with up-to-date plugins.
Edit: More details
The line with the weird behavior is inside a handler for an ExplorerManager selected-nodes property change. The weird thing is that it still doesn't work when the MyNode instance isn't in the heirarchy that the ExplorerManager is using (it's not even the same class as the nodes in the ExplorerManager), and isn't being used for anything else.
Accessing the nodes instead of the underlying model is actually necessary for my use case (I need to do stuff with the PropertySets), the MyNode example is just a simpler case that still has the problem.
It is recommended to use org.openide.nodes.ChildFactory to create child nodes unless you have a specific need to use one of the Children APIs. But for the common cases the ChildFactory is sufficient.
One thing to keep in mind when using the Nodes API is that it is only a presentation layer that wraps your model and used in conjunction with the Explorer API makes it available to the various view components in the NetBeans platform such as org.openide.explorer.view.BeanTreeView.
Using a model called MyModel which may look something like:
public class MyModel {
private String title;
private List<MyChild> children;
public MyModel(List<MyChild> children) {
this.children = children;
}
public String getTitle() {
return title;
}
public List<MyChild> getChildren() {
return Collections.unmodifiableList(children);
}
}
You can create a ChildFactory<MyModel> that will be responsible for creating your nodes:
public class MyChildFactory extends ChildFactory<MyModel> {
private List<MyModel> myModels;
public MyChildFactory(List<MyModel> myModels) {
this.myModels = myModels;
}
protected boolean createKeys(List<MyModel> toPopulate) {
return toPopulate.addAll(myModels);
}
protected Node createNodeForKey(MyModel myModel) {
return new MyNode(myModel);
}
protected void removeNotify() {
this.myModels= null;
}
}
Then, implementing MyNode which is the presentation layer and wraps MyModel:
public class MyNode extends AbstractNode {
public MyNode(MyModel myModel) {
this(myModel, new InstanceContent());
}
private MyNode(MyModel myModel, InstanceContent content) {
super(Children.create(
new MyChildrenChildFactory(myModel.getChildren()), true),
new AbstractLookup(content)); // add a Lookup
// add myModel to the lookup so you can retrieve it latter
content.add(myModel);
// set the name used in the presentation
setName(myModel.getTitle());
// set the icon used in the presentation
setIconBaseWithExtension("com/my/resouces/icon.png");
}
}
And now the MyChildrenChildFactory which is very similar to MyChildFactory except that it takes a List<MyChild> and in turn creates MyChildNode:
public class MyChildFactory extends ChildFactory<MyChild> {
private List<MyChild> myChildren;
public MyChildFactory(List<MyChild> myChildren) {
this.myChildren = myChildren;
}
protected boolean createKeys(List<MyChild> toPopulate) {
return toPopulate.addAll(myChildren);
}
protected Node createNodeForKey(MyChild myChild) {
return new MyChildNode(myChild);
}
protected void removeNotify() {
this.myChildren = null;
}
}
Then an implementation of MyChildNode which is very similar to MyNode:
public class MyChildNode extends AbstractNode {
public MyChildNode(MyChild myChild) {
// no children and another way to add a Lookup
super(Children.LEAF, Lookups.singleton(myChild));
// set the name used in the presentation
setName(myChild.getTitle());
// set the icon used in the presentation
setIconBaseWithExtension("com/my/resouces/child_icon.png");
}
}
And we will need the children's model, MyChild which is very similar to MyModel:
public class MyChild {
private String title;
public String getTitle() {
return title;
}
}
Finally to put it all to use, for instance with a BeanTreeView which would reside in a TopComponent that implements org.openide.explorer.ExplorerManager.Provider:
// somewhere in your TopComponent's initialization code:
List<MyModel> myModels = ...
// defined as a property in you TC
explorerManager = new ExplorerManager();
// this is the important bit and we're using true
// to tell it to create the children asynchronously
Children children = Children.create(new MyChildFactory(myModels), true);
explorerManager.setRootContext(new AbstractNode(children));
Notice that you don't need to touch the BeanTreeView and in fact it can be any view component that is included in the platform. This is the recommended way to create nodes and as I've stated, the use of nodes is as a presentation layer to be used in the various components that are included in the platform.
If you then need to get a child you can use the ExplorerManager which you can retrieve from the TopComponent using the method ExplorerManager.Provier.getExplorerManager() which was implemented due to the fact that your TopComponent implemented ExplorerManager.Provider and is in fact the way that a view component itself gets the nodes:
ExplorerManager explorerManager = ...
// the AbstractNode from above
Node rootContext = explorerManager.getRootContext();
// the MyNode(s) from above
Children children = rootContext.getChildren().getNodes(true);
// looking up the MyModel that we added to the lookup in the MyNode
MyModel myModel = nodes[0].getLookup().lookup(MyModel.class);
However, you must be aware that using the Children.getNodes(true) method to get your nodes will cause all of your nodes and their children to be created; which weren't created due to the fact that we told the factory that we wanted it to create the children asynchronously. This is not the recommended way to access the data but instead you should keep a reference to the List<MyModel> and use that if at all possible. From the documentation for Children.getNodes(boolean):
...in general if you are trying to get useful data by calling this method, you are probably doing something wrong. Usually you should be asking some underlying model for information, not the nodes for children.
Again, you must remember that the Nodes API is a presentation layer and is used as an adapter between your model and your views.
Where this becomes a powerful technique is when using the same ChildFactory in different and diverse views. You can reuse the above code in many TopComponents without any modifications. You can also use a FilterNode if you need to change only a part of the presentation of a node without having to touch the original node.
Learning the Nodes API is one of the more challenging aspects of learning the NetBeans platform API as you have undoubtedly discovered. Once you have some mastery of this API you will be able to take advantage of much more of the platforms built in capabilities.
Please see the following resources for more information on the Nodes API:
NetBeans Nodes API Tutorial
Great introduction to the Nodes API by Antonio Vieiro
Part 5: Nodes API and Explorer & Property Sheet API by Geertjan Wielenga
JavaDocs for the Nodes API
Timon Veenstra on the NetBeans Platform Developers mailing list solved this for me.
Actions on the explorerManager are guarded to ensure consistency. A
node selection listener on an explorer manager for example cannot
manipulate the same explorer manager while handling the selection
changed event because that would require a read to write upgrade. The
change will be vetoed and die a silent death.
Are you adding the MyNode root node to the explorer manager on
initialization, or somewhere else in a listener?
My problem line is in an ExplorerManager selection change listener. I guess the Children.MUTEX lock is getting set by ExplorerManager and preventing the Children.Keys instance from populating its Nodes...?
Anyways, I moved my Node access into a EventQueue.invokeLater(...), so it executes after the selection changed event finishes, and that fixed it.

MEF: how to import from an exported object?

I have created a MEF plugin control that I import into my app. Now, I want the plugin to be able to import parts from the app. I can't figure how setup the catalog in the plugin, so that it can find the exports from the app. Can somebody tell me how this is done? Below is my code which doesn't work when I try to create an AssemblyCatalog with the current executing assembly.
[Export(typeof(IPluginControl))]
public partial class MyPluginControl : UserControl, IPluginControl
[Import]
public string Message { get; set; }
public MyPluginControl()
{
InitializeComponent();
Initialize();
}
private void Initialize()
{
AggregateCatalog catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
CompositionContainer container = new CompositionContainer(catalog);
try
{
container.ComposeParts(this);
}
catch (CompositionException ex)
{
Console.WriteLine(ex.ToString());
}
}
}
You don't need to do this.
Just make sure that the catalog you're using when you import this plugin includes the main application's assembly.
When MEF constructs your type in order to export it (to fulfill the IPluginControl import elsewhere), it'll already compose this part for you - and at that point, will import the "Message" string (though, you most likely should assign a name to that "message", or a custom type of some sort - otherwise, it'll just import a string, and you can only use a single "string" export anywhere in your application).
When MEF composes parts, it finds all types matching the specified type (in this case IPluginControl), instantiates a single object, fills any [Import] requirements for that object (which is why you don't need to compose this in your constructor), then assigns it to any objects importing the type.