by Nik Kalyani
Friday, May 27, 2005 3:19:54 PM (Pacific Standard Time, UTC-08:00)
Laverne Douglas asks:
I would like to ask some questions about your site and how you implemented some of the features. But to be fair to all your viewers; it would be best answered in your blog. Specifically I would like to now about your store and downloads pages.
The download appears to be your navigator showing hidden pages on your site with roles assigned to allow downloads of items.
The store is unique from my perspective. Is it of your own design?
Nik's answer:
The Downloads page of the site is indeed the Navigator module being dog-fooded prior to release. Basically, Navigator allows you to create a hierarchy of nodes organized into folders. Each folder can either be public or private. If it's public, it is visible on all instances of Navigator on a portal. Each folder also has role-based access.
Each node can be one of the following:
URL: Which can be a GET or a POST. The POST can be used to authenticate to other apps for example. It can also be an RSS feed and can open on the right or in a new window.
HTML Content: This can be any content. Optionally, you can have a downloadable file, and optionally you can have a custom license that is presented before download. This is what you see on the Speerio Downloads page. Finally, you can choose to have Page Ratings for the node and also attach the node to multiple, independent RSS feeds through the NewsWire module. Access to the node is role-based and you can choose if the security is for the node AND file attachment, or only for the file attachment.
ASP.Net Control: This can be any ASP.Net control that is capable of being initialized without code-behind. You can specify property values for the control and the control will be initialized with those values. You can also pass any User or Portal property value to the user control. This allows you to easily integrate non-DNN content into DNN.
It has some other bells and whistles such as DNN Help integration, drag and drop node ordering, etc.
The Navigator module will be available as a free Community Edition and also as a Pro Edition. The Community Edition will have basic URL and HTML hierarchical content display. The pro version will have everything described above. The DNN 2.x version is being dog-fooded on my site, and the DNN 3.x version is doing that on DotNetNuke.com.
The store is the standard SnowCovered Portal Store offering. I modified it (not much actually...mostly CSS and graphics) to be more consistent with the Speerio brand. The only significant enhancement I made is to add a more robust PayPal payment processor that has SMS notification. When an order comes through, I get a text message on my mobile and then when the customer downloads the file, I get another text message. If more than 15 mins. elapse between the two, I know there is a problem and can pro-actively contact the customer and resolve the problem.
by Nik Kalyani
Monday, April 25, 2005 12:43:46 AM (Pacific Standard Time, UTC-08:00)
Dharmesh Shah asks:
I’m exploring the use of third-party controls (like Infragistics, ComponentArt, etc.) within my custom modules. My first round of experimentation with Infragistics seems to indicate a problem because of some of the Javascript and other stuff the control tries to insert. Do you have any tips or experience in using these types of controls inside a DNN module?
Nik’s response:
This is an issue that is going to plague DNN developers for some time to come. To understand why, let’s examine how components use Javascript.
Generally speaking, a third-party component will inject JS into your page in one of two ways: (1) by having a <script src=”…”> tag that links to a .js file, or (2) by injecting the script in-line using a <script> tag without the “src” attribute. The former is more efficient because it can benefit from browser caching. It is used for generic code and not for instance-specific initialization etc. The latter is necessary because it allows server-side code to create script on-the-fly. For example, in order to render the portal menu, the data for the menus is rendered on-the-fly.
Most components are written for ASP.Net applications. Your average ASP.Net application makes use of usercontrols for “shared code” to avoid repeatedly coding things such as headers, footers, feedback forms etc. Embedding a third-party component into these usercontrols is not a problem because it is rare that the same usercontrol is used multiple times on a page. In the case of DNN, usercontrols manifest themselves as “modules” and as any DNN user knows, a module may appear any number of times on a page. This is where the problem begins.
Forward-thinking component developers have envisioned this scenario and taken measures necessary to ensure the uniqueness of dynamically injected variables and objects in code. If a component developer has not done this, it’s game over when you have multiple instances of the component on the same page since the initialization code will reference only the last instance (if it even works). For example, let’s say we have an image with an ID of “MyImage” and there is a variable tracking the position of this image. The (unoptimized) code injected might look something like this:
var imgX = document.getElementById(“MyImage”).left; var imgY = document.getElementById(“MyImage”).top;
If this code appears in a DNN module that is instantiated four times on a page, imgX and imgY will be set to the last occurrence of the code. Any further U.I. manipulation by JS will throw errors or produce generally erratic behavior.
This is the first thing to look for in a third-party component that uses client-side JS. Does it have the ability to be instantiated multiple times on the same page. The test scenario for this is easy. Download the trial version and modify one of the samples so two instances of the component are on the same page (do give them different ID’s). Does it work? If it seems to be working, double-check the page source. Look for Javascript variables and ensure that variables have a unique prefix or suffix. In most cases this will be driven by the ClientID property of the usercontrol as this is the Microsoft-recommended property to use for client-side identiciation of server-side controls.
I wish I could say that there was an easy workaround if the component does not readily allow multi-instance capability. There just isn’t. Your best bet is to contact the developer and make them aware of this problem. They may have a workaround or might make the change for you.
Another problem area is with initialization. A number of components will attach a handler to the body.onLoad() cleint-side event of a page to perform initialization. The problem occurs when the component is not well-behaved and or just plain dumb. In this case, it will either over-write an existing handler defined by some other component or even DNN itself, or just append multiple, duplicate handlers for multiple instances of a module. Again, this is something to verify through the page source.
The ID’s issued to dynamically generated U.I. objects is another problem area. Like variables and code objects, it is imperative that these U.I. objects be given unique ID’s otherwise when code tries to manipulate the object, it will fail.
The final and most perplexing problem is one of cross-compatibility. A component might do everything right, but if it sets a particular U.I. property to a certain value, and another module on the page emits script that also manipulates the same U.I. property, the result is disastrous. In this case, if changing code is not an option, the only solution is to not have the modules on the same page.
I hope this information will be helpful in figuring out which components are suitable for DNN and which ones aren’t.
by Nik Kalyani
Tuesday, April 19, 2005 4:07:50 PM (Pacific Standard Time, UTC-08:00)
Navin Pathuru asks:
Currently we have a requirement where our client wants us to do something about loading Dnn in Netscape 4.x version. We know Dnn uses a lot of client side javascript so there are going to be problems with this. But we just want to do our best. The approach we want to take is to figure out the browser version and load a different skin one which has really minimal graphics when we detect it is Netscape 4.x. I am wondering if you will be able to write your thoughts about this approach in your blog.
Nik’s response:
Navin, this is a great question. Before I answer it, I do want to point out that most of the client-side Javascript you see on a DNN page is emitted by ASP.Net, the menu and other modules. DNN itself does not generate a lot of client-side script, especially if you disable the Client API.
Now, to answer your question. Since we have the DNN source code, the dime answer is change the core to suit your needs. But this is neither fun nor practical. Let’s go on to the dollar answer.
DNN uses the tab settings (inherited or set directly) to determine which skin is to be displayed. You can override this with querystring parameters to force a certain skin to be displayed. Also, due to its IBuySpy legacy, DNN has the rather unfortunate artifact called the Admin skin which forces a skin switcheroo whenever a non-View module control is displayed. (I think this will go away as part of DNN’s continued evolution.)
So, if there is no way to dynamically switch the skin, how is this problem to be solved. After noodling about this for a minute, it occurred to me. Although DNN allows skins to be intelligent, most skins are dumb, i.e. they intersperse some DNN controls, HTML, images and CSS to render a page layout. Most skins do not take advantage of their ability to do anything ASP.Net allows by virtue of their being usercontrols. This is precisely what we can do to solve the problem at hand.
The solution consists of a single file that I’ll call a “Skin Proxy.” The Skin Proxy can be used to dynamically select a skin based upon any user, portal, tab or browser properties. In my example, I have used a browser property since that was the focus of Navin’s question. Basically, the Skin Proxy creates a string based on some browser properties and then attempts to load a control (i.e. a skin) with a name matching the string.
SkinProxy.ascx
<%@ Control language="c#" %> <script runat="server"> protected void Page_Load(object s, EventArgs e) { string browser = Request.Browser.Browser.ToLower(); string version = Request.Browser.MajorVersion.ToString();
if (Request.QueryString["debug"] != null) Response.Write(browser + version + ".ascx"); else { try { this.Controls.Add(this.LoadControl(browser + version + ".ascx")); } catch { this.Controls.Add(this.LoadControl("Default.ascx")); } } } </script>
In this code, the first couple of lines get the browser and browser major version into string variables. Then, an attempt is made to load a control whose name corresponds to the concatenated browser and version. If this is unsuccessful, a control named “Default.ascx” is loaded. (If you add a querystring variable named “debug” the script reports the name of the control that would be loaded instead of actually loading it. This is helpful for figuring out what to name your control.)
I tested this out with IE and Firefox and everything seemed to work OK. Please do post here if you find problems with the script.
by Nik Kalyani
Sunday, March 13, 2005 1:23:21 PM (Pacific Standard Time, UTC-08:00)
nokiko asks:
I am working on some new containers and dnn enhancements. I want to know in my container which moduleid is loaded so lets say i have an ascx container and in there i have the following:
<div id=''mod_<% functiontogetmoduleidhere'">rest of container ...</div>
Is there a function or another way that in an ascx container I would know which moduleid is loaded in there.
So that way lets say i have 2 modules one one page and i want the ascx container to render
<div id="mod_36">content here</div> <div id="mod_37">content here</div>
i would need this to imge replace techniques on my h3 tags for instance
#mod_36 h3 { background:url(announcements.gif) } #mod_37 h3 { background:url(events.gif)}
Nik’s answer:
That’s an excellent question nokiko. In DotNetNuke 3.x, the Container class has a public, static method called GetPortalModuleBase(). This method requires one parameter — a usercontrol that is present in the container. With this knowledge, it is easy to figure out a solution. For example, most of the default DNN containers have a title defined using: <dnn:TITLE runat="server" id="dnnTITLE" />. In any of these containers, you can add the following line to display the ModuleId of the module present in the container:
<%= DotNetNuke.UI.Containers.Container.GetPortalModuleBase(dnnTitle).ModuleId.ToString() %>
You can also use the same technique to display any other Portal, Tab, Module or User property to customize the appearance of containers dynamically:
<%= DotNetNuke.UI.Containers.Container.GetPortalModuleBase(dnnTitle).PortalSettings.PortalName %> <%= DotNetNuke.UI.Containers.Container.GetPortalModuleBase(dnnTitle).PortalSettings.ActiveTab.TabName %> <%= DotNetNuke.UI.Containers.Container.GetPortalModuleBase(dnnTitle).ModuleConfiguration.ModuleTitle %> <%= DotNetNuke.UI.Containers.Container.GetPortalModuleBase(dnnTitle).UserInfo.FirstName %>
If you don’t have a need for the DNN Title control in your container, that’s OK. You can add it anyway with an attribute of “Visible=false”. It is possible to achieve this result without using any skin control, however it requires a little more code and is not worth the effort in most situations.
by Nik Kalyani
Wednesday, March 09, 2005 3:57:40 PM (Pacific Standard Time, UTC-08:00)
How do you create a page that allows a user to select the font size?
Nik’s answer:
You can do this using CSS or Javascript or a combination of the two. Here is some sample HTML code with embedded CSS that demonstrates how it is done:
<html> <style>
.xsmall { font-size: 0.5em; } .medium { font-size: 1.0em; } .large { font-size: 1.5em; } .xlarge { font-size: 2.0em; }
.fauxLink { text-decoration: underline; cursor: hand; }
</style>
<body> <p>This code demonstrates how to make a web page with user selectable font sizes. The trick is to use CSS with relative point sizes.</p> <p>You can set the "font-size" CSS attribute to an "em" value. This is the value of the character box for a given font (i.e. the "M" character). Using an em value sets the font size to a relative value. For example, 1.3em is the same as 130%.</p> <p> <span class="fauxLink" onClick="document.body.className='xsmall'">x-small</span> | <span class="fauxLink" onClick="document.body.className='medium'">medium</span> | <span class="fauxLink" onClick="document.body.className='large'">large</span> | <span class="fauxLink" onClick="document.body.className='xlarge'">x-large</span> </p>
<div> Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nullam eu sem. In commodo. Integer id lectus a enim fermentum molestie. Donec fringilla ligula vel justo. Aliquam elementum wisi luctus lacus. Etiam leo. Duis auctor elit quis mauris. Ut elit lectus, luctus quis, rhoncus sed, lacinia in, velit. Pellentesque vulputate congue magna. Proin luctus. Pellentesque rhoncus, metus eget laoreet ornare, lacus risus mollis nibh, non interdum augue sapien nec metus. Donec sem. Vestibulum rutrum, urna non cursus tristique, tellus erat sagittis turpis, at venenatis massa massa egestas pede. Praesent ultrices malesuada nisl. Etiam ac felis eget ligula venenatis suscipit. Suspendisse ultricies lacus eu eros. Aliquam bibendum adipiscing ante. </div> </body> </html>
As you can see, it is very easy to accomplish this. However, this is only half the solution. Presumably the user wants the size selection to persist so it’s a good idea to use a cookie to store their preference. Then, no matter which page they visit on your site, text will be displayed at their preferred size. Furthermore, when they return to your site at a later date, depending on the duration of the cookie, the site will automatically adjust the size.
For DotNetNuke, I created a component that does this for you (by allowing the user to select a stylesheet for the page and remembering it for future visits). This is part of the Speerio SkinWidgets suite. You can see a demo here.
by Nik Kalyani
Sunday, February 27, 2005 10:39:34 AM (Pacific Standard Time, UTC-08:00)
pmgerholdt asks:
If you could field a newbie question - I have your "My Modules" solution open in VS. Say I start a VB Web Project. I can do that much - build a web project. How then do I get it to appear in DNN? Do I have to copy files to a zip file and load it via DNN's interface for modules? Or does it automagically appear in DNN by virtue of sharing its space? (That's why I recompiled DNN - I thougth your RandomImage Mod would become part of DNN ...)
And is there a difference between a module and a PA ... or is a module an instance of a PA - or is a PA one way of making a module while there are others?
Aside from just building from scratch, I've seen three approaches. Yours looks most simple and direct to me if I can get past the fact that I don't understand some essential basics. I've also looked at Bo's instructions and gone a ways down that road. But that really is extending the core, isn't it, rather than providing modules? Finally there are the code templates on DNNJungle.
Am I right in seeing these as three different approaches toward a similar goal?
Nik's answer:
Newbie questions are good. They force new synaptic links.
To answer your first question, there is no co-relation between the process of adding modules (controls) to the DotNetNuke DesktopModules folder and the module listing in your DNN portal. It would be nice if this happened automagically, but the unintended consequences would not be pleasant. For this reason, you must do one of the following to have a module appear in the listing:
1) Add it manually: This is the quickest way, especially when you're developing. Login as Host, and go to the Module Definitions page. Create a new Module definition and add the controls corresponding to your module to this definition. (I have intentionally left this description vague as it is a topic that deserves its own item.) Once this is done, your module will appear in the Modules Listing.
When you use the My Modules approach, you only need to build the custom module you are working on at that time. If you have a reference to your project from the _DNNStub project as recommended in the instructions, the important task of ensuring that your module's PA (private assembly or DLL) is correctly placed in the DNN "bin" folder is automatically handled.
Anyway, adding the Module Definition is all you need to do. After this, you can go to a portal page and add the module to the page. At this point, you will realize that you goofed somewhere. I have no statistical evidence, but I am inclined to believe that no one has ever created a module (that does anything worthwhile) that got displayed the first time it was added to a page during development.
There are a multitude of things that could be wrong. The most likely -- a problem in your ascx script file. This is where the DNNDebug.aspx script that's included is of use. Fire up a new browser window and call up the DNNDebug.aspx script. Type in the path to your control (example: ~/DesktopModules/ModuleName/somecontrol.ascx) and DNNDebug.aspx will tell you where the problem is. If it loads without error, then you know that the problem is in code-behind. Pat yourself on the back for successfully creating a working script with ASP.Net's mind-numbingly annoying syntax, and go back to VS.Net and your module's code-behind.
From here, you are on your own (unless you have a specific question).
2) Add it with the installer: This is a task best left for when your module is working beautifully on your development machine and you are ready for your ego to be crushed by end-users. Packaging a module for deployment basically involves zipping up the resources and creating a .dnn file. This is too detailed a topic to discuss here so I will (lazily) skip it.
Modules, PAs and Skin Objects
When a new platform evolves, so does its terminology. Often, to the chagrin of newbies as the terminology tends to make confusing some very simple concepts. Here then, is the non-authoritative guide to these terms:
Module: A module in the DNN context is a mini-application that runs within the DNN portal framework. It is a term that represents the composite product as viewed by users of the portals (regardless of whether they are host/admin/end-user). A module may consist of ASP.Net controls, assemblies, images, stylesheets, the kitchen sink, SQL script etc.
PA: PA is an abbreviation of "Private Assembly" and is a synonym for DLL (dynamic link library). .Net assemblies can be DLL's or executables (EXE's). Executables are standalone Windows programs, while DLL's need a host. In the case of DNN, the host is the ASP.Net engine. When you have an assembly that is stored in a (web) application's bin folder it is called a "private" assembly as it is usable only by that application. At this point, as an attentive reader, you are probably thinking, "oh cool...maybe there are also public assemblies?" Well yes and no. Public assemblies exist but to avoid the syntactic challenge of having the same abbreviation, public assemblies are called Shared Assemblies. They are stored in an area of your computer called the GAC (Global Assembly Cache) and are available to all applications. More about this here.
Skin Object: DNN skins can use standard ASP.Net usercontrols and components to enhance the portal. You can develop skins as HTML files (parsed and converted to ASP.Net user controls when uploaded), or you can bypass this process and code them as an ASP.Net user control (ascx) yourself. If you go the HTML route, there has to be a mechanism by which the HTML code can indicated how/where the ASP.Net usercontrols/components that are to enhance that skin are to be embedded. For this, DNN has an XML support file that must accompany the skin. Altogether time-consuming and quite useless. I recommend you create a skin as an ascx file from the get-go. It is not that different from HTML and is a lot easier to use and troubleshoot.
Anyway, coming back to definitions. My definition of "skin object" is "a term that is useless and should go the way of the Dodo." In all seriousness, why have a new term when ASP.Net usercontrol or ASP.Net component work. So when you see "skin object" just thing ASP.Net usercontrol or component.
Different approaches
I cannot speak for other people's work/resources. The "My Modules" solution exists for one simple reason -- to make it easy to develop modules if you have no interest in modifying the DNN core code. The solution provides clean upgradability, since when you upgrade the DNN code, you have to do nothing in the "My Modules" solution configuration. You will have to rebuild the solution and if there are breaking-changes in DNN itself, handles those in your code, but other than that, you no longer have to struggle with DLL conflicts and VS.Net cache issues while compiling. I have almost 50 projects and never see any DLL conflicts with this approach.
I will point out one thing that will help you distinguish how a resource is applicable. My approach falls in the realm of VS.Net "organization." A way to ensure that your VS.Net solution/project works with minimal headache and maximum efficiency. The other realm is VS.Net "development." Templates/scripts etc. are all aids to speeding up the process of development with the goal of creating an "end-user solution."
As developers, we frequently lose sight of the end-user. Although it is fun and challenging to play with code, ultimately most of us get a pay-check only if our experimentation produces a solution -- not a module, skin object, PA or executable -- but a solution, for the end-user. I encourage you to use as many resources others have generously made available to create a development environment that accelerates solution development by allowing you to focus on the task and not the tool.
Peer I/O out.
by Nik Kalyani
Sunday, February 27, 2005 9:33:16 AM (Pacific Standard Time, UTC-08:00)
I frequently get emails from random people asking me questions about random issues, mostly technical. I am extremely curious by nature and have a hard time leaving a technical problem unsolved and so I end up finding an answer. It's a good way to learn new things and also hone problem-solving skills.
Since the knowledge gained is lost in that email transaction, I have decided to formalize the process and make it part of my blog. If you have a technical question, email me at asknik ATnospam speerio.net. I will try and post an answer to interesting questions in the Ask Nik category of my blog.
|