How to add file with additional properties into Liferay's Document Library - java

I'm trying to figure this out, I was asking this question in Liferay's forum here - the last entry.
And only thing I'm able to come up with is creating an Expando for FileEntry, which seems very complicated. Who knows if it makes sense. I don't like the expando feature since I couldn't query them via hibernate properly.
Does anybody know the answer for my question in that Liferay forum ?
The problem is that,
DLAppLocalServiceUtil.addFileEntry(...);
DLLocalServiceUtil.addFile(....);
doesn't let you store any additional information / properties about the file. As a result, one would have to use JackRabbit directly, instead of using Liferay's JCRHook. But you loose all the advantages that Document Library brings.

Yep, the only option is to use Expando AKA custom properties/fields. In case of fileEntry you don't need to create table and columns programmatically but you can set it up in Control Panel > custom fields.
After that you have a few options how to populate the expando values.
fileEntry.getExpandoBridge().setAttribute("propName", "propValue")
or if you get the properties from view layer
<liferay-ui:custom-attributes-available className="<%= DLFileEntry.class.getName() %>">
<liferay-ui:custom-attribute-list
className="<%= DLFileEntry.class.getName() %>"
classPK="<%= (fileVersion != null) ? fileVersion.getFileVersionId() : 0 %>"
editable="<%= true %>"
label="<%= true %>"
/>
</liferay-ui:custom-attributes-available>
and then
ServiceContext serviceContext = ServiceContextFactory.getInstance(
DLFileEntry.class.getName(), actionRequest);
serviceContext gets populated by parameters in actionRequest and you then just call
fileEntry.getExpandoBridge().setAttributes(serviceContext)
Finally, you may need to query for fileEntries with particular properties
public Hits search() {
Map<String, Serializable> attributes = new HashMap<String, Serializable>();
attributes.put("propertyName", "propertyValue");
SearchContext searchContext = new SearchContext();
searchContext.setAttributes(attributes);
Indexer indexer = IndexerRegistryUtil.getIndexer(FileEntry.class);
return indexer.search(searchContext);
}
Of course, this solution may seem a little complicated, because Liferay Document Library is not a JCR content repository, but it's literally a document library that provides abstraction layer for concrete repo implementations via Hooks, such as JCRHook ( where files are stored into jackrabbit repository ), CMIS support, migration support etc. It also handles permissionChecking, fileVersioning, document workflow and asset management.
So if you intend to do something more complicated and you will have to query the properties/metadata, change them and expand them. You should consider using JCR repository directly...

Related

Add Freemarker support to customized JSP tag

