Tuesday, February 06, 2007
by Nik Kalyani
Tuesday, February 06, 2007 7:34:44 AM (Pacific Standard Time, UTC-08:00)

The GoDaddy-MySpace fiasco appears to have run its course. As I read blog posts and news stories, one thing that stood out was the intense focus on the legal and business implications of GoDaddy’s actions. I do not know the exact details, but I can speculate that “business” folks at MySpace contacted “business” folks at GoDaddy who then directed technical folks to act on the request to shut-down Seclists.org. GoDaddy and MySpace (but mostly GoDaddy) has been at the receiving end of a fair amount of negative P.R. as a result. Let’s put our business hats aside for a moment and put on our technical hats (after all this is a technology blog).

I think that if the folks at MySpace or GoDaddy had consulted with their in-house technical and information security resources a much simpler and effective solution could have been employed. It’s a big assumption on my part that in-house technical resources were not consulted going by the sheer stupidity of the public actions (or perhaps they were, and subsequently ignored, which is often the case).

Any sane information security person would have informed them of two very important things:

  • On the Internet there is no “unpublish” feature. Once information is out there, you have to assume that it is there in perpetuity.
  • Usernames and passwords, once compromised must be changed as the associated accounts are no longer secure.

The course of action that MySpace took was the exact opposite of what they could and should have done — run a script on the published list of usernames to permanently disable each one and contact the account owners about what they need to do to regain access. This would have been terribly inconvenient for all the users involved, but it would have made the leaked usernames inconsequential.

GoDaddy simply compounded things for itself by not pushing back on this and instead shutting down the domain at MySpace’s request.

There have been many blog posts asking people to reconsider using GoDaddy as a registrar, including this one by fellow LinkedIn blogger Marc Freedman. I thought about this and decided against it. First of all, it’s a huge pain to move active domains and even with careful planning there will likely be some site down time during the switch. Secondly, there is a cost associated with it.

I would switch if GoDaddy did this sort of thing repeatedly and blatantly. However I am not convinced this is the case. While it is more fun and buzz-worthy to bash GoDaddy on this issue from the grandiose perspective of freedom and laws, the truth of the matter is that this episode highlights one commonality between GoDaddy and MySpace — incompetent people. I somehow doubt switching registrars is going to provide any measure of protection.  

#    Comments [1] - Trackback    

 Wednesday, January 31, 2007
by Nik Kalyani
Wednesday, January 31, 2007 10:54:34 AM (Pacific Standard Time, UTC-08:00)

In a previous post DotNetNuke Module Settings Made Simple, I had shared some code to make DotNetNuke module settings easier to manage. One of the methods in an included class permitted the use of tokens in stored settings values. This allows for many possibilities that are user-, tab- or portal-driven. In that post, I had omitted the code for token substitution because it is a bit lengthy. Here, then, is code for substituting string tokens with portal values.

Although the intended use is for module settings, there’s no reason this code cannot be used for just about any scenario where tokens need to be injected. 

using System;
using System.Web;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using System.Reflection;
using DotNetNuke;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Users;
using DotNetNuke.Entities.Tabs;
using DotNetNuke.Security;
using DotNetNuke.Security.Roles;
using DotNetNuke.Common;
using DotNetNuke.Common.Utilities;
using DotNetNuke.Services.Localization;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Entities.Modules.Actions;
using DotNetNuke.Services.Personalization;
using DotNetNuke.Modules.HTMLEditorProvider;
using DotNetNuke.Framework.Providers;
/* Copyright (c) 2007, Speerio, Inc. This notice may not be removed. */
namespace Speerio.DNN.Common
{
    /// <summary>
    /// Summary description for PortalTokens.
    /// </summary>
    public class PortalTokens
    {
        public const string UnknownUser = "~Public";
 
        public static string Replace(string source)
        {
            return(Replace(source, UnknownUser, null));
        }
 
        public static string Replace(string source, ModuleInfo moduleConfig)
        {
            return(Replace(source, UnknownUser, moduleConfig));
        }
 
