Can we have custom permissions defined for the portal? - permissions

Environment: Liferay 6.1 GA3 EE
Can we have custom permissions defined for the portal?
We can create custom permissions in our plugin portlet through creating an XML with <portlet-resource> tag and defining the <action-key> within it.
And when I go to define permissions for a Role in Control Panel my portlet appears in the section Site Applications, now what I want is to create custom permissions (not through EXT) in a portlet or hook that should have a separate category as My Custom and should have custom permissions like <action-key>ACCESS_EMAIL</action-key>, <action-key>ACCESS_TOOLSET<\action-key> etc.
In short my custom category should appear within section Portal as shown in the following figure while I define the permission for a custom Portal (regular) role:
I would like to use this permission not for a specific portlet but need to use it inside jsp-hooks or any other of my custom portlets. Just like we have ADD_SITE, ADD_USER etc permissions in Portal --> General, I want to have these permissions as generic.
Edit
To make the portlet appear in any of the section I created a simple custom-portlet, so the portlet appeared in the Site Application section and if I want I can make it appear in the Control Panel sections as well.
But now the problem is I don't have any view nor any implementation in this portlet so I make it hidden by updating the liferay-display.xml and putting it under category.hidden. This also hides it from the Define Permission drop-down.
And if I don't use the lifeay-display.xml liferay puts it under the Undefined category while accessing it from +Add menu in dockbar. :-(
Thank You

This is how we achieved it:
Create a custom portlet with the permissions file for this portlet like /resource-actions/custompermission.xml to specify the different custom permissions we want. The full steps are identical to this wiki.
Make this portlet a hidden portlet so that it won't appear in the Add menu in dockbar, neither in Control-panel nor in the Define Permissions drop-down.
Now create a JSP hook (you can either create a separate plugin or include the hook in the custom-portlet defined in point no. 1) and modify the /docroot/html/portlet/roles_admin/edit_role_permissions_navigation.jspf to include our custom category:
<aui:form action="<%= currentURL %>" name="resourceSelection">
<aui:fieldset>
<aui:select changesContext="<%= true %>" name="add-permissions" onchange='<%= renderResponse.getNamespace() + "addPermissions(this);" %>'>
<aui:option label="" />
<%-- Our customization starts here --%>
<%--
We have added our very own option group but this is not required just the <aui:option> will do
--%>
<optgroup label="<liferay-ui:message key="custom" />">
<%
if (_isCustomPermissionsPortlet(CUSTOM_PERMISSIONS_PORTLET)) {
editPermissionsURL.setParameter("portletResource", CUSTOM_PERMISSIONS_PORTLET);
editPermissionsURL.setParameter("showModelResources", "0");
%>
<%--
and here we add our own Permission category drop-down option
--%>
<aui:option label="custom-permissions"
selected="<%= _isCurrent(editPermissionsURL.toString(), portletResource, showModelResources) %>"
value="<%= editPermissionsURL.toString() %>" />
<%
}
%>
</optgroup>
<%-- Our customization ends here --%>
<c:choose>
<c:when test="<%= role.getType() == RoleConstants.TYPE_SITE %>">
<optgroup label="<liferay-ui:message key="administration" />">
<% // Other liferay stuff continues ...
and at the end of the JSP we have:
// ... other liferay stuff
private boolean _isOrganizationPortlet(String portletId) {
return ArrayUtil.contains(_ORGANIZATION_PORTLETS, portletId);
}
private boolean _isPortalPortlet(String portletId) {
return ArrayUtil.contains(_PORTAL_PORTLETS, portletId);
}
// Our customization starts here
// portlet's ID
public static final String CUSTOM_PERMISSIONS_PORTLET = "CustomPermissions_WAR_CustomPermissionsportlet";
private static final String[] _CUSTOM_PERMISSIONS_PORTLET = {
CUSTOM_PERMISSIONS_PORTLET,
};
private boolean _isCustomPermissionsPortlet(String portletId) {
return ArrayUtil.contains(_CUSTOM_PERMISSIONS_PORTLET, portletId);
}
// Our customization ends here
This is how it looks in the Control Panel:
We can move the permissions in the Portal section as well by moving our customized code to that place in the JSP.
The advantage of having it above is that it will be displayed when we want to set Define Permissions for Site Role.
Hope this helps somebody.

Related

How to submit a form to specific area in ASP.NET Core MVC project?

