A former twenty something in technology

All posts in DotNetNuke

Reset your DNN Password

Categories: DotNetNuke
Comments: 3

UnlockDNN-ss1
I’ve previously shown how in DNN you could recover your DNN host password. In present times DNN has shifted it’s default password policy in DNN 7.1.0 to use a hash password.

What this means is that you can no longer simply see what the current password is for any of your users. While this is great for security it’s a problem if you’ve forgotten your password and smtp is down.

Expanding upon my previous script I’ve created a single page utility application that will allow you to:

  • See top 25 super users.
  • Change any users password.
  • Create a new super user.
Unlock DNNDownload Now

Now just save this file to the root of your DNN application (beside the default.aspx) and happy resetting.

Something that I thought was missing from DNN’s DAL2 was a generic implementation of it’s repository base class that you could inherit from instead of repeating more boiler plate.

Ultimately why this is important should be apparent when you see the implementation below. But spoiler alert: It’s very little code!

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;

namespace Components.Data
{
    public abstract class RepositoryImpl<T> : IRepository<T> where T : class
    {

        public virtual void Delete(T item)
        {
            using (IDataContext db = DataContext.Instance()) {
                dynamic repo = db.GetRepository<T>();
                repo.Delete(item);
            }
        }

        public virtual void Delete(string sqlCondition, params object[] args)
        {
            using (IDataContext db = DataContext.Instance()) {
                dynamic repo = db.GetRepository<T>();
                repo.Delete(sqlCondition, args);
            }
        }

        public virtual IEnumerable<T> Find(string sqlCondition, params object[] args)
        {
            IEnumerable<T> list = default(IEnumerable<T>);
            using (IDataContext db = DataContext.Instance()) {
                dynamic repo = db.GetRepository<T>();
                list = repo.Find(sqlCondition, args);
            }
            return list;
        }

        public virtual IPagedList<T> Find(int pageIndex, int pageSize, string sqlCondition, params object[] args)
        {
            IPagedList<T> list = default(IPagedList<T>);
            using (IDataContext db = DataContext.Instance()) {
                dynamic repo = db.GetRepository<T>();
                list = repo.Find(pageIndex, pageSize, sqlCondition, args);
            }
            return list;
        }

        public virtual IEnumerable<T> Get()
        {
            IEnumerable<T> list = default(IEnumerable<T>);
            using (IDataContext db = DataContext.Instance()) {
                dynamic repo = db.GetRepository<T>();
                list = repo.Get();
            }
            return list;
        }

        public virtual IEnumerable<T> Get<TScopeType>(TScopeType scopeValue)
        {
            IEnumerable<T> list = default(IEnumerable<T>);
            using (IDataContext db = DataContext.Instance()) {
                dynamic repo = db.GetRepository<T>();
                list = repo.Get<TScopeType>(scopeValue);
            }
            return list;
        }

        public virtual T GetById<TProperty>(TProperty id)
        {
            T item = default(T);
            using (IDataContext db = DataContext.Instance()) {
                dynamic repo = db.GetRepository<T>();
                item = repo.GetById<TProperty>(id);
            }
            return item;
        }

        public virtual T GetById<TProperty, TScopeType>(TProperty id, TScopeType scopeValue)
        {
            T item = default(T);
            using (IDataContext db = DataContext.Instance()) {
                dynamic repo = db.GetRepository<T>();
                item = repo.GetById<TProperty, TScopeType>(id, scopeValue);
            }
            return item;
        }

        public virtual IPagedList<T> GetPage(int pageIndex, int pageSize)
        {
            IPagedList<T> list = default(IPagedList<T>);
            using (IDataContext db = DataContext.Instance()) {
                dynamic repo = db.GetRepository<T>();
                list = repo.GetPage(pageIndex, pageSize);
            }
            return list;
        }

        public virtual IPagedList<T> GetPage<TScopeType>(TScopeType scopeValue, int pageIndex, int pageSize)
        {
            IPagedList<T> list = default(IPagedList<T>);
            using (IDataContext db = DataContext.Instance()) {
                dynamic repo = db.GetRepository<T>();
                list = repo.GetPage<TScopeType>(scopeValue, pageIndex, pageSize);
            }
            return list;
        }

        public virtual void Insert(T item)
        {
            using (IDataContext db = DataContext.Instance()) {
                dynamic repo = db.GetRepository<T>();
                repo.Insert(item);
            }
        }

