Click here to Skip to main content
Click here to Skip to main content

Personal .NET Portal

, 16 Oct 2003
Rate this:
Please Sign up or sign in to vote.
A Personal .NET Portal with Forms Authentication and XML Files for storage.

Introduction

This article describes how I build my Personal Portal (in other words: a Homepage). The idea was to develop a Portal that is easy to deploy (e.g. no Database) and easy to use.

These technical problems are solved in this solution

  • Storage The portal doesn't use a database. Instead XML-Files are used. Reading/Writing is done with the XML Serializer and Datasets.
  • Dynamically loading of Web User Controls The portal has Tabs and Modules. Modules are Web User Controls which are loaded at runtime. The definition is stored in a XML File.
  • Authentication Forms Authentication is used. Users and Roles are stored in a XML File.
  • HttpHandler A HttpHandler is used to access Tab-Pages.

Background

First I wanted to use the IBuySpy Portal. But it isn't (or wasn't) free and uses a database. After some searching at Codeproject and Sourceforge I decided to implement my own Portal.

This Project is hosted at Sourceforge. I would be happy about feedback and suggestions . Also new modules or improvements are welcome. A Demo Portal can be found here .

Using the code

Feel free to use the Portal under the GPL License or code snippets freely.

Summary

A Portal has Tabs. Each Tab can have Sub-Tabs and Modules. Modules are providing content. A Module can be placed right, in the middle or on the left side on a Tab.

A Module consists of a View Web User Control and a Edit Web User Control. The edit control is optional. Both controls are derived from the Module/EditModule class. Those classes are providing information (Reference, Name, etc.) and a configuration infrastructure. Modules are located in the Modules/<ModuleName> directory.

Tabs and Modules have Roles assigned. Users in those roles may view/edit Tabs/Modules. There are four built-in roles:

  • Admin - Administrator Role. May edit/view everything
  • User - Signed in User
  • Everyone - Signed in User or Anonymous User
  • Anonymous - Not signed in User

The Portal definition and Users are stored in a XML File.

Currently there is a "Frame" and a "Table" version (configured in the web.config file). This is how the Portal is rendered: In Frames or in a Table.

Classes

Class Name Description
Definitions
PortalDefinition This is the main Portal Definition class. It contains various helper functions and a list of Tabs.
PortalDefinition.Tab The Tab Definition. A Tab has a Name, Reference, Roles, Sub Tabs and left/middle/right Modules.
PortalDefinition.Module A Module has a Name, Reference and Roles.
PortalDefinition.Role A Role is just a Name and Base Class for View or Edit Roles
PortalDefinition.EditRole Edit Role.
PortalDefinition.ViewRole View Role.
Users User/Roles Dataset.
ModuleSettings Defines Module Settings like the Module Control Name.
Controls/Rendering
EditLink Renders the Edit Link.
ModuleFailed Renders a "Module failed to load".
ModuleHeader Renders the Module Header.
OverlayMenu Renders a Menu.
OverlayMenuItem Renders a Menu Item.
PortalTab This is the main Protal Render Control!
TabMenu Renders the Tab Menu.
TabPath Renders the current Tab Path.
TabHttpHandler The HttpModule for "translating" URLs.
Helper
Helper Common Helper Class.
UserManagement Usermanagement Methods.
API
Config Configuration Helpers.
EditModule Base Class for Module Edit Controls.
Module Base Class for Module View Controls.

ASPX-Pages are used as container for Web User Controls. They have hardly program logic.

Storage

Two things must be stored: the Portal Definition and Users/Roles.

The Portal definition is stored with the XML-Serializer.

[XmlRoot("portal"), Serializable]
public class PortalDefinition
{
  // Static Serializer
  private static XmlSerializer xmlPortalDef = 
     new XmlSerializer(typeof(PortalDefinition));
  ...
  // Loads the Portal Definition
  public static PortalDefinition Load()
  {
    // Create a Text Reader which reads the file
    XmlTextReader xmlReader = new XmlTextReader(
            Config.GetPortalDefinitionPhysicalPath());
    // Deserialize
    PortalDefinition pd = (PortalDefinition)xmlPortalDef.Deserialize(
      xmlReader);
    // Close the file
    xmlReader.Close();

    return pd;
  }
  // Saves the Portal Definition
  public void Save()
  {
    // Create a Text Writer which writes the file
    XmlTextWriter xmlWriter = new XmlTextWriter(
          Config.GetPortalDefinitionPhysicalPath(), System.Text.Encoding.UTF8);
    // Set Formating
    xmlWriter.Formatting = Formatting.Indented;
    // Serialize
    xmlPortalDef.Serialize(xmlWriter, this);
    // Close the file
    xmlWriter.Close();
  }
  ...
}

