Get the number of users for each role - asp.net-membership

I'm using the default .NET Membership Provider and filling a Gridview with roles, using Roles.GetAllRoles(). I have a commandfield to delete roles and a boundfield for the role itself. What I need to do is to add the number of users per role, the role column would look like this;
Administrators(4)
Supervisors(12)
To prevent deleting a role that has users, I was thinking of using the RowCreated event to fetch the database and add the value to the roles. Is there a better way to do it?
Edit: Solution
To achieve what I need I had to use RowDataBound event.
protected void RoleList_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
string roleName = ((Label)e.Row.FindControl("RoleNameLabel")).Text;
if (roleName.Trim().Length > 0 && Roles.RoleExists(roleName))
{
Int32 roleCount = Roles.GetUsersInRole(roleName).Count();
((Label)e.Row.FindControl("RoleNameLabel")).Text += "(" + roleCount + ")";
}
}
}

You can use Roles.GetUsersInRole. For example:
If Roles.GetUsersInRole(strRole).Count > 0 Then
'do NOT delete this role
End If

Related

How to create separate DetailTable on each row in a RadGrid?

I have a telerik radgrid where columns and detail tables are declared like:
<telerik:RadGrid>
<Columns>
<telerik:GridBoundColumn/>
<telerik:GridBoundColumn/>
</Columns>
<DetailTables>
<telerik:GridTableView
<Columns>
<telerik:GridBoundColumn/>
<telerik:GridBoundColumn/>
</Columns>
</telerik:GridTableView
</DetailTables>
</telerik:RadGrid>
Which gives a nested grid like this:
Now, what I want is to be able to specify a detail table (those sub tables) per row, programmatically.
(I cannot be sure that the columns for the nested table that comes up when I expand the line fgvbvb will be the same as the columns when expanding the line xcxcv).
I have tried, without luck in the OnDataBound handler of the radgrid (in which I omitted <DetailTables>) to access the data structure for nested tables like this:
protected void OnRadGridDataBound(object sender, EventArgs e)
{
foreach (GridDataItem item in grdActivitiesToCopy.MasterTableView.Items)
{
var dg = item.ChildItem.NestedTableViews[0];
}
}
This will overindex the array NestedTableViews because it is empty. Also, item.ChildItem.NestedTableViews has no setter.
How do I populate each row with a detail table one by one manually?
According to Telerik:
RadGrid does not support mixing declarative grid columns with grid
columns added dynamically at runtime. You should either create all the
columns in the grid programmatically, or else define them all in the
ASPX file. When creating Detail tables, it should be created in the
PageInit event.
Creating a Hierarchical Grid Programmatically:
You should follow these basic steps in order to create hierarchical
RadGrid programmatically in the code-behind (having a data source
control for data content generation):
Create the grid dynamically in the Page_Init handler of the page by
calling its constructor.
Specify the preferred settings for your grid instance through its
properties.
Create columns for the grid dynamically. Keep in mind that you have to
first set their properties and then add them to the
MasterTableView/GridTableView collection (discussed in the first
paragraph of this same topic). Thus, their ViewState will be properly
persisted (as LoadViewState is raised after the Init event of the
page).
Set the proper ParentTableRelations for the GridTableViews (along with
their MasterKeyField and DetailKeyField attributes) and DataKeyNames
for the MasterTableView/GridTableViews in the code-behind of the page.
Assign data sources (through the DataSourceID attribute) for each
table in the grid hierarchy.If you do not want to use declarative
relations, generate the data in the NeedDataSource/DetailTableDataBind
handlers of the grid. On DetailTableDataBind you can determine which
data source should be related to the currently bound GridTableView by
checking its Name/DataSourceID property. Here, the Name property must
have a unique value for each detail table (this value has to be
defined previously by the developer) and the DataSourceID is the ID of
the DataSource control responsible for the corresponding detail table
content generation.
Code Sample:
RadGrid RadGrid1 = new RadGrid();
RadGrid1.DataSourceID = "SqlDataSource1";
RadGrid1.MasterTableView.DataKeyNames = new string[] { "CustomerID" };
RadGrid1.Skin = "Default";
RadGrid1.Width = Unit.Percentage(100);
RadGrid1.PageSize = 15;
RadGrid1.AllowPaging = true;
RadGrid1.AutoGenerateColumns = false;
//Add columns
GridBoundColumn boundColumn;
boundColumn = new GridBoundColumn();
boundColumn.DataField = "CustomerID";
boundColumn.HeaderText = "CustomerID";
RadGrid1.MasterTableView.Columns.Add(boundColumn);
boundColumn = new GridBoundColumn();
boundColumn.DataField = "ContactName";
boundColumn.HeaderText = "Contact Name";
RadGrid1.MasterTableView.Columns.Add(boundColumn);
//Detail table - Orders (II in hierarchy level)
GridTableView tableViewOrders = new GridTableView(RadGrid1);
tableViewOrders.DataSourceID = "SqlDataSource2";
tableViewOrders.DataKeyNames = new string[] { "OrderID" };
GridRelationFields relationFields = new GridRelationFields();
relationFields.MasterKeyField = "CustomerID";
relationFields.DetailKeyField = "CustomerID";
tableViewOrders.ParentTableRelation.Add(relationFields);
RadGrid1.MasterTableView.DetailTables.Add(tableViewOrders);
Please refer to this help article for more details:
http://docs.telerik.com/devtools/aspnet-ajax/controls/grid/defining-structure/creating-a-radgrid-programmatically#creating-a-hierarchical-grid-programmatically
First of all , because of the life cicle of a asp page. You can't access to a event on a detail table.
If you need to access detail tables , items etc ..
You need to add an method to the PreRender in the MasterTableView like this:
<MasterTableView DataSourceID="myDataSource"
AllowMultiColumnSorting="True"
DataKeyNames="Key1,Key2,KeyN"
HierarchyDefaultExpanded="True"
OnPreRender="Unnamed_PreRender" >
The method will recursively iterate through the grid.
The way you do it can change depending on your HieararchyLoadMode.
So this is my way to do it, easiest way exist if you are on Client or Serverbind mode.
Traversing and load mode by the telerik doc .
I'm pretty sure you don't want to :
"populate each row with a detail table one by one manually"
You want to have Multiple table at a Sub Level in your grid and display the rigth one programmatically.
And this is can be done in two easy step:
1/. Create every Detail table in your apsx page.
Please refer to this documentation for more information :
Several tables at a level
2/. Handle the display:
protected void Unnamed_PreRender(object sender, EventArgs e)
{
if (!IsPostBack) myControler(MASTERGRID.MasterTableView);
}
private void myControler(GridTableView gridTableView)
{
GridItem[] nestedViewItems = gridTableView.GetItems(GridItemType.NestedView);
foreach (GridNestedViewItem nestedViewItem in nestedViewItems)
{
foreach (GridTableView nestedView in nestedViewItem.NestedTableViews)
{
if (nestedView.Name == "mytable12" && nestedView.Items.Count == 0)
{ HideExpandColumn(nestedView, nestedView.ParentItem["ExpandColumn"]); }
else if (nestedView.Name == "mytable23")
{
if (nestedView.Items.Count == 0)//
HideExpandColumn(nestedView, nestedView.ParentItem["ExpandColumn"]);
else
{ }
}
if (nestedView.HasDetailTables)
{ myControler(nestedView); }
}
}
}
private void HideExpandColumn(GridTableView _GNVI, TableCell _cell)
{
if (_cell.Controls.Count > 0)
{
_cell.Controls[0].Visible = false;
_cell.Text = " ";
}
_GNVI.Visible = false;
}
You can hide a detail table using :
HideExpandColumn(nestedView, nestedView.ParentItem["ExpandColumn"]);
Or you can hide the parent of the detail table you tested using the detail table that is in param of the controler :
HideExpandColumn(gridTableView, nestedView.ParentItem["ExpandColumn"]);
HideExpandColumn will hide the expand control that stay sometimes even if you hide th detail table.
Bonus: If you need to access to a control in a detail table.
You can use this:
public static class ControlExtensions
{
public static Control FindIt(this Control control, string id)
{
if (control == null) return null;
Control ctrl = control.FindControl(id);
if (ctrl == null)
{
foreach (Control child in control.Controls)
{
ctrl = FindIt(child, id);
if (ctrl != null) break;
}
}
return ctrl;
}
}
Calling it in your controler like this :
else if (nestedView.Name == "DetailPV")
{
if (nestedView.Items.Count == 0)
HideExpandColumn(gridTableView, nestedView.ParentItem["ExpandColumn"]);
else
{
RadLabel ctrl = (RadLabel)this.FindIt("RadLabel11");
ctrl.Text += "<b>" + nestedView.Items.Count.ToString() + "</b>";
}

Knockout Performance - Filtering an Observable Array

I'm new to Knockout, and I'm trying to use it on a page showing a system's users and the roles that each user has.
The data is in an observableArray of users. The user object has a roles property, which is another observableArray. This second array contains an object for each role, with an ID and a boolean "granted" property.
I want to be able to display all of the users with a specific role, so there's a checkbox for each role - when one of these is checked, the list should show the users with that role.
The problem I've got is that filtering the 1,000 or so users by roles takes several seconds. Filtering by the text in the name is very quick (a few milliseconds), but filtering by role is not. I've put some timing code in, and the issue is the method I'm using to check whether the user has the selected role(s) so I'm just wondering whether there's a better way of doing it, maybe using some Knockout magic.
Below is the ko.computed on the view model that I'm using to do the filtering. The results table is bound to this function.
self.filteredUsers = ko.computed(function () {
var textFilter = self.filter(); // this is an observable bound to a text field
var checkedRoles = self.selectedRoles(); // this is a computed, which returns an array of checked roles
return ko.utils.arrayFilter(self.users(), function (user) {
var match = true;
if (user.displayName.toLowerCase().indexOf(textFilter.toLowerCase()) == -1) {
match = false;
}
// for each ticked role, check the user has the role
for (var i = 0; i < checkedRoles.length; i++) {
var roleMatch = false;
for (var j = 0; j < user.roles().length; j++) {
if (user.roles()[j].roleId === checkedRoles[i].roleId && user.roles()[j].granted()) {
roleMatch = true;
break;
}
}
if (!roleMatch) {
match = false;
}
}
return match;
});
});
I think that a good optimization would be creating a grantedRoles computed on your user object. This computed would return an object that you can use as an index, would contain properties keyed by a role's unique identifier and would only contain roles that are granted.
Then in filteredUsers, you would check the grantedRoles object against each checked role, rather than looping through user.roles() for each checked role.

Created By LoginName (ID) with SPMetal in SharePoint 2010

I'm working with the OOB blog sites in SP2010. I'm using SPMetal to generate entity classes for the Posts list (among others). I've used a parameters.xml file to get the other columns that I need that aren't included by default.
One of the things that I want to do is to get the users' My Site url. I am able to do this with CAML relatively easily. However I need to do it using Linq. I can't figure out how to get the login id (i.e. domain\id) for the Author Field. I've looked through the Contact content type and it doesn't appear to have anything to help.
Has anyone run across this or gotten the login id for a user with SPMetal?
if you create Entity of Posts list using SPMetel.exe and if in Posts list having Suppose Field Type is User than automatically return two methods of like LookupId and LookupValue.
In my case : I have take promoterid As a Field name in Posts list in in my Entity having two method
private System.Nullable<int> _promoterId;
private string _promoter;
[Microsoft.SharePoint.Linq.ColumnAttribute(Name="promoterid", Storage="_promoterId", FieldType="User", IsLookupId=true)]
public System.Nullable<int> PromoterId {
get {
return this._promoterId;
}
set {
if ((value != this._promoterId)) {
this.OnPropertyChanging("PromoterId", this._promoterId);
this._promoterId = value;
this.OnPropertyChanged("PromoterId");
}
}
}
[Microsoft.SharePoint.Linq.ColumnAttribute(Name="promoterid", Storage="_promoter", ReadOnly=true, FieldType="User", IsLookupValue=true)]
public string Promoter {
get {
return this._promoter;
}
set {
if ((value != this._promoter)) {
this.OnPropertyChanging("Promoter", this._promoter);
this._promoter = value;
this.OnPropertyChanged("Promoter");
}
}
}
than after i can able to use using linq query
i.e
SPWeb oWebsiteRoot = SPContext.Current.Web;
EntitiesDataContext objent = new EntitiesDataContext(oWebsiteRoot.Url);
EntityList<PostsItem> evnitems = objent.GetList<PostsItem>("Posts");
var i = from item in evnitems
where item.PromoterId == SPContext.Current.Web.CurrentUser.ID
select item;

Users restrictions for associated data in ASP Membership

I have a site I'm porting to MVC to clean up the code and simplify things. I use the asp membership and profile providers, but I'm wondering if I'm doing this correctly for my situtation. I'm pretty new to MVC, so I wan to get this right in the early stages.
Users are individuals and they are part of larger "institutions" that they either set up or pick at registration. In this case, the institution is a winery. I want the users to be able to view all wines from every winery, but only edit ones that belong to them.
What's the best way to do this? Right now I render the link to the edit field in my index view based on their instution ID and the producer ID. I feel like a data annotation might work better here, but I don't exactly how to implement that for a group of wines. Do I need multiple providers? I use roles to limit the editing, but right now an editor role could manually enter the path of another wine to edit it when that wine doesn't belong to them.
Any pointers here would be awesome. I know I can do it in the controller methods, but I'm looking for the 'right' way to do it. Thanks.
I'm running into the same issue at work right now, and the best proposed solution we have right now is implementing an "ownership" table. You won't be able to solve this using roles.
So basically you have an owner ID, owned object's ID, and the type of objects ID all held together. Lets take an edit request for example. We know that you can only edit the data person X owns, so we have a stored procedure that if a key combination exists in our ownership table where person.ID = owner ID, and item.ID = object ID, and item.TypeID = objectTypeID. If it exists, it goes along performing its edits, otherwise it returns an error.
You can use this scheme to return ownership lists, user validation, and a host of other issues you may come across. You probably won't need the ObjectTypeID if you only have one type's ownership being tracked. Hope this helps!
I figured this out by applying a custom AuthorizeAttribute to the edit, delete, and create actions.
Here is what I ended up doing:
public class ProducerEditAttribute : AuthorizeAttribute
{
private vfContext db = new vfContext();
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
bool bAdmin = filterContext.HttpContext.User.IsInRole("admin");
bool bProdEdit = filterContext.HttpContext.User.IsInRole("producereditor");
bool bProd = filterContext.HttpContext.User.IsInRole("producer");
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if (bAdmin)
{
//authorized
}
if (bProdEdit || bProd)
{
//check for current wine belonging to the producer.
Producer currentProd = db.Producers.Find(Profile.GetProfile(filterContext.HttpContext.User.Identity.Name).InstitutionID);
Wine currentWine;
object WineID;
if (filterContext.RouteData.Values.TryGetValue("id", out WineID))
{
currentWine = db.Wines.Find(int.Parse(WineID.ToString()));
if (currentProd.Wines.Contains(currentWine) && bProdEdit)
{
//authorized
}
else if (bProd)
{
var result = new ViewResult();
result.ViewName = "Login.cshtml"; //this can be a property you don't have to hard code it
result.MasterName = "_Layout.cshtml"; //this can also be a property
result.ViewBag.Message = "You do not have permission for add/edit/delete funciontionality. Please request.";
filterContext.Result = result;
}
else
{
var result = new ViewResult();
result.ViewName = "Login.cshtml";
result.MasterName = "_Layout.cshtml";
filterContext.Result = result;
}
}
}
else
{
var result = new ViewResult();
result.ViewName = "Login.cshtml";
result.MasterName = "_Layout.cshtml";
}
}
}
}

