Last month, I outlined the specifications for a Web Part-based Admin Panel. This article reviews the code involved in building the Admin Panel. The code in this article relies to a limited degree on class libraries, which are not described in detail. If you are interested in more information about anything in the article, just provide a comment on the blog and answers will appear.
The Admin Panel is a Web Part. This allows the designer to include personalizable properties, which allow for customization of the Web Part. Properties include:
- AdminRoles: a comma-delimited list of Roles that are allowed access to admin level functions included in the Web Part. The default Role is “Admin”.
- LinkCssClass: a css class used to style the links. The default is “hyperlinkV”.
- MouseOverCssClass: a css class used to style the links when the user hovers over a link. The default is “hyperlinkVMouseOver”.
- TargetZone: the Web Part Zone where Web Parts are created. The default is “Row1Column2”.
- NavLinksFile: an xml file that contains navigation information for the site to implement the ‘My Links” feature*. The default is the path to the NavLinksFile used in the site.
- MenuName: the name of the menu to display to the user from the NavLinksFile to implement the “My Links” feature. The default menu name is “My Links”.
* The “My Links” feature was added after the specification was finished. A customer said “Wouldn’t it be nice if….” and My Links was born.
Since properties are standard items for a Web Part, the code is not included for the properties.
The Admin Panel and the Web Page
The Admin Panel is added to a web page. When the page is displayed, it displays the CMS Admin Panel shown in the figure below. Once the user is logged in, the Admin Panel Web Part can be edited to set the Personalizable properties to values other than the default values.
The categories in the Admin Panel went through extension partitioning during the design and initial beta testing. By putting the administration related functions into a single page, a number of revelations were identified:
- What is the REAL purpose of the Web Part?
- Cut out the fat in the Web Part
- Keep it simple, so that default properties make the Web Part usable when initially added to the page
- What is the correct partitioning of the Web Parts? (A lot of User Experience discussions went into this stage)
- How do users see administration being done?
- What is the travel required between Web Parts?
- What administration-related Web Parts are missing?
- It is easy to carried away in this stage – after all, we do love to write code:-)
Figure 1: CMS Admin Panel (shown for a logged in user)
The left Admin Panel shows what a logged in user with the Admin role sees. The right Admin Panel shows what a logged in user without the Admin role sees. Both are based on the default roles included in Web Parts.
<%@ Page MasterPageFile="~/App_Master/W1MasterPage.master" Title="Jetfire Administrator's Tools" %>Code Snippet 1: The Admin Panel in a Web Page
<%@ Register TagPrefix="cc1" Namespace="TrackerRealm.WebPartsE" Assembly="TrackerRealm.WebPartsE" %>
<asp:Content ID="Content2" runat="server" ContentPlaceHolderID="ContentPlaceHolder1">
<table width="100%">
<tr valign="top"><td>
<asp:WebPartZone ID="Row1Column1" runat="server" HeaderText="Row 1 Column 1" >
<ZoneTemplate>
<cc1:AdminPanelPart ID="AdminPanelPart1" runat="server" />
</ZoneTemplate>
</asp:WebPartZone>
</td><td style="width:600px;">
<asp:WebPartZone ID="Row1Column2" runat="server" HeaderText="Row 1 Column 2" >
<ZoneTemplate>
</ZoneTemplate>
</asp:WebPartZone>
</td></tr>
</table>
</asp:Content>
Admin Panel and Site Initialization
The Admin Panel has real value during the design of a new website. View the page with the Admin Panel on it for a new site and the user is prompted to create a new user. If there is at least one user present but the user is not logged in, then the user is prompted to login. One of the requirements is that users MUST be logged in to use the Admin Panel.
// Check if there are users for this domain
if (Membership.GetAllUsers().Count < 1)
{ // Starting Condition for system: Create User and Login
this.AddError("CreateErr", "Create a CMS User");
CC.AddCreateUserPart("Login", Unit.Empty, this);
return;
}
if (!this.LoginProfile.CP.IsAuthenticated)
{ // User must login
this.AddError("LoginErr", "CMS Login is required to access the CMS Admin Panel");
CC.AddLoginPart("Login", Unit.Empty, this);
return;
}
Code Snippet 2: Site Initialization codePanels
Each major category in the menu is a Panel that hides and shows the links to Web Parts. Code Snippet 3 shows the method call to create the panel. The method takes an id, title, tooltip and parent control.
Panel pnl = AdminPanelPart.AddPanel("Page", "Page Management", "Tools to manage pages", this);
Code Snippet 3: Add a panel for the linksLinks to Web Parts are added to each panel. Since reflection is used to decide whether the Web Part is visible to the user, it is not known ahead of time what panels are displayed. Therefore a simple test is used to check if the panel should be visible.pnl.Parent.Visible = pnl.Controls.Count > 0;Code Snippet 4: Show the panel ONLY IF it contains linksFigure 2 shows the Admin Panel when a user has expanded the Page Management category. There are 4 links, each displayed using the css class defined in the site’s stylesheet.Figure 2: CMS Admin Panel showing the Page Management menu expandedLinks
Links for a set of Web Parts are added to the panel for each category. The objective is to have a simple method for adding the link to the panel. This method contains two parameters, the first for the type of Web Part being added and the second for the panel control that the link is added to. So simple – bizarre, that it should take about 6 iterations before arriving at this.
this.AddLink(typeof(CreatePagePart), pnl);
Code Snippet 5: Add a Link to a category
Code Snippet 6 shows the code used to add a link to the panel. The Admin Panel Web Part exposes library and editor functions. That is why the logged in user must be able to enter share scope on the page. Web Parts are dynamically added the first time that the Admin Panel is run in the AdminPanelPart.GetWebPart method. The Admin Panel Web Part has over 30 Web Parts that it dynamically creates and adds to the page. To display all Web Parts to the user at once is overwhelming, so the default is to hide all of the Web Parts that are dynamically added to the page.
Each Administration Web Part adheres to the IAdminRole interface, which identifies the visibility of the Web Part in the panel for the logged in user.
Finally, a link to the Web Part is included in the Admin Panel. A number of points about this:
- The Web Part Title is displayed and the Web Part Description is used as a link mouseover. By using reflection to find the Web Part on the page, the stored value of the Title and Description is retrieved from the Personalization Database to provide the customer with a customized Admin Panel. e.g. if the Customer does not like the Title: Update Page Meta Data, it is easy to change the title to something else which then is displayed in the Admin Panel.
- The LinkButton control is styled using the css classes.
- The Full Name of the Web Part is added to the command argument and is used in the event handler to display this Web Part as the visible Web Part.
private void AddLink(Type t, Control parent)
{
if (!this.mgr.Personalization.CanEnterSharedScope)
return;
WebPart wp = AdminPanelPart.GetWebPart(this.mgr, this.targetZone, t);
if (wp == null)
{
this.AddError("NoWP" + t.Name, "Cannot get Web Part: " + t.FullName);
return;
}
IAdminRole admin = wp as IAdminRole;
if (admin != null)
{ // Check that user is allowed access to the Web Part
if (!admin.IsAdminPanelVisible(admin.AdminRole))
return;
}
LinkButton link = C.AddLinkButton("link" + t.Name, wp.Title, wp.Description, linkCssClass, Unit.Percentage(100), parent);
C.AddStyle(link, linkCssClass, mouseOverCssClass);
C.AddBreak(1, parent);
link.CommandArgument = t.FullName;
link.Command += new CommandEventHandler(link_Command);
}
Code Snippet 6: Code for adding a linkThe GetWebPart method looks through all Web Parts in the page for the requested Web Part. (this means that a single instance of any Web Part is included on the page.) If the requested Web Part is not found, then the Web Part is dynamically created (this will be the subject of another blog article). Finally, properties are set in the Web Part and the found Web Part is returned up the stack.public static WebPart GetWebPart(WebPartManager mgr, string targetZone, Type t)
{
foreach (WebPart wp1 in mgr.WebParts)
{
if (wp1.GetType().FullName != t.FullName)
continue;
return wp1;
}
string error = String.Empty;
WebPart wp = L.CreateWebPart(mgr, t.FullName, targetZone, ref error);
if (wp == null)
throw new Exception("Cannot create: " + t.FullName + " " + error);
ITWebPart iwp = wp as ITWebPart;
if (iwp != null)
iwp.LoginType = LoginType.COR;
return wp;
}
Code Snippet 7: Get the requested Web PartWhen the user clicks a link for a specific Web Part, the code in Code Snippet 8 is invoked. The variable, ‘wpTypes’ (Web Part Types) is a list of strings, i.e. the Full Names of the Web Parts to be displayed to the user. The list is stored in the ViewState, thus keeping the Web Part visible until it is no longer required.void link_Command(object sender, CommandEventArgs e)
{
string wpType = e.CommandArgument.ToString();
this.wpTypes.Clear();
this.wpTypes.Add(wpType);
}
Code Snippet 8: Display the Web PartAdmin Roles Interface
One of the goals of the Admin Panel is to highly customizable for each customer. It is typical that each customer partitions the work done in the organization differently. Therefore, it is important that the tools can be molded to how the customer wants to use them.
Initially, the links were added to the page by instantiating the Web Part type. The problem with this approach is that the default properties are present. It became clear quickly that Reflection was required to get the Web Part on the page and the properties that may have been changed by the customer. The Admin Role interface was a simple way to identify what roles were allowed access to each administration Web Part and hence whether it was visible to the logged in user.
By having each Administration Web Part adhering to the IAdminRole interface, this simplified the code and created consistency across the Admin Panel user interface usage.
public interface IAdminRole
{
/// <summary>
/// A comma-delimited list of Roles that are allowed to use the Web Part.
/// Used in association with Web Part that use the Admin Panel.
/// </summary>
string AdminRole
{
get;
set;
}
/// <summary>
/// Used by the Admin Panels to identify that the Web Part is visible to the user
/// </summary>
/// <param name="roles">A comma-delimited list of Roles</param>
/// <returns>true is the Web Part is visible in the Admin Panel</returns>
bool IsAdminPanelVisible(string roles);
}Code Snippet 9: IAdminRole interface
My Links
The ‘My Links’ feature was simple to add. Just get the links from the Navigation Xml File and add each link as a LinkButton to the Web Part as part of the ‘My Links’ panel. Note that the method Link.Get includes a LoginProfile. This ensures that the links displayed match the roles of the logged in user.
if (this.navLinksFile != String.Empty)
{
XmlDocument doc = C.ReadXmlFile(this.navLinksFile);
List<Link> links = Link.Get(this.Context, this.LoginProfile, doc);
List<Link> myLinks = links.FindAll(l => l.MenuName == this.menuName);
if (myLinks.Count > 0)
{
Panel pnl = AdminPanelPart.AddPanel("MyLinks", "My Links", "Display a set a site favourite links", this);
int i = 0;
foreach (Link link in myLinks)
{
this.AddLink(i, link, pnl);
i++;
}
}
}
Code Snippet 10: Adding My Links to the Admin PanelThe Last Word
As mentioned above, over 30 Web Parts are present on the Admin Panel page. When Editing the page, it is very confusing UNLESS the content of each Web Part is invisible, as shown in Figure 3.
Another comment is that the page with the Admin Panel should deny logged in users edit and catalog access. By doing this, as the user navigates the site, they do not have to manually change from Edit/Catalog to Browse mode for the Admin Panel page.
Figure 3: CMS Admin Panel in Edit Mode
0 comments:
Post a Comment