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...

10/22/2010

SupportedFeatures field, Search Indexer exceptions, oh my.

I had one of my usual strange problems tonight. The search indexer on a DNN web site I created some months ago would not index the entire site. I got suspicious and checked the event log, where there were pages after pages of general exceptions.

 

Most errors seemed to be something like:

System.ArgumentNullException: Value cannot be null. Parameter name: type at System.Activator.CreateInstance(Type type, Boolean nonPublic) at DotNetNuke.Framework.Reflection.CreateObject(String TypeName, String CacheKey) at DotNetNuke.Services.Search.ModuleIndexer.GetModuleList(Int32 PortalID)

 

Okay, this could not be the real error! I had to find the real cause.

 

First try - cleaning the search index in case it's been corrupted

 

The first thing I tried (and I advice you to do that before you try anything else) was to start with a clean search index, in case it was somehow corrupted. What you can do is the following (taken from this post):

 

If by accident the search index got corrupted, there will be no serach results displayed any further and you have to delete the search index tables manually. Follow these steps:

Login as Superuser ("host" by default)
In Host Menu select item "SQL"
Copy the following 4 lines and paste them into the text box:
truncate table {databaseOwner}{objectQualifier}SearchItemWordPosition
DELETE {databaseOwner}{objectQualifier}SearchItemWord
DELETE {databaseOwner}{objectQualifier}SearchWord
DELETE {databaseOwner}{objectQualifier}SearchItem
Activate the check box and hit Run
The search index will be rebuilt automatically by the scheduler.

 

(Although this is the right way, I've found that a DELETE {databaseOwner}{objectQualifier}SearchItem will be sufficient. If you don't have a special db owner or a specific object qualifier, a DELETE from SearchItem will do the same job, either on your SQL Server Management Studio or your Host-SQL page).

 

Also, you don't have to wait for the scheduler. Go to host-search admin and rebuild the index yourself.

 

Second try - checking and correcting the SupportedFeatures field on the DesktopModules table

 

Well, starting with a clean index didn't help, so I searched further. What I found surprised me. Let's see what this post says:

 

It seems that the desktopmodules table has a supportedfeatures field that should be set to 0 or higher.  But sometimes when modules are installed they end up with a -1 so this causes the error.  Setting the fields to 0 when they are -1 fixes it, like so:

UPDATE [databasename].[dbo].[DesktopModules]
   SET [SupportedFeatures] = 0
WHERE [SupportedFeatures] = -1

This is for SQL Server Express... don't know how it would work in the SQL page of DNN.

 

The solution

 

Well, I did a check on my own DesktopModules table. And suprise, surprise, two modules had a value of -1 for the SupportedFeatures field. As expected, these were the two modules that were generating the exceptions and blocked the indexer from indexing the whole site.

 

I did what the post said, and kaboom! Indexer worked perfectly, no more exceptions.

 

What the SupportedFeatures field actually does

 

What troubled me was what this field actually represents. I had values of 6, 7, 0 etc in other modules' records. So I dug a bit further and found this post:

 

This is a bit field where

2^0 = 1 indicates IPortable

2^1 = 2 indicates iSearchable

2^2 = 4 indicates iUpgradable

add those values for  combinations (i.e. 7 = 1 +2+4 = IPortable,iSearchable and iUpgradable)

 

So this solved the mystery. Those two modules were installed in a wrong way and got an invalid value of -1 for their SupportedFeatures field, which in turn caused the exceptions when the Search Indexer ran.

 

Where and when can this happen?

 

My DNN site was version 5.2.2, but as far as I understand this thing can happen on various DNN versions, depending on the modules installed and how well the installation process is executed. I can't say that exceptions from the Search Indexer are always due to this particular problem, so use this proposed solution with extreme caution if it happens to you, and always have a database backup handy!

Read more...

7/04/2010

(Not a) tutorial: Creating my first DNN scheduled task

In this post, I’ll describe my (successful) attempt to create a basic email queue functionality using a custom DNN scheduled task.

 

Why not a tutorial? Because my intention is to show you what I have done, without telling you that this is the right (or the only) way to do it, or demonstrating this as a techically complete and correct solution. This work is far from finished – it just covers basic functionality and is not usable out of the box. But if you’re struggling with DNN scheduled tasks, reading below may provide you with a way to get started.

 