        public static string Replace(string source, string unknownUserText, ModuleInfo moduleConfig)
        {
            PortalSettings portalSettings = (PortalSettings) HttpContext.Current.Items["PortalSettings"];
 
            // PORTALSETTINGS tokens
            source = PortalTokens.ReplacePortalTokens(source, portalSettings);
 
            // USERINFO tokens
            source = PortalTokens.ReplaceUserTokens(source, unknownUserText);
 
            // ROLEMEMBER tokens
            source = PortalTokens.ReplaceRoleTokens(source);
 
            // TABSETTINGS tokens
            source = PortalTokens.ReplaceTabTokens(source, portalSettings.ActiveTab);
 
            // MODULESETTINGS tokens
            if (moduleConfig != null)
                source = PortalTokens.ReplaceModuleTokens(source, moduleConfig);
 
            // USERPROFILE tokens
            source = PortalTokens.ReplaceProfileTokens(source);
 
            // REGEX tokens
            source = PortalTokens.ReplaceRegExTokens(source);
 
            return(source);
        }
 
        private static string ReplaceRegExTokens(string source)
        {
            // REGEX tokens
            if (source.IndexOf("<REGEX:") > -1)
            {
                int reToken = source.IndexOf ("<");
                if (reToken > -1)
                {
                    try
                    {
                        string rePair = source.Substring(reToken+1);
                        int term = rePair.IndexOf(">");
                        if (term > -1)
                        {
                            rePair = rePair.Substring(0,term);
                            string[] reParams = rePair.Split(',');
                            if (reParams.Length == 2)
                            {
                                source = source.Replace("<" + reParams[0] + "," + reParams[1] + ">","");
                                reParams[0] = reParams[0].Replace("REGEX:","");
                                source = Regex.Replace(source, reParams[0], reParams[1], RegexOptions.IgnoreCase);
                            }
                        }
                    }
                    catch
                    {
                    }
                }
            }
            return(source);
        }
                        
        private static string ReplacePortalTokens(string source, PortalSettings portalSettings)
        {
            if (source.IndexOf("[PORTALSETTINGS:") > -1)
            {
                try { source = source.Replace("[PORTALSETTINGS:PortalId]",portalSettings.PortalId.ToString()); } catch {}
                try { source = source.Replace("[PORTALSETTINGS:UploadDirectory]",portalSettings.HomeDirectory); } catch {}
                try { source = source.Replace("[PORTALSETTINGS:HomeDirectory]",portalSettings.HomeDirectory); } catch {}
                try { source = source.Replace("[PORTALSETTINGS:PortalName]",portalSettings.PortalName); } catch {}
                try { source = source.Replace("[PORTALSETTINGS:AdministratorId]",portalSettings.AdministratorId.ToString()); } catch {}
                try { source = source.Replace("[PORTALSETTINGS:AdministratorRoleId]",portalSettings.AdministratorRoleId.ToString()); } catch {}
                try { source = source.Replace("[PORTALSETTINGS:DefaultLanguage]",portalSettings.DefaultLanguage); } catch {}
            }
            return(source);
        }
 