        public virtual void Update(T item)
        {
            using (IDataContext db = DataContext.Instance()) {
                dynamic repo = db.GetRepository<T>();
                repo.Update(item);
            }
        }

        public virtual void Update(string sqlCondition, params object[] args)
        {
            using (IDataContext db = DataContext.Instance()) {
                dynamic repo = db.GetRepository<T>();
                repo.Update(sqlCondition, args);
            }
        }
    }
}

What this class will allow you to do is create a simple class that inherits RepositoryImpl and gain access to all the CRUD operations.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using DotNetNuke.Data;

namespace Components.Data
{

    public interface IAnyEntityRepository : IRepository<AnyEntityName>
    {
    }

    public class AnyEntityRepository : RepositoryImpl<AnyEntityName>, IAnyEntityRepository
    {
    }
}

Additionally this structure will allow you to use dependency injection like Ninject to inject the concrete class using the IAnyEntityRepository interface.

Dependency injection (DI) has several advantages that I myself haven’t fully wrapped my head around all the use cases. But this article isn’t going to be focused on explaining why DI is important but rather how you can implement it in DNN.

Getting Started

Using nuget you can install ninject into your module.

Install-Package ninject

Searching the internet you may find a package called Ninject.Web and may find yourself thinking this is exactly what I need!. Unfortunately this extension assumes that you have a standard user control or access directly to the page and does not allow you inherit from PortalModuleBase

The primary abstraction in Ninject.Web is the creation of a KernelContainer which is a static class to broker all your DI interactions. So you will need to create that class.

using Ninject;

sealed class KernelContainer
{

     private static IKernel _kernel;

     public static IKernel Kernel {
          get { return _kernel; }

          set {
               if (_kernel != null) {
                    throw new NotSupportedException("The static container already has a kernel associated with it!");
               }

               _kernel = value;
          }
     }

     public static void Inject(object instance)
     {
          if (_kernel == null) {
               throw new InvalidOperationException(String.Format("The type {0} requested an injection, but no kernel has been registered for the web application. Please ensure that your project defines a NinjectHttpApplication.", instance.GetType()));
          }

          _kernel.Inject(instance);
     }
}

Next we must new up an instance of the kernel by hooking into the application start pipeline. An easy way to do this is with the WebActivatorEx library that comes with ninject. This will fire once at application start and create a static instance of your KernalContainer and assign a new instance of StandardKernel. Finally you write up or bind if you will all of your interfaces to their concrete classes.

[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(Components.DI.Bindings), "RegisterServices")]
namespace Components.DI
{
	public static class Bindings
	{
		public static void RegisterServices()
		{
			KernelContainer.Kernel = new StandardKernel();
			KernelContainer.Kernel.Bind<IDataRepository>().To<DataRepository>().InSingletonScope();

		}
	}

}

After that we need to tell the KernelContainer that our module user control may have classes that need to be injected. This is done by adding an abstraction to the PortalModuleBase. I call mine CustomModuleBase but you can call yours whatever you like.

using System.Web.UI;
using DotNetNuke.Entities.Modules;

public class CustomModuleBase : PortalModuleBase
{
		public CustomModuleBase()
		{
			KernelContainer.Inject(this);
		}
}

Now you can utilize the ninject inject attribute on any constructor, method, or property in your class and it will inject that class into into the interface.

public class Main : CustomModuleBase
{
	[Inject()]
	public IDataRepository_repo { get; set; }
}

Server Side Caching in DotNetNuke

Categories: DotNetNuke
Comments: No

The most expensive bottleneck in your application is asking for data from your SQL database. The fact of the matter is that it doesn’t matter is it doesn’t how many rows of data you request, the sheer request itself is costly.

The function below illustrates how easy it is to cache an object in DotNetNuke. It’s as simple as calling a standard function; checking in the cache if the value already exists; if not go get the data and then add it to the cache; return the cached object.

public static int GetInfoObject(int contentId)
{
	var cache = DataCache.GetCache(Constants.ModuleCacheKey + Constants.ContentType + contentId.ToString());
	if (cache == null)
	{
		var timeOut = 20 * Convert.ToInt32(Host.PerformanceSetting);
		cache = controller.GetInfoObject(contentId);
	if (timeOut > 0 & cache != null)
	{
		DataCache.SetCache(Constants.ModuleCacheKey + Constants.ContentType + contentId.ToString(), cache, TimeSpan.FromMinutes(timeOut));
	}
	}
	return cache;
}