Users are stored in a Dataset

public class UserManagement
{
  ...
  // Reads the User Dataset from a file
  public static Users GetUsers()
  {
    Users u = new Users();
    u.ReadXml(Config.GetUserListPhysicalPath());
    return u;
  }
  
  // Writes the User Dataset to a file
  public static void SetUsers(Users u)
  {
    u.WriteXml(Config.GetUserListPhysicalPath());
  }
  ...
}

Authentication

Forms Authentication is used, but I can imagine that Windows Authentication would also work.

First, in the Login Module, your credentials are validated. This is done through a helper method.

Login.ascx

void OnLogin(object sender, EventArgs args)
{
  if(Portal.UserManagement.Login(account.Text, password.Text))
  {
    Response.Redirect(Request.RawUrl);
  }
  else
  {
    lError.Text = "Invalid Login";
  }
}
UserManagement.cs
  
public static bool Login(string account, string password)
{
  // Load Dataset
  Users u = GetUsers();

  // Find user
  Users.UserRow user = u.User.FindBylogin(account.ToLower());
  if(user == null) return false;
  
  // Check password
  if(user.password != password) return false;

  // Set Authentication Cookie
  FormsAuthentication.SetAuthCookie(account, false);
  return true;
}  

When a Http-Request occurs, Global.Application_AuthenticateRequest is called. There the user roles are set.

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
  if(Request.IsAuthenticated)
  {
    // Read users roles
    string[] roles = UserManagement.GetRoles(
            HttpContext.Current.User.Identity.Name);
    // Set a new Principal with the proper roles
    HttpContext.Current.User = new GenericPrincipal(
      HttpContext.Current.User.Identity, roles);
  }
}
UserManagement.cs
  
public static string[] GetRoles(string account)
{
  // Load the User Dataset
  Users u = GetUsers();

  // Find the current user
  Users.UserRow user = u.User.FindBylogin(account.ToLower());
  if(user == null) return new string[0];

  // Read users roles and add to a string array
  Users.UserRoleRow[] roles = user.GetUserRoleRows();
  string[] result = new string[roles.Length];  
  for(int i=0;i<roles.Length;i++)
  {
    result[i] = roles[i].RoleRow.name;
  }

  return result;
}

Loading Controls Dynamically

The PortalTab.ascx Web User Control renders the Tabs. The current Tab reference is passed as a URL Parameter. Modules are loaded in the OnInit Event so they can process their OnLoad Events.

public abstract class PortalTab : System.Web.UI.UserControl
{
  // Table Cells where the Modules are rendered
  protected HtmlTableCell left;
  protected HtmlTableCell middle;
  protected HtmlTableCell right;

  // Renders a Module Column
  private void RenderModules(HtmlTableCell td, PortalDefinition.Tab tab,
           ArrayList modules)
  {
    // Hide the cell if there where no Modules
    if(modules.Count == 0)
    {
      td.Visible = false;
      return;
    }
    foreach(PortalDefinition.Module md in modules)
    {
      // Only if the User has view rights
      if(UserManagement.HasViewRights(Page.User, md.roles))
      {
        md.LoadModuleSettings();

        // Initialize the Module
        Module m = null;

        // Load the Module
        if(md.moduleSettings == null)
        {
          m = (Module)LoadControl(Config.GetModuleVirtualPath(md.type) 
                  + md.type + ".ascx");
        }
        else
        {
          m = (Module)LoadControl(Config.GetModuleVirtualPath(md.type) 
                  + md.moduleSettings.ctrl);
        }
        // Initialize the Module
        m.InitModule(tab.reference, md.reference, 
          Config.GetModuleVirtualPath(md.type), 
          UserManagement.HasEditRights(Page.User, md.roles));
          
        // Each Module can decide if it wants to be rendered. 
        // The Login Module does so.
        if(m.IsVisible())
        {
          // Add Module Header
          ModuleHeader mh = (ModuleHeader)LoadControl("ModuleHeader.ascx");
          mh.SetModuleConfig(md);
          td.Controls.Add(mh);

          // Add Module
          HtmlGenericControl div = new HtmlGenericControl("div");
          div.Attributes.Add("class", "Module");
          div.Controls.Add(m);
          td.Controls.Add(div);
        }
      }
    }
  }

