Wednesday, August 20, 2008

Role Enabled Custom Controls

So we did all this work to a membership provider and a role provider for ASP.NET and it was a lesson in patients as we figured out how it was all suppose to work.  However, now we have it I found out that the ASP.NET controls are not enabled for roles.  It works well with our Forms authentication for blocking access to pages where the user doesn't have the right role.  However, I want to display controls and not display controls based on the user's role.  I love to create custom controls that work just like we want them to work.  So I subclassed the Panel control and "roles" enabled it.  Code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace WebUtility
{
    [DefaultProperty("Text")]
    [ToolboxData("<{0}:RolePanel runat=server></{0}:RolePanel>")]
    public class RolePanel : Panel
    {
        /// <summary>
        /// Gets or sets the roles
        /// </summary>
        /// <value>The on click.</value>
        [Bindable(true)]
        [DefaultValue("")]
        [Localizable(true)]
        public string Roles
        {
            get
            {
                String s = (String)ViewState["Roles"];
                return s ?? string.Empty;
            }
            set
            {
                ViewState["Roles"] = value;
            }
        }

        protected override void OnLoad(EventArgs e)
        {
            Boolean bFound = false;
            foreach (String role in Roles.Split(','))
                if (Page.User.IsInRole(role.Trim()))
                {
                    bFound = true;
                    break;
                }

            if ((Visible == true) && (bFound == false))
                Visible = false;

            base.OnLoad(e);
        }
    }
}

Now all I have to do is to all this ASP.NET code to our pages:

<webutility:RolePanel runat="server" ID="adminRoleHyperLink" Roles="AdministrationAccess">Admin</webutility:RolePanel>

The new Roles property is a comma delimited list of "roles" that are checked against the users permissions when the page loads, if they have one role in the list they can see everything in the panel.

Note that the same code will work for most all ASP.NET controls without changing it, just modify the name of the control you are subclassing.

{6230289B-5BEE-409e-932A-2F01FA407A92}

 

 

Tuesday, August 19, 2008

Dynamic Javascript in an Update Panel - A Better Way

Last post was about how dynamic javascript in the update panel was not being re-executed on a partial postback.  I have a better way of making it execute -- parse the controls in the UpdatePanel and call the javascript method eval().

Here is the hook:

Page.ClientScript.RegisterStartupScript(this.GetType(), "LoadHandler", "function LoadHandler(){EvalScript($get('" + updatePanel.ClientID + "'),'scriptId');};\r\n", true);

Page.ClientScript.RegisterStartupScript(this.GetType(), "AddLoadHandler", "Sys.Application.add_load(LoadHandler);\r\n", true);

EvalScript looks like this:

function EvalScript(control, id)
{
 if(!control){return;}
 for(var n=0;n<control.children.length;n++)
 {
     if((control.children[n].tagName == 'SCRIPT') && (control.children[n].id == id)){
         var scriptElement = control.removeChild(control.children[n]);
         eval(scriptElement.innerHTML);
     }else{
         EvalScript(control.children[n], id);
     }
 }
}

Basically EvalScript searchs client side all the controls in the updatepanel control for one with the name specified, then calls Eval on it's innerHTML.  Just a note, you don't want inside comments in your <SCRIPT> tag, they don't eval() well.

{6230289B-5BEE-409e-932A-2F01FA407A92}

Friday, August 15, 2008

Dynamic Javascript in an Update Panel

If you are using an Ajax UpdatePanel in ASP.NET and you have a control that renders javascript, that javascript will not change when on a partial postback.  For example, I have a Repeater control that loops though so GEO points and outputs javascript to add PushPin to a Virtual Earth map.  If a partial postback is called, and the Repeater rebinds it recreates the javascript, however on the client side the ScriptManager just replaces the UpdatePanel innerHTML with then new code.  It doesn't tell the javascript engine to reparse that code and recreate the functions that are being replaced.  To solve this problem I had to write some javascript client code that would parse the return, and move my <SCRIPT> tags to the head of the page.  This "woke up" the javascript engine and the new functions where recompiled.

How to hook it up:

1) The first thing to do is to tell the ScriptManager to parse the innerHTML of the UpdatePanel and move all script blocks to the head of the page (note: you need to have a page head).  You can do this like this:

Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);

2) Next you need to add a function that call EndRequestHandler that will call the parser, like this:

function EndRequestHandler(sender,args){ParseScript($get('UpdatePanelId'));};

Mine looks like this:

Page.ClientScript.RegisterStartupScript(this.GetType(), "EndRequestHandler", "function EndRequestHandler(sender,args){ParseScript($get('" + updatePanel.ClientID + "_Container'));};\r\n", true);

Not that the ParseScript function traverses the controls in the page and find childern with the tag name of <SCRIPT>, so I wanted to start with a UpdatePanel parent, and since the id is dynamically generated, I need to add the EndRequestHandler client side javascript from the server.

3) Next write ParseScript to traverse the childern and move all SCRIPT tags to the head, I will let you write your own code, cause this is QED.

{6230289B-5BEE-409e-932A-2F01FA407A92}

Thursday, August 14, 2008

I am back...

I took off 4 months to remodel my kitchen, in that time I did no programming.  Now I am back to work for at least a week now.  I havn't forgotten anything (except for the SQL Server sa password -- which means that it is a good one.)  I can program just as well as before I left. 

{6230289B-5BEE-409e-932A-2F01FA407A92}

Monday, August 11, 2008

DAL using subsonic

I was investigating a starter website and noticed that it mentioned subsonic. Since third-party components can be tricky for implementation and security, I went to see what it was. Subsonic is an open source Data Access Layer. Someone has probably mentioned it to me before but it didn't register. The website has videos using subsonic which are great. Subsonic connects to the database and creates a DAL for a .Net project which uses intellisense for discovery. The only thing that worries me is that the connection isn't through SPs they create but they say they parse the SQL commands so that SQL injection isn't a problem. I also watched another video of theirs about their REST handler on their tips and tricks page. While I still like .NetTiers for my DAL (which requires a purchase of CodeSmith), I liked the easy of use and the fact that CodeSmith is not required. Just as I was grumbling to myself about a startersite for this, I found the link on the front page of their site for just that. It's a content management site that includes login, page generation, etc. The video cut out on me halfway through but I got the main idea and I liked it. I also noticed that the video mentioned FCKeditor which I hadn't heard of before.

I like this stuff but will probably use it for my own admin pages. It's not that the product is bad but I want to go through generated sp's and I like that .NetTiers gives me an admin site and web service. .NetTiers is also a layer I know who to code to and go farther with in terms of specificity and inheritance. But I really like this subsonic stuff.