When you should cache an object

Any time you will need to request that action more than once in a user single request
Any time that data is likely not going to change for several user requests
Any time you are sorting or filtering the same collection of data multiple times.
When not to cache

When the amount of data exceeds the amount of memory on your server.
Data is only going to be access by one person every few days.
The point is that almost all data should be cached in some shape or form. If your application contains a forum with millions of entries then you obviously can’t cache them all. But perhaps then consider only caching the top 100 posts that 90% of your users are going to see every time they hit the homepage of your forum.

Reminders when caching

When adding or updating a row in your database. Make your SQL provider call as normal but have your business logic layer update that object into your cache as well. This will eliminate the need to make another return trip back to the database to get the same data.
DotNetNuke caching provider is capable of updating all web heads automatically with any changes to the cache
In my next blog I’ll discuss the benefits of client side caching.

DotNetNuke SuperFan Recap 2011

Categories: DotNetNuke
Comments: No

I had a blast during the voting process with Clint, Richard, Gifford, Ralph and all the others who entered the contest. I was really nervous that Clint was going to beat me so I enlisted everyone I knew to check out DotNetNuke and vote for me every chance they got. Even then I was still worried! By the time we got to DNN World I was just excited about all the cool things that we did with this contest and the friendships I made. I’m just glad DNN cake was pre-contest or I would have lost for sure!

Special thank you goes out to Will Strohl who not only put on the contest but was an amazing host during my visit to DNN Corp HQ. Had such a great time traveling through Silicon Valley and seeing all the other local companies like Apple, Yahoo, and Google

During my visit to DNN’s HQ in San Mateo I got to sit down and really talk tech and the future of DotNetNuke. Chief Marketing Officer Mitch Bishop’s vision on showcasing what DNN has to offer to everyone in the community is amazing and I can’t wait to see all those ideas come into reality.

Joe Brinkman was even on hand to sit down w/ me and talk about what the cloud has to offer for DotNetNuke and ways myself and everyone can get the most out of it.

What I found the most inviting is how passionate everyone who is working on DotNetNuke is, everyone from HR to sales down to the accounts who have a great spirit and joined into all the festivities. Including some DNNFoos which Will Strohl and I dominated Chris Hammond and Richard Dumas.

I also got to see some of the other office tech equipment in the conference room where I video linked to Scott Willhite the Directory of Community Programs and Co-Founder of DotNetNuke. It was nice to get a real in depth look at the beginning of DNN .

I hope DNN Corp plans on holding the DNN Superfan again next year at the DNN World conference and I would love the opportunity to pass the crown to the next Super Fan.

DotNetNuke ships with a URL provider that over the years has gone through several updates in how it can handle URLs

Original Method – Default.aspx?TabId=52

Second Version – /Home/TabId/52/Default.aspx

Human Friendly (Current Default) – /Home.aspx

The new human friendly is great but still requires that the user append the .aspx to the end of the page names.

Case Study

Lets say you have a business card or promotion and you want to advertise a specific page on your site that people could actually type in

“www.domain.com/promo”

Typically what you would do is FTP into your DotNetNuke root directory, create a folder called promo and then create a default.htm page to redirect to www.domain.com/promo.aspx

Rewriting the URL

With IIS7 and Url Rewriting you can now add some lines to your web.config file and handle urls without the .ASPX extension. Below is an example of such a rewrite rule and you will place this inside of your node.

<rewrite>
    <rules>
        <rule name="sanitize aspx">
          <match url="(^.*)" />
          <conditions logicalGrouping="MatchAll">
            <add negate="true" input="{REQUEST_FILENAME}" matchType="IsFile" />
            <add negate="true" input="{REQUEST_FILENAME}" matchType="IsDirectory" />
            <add input="{URL}" pattern="(.*).(.*)" negate="true" />
          </conditions>
          <action url="{R:1}.aspx" type="Rewrite" />
        </rule>
    </rules>
</rewrite>

This code works by first making sure that a file or directory with the /promo does not already exist. If it does then the physical file will take precedence over the rewrite rule. Second it will then take the domain url and append the .aspx to the end if it does not exist. So when you type in /promo behind the scenes it is serving up the page /promo.aspx. This is done without redirecting or changing the url.

Now all your URLs in DotNetNuke can be simple and even more pretty.

Update 1/25/2012: Here is an alternative version if you want to do 301 redirects to the page name vs rewriting the url.