I have created an ASP.NET Core 5 MVC project and I tried to add an identity area to my project to handle all authentication and authorization tasks in it. I also registered the area in Startup.cs and I am able to route to register action using /identity/action/register.
But on the Register page when I want to change the target controller in identity area using code below:
<form asp-area="Identity" asp-controller="Account" asp-action="Register"
asp-route-returnurl="#ViewData["ReturnUrl"]" method="post"
class="form-horizontal" role="form">
then what I get in my rendered HTML is this:
<form method="post" class="form-horizontal" role="form"
action="/Account/Register?area=Identity" novalidate="novalidate">
The form doesn't target the identity area.
I've been looking for a solution all day, but none of what I found on the Web has worked for me.
Thanks
As far as I know, the form tag helper will check the route endpoints rule is exists or not when it rendered as html.
If you set the wrong endpoint routing, it couldn't render the right url as you excepted.
I suggest you could try to follow below steps to check if your application route and area folder structure is right.
1.You should make sure your area folder structure like this:
Project name
Areas
Identity
Controllers
HomeController.cs
ManageController.cs
Views
Home
Index.cshtml
Manage
Index.cshtml
About.cshtml
2.You should make sure you have add the right area route in the app.UseEndpoints method.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "areas",
pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"
);
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
In addition to Brando Zhang's answer please also make sure that you specify area attribute to your area controllers like below
[Area("Identity")]
public class AccountController : Controller
{
//Your code here
}

ASP.NET Core 3.0 - Identity UI Manage folder not receiving layout

I have Identity scaffolded out in accordance with the documentation, and everything works properly except for the layout of the /Manage folder.
The directory setup is exactly as it gets scaffolded.
Directory setup with all misc. files removed
For clarity's sake:
/Areas/Identity/Pages/Account/Manage is the problem folder.
/Pages Contains the _ViewStart file which sets the Layout from my Views/Shared folder.
/Pages/Account Receives the layout from _Viewstart and works properly.
/Pages/Account/Manage Everything in here only receives the _ViewStart layout. The _Layout file in here isn't automatically found by the pages inside of it.
Areas/Identity/Pages/Account/Manage/_Layout.cshtml
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Manage your account</h1>
<div>
<h4>Change your account settings</h4>
<hr />
<div class="row">
<div class="col-md-3">
<partial name="_ManageNav" />
</div>
<div class="col-md-9">
#RenderBody()
</div>
</div>
</div>
#section Scripts {
#RenderSection("Scripts", required: false)
}
This is exactly as it gets scaffolded in and the layout only breaks when you change AddDefaultIdentity() to AddIdentity(). I work with a reference I scaffolded and it just leads me to believe I'm not accounting for something when removing the default UI. The only workaround I found was manually setting the layout of each .cshtml file within /Manage
#{
Layout = "_Layout";
}
This fixes everything and causes the layout to work properly for the pages inside of /Manage. I read the documentation and it states that each Razor Page controller should search its own folder for a _Layout file before searching elsewhere. Is there a reason it isn't detecting the file?
"The only workaround I found was manually setting the layout of each .cshtml file within /Manage":
You don't have to do that. Simply create a _ViewStart.cshtml under your Manage/ foler:
#* file: Manage/_ViewStart.cshtml *#
#{
Layout = "_Layout"; // Use a partial layout name instead of absolute name
}
Also be careful that the default Manage/Layout.cshtml uses a parent layout of /Areas/Identity/Pages/_Layout.cshtml which might not exist in your scaffolded files :
#* file: Manage/Layout.cshtml *#
#{
Layout = "/Areas/Identity/Pages/_Layout.cshtml"; // you might want to change this to `/Views/Shared/_Layout.cshtml`
}
"it states that each Razor Page controller should search its own folder for a _Layout file before searching elsewhere"
That's true only when you're using a partial _Layout name. However, if you're using an absolute name that starts with a slash, it will use that layout directly. See official docs :
When a partial name is provided, the Razor view engine searches for the layout file using its standard discovery process. The folder where the handler method (or controller) exists is searched first, followed by the Shared folder. This discovery process is identical to the process used to discover partial views.
In your case, the layout name /Areas/Identity/Pages/_Layout.cshtml, which starts with a /, is not a partial name. That's the reason why your pages cannot discover the layout. In order to fix this issue, using a partial name _Layout instead. (This can be done by a single _ViewStart.cshtml file as I do above, don't add it for every page)
Finally, you might wonder why it renders normally when using AddDefaultIdentity(). As you find out, the AddDefaultIdentity() will add the default UI, which eventually invokes the AddRelatedParts() method. This allows that it fallbacks to the default UI when there's no such a layout or page. e.g., when you scaffold Identity with Visual Studio, it offers a list which you can use to override the default pages. The above /Areas/Identity/Pages/_Layout.cshtml comes from the default UI.