  // Called by the ASP.NET Framework
  override protected void OnInit(EventArgs e)
  {
    PortalDefinition.Tab tab = PortalDefinition.GetCurrentTab();

    // Check user rights
    if(UserManagement.HasViewRights(Page.User, tab.roles))
    {
      // Render
      RenderModules(left, tab, tab.left);
      RenderModules(middle, tab, tab.middle);
      RenderModules(right, tab, tab.right);
    }

    // CODEGEN: This call is required by the ASP.NET Web Form Designer.
    InitializeComponent();
    base.OnInit(e);
  }
  ...
}

HttpHandler

The HttpHandler is used to "translate" URLs from "http://server/Portal/main.tab.ascx" to "http://server/Portal/RenderTable.aspx?TabRef=main". This is interesting if you want to analyze your Web Server Log Files.

To get this work you must add this to your web.config file

<system.web>
  <httpHandlers>
    <add verb="*" path="*.tab.aspx" type="Portal.TabHttpHandler, Portal" />
  <httpHandlers>
<system.web>

".tab.aspx" is used as a extension, because otherwise you have to reconfigure your IIS.

This HttpModule does nothing else than a simple Server.Transfer

public class TabHttpHandler : IHttpHandler, IRequiresSessionState
{
  public void ProcessRequest(HttpContext context) 
  {
    // Parse the URL and extract the Tab Reference
    string path = context.Request.Url.AbsolutePath.ToLower();
    string tabRef = path.Substring(path.LastIndexOf("/") + 1); 
          // get "TabRef.tab"
    tabRef = tabRef.Substring(0, tabRef.LastIndexOf(".tab.aspx")); 
          // get "TabRef"

    // Save URL Parameter
    Hashtable r = new Hashtable();
    foreach(string key in context.Request.QueryString.Keys)
    {
      r[key] = context.Request[key];
    }

    r["TabRef"] = tabRef;
  
    // Read the configuration to determinate the current main page
    string url = Portal.API.Config.GetMainPage();
    
    // Build the URL
    bool firstParam = true;
    foreach(DictionaryEntry e in r)
    {
      if(firstParam)
      {
        url += "?";
        firstParam = false;
      }
      else
      {
        url += "&";
      }
      url += e.Key.ToString() + "=" + e.Value.ToString();
    }
    
    // Redirect
    context.Server.Transfer(url);
  }
  
  public bool IsReusable
  {
    get
    {
      return true;
    }
  }
}

Creating a Module

Creating a Module is simple. Just create a directory in the Modules directory and put there the View- and Edit Web User Control. The Controls names must be (or can be reconfigured) <ModuleName>.ascx and Edit<ModuleName>.ascx.

Implementation

Just derive form Portal.API.Module or Portal.API.EditModule and implement "IsVisible" if necessary.

The Module Class provides some properties and methods which describes the Module.

Current Module Settings

Name Description
IsVisible Can be overridden. Tells the Portal Framework if the Module should be rendered or not.
TabRef The current Tab Reference. This is a unique string.
ModuleRef The current Module Reference. The Module Reference is not necessarily unique. TabRef + ModuleRef is unique.
ModuleVirtualPath The virtual path to the Module.
ModulePhysicalPath The physical path to the Module.
BuildURL Build a URL to the current Page. Use this method to implement Modules that needs URL Parameter.
ModuleHasEditRights True if the current user has edit rights.

Configuration

Each Module has the responsibility to store its configuration and state. The Portal API provides some Helper Methods.