        private static string ReplaceUserTokens(string source, string unknownUserText)
        {
            if (source.IndexOf("[USERINFO:") > -1)                        
            {
                try
                {
                    bool auth = HttpContext.Current.Request.IsAuthenticated;
                    UserInfo userInfo = null;
                    if (auth)
                        userInfo = (UserInfo) HttpContext.Current.Items["UserInfo"];
 
                    try { source = source.Replace("[USERINFO:UserID]",(auth ? userInfo.UserID.ToString() : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:FullName]",(auth ? userInfo.FirstName + " " + userInfo.LastName : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:FirstName]",(auth ? userInfo.FirstName : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:LastName]",(auth ? userInfo.LastName : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:Street]",(auth ? userInfo.Profile.Street : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:City]",(auth ? userInfo.Profile.City : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:Region]",(auth ? userInfo.Profile.Region : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:PostalCode]",(auth ? userInfo.Profile.PostalCode : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:Country]",(auth ? userInfo.Profile.Country : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:Password]",(auth ? userInfo.Membership.Password : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:Email]",(auth ? userInfo.Membership.Email : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:Unit]",(auth ? userInfo.Profile.Unit : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:Telephone]",(auth ? userInfo.Profile.Telephone : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:Username]",(auth ? userInfo.Username : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:IsSuperUser]",(auth ? userInfo.IsSuperUser.ToString() : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:AffiliateID]",(auth ? userInfo.AffiliateID.ToString() : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:Website]",(auth ? userInfo.Profile.Website : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:TimeZone]",(auth ? userInfo.Profile.TimeZone.ToString() : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:IM]",(auth ? userInfo.Profile.IM : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:Fax]",(auth ? userInfo.Profile.Fax : unknownUserText)); } catch {}
                    try { source = source.Replace("[USERINFO:Cell]",(auth ? userInfo.Profile.Cell : unknownUserText)); } catch {}
 
                }
                catch
                {
                }
            }
            return(source);
        }
 
 
        private static string ReplaceRoleTokens(string source)
        {
            string tmp = source;
 
            if (source.IndexOf("<ROLEMEMBER:") > -1)
            {
                try
                {
                    string pattern = "(?<token>\\<ROLEMEMBER:(?<roles>.*?)\\|(?<unknown>.*?)\\>)";
                    Regex r = new Regex(pattern, RegexOptions.IgnoreCase );
 
                    ArrayList tokens = new ArrayList(10);
                    ArrayList roles = new ArrayList(10);
                    ArrayList unknowns = new ArrayList(10);
                    Match m;                           
                    for (m = r.Match(source); m.Success; m = m.NextMatch())
                    {
                        tokens.Add(m.Groups["token"].ToString());
                        roles.Add(m.Groups["roles"].ToString().Trim());
                        unknowns.Add(m.Groups["unknown"].ToString().Trim());
                    }
 
                    RoleController roleController = new RoleController();
                    bool auth = HttpContext.Current.Request.IsAuthenticated;
                    UserInfo userInfo = null;
                    string[] userRoles = {};
                    if (auth)
                    {
                        userInfo = (UserInfo) HttpContext.Current.Items["UserInfo"];
                        userRoles = roleController.GetPortalRolesByUser(userInfo.UserID, userInfo.PortalID);
                        if (userRoles != null)
                        {
                            for(int u=0;u<userRoles.Length;u++)
                                userRoles[u] = userRoles[u].ToLower();
                        }
                    }
                    for(int idx=0;idx<tokens.Count;idx++)
                    {
                        string token = tokens[idx].ToString();
                        string defaultValue = unknowns[idx].ToString();
                        string[] roleList = roles[idx].ToString().Split(',');
                        string selectedRole = "";
                        for(int l=0;l<roleList.Length;l++)
                        {
                            for(int u=0;u<userRoles.Length;u++)
                            {
                                if (roleList[l].ToLower() == userRoles[u])
                                {
                                    selectedRole = roleList[l];
                                    break;
                                }
                            }
                            if (selectedRole != "")
                                break;
                        }
 
                        try { source = source.Replace(tokens[idx].ToString(), (selectedRole == "" ? defaultValue : selectedRole));  } 
                        catch {}
                    }
                    return(source);
                }
                catch
                {              
                }
            }
            return(source);
        }
 
 
        private static string ReplaceProfileTokens(string source)
        {
            string tmp = source;
 
            if (source.IndexOf("<USERPROFILE:") > -1)
            {
                try
                {
                    string pattern = "(?<token><USERPROFILE:(?<container>.*?),(?<key>.*?),(?<unknown>.*?)>)";
                    Regex r = new Regex(pattern, RegexOptions.IgnoreCase );
 
                    ArrayList tokens = new ArrayList(10);
                    ArrayList containers = new ArrayList(10);
                    ArrayList keys = new ArrayList(10);
                    ArrayList unknowns = new ArrayList(10);
                    Match m;                           
                    for (m = r.Match(source); m.Success; m = m.NextMatch())
                    {
                        tokens.Add(m.Groups["token"].ToString());
                        containers.Add(m.Groups["container"].ToString().Trim());
                        keys.Add(m.Groups["key"].ToString().Trim());
                        unknowns.Add(m.Groups["unknown"].ToString().Trim());
                    }
                    for(int idx=0;idx<tokens.Count;idx++)
                    {
                        object keyObject = Personalization.GetProfile(containers[idx].ToString(),keys[idx].ToString());
                        string keyValue = "";
                        if (keyObject != null)
                            keyValue = keyObject.ToString();
                        try { source = source.Replace(tokens[idx].ToString(), (keyValue == "" ? unknowns[idx].ToString() : keyValue));  } catch {}
                    }
                    return(source);
                }
                catch
                {              
                }
            }
            return(source);
        }
 
        private static string ReplaceTabTokens(string source, TabInfo tabSettings)
        {
            if (source.IndexOf("[TABSETTINGS:") > -1)
            {
                try
                {
                    try { source = source.Replace("[TABSETTINGS:PortalId]",tabSettings.PortalID.ToString()); } catch {}
                    try { source = source.Replace("[TABSETTINGS:TabId]",tabSettings.TabID.ToString()); } catch {}
                    try { source = source.Replace("[TABSETTINGS:TabName]",tabSettings.TabName); } catch {}
                    try { source = source.Replace("[TABSETTINGS:Title]",tabSettings.Title); } catch {}
                    try { source = source.Replace("[TABSETTINGS:AuthorizedRoles]",tabSettings.AuthorizedRoles); } catch {}
                    try { source = source.Replace("[TABSETTINGS:AdministratorRoles]",tabSettings.AdministratorRoles); } catch {}
                    try { source = source.Replace("[TABSETTINGS:ParentId]",tabSettings.ParentId.ToString()); } catch {}
                    try { source = source.Replace("[TABSETTINGS:Level]",tabSettings.Level.ToString()); } catch {}
                    try { source = source.Replace("[TABSETTINGS:Skinsource]",tabSettings.SkinPath); } catch {}
                    try { source = source.Replace("[TABSETTINGS:SkinSrc]",tabSettings.SkinSrc); } catch {}
                }
                catch
                {
                }
            }
            return(source);
        }
 
        private static string ReplaceModuleTokens(string source, ModuleInfo moduleConfig)
        {
            if (source.IndexOf("[MODULESETTINGS:") > -1)
            {
                try
                {
                    try { source = source.Replace("[MODULESETTINGS:ModuleId]",moduleConfig.ModuleID.ToString()); } catch {}
                    try { source = source.Replace("[MODULESETTINGS:TabId]",moduleConfig.TabID.ToString()); } catch {}
                    try { source = source.Replace("[MODULESETTINGS:ModuleDefId]",moduleConfig.ModuleDefID.ToString()); } catch {}
                    try { source = source.Replace("[MODULESETTINGS:ModuleOrder]",moduleConfig.ModuleOrder.ToString()); } catch {}
                    try { source = source.Replace("[MODULESETTINGS:PaneName]",moduleConfig.PaneName); } catch {}
                    try { source = source.Replace("[MODULESETTINGS:ModuleTitle]",moduleConfig.ModuleTitle); } catch {}
                    try { source = source.Replace("[MODULESETTINGS:AuthorizedEditRoles]",moduleConfig.AuthorizedEditRoles); } catch {}
                    try { source = source.Replace("[MODULESETTINGS:AuthorizedViewRoles]",moduleConfig.AuthorizedViewRoles); } catch {}
                    try { source = source.Replace("[MODULESETTINGS:ControlSrc]",moduleConfig.ControlSrc); } catch {}
                    try { source = source.Replace("[MODULESETTINGS:ControlTitle]",moduleConfig.ControlTitle); } catch {}
                    try { source = source.Replace("[MODULESETTINGS:DesktopModuleId]",moduleConfig.DesktopModuleID.ToString()); } catch {}
                    try { source = source.Replace("[MODULESETTINGS:FriendlyName]",moduleConfig.FriendlyName); } catch {}
 
                }
                catch
                {
                }
            }
            return(source);
        }
 
    }
}
#    Comments [1] - Trackback    