<rewrite>
    <rules>
        <rule name="sanitize aspx" stopProcessing="true">
          <match url="(^.*)" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
            <add input="{URL}" pattern="(.*).(.*)" negate="true" />
          </conditions>
          <action type="Redirect" url="{R:1}.aspx" redirectType="Permanent" />
        </rule>
    </rules>
</rewrite>

Recover Your DotNetNuke Host Password

Categories: DotNetNuke
Comments: 20

Many times users will only have the portal admin account and not the host account which contains the highest level of permissions and allows for installing modules and overall portal maintenance.

One of the most common issues website owners have is they outsource the setup and maintenance of their website then for one reason or another they need to bring in another developer and the first thing that developer will ask is: “What is your HOST password?”.

If you don’t have the answer to that question don’t worry there are a few ways to recover this password. The long way is to find a user whos password you do know replace the encrypted password and salt key of the host user with the user you do know. That works fine and is useful and all but requires a firm understanding of using SQL and the inner workings of DNN. But there is a better way.

Forgot Password system will not work.

DotNetNuke for security reasons will not return a password in the forgot password system when sending emails to the host user accounts.

 

I have FTP access to my website.

If you have FTP access to your website you can recover your host password with a little bit of coding. Follow the steps below and we’ll create a webpage that will return your host password.

  • Create a new text file called RecoverPassword.aspx
  • Paste in the follow code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
<%@ Page Language="C#" %>
<script runat="server">
void Page_Load(object sender, System.EventArgs e)
{
DotNetNuke.Entities.Users.UserInfo uInfo =
DotNetNuke.Entities.Users.UserController.GetUserById(0, 1);
if (uInfo != null)
{
string password =
DotNetNuke.Entities.Users.UserController.GetPassword(ref uInfo, String.Empty);
Response.Write("Password: " + password);
}
else
{
Response.Write("UserInfo object is null");
}
}
</script>
<html>
<head>
<title>Recover Password</title>
</head>
<body>
</body>
</html>
view raw file1.cs This Gist brought to you by GitHub.
  • Now upload the file to the root of your DotNetNuke installation and visit the page.
  • Copy the password and REMEMBER to delete this page IMMEDIETELY so you don’t forget and your host password is exposed.

Slight variations to this script can be made if the developer removed the default host user account or you simply want to retrieve the password for another user.

This will be a quick post. If anyone is like me and using the 4.1 version of the core DotNetNuke blog module than you’ve probably noticed that the “Written by:” is blank when viewing an individual blog entry.

I did a little debugging and found the problem in the ViewEntry.ascx.vb on Line 119

If m_oBlog.ShowFullName Then
lblUserID.Text = m_oBlog.UserFullName
Else
lblUserID.Text = m_oBlog.UserName
End If

This needs to be changed to:

If m_oBlog.ShowFullName Then
lblUserID.Text = m_oEntry.UserFullName
Else
lblUserID.Text = m_oEntry.UserName
End If

I’ve provided the details and a link to a fixed version of the DLL on codeplex

But if you you don’t want to do that. You can use a little jQuery magic like I originally did to fix the problem until an official update is released.

<script type="text/javascript">

jQuery(document).ready(function(){
jQuery(".blog_author").html("Written by: Jonathan Sheely");
});
</script>

[Update: 7/12/2011] Original DLL posted to Codplex required DNN version 5.6.2. I’ve updated codeplex with a newer DLL that only requires DNN 4.7.0 or later. You can also download that DLL here from my blog

How to Backup DotNetNuke

Categories: DotNetNuke
Comments: No

This guide is for those who have a dedicated server and need to backup your DotNetNuke websites on a regular basis to prevent a disaster.

There is plenty of software out there that will take care of backing up your website but the problem is it is either A. Too expensive or B. Doesn’t backup everything you need.

Here is how I backup my websites and you can take some of these steps and bend them into what works best for you. These methods utilize simple command line and batch scripts to accomplish everything you’ll need. You will also need to install 7-Zip to handle the compression

Step 1: Backing up your databases

This script was primarily written for SQL Express in order to give you a scheduled backup that Full SQL has but is just as viable to use with the full version of SQL. The result of this script will leave you with a directory of .BAK files of every database in your installation with a date stamp.

