By Jonathan Sheely on
2/4/2012 10:52 PM
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.
|
By Jonathan Sheely on
12/21/2011 2:26 PM
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>
|
By Jonathan Sheely on
6/30/2011 11:49 AM
I decided to try and solve a few problem with DotNetNuke module development when utilizing javascript that continue to torture me in both performance and organization.
The main issue is how to handle Javascript functions when you have multiple modules of the same type on the page. DotNetNuke core has made strides in this by extending methods to the ClientAPI by way of RegisterClientScriptBlock. The RegisterClientScript block will allow you to link to an external javascript file which will then get added to the of the document. The files are inserted by key and if you add two modules on a page it will simply not add another instance of that object if that key already exists.
The problem I have with this is when I need to have client side variables that are keyed to specific modules. I solved this dilemma by adding javascript variables to the UserControls .ascx files accessing public properties like <%= ModuleId%>. However this started causing me scaling problem when I needed to call functions to access module specific variables. I needed a way to merge the RegisterClientScriptBlock method with Ascx file specific variable code.
Enter the Singleton class for Javascript.
function CG_AllItems() {
var instance = [];
this.singletonInstance = null;
var getInstance = function () {
if (!this.singletonInstance) {
this.singletonInstance = createInstance();
}
return this.singletonInstance;
}
var createInstance = function () {
return {
add: function (moduleId) {
var obj = new Object
obj.moduleId = moduleId;
obj.Init = function () {
jQuery(document).ready(function () {
alert(this.moduleId);
});
}
instance.push(obj);
return instance;
},
instance: function (id) {
for (i = 0; i < instance.length; i++) {
if (instance[i].moduleId == id) {
return instance[i];
}
}
},
instances: function () {
return instance;
}
}
}
return getInstance();
}
This can now be called from the RegisterClientScriptBlock where it will only be initialized the one time. Then I can instantiate the class in each module by calling:
var myItems = new CG_AllItems();
myItems.add(<%=moduleId %>);
myItems.instance(<%=moduleId %>).Init();
At this point myItems will contain an instance an object keyed by the moduleId. The object can contain any number of methods and variables all keyed by that moduleId. The benefits of the singleton class is every time you reinitialize the class and assign it to var myItems it will retain it’s previous state and will contain all the objects previously instantiated.
Now any functions that need to by called for this module can by called by simply calling:
cg_AllItems.instance(<%=moduleId%>).Init();
//Where Init is the name of any function you wish to run.
In my example Init() is configured as an anonymous function but could be setup as a named function as well.
You’ll also need to consider how to access DOM objects within your functions. I solved by appending the <%=moduleID%> to the names of the DOM objects
”>. Now within your class you can get the object by:
jQuery("#myItems_" + this.moduleId).show();
The benefits of this overall method is that any functions called within the moduleId keyed objects can be fired from within the object itself and will always have access to all the variables contained within that object that are assigned by each module. This is still a work in progress but so far everything is working out well.
If you have any to help add to the process or a better way to handle this please feel free to add a comment.
|
By Jonathan Sheely on
6/26/2011 12:41 PM
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:
<%@ 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>
- 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.
|
By Jonathan Sheely on
5/17/2011 4:20 PM
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 too:
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.
[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
|
By Jonathan Sheely on
5/14/2011 6:32 PM
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
- 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
- Utilizes 7-Zip to go through every .BAK file in this directory and compress it to save space
- Deletes the no longer needed .BAK files
- 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:
- Gets a date stamp in the format of 20110514 to use to append to file names
- 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
- 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.
|
By Jonathan Sheely on
5/14/2011 5:04 PM
I was awarded the grand prize for creating the a charting DotNetNuke module for the Hackathon. The objective was to use visual elements such as jQuery to create a UI friendly application in 4 days.
The grand prize was a Apple iPad
See related articles:
|
By Jonathan Sheely on
5/14/2011 5:03 PM
If you're like me and find the use of WWW in URLs both unnecessary and annoying then here is a little tip for you to easily remove it from all your urls.
Note: Before you proceed make sure you have an DNS (A) record setup already for your domain with and without the www.
DotNetNuke provides a FriendlyURL Provider that allows for regular expression matches in Host Settings. Here you can create a new rule that removes the WWW and issues a 302 redirect to the non-www domain.
Create a new rule that:
- Matches: (.*)www.(.*)
- Replaces: $1$2
That's it!
|
By Jonathan Sheely on
5/14/2011 4:58 PM
The DNN EventLog contains a great deal of information. It Includes everything from critical errors to mundane items such as when a user logs in. All of which enables it to fill up very quickly and can be a burden to manage if you have multiple installations of DotNetNuke.
In order to solve this problem I wrote a SQL script that can be run as part of your server’s maintenance plan or ad-hoc. The goal of the script is to find all the DotNetNuke databases on your server and truncate the EventLog table.
How it works:
- Searches through all your databases.
- Locates the databases that contain a DotNetNuke application.
- Finds the EventLog even if an object qualifier has been added to the tables.
- Truncates the table.
--Application: Clear EventLog in all DotNetNuke Databases
--Created By: Jonathan Sheely
DECLARE @DEBUG BIT
SET @DEBUG=1 --Switch from EXEC to PRINT
DECLARE @dbname NVARCHAR(100)
DECLARE @EventLogTableName NVARCHAR(100)
DECLARE @ParmDefinition NVARCHAR(100)
DECLARE @sqlstr NVARCHAR(MAX)
DECLARE @sqlout NVARCHAR(100)
SET @ParmDefinition = N'@objOut nvarchar(100) output';
DECLARE databases CURSOR FOR
SELECT [name] FROM sys.databases
OPEN databases
FETCH databases INTO @dbname
WHILE (@@FETCH_STATUS<>-1)
BEGIN
SET @sqlstr='USE [' + @dbname + '];
SELECT @objOut=1
FROM sys.tables
WHERE [name] = ''aspnet_Applications'';'
SET @sqlout=NULL
EXEC sp_executeSQL @sqlstr,@ParmDefinition,@objOut=@sqlout OUTPUT
IF @sqlout=1
BEGIN
SET @sqlstr='USE [' + @dbname + '];
SELECT @objOut=loweredApplicationName
FROM aspnet_Applications;'
SET @sqlout=NULL
EXEC sp_executeSQL @sqlstr,@ParmDefinition,
@objOut=@sqlout OUTPUT
IF @sqlout='dotnetnuke'
BEGIN
SET @sqlstr='USE [' + @dbname + '];
SELECT @objOut=[name]
FROM sys.tables
WHERE [name] like ''%EventLog'';'
SET @sqlout=NULL
EXEC sp_executeSQL @sqlstr,@ParmDefinition,
@objOut=@sqlout OUTPUT
IF @sqlout IS NOT null
BEGIN
SET @EventLogTableName=@sqlout
SET @sqlstr = 'USE [' + @dbname + '];
TRUNCATE TABLE ' + @EventLogTableName +';'
IF @DEBUG=0
BEGIN
EXEC sp_executeSQL @sqlstr
END
ELSE
BEGIN
PRINT @sqlstr
END
END
END
END
FETCH databases INTO @dbname
END
CLOSE databases
DEALLOCATE databases
|