8/09/2011

What modules I used to develop www.alunet.com (Part 1 - introduction and Dynamic Forms from Datasprings)

Instead of writing another boring "top 10" list of modules "you must use before you die", I chose to present a site I developed (currently online but in Beta stage). This is a unique opportunity, since I usually don't get permission or don't have enough time to publish details on my work.

 

The site is www.alunet.com, a global directory related to aluminium companies and products, and has been developed using the following DNN modules:

 

alunetcom_main

 

Dynamic Forms from Datasprings

IndooGrid from IndooLab

Open Web Studio from R2integrated

News Articles from Ventrian

Live Tabs from Mandeeps

Navigation Suite from DNN360.net

SmokeRanch Ad Manager from Smoke Ranch Software

 

(Please be aware that the design is not mine - the layout and site structure was entirely designed by the client and my job was to implement all the functionality needed).

 

The site is also heavily using jQuery, Fancybox (a jQuery plugin for displaying images and/or html) as well as ImageGen, an image resizer originally developed for the Umbraco CMS that can function stand-alone in any .NET - based website.

 

Let's discuss where I used what and why:

 

Dynamic Forms from Datasprings

 

Dynamic Forms is being used for almost all end-user data submission tasks. This is a very powerful forms generator for DotNetNuke, which allows you to create data-driven forms, storing data either in the module's internal database schema or in any custom external table structure. I am using custom tables for companies, products and categories so I chose the second approach. You can easily load data to your forms as well as save data to your database via SQL queries or stored procedures. You can populate form fields using SQL, and this is especially useful with lookup data used in combo boxes or checkbox groups.  The module supports various goodies like injecting your own javascript, creating your own validations and launching events on form submission that can send email or write to the database. I used Dynamic Forms for the product and company data submission forms that are displayed to registered users.

 

alunetcom_companyform

 

 

Registered users can submit their company data. Each user can submit only one company, which is approved by the site's administrator. Companies must have a main activity and subactivities that fall under their main activity (it's a tree with 3 levels). After initial submission, the user must select a main activity and subactivities. (again, Dynamic Forms is used for the selection forms).

 

When this process is complete, the company must be approved by the site administrators. Users get an email when this is done. (Of course, administrators also get an email for each submitted company). This is achieved via Dynamic Forms post-submission events.

 

