Sub-Component Evaluations

cancel
Showing results for 
Search instead for 
Did you mean: 

Sub-Component Evaluations

ddraper
Intermediate II
0 14 7,225

Introduction

In the last blog post I showed how to extend a Component and hide its default Sub-Component through the use of an Evaluation. In this blog post I'm going delve further into Evaluations and introduce Evaluators demonstrating how they should be defined and how they can be used. These extensibility features are available in the latest Alfresco Community source code and will later appear in Alfresco Enterprise 4.0.

Tutorial

We're going to create a similar module to the last one - but instead of hiding the title bar on the user dashboard we're going to hide the title bar on the site dashboard, but only if the site has a specific name.

Update the 'blog-demo.xml' file and add the following module definition:

<module>
    <id>Blog Module (Conditionally Hide Title)</id>
    <components>

        <component>

            <scope>page</scope>

            <region-id>title</region-id>

            <source-id>site/{site}/dashboard</source-id>

            <sub-components>

                <sub-component id='default'>

                    <evaluations>

                        <evaluation id='HideIfBlogDemoSite'>

                            <evaluators>

                                <evaluator type='blog.demo.evaluator'></evaluator>

                            </evaluators>

                            <render>false</render>

                        </evaluation>

                    </evaluations>

                </sub-component>

            </sub-components>

        </component>

    </components>

</module>

Site dashboards are similar to user dashboards in that new configuration is created from a preset for each new site. This means we need to target our component using the {site} parameter (we could of course accomplish the same objective by specifying a specific site in the 'source-id' if we wanted, but this blog is about evaluators!)

The evaluator 'type' attribute must map to a Spring bean id defined in the application context. We therefore need to create this Evaluator and defined it as a Spring bean.

First create the following class:

package blog.demo;

import java.util.Map;
import org.springframework.extensions.surf.RequestContext;
import org.springframework.extensions.surf.extensibility.impl.DefaultSubComponentEvaluator;

public class BlogDemoEvaluator extends DefaultSubComponentEvaluator
{

    public boolean evaluate(RequestContext context, Map<String, String> params)

    {

        boolean result;

        String site = context.getUriTokens().get('site');

        if (site == null)

        {

            site = context.getParameter('site');

        }

        result = (site != null && site.equals('blogdemo'));

        return result;

    }

}

Then create a new file called 'spring-surf-extensibility-context.xml' and place it in a new 'org.springframework.extensions.surf' package in your JAR file (any file that fits the pattern 'org.springframework.extensions.surf.*-context' will get processed into the application context). This file should contain the following:

<?xml version='1.0' encoding='UTF-8'?>
<beans xmlns='http://www.springframework.org/schema/beans'
              xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
              xsi:schemaLocation='http://www.springframework.org/schema/beans
                                  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd'>

    <bean id='blog.demo.evaluator' class='blog.demo.BlogDemoEvaluator'/>
</beans>

Build and deploy your JAR to Alfresco Share as described in the previous blog posts and then restart the web server.

Log on to Alfresco Share and create 2 new sites: 'BlogDemo' and 'SomeOtherSite' - as the new module has not yet been deployed you should see the title bar on both sites.

Screen shot of Blog Demo site with the title displayedScreen shot of Some Other Site With Title displayed

Now go to the Module Deployment WebScript (http://localhost:8080/share/page/modules/deploy - assuming you're using the defaults, adjust as necessary) and deploy the new module (see previous posts for detailed instructions on deploying modules).

When you refresh the dashboard pages for the 'BlogDemo' and 'SomeOtherSite' sites you should see that the title bar is displayed on the latter, but not the former.

Screen shot of Blog Demo Site with no title

Evaluators can also accept parameters in order that they can be easily re-used. Update the 'blog-demo.xml' file so that the evaluator is configured as follows:

<evaluator type='blog.demo.evaluator'>
<params>
        <site>someothersite</site>
    </params>

</evaluator>

...and modify the BlogDemoEvaluator class 'evaluate' method so that it looks like this:

public boolean evaluate(RequestContext context, Map<String, String> params)
{
    boolean result;
    String site = context.getUriTokens().get('site');
    if (site == null)
    {
        site = context.getParameter('site');
    }

String targetSite = params.get('site');
    result = (site != null && site.equals(targetSite));

    return result;
}

Re-build and re-deploy the JAR and then restart the web server and you should now see that the 'SomeOtherSite' title bar is hidden and the 'BlogDemo' title bar is displayed.

Screenshot of Some Other Site With No Title

When setting evaluator parameters the element name is the parameter key and the element value is the parameter value. All of the configured parameters will be passed as the 'params' argument when the 'evaluate' method is called. Parameters can also accept token substitution - so you could set a parameter of:

<site>{site}</site>

Which would make the evaluator pass regardless of whatever site dashboard is being displayed (although this would be a rather pointless exercise!).

Background Information

Evaluations

The purpose of an Evaluation is to dynamically alter a Sub-Component based on the current request. This is done by either changing the Web Script URL, updating the properties passed to the Web Script or preventing the Sub-Component from rendering entirely (which was demonstrated in the previous blog post).

A Sub-Component should be defined with a default Web Script URL and properties which will be used if no evaluations are configured or none of the evaluations that are configured evaluate successfully. If more than one evaluation is defined then each will be processed in turn and the first to evaluate successfully will have the opportunity to override the Web Script URL and/or properties or to hide the Sub-Component.

Evaluations are processed in the order in which they are defined in the configuration file. Evaluations defined in extension modules are processed before evaluations in the 'base' Component configuration. If more than one extension module defines evaluations for the same Sub-Component then they processed in the deployment order defined through the Module Deployment UI (/service/page/modules/deploy).

We've used evaluations internally to update add, hide and change various Sub-Components in the Document Library page depending upon what type of site is being viewed.

Evaluators

Every Sub-Component Evaluator must be configured in the Spring application context as a bean and must implement the org.springframework.extensions.surf.extensibility.SubComponentEvaluator interface. The interface defines a single method called evaluate that takes a org.springframework.extensions.surf.RequestContext and a parameter map as arguments.

A Sub-Component Evaluation will call the evaluate method on each Evaluator that is configured with and will only be considered to have evaluated successfully if every evaluate method returns true.

A number of SubComponentEvaluator instances are already configured in the Alfresco Share application context for testing various criteria (e.g. whether or not the requested page relates to a particular site) but it is a fairly simple exercise to create your own instances.

The RequestContext instance passed into the evaluate method will let you access pretty much everything you need to know about the request being evaluated (there's too much information to list, the best way to see everything available would be to attach a Java debugger to the web server and set a break point in an evaluator an inspect an instance).

<module>

<id>Blog Module (Conditionally Hide Title)</id>

<components>

<component>

<scope>page</scope>

<region-id>title</region-id>

<source-id>site/{site}/dashboard</source-id>

<sub-components>

<sub-component id='default'>

<evaluations>

<evaluation id='HideIfBlogDemoSite'>

<evaluators>

<evaluator type='blog.demo.evaluator'></evaluator>

</evaluators>

<render>false</render>

</evaluation>

</evaluations>

</sub-component>

</sub-components>

</component>

</components>

</module>

14 Comments
blog_commenter
Active Member
Thanks again for these postings.

Compared to other web development frameworks, this seems very 'heavy'.
To have to write a Java bean and configure the bean with a new context xml file seems like a lot of work to simply add a string comparison conditional.  And debugging with a Java debugger adds to the complexity. And each iteration in the development with server restarts seems time consuming.

I really liked that fact that the Alfresco server-side javascript language had moved so far along up until 3.4.x.
Is there a Javascript equivalent to what you've done here?
ddraper
Intermediate II
Thanks for the feedback George...

You're right that this is 'heavy' if all you were doing was a simple String comparison, however since the ultimate result of the evaluation is just a boolean result I didn't see the value in showing a more complex example.  One of the main advantages of dropping back into Java and making the evaluators Spring beans is that you then have complete access to all the other Spring Surf bean resources.

For example, if you wanted to you could write an evaluator that accessed the remote endpoints or used the WebScript framework or you could write your own evaluator that used a server side JavaScript engine, you could use the same evaluator everywhere and then use parameters to reference the scripts you wanted to use to perform the evaluation.

Also, this only describes the default evaluation behaviour - if you really wanted to you could change your Spring Surf configuration to reference your own implementation of the AdvancedComponent interface and create your own default SubComponent evaluation mechanism.

Hopefully we've been able to provide the basic evaluators that people will need 'out-of-the-box' so it won't be necessary to write your own and you won't need to spend a lot of time developing and debugging them. However, we've tried to do is make this as extensible as possible so that people aren't constrained (as they might have been if we'd just done a JavaScript only evaluation mechanism).

The current implementation was able to satisfy our requirements of converting the Resource Management and Portal capabilities into modules so we haven't taken it any further at the moment - however, that's not to say that someone in the Alfresco Community might not decide contribute a JavaScript evaluation mechanism.
blog_commenter
Active Member
Hi David,

do you have example code on how to simply replace the default component with a custom component (global component, without evaluators). I struggled with the syntax and only managed to hide the original component and add an additional subcomponent. It does the job but I believe it should work better by simply replacing the url if I understood correctly.
ddraper
Intermediate II
Hi Florian,

Sure no problem... this is pretty much the most simple code snippet you can provide to achieve this:


<module>
   <id>Replace Default User Dashboard Title</id>
   <components>
      <component>
         <scope>page</scope>
         <region-id>title</region-id>
         <source-id>user/{userid}/dashboard</source-id>
         <sub-components>
            <sub-component id='default'>
               <url>/some/new/webscript</url>
            </sub-component>
         </sub-components>
      </component>
   </components>
</module>


I've tested this locally... and it simply replaces the title bar on user dashboard pages (obviously you need to provide the WebScript that backs the URL I've created). Evaluations are entirely optional, they're only required if you wish to override the default URL and/or properties. In this example I'm overriding the defaults, but if I provided an Evaluation that succeeded then it's URL/properties would get used (if it provided any).

I hope this helps - let me know if you have any more questions,

Thanks,
Dave
blog_commenter
Active Member
Hi David,

thank you for your tutorials. I am following your posts and so far I managed to run your examples, but I'm having problems with this one.
If I follow everithing you said and deploy the jar file into alfresco, what I get is that share stops working displaying this error:

HTTP Status 404 -

type Status report

message

description The requested resource () is not available.

Apache Tomcat/6.0.29

I think the hierarchy of the project packages is right, but can't confirm it. In particular, I am not sure about where to put the compiled class file. Also by manually deploing the files the same result occurs. Can you help me here? Thanks
blog_commenter
Active Member
Hi,

made an example of a type subcomponent evaluator, for the example see http://experiencewithalfresco.blogspot.dk/2012/06/type-subcomponent-evaluator.html

It uses a repo call to get type of the current node ... any smarter way, when you are in Share java-domain?

It seems that the evaluator gets called twice on page load?

/rasmus
ddraper
Intermediate II
Hi Rasmus,

The reason that the evaluator gets called twice on every page is because of the 'double-pass' processing used to handle WebScript dependency requests. Technically every Component and Sub-Component is processed twice (the first time only the .head.ftl file is processed, the second time the rest of the WebScript is processed). This was done originally to allow WebScripts to register JavaScript and CSS dependency files to be imported in the head element of the page - we now have an alternative approach to this which is documented in some of my more recent blogs.

I think that your remote call will be necessary because the only information available will be whatever is currently in the RequestContext and that is unlikely to contain the information that you need.

Regards,
Dave
blog_commenter
Active Member
I'm completely new to Alfresco & Surf and a bit rusty on Spring itself also. I was able to implement the previous two tutorials (via files dropped directly into WEB-INF, not JAR approach, which is required for our project).



This tutorial involves creating a spring bean. I can't quite figure out where to put it in the file hierarchy so that the example works. Currently I get the following error indicating the bean cannot be found:





ERROR [web.context.ContextLoader] [pool-2-thread-1] Context initialization failed org.springframework.beans.factory.CannotLoadBeanClassException:



Cannot find class [blog.demo.BlogDemoEvaluator] for bean with name 'blog.demo.evaluator' defined in file [/home/alexk/workspace/cabnet-alfresco/share/target/share/WEB-INF/classes/org/springframework/extensions/surf/spring-surf-extensibility-context.xml]; nested exception is java.lang.ClassNotFoundException: blog.demo.BlogDemoEvaluator





Could you please specify which file should contain BlogDemoEvaluator class and where it's location in file hierarchy? The same for  spring-surf-extensibility-context.xml.



E.g. one of the combinations I've tried was creating BlogDemoEvaluator.java under src/main/resources/alfresco/side-data/extensions and put spring-surf-extensibility-context.xml there as well. In our dev environment, these automatically get copied to WEB-INF/classes/alfresco/site-data/extensions and therefore get deployed. This is the way I was able to complete the previous two tutorials.
ddraper
Intermediate II
Well, your spring-surf-extensibility-context.xml file is definitely being found and processed (otherwise you wouldn't be getting the ClassNotFoundException for the evaluator. You should be able to place the class (within the correct package structure obviously) directly into the WEB-INF/classes directory.
blog_commenter
Active Member
could anybody explain how can we have evaluator based on aspect I mean how to use hasAspectEvaluator to  hide component?
ddraper
Intermediate II
It really depends upon what page/component you're wanting to use the evaluator on. I'm guessing you want to do some with the details view page and want to change the page depending upon whether or not the node displayed has a particular aspect applied (?)



In which case you should be able to get the nodeRef from the URL tokens and will probably need to use that to make a REST API call to the repository to get the details of what aspects that node has. You can then compare them against the configuration of your module to determine whether or not the evaluation has passed.
blog_commenter
Active Member
Yes I want to display components on document details page based on weather particular aspect is applied on that node or not.

So, you mean to say I have to create my custom Evaluator which extends DefaultSubComponentEvaluator class and need to do whatever you have explained? I was under impression I can use the OOTB evaluator hasAspectEvaluator for this purpose but I guess those are limited to restrict actions only am I correct?
ddraper
Intermediate II
The hasApectEvaluator refers to a DocumentLibrary evaluator (an unfortunate overlapping of the names). You should see that it has a different class hierarchy. Unfortunately there is not an out-of-the-box aspects evaluator for extension modules.
blog_commenter
Active Member
Hi Dave,

Thanks for your prompt reply,It clarify my doubt. if you get a chance please comment on this one I am facing one more problem with module extension

https://forums.alfresco.com/comment/135423#comment-135423