DECLARE @BackupFile varchar(255), @DB varchar(30), @Description varchar(255), @LogFile varchar(50)
DECLARE @Name varchar(30), @MediaName varchar(30), @BackupDirectory nvarchar(200)
SET @BackupDirectory = E:\SQL Data\Backup\'
--Add a list of all databases you don't want to backup to this.
DECLARE Database_CURSOR CURSOR FOR SELECT name FROM sysdatabases
WHERE name <> 'tempdb' AND name <> 'model' AND name <> 'Northwind'
OPEN Database_Cursor
FETCH next FROM Database_CURSOR INTO @DB
WHILE @@fetch_status = 0

BEGIN
SET @Name = @DB + '( Daily BACKUP )'
SET @MediaName = @DB + '_Dump' + CONVERT(varchar, CURRENT_TIMESTAMP , 112)
SET @BackupFile = @BackupDirectory + + @DB + '_' + 'Full' + '_' +
CONVERT(varchar, CURRENT_TIMESTAMP , 112) + '.bak'
SET @Description = 'Normal' + ' BACKUP at ' + CONVERT(varchar, CURRENT_TIMESTAMP) + '.'

IF (SELECT COUNT(*) FROM msdb.dbo.backupset WHERE database_name = @DB) > 0 OR @DB = 'master'
BEGIN
SET @BackupFile = @BackupDirectory + @DB + '_' + 'Full' + '_' +
CONVERT(varchar, CURRENT_TIMESTAMP , 112) + '.bak'
SET @Description = 'Full' + ' BACKUP at ' + CONVERT(varchar, CURRENT_TIMESTAMP) + '.'
END
ELSE
BEGIN
SET @BackupFile = @BackupDirectory + @DB + '_' + 'Full' + '_' +
CONVERT(varchar, CURRENT_TIMESTAMP , 112) + '.bak'
SET @Description = 'Full' + ' BACKUP at ' + CONVERT(varchar, CURRENT_TIMESTAMP) + '.'
END
BACKUP DATABASE @DB TO DISK = @BackupFile
WITH NAME = @Name, DESCRIPTION = @Description ,
MEDIANAME = @MediaName, MEDIADESCRIPTION = @Description ,
STATS = 10
FETCH next FROM Database_CURSOR INTO @DB
END
CLOSE Database_Cursor
DEALLOCATE Database_Cursor

Now that you have the ability to backup all your databases. We need to schedule this to run on a regular basis and clean up old versions so we don’t end up filling up our entire hard drive. For this step I’ve created a batch file that will

  1. Runs SQLCMD which comes with MS SQL connecting to (period = localhost. Your installation may be at .\SQLEXPRESS instead) your SQL installation and run the above SQL script
  2. Utilizes 7-Zip to go through every .BAK file in this directory and compress it to save space
  3. Deletes the no longer needed .BAK files
  4. Removes any file in this directory that is longer than 14 days.
sqlcmd -S . -i "E:\SQL Data\Scripts\Backup_All_Databases.sql"
forfiles /p "E:\SQL Data\Backup" /s /m *.bak /c "CMD /c C:\Progra~1\7-zip\7z.exe a -tzip -mx=10 @PATH.zip @PATH"
forfiles /p "E:\SQL Data\Backup" /s /m *.bak /c "CMD /C del /Q @FILE"
FORFILES /p "E:\SQL Data\Backup" /s /m *.* /d -14 /c "CMD /C del /Q @FILE"

All that’s left now is to create a Scheduled Task to run the above batch file every night and you have SQL backups!

Step 2: Backing up your website

An important step that is OFTEN overlooked is backing up the website files associated with your website AS WELL as the databases. Backing up your websites can be just as easy as the databases. All you need is another batch file:

  1. Gets a date stamp in the format of 20110514 to use to append to file names
  2. Gets a list of all the directory names where your websites are stored. (Recommend that all your websites be stored in a directory by their domain name. ex: C:\webs\twentytech.net”). This will then send that directory to 7-Zip for compression and output the results of that zip file to your C:\Backup directory
  3. Removes any file in any of the sub directories that is older than 14 days.
for /f "tokens=2-4 delims=/ " %%a in ('date /t') do (set mydate=%%c%%a%%b)
for /f %%a IN ('dir C:\webs\ /b /ad') do C:\Progra~1\7-zip\7z.exe a -tzip -mx=10 "C:\Backups\%%a\%%a_%mydate%.zip" "C:\webs\%%a"
forfiles /p "C:\Backups" /s /m *.* /d -14 /c "CMD /C del /Q @FILE"
Like before just create a scheduled task to run this batch file every night and you will have a 14 day backup of your website.