Apache Tiles : change template page at runtime - spring-mvc

I have a question : suppose that in a spring MVC 3.0 enviroment i manage Views with Tiles : I have a xml file with definitions of all views. Every view extends a specific template. I have two templates : one for rendering a completeDOM () and one for partialDOM (.....).The problem is, there are some views that can be retrieved in fullDOM and also in partialDOM, but i don't want to write two similars definitions.
I was thinking to a dynamic approach : inject the template of a view at runtime, specifying an http parameter which should contains the name of the template. If the request contains the parameter, than Tiles should override the template exteded by the view, with the template detected by http parameter value.
Some suggestions?

I know this is an old question but I had needed to do this very thing so I thought I'd share my solution.
Tiles allows what they refer to as "runtime composition", which lets you modify definitions. So you can reuse an existing definition and just swap the template:
<tiles:insertDefinition name="existingDefinition" template="alternateTemplate.jsp" />

In spring tilesConfigurer you need to set mutable container:
<property name="useMutableTilesContainer" value="true"/>
<property name="checkRefresh" value="true"/>
And in your Spring Controller:
ModelAndView model = new ModelAndView();
MutableTilesContainer container = (MutableTilesContainer)ServletUtil.getContainer(request.getSession().getServletContext());
Attribute attribute = new Attribute("your template jsp");
HashMap<String, Attribute> attributes = new HashMap<String, Attribute>();
attributes.put("body", attribute);
Definition definition = new Definition("your definition name", "your jsp", attributes);
definition.setExtends("your definition template name");
definition = PatternUtil.replacePlaceholders(definition, "your definition name", new Object());
container.register(definition, request, response);
model.setViewName("your definition name");

I think a view preparer might help:
http://tiles.apache.org/framework/tutorial/advanced/preparer.html

Related

Update Document Template Dynamically in Alfresco from property page

