How to obtain Role of a logged in User in Liferay Themes? How to check if User belongs to a particular role?
UserLocalService has hasRoleUser method which can be used to find out if User belongs to a particular role.
Below code can be put in navigation.vm file under templates folder.
#set($UserLocalServiceUtil = $serviceLocator.findService("com.liferay.portal.service.UserLocalService"))
#if ($UserLocalServiceUtil.hasRoleUser(roleID, $user.getUserId())) // It takes roleID as input to check.
//Proceed with whatever you want to
#else
//Proceed with something else
Note: Instead of com.liferay.portal.service.UserLocalService, if you use com.liferay.portal.service.UserLocalServiceUtil,
as might be found in some resources like this then you will encounter below exception,
ERROR com.liferay.portal.kernel.bean.BeanLocatorException: org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'com.liferay.portal.service.UserLocalServiceUtil' is defined
com.liferay.portal.kernel.bean.BeanLocatorException: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'com.liferay.portal.servi
ce.UserLocalServiceUtil' is defined
Another way is,
#set($role=$serviceLocator.findService("com.liferay.portal.service.RoleLocalService"))
$role.getUserRoles($user_id)
Just loop through the $user object defined in init.vm
#set ($user_roles = $user.getRoles())
#foreach($role in $user_roles)
$role.name<br />
#end
Related
In my module, in OSGI component declarationIi need to use a property which will be in my portal-ext.properties, like that :
#Component(
immediate = true,
property = {
"dispatcher=FORWARD",
"dispatcher=REQUEST",
"servlet-context-name=",
"servlet-filter-name=Detail UC Filter",
"url-pattern=/web/guest/" + PropsUtil.get("myPath") + "/*"
}
But i get the compilation error : " The value for annotation attribute Component.property must be a constant expression". How can i do to use a property here?
But i get the compilation error : " The value for annotation attribute Component.property must be a constant expression". How can i do to use a property here?
The entry:
"url-pattern=/web/guest/" + PropsUtil.get("myPath") + "/*"
is the problem. This is because annotations can only have values that are compile-time constants. Obviously this property is not a compile time constant as its value depends on calling a method.
If you want to supply property values at runtime then you can do this in OSGi using Configuration Admin. All Declarative Services components are configurable by default, using a pid which is either:
User configured by setting #Component(configurationPid="foo")
User configured by setting #Component(name="bar")
Defaulted using the fully qualified class name of the component implementation
When you supply a configuration dictionary to Configuration Admin which matches the pid for your DS component then it will be bound to the component.
Your component properties will be merged with the configuration dictionary (with the configuration overriding the static properties). This can be received by your component using an #Activate method
If your component is registered as a service then your service properties will also be updated.
If your component has a #Modified method then these changes will be dynamic, otherwise your component instance will be deactivated and discarded, and a new instance created and activated.
You can force your component not to activate until a configuration has been provided by setting your component's configuration policy. This is useful when you have a property that needs to exist, but can't be known until runtime.
#Component(configurationPolicy=ConfigurationPolicy.REQUIRE)
You can set any of these properties using a config admin configuration.
So one approach is to have a separate component that writes a configuration for this component.
You can use configurationPolicy = ConfigurationPolicy.REQUIRE to prevent the component to become activated before this configuration is present.
Another approach is to use a component factory. See this blog from Scott.
The requirements of the application that I'm building demands that user roles are to be dynamic, they will be stored in the database, and they will also be mapped to functionalities (forms) of the application, also stored in the database.
Restricting a role from accessing a specific page won't be difficult, but the requirements also states that form inputs must be customized based on roles, which means, an input can be mandatory or not, visible or not, read-only or not based on the role.
My approach to control these restrictions is based on creating a property file for each role, which will store all the inputs of all the forms in the application, as keys, and a long string as value in which we define the state of the input, like the following:
user-inputs.properties
# form.input=mandatory:visibility
searchBooks.bookName=true:true
searchBooks.bookCategory=false:true
searchBooks.authorName=false:false
admin-inputs.properties
searchBooks.bookName=true:true
searchBooks.bookCategory=false:true
searchBooks.authorName=false:true
And then do some magic Java code, whenever a form is accessed, read its inputs properties from the file of the specific user role, and parse the values so I could provide the right value for the rendered="" and required="" attribute of an <h:inputText/>.
This could be a solution, but the inputs of the application are much more than a book name and category, means I will be putting lots of required and rendered attributes which will make JSF pages look ugly with huge amount of variables in the managed bean.
Is there a better approach/framework/solution to my issue?
I think that you are in the right way, and i will continue using your approach which consists of creating multiple property files, one for each user, except that we will not use a any "huge amount of variables
in the managed bean".
So, the first step consists on managing multiple resource properties using a single resource bundle prefix ( the <var></var> in <resource-bundle>), in the second step we will see how to switch between those files, and in the last step we will read from property file using JSTL.
Managing multiple property files:
We start by defining our ResourceBundle in the faces-config file:
<application>
<resource-bundle>
<base-name>UserMessages</base-name>
<var>msgs</var>
</resource-bundle>
</application>
UserMessages is a ResourceBundle where we will implement the logic that allow us to switch between our property files (assuming that yourpackage.user-inputs is the fully qualified name of your user-inputs.properties):
import java.util.Enumeration;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import javax.faces.context.FacesContext;
public class UserMessages extends ResourceBundle {
public UserMessages() {
// we are loading user-inputs.properties as the default properties file
setParent(getBundle("yourpackage.user-inputs", FacesContext.getCurrentInstance()
.getViewRoot().getLocale()));
}
#Override
protected Object handleGetObject(String key) {
// we could just return parent.getObject(key) but we want to respect JSF recommandations
try {
return parent.getObject(key);
} catch (MissingResourceException e) {
return "???" + key + "???";
}
}
#Override
public Enumeration<String> getKeys() {
return parent.getKeys();
}
// this is the method that will allow us to switch between our .properties
public void setResourceBundle(String basename) {
setParent(getBundle(basename, FacesContext.getCurrentInstance()
.getViewRoot().getLocale()));
}
}
Switching between property files:
In order to switch from a property file to another we will need to use the method setResourceBundle(String basename) that we just declared in our class above, So in the managed bean where you are declaring your business logic and where you are intending to switch files depending on the user's role, you need to inject the bundle, like:
//don't forget adding getters and setters or you end with NullPointerException
#ManagedProperty("#{msgs}")
private UserMessages userMesssages;
Then, to switch to another file (admin-inputs.properties), just use it like this:
//yourpackage.admin-inputs is the fully qualified name
userMesssages.setResourceBundle("yourpackage.admin-inputs");
NB: You can inject the bundle in that way (above) only in request scoped beans, to use it in broader scopes please see: Read i18n variables from properties file in a Bean
Now, as we can switch easily from the user-inputs to the admin-inputs, the last step is the easiest one.
Parsing the property file:
The bad news, is that when using this approach you will need to add rendered="" and required="" attribute to every input you are willing to manage (but don't forget that the good ones was that you will not need to manage variables in managed beans ;) ).
First, you need to add JSTL namespaces declaration on the top of your xhtml file:
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
you can find more about JSTL functions in the javadocs, regarding the function substringAfter:
Returns a subset of a string following a specific substring.
Example:
P.O. Box: ${fn:substringAfter(zip, "-")}
The function substringBefore:
Returns a subset of a string before a specific substring.
Example:
Zip (without P.O. Box): ${fn:substringBefore(zip, "-")}
Second, as the first part of your String represents the required attribute:
//Returns the substring of msgs['searchBooks.authorName'] before the first occurrence of the separator ':'
required="${fn:substringBefore(msgs['searchBooks.authorName'], ':')}"
and the second part:
//Returns the substring of msgs['searchBooks.authorName'] after the first occurrence of the separator ':'.
rendered="${fn:substringAfter(msgs['searchBooks.authorName'], ':')}"
See also:
JSF Internationalization f:loadbundle or through faces-config:
Performance point
Difference between by Application#getResourceBundle() and ResourceBundle#getBundle() in JSF 2.0
How to remove the surrounding ??? when message is not found in
bundle
Context Sensitive Resource Bundle entries in JavaServer Faces
applications – going beyond plain language, region & variant
locales
I need the Spring Integration configuration to handle the case where there will be a variable number of queues which the application will receive messages on.
Have tried the following configuration:
<int-jms:message-driven-channel-adapter id="dsToT2"
destination-name="#{tConfigurer.getDsToTQueues().values().toArray().length>2?
dsConfigurer.getDsToTQueues().values().toArray()[2]:null}"
connection-factory="connectionFactory"
channel="ackToTChannel"/>
but, if the destination-name resolves to null, the following exception is thrown:
java.lang.IllegalArgumentException: 'destinationName' must not be null
What is the best way to handle this scenario?
Thanks
So, the problem is here that you get IllegalArgumentException on application startup.
If really don't know if your detination will be null or not, you shoudl do some Java code:
mark your <int-jms:message-driven-channel-adapter> with auto-startup="false"
Introduce separate bean for DefaultMessageListenerContainer with autoStartup=false too, and inject it to the <int-jms:message-driven-channel-adapter>
As far as destination-name is a property of that DefaultMessageListenerContainer you should right some code to resolve your destination on application startup and inject the value (if any) to the container bean.
And call start() of <int-jms:message-driven-channel-adapter>. It is a AbstractEndpoint bean with id dsToT2
Note, you can't provide null to the destination-name attribute. Your AC will fail on startup when it tries to populate bean properties. In this case will be called AbstractMessageListenerContainer#setDestinationName, which, in turn, does the check
Assert.notNull(destinationName, "'destinationName' must not be null");.
However, you can try to provide empty string '' instead of null and add similar SpEL condition for auto-startup attribute.
HTH
I had declared and used a global variable in ssjs library as below:
var backendDoc:NotesDocument = null;
function savedata () {
print (backendDoc.getItemValueString("fieldname")); // crash here
}
I assigned a document object to it in the Edit button just after changing docuemnt mode from read to edit:
backendDoc = document1.getDocument(); // get backend document from datasource called document1
The code in above function return error NotesDocument.getItemValueString("string")) null. Apparently, the backendDoc is null.
Any ideas how to assign value and use global variable in ssjs library? Thanks in advance
There are 2 problems with your code:
as Michael pointed out: you should use a scoped variable. Global variables in script libraries are actually application global (think applicationScope) and might be unloaded any time if memory gets tight (behavior of them depends on the XPages version)
You can't use NotesObjects here. Between the calls the C Object that backs the JS object is released and your object becomes invalid.
You can either store the NoteId in a scoped variable and retrieve the NotesDocument every time or actually use a JSON structure to keep the values you are interested in and only read/write when actually needed (load/save event). Hope this helps
I think you have to use a scoped variable in which you store the universalid of the document. This can then be used at any script to initialize the backend document.
From a ssjs you can set a scoped variable using the put method and the get method to read the variable. Example to set and read a scoped variable in session scope :
sessionScope.put(“myvar“,“myvalue“)
sessionScope.get(“myvar“)
To learn more about scoped variables watch this
http://notesin9.com/index.php/2009/11/07/episode-4-intro-to-scoped-variables/
how is it possible to pass the allowed roles as property?:
<sec:ifAnyGranted roles="#{item.allowedRolesToRender}">
Where Item is not a bean but the var of an dataList:
<rich:dataList value="${handler.itemlist}" var="item"
I tried to return array/comma-separated-string/list but it seems the the get method is never called. And i always get:
com.sun.facelets.FaceletException: roles must be given
at org.springframework.security.taglibs.facelets.IfAnyGrantedTag.apply(IfAnyGrantedTag.java:41)
Thanks
Use like this.
<sec:authorize ifAnyGranted="#{item.allowedRolesToRender}">
And allowedRolesToRender should be given as a comma-separated list of strings
Reference