Click here to Skip to main content
Email Password   helpLost your password?

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

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:

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

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
General.net2.0 error
longren629
0:02 28 May '07  

QuestionVB.Net version?
HACKERGY
3:14 4 Sep '05  
Would you consider making a VB.Net cersion of it please?

Thanks.
AnswerRe: VB.Net version?
Anonymous
6:56 4 Sep '05  
Hi!

Nope - I personally don't like VB. And BTW: It's nearly the same work so it's not woth.

CU, Arthur
AnswerRe: VB.Net version?
Maruf Maniruzzaman
1:43 23 Sep '07  
Compile to produce binary and use Reflector tool to decompile in VB .NET.

Maruf Maniruzzaman
Dhaka, Bangladesh.
Blog you should not miss
Tomorrow is a blank page

GeneralError message
Rahman Mahmoodi
5:30 26 Aug '05  
hello,

it raises error for me ....

it complains about portal module that it can't get it from the web server?
And i can't find something like portal in those folder either.

any hint?

rahman
GeneralPortal Language
rafael355
9:55 12 Aug '05  
Hello:

I'm new to ASP.Net and I'm using your Portal as a tool to learn and practice ASP.Net.
I downloaded the Portal and I managed to install it. However, most of the language is in German.
Can anyone please tell me where can I change it to English?
Thank you!
GeneralRe: Portal Language
Anonymous
2:18 14 Aug '05  
Hi!

Just change the globalization Settings (culture and uiCulture) in your web.config file.
Do not forget to use the lastes version from sf.net.

CU, Arthur
GeneralRe: Portal Language
rafael355
8:21 15 Aug '05  
Thank you very much!
GeneralGreat job
paulsoren
12:12 29 Jun '05  
I'm digging my way through the code and just wanted you to know that I thing you've done a great job.Smile
Generallogin fails on redirected site
anonymous
12:42 15 Jun '05  
perhaps I missed an episode, but :
* when I login accessing the site by it's true name, it's all gine, it works
* as my site can be accessed by some aliases(dynamic DNS names), accessing the site by such an alias, the portal displays OK but any login fails...
Why ?
As a coincidence, in DotNetNuke portal, the "portals" table contains all the aliases allowed to access the site.?!?.. Please fellows, give me a hand..
GeneralTabs don't wrap
Christopher Scholten
18:12 13 Jun '05  
The tabs don't wrap when there are too many tabs to be displayed on one line. This is a problem when tabs can be dynamically added (ie. if there is only a fixed number of tabs, then this issue need not be addressed).
GeneralMSI Installation Package?
venkriss
5:12 13 Apr '05  
First of All, This is a very nice project for hosting our home pages on free hosting server. Very well done.
I am just wondering if it is possible for you to provide an msi installation package to make the installation process more easier.

Thanks,
venkriss
GeneralRe: MSI Installation Package?
Anonymous
11:07 14 Apr '05  
Hi!

Sure it's possible, but when you use a free hosting server you wouldn't be allowed to install MSI Packages.

With the newest Version (http://sourceforge.net/projects/dotnetportal/) installation is very easy - just copy the Portal into a IIS-Application (or with Mono an Apachedirectory) an start the Application. All install procedures are done at the first startup.

CU, Arthur
GeneralImageBrowser ?
dbui
13:41 23 Mar '05  
Thanks for the share and great job.

Does anyone have problem with image browsing control? Somehow I could not get it display the image folder in the config file.

I tried to create the photos dir at different location in wwwroot, but still got no luck.

I got the latest verion (v1.1 as of 3/21/05).

Please help.

thanks
GeneralRe: ImageBrowser ?
dbui
9:24 24 Mar '05  
Just found out the solution.

Need to have config file corresponding to the ref id.



"Things are difficult b4 it's easy!"
GeneralSub Tab don't display
Fabio972
23:09 17 Mar '05  
I have add some "subs tabs" but they don't appears Confused
I have set the view role to everyone, but I can't see them.

Does "sub tab" are working ?

Realy Great First Portal DB-Less. Is there a link to an open source web page for it ?
Do you still work on it ?
GeneralRe: Sub Tab don't display
arthur zaczek
2:12 18 Mar '05  
You can find the lates Version at http://sourceforge.net/projects/dotnetportal

With Subtab you have two options:

1) Modify the web.config Key "" - then you'll see the SubTabs in the Menu.
2) Add a TabList or TabTree Module on the Parent Tab.

Yes, work is still in progress. Actually we are extending the Usermanagement.

CU, Arthur
GeneralExcellent !!!
Fabio972
11:51 17 Mar '05  
This is awesome WTF .

I was looking for a light portal like DotNetNuke. The 30Mo of this very good job is too much for my hosting Wink