 Tuesday, January 30, 2007
by Nik Kalyani
Tuesday, January 30, 2007 6:50:14 PM (Pacific Standard Time, UTC-08:00)

Many businesses that develop websites for customers using DotNetNuke have to field questions about reference sites. Here is the answer —


Computer Hardware:

Manufacturing:

Software:

Pharma:

Travel/Logistics:

Education:

Financial Services:

Non-Profits:

Entertainment:

 

#    Comments [1] - Trackback    

 Monday, January 29, 2007
by Nik Kalyani
Monday, January 29, 2007 10:23:52 AM (Pacific Standard Time, UTC-08:00)

I naively assumed that the Office team dog-fooded the Outlook 2007 RSS Feeds capability before shipping the product. Based on my experience, I am inclined to believe that no testing was done on this aspect of Outlook. Here are some issues that are obvious bugs or shortcomings. Some of these are so blatant that it makes you wonder if anyone on the Office team even uses RSS on an everyday basis.

RSS Page: There is no provision (at least none I could find) for easily customizing the page you see when you click RSS Feeds in Outlook. What I expect to see is a summary of the newest items from all the feeds in my subscription list. Instead I see a useless intro to RSS and a more useless directory of various Microsoft feeds.

Adding Feeds: Adding feeds should be as simple and easy as possible. If I chance upon a good feed in my browser, I am inclined to right-click and copy the feed link. I then right-click on RSS Feeds and select “Add a new RSS feed…” which displays the “New RSS Feed” dialog. This is where the problem begins. I cannot right-click and paste the link in my clipboard. I have no idea why. Instead, I have to use Ctrl-V or Edit – Paste. This is a nuisance. I also have no ability to customize anything about the feed when I add it. To do that, I have to click “Tools,” “Account Settings…,” “RSS Feeds,” highlight feed, “Change…”… This is ridiculously complicated.

Feed Handling: This is where things go seriously wrong. I am convinced that Outlook 2007 has a built-in mechanism for arbitratily flagging feed items as read or unread with the additional capability of automatically duplicating items. This gets annoying very quickly, especially on blog feeds. It’s a simple XML feed and Outlook should be able to figure out if an item is already present and not duplicate it. The read/unread flagging is also not rocket science.

Post Display: Reading a post with images is another annoyance. Most post images are not important, but when a blogger has posted a screen-cap or a photo, one needs to be able to look at the image. Outlook will mysteriously hide the images in a post until you click on it. This is weird behavior. Once you select the option to be able to view images, you should be able to view the images. Requiring an additional click on each image cannot be an intentional feature…this has to be a bug and a pretty serious one too.

All-in-all the RSS reader in Outlook is junk. It’s one saving grace is the ability to export the list of feeds in OPML format so you can switch to different reader.

 

#    Comments [2] - Trackback    

by Nik Kalyani
Monday, January 29, 2007 9:19:07 AM (Pacific Standard Time, UTC-08:00)

Steve Krug loves tabs for web page navigation and shares examples of tabs that work and those that don’t in his popular book on web usability “Dont’ Make Me Think.” In reading his thoughts on tabs, one insight that I gained is that for tabs to work really well, the visual representation should mimic real-world tabs used in binders, reports etc. While there are many scripts and components available for tabs, most of them tend to ignore an important aspect of tabs — overlapping. Some developers/designers use faux 3D to give this appearance, but nothing beats the real thing. Some quick CSS and Javascript later, I came up with the following:

OverlappingTabs

The overlap is subtle, but in my testing, it does make a difference as the tab content pops up versus just appearing. You can see the difference it makes if you take a look at a tabbed-dialog in Windows. The overlap of tabs is minimal, but visually, it makes quite a difference.

My solution was to create a tab script that works equally well whether the tabs are created directly in HTML or indirectly through server-side script. The HTML for creating the above is quite simple:

<div class="tabContainer">
 <ul id="MyTabs"><li>Home</li><li>Products</li><li>Support</li><li>About</li></ul>
</div>

To “tabify” this code, you use the following Javascript:

renderTabs(“MyTabs”);

To add more client interactivity, you can pass two additional (optional) parameters:

– name of a function to call when a tab is clicked. The function is passed the unordered list DOM object and tab index that was clicked as parameters
– index of tab to display

Here’s an example:

renderTabs(“MyTabs”, “clickHandler”);

function clickHandler(ul, tabIndex)
{
      alert(“Tab “ + tabIndex + “ was clicked.”);
}

The script, including CSS, images and a sample handler is here: OverlappingTabs.zip (tested with IE6/7 and Firefox 1.5)

 

#    Comments [1] - Trackback    

RSS feed
Search and Links
Bling

View Nik Kalyani's profile on LinkedIn

Contact me: nik*kalyani.com (replace "*")

TechBubble
www.flickr.com
This is a Flickr badge showing public photos from techbubble. Make your own badge here.
Statistics
Total Posts: 204
This Year: 22
This Month: 0
This Week: 0
Comments: 231
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2008
Nik Kalyani
Sign In
All Content © 2008, Nik Kalyani