Name Description
ModuleConfigFile Physical Path to the configuration file. (<ModulePhysicalPath>\Module_<ModuleRef>.config)
ModuleConfigSchemaFile Physical Path to the configuration schema file. (<ModulePhysicalPath>\ Module_<ModuleRef>.configModule.xsd)
ReadCommonConfig Reads (XML Deserialize) the common configuration file.
ReadConfig Reads (XML Deserialize/Dataset) the configuration file.
WriteConfig Writes (XML Serialize/Dataset) the configuration file.

Furthermore each Module can a configure its control files. These settings are stored in the "ModuleSettings.config" file.

<module>
  <ctrl>Counter.ascx</ctrl>
  <editCtrl>none</editCtrl>
</module>

The "ctrl" Tag defines the View Web User Control. The "editCtrl" Tag can contain "none", which means: there is no Edit Control. E.g. the Login or HitCounter Modules are using this.

Example (Simple Html Module)

This simple Module reads a .htm file and renders it into a DIV Tag.

View Control Html.ascx:

<%@ Control Language="c#" Inherits="Portal.API.Module" %>
<%@ Import namespace="System.IO" %>
<script runat="server">
    private string GetPath()
    {
        return ModulePhysicalPath + ModuleRef + ".htm";
    }

    void Page_Load(object sender, EventArgs args)
    {
        // Open file            
        if(File.Exists(GetPath()))
        {
            FileStream fs = File.OpenRead(GetPath());
            StreamReader sr = new StreamReader(fs);
            content.InnerHtml = sr.ReadToEnd();
            fs.Close();
        }        
    }

</script>
<div id="content" runat="server">
</div>

Edit Control EditHtml.ascx:

<%@ Control Language="c#" autoeventwireup="true" 
    Inherits="Portal.API.EditModule" %>
<%@ Import namespace="System.IO" %>
<script runat="server">

    private string GetPath()
    {
        return ModulePhysicalPath + ModuleRef + ".htm";
    }

    void Page_Load(object sender, EventArgs args)
    {
        if(!IsPostBack)
        {
            // Open file            
            if(File.Exists(GetPath()))
            {
                FileStream fs = File.OpenRead(GetPath());
                StreamReader sr = new StreamReader(fs);
                txt.Text = sr.ReadToEnd();
                fs.Close();
            }
        }
    }
    
    void OnSave(object sender, EventArgs args)
    {
        FileStream fs = null;
        try
        {
            fs = new FileStream(GetPath(), FileMode.OpenOrCreate, 
                 FileAccess.ReadWrite, FileShare.None);
            fs.SetLength(0); // Truncate
            StreamWriter sw = new StreamWriter(fs);
            sw.Write(txt.Text);
            sw.Close();
            
        }
        finally
        {
            if(fs != null)
            {
                fs.Close();
            }
        }
        RedirectBack();
    }

</script>
<asp:TextBox id="txt" Width="100%" Height="300px" 
    TextMode="MultiLine" Runat="server"></asp:TextBox>
<asp:LinkButton CssClass="LinkButton" runat="server" OnClick="OnSave">
    Save & Back</asp:LinkButton>

Work from others

Not all work is from me. I took some code from others and made Modules. Thanks!

History

  • 17.10.2003 Version 1.0.2 uploaded. TreeWebControlPrj is now in the zip file.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Arthur Zaczek
Founder dasz.at OG
Austria Austria
No Biography provided

