In my company we're currently working in our portal. It will be something like a social network.
I'm working on the "profile" part of the portal.
I was wondering, as I'm new to C#, how can I implement a "see-only-what-i-let-you" functionality.
Take for instance Facebook. There you can show only parts of your profile to some people, other parts to other people, you have full acess as others may have no acess at all.
That's precisely what I need to implement.
We're working with MVC3/jQuery1.5/WCF.
One option would be to split your view up in to child actions:
#{Layout = "~/Views/Shared/Layouts/MasterLayout.cshtml";}
<div class="splitter">
<div class="left-column">
#Html.Action("Navigator")
#Html.Action("MyPosts")
</div>
<div class="main-content">
#Html.Action("RecentStories")
#Html.Action("Adverts")
</div>
</div>
Then in your controller each action decides if the current user can see this piece of content, e.g.
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult RecentStories()
{
if(current_user_cannot_access_this_content())
return View("BlankView"); // Might want to render some place holder content
// Setup necessary view data by pulling back content from the database.....
return View(); // Render the /Views/<controller>/RecentStories.cshtml view
}
Try to cache user permission data across child actions if possible (depends on how your calculation of what a user can see works).
This code is assuming all your actions are running off the same controller. You can hit a different controller for a child action just by passing the name of the controller as the second parameter.
Related
I need to implement some logic to all views globally. I have a _layout view. How can I run logic (via a controller) for the shared layout?
I'm looking to implement the following logic:
Check if the user can perform an action of controller (if not, return to the previous page)
Check if the user account is active (after login, a user account can be disabled) (if not, log out and return to the login page)
Update the user log (update a table to save the IP and the time of the last user request to the server)
Hide controls for the user (prevent the user from seeing a control or changing any data)
In ASP.NET Web Forms 4, I created a class called Users, and this class was called inside of the master page's .cs code behind.
How can I accomplish something similar in ASP.NET Core MVC?
Welcome to MVC! The transition from ASP.NET Web Forms can be a bit frustrating as the programming models are so different. But once you get past the conceptual hurdles, you'll find a lot of tasks are much cleaner and easier to maintain.
I struggled with a similar challenge myself when migrating from ASP.NET Web Forms to, first, the ASP.NET MVC Framework and, later, ASP.NET Core MVC. There are a few common strategies for addressing problems like this, which I'll summarize below.
Custom Filter
For most of what you're talking about, the textbook solution is to create an custom filter, which is a piece of code that can be applied to multiple actions or controllers, and will be called at various stages of execution (e.g., before or after an action). You can also wrap filters in attributes, which makes it easy to apply them as an annotation to any action or controller. You can also register filters globally so they apply to all actions on all controllers. Filters are especially useful when you want to validate a request based on the current request—including potentially the current user.
View Component
Another approach, introduced with ASP.NET Core, is a View Component, which is a bit like a partial view, except that it has code behind it that operates a lot like a controller. This is useful if you need reusable components that share their own model on every page of your site, as it allows that to be constructed independently of any one action, while being shared across multiple views.
The textbook example of a view component is a login control, which receives a view model with information about the currently authenticated user, and uses that to conditionally display e.g., a sign-in or sign-out link.
Aside: I often use view components for centralizing my navigation, so I don't need to relay the data for the navigation down to every single view model. That's beyond what you're asking about here, but may help further conceptualize when a view component is useful.
Business Object
From a code organization perspective, you can still maintain much of the logic inside a custom User class, or even as extension methods off of e.g. the ASP.NET Core ClaimsPrincipal class (which is returned from the HttpContext.User property). Determining what the current state of the user is for the purposes of e.g. authorization makes sense in a separate class; handling the ASP.NET Core response to that (such as redirection to a previous page) belongs in the filter or view component.
And, of course, you can also relay (properties from) this class to your view via your action's or view component's view model if you need to rely upon them to customize the user interface.
Conclusion
There are obviously a number of approaches here—as well as some I haven't mentioned, such as implementing a base controller—but this should help get you started in thinking about how to centralize site-wide business logic without needing to repeat it in every action of every controller, or polluting your _Layout.cshtml with logic that would normally reside in a controller.
First I wouldn't recommend a Controller for the _layout page as this is not how the MVC architecture works.
You could create a view using the layout page, like UserView.cshtml.
Then you can create a UserController.cs and your actions in it.
I hope I could help.
Check if the user can perform an action of controller (if not, return
to the previous page) Check if the user account is active (after
login, a user account can be disabled) (if not, log out and return to
the login page) Hide controls for the user (prevent the user from
seeing a control or changing any data)
You can use #if(User.IsInRole("Admin")) to determine the identity of the user in the layout, and then if the user can't perform an action of controller, use class="disabled" to disable the action.
If is the user you want to show a control , you can contains this control after it is judged to be true. So hide controls for the user.
For example, if it is admin, you can add a role or view the current role.
Demo as below:
_layout:
#inject SignInManager<AppUser> SignInManager
#inject UserManager<AppUser> UserManager
#using Microsoft.AspNetCore.Identity
#using Identity.Models
<!DOCTYPE html>
<style>
.disabled {
pointer-events: none;
color: grey;
text-decoration: none;
}
</style>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>#ViewBag.Title</title>
<link href="~/twitter-bootstrap/css/bootstrap.css" rel="stylesheet" />
<p>Login</p>
#if(User.IsInRole("Admin"))
{
<p>Admin</p>
<p>Role</p>
<p>Claims</p>
<li >
<a asp-area="" asp-controller="Role" asp-action="Create" >Hello #UserManager.GetUserName(User)! Create a Role</a>
</li>
}
else
{
<p><a class="disabled"href="/Admin" target="_blank" >Admin</a></p>
<p><a class="disabled" href="/Role" target="_blank" >Role</a></p>
<li >
<a asp-area="" asp-controller="Role" class="disabled"asp-action="Create" >Hello #UserManager.GetUserName(User)! Create a Role</a>
</li>
}
</head>
<body class="m-1 p-1">
#RenderBody()
</body>
</html>
result:
Update the user log (update a table to save the IP and the time of the
last user request to the server)
Use javascript to get the URL of the current request and the time of the current request, and then use ajax to call the action of the back-end controller, and record the time of the obtained request into the database.
I have a notifications badge in the header of my _Layout.cshtml file. This is so I can constantly show the user their notifications regardless of what page they are on.
Notifications <span class="badge">#ViewData["NotificationCount"]</span><br>
However, I am wondering if there are better ways to do this. It seems like I need to do the following at the top of every single one of my views:
#{
ViewData["ReminderCount"] = Model.Notifications.Count();
}
This also means I need to have a list of notifications as part of every single page ViewModel throughout my application.
Am I missing something here? I feel like there has to be a better way to handle things like this in ASP.NET MVC.
The ideal thing would be to load the user's notifications one time as soon as they login and go to their dashboard and then continue to show that number across all pages unless they check the notifications, then the number clears.
What you can do is create an action and against that action create a partial view and in the _Layout.cshtml call it, this will save you from code duplication and ViewData :
You action code will look like:
public ActionResult Notifications()
{
var model = Model.Notifications.Count();
return View("_NotificationsPartial",model);
}
your partial view _NotificationsPartial.cshtml would be like:
#model System.Int32
<a href="#">Notifications
<span class="badge">#Model</span>
Now you just need to call it in your _Layout.csthml:
#Html.Action("Notifications","SomeController")
I have 3 different view(DetailView, CardView, Column) template/html pages to show in single-page. The user can switch between these 3 view.
I want to bind single view at a time in page, if user switch it will remove previous view and bind the new view. I have data in Model for bind the view so, I no need to call service to bind data. I want toggle between these three-view without refresh page and loading data.
Problem is, if bind three view it will conflict with div-id and there are lots of html-code for all view in DOM.
Please suggest me how to toggle between these different view without loading & refreshing page??
<body>
<div ng-include="'detailView.html'" ng-show="detailView"></div>
<div ng-include="'cardView.html'" ng-show="cardView"></div>
<div ng-include="'cardView.html'" ng-show="cardView"></div>
</body>
As i know Angular Apps are SPA (Single Page Application) so if you switch pages by routing its default behavior its the one that you are asking for. Its not reloading/refreshing the page. It remove the previous view and bind the new one.
Check this guide : https://scotch.io/tutorials/single-page-apps-with-angularjs-routing-and-templating
Also try to use $location service to switch routes. It does not reload the page.
$location.path("/your-route").
Angular has routing module. This way you can define a route (page) with it's own URL, HTML template and controller.
configuration example:
YOUR_MODULE.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/phones', {
templateUrl: 'partials/phone-list.html',
controller: 'PhoneListCtrl'
}).
when('/phones/:phoneId', {
templateUrl: 'partials/phone-detail.html',
controller: 'PhoneDetailCtrl'
}).
otherwise({
redirectTo: '/phones'
});
}]);
You can read more about it in angular's documentation:
https://docs.angularjs.org/tutorial/step_07
For bigger applications I would suggest you to use UI-ROUTER:
https://github.com/angular-ui/ui-router
Anyway, if you're looking for something simple without any routing, you should use NG-IF instead of NG-SHOW.
NG-SHOW just hiding the HTML by css (display none) which means there might be conflicts for elements with the same IDs.
NG-IF will remove the element from the DOM, so there won't be any conflicts.
Good luck!
From what i could understand, when the first time the page loads, you have certain flag up to show that view and corresponding call to a service to bind data to that view.
Next time, the model is updated and a new flag is set, a new view comes into play and a similar service binds data..
Initially set the model all to false and make one true for default.
Toggle through view as:
<body>
<div ng-include="'detailView.html'" ng-if="detailView"></div>
<div ng-include="'cardView.html'" ng-if="cardView"></div>
</body>
Through this, at a particular time only one div is active and id would not conflict.
In the controller:
If($scope.detailView == true){
//Call to service for data..
}
Similarly, when the new model is updated , set all previous to false.
Please update your query to more clarify your objective.
So I'm new to MVC 4 and I'm currently contemplating on what should I do.
CURRENT CODE
I made several SHARED layouts for each type of user. So I now have, _AdminLayout.cshtml for the Admin's sidebar, _FacultyLayout.cshtml for Faculty, and _StudentLayout.cshtml
PROBLEM
I have several types of users: Admin, Student, and Faculty. The content of the sidebar changes depending on which type of user is logged in.
Are there any other solutions for this?
I was planning to put all the sidebars in one SHARED LAYOUT but MVC 4 doesn't have Controllers for Shared Layouts unlike in Web Forms Master Pages, there is a Codebehind file. I'm just trying to shorten my code and lessen the redundancy between the 3 SHARED LAYOUTS that I currently have.
You can have one single layout and use #Html.Action() to render the sidebar as a partial view depending on the role of the user. This could go in a BaseController from which all other controlles inherit, or in a separate controller (say) LayoutController
[ChildActionOnly]
public ActionResult SideBar()
{
var role = ... // your code for getting the users role
if (role == "Admin")
{
return PartialView("_AdminSideBar");
}
else if (role == "Faculty")
{
return PartialView("_FacultySideBar");
}
else if ..........
}
and then create a associated partial views _AdminSideBar.cshtml, _FacultySideBar.cshtml etc.
Then in the Layout Page
......
<div id="sidebar">
#{ Html.RenderAction("", ""); }
</div>
I have a Model that loads the Sidebar for my webpage, along with a Model that loads the main content. The content Model will be different for each page whilst the Sidebar model will remain constant. The content Model will change by the user clicking links:
~/Home/About
~/Home/Contact
What I ideally want to to put a line of code in _Layout.cshtml that loads a Controller that returns a PartialView displaying the Sidebar Model. So we might have:
<div id="sidebar">
#Html.Render("~/SidebarController/GetSidebar");
</div>
<div id="content">
#RenderBody()
</div>
But I know this won't work. How do I achieve this?
What I would do is to use #Html.Action("GetSidebar") in the _Layout.cshtml file, then you can have an action in your controller
public ActionResult GetSidebar()
{
//do stuff, populate menu items from database? etc
// Pass the data to the partial view
return PartialView("_Sidebar");
}
You would need this in each of your controllers unless you put this in a base controller, which you can then inherit on all your other controllers and add [ChildActionOnly] to the top of your action so that it can not be called directly.
I do the same thing you are trying to do. I use:
#Html.Action("GetSidebar", "SidebarController")
to draw my side bar and it works fine. I use ajax calls when changing views though so as to save on loading the sidebar over and over again and I have the ajax target the "content" div replacing its content with the partial view that represents each page.