I can say now that with only 1,2Mo, PortalSrc is really a good option.
It works the first time for me. The way to admin tabs is like DNN. Just miss the preview mode Wink


A really cool job. Congratulations and thank you. Cool

^_^
GeneralProblem with "MainPage.tab.aspx"
Anonymous
16:38 1 Mar '05  
Hi, when I click the tabs "Home":

Server Error in '/IPortal' Application.
--------------------------------------------------------------------------------

Category does not exist.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.InvalidOperationException: Category does not exist.

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:


[InvalidOperationException: Category does not exist.]
System.Diagnostics.PerformanceCounterLib.CounterExists(String machine, String category, String counter) +285
System.Diagnostics.PerformanceCounter.Initialize() +235
System.Diagnostics.PerformanceCounter..ctor(String categoryName, String counterName, String instanceName, Boolean readOnly) +127
System.Diagnostics.PerformanceCounter..ctor(String categoryName, String counterName, Boolean readOnly) +21
System.Diagnostics.PerformanceCounter..ctor(String categoryName, String counterName) +11
Portal.Modules.ServerInformation.ServerInformation.GetUpTime() +53
Portal.Modules.ServerInformation.ServerInformation.GetServerInfo() +190
Portal.Modules.ServerInformation.ServerInformation.Page_Load(Object sender, EventArgs e) +85
System.Web.UI.Control.OnLoad(EventArgs e) +67
System.Web.UI.Control.LoadRecursive() +35
System.Web.UI.Control.LoadRecursive() +98
System.Web.UI.Control.AddedControl(Control control, Int32 index) +307
System.Web.UI.ControlCollection.Add(Control child) +153
Portal.PortalTab.RenderModules(HtmlTableCell td, Tab tab, ArrayList modules) +553

[Exception: Category does not exist.]
Portal.PortalTab.RenderModules(HtmlTableCell td, Tab tab, ArrayList modules) +612
Portal.PortalTab.CreateChildControls() +80
System.Web.UI.Control.EnsureChildControls() +100
System.Web.UI.Control.PreRenderRecursiveInternal() +38
System.Web.UI.Control.PreRenderRecursiveInternal() +125
System.Web.UI.Control.PreRenderRecursiveInternal() +125
System.Web.UI.Page.ProcessRequestMain() +1499




--------------------------------------------------------------------------------
Version Information: Microsoft .NET Framework Version:1.1.4322.2032; ASP.NET Version:1.1.4322.2032
GeneralRe: Problem with "MainPage.tab.aspx"
Anonymous
16:50 1 Mar '05  
Got it, it is the Module ServerInformation' s problem
GeneralLogin problem
mitmil
13:34 6 Feb '05  
I can login and see control tabs in my computer with IIS, but after installation in aihs.net server i can see only login tab. If i enter login and password - nothing happen.Frown I see page with login. If i enter insted of admin - user i see "Invalid login".
site www.pc-robot.de
user Admin
password admin
GeneralRe: Login problem
mitmil
10:50 10 Feb '05  
Problems were because cookies are not enabled.
I am sorry...
GeneralFreeTextBox
apagnier
5:21 19 Dec '04  
Hello,

When I run default.aspx, there's some problem to locate FreeTextBox assembly. I provide the 2.0.7 version of this one, it's located now, but it seems there's a problem with this version of the assembly.
Which version of FreeTextBox to run Personal Portal ?

Thx

Arnaud
GeneralRe: FreeTextBox
arthur zaczek
7:15 19 Dec '04  
Hi!

There is no FreeTextBox Assembly!!

There are no external Assemblies needed, all Assebmlies that are used are included in the Project.

CU, Arthur
GeneralParser Error Message: Type 'ASP.OverlayMenu_ascx' does not have a property named 'MenuItem'.
frankie_nova@yahoo.com
22:56 25 Aug '04  
Hi Gurus:

I installed and try to run the portal program but I however encountered the mentioned error? Can anyone give me some guidance how I can resolve the problem?

Thanks

Error Message displayed in the web browser as below
=======================================================Cry

Server Error in '/Frankie' Application.
--------------------------------------------------------------------------------

Parser Error
Description: An error occurred during the parsing of a resource required to service this request. Please review the following specific parse error details and modify your source file appropriately.

Parser Error Message: Type 'ASP.OverlayMenu_ascx' does not have a property named 'MenuItem'.

Source Error:


Line 15:
Line 16: <ucm:OVM id="ovm" runat="server" RootText="Edit Tab"> Line 17: Line 18: Line 19:

Source File: c:\inetpub\wwwroot\Frankie\PortalTab.ascx Line: 17


--------------------------------------------------------------------------------
Version


Last Updated 17 Oct 2003 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010