Comments and Discussions

 
GeneralMy vote of 4 Pinmemberchandru20117-Mar-12 0:03 
Generalgreat app PinmemberImade11-May-11 6:30 
General.net2.0 error Pinmemberlongren62927-May-07 23:02 
QuestionVB.Net version? PinmemberHACKERGY4-Sep-05 2:14 
AnswerRe: VB.Net version? PinsussAnonymous4-Sep-05 5:56 
AnswerRe: VB.Net version? PinmemberMaruf Maniruzzaman23-Sep-07 0:43 
GeneralError message PinmemberRahman Mahmoodi26-Aug-05 4:30 
GeneralPortal Language Pinmemberrafael35512-Aug-05 8:55 
GeneralRe: Portal Language PinsussAnonymous14-Aug-05 1:18 
GeneralRe: Portal Language Pinmemberrafael35515-Aug-05 7:21 
GeneralGreat job Pinmemberpaulsoren29-Jun-05 11:12 
Generallogin fails on redirected site Pinsussanonymous15-Jun-05 11:42 
GeneralTabs don't wrap PinmemberChristopher Scholten13-Jun-05 17:12 
QuestionMSI Installation Package? Pinmembervenkriss13-Apr-05 4:12 
AnswerRe: MSI Installation Package? PinsussAnonymous14-Apr-05 10:07 
QuestionImageBrowser ? Pinmemberdbui23-Mar-05 12:41 
AnswerRe: ImageBrowser ? Pinmemberdbui24-Mar-05 8:24 
GeneralSub Tab don't display PinmemberFabio97217-Mar-05 22:09 
GeneralRe: Sub Tab don't display Pinmemberarthur zaczek18-Mar-05 1:12 
GeneralExcellent !!! PinmemberFabio97217-Mar-05 10:51 
GeneralProblem with &quot;MainPage.tab.aspx&quot; PinsussAnonymous1-Mar-05 15:38 
GeneralRe: Problem with &quot;MainPage.tab.aspx&quot; PinsussAnonymous1-Mar-05 15:50 
GeneralLogin problem Pinmembermitmil6-Feb-05 12:34 
GeneralRe: Login problem Pinmembermitmil10-Feb-05 9:50 
GeneralFreeTextBox Pinmemberapagnier19-Dec-04 4:21 
GeneralRe: FreeTextBox Pinmemberarthur zaczek19-Dec-04 6:15 
GeneralParser Error Message: Type 'ASP.OverlayMenu_ascx' does not have a property named 'MenuItem'. Pinmemberfrankie_nova@yahoo.com25-Aug-04 21:56 
QuestionHow I can have tabs in the main page? Pinmembermercedites1-Jun-04 0:06 
Generalproblem with admin tabor and admin portal Pinmembermercedites27-May-04 0:53 
GeneralRe: problem with admin tabor and admin portal Pinmemberarthur zaczek27-May-04 1:18 
GeneralRe: problem with admin tabor and admin portal Pinmemberarthur zaczek28-May-04 4:25 
GeneralCan't log in first time PinmemberBorey20-May-04 17:57 
GeneralRe: Can't log in first time Pinmemberarthur zaczek27-May-04 1:15 
GeneralRe: Can't log in first time PinmemberBorey27-May-04 11:36 
GeneralRe: Can't log in first time PinsussAnonymous2-Jun-04 5:15 
QuestionWhere can I find in Inet real WAP site written with ASP.NET? Pinmembersoniko16-Apr-04 1:31 
GeneralSystem.ArgumentNullException: Value cannot be null. Parameter name: value (Why) Pinmemberzhang.bin6-Apr-04 15:40 
GeneralRe: System.ArgumentNullException: Value cannot be null. Parameter name: value (Why) Pinmemberarthur zaczek16-Apr-04 1:50 
Generalsir..how do u get the reference PinsussAnonymous20-Mar-04 3:03 
GeneralRe: sir..how do u get the reference Pinmemberarthur zaczek21-Mar-04 0:17 
GeneralThe front page is displayed..then error Pinsussjonathfernando16-Mar-04 2:48 
GeneralRe: The front page is displayed..then error Pinmemberarthur zaczek16-Mar-04 4:06 
GeneralRe: The front page is displayed..then error PinsussAnonymous17-Mar-04 5:47 
GeneralRe: The front page is displayed..then error Pinmemberarthur zaczek19-Mar-04 0:47 
Generaltabs are only visible to the admin PinsussAnonymous20-Mar-04 2:43 
QuestionFourm - DB problems? Pinmemberneggs9-Mar-04 22:55 
AnswerRe: Fourm - DB problems? Pinmemberarthur zaczek10-Mar-04 3:19 
GeneralConnection String Problem Pinmemberjasontran8-Mar-04 8:32 
GeneralRe: Connection String Problem Pinmemberarthur zaczek8-Mar-04 22:22 
GeneralRe: Connection String Problem Pinmemberjasontran9-Mar-04 4:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140821.2 | Last Updated 17 Oct 2003
Article Copyright 2003 by Arthur Zaczek
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid