Attached is the presentation and the code used in the ODNC presentation on June 6, 2011.
Visual Studio Code and presentation.
Intro To Rx Extensions Presentation
Attached is the presentation and the code used in the ODNC presentation on June 6, 2011.
Visual Studio Code and presentation.
Intro To Rx Extensions Presentation
I am looking forward to Ottawa IT Camp (previously called Code Camp) 2011 scheduled for Saturday, April 16, 2011.
IT Camp is a one-day training event for developers, database administrators and IT people.
We will be there to talk about some topic in Jetfire.
Today I presented this topic at the Ottawa Dot Net Community. Good turn-out with about 20 people. This was a one-hour lunch-time presentation.
The agenda covered:
The overview is a series of slides in the PowerPoint presentation providing a quick summary of Web Parts from Personalization tables to Zones for browsing, editing and catalogs.
The mini-spec is a one page summary of the key points for the Admin Panel (see slide 14).
Finally the last 35 minutes looked at the key design issues and how to solve them in code:
In designing an Admin Panel, I am adamant that labels on the user interface be totally customizable. This means that the labels not only map to the Web Part properties, such as Title, but also track personalization changes that a customer makes to a Web Part.
Instantiation creates objects where the properties have default values. Reflection tries to find the Web Part on the page and use the values saved in the Personalization database.
When there over three Web Parts on an Admin Page, it is important to hide the Web Parts that are not in use. I call this a “Point, click, Use” interface. Point the mouse at the link for the web part that you want to use. Click the link. Use the Web Part that appears on the screen. There should only be one Admin Web Part visible on the page.
Finally the panel is initialized when we use reflection to create Web Parts. This means that the first time that the page is displayed, all Web Parts are automatically added to the page. We are ready to customize them.
Slides and sample Demo Admin Panel Web Part code are included.
Contents include:
This new version of the dynamic programming language Jetfire (available for download on Codeplex) provides a number of new features and improvements. To provide these new features and improvements a new expression parser has been written to employ Linq expression trees. Version 1.3 contains both the new expression parser and the original expression interpreter. This allows the original expression interpreter to be used by simply changing a setting should any problems be encountered.
Version 1.3.x will provide the following:
As a designer, I constantly struggle with the time required for designing great software programs and writing user documentation that helps people understand how to use the program. [Some people argue that if it’s a great program, it doesn’t need documentation, but I know that even the brightest users often need a push in the right direction for how to get the most out of the product.]
I am particularly interested in auto-generating documentation for Web Part Properties. The CMS Admin Panel got me thinking about this problem. The Admin Panel needed an Admin Help Index – a list of the Web Parts in the Admin Panel, who can access them and a short summary of what they do. Since this information is readily available in the Web Part itself, I decided to auto-generate this documentation.
I auto-generated the Admin Help Index in a Web Part using the following algorithm:
// Order the Web Parts in alphabetical orderSome notes about the algorithm used in Code Snippet 1:
WebPart[] webParts = new WebPart[mgr.WebParts.Count];
mgr.WebParts.CopyTo(webParts, 0);
IEnumerable<WebPart> parts = webParts.OrderBy(x => x.DisplayTitle);
foreach (WebPart wp in parts)
{
IAdminRole admin = wp as IAdminRole;
if (admin == null)
continue;
C.AddBreak(2, this);
C.AddLabel(String.Empty, "Title: " + C.AddStyle2H3(wp.DisplayTitle), Unit.Empty, this);
C.AddBreak(1, this);
if (admin.AdminRole != String.Empty)
{
C.AddLabel(String.Empty, "* Roles required to access: " + admin.AdminRole, Unit.Empty, this);
C.AddBreak(1, this);
}
C.AddLiteral("* Description: " + wp.Description, this);
}
Code Snippet 1: Generate an Admin Help Index
/// <summary>
/// Gets and Sets the format that the documentation is output: wiki, text, html
/// </summary>
[Category("Display"), Browsable(true), Personalizable(PersonalizationScope.Shared),
WebDisplayName("Output Type"), DisplayName("Output Type"),
Description("Describes the format that the documentation is output: wiki, text, html.")]
public OutputType OutputType
{
get { return this.op; }
set { this.op = value; }
}
string wpType = this.Context.Request.QueryString.Get("name");Code Snippet 3: Create the Web Part
if (wpType == null)
this.OutputText("No request received");
Type type = System.Web.Compilation.BuildManager.GetType(wpType, false);
if (type == null)
this.OutputText("Cannot find " + wpType);
WebPart wp = Activator.CreateInstance(type) as WebPart;
if (wp == null)
this.OutputText(wpType + " is not a Web Part");
With a text version of the type present, we can use reflection to get the Class and then create an instance of the class. If the type is not a Web Part class, then the user receives no output ("Cannot find " + wpType).
sb.AppendFormat("{0}{1}{2}{3}", this.h4s, wp.Title, this.h4e, this.br);
sb.AppendFormat("{0}Description{1}: {2}{3}", this.bs, this.be, wp.Description, this.br);
sb.AppendFormat("{0}Properties{1}{2}", this.h5s, this.h5e, String.Empty);
sb.AppendFormat("Properties are listed alphabetically{0}", this.br);
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(wp);
PropertyDescriptor[] props = new PropertyDescriptor[properties.Count];
properties.CopyTo(props, 0);
IEnumerable<PropertyDescriptor> pds = props.Where<PropertyDescriptor>(x => x.Category == "Display")
.OrderBy(x => x.DisplayName);
// Sets an PropertyDescriptor to the specific property.Code Snippet 4 shows the steps involved in auto-generating the documentation for the Web Part. It looks unnecessarily complicated with the formatting options defined by the instance variables. Let’s dissect the first statement: sb.AppendFormat("{0}{1}{2}{3}", this.h4s, wp.Title, this.h4e, this.br);
foreach (PropertyDescriptor p in pds)
{
switch (p.Name)
{
case "Title":
case "Description":
continue;
}
sb.AppendFormat("{0} {1}{2}{3}: {4}{5}", this.star, this.bs, p.DisplayName, this.be, p.Description, this.br);
object o = p.GetValue(wp);
if (o == null)
continue;
string value = o.ToString();
if (value != String.Empty)
sb.AppendFormat("{0}{1} Default Value: {2}{3}", this.tab, this.star, value, this.br);
}
Code Snippet 4: Auto-Generate the Property list for a Web Part
IEnumerable<PropertyDescriptor> pds = props.Where<PropertyDescriptor>(x => x.Category == "Display")
.OrderBy(x => x.DisplayName);Copy the properties into an array and then using System.Linq extensions, we find the Display Category properties and order them by DisplayName.private void OutputText(string text)
{
if (this.outputType == OutputType.Html)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
string styleSheet = String.Format("http://{0}{1}/app_themes/{2}/stylesheet.css",
context.Request.Url.Authority, context.Request.ApplicationPath, "default");
sb.AppendFormat(@"<html><head><link href=""{0}"" type=""text/css""
rel=""Stylesheet""/></head><body>", styleSheet);
sb.Append("</body></html>");
// Write the content to the Web Page
context.Response.ContentType = "text/HTML";
context.Response.Write(sb.ToString());
context.Response.End();
return;
}
// Display the Documentation as a text file (applies to txt and wiki options)
this.Context.Response.ContentType = "text/plain";
this.Context.Response.Write(text);
this.Context.Response.End();
}
Code Snippet 5: Output the text
Making an impact on web pages is about keeping the information concise and attractive. Most home pages use a scroller for news and/or events.
The Jetfire News Scroller makes it easy to add news to your home page. It includes:
Jetfire Persistent Scripting Language is an open source, object oriented, scripting language designed to make persistent programs, sometimes called workflows, very easy to write and maintain. The News Item displayed in the scroller is a simple program, which consists of:
namespace JetfireApps
{
// This workflow creates News Items with an expiry date.
// Use 'IsCurrent' to check the Expiry Date and identify if the News Item is current.
// state Active means that the News Item is current.
public workflow NewsItem
{
// Add workflow constructor
public NewsItem()
{
Tags = new List();
enterstate Start();
Created_Date = DateTime.Now;
Created_By = User.Login.Name;
Expiry_Date = DateTime.Today.AddYears(1);
}
// Add states to the workflow
public Start() { }
public Active() { }
public Inactive() { }
// Add Properties
// Use Subject as News Title
// Use ToolTip as News Description
// Add Expiry Date
public bool Never_Expires
{
get;
set;
}
public DateTime Created_Date
{
get;
private set;
}
public plainString Created_By
{
get;
private set;
}
public DateTime LastUpdated_Date
{
get;
private set;
}
public plainString LastUpdated_By
{
get;
private set;
}
public DateTime Expiry_Date
{
get;
set;
}
public List Tags
{
get;
private set;
}
public bool IsCurrent
{
get
{
if (Never_Expires)
{
return true;
}
if (DateTime.Now.Preceeds(Expiry_Date))
{
return true;
}
// This news item is now expired
enterstate Inactive();
return false;
}
}
// Add Command Methods
public void FinishEdit() : states(Start,Active)
{
if (Subject == "")
{
throw exception("Enter Title");
}
if (ToolTip == "")
{
throw exception("Enter Description");
}
LastUpdated_Date = DateTime.Now;
LastUpdated_By = User.Login.Name;
enterstate Active();
}
public void Set_Inactive() : states(Active)
{
enterstate Inactive();
}
public void Set_Active() : states(Inactive)
{
enterstate Active();
}
}
}Someone has to create the news that is displayed on the website. Figure 1 shows a portion of a Jetfire page that we use to create our news. There are two Web Parts displayed on the figure.
The Body of the News Item is a Rich Text Editor that allows the user to input rich text, images and links.
Figure 1: News Editor in web page
Figure 2: News Scroller in web page
Figure 3: A subset of properties used to configure the News Scroller (below)
Figure 2 shows the News Scroller in the home page of http://www.jetfire.ca. The Web Part is configured to display a new item every 400 milli-seconds. The user may also choose to manually select a News Item.
The News Item program can be configured in different ways by a Web Master for display on the web page. Options include:
Figure 3 shows a subset of the edit properties for the News Scroller. The Web Master has a number of options that describe how the News Scroller will operate.
jQuery is a fast and concise JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development. We use this as our AJAX JavaScript library. jquery has an active community, who are constantly submitting new programs. One such program is Featured Content Glider: by http://www.dynamicdrive.com. It includes the javascript and the css stylesheet used in the news scroller.
Customers using Jetfire Workflows rely on websites with good connectivity to stay connected to their business and keep their business processes up-to-date. Recently, our ISP moved their Data Center to be better served by a higher speed internet connection. Web pages are definitely served faster, but I also noticed busy intervals during the day leading to page rendering delays and often dropped logins for customers. The dropped logins require users to re-login – a nuisance when you are busy.
Session State in the web.config file is set up as ‘InProc’, meaning that the Session State is stored in memory. By changing Session State to ‘SqlServer’, the goal was to avoid unnecessary re-logins.
In a hosted environment, this procedure is:
In an ISP environment, step 1 is changed to create the database and upload the database schema.
For our lazy deployment person (that’s me), even this simple procedure was too complicated. I needed a one-click procedure to
manage the transition automatically and minimize the amount of effort required to convert each site.
These are the design steps used to add the session state tables to an existing database and then create a Web Control to change the session state.
The figure on the right shows the desired end result – a web control that changes Session State from memory storage to database storage and simple enough that the customer administrator can do it.
The .NET Framework includes an install database and tables script for ASP State. Since we are adding the ASPState tables to an existing database, we need to create an sql script that only addresses the tables (and stored procedures).
This involves installing the ASPState tables in my development system. The sql script, InstallSqlState.sql, to do this is found in one of the following folders; system drive\ Windows\ Microsoft.NET\ Framework\version\.
Once the ASPState database and tables are created, generate the script for the ASPState tables. I used the Database Publishing Wizard found in codeplex project: http://sqlhost.codeplex.com. Just follow the instructions.
The sql script is not a slam dunk at this point. There are a few gotchas that need to be addressed.
Code Snippet 1: SQL Script for ASPState tables
The above code snippet shows a portion of the SQL script for installing ASPState tables.
Note: The sql script is over 1100 lines of code, which is why it is not included in its entirety.
Code Snippet 2: Create the ASPState Tables
The above code snippet creates the tables for the ASPState tables.
The end result is tables: ASPStateTempApplications and ASPStateTempSessions, along with all Stored Procedures are installed in the target database.
This section addresses how to change the mode for Session State programmatically. (The user interface code is straight-forward.) The Session State is located in the web.config file at the root of the website. It is retrieved using the code found in Code Snippet 3.
this.config = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~/");
SystemWebSectionGroup webGroup = this.config.GetSectionGroup("system.web") as SystemWebSectionGroup;
this.sessionState = webGroup.SessionState;
Code Snippet 3: Get the Session State from web.config
The ‘ChangeMode’ method, shown in Code Snippet 4, is called from the link click event of the Web Control. Key points include:
Code Snippet 4: Change the Mode of the Session State
There is a lot of flexibility in the SQLServer mode for Session State. Read the MSDN help closely to ensure that you engineer the correct operation for your application.
When the customer Administrator converts the storage of Session State from memory, ‘InProc’ to database, ‘SQLServer’, all users need to re-login because when the web.config is changed for this procedure and of course, because the Session State for each user is now null.
Further web testing showed that my work was not finished. There was one object that was stored correctly when the Session State Mode was ‘InProc’. However, it was real clear that the object needed to be serialized to the database when the Session State was changed to ‘SQLServer’. Whereas, ‘InProc’ stores a reference to the object in the Session memory, ‘SQLServer’ serializes the complete object to the database for each Session.
This required some re-work of the class in question and the code that used the object.