Tag helpers do not resolve when view is outside the Views folder

With Asp .Net 5 beta 6 the tag helpers do not resolve when the view is located outside the Views folder. The folder structure that I would like is one based on function rather than on file types. The controller, view models and views for a particular function I would like grouped together in a folder that reflects the controller name. Here is an example of the folder structure that I would like in the project:
UI
- Account
- AccountController.cs
- AccountViewModels.cs
- Login.cshtml
- Register.cshtml
- Students
- StudentsController.cs
- StudentsViewModels.cs
- CaptureStudent.cshtml
To accomplish this I have created a custom view engine:
public class CustomUIViewEngine : RazorViewEngine
{
public CustomUIViewEngine(IRazorPageFactory pageFactory,
IRazorViewFactory viewFactory,
IOptions<RazorViewEngineOptions> optionsAccessor,
IViewLocationCache viewLocationCache) :
base(pageFactory, viewFactory, optionsAccessor, viewLocationCache)
{
}
public override IEnumerable<string> ViewLocationFormats
{
get
{
var viewLocationFormats = base.ViewLocationFormats
.Union(new string[] { "~/UI/{1}/{0}.cshtml" });
return viewLocationFormats;
}
}
}
Which I register in startup.cs as follows:
services.AddMvc().ConfigureMvcViews(options =>
{
options.ViewEngines.Clear();
options.ViewEngines.Add(typeof(CustomUIViewEngine));
});
This custom view engine does work and the view gets located and rendered, with the only problem being that the tag helpers are not correctly rendered, so instead of seeing this:
<form method="post" class="form-horizontal" role="form" action="/Account/Login">
I am seeing this in the rendered source:
<form asp-controller="Account" asp-action="Login" method="post" class="form-horizontal" role="form">
Does anyone know why the tag helpers are not rendering correctly when the view is located outside the Views folder and if there is any way to correct this in an Asp.Net 5 web application?
Are you adding your TH's in the _ViewImports file, and if so are the using statements being recognized? Have you tried adding the #addTagHelper directly to a view?
Some of this is mentioned in my Authoring Tag Helpers
I go into to detail on inheritance hierarchy, adding, removing in an upcoming article.

How can I use cshtml files with Durandal?

I got the DurandalJS StarterKit template on VS2012... All works great...
But in some views I need to do something like that:
#if (Roles.IsUserInRole("Administrators"))
{
<p>Test</p>
}
However with durandal all my views are '.html' files... Is that possible to use '.cshtml' files to access some information like that?
Or is there any other way to do that with durandal?
Junior
I am doing it like this:
Create a generic controller for Durandal views:
public class DurandalViewController : Controller
{
//
// GET: /App/views/{viewName}.html
[HttpGet]
public ActionResult Get(string viewName)
{
return View("~/App/views/" + viewName + ".cshtml");
}
}
Register a route:
routes.MapRoute(
name: "Durandal App Views",
url: "App/views/{viewName}.html",
defaults: new { controller = "DurandalView", action = "Get" }
);
Copy Views/web.config to /App/views/web.config (so Razor views work in this location).
This lets me use the normal Durandal conventions (even the html extension for views), and put durandal views as cshtml files in their normal location without adding any more server code.
If you also have static html views, you can also place the cshtml views in a subfolder or use the normal MVC /Views folder.
I wouldn't recommend using ASP.NET MVC with Durandal.
What you are probably looking to do is use the Razor view engine (to get the benefits of a compiler, strong typing etc.) which exists independently from ASP.NET MVC. Just WebAPI for data I/O is more than enough to very efficiently create a Durandal.js application.
If you are interested in using Razor/CSHTML with Durandal and Knockout there is an open source option out there called FluentKnockoutHelpers that may be exactly what you are looking for. It offers much of the 'nice' parts of ASP.NET MVC allowing you to use the awesome abilities of Durandal and Knockout with almost no downfalls.
Source
Live demo using Durandal.js
In a nutshell it provides a bunch of features which makes doing Durandal/Knockout development just as easy as ASP.NET MVC. (You simply provide a C# type that your JavaScript model is based off of for most of the features.) You only have to write JavaScript and un-compiled markup for complicated cases which is unavoidable and no different than MVC! (Except in MVC your code would also likely end up would also be a big jQuery mess which is why you are using Durandal/Knockout in the first place!)
Features:
Painlessly generate Knockout syntax with strongly typed, fluent, lambda expression helpers similar to ASP.NET MVC
Rich intellisense and compiler support for syntax generation
Fluent syntax makes it a breeze to create custom helpers or extend whats built in
OSS alternative to ASP.NET MVC helpers: feel free to add optional features that everyone in the community can use
Painlessly provides validation based on .NET types and DataAnnotations in a few lines of code for all current/future application types and changes
Client side JavaScript object factory (based on C# types) to create new items in for example, a list, with zero headaches or server traffic
Example without FluentKnockoutHelpers
<div class="control-group">
<label for="FirstName" class="control-label">
First Name
</label>
<div class="controls">
<input type="text" data-bind="value: person.FirstName" id="FirstName" />
</div>
</div>
<div class="control-group">
<label for="LastName" class="control-label">
Last Name
</label>
<div class="controls">
<input type="text" data-bind="value: person.LastName" id="LastName" />
</div>
</div>
<h2>
Hello,
<!-- ko text: person.FirstName --><!-- /ko -->
<!-- ko text: person.LastName --><!-- /ko -->
</h2>
Provide FluentKnockoutHelpers with a .NET type and you can do this in style with Intellisense and a compiler in Razor / CSHTML
#{
var person = this.KnockoutHelperForType<Person>("person", true);
}
<div class="control-group">
#person.LabelFor(x => x.FirstName).Class("control-label")
<div class="controls">
#person.BoundTextBoxFor(x => x.FirstName)
</div>
</div>
<div class="control-group">
#person.LabelFor(x => x.LastName).Class("control-label")
<div class="controls">
#person.BoundTextBoxFor(x => x.LastName)
</div>
</div>
<h2>
Hello,
#person.BoundTextFor(x => x.FirstName)
#person.BoundTextFor(x => x.LastName)
</h2>
Take a look at the Source or Live Demo for an exhaustive overview of FluentKnockoutHelper's features in a non-trivial Durandal.js application.
Yes, you can absolutely use cshtml files with Durandal and take advantage of Razor on the server. I assume that also means you want MVC, so you can do that too and use its routing.
If you don;t want the routing then you can set the webpages.Enabled in the web.config, as the other comments suggest.
<add key="webpages:Enabled" value="true" />
I don't recommend that you use .cshtml files as views directly. You're better off placing the .cshtml files behind a controller.
For example, take the HotTowel sample, edit /App/main.js, and replace the function definition with the following:
define(['durandal/app',
'durandal/viewLocator',
'durandal/system',
'durandal/plugins/router',
'durandal/viewEngine',
'services/logger'],
function (app, viewLocator, system, router, viewEngine, logger) {
Note that we added a reference to the Durandal viewEngine. Then we need to replace
viewLocator.useConvention();
with
viewLocator.useConvention('viewmodels', '../../dynamic');
viewEngine.viewExtension = '/';
The first argument to viewLocation.useConvention sets the /Apps/viewmodels/ directory as the location for the view models js files, but for the view location, uses the URL http://example.com/dynamic/, with an extension of '/'. So that if Durandal is looking for the view named 'shell', it will reference http://example.com/dynamic/shell/ (this is because the view directory is mapped relative to the viewmodels directory, hence /App/viewmodels/../../dynamic will give you simply /dynamic).
By convention, this previous URL (http://example.com/dynamic/shell/) will be mapped to the controller DynamicController, and the action "Shell".
After this, you simply add a controller - DynamicController.cs, like this:
// will render dynamic views for Durandal
public class DynamicController : Controller
{
public ActionResult Shell()
{
return View();
}
public ActionResult Home()
{
return View();
}
public ActionResult Nav()
{
return View();
}
public ActionResult Details()
{
return View();
}
public ActionResult Sessions()
{
return View();
}
public ActionResult Footer()
{
return View();
}
}
Create .cshtml files for each of the above actions. This way you get to use controllers, server side IoC et al to generate dynamic views for your SPA.
DurandaljS is a client framework which forms mainly a solid base for single-page apps (SPA).
I assume you are using asp.net web API as your server technology. In that case, you can determine the user's role inside your API controller and based on that return data to the client. On the client you can use Knockout "if" binding in order to show / hide certain areas of your page.
What you perhaps can do is placing this code in the Index.cshtml.
Following link shows how to customize moduleid to viewid mapping
http://durandaljs.com/documentation/View-Location/
by convention durandal tries to find view url in following steps
1) Checke whether object has getView() function which returns either dom or a string ( url for the view)
2) If object does not have getView function then checks whether object has viewUrl property
3) If above two steps fails to produce url or a DOM view drundal falls to default convention
which maps moduleid xyz.js to view xyz.html using view url ( path of Views folder ) defined in main.js
so for moduleid xyz.js path of the view will be views/xyz.html
you can overwrite this default mapping behavior by overwriting convertModuleIdToViewId function.
So there are many ways you can customize your view url for specific model (.js object)
I made an extension to Durandal which gives you the ability to place an applicationContent div in your cshtml file together with the applicationHost div. In applicationContent you can now use both ASP .Net MVC syntax together with knockout bindings.
Only thing I did was put some extra code in the viewLocator.js file which looks for an applicationContent div:
locateViewForObject: function(obj, area, elementsToSearch) {
var view;
if (obj.getView) {
view = obj.getView();
if (view) {
return this.locateView(view, area, elementsToSearch);
}
}
if (obj.viewUrl) {
return this.locateView(obj.viewUrl, area, elementsToSearch);
}
view = document.getElementById('applicationContent');
if (view) {
return this.locateView(view, area, elementsToSearch);
}
var id = system.getModuleId(obj);
if (id) {
return this.locateView(this.convertModuleIdToViewId(id), area, elementsToSearch);
}
return this.locateView(this.determineFallbackViewId(obj), area, elementsToSearch);
},
Your original cshtml file can now do something like this:
<div class="row underheader" id="applicationContent">
<div class="small-5 columns">
<div class="contentbox">
#using (Html.BeginForm("Generate", "Barcode", FormMethod.Post, Attributes.Create()
.With("data-bind", "submit: generateBarcodes")))
{
<div class="row formrow">
<label for="aantalBijlagen">#Translations.Label_AantalBijlagen</label>
</div>
<div class="row">
<select name="aantalBijlagen" class="small-6 columns">
<option>0</option>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
</select>
</div>
<div class="row">
<button class="button right" type="submit" id="loginbutton"><span class="glyphicon glyphicon-cog"></span> #Translations.Action_Generate</button>
</div>
}
</div>
</div>
<div class="small-7 columns" data-bind="if: hasPdfUrl">
<div class="contentbox lastcontent">
<iframe data-bind="attr: {src: pdf_url}"></iframe>
</div>
</div>
You can find my fork of the durandal project here and a small blogpost of what and how I did this here.
I'm not very familiar with DurandalJS but because it's a client-side system, it should make no difference what technology is used on the server to generate the HTML markup. So if you use Razor CSHTML files to generate the HTML on the server, DurandalJS should work just fine with it.
If you're getting a particular error then please share that error, but I can't think of any reason why it wouldn't work.