Afterwards, the user is able to submit up to a specific number of products for the company (currently 10, but it's as easy to change as a record in a database table) and assign each product to one of the subactivities they have defined for their company. Actually, the "main activity" acts only as a grouping category, while subactivities are the actual activities of the company.

 

alunetcom_productform

 

When editing the subactivity list, the forms present only subactivities that can be selected depending on the main activity chosen and do not let the user unselect a subactivity he has already assigned products to.

 

 

alunetcom_activities

 

Users have their own "control panel" where they can manage companies, products and activities. This works mainly with Open Web Studio, which we'll examine later on.

 

Dynamic Forms pros:

  • Very powerful
  • Datastore in own schema or custom database
  • Post-submission events (email, sql, etc.)
  • Field events (when you fill a field - you can hide/show sections depending on value etc.)
  • Great selection of controls - including data-driven comboboxes, listbox/radio groups, rich text editor, image and file upload controls.

Dynamic Forms cons:

  • Bugs arise when you try to push it too far
  • Somewhat expensive - you must make sure that you need it
  • Not very good / easy control on final layout
  • Image and file upload controls have many features (e.g. unique filenames, automatic thumbnail resizing) but need a lot of work to operate correctly.

Company website: www.datasprings.com

DotNetNuke Store: Click

 

Click here to read Part 2 (Open Web Studio)

Read more...

5/04/2011

Injecting CSS / Javascript on your page, revised

A long time ago, I had posted a method to include Javascript in your .ascx skin’s HEAD section. Things have evolved till then, DNN supports JQuery out of the box, and we need an easy way to inject scripts and css stylesheets in our page, taking care that core stylesheets and jquery are loaded BEFORE our scripts, so they can work correctly. This is especially important with JQuery plugins.
 
Latest DNN versions include two placeholder controls on the default.aspx page, with Ids CSS and SCRIPTS accordingly. These are the controls that can hold our scripts and stylesheets after all core stuff has been loaded, and we can have code inside our .ascx skins that uses these controls and injects stuff like jquery plugins or custom stylesheets inside our page’s HEAD section.
 
I’ve written some reusable code to do so, which, in this basic implementation, can be used to control what scripts and stylesheets are loaded per skin. Of course, you can customize it even more to control things even on a tab level.
 
The code has basically two functions, AddCSS and AddJS (what they do is obvious). You must override the Page_Init function and add your calls to the functions there.
 
Here’s the code. You can use it inline inside your .ascx page, preferrably right after your last @Register statement. 
 
 
<script runat="server">
''' <summary>
''' An enum which holds the various type of elements to be
''' injected in the HEAD section of our page
''' </summary>
''' <remarks></remarks>
Private Enum htmlHeadElementType As Integer
css = 1
javascript = 2
End Enum

''' <summary>
''' Add a CSS element to the HEAD section of our page
''' </summary>
''' <param name="csspath">The path to the CSS file</param>
Private Sub AddCSS(ByVal csspath As String)
AddHTMLHeadElement(csspath, htmlHeadElementType.css)
End Sub

''' <summary>
''' Add a JavaScript element to the HEAD section of our page
''' </summary>
''' <param name="jsPath">The path to the js file</param>
Private Sub AddJS(ByVal jsPath As String)
AddHTMLHeadElement(jsPath, htmlHeadElementType.javascript)
End Sub

''' <summary>
''' This is the actual function.
''' It addds the element to the HEAD section of our page.
''' </summary>
''' <param name="elementPath">The path to the file (css, js etc)</param>
''' <param name="elementType">a htmlHeadElementType corresponding
''' to the type of the element
''' we are adding to the HEAD section.
''' </param>
Private Sub AddHTMLHeadElement( _
ByVal elementPath As String _
, ByVal elementType As htmlHeadElementType)

Dim containerControl As Control

Select Case elementType

Case htmlHeadElementType.css

containerControl = Me.Page.FindControl("CSS")

Case htmlHeadElementType.javascript

containerControl = Me.Page.FindControl("SCRIPTS")

End Select

If Not containerControl Is Nothing Then

'Create our generic html control
Dim oLink As HtmlGenericControl

'Decide on what type of element to add
Select Case elementType

Case htmlHeadElementType.css

oLink = New HtmlGenericControl("link")
oLink.Attributes("rel") = "stylesheet"
oLink.Attributes("type") = "text/css"
oLink.Attributes("href") = elementPath


Case htmlHeadElementType.javascript

oLink = New HtmlGenericControl("script")
oLink.Attributes("language") = "javascript"
oLink.Attributes("type") = "text/javascript"
oLink.Attributes("src") = elementPath

End Select

'Add a script reference to the head section
If Not oLink Is Nothing Then
containerControl.Controls.Add(oLink)
End If

End If

End Sub


Private Sub Page_Init( _
ByVal sender As System.Object _
, ByVal e As System.EventArgs _
) Handles MyBase.Init

'Add the various files to the HEAD section of our page.
AddJS("/somepath/myscript.js")
AddCSS("/somepath/mystylesheet.css")
End Sub

</script>



Read more...

3/23/2011

Fix: Points not added to core DNN Maps module

The DNN Maps module is very simple and useful, but you may come across a problem: When you add points they are simply not added (and when you go to “data” later you see nothing on the list). This behaviour has been reported for module version 01.00.09.

 

The fix is very simple and I’m posting it here in order to facilitate anyone who’s been having the same problem:

 

Go to your DNN root folder and find

DesktopModules\Map\Sources\Dotnetnuke.Map.Standard.js

Change the return value in the mapWriteData function as follows.

 

from:

return encodeURIComponent(strvalue)
to:

return strvalue

 

And points will be added normally.

 

You may need to clear your browser’s cache in order to get it working.

Source: DNN Support Forums

Read more...

2/20/2011

Datasprings Dynamic Forms and image upload problems - a fix for specific scenarios

This may not interest lots of people, but if you're using the very powerful Datasprings' Dynamic Forms module, you may come across it.

 

The setup

 

Historically, there have been quite a few bugs with the image upload field in this module, especially when using in combined with the Preview functionality. I had the following setup:

 

Dynamic Forms module with more than one image upload field.

Preview ON

Thumbnail ON

Initial SQL rendering/bind ON

More than one image fields

 

Other attributes for image fields (not that it matters, just describing the full scenario):

Custom image folder

Save filename only

Save system generated unique name

 

I wanted to setup Dynamic Forms to let a user upload, let's say, four images on the same form. I needed to save those in my own database tables and load them from those tables, so I had initial SQL rendering/bind as well as an sql event to save the images back to my custom database table.

 

The problem

 

When you use your own sql query to get data for your form, (at least at versions 03.30 to 03.40) thumbnails do not get loaded the first time the form is loaded. Instead, you see the full-sized image (after all, that's what stored in the database).

 

If you cause a postback (e.g. by clicking on the "upload new image" link on another image field) then the image url is correctly prefixed with "thumb_" and it is presented in the correct dimensions.

 

The solution

 

To overcome this, I just added code to my select query that would put the "_thumb" prefix in place even if it wasn't there, like the following:

 

SELECT

  field1

, field2

, ...(other fields)

, case

  when ltrim(rtrim(isnull(myfield,''))) =''

  then ''

  else 'thumb_' + myfield

  end as myfield

, ...(other fields)

...(rest of query)

 

So this would always add the thumb_ prefix to the field "myfield".

 

This solved the problem with the initial form rendering. The first time a user sees the form, the thumbnails are shown instead of the full images.

 

The second problem

 

The first solution caused an additional problem: In the case of a postback, as I mentioned before, DF puts its own "thumb_" prefix in the images that are displayed on image fields! So if you attempt to click on "upload new image" in any fields, all the other fields will now have an image starting with "thumb_thumb_", and, since no filename exists that starts with this prefix, broken images will be all you'll see.

 

Please note that you'll see this only if you're using two or more image fields on the same form (or use any other postback that triggers this behaviour).

 

The additional solution

 

I found no way to circumvent this, since it's by design. So I had to resort to JQuery in order to correct the problem. I added a Custom Javascript (you can do that in the Module Configuration section) like the following one:

 

$(document).ready(function() {

    $('img[src*="/Portals/0/imageuploadfolder/"]').each(function(i,d) {
   
        $(d).attr("src",
                 $(d).attr("src").replace("thumb_thumb_", "thumb_")
                 );

    });
});

What it does is take all images that have an src attribute that contains the path /portals/0/imageuploadfolder (replace this with your path) and changes any double "thumb_" prefixes to a single one.

 

This dual workaround seems to work good so far. Now I know this is a very special situation (Dynamic Forms with more than one image field and initial sql rendering) but I hope someone benefits from this. Take care.

Read more...

12/09/2010

Hide those help icons, site-wide

Admit it. Those little questionmark help icons are ugly. Even if you replace the icon image with something more fancy, you still have to display them everywhere, even at the front-end (for example, the Feedback module uses them). And, if you’re developing for more than one language, that creates an awful lot of work for you, since you have to translate all texts that appear as tooltips when you hover over the icons.

 

There’s an easy way to hide them, though, and it’s site-wide. It will also apply to any third-party modules that use the dnn label control for providing labels for form controls.

 

Go to controls\labelcontrol.ascx and change the following line:

   <asp:image id="imgHelp"  runat="server" imageurl="~/images/help.gif" enableviewstate="False" />

 

to

   <asp:image visible="false" id="imgHelp"  runat="server" imageurl="~/images/help.gif" enableviewstate="False" />

 

That’s right. A visible=”false” attribute and the icons will disappear.

 

Also, if you notice that the labels are shifted to the left a bit (especially in IE), then go two lines above and change the line:

 

<span style="width:15px">

to

<span style="width:0px">

 

Or delete the span tag all together.

 

You can find a relevant thread on DotNetNuke.com with some more tips and tricks for the labelcontrol.ascx file here.

 

Have fun!

Read more...

11/17/2010

Using resource files in embedded user controls in skins

If you are editing ASCX skins, then you have probably been tempted to introduce a bit of reusability by creating custom user controls and embedding them to your skins. These can actually function like skin objects, but without all the fuss. I’ve found it easy to create a couple of those in cases where I may need, for example, a common footer for all my skins, but of course you can do a great deal more with them, like introduce functionality without having to copy all the code to every skin file you make.

 

In any case, you have to register your control inside your skin file like this (names of folders, portal and skin path and tagprefix/name are arbitrary):

 

<%@ Register TagPrefix="MyCompany" TagName="FOOTER" src="~/Portals/0/Skins/myskin/myskinobjectsfolder/footer.ascx" %>

 

And use it in your skin file like this:

 

<MyCompany:FOOTER id="myFooterId" runat="server" />

 

The question is: How do you localize these little bastards? Even if you add DNN controls on them, the ResourceKey property continues to get data only from the skin’s resource file, not the control’s.

 

Of course, you can use your skin’s resource file to get localized strings and other data but this isn’t very convenient since you’ll have to add data to every resource file corresponding to each one of your skins. And I’m not going to talk about app_globalresources either! :)

 

(If you don’t know that you can have resource files for your skins, yes, you can. You can have a skins/app_localresources folder and you can have .resx files there  and you can use the ResourceKey attribute in your DNN control tags to get localized data for your controls in your skins).

Well, based on this excellent post here about localizing embedded controls, I thought it would be good to summarize and simplify the process a bit. So here it goes:

 

You can give an embedded user control its own resource file by following two different approaches:

 

1. Make it inherit PortalModuleBase

2. Specify the resource file yourself and make the control use it.

 

Approach 1: Inheriting PortalModuleBase

You just have to add:

 

Inherits="DotNetNuke.Entities.Modules.PortalModuleBase"

 

in your control’s header.

E.g.

 

<%@ Control language="vb" AutoEventWireup="false" Explicit="True" Inherits="DotNetNuke.Entities.Modules.PortalModuleBase" %>

 

Doing so will give you access to the LocalResourceFile property, which will in turn point to the .resx file located in the app_localresources folder right under your control’s location.

 

But what .resx file, exactly? Not the one you expected.

 

The resx file will NOT follow the file name of your user control, but the ID by which it is called. If your control’s filename is MyFooter.ascx, and you call it with an ID of “myFooterId” then your resource file must be named “myFooterId.ascx.resx”.

Then, you can use code like this to get a localized value, for example, for the “Footer.Text” key:

 

<%=DotNetNuke.Services.Localization.Localization.GetString("Footer.Text", LocalResourceFile)

%>

Ugly, because you have to remember to always use the same ID and also because you can’t use two instances of your control in the same skin. Also, inheriting PortalModuleBase restricts your coding flexibility.

 

Approach 2: Forcing the user control to use a resource file

 

If you don’t want your user control to inherit PortalModuleBase but you still need it to have its own resource files, you can define a LocalResourceFile property yourself. This is written directly in the .ASCX file but, of course, you can put it in code-behind too. (sorry about the line breaks):

 

<script runat="server">
Public ReadOnly Property LocalResourceFile As String
     Get
         Return Me.TemplateSourceDirectory & _
     "/" & _
DotNetNuke.Services.Localization.Localization.LocalResourceDirectory & _
     "/" & _
     System.IO.Path.GetFileNameWithoutExtension(me.AppRelativeVirtualPath)
     End Get
End Property

Public Function GetLocalizedString ( _

ByVal key as string) as string
    Dim retVal as String
    retVal = _
DotNetNuke.Services.Localization.Localization.GetString( _
    key, LocalResourceFile)
    return (retVal)
End Function

</script>

 

What we did here was to create our own LocalResourceFile property. This tells the control to use a resource file that follows the file name of the control itself. So, if your control is named footer.ascx, your resource file will lie in app_localresources\footer.ascx.resx. If you need to specify a differently – named resource file, you just replace the GetFileNameWithoutExtension call with your own string. E.g. if you use “myownFooter” there, then there must be a myownFooter.ascx.resx file in the app_localresources folder.

 

In addition, we created a GetLocalizedString function which will just take the key and return the string from the resource file.

We can use this function in our user control to get localized data as follows:

 

<%=GetLocalizedString("Footer.Text")%>

 

This is much simpler and much more flexible.

Read more...

11/04/2010

How to quickly switch your "localhost" binding to another dnn site

Warning: This is for advanced users only.

 

The situation

  • You've got a development machine which has more than one DNN sites that you are currently working on.
  • You're using host headers on those sites to have multiple root-level DNN sites. (And you know the terms "host header" and "binding")
  • You are using IIS7 or higher with Windows 7 or Windows 2008. (I suspect it may work on Vista too, but who's still got Vista? :) ).

(Don't continue reading if the above don't match your case. One of the most common reasons for the above scenario could be that there are root-level urls inside your DNN site, generated by custom modules or just hard-coded into skins / modules, so you have to make your development PC believe that this is a root-level site and not a virtual directory of your default web site. Or, even worse, if you're working on a copy of a site that's already online and there are links and other stuff pointing to the WHOLE domain, you may have to alter your hosts file to make your PC believe that your local site is running on the exact same domain. If you're dealing with DNN a lot, you've probably seen that we don't live in a perfect world and such cases do exist.)

 

 

The problem

You need to configure some modules on your installations, but the modules you have installed can run without limits only on "localhost". Furthermore, some modules, (for example, IndooGrid) need you to buy a licence if you run them ANYWHERE else than localhost - even if it's a test / development machine. But you don't want to buy additional licences since you can do your development work on localhost.

 

What you need

You need a way to quickly switch between what you see when you type "localhost" on your PC's browser, so that you can work on the site of your choice each time.

 

Of course, you can do it by altering your sites' host headers in IIS but that wouldn't be quick. And, I won't say a word about using different virtual machines, each with one localhost site - I've seen it happen. :)

 

The solution

Let's suppose I've got two sites, let's say Site1 and Site2. These listen to their respective host headers, let's say site1.mypc.local and site2.mypc.local. You have configured your Site1 to listen to "localhost"  too. Currently, when you type "localhost" on your browser you see Site1.

 

Now, you either have configured your IIS to have an extra host header (*:80) for Site 1 or you have changed the location of your "default web site" to point to Site1's folder on your hard disk.

 

Do the following:

 

1. STOP your default web site (if that's what you're using to do the trick).

 

2. Go to all the sites that you need to implement the "switching" on, and add a portal alias for localhost so that they can respond to the call (if there is not one already). If you do so, an IISRESET would be good too, in order to avoid the dreaded "redirect loop" error that can happen from time to time.

 

3. Create a batch file with the following and save it as changehost.bat (or any name you choose):

 

@echo off

if "%1" == "site1" goto site1
if "%1" == "site2" goto site2
if "%1" == "" goto error

:site1
%windir%\System32\inetsrv\appcmd set site /site.name:site2 /-bindings.[protocol='http',bindingInformation='*:80:']
%windir%\System32\inetsrv\appcmd set site /site.name:site1 /+bindings.[protocol='http',bindingInformation='*:80:']
goto end

:site2
%windir%\System32\inetsrv\appcmd set site /site.name:site1 /-bindings.[protocol='http',bindingInformation='*:80:']
%windir%\System32\inetsrv\appcmd set site /site.name:site2 /+bindings.[protocol='http',bindingInformation='*:80:']
goto end

:error
@echo You have to provide a parameter!

:end

(Sorry for the wrapping, each call to appcmd should be on a single line.)

 

Let's explain what this batch file does. You call it like this (make sure you have administrator rights):

 

changehost site2

 

And you expect to see your "site2" responding to localhost instead of "site1".

 

What the batch file does is go to the matching section of the batch code, following the IFs. When it gets there, it does two things:

 

First, it uses appcmd.exe (an utility located in your windows directory\system32\inetsrv folder, which allows you to access several IIS properties from the command line) to add a binding to localhost (*:80) to the site you need and REMOVE this binding from any other of your sites that possibly has it. Essentially, it does what you would do by hand - go to iis, add a binding to the site of your choice, remove the binding from the previous site since you are not allowed to have two sites with the same host header.

 

The result is that you will always have one and only one site bound to localhost, and that you can change what site this is by just executing your batch file.

 

Some things to have in mind:

 

The parameter's values can be anything you want, as long as there is a section inside the batch file you can GOTO if you match a parameter value. I've used the same names as the names of the sites in order to have some consistency, but this isn't needed. "Foo" and "Poo" would do the same job, as long as there was a "Foo" section for site1 and a "poo" section for site2.

 

The /site.name switch needs the actual site's name as it is declared in IIS.

 

You must FIRST remove any possible localhost bindings (/-bindings switch) and add the localhost binding (*:80) last (/+bindings switch), otherwise you'll get an error and the binding won't be added because it will exist on another site.

 

If a site does not have a localhost binding, you'll get a message that it can't be found, like this one:

 

ERROR ( message:Cannot find requested collection element. )

Don't be alarmed, it's normal. Appcmd just tried to remove a non-existent binding, no problem.

 

If you try to run this script twice for the same site, you'll also get an error like this:

 

Cannot add duplicate collection entry of type 'binding' with combined key attributes 'protocol,bindingInformation' respectively set to 'http, *:80:'. )

 

This means that you tried to add a binding to *:80 to the site that already had it. That's perfectly normal, too, nothing to worry about, no changes will be made.

 

The example is for two sites, if you have three you'll have to adjust the number of sections, your IF statements and your appcmd calls accordingly. Remember, we're doing a simple thing: Attempting to remove localhost bindings from sites that may have it (only one will) and add a localhost binding to the site we want.

 

I hope this helps a bit. It worked for me, if anyone tries it, please let me know if you have succeeded.

 

Standard disclaimer / warning: Even if you are an advanced user, please use this information at your own risk. It's easy to mess things up (actually, the only thing you'll mess up is your bindings - you won't lose any data but it's enough to make your development sites unaccessible if you don't know what you're doing). I cannot be held responsible if any of the above information proves misleading or incorrect.

 

Happy localhosting! :)

Read more...
Related Posts with Thumbnails

Recent Comments

Free DotNetNuke Stuff

Free DotNet Videos

  © Blogger template The Professional Template by Ourblogtemplates.com 2008

Back to TOP