I have one document template as like attached screen shot. now my requirement is i want to dynamically update some values of that document when i will upload it in alfresco site. i will either create that Document through node template or upload. I want to provide those values from document property page.
can anyone help me how can i achieve this.
I tried it using java script, but i am getting some problem. so i am thinking for using Alfresco CMIS API. But i am quite new in alfresco cmis. so can any one tell the better way which i can use for this task.
Thanks in advance.
I think you should trigger an custom action by a Rule
When someone update a node (for example by editing properties), your action will be triggered and you will be able to :
get the content of your node
get the properties of your node
use the apache poi library provided by alfresco (my guess is that you use word documents) to find your parameters, and the replace them by the value you find in the property nodes.
Here is an excellent tutorial to learn how to create a custom action.
Note : You may have to keep the original templated document if you want to change a second time your properties (by using versionning for example).
Edit (see discussion below) :
I assume you know how to use rules in alfresco : http://docs.alfresco.com/5.0/tasks/library-folder-rules-define.html
Declare a new action to assign to a rule. This action must be triggered when the excel is dropped or updated
Create an action implementing your need :
public class MyAction extends ActionExecuterAbstractBase {
...
#Override
protected void executeImpl(final Action arg0, final NodeRef arg1) {
// your code here
}
....
}
This action will :
Take the nodeRef parameter (which is the excel file) and load the file
...
Inputstream inputstream = getFileFolderService().getReader(arg1).getContentInputStream();
...
NPOIFSFileSystem fs = null;
HSSFWorkbook workbook = null;
try {
fs = new NPOIFSFileSystem(inputstream);
workbook = new HSSFWorkbook(fs.getRoot(), true);
//get your data with POI
For each row of your excel :
Make a copy of your template :
find it :
getSearchService().query(searchParameters)
copy it :
getFileFolderService().copy(sourceNodeRef, targetParentRef, newName)
Do transformations of your new word content (Find occurrences and replace with poi library).
Update the content of your new file :
ContentWriter writerDoc = getContentService().getWriter(document.getNodeRef(), ContentModel.PROP_CONTENT,
true);
writerDoc.putContent(file/inputStream);
In your context file, declare you action :
<bean id="my-action" class="x.y.z.MyAction" parent="action-executer">
<property name="searchService">
<ref bean="searchService" />
</property>
<property name="nodeService">
<ref bean="nodeService" />
</property>
<property name="contentService">
<ref bean="contentService" />
</property>
<property name="fileFolderService">
<ref bean="FileFolderService" />
</property>
....
</bean>
In Share, assign to a folder a rule with the action you have created.

how to hide metadata in web api 2, odata

I have defined odata route using MapODataServiceRoute in my WebApiConfig.
config.Routes.MapODataServiceRoute("CompanyoOdata", "odata", GetImplicitEdm(config));
private static IEdmModel GetImplicitEdm(HttpConfiguration config)
{
ODataModelBuilder builder = new ODataConventionModelBuilder(config, true);
builder.EntitySet<Company>("Company");
builder.EntitySet<Photo>("Photos");
builder.EntitySet<Country>("Country");
return builder.GetEdmModel();
}
The data service works just fine. But I want to achieve few things.
I don't want to expose my metadata or associations because i'm using it internally and will not need metadata. How can I restrict access to these information (i.e restrict access to http://www.sample.com/odata/#metadata or http://www.sample.com/odata/$metadata)
secondly, I want to ignore some properties from getting serialized. I found two ways of doing this.
Using data contracts and marking properties with [DataMember] attribute or [IgnoreDataMember] attribute
Using Ignore method on EntitySet when building the model
I can't use the first method as I'm using Database first approach for entity framework hence can't decorate the entity with attributes. I thought I can achieve this by using MetaDataType, but it seems it only works for DataAnnotations.
I used second method with success, but you can't pass more than one property in the ignore method. Has to do it to individual property that I need to ignore, which is a bit tedious. Is there another way to do this?
any help really appreciated.
If want to hide metadata (/$metadata) or service document (/), can remove the the MetadataRoutingConvention from existing routing conventions, e.g.:
var defaultConventions = ODataRoutingConventions.CreateDefault();
var conventions = defaultConventions.Except(
defaultConventions.OfType<MetadataRoutingConvention>());
var route = config.MapODataServiceRoute(
"odata",
"odata",
model,
pathHandler: new DefaultODataPathHandler(),
routingConventions: conventions);
If only expose a few properties per type, can use ODataModelBuilder instead of ODataConventionModelBuilder. E.g., some example:
ODataModelBuilder builder = new ODataModelBuilder();
EntityTypeConfiguration<Customer> customer = builder.EntitySet<Customer>("Customers").EntityType;
customer.HasKey(c => c.Id);
customer.Property(c => c.Name);

<table:column> Roo-tag for property of referenced entity

I've got a two classes (pupil, class) in a Roo-project and their scaffolded views.
pupil and class have a 1:1 relationship
In the list.jspx of pupil I'd like to display a column for a property of class.
I don't know the correct attributes to give to the table:column-tag.
This following example gives the error:
SpelEvaluationException: EL1027Epos 4): Indexing into type 'com.pupil' is not supported
<table:table data="${pupil}" duplicate="true" id="l_com_pupil" path="/admin/pupil" z="user-managed">
<table:column id="c_com_pupil_pupilName" property="pupilName" z="user-managed"/>
<!-- I'd like to display the attribute teacher_name of the class 'class' here but it doesn't work -->
<table:column id="c_com_pupil_class_teacherName" property="teacherName" z="user-managed"/>
</table:table>
Instead of messing around with the jspx files, you can simply do this by implementing a converter for the Teacher entity within the ApplicationServiceFactoryBean.java.
See the below conversion method for an example.
static class com.mycompany.test.controllers.ApplicationConversionServiceFactoryBean.TeacherConverter implements org.springframework.core.convert.converter.Converter<com.mycompany.test.domain.master.Teacher, java.lang.String> {
public String convert(Teacher teacher) {
return new StringBuilder().append(teacher.getName()).toString();
}
}
By default, Roo generates these converters and they are stored within the ApplicationConversionServiceFactoryBean_Roo_ConversionService.aj file.
You can push in refactor the related method for the Teacher entity from this aspectJ file into the
ApplicationServiceFactoryBean.java file and then implement your own conversion which will be used to show the Teacher name across the application as in the above example.
Cheers and all the best with Roo!
This is how I did it, not for listing, but rather for showing the name of the teacher when you view the pupil entity:
Edit the controller and specifically the method show (in the java file, not in the aj file, of course).
Add an attribute to your UI Model, for instance "teacherName" (use Model.addAttribute), where you populate the teacherName with the desired name.
Add in the show.jspx file something like:
<div><label for="_pupilTeacher">Teacher Name:</label><div class="box">${teacherName}</div></div><br/>
(alternatively, you could create a new tagx file with your own parameters)
Hope it helped.
Radu

How does Spring encode POJOs in GET requests for #ModelAttribute?

I have a Spring MVC controller set up like so:
#RequestMapping("activityChart")
public ModelAndView activityChart(
#RequestParam(required=false, value="parent") String parent,
#RequestParam(required=false, value="expand") String[] expand,
#ModelAttribute PaginationArgs paginationargs) throws IOException {
// ... return template renderer
}
Where PaginationArgs is a two-field POJO. I want to construct a URL string that includes values for paginationargs. It's easy to do for parent and expand - activityChart?parent=foo&expand=bar&expand=baz, but what's the right way to encode a POJO's fields?
JSP takes care of this in Spring with a <form:form modelAttribute='paginationargs'> tag, but our project isn't using JSP but Jamon instead.
In the simple case parameter names should be the same as the corresponding field names of the model object. So, if PaginationArgs has fields page and size, it would be like page=1&size=10.
In more complex cases Spring can accept parameters whose names are formed as described in 5.4 Bean manipulation and the BeanWrapper.

ASP.NET MVC 2 How are urls/routes, views related to controllers?

Could someone explain how routes are associated with controllers in MVC 2? Currently, I have a controller in /Controllers/HomeController.cs and a view /Home/Index.aspx.
My route registration method looks like this:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{resource}.aspx/{*pathInfo}");
routes.MapRoute(
"Default",
// Route name
"{controller}/{action}/{id}",
// URL with parameters
new { controller = "Home", action = "Index", id = "" }
// Parameter defaults
);
}
If I request the URL: http://localhost/Home/Index, then the request is correctly handled by HomeController.Index().
However, for the life of me, I can't figure out how the url /Home/Index gets pointed to HomeController. The view aspx doesn't, as far as I can tell, reference HomeController, HomeController doesn't reference the view, and the route table doesn't explicitly mention HomeController. How is this magically happening? Surely I'm missing something obvious.
then
This is the convention in ASP.NET MVC.
When using the DefaultControllerFactory this convention is buried inside the internal sealed class System.Web.Mvc.ControllerTypeCache (typical for Microsoft to write internal sealed classes). Inside you will find a method called EnsureInitialized which looks like this:
public void EnsureInitialized(IBuildManager buildManager)
{
if (this._cache == null)
{
lock (this._lockObj)
{
if (this._cache == null)
{
this._cache = GetAllControllerTypes(buildManager).GroupBy<Type, string>(delegate (Type t) {
return t.Name.Substring(0, t.Name.Length - "Controller".Length);
}, StringComparer.OrdinalIgnoreCase).ToDictionary<IGrouping<string, Type>, string, ILookup<string, Type>>(delegate (IGrouping<string, Type> g) {
return g.Key;
}, delegate (IGrouping<string, Type> g) {
return g.ToLookup<Type, string>(t => t.Namespace ?? string.Empty, StringComparer.OrdinalIgnoreCase);
}, StringComparer.OrdinalIgnoreCase);
}
}
}
}
Pay attention how the grouping is made. So basically the DefaultControllerFactory will look inside all the referenced assemblies for types implementing the Controller base class and will strip the "Controller" from the name.
If you really want to dissect in details ASP.NET MVC's pipeline I would recommend you this excellent article.
The default views engine that comes with ASP.NET MVC works on the following conventions:
You have a folder structure like this:
- Controllers\
|- HomeController.cs
- Views\
|- Home\
|-- Index.aspx
|- Shared\
When a request comes in, and matches a route defined in the RegisterRoutes method (see things like URL routing for more on that), then the matching controller is called:
routes.MapRoute(
"Default", // Route name, allows you to call this route elsewhere
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
In the default route, you are also specifying a default controller (without the "Controller" suffix) - the routing engine will automatically add Controller onto the controller name for you - and a default action.
In your controller, you call the simple method:
public ActionResult Index(){
return View();
}
The default view engine then looks for an aspx file called Index (the same as the action) in a folder called "Home" (the same as the controller) in the "Views" folder (convention).
If it doesn't find one in there, it will also look for an index page in the Shared folder.
From the ASP.NET MVC Nerd Dinner sample chapter
ASP.NET MVC applications by default use a convention-based directory naming structure when resolving view templates. This allows developers to avoid having to fully-qualify a location path when referencing views from within a Controller class. By default ASP.NET MVC will look for the view template file within the \Views\[ControllerName]\ directory underneath the application.
The \Views\Shared subdirectory provides a way to store view templates that are re-used across multiple controllers within the application. When ASP.NET MVC attempts to resolve a view template, it will first check within the \Views\[Controller] specific directory, and if it can’t find the view template there it will look within the \Views\Shared directory.
When it comes to naming individual view templates, the recommended guidance is to have the view template share the same name as the action method that caused it to render. For example, above our "Index" action method is using the "Index" view to render the view result, and the "Details" action method is using the "Details" view to render its results. This makes it easy to quickly see which template is associated with each action.
Developers do not need to explicitly specify the view template name when the view template has the same name as the action method being invoked on the controller. We can instead just pass the model object to the View() helper method (without specifying the view name), and ASP.NET MVC will automatically infer that we want to use the \Views\[ControllerName]\[ActionName] view template on disk to render it.
Edit to add:
Some example routes I've set up, that explicitly set the controller are:
routes.MapRoute(
"PhotoDetailsSlug",
"Albums/{albumId}/{photoId}/{slug}",
new {controller = "Albums", action = "PhotoDetails"},
new {albumId = #"\d{1,4}", photoId = #"\d{1,8}"}
);
Here I'm explicitly stating that I'm using the Albums controller, and the PhotoDetails action on that, and passing in the various ids, etc to the that action.
Inside the action Index there is a statement return View(). When a blank View is returned, the DefaultViewEngine searches several default folders for the name of the Controller method(specifically inside the FindView method). One of them is the Views/Home directory because Home is the name of the controller. There it finds the Index file, and uses it to display the result.

Resources