I found great help in this excellent post here, which you should read for more information on creating your own scheduled tasks in DNN. Actually, this article here is a simple application of the tutorial I found in that post. Read it, it’ll be extremely helpful.

 

Let’s get to the point.

 

Purpose:

 

I had the need to notify custom groups of DNN registered users upon submission of some forms created either with the XMOD Pro or the Datasprings Dynamic Forms module. Although both modules support sending emails upon submission, they are restricted to sending emails to specific people, lists, or roles.

 

I needed something more complex, such as a database query based on several tables, in order to determine who would get the notification. Both modules allow issuing queries to the database upon submission, so it seemed right to create my own email queue, consisting of a database table (the queue) and a scheduled task to send the mails.


What’s more, a queue can help where there is a bunch of emails to be sent – let’s say you need to send 1k emails upon the next form submission – you don’t have to send them all at once. You just send a dozen each time the scheduled task is ran, until the queue is empty.

 

Database objects

 

So – what did I need first? Of course, a table to hold my queue.

 

CREATE TABLE [dbo].[DotSee_MailQueue]( 
[MailID] [int] IDENTITY(1,1) NOT NULL,
[FromUser] [nvarchar](500) NOT NULL,
[ToUser] [nvarchar](500) NOT NULL,
[Subject] [nvarchar](500) NOT NULL,
[BodyHtml] [nvarchar](max) NULL,
CONSTRAINT [PK_DotSee_MailQueue] PRIMARY KEY CLUSTERED
(
[MailID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY]

GO


Yes, it’s a silly table but enough for my basic functionality: From, To, Subject, Body and an id. All the necessary information to send an email.


Then I needed a stored procedure to retrieve information from that table:



CREATE PROCEDURE [dbo].[DotSee_GetMailQueueItems] 
AS
BEGIN
SET NOCOUNT ON;

Select top 10 [MailID],[FromUser],[ToUser],[Subject],[BodyHtml] FROM [dbo].[DotSee_MailQueue]

END

GO


Note: I prefix my tables with “DotSee_” since this is the name I have selected for my custom work (namespaces included, as you’ll see below). Don’t give it serious attention, it’s just my convention.


Setting up a project for my scheduled task


There are more than one ways to begin creating a scheduled job for DotNetNuke using Visual Studio, but there is a common denominator: Your job MUST be a DLL file in DotNetNuke’s bin directory. So you can go either with a Web Application Project or with a Class Library. I chose Class Library. Here’s what I did:



  1. Created a new Class Library project.

  2. Added a reference to DotNetNuke.dll from an existing DNN installation (and set Copy Local to false)

  3. Added a reference to Microsoft.ApplicationBlocks.Data from the same DNN installation (in order to be able to use DNN’s own DAL for interacting with the database)

  4. Set the build path INSIDE DNN’s bin folder to save me time copying and recopying the dll.

  5. Went to the project’s properties and deleted the contents of the root namespace field. Yes, it should be empty for your dll to work. Don’t miss that.



sche1


Regarding (4), I did this work on a virtual machine, where I had a DNN installation and my class library project on the same computer. I suggest you follow some similar approach, since it is very easy to test and debug (you can debug only by attaching to the w3wp.exe process, but it’s better than nothing).


Writing the code


Two more things you must know: Your custom class must inherit DotNetNuke.Services.Scheduling.SchedulerClient and the only method that is called by DNN’s scheduler is DoWork().


So here’s my custom scheduler class:



Imports DotNetNuke.Services.Scheduling 
Imports DotNetNuke.Services.Exceptions

Imports System
Imports System.Web
Imports System.IO

Namespace DotSee.DnnScheduledMailNotifier

Public Class Mailer
Inherits SchedulerClient

' requires a special constructor which
' accepts a ScheduleHistoryItem
Public Sub New( _
ByVal objScheduleHistoryItem _
As ScheduleHistoryItem)

MyBase.New()
Me.ScheduleHistoryItem = objScheduleHistoryItem

End Sub

Public Overrides Sub DoWork()
Try
' perform some tasks
MailWorker.DoWork()

' report success to the scheduler framework
ScheduleHistoryItem.Succeeded = True

Catch ex As Exception

ScheduleHistoryItem.Succeeded = False
ScheduleHistoryItem.AddLogNote _
("EXCEPTION" + ex.ToString())
Errored(ex)
Exceptions.LogException(ex)

End Try
End Sub
End Class

End Namespace


Apart from the MailWorker.DoWork() line (code coming right below), the class can be used as a template for writing your own scheduled tasks. (You will use your own namespace, of course). I put my specific code in a different class so that it would be easy to make the distinction between code needed for my specific need and the “template” code that every DNN scheduled task needs.


Here’s my MailWorker class (DotNetNuke.dll referenced was version 5.2.1, GetHostSettingsDictionary may not work with 4.x):



Imports DotNetNuke.Services.Mail
Imports DotNetNuke
Imports DotNetNuke.Data
Imports DotNetNuke.Entities.Host


Namespace DotSee.DnnScheduledMailNotifier


Public Class MailWorker

Private Sub New()

End Sub

Public Shared Sub DoWork()

Dim d As Dictionary(Of String, String)
d = Host.GetHostSettingsDictionary()

Dim dnp As DataProvider
dnp = DataProvider.Instance
Dim dr As IDataReader

Dim ids As New List(Of Int32)

dr = dnp.ExecuteReader( _
"DotSee_GetMailQueueItems", Nothing)

If Not dr.Read() Then Return

While dr.Read

Mail.SendMail( _
dr("fromuser").ToString _
, dr("touser").ToString _
, "" _
, dr("subject").ToString _
, dr("bodyhtml").ToString _
, "" _
, "" _
, d("SMTPServer") _
, d("SMTPAuthentication") _
, d("SMTPUsername") _
, d("SMTPPassword") _
)

ids.Add(CType(dr("mailid"), Int32))

End While

For Each id In ids
dnp.ExecuteSQL( _
String.Format( _
"DELETE FROM DotSee_MailQueue WHERE mailid={0}" _
, id.ToString))
Next

End Sub

End Class

End Namespace


What it actually does (in a brutal way, I should add) is check the table, get 10 items at a time (via the DotSEe_GetMailQueueItems stored procedure defined earlier), send an email for each of the items and then delete the corresponding records from the table. (Ok, I know the way it does it sucks a bit, but It’s only quick and dirty functionality, remember).


So when I compile this beauty, my DotSee.DNNScheduledMailNotifier.dll goes inside DNN’s bin folder, and now what I have to do is tell DNN that this is a scheduled task and that the scheduler should include it. Here’s how:



  1. Host-Schedule and click “Add Item to Schedule”.

  2. Set friendly name to whatever you want

  3. Full class name and assembly should be your assembly name and your class name (the class containing the DoWork method) combined, as in AssemblyName.Classname. My Assembly’s name is DotSee.DnnScheduledMailNotifier and my class name is Mailer, so the full name is DotSee.DnnScheduledMailNotifier.Mailer.

  4. Check the “schedule enabled” checkbox

  5. Time lapse MUST have some value or else the task will not run at all.



sche2



That’s it! The task is ready to run. I tested it by manually inserting records into the DotSee_MailQueue table and it worked well, even with hundreds of records, sending 10 emails every 5 seconds.


So now I have my basic system ready. When I need to send emails to groups of people not defined by roles or other DNN constructs, I just have to write a good SQL query that fills the table with recipients that correspond to the criteria I need every time and presto!


Of course, there’s plenty of room for configuration / parameterization (for example, the number of emails sent each time or the type of email – html or plain text, or even a check for whether delivery was successful). But I guess you got the idea.


Thank you for your time.

Read more...

4/24/2010

ModuleMaster, an admin module that was really missing from DNN

ModuleMaster is what I was looking for (and often considered developing myself). It can link modules – along with their settings – on the pages of your choice. Simple as that, but DNN doesn’t give you this option – you can either mark a module as “displayed in all pages”, in which case you only have linked modules to your original one – meaning that if you change something in one, it’ll change in every linked module, or copy the module by hand and then apply settings by hand (IPortable is not always well-implemented).

 

This module is a time-saver, especially for sites that have a number of pages already developed (and more especially if you’re using templates to create new pages). Imagine the scenario where the client asks you to apply a new text/html module, for example, in about 500 pages that you have already created, demanding to be able to edit each one of them independently of the others and needing to use a specific container. Count the DAYS it’ll take you to do it by hand. EDIT: Unfortunately, as I realised later, the module just creates “shadow” copies – i.e., links and not new instances, but remains useful whatsoever.

 

If anybody tries it, please post a comment here. I haven’t tried it yet, but looks very promising and it already has a couple of good reviews on SnowCovered.

Read more...

3/26/2010

Using page titles instead of names on the DNN breadcrumb skinobject

Simple, but useful. A fellow developer needed to use the page title (and not the page name which is the default) on the breadcrumb skinobject of his skin. The reason was that his menu had limited real estate so he had to keep names short, but he needed to give more information on the navigation breadcrumb.

 

The solution is just so simple! In your skin’s .ascx file, you’ll see a definition like the following:

 

<dnn:BREADCRUMB runat="server" id="dnnBREADCRUMB"  CssClass="Breadcrumb" RootLevel="0" Separator="&nbsp;>&nbsp;" />

 

If you add the following attribute:

UseTitle="True"

 

You’ll have something like this:

<dnn:BREADCRUMB runat="server" id="dnnBREADCRUMB"  CssClass="Breadcrumb" RootLevel="0" Separator="&nbsp;>&nbsp;" UseTitle="True" />

 

And then your breadcrumb will be using page titles instead of names.

 

Enjoy.

Read more...

Quick snippet: Show first n characters of a string without cutting words in half with OWS

Just a quickie for those of you who use the free Open Web Studio DNN RAD environment: I needed to show only the first n characters of a string that I got via a query in OWS, but wanted to “stop” display at the end of a word, (that is, not being very strict on the n character limit).

 

If our field is, for example, called “description” and the character limit is, let’s say, 100, and we need to insert three dots (…) after the end of the string if it’s only part of the original, here’s the function (be sure to write it on a single line):

 

{IIF, "[FORMAT,"[description]", {LENGTH}]>100", "[FORMAT,"[description]", {LEFT:[FORMAT,"[FORMAT, "[description]", {LEFT:100}]",{LASTINDEXOF: }]}]...","[description]"}

 

Change the field name, the number of characters and the three dots at the end to whatever suits you.

 

Of course there are several ways to achieve that via your SQL query itself, but that’s a quick way to do it in OWS if you can’t modify the query (when you’re using a view that is used somewhere else, for example).

 

Enjoy.

Read more...

3/15/2010

Free search/replace admin module from Engage Software

f3 This one is cute. It,s called Engage F3 and it lets you search and replace text in Text/Html and Engage Publish modules, site-wide. It’s intended for admin use only.

 

You can download it for free from Snowcovered.

Read more...

2/25/2010

Free HTML Template Repeater module – TEXT/HTML replacement

A simple idea, but someone had to conceive it and develop it. We often need to repeat a template made for a text/html module, without necessarily having the data stored in the database. Moreover, we need to give editors an easy way to enter data without having to know HTML to edit the markup.

 

There already are solutions, like Open Web Studio (formerly Listx) and the core Form and List module (formerly User Defined Table) module which can achieve similar results, but in a more complex and time-consuming way for the developer when it comes to simple, non-database-bound implementations like the one described above.

 

Enter HTML Template Repeater, created by Software’s Edge. You can define a template just as you would normally do in a Text/HTML module, defining placeholders for “variables” that you need to be replaced when the module output is rendered. The module provides you with a form to create “records” based on those variables and then you can repeat your template for as many records as you have entered. It even has paging functionality!

 

You can download the module from Codeplex, or see a video tutorial here. Another blog post / tutorial on the module is here.

Read more...

ERD Data Model and Meta Data Dictionary for DNN CE 5.2.2

Mr. Chodnicki from R2integrated has created a very useful data model and meta data dictionary for DotNetNuke Community Edition 5.2.2, which you can download by clicking here.

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