How do I get a link for a protected page and redirect the user to the login page?

In my Sitecore site I need a page viewable only to authorized users. I have allowed read and inheritance for the role I want and denied read and inheritance for extranet\anonymous. The item is part of a group where the other items are not protected. This list of items is databound and rendered as navigation links on the site.
var id = new Sitecore.Data.ID("<guid here>");
var item = Sitecore.Context.Database.GetItem(id);
// protected item is not part of Children collection when user is anon.
this.navrepeater.DataSource = item.Children;
this.navrepeater.DataBind();
When I'm logged in, a link to the protected item is shown and I can view the page. When I'm not logged in (operating as extranet\anonymous), the link is not shown at all. When I go the url directly I get a 403 error. In my web.config I have set the loginPage attribute on the site node but I don't get redirected.
<site name="mysite" ... loginpage="/login.aspx" />
So,
How do I display the link to the protected page for anonymous users
How do get sitecore to redirect the user to the login page when needed
1) You can wrap the code that retrieves the items for the nav links in the SecurityDisabler to show the link even if they can't view the page:
using(new SecurityDisabler()) { // this bypasses any security
this.navrepeater.DataSource = item.GetChildren(); // note that the Children property is deprecated, use the GetChildren() method instead
}
2) If you make your page's code behind class inherit from Sitecore.Shell.Web.UI.SecurePage it will handle the check and redirect to the login page for you. No coding needed.