I have a customized JSP tag library with a Java class (extending TagSupport) that generates the output for my web application. It has some parameters that are formed into HTML code using a StringBuilder.
Now the generated HTML is becoming more complex and hard to handle with calls of StringBuilder.append, so I'd like to replace the code generation with a Freemarker template.
I already found out that I could use a generic Struts component tag instead, because the Struts tags already use Freemarker template files, so I could write a tag like:
<s:component template="/components/myStruct.ftl">
<s:param name="myParam" value="%{'myParam'}" />
</s:component>
Then writing the specified template file myStruct.ftl would probably solve my problem. I actually did not try if Struts really finds and uses that file correctly, but I optimistically expect it to work.
My question is, if it's also possible to retain the current code with the customized tag
<my:struct param="myParam" />
and only change the Java class linked to that tag.
I've found code that reads a Freemarker template:
Configuration config = FreemarkerManager.getInstance().getConfiguration(pageContext.getServletContext());
config.setServletContextForTemplateLoading(pageContext.getServletContext(), "/components");
Template templ = config.getTemplate("myStruct.ftl");
templ.process(params, pageContext.getOut());
but it seems very circuitously to me and I wondered what would be the "standard" way to do it. Additionally it seemed that you cannot use tags from the Struts tag library in a template used like this. (I ran into an ArrayIndexOutOfBoundException, caused by Sitemesh... I did not analyze it yet.)
My intention was to keep the Java class as some kind of wrapper around the Struts component tag. Maybe somthing like:
OgnlValueStack stack = TagUtils.getStack(pageContext);
Component c = new Component(stack);
c.addParameter("param", param);
But I don't know how to continue this code stub. It may be crap anyway.
Is there an easy/"standard" way to do this or do I simply have to get rid of the customized tag?
Thanks in advance.
A friend of mine sent me this link:
http://cppoon.wordpress.com/2013/02/27/how-to-create-a-struts-2-component-with-freemarker/
This is what I was looking for. The gist is to change the customized tag to not extend TagSupportbut AbstractUITag which makes it a Struts tag instead of a JSP tag, roughly speaking.
This enables the automatic linkage (by name and path conventions) to my Freemarker template. I basically followed the instructions on that page. I only added the methods that are abstract in the super class, so they had to be implemented.
IMO the site lacks of a description of how the UI bean class is linked to the tag class. But as the IDE forces you to implement the getBean method inside the tag class, you quickly get to this code (using the classes described on that site):
#Override
public Component getBean(OgnlValueStack stack, HttpServletRequest request, HttpServletResponse response)
{
Pagination pagination = new Pagination(stack, request, response);
pagination.setList(list);
return pagination;
}
This might not be completely correct for the recent Struts, but it worked for the ancient version I've got to use.
Thanks again to the guy who sent me the link :)

Dynamically creating a JSF form with java reflection

I'm trying to create a simple crud form to insert data into a database with hibernate, without knowing what the object type is. The ultimate goal is to only have one insert form for every table in the database. So far i get the methods that the current object has, check to see if it has any set methods and create a text input for every field that has a set.
UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
HtmlPanelGrid hpg = (HtmlPanelGrid) viewRoot.findComponent("panel");
for (Method method : declaredFields) {
String name = method.getName();
if (name.contains("set")) {
HtmlOutputText hot = new HtmlOutputText();
HtmlInputText hit = new HtmlInputText();
hot.setValue(name.substring(3));
try {
hit.setValue(newObject.getClass().getMethod(name, String.class));
} catch (Exception ex) {
Logger.getLogger(ReflectController.class.getName()).log(Level.SEVERE, null, ex);
}
hpg.getChildren().add(hot);
hpg.getChildren().add(hit);
}
}
Here newObject is the object that is going to be inserted into the database later with hibernate. My problem is this:
How do assign a certain field from that object to the text input that is being created at the moment. So far if I put the method in the value like I'm doing above, it will just print out the method in the value attribute for that input. what i want is that when this form is submited, for to assign the value in that text box to the property with that name.
I can give you a partial answer - You need to create a ValueExpression dynamically
Application app = FacesContext.getCurrentInstance().getApplication();
hit.setValueExpression("value", app.getExpressionFactory().createValueExpression(FacesContext.getCurrentInstance().getELContext(), "#{bean.item}", Item.class));
The hard part will be creating the valueExpression that will actually map to a field within your object's value. That requires a great deal more thought but you will for sure need the dynamic valueExpression. As written, this will result in the execution of your bean's setItem();method with a parameter of type Item. You will require something a little more complex.
In JSF, binding input components to properties is accomplished with EL-expressions. You can create one programmatically as Steve shows, but that syntax is really ugly. On a related note, programmatic manipulation of the component tree is a rather unorthodox way of using JSF. The orthodox way to tackle your requirement would be something like:
<ui:repeat var="prop" value="#{genericEditorBean.propertyNames}">
<h:outputLabel value="#{prop}" for="input"/>
<h:inputText id="input" value="#{genericEditorBean.object[prop]}"/>
</ui:repeat>
where
public List<String> getPropertyNames() {
List<String> propertyNames = new ArrayList<>();
BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
propertyNames.add(pd.getName());
}
return propertyNames;
}
(There really is no reason to reimplement scanning for Java Bean properties when the Java API offers a class for that very purpose. Unlike your home-grown version, this will also handle properties inherited from a super class ...)
I once used an open-source library named MetaWidget to do this.
It was a few years ago, but it worked well and was easy to set up.
It looks like the project is still active:
http://metawidget.sourceforge.net/index.php

