If you want your website to be Web 2.0–worthy, there are some guidelines you can/should follow to ensure that your site visitors will instantly develop an appreciation for your insightful design and ability to stay current. Here is a partial list:
1) Markup: Ensure that your site’s markup is XHTML 1.0 Strict standards-compliant. For bonus points, place appropriate chiclets at the bottom so visitors know that you are a CSS God and should they wish to examine the source code, they will not find any ugly
| tags, much, much easier to comprehend. When defining the style classes for the list elements, try and include as many CSS hacks as possible. Sure, this makes your site work in all browsers, but most importantly, it conveys your commitment to customer satisfaction. It shows how you will go out of your way to ensure that the three users visiting your site with the WallyWollaWidget Browser v0.5 alpha also have a fantastic user experience and, of-course, ease of content comprehension.
4) AJAX: Do a search-and-replace of all site content and change all occurrences of “XMLHttp” with “AJAX.” If your search does not return any results, then you have a serious problem. Immediately edit content to ensure that there are at least 3–4 references to AJAX on each page. If you have the time, add functionality that allows users to drag things around the page. This will keep them occupied and if you don’t have any meaningful content on the site, it will convince them that your site is a must-bookmark anyway. 5) Soft lines: Thoroughly check your site to ensure that there are no unsightly dark lines. Change the color of all lines to light grey. If possible, ensure that one end of each line fades into the background. Dark lines have been known to cause problems with content flow. By using light grey lines, you can be assured that your site visitors will not lose their train of thought between paragraphs. 6) Rounded Corners: Web designers are finally catching-on to a concept that furniture desginers are all too familiar with. Sharp corners hurt! Round-off all corners on your site. It’s OK to leave the occasional odd corner sharp if the other three are rounded. This is stylish and as long as the sharp corner is out of the way, it is unlikely to cause any harm. Superior web sites will combine gradients, rounded corners and soft lines for the ultimate in user comfort while browsing the site. 7) Badges and Chiclets: Badges and chiclets are an important aspect of any Web 2.0 site as they give users choices that they would otherwise not have. For instance, an average user would never know the URL for the official CSS specification. However, the convenient CSS chiclet on your site will put this spec one click away. Your site visitors will be overjoyed. I don’t have to tell you how critically important it is to have one or more RSS feeds on your site. Note that the orange XML chiclet is now uncool. You must use the official RSS logo and for best results, include chiclets from 5–10 other sites for the huge numbers of people that are specific about the feeds they subscribe to such as RSS 0.91, RSS 0.92, Atom, PomDiddly 1.0 etc. 9) Tabs: Find a way to incorporate tabbed-navigation on your site. It goes without saying, but I will say it anyway — make sure the tabs are rounded, have a gradient and use soft lines. It is also helpful to include numbers in tabs. It’s OK if the visitor has no way of knowing what the number represents. Just seeing the number there hints at the plethora of content that awaits them if they should choose to click the tab. 10) Reflections: Adding reflection to one or more images and text on the page will instantly add a lustre to your site pages that your content never can. Another advantage is that you do not need to update content for many months and your site will still continue to look fresh. Remember to add a “beta” kicker to your primary site logo. 11) License: Having put in all the effort in creating a fantastic Web 2.0 site, the last thing you would want to do is confuse site visitors who want to borrow a background image or graphic from your site. Eliminate the guesswork and include an ultra-cool “Creative Commons License” chiclet on your site. Even if no one borrows the graphics, at least they get a sense of your fairness and willingness to share. (If it isn’t clear, this is a total tongue-in-cheek post.)
If you create enough DotNetNuke modules, you will soon notice that you repeat code to retrieve and store settings. I got fed-up with this and wrote a common engine with an XML-driven UI to manage settings in my modules. Now, when I create a new module, I just change one XML file and I have the UI for the user to edit/save module settings ready to go. I am not ready to share the complete UI yet, however, I thought I would start by sharing my code for managing the settings. For any DNN module, the code allows you to store and retrieve settings with three scope types — User, Module and Global. “User” scope is a user-specific value that is persisted using the DNN core personalization support; “Module” scope is a value that is specific to one instance of a module; “Global” scope is a value that is specific to all instances of the current module type. I suspect the User scope may have been rendered obsolete by the profile capabilities in DNN 3.3.4/4.3.4, however everything still works. There are some useful helper methods that can check if a setting exists and strong-typed methods for retrieving string, numeric and boolean values. One item bears explaining — enumerated values. The enumeration is quite limited and is actually a “role enumeration.” It allows you to have a setting that indirectly refers to the roles granted view/edit rights at the module level. For example, if you store an enumerated value as “e”, when retrieving the value, GetEnumerated() will return true if the current user has edit permissions for the module. The enumerations are as follows: “*” — All Users — always returns true I use this to set a module’s boolean property to True or False. This approach makes it a lot easier to deal with simple permissions instead of having the user select actual Roles for various settings. The code is simple enough that you should be able to figure it out quickly (sorry for the lack of comments). The key methods are “Save,” “Get” and “Delete.” Here is the code. Use it as you wish, but please do not delete the copyright line. Speerio Module Settings Controller SettingsController.cs
using System;
using System.Web;
using System.Collections;
using DotNetNuke.Security;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Users;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Services.Personalization;
// Copyright 2006 Speerio, Inc. (Author: Nik Kalyani)
namespace Speerio.DNN.Common
{
public class SettingsController {
private int moduleId = -1; private int globalId = -1; private Hashtable moduleSettings = null; private Hashtable globalSettings = null; private Hashtable userSettings = null; private UserInfo userInfo = UserController.GetCurrentUserInfo();
private PortalSettings portalSettings = (PortalSettings) HttpContext.Current.Items["PortalSettings"]; private PersonalizationController personalizationController = new PersonalizationController(); private ModuleController moduleController = new ModuleController(); private ModuleInfo moduleInfo = null; private bool createMissing = false; public enum SettingScope { User, Module, Global }; public SettingsController(int moduleId) : this(moduleId, false) {
} public SettingsController(int moduleId, bool createMissing) {
ModuleId = moduleId; moduleInfo = moduleController.GetModuleByDefinition(portalSettings.PortalId,"Site Settings");
GlobalId = moduleInfo.ModuleID; if (HttpContext.Current.Request.IsAuthenticated)
userSettings = personalizationController.LoadProfile(userInfo.UserID, portalSettings.PortalId).Profile; moduleSettings = moduleController.GetModuleSettings(ModuleId); globalSettings = moduleController.GetModuleSettings(GlobalId); } private bool CreateMissing {
get{ return(createMissing); }
set{ createMissing = value; }
} public Hashtable GlobalSettings
{
get{ return(globalSettings); }
} public Hashtable ModuleSettings
{
get{ return(moduleSettings); }
} public Hashtable UserSettings
{
get{ return(userSettings); }
} private int ModuleId {
get{ return(moduleId); }
set{ moduleId = value; }
} private int GlobalId {
get{ return(globalId); }
set{ globalId = value; }
} public void Delete(string key) {
Delete(key, SettingScope.Module); } public void Delete(string key, SettingScope scope) {
try
{
switch(scope)
{
case SettingScope.Module :
moduleController.DeleteModuleSetting(ModuleId, key); break;
case SettingScope.Global :
moduleController.DeleteModuleSetting(GlobalId, key); break;
case SettingScope.User :
PersonalizationInfo pInfo = personalizationController.LoadProfile(userInfo.UserID, portalSettings.PortalId); Hashtable userProfile = pInfo.Profile; pInfo.Profile.Remove(key); personalizationController.SaveProfile(pInfo, userInfo.UserID, portalSettings.PortalId); break;
} } catch
{
} } public string Get(string key) {
return(Get(key, "", SettingScope.Module, false)); } public string Get(string key, bool replaceTokens) {
return(Get(key, "", SettingScope.Module, replaceTokens)); } public string Get(string key, SettingScope scope) {
return(Get(key, "", scope, false)); } public string Get(string key, SettingScope scope, bool replaceTokens) {
return(Get(key, "", scope, replaceTokens)); } public string Get(string key, string defaultValue) {
return(Get(key, defaultValue, SettingScope.Module, false)); } public string Get(string key, string defaultValue, bool replaceTokens) {
return(Get(key, defaultValue, SettingScope.Module, replaceTokens));
} public string Get(string key, string defaultValue, SettingScope scope, bool replaceTokens) {
string settingValue = GetSettingValue(key, scope);
string returnValue = ""; if (settingValue == null) {
if (CreateMissing) Save(key, defaultValue, scope);
returnValue = defaultValue; } else
returnValue = settingValue; // PortalTokens does a search and replace of embedded
// tokens in strings. Since it is not included with
// this code, I have commented it out.
// if (replaceTokens)
// return(PortalTokens.Replace(returnValue, "~Public", moduleInfo));
//else
return(returnValue);
} public void Save(string key, string keyValue) {
Save(key, keyValue, SettingScope.Module); } public void Save(string key, string keyValue, SettingScope scope) {
SaveSettingValue(key, keyValue, scope); } public int GetNumeric(string key) {
return(GetNumeric(key, 0, SettingScope.Module));
} public int GetNumeric(string key, SettingScope scope) {
return(GetNumeric(key, 0, scope));
} public int GetNumeric(string key, int defaultValue) {
return(GetNumeric(key, defaultValue, SettingScope.Module));
} public int GetNumeric(string key, int defaultValue, SettingScope scope) {
string tmpSettingValue = GetSettingValue(key, scope);
if (tmpSettingValue == null) {
if (CreateMissing) SaveNumeric(key, defaultValue, scope);
return(defaultValue);
} else
{
int settingValue = defaultValue;
try
{
settingValue = Convert.ToInt32(tmpSettingValue); } catch
{
settingValue = defaultValue; } return(settingValue);
} } public void SaveNumeric(string key, int keyValue) {
SaveNumeric(key, keyValue, SettingScope.Module); } public void SaveNumeric(string key, int keyValue, SettingScope scope) {
SaveSettingValue(key, keyValue.ToString(), scope); } public bool GetBoolean(string key) {
return(GetBoolean(key, false, SettingScope.Module)); } public bool GetBoolean(string key, SettingScope scope) {
return(GetBoolean(key, false, scope)); } public bool GetBoolean(string key, bool defaultValue) {
return(GetBoolean(key, defaultValue, SettingScope.Module));
} public bool GetBoolean(string key, bool defaultValue, SettingScope scope) {
string settingValue = GetSettingValue(key, scope);
if (settingValue == null) {
if (CreateMissing) SaveBoolean(key, defaultValue, scope);
return(defaultValue);
} else
return(settingValue == "1" ? true : false); } public void SaveBoolean(string key, bool keyValue) {
SaveBoolean(key, keyValue, SettingScope.Module); } public void SaveBoolean(string key, bool keyValue, SettingScope scope) {
SaveSettingValue(key, (keyValue ? "1" : "0"), scope); } public bool GetEnumerated(string key) {
return(GetEnumerated(key, "n", SettingScope.Module)); } public bool GetEnumerated(string key, SettingScope scope) {
return(GetEnumerated(key, "n", scope)); } public bool GetEnumerated(string key, string defaultValue) {
return(GetEnumerated(key, defaultValue, SettingScope.Module));
} public bool GetEnumerated(string key, string defaultValue, SettingScope scope) {
bool returnValue = false; string settingValue = GetSettingValue(key, scope);
if (settingValue == null) {
if (CreateMissing) SaveEnumerated(key, defaultValue, scope);
settingValue = defaultValue; } switch(settingValue)
{
case "*": returnValue = true; break; case "r": if (PortalSecurity.IsInRole(portalSettings.RegisteredRoleName)) returnValue = true; break; case "e": if (PortalSecurity.HasEditPermissions(ModuleId)) returnValue = true; break; case "a": if (PortalSecurity.IsInRole(portalSettings.AdministratorRoleName)) returnValue = true; break; case "n": returnValue = false; break; } return(returnValue);
} public void SaveEnumerated(string key, string keyValue) {
SaveEnumerated(key, keyValue, SettingScope.Module); } public void SaveEnumerated(string key, string keyValue, SettingScope scope) {
SaveSettingValue(key, keyValue, scope); } private void SaveSettingValue(string key, string keyValue, SettingScope scope) {
try
{
switch(scope)
{
case SettingScope.Module :
moduleController.UpdateModuleSetting(ModuleId, key, keyValue); break;
case SettingScope.Global :
moduleController.UpdateModuleSetting(GlobalId, key, keyValue); break;
case SettingScope.User :
PersonalizationInfo pInfo = personalizationController.LoadProfile(userInfo.UserID, portalSettings.PortalId); Hashtable userProfile = pInfo.Profile; if (pInfo.Profile.Contains(key))
pInfo.Profile[key] = keyValue; else
pInfo.Profile.Add(key, keyValue); personalizationController.SaveProfile(pInfo, userInfo.UserID, portalSettings.PortalId); break;
} } catch
{
} } public bool SettingExists(string key) {
return(SettingExists(key, SettingScope.Module));
} public bool SettingExists(string key, SettingScope scope) {
bool returnValue = false; try
{
switch(scope)
{
case SettingScope.Global : if (GlobalSettings[key] != null) returnValue = true; break; case SettingScope.Module : if (ModuleSettings[key] != null) returnValue = true; break; case SettingScope.User : if (UserSettings[key] != null) returnValue = true; break; } } catch
{
} return(returnValue);
} private string GetSettingValue(string key, SettingScope scope) {
string settingValue = null; try
{
switch(scope)
{
case SettingScope.Global : settingValue = (string) GlobalSettings[key]; break; case SettingScope.Module : settingValue = (string) ModuleSettings[key]; break; case SettingScope.User : settingValue = (string) UserSettings[key]; break; } } catch
{
} return(settingValue);
} } } Aug 142006
Microsoft’s announcement yesterday of the free XNA Game Studio Express will bring about huge changes in XBox game development. Now, any teen with time to spare (ummm…that would be all of them) can create games for the XBox. It’s unlikely we will see games with the detail and quality that million-dollar development budgets can bring about, but the difference will be made-up in creativity. While the established game developers focus on the high-end gaming experience, expect to see a wave of engaging, if not as visually/aurally immersive, games flood the net. Aug 092006
Starting with DotNetNuke v3.3/4.3, all dependencies on the Microsoft MemberRole assembly were removed. DotNetNuke includes a Role Provider, a Profile Provider and a Member Provider that works directly with ASP.Net membership. When the MemberRole dependency was introduced in DotNetNuke v3.x, many portal administrators who used the multi-portal Single Sign-On capability in DotNetNuke v2.x had to resort to third-party solutions to make SSO work across portals. With DNN v4.3 (possibly v3.3 also; I have not tested), it looks like SSO, or at least the potential for SSO is back. If you add a row for a user in the UserPortals table, the user will effectively be able to authenticate seamlessly to every portal for which her/his user ID has a portal ID assigned. It’s that simple. Everything works as you would expect — roles are portal-specific; the user’s profile is portal-independent and authentication works regardless of which portal a user initially logs into. Implementing a UI for administering SSO looks to be as simple as having a module where you can link/unlink users to/from portals. Nice. Bugle is a project that consists of Google queries that help identify security bugs in open source software. This is a very interesting concept and while it may help hackers find vulnerabilities in software easier than scouring the code, I think it is more useful for open source project teams. By their very nature, open source projects are generally collaborative and therefore it is easy for unsecure code to creep in. The Bugle technique effectively helps detect high-level vulnerabilities in contributed code that has not been carefully scrutinized by the project security administrator. This brings up another topic, which is better suited for its own post, but I will briefly mention here. While open source is supposed to result in more secure code because many more eyeballs are reviewing the code, the reality is that few people that use open source software actually look at the code. Most people are in it for the “free” aspect, not necessarily for the code. If the project developers miss a vulnerability in the code, it may not be detected for a long time. How is this any different from commercial, closed source projects? |
