Monday, August 28, 2006
by Nik Kalyani
Monday, August 28, 2006 5:36:32 AM (Pacific Standard Time, UTC-08:00)

Testing an implementation of the DNN ISearchable interface implementation for a module can be time-consuming and slow if you rely on the DNN search engine indexer to run and then either check the database for results or use the Search UI. There is a simpler way.

Copy and paste the below script into DNNSearch.aspx (or grab the attachment at the end of this post), place the file in the root folder of your app and you can test ISearchable for any module instance with ease. The script requires you to provide a TabId and a ModuleId. It then queries the database for the BusinessController defined in the DesktopModules table and instantiates it exactly as the DNN search indexer does. It then calls GetSearchItems() and displays the results.

Unlike the DNN search indexer, DNNSearch does not make any changes to the database. It is useful only for testing if the ISearchable implementation is working correctly and does not provide any insights into any issues that the search index provider you are using may have.

 

DNNSearch.aspx

<%@ Import namespace="DotNetNuke.Entities.Modules" %>
<%@ Import namespace="DotNetNuke.Services.Search" %>
<%@ Import namespace="DotNetNuke.Common" %>
<%@ Page Language="c#" AutoEventWireup="false" %>
<script runat="server">
 
    void Results_Click(object sender, EventArgs e)
    {
        int moduleId = -1;
        try
        {
            moduleId = Convert.ToInt32(ModuleId.Text);
        }
        catch
        {
        }
 
        int tabId = -1;
        try
        {
            tabId = Convert.ToInt32(TabId.Text);
        }
        catch
        {
        }
 
        if ((moduleId > -1) && (tabId > -1))
            GetSearchResults(moduleId, tabId);
        else
            SearchResults.Text = "Both Module ID and Tab ID are required";
    }
 
    void GetSearchResults(int moduleId, int tabId)
    {
        ModuleController moduleController = new ModuleController();
        ModuleInfo moduleInfo = moduleController.GetModule(moduleId, tabId);
        StringBuilder sb = new StringBuilder();
 
        if (moduleInfo == null) 
        {
            SearchResults.Text = "No module found with ModuleID=" + moduleId.ToString() + " and TabID=" + tabId.ToString();
            return;
        }
 
        if (moduleInfo.BusinessControllerClass == "")
            SearchResults.Text = "The BusinessControllerClass in the database is blank.";
        else
        {
            try
            {
                object bizController = DotNetNuke.Framework.Reflection.CreateObject(moduleInfo.BusinessControllerClass, moduleInfo.BusinessControllerClass);
                if (bizController == null)
                    SearchResults.Text = "The Business Controller Class <b>" + moduleInfo.BusinessControllerClass + "</b> could not be instantiated.";
                else
                {    
                    SearchContentModuleInfo contentInfo = new SearchContentModuleInfo();
                    contentInfo.ModControllerType = (ISearchable) bizController;
                    contentInfo.ModInfo = moduleInfo;
                    SearchItemInfoCollection results = contentInfo.ModControllerType.GetSearchItems(contentInfo.ModInfo);
                    if (results != null)
                    {
                        int counter = 0;
                        foreach(SearchItemInfo searchItem in results)
                        {
                    if (moduleInfo.ModuleID == searchItem.ModuleId)
                    {
                        sb.Append("<p>Title: " + searchItem.Title);
                        sb.Append("<br>GUID: " + searchItem.GUID);
                        sb.Append("<br>Date: " + searchItem.PubDate.ToLongDateString());
                        sb.Append("<br>Description: " + searchItem.Description + "</p>");
                    }
                    counter++;
                        }
                        SearchResults.Text = counter.ToString() + " results found.<br>" + sb.ToString();
                    }
                    else
                        SearchResults.Text = "No search results.";
                }
            }
            catch(Exception e)
            {
                SearchResults.Text = "Error: " + e.Message + "<br><br>" + e.StackTrace;
            }
        }
    }
 
    override protected void OnInit(EventArgs e)
    {
        Results.Click += new EventHandler(Results_Click);
        base.OnInit(e);
    }
 
</script>
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
    <HEAD>
        <title>Speerio ISearchable Test</title>
        <style>
              body, p {font-family: Verdana; font-size: 9pt}
        </style>
    </HEAD>
    <body>
        <p><font size="4">DNNSearch Script - by <a href="http://www.speerio.net">Speerio, Inc.</a></p>
        <p><font color="red" size="4">WARNING: Do not leave this script installed on a production system.</font></p>
        <form id="Form1" method="post" runat="server">
            <p>Tab ID: <asp:TextBox ID="TabId" Runat="server"></asp:TextBox></p>
            <p>Module ID: <asp:TextBox ID="ModuleId" Runat="server"></asp:TextBox></p>
            <asp:Button ID="Results" Runat="server" Text="Get Search Results" /></p>
            <p>To test for user-specific results, add code to GetSearchItems() to check for userid=N in querystring.</p>
            <p><b>Search Results:</b></p>
            <asp:Label ID="SearchResults" Runat="server" />
        </form>
    </body>
</HTML>

DNNSearch.zip (1.48 KB)

#    Comments [2] - Trackback    

 Wednesday, August 16, 2006
by Nik Kalyani
Wednesday, August 16, 2006 8:21:25 PM (Pacific Standard Time, UTC-08:00)

Note to startups -- When coming-up with marketing language for your site, pay attention or you will end-up with self-fulfilling prophecies. "Kiko is a great, dead simple calendar" might not have been the most appropriate choice of words for Kiko.

#    Comments [0] - Trackback    

by Nik Kalyani
Wednesday, August 16, 2006 9:46:10 AM (Pacific Standard Time, UTC-08:00)

Wouldn't you know it...I post a spoof about Web 2.0 Design Guidelines and the very next day discover a site that dynamically generates Web 2.0 logos. Here's the devTao Web 2.0 logo rendered by the site:

 

Get your own Web 2.0 logo here: http://msig.info/web2.php

#    Comments [0] - Trackback    

by Nik Kalyani
Wednesday, August 16, 2006 6:02:24 AM (Pacific Standard Time, UTC-08:00)

On the DotNetNuke forums today, jstemper posed a question about how to speed up DNN development, specifically, the delay caused by app re-start when a module is recompiled.

I have invested a considerable amount of time researching the intricacies of Fusion probing to faciliate the co-existence of third-party assemblies with different versions in the same bin folder. In an earlier post on Managing assembly versions in ASP.Net I had provided tips on doing this. Reading this post got me thinking about applying the same technique for DNN development. I did a quick test and everything seems to work. Here is how you can speed-up DNN module development by skipping the app restart that occurs when assemblies in the "bin" folder are updated.

1) Create a "bin" folder under ~/DesktopModules (i.e. ~/DesktopModules/bin)

2) Modify your DNN web.config as follows:

            <runtime>
              <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
                   <probing privatePath="bin;DesktopModules\bin" />
              </assemblyBinding>  
           </runtime>

If you have a <runtime><assemblyBinding> section already present, just add the <probing ... > element, otherwise, you can just add this whole block right before </configuration>.

The "privatePath" attribute tells Fusion where to search for assemblies referenced by an application.

3) Change your module's script (ascx) file so the "Inherits" attribute includes the assembly name like this:

Inherits="Speerio.DNN.Modules.SkinStudio.Editor, Speerio.DNN.Modules.SkinStudio"

This corresponds to a typename of "Speerio.DNN.Modules.SkinStudio.Editor" and an assembly file "Speerio.DNN.Modules.SkinStudio.dll" (note: the extension should not be included in the "Inherits" attribute value).

4) Change your module’s VS.Net project build folder to ~/DesktopModules/bin

That's it. Now, when you recompile your module, there will be no application restart and the only assemblies that are converted from bytecode to native code are your module assemblies.

Please post a comment if you encounter any problems so I can modify the procedure if necessary.

#    Comments [4] - Trackback    

 Tuesday, August 15, 2006
by Nik Kalyani
Tuesday, August 15, 2006 6:26:51 PM (Pacific Standard Time, UTC-08:00)

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
"r" -- Registered Users -- returns true if user is member of Registered Users role
"e" -- Editors -- returns true if user is member of a role that has edit permissions
"a" -- Administrators -- returns true if user has administrator rights for the portal
"n" -- No access -- returns false

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);
        }
    }
}
#    Comments [3] - 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: 233
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