How to create a modular JSF 2.0 application?

I have an application with a well defined interface. It uses CDI for resolution of the modules, (Specifically it uses Instance<> injection points on API interfaces to resolve modules) and passes various data back and fourth via the interfaces without issue. I've intentionally kept the API and implementation separate, and the modules only inherit from the API to avoid tight coupling, and the application only knows of the modules through runtime dependancies, and data passing accomplished via the APIs. The application runs fine without the modules, which can be added simply by dropping the jar into the WEB-INF/lib folder and restarting the app server.
Where I'm running into issues is that I want the modules to create a portion of the view, and I therefor want to invoke, in a portable way, either a JSF component, or do an include from the module in order to have it render its view. I already have resolved what module I want to invoke, and have references to the module's interface ready. The way I initially thought to do this was to do a ui:include that asks the module to supply where it's view template is, but I have no idea how to answer that query in a meaningful way, as view resolution is done from the application root, not the library root.
The executive summary is that I have no idea how to jump the gap from Application to Library using JSF for .xhtml (template/component) files.
Using a CC would be nice, but how do I specify that I want a particular CC instance at runtime, instead of having that hard coded into the page?
I can of course invoke the application code directly and ask it for markup, but this seems really brute force, and once I have the markup, I'm not sure exactly how to tell JSF to evaluate it. That said, I can imagine a component that would take the resource path, grab the markup and evaluate it, returning the completed markup, I just don't know how to implement that.
I'd rather avoid forcing module developers to go the heavy duty UIComponent approach if possible, which means either a dynamic way of doing ui:include (or some equivalent) or a dynamic way of invoking CCs. (I don't mind coding the UIComponent approach ONCE in the application if that's what it takes to make module developers' lives easier)
Any suggestions on where I should look to figure this out? (I'll post the answer here if I find it first)
I understand that your question basically boils down to How can I include Facelets views in a JAR?
You can do this by placing a custom ResourceResolver in the JAR.
public class FaceletsResourceResolver extends ResourceResolver {
private ResourceResolver parent;
private String basePath;
public FaceletsResourceResolver(ResourceResolver parent) {
this.parent = parent;
this.basePath = "/META-INF/resources"; // TODO: Make configureable?
}
#Override
public URL resolveUrl(String path) {
URL url = parent.resolveUrl(path); // Resolves from WAR.
if (url == null) {
url = getClass().getResource(basePath + path); // Resolves from JAR.
}
return url;
}
}
Configure this in webapp's web.xml as follows:
<context-param>
<param-name>javax.faces.FACELETS_RESOURCE_RESOLVER</param-name>
<param-value>com.example.FaceletsResourceResolver</param-value>
</context-param>
Imagine that you've a /META-INF/resources/foo/bar.xhtml in random.jar, then you can just include it the usual way
<ui:include src="/foo/bar.xhtml" />
or even dynamically
<ui:include src="#{bean.path}" />
Note: since Servlet 3.0 and newer JBoss/JSF 2.0 versions, the whole ResourceResolver approach is not necessary if you keep the files in /META-INF/resources folder. The above ResourceResolver is only mandatory in Servlet 2.5 or older JBoss/JSF versions because they've bugs in META-INF resource resolving.
See also:
Packaging Facelets files (templates, includes, composites) in a JAR
JSF facelets template packaging
I was looking for information on the same topic and came across this link: How-to: Modular Java EE Applications with CDI and PrettyFaces which worked really well for me.
By the way.. you can avoid implementing your own resource resolver when you're using seam solder (currently being integrated into apache deltaspike) which is a really useful library complementing CDI (your typical Java EE 6 component model)
I too experimented with modularity in jsf applications. Basically I built a template interface with a toolbar which gets filled with buttons provided by each module. Typically you will do this by providing a List of Strings as a Named Object:
#Produces
#SomethingScoped
#Named("topMenuItems")
public List<String> getTopMenuItems(){
return Arrays.asList("/button1.xhtml", "/button2.xhtml", "/button3.xhtml");
}
Notice how each of the buttons may come from a different module of the jsf application.
The template interface contains a panel where
you can use it in your markup the following way (at your own risk ;) ) :
....
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
....
<xy:toolbar>
<xy:toolbarGroup>
<c:forEach items="#{topMenuItems}" var="link">
<ui:include src="#{link}" />
</c:forEach>
</xy:toolbarGroup>
</xy:toolbar>
<xy:panel>
<ui:include src="#{contentPath}"/>
</xy:panel>
This was the toolbar and the content panel.
a simple button or view definition may look like this:
<ui:composition ...>
<xy:commandButton actionListener="#{topMenuController.switchContent()}"
value="Test" id="testbutton" />
</ui:composition>
lets name this artifact view1.xhtml
When this button is pushed (which doesn't trigger a postback using the actionListener, we want to reload the content using ajax) the switchContentMethod in your controller may change the string returned by getContentPath :
public void switchContent(){
contentPath = "/view1.xhtml";
}
#Produces
#SomethingScoped
#Named("contentPath")
public String getContentPath(){
return contentPath;
}
now you can change the view displayed in the panel using the button in the menubar which sort of gives you navigation without page reloads.
Some advice ( or 'what I've learned' ) :
You may wanna choose a large scope for the getTopMenuItems method
Don't nest the ui:include tag. Unfortunately this is not possible (e.g. your view1.xhtml cannot include another composition). I really
would like something like this to be possible as you can build really
modular jsf views with this, kinda like portlets only without the
portlets.. =D
doing ui:include in container components like tabviews also proves problematic.
generally it's not advisable to mix JSTL (c:forEach) and JSF. Still I found this to be the only way working as ui:repeat gets
evaluated too late e.g. your included content does not appear.

Conditionally Render In JSP By User

I'm trying to make a simple forum just to get the hang of the Spring Security and MVC frameworks.
For simplicity's sake, let's I have a JSP to view a forum post, which looks like the following:
<body>
...
Title: ${forumPost.title} <br>
Author: ${forumPost.author.name} <br>
Message: {forumPost.message} <br>
<security:authorize ifAnyGranted="ROLE_ADMIN">
Edit: Edit
</security:authorize>
...
</body>
My problem is: not only should an Administrator be able to edit this post, but the original author should be able to as well. Therefore, I only want ROLE_ADMIN and the original author to be able to see the Edit link. However I'm not sure how to filter by user with the security:authorize tag, or if I'll need to go about this a different way.
Any suggestions would be much appreciated. Thanks!
Assuming that you have a controller that sits behind this page, I would simply add a canEditPost field to the ModelAndView that looks something like (semi-pseudocode):
private boolean isAdmin() {
Authentication currentAuthObj = SecurityContextHolder.getContext().getAuthentication();
List<GrantedAuthority> authorities = Arrays.asList(currentAuthObj.getAuthorites());
for (GrantedAuthority auth : authorities) {
if ("ROLE_ADMIN".equals(auth.getAuthority())) {
return true;
}
}
return false;
}
boolean currentUserIsAuthor = ...;
modelAndView.addObject("canEditPost",
Boolean.valueOf(currentUserIsAuthor || isAdmin());
And then in your view just reference $canEditPost.
It's generally better for the view to just reference a simple flag in the model than have the view/template doing the actual logic.
Does your Author object implement equals in such a way that each author is unique?
If so, you could simply check if the Author is the same as the current user (You'd have two sets of tags).
you can have conditions
<% if(mycondition.isTrue()){ %>
<security:authorize ifAnyGranted="ROLE_ADMIN">
Edit: Edit
</security:author
<% }%>
#matt b's answer is a great way to do it and is probably what I'll end up doing. But I found another way that is a bit more complicated but will achieve what I put in this post.
I did a bit of reading and found out that you can handle security at the domain object level and essentially give read/write/delete privileges to a role or to an arbitrary object, for example, the current user ID. In this example, I would give the current user id access to a domain object, in this case a ForumPost object that has its own unique id from the database.
The current user id would then be granted read and write access, which can (via the XML configuration) be defined as a custom role of sorts (I believe the correct term is actually Voter). I could then name this voter MESSAGE__EDIT.
So, in my JSP I could then use the following:
security:authorize ifAnyGranted="MESSAGE_EDIT"
And it would (again, through XML configuration) get the current user id and give access based on the current domain object, in this case, a ForumPost object.
It is a fair bit more work than it sounds, but it can definitely be done.
Some documentation on all this can be found in the Domain Object Security section in the Spring Security Reference Documentation (http://static.springframework.org/spring-security/site/reference/html/springsecurity.html (for some reason the link to the Domain object Security section is broken for now)).

How to control submit url action in Wicket form?

I have a wicket web application with Page mounted to bookmarkable alias. The page contains a form object with submit action.
The problem is that though the form belongs to the page the action url doesn't contain page alias, but rather created in cryptic form of wicket action.
Is there a way to adjust that behavior, so link will be like page_alias/submit ?
...
setRenderStrategy(IRequestCycleSettings.ONE_PASS_RENDER);
mountBookmarkablePage("/resetpwd", ResetPasswordPage.class);
...
public ResetPasswordPage(final String id, final PageParameters parameters) {
final Form form = new StatelessForm();
form.add(new Button("submit") {
public void onSubmit() {
...
});
add(form);
If you subclass StatelessForm instead of Form, this will take you part of the way. Rather than having something like
action="myapp/?wicket:interface=:1:eventEditor::IFormSubmitListener::"
with the page containing the form mounted at a bookmarkable URL, you'll get something like, for example,
action="myapp/mount/path/some/params/?wicket:interface=:0:eventEditor::IFormSubmitListener::"
This uses a MixedParamUrlCodingStrategy for the mount in WebApplication.init()
You can then override encodeUrlInHiddenFields() to return true, which will give you a clean URL in the action attribute.
However, all this doesn't really change the way Wicket works with forms, i. e., you still have some Wicket-specific state data in the client's markup. The reason why this is so hard, I believe, is that Wicket is meant to help you build a web app that has state. I noticed that Wicket does a lot of stuff (like comparing submitted form values with what the model's getters return before the setters are called) behind the scenes, that I know too little about to be comfortable when just cutting it out.
You can use Wicket to provide RESTful web services, though, as outlined in this blog post. There's also a project on Google code called wicket-rest that expands on this idea. Note that this seems to work as simple as it does because it just never uses the whole component based UI building stuff.
The guy who wrote this post had a different problem, but it helped me understand Wicket forms a little better anyway.
you can hide a lot of the request mumbo jumbo by using a HybridUrlCodingStrategy like so:
mount(new HybridUrlCodingStrategy("/resetpwd", ResetPasswordPage.class));
Then when the click submit, assuming you don't redirect to a new page, the url would change from
mysite.com/DocRoot/resetpwd
to
mysite.com/DocRoot/resetpwd.1
or if you really want it to be mysite.com/DocRoot/resetpwd/submit you could create a new bookmarkable page, say ResetPasswordResult.class, set your response page to that and mount it at "/resetpwd/submit"
You might look at other encoding strategies to see if their is another that suits you better:
http://cwiki.apache.org/WICKET/url-coding-strategies.html
You can take a look at http://day-to-day-stuff.blogspot.com/2008/10/wicket-extreme-consistent-urls.html and try to adapt that for Forms.

Categories