Custom column in LinqServerModeDataSource

I have table in my database names User with fields:
Id
FirstName
LastName
LoginName
Now I would like to present this table in ASPxGridView using LinqServerModeDataSource.
What I did is :
<dxdtlnq:LinqServerModeDataSource ID="LinqServerModeDataSource1" runat="server" OnSelecting="LinqServerModeDataSource1_OnSelecting"
ContextTypeName="MyContext" EnableDelete="True"
EnableInsert="True" EnableUpdate="True" TableName="Users" >
</dxdtlnq:LinqServerModeDataSource>
protected void LinqServerModeDataSource1_OnSelecting(object sender, LinqServerModeDataSourceSelectEventArgs e)
{
MyContext context = new MyContext();
var qry = from s in context.Users select s;
e.QueryableSource = qry;
}
That works great with my ASPxGridView. I can display data, insert and delete but now I would like to have additional column UserName which is FirstName + LastName.
So I did something like that:
Added appropriate column to my ASPxGridView (FieldName = "UserName")
and modified OnSelecting handler:
protected void LinqServerModeDataSource1_OnSelecting(object sender, LinqServerModeDataSourceSelectEventArgs e) {
KozuModelDataContext context = new MyContext();
var qry = (from s in context.Substitutions
select new
{
Id = s.Id,
FirstName = s.FirstName,
LastName = s.LastName,
LoginName = s.LoginName,
UserName = s.FirstName + " " + s.LastName,
}).AsQueryable();
e.KeyExpression = "Id";
e.QueryableSource = qry;
}
Now data in the grid is displayed buyt when I want to insert or edit data fields cannot be filled, textboxes doesnt respond in inserting form I cant type in any text.
Is there any solution for inserting and editing data in this manner ?
Thanks for help
I have tried to reproduce this issue and see this behavior. To be able to edit data using this approach, I suggest that you do the following:
1) Handle the ASPxGridView.CellEditorIntitialize event and set the e.Editor.ReadOnly property to false. This will allow the end-user to modify data in the EditForm editors;
2) Handle the ASPxGridView.RowUpdating (RowInserting) event and update data manually. Also, you should set the e.Cancel parameter to true (to prevent the grid from updating data itself) and also call the GridView's CancelEdit method to close the EditForm.
I should also mention that there is no need to fetch data for the UserName from the DB. This can be done using the ASPxGridView's CustomColumnDisplayText event handler:
protected void ASPxGridView1_CustomColumnDisplayText(object sender, DevExpress.Web.ASPxGridView.ASPxGridViewColumnDisplayTextEventArgs e) {
if(e.Column.FieldName == "") {
object[] values = ASPxGridView1.GetRowValues(e.VisibleRowIndex, new string[] { "FirstName", "LastName" }) as object[];
e.DisplayText = values[0].ToString() + " " + values[1].ToString();
}
}
If this approach works for you, you can avoid using the Selecting event and thus set the LinqServerModeDataSource's TableName. This will allow you to provide the data editing feature not using the approach I explained above.

Resources