<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://weblogs.asp.net/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>irob's blog. : DNN</title><link>http://weblogs.asp.net/ianrobinson/archive/tags/DNN/default.aspx</link><description>Tags: DNN</description><dc:language>en</dc:language><generator>CommunityServer 2007 SP1 (Build: 20510.895)</generator><item><title>"Improving the Secured File UX" Part Two</title><link>http://weblogs.asp.net/ianrobinson/archive/2008/12/06/quot-improving-the-secured-file-ux-quot-part-two-next-steps.aspx</link><pubDate>Sun, 07 Dec 2008 04:43:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6768941</guid><dc:creator>irobinson</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/ianrobinson/rsscomments.aspx?PostID=6768941</wfw:commentRss><comments>http://weblogs.asp.net/ianrobinson/archive/2008/12/06/quot-improving-the-secured-file-ux-quot-part-two-next-steps.aspx#comments</comments><description>&lt;LINK href="http://www.engagesoftware.com/portals/2/syntaxhighlighter/css/SyntaxHighlighter.css" type=text/css rel=stylesheet&gt;&lt;/LINK&gt;
&lt;SCRIPT language=javascript src="http://www.engagesoftware.com/portals/2/syntaxhighlighter/js/shCore.js"&gt;&lt;/SCRIPT&gt;

&lt;SCRIPT language=javascript src="http://www.engagesoftware.com/portals/2/syntaxhighlighter/js/shBrushVB.js"&gt;&lt;/SCRIPT&gt;

&lt;SCRIPT language=javascript src="http://www.engagesoftware.com/portals/2/syntaxhighlighter/js/shBrushJScript.js"&gt;&lt;/SCRIPT&gt;

&lt;P&gt;In &lt;A href="http://dnngallery.net/Blog/articleType/ArticleView/articleId/65/Improving-the-Secured-File-Download-UX-for-Unauthenticated-Users.aspx" target=_blank mce_href="http://dnngallery.net/Blog/articleType/ArticleView/articleId/65/Improving-the-Secured-File-Download-UX-for-Unauthenticated-Users.aspx"&gt;Improving the Secured File Download UX for Unauthenticated Users&lt;/A&gt; I elaborated on a &lt;STRIKE&gt;workaround&lt;/STRIKE&gt; hack to display a friendly error message and redirect to the login page when trying to access a file that had been through DotNetNuke's file ticket system.&lt;/P&gt;
&lt;P&gt;If you're in a situation where that solution makes sense for you - great. But what about future releases of the application? Should this functionality exist within the framework itself? Is it too trivial? Does it make sense for everyone that uses the framework so much so that it should be a part of it?&lt;/P&gt;
&lt;P&gt;To find out, I put the issue in &lt;A href="http://support.dotnetnuke.com/" target=_blank mce_href="http://support.dotnetnuke.com"&gt;DotNetNuke's Gemini&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;I suggest that when you encounter a workaround/hack/enhancement such as this:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Don't &lt;STRONG&gt;just &lt;/STRONG&gt;fix it for now&lt;/LI&gt;
&lt;LI&gt;These "fringe areas" are one of the places where the "community at large" can be of most help. Let the core team focus on the big stuff.&lt;/LI&gt;
&lt;LI&gt;So...put it in Gemini!&lt;/LI&gt;
&lt;LI&gt;And, ideally, try to give the core team a head start.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;So, in an effort to follow the above path, I cracked open my handy DotNetNuke source code and made a few changes.&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;update: I'm providing the following information for reference only, I really don't think you should make this change to your DNN application. My intention is just to remind you that the core code is there, we can easily make changes, test them out, and suggest to the DotNetNuke team that they incorporate those changes into the application.&lt;/EM&gt;&lt;/P&gt;
&lt;H3&gt;Background&lt;/H3&gt;
&lt;P&gt;When the user requests the file, the LinkClick.aspx "page" is requested. (e.g. "&lt;EM&gt;http://localhost/DotNetNuke_2/LinkClick.aspx?fileticket=XKDsP6pHvtQ=&amp;amp;tabid=36&amp;amp;mid=410&lt;/EM&gt;"). This really isn't a page, but is a handler registered in the application's web.config.&lt;/P&gt;
&lt;P&gt;The actual class that handles the file request is called "FileServerHandler" and is found in &lt;EM&gt;Library\Components\FileSystem\FileServerHandler.vb&lt;/EM&gt; in version 4.9 and &lt;EM&gt;\Library\Services\FileSystem\FileServerHandler.vb&lt;/EM&gt; in DotNetNuke 5.0 RC2.&lt;/P&gt;
&lt;P&gt;While some slight refactoring has been done to this class between 4.9 and 5.0, the portion we're concerned with has not changed: &lt;/P&gt;&lt;PRE class=vb name="code"&gt;' serve the file
If TabId = Null.NullInteger Then
    If Not FileSystemUtils.DownloadFile(_portalSettings.PortalId, Integer.Parse(UrlUtils.GetParameterValue(URL)), blnClientCache, blnForceDownload) Then
        context.Response.Write(Services.Localization.Localization.GetString("FilePermission.Error"))
    End If
Else
    If Not FileSystemUtils.DownloadFile(_portalSettings, Integer.Parse(UrlUtils.GetParameterValue(URL)), blnClientCache, blnForceDownload) Then
        context.Response.Write(Services.Localization.Localization.GetString("FilePermission.Error"))
    End If
End If&lt;/PRE&gt;
&lt;P&gt;The above code is responsible for the original behavior of showing the localized file permission error message.&lt;/P&gt;
&lt;H3&gt;Goals for Improvement&lt;/H3&gt;
&lt;P&gt;If the user is not able to download the file:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;If they are not logged in, let's take them to the current portal's login URL with a return URL for the file ticket.&lt;/LI&gt;
&lt;LI&gt;If they are logged in, display the localized FilePermission.Error message, as usual.&lt;/LI&gt;&lt;/OL&gt;
&lt;H3&gt;Adding a Utility Method&lt;/H3&gt;
&lt;P&gt;First, let's add a method to help us get the current portal's login URL:&lt;/P&gt;&lt;PRE class=vb name="code"&gt;Private Function GetLoginUrl(ByVal portalSettings As PortalSettings, ByVal request As HttpRequest) As String
    If Not portalSettings Is Nothing AndAlso Not request Is Nothing Then
        Dim tabId As Integer = portalSettings.ActiveTab.TabID
        Dim controlKey As String = "Login"
        If Not Null.IsNull(portalSettings.LoginTabId) AndAlso String.IsNullOrEmpty(request.QueryString("override")) Then
            controlKey = String.Empty
            tabId = portalSettings.LoginTabId
        ElseIf Not Null.IsNull(portalSettings.HomeTabId) Then
            tabId = portalSettings.HomeTabId
        End If
        Return DotNetNuke.Common.Globals.NavigateURL(tabId, controlKey)
    Else
        Throw New ArgumentNullException(If(portalSettings Is Nothing, "portalSettings", "request"))
    End If
End Function&lt;/PRE&gt;
&lt;P&gt;This method is intended to yield the correct login page whether a custom one is specified or not. It could potentially use the current tab, or the home tab as well. Just playing it safe.&lt;/P&gt;
&lt;H3&gt;Adding the core enhancement&lt;/H3&gt;
&lt;P&gt;Now, we need to figure out whether or not the user is logged in, and perform one of two actions. Either redirect them to the login page, or let them know that they don't have sufficient privileges to view the file.&lt;/P&gt;&lt;PRE class=vb name="code"&gt;Private Sub RedirectToLoginOrDisplayErrorMessage(ByVal context As HttpContext, ByVal _portalSettings As PortalSettings)
    If (context.Request.IsAuthenticated = False) Then
        Dim loginPage As String = GetLoginUrl(_portalSettings, context.Request)
        Dim returnUrl As String = HttpUtility.UrlEncode(HttpContext.Current.Request.Url.PathAndQuery)
        context.Response.Redirect(loginPage + "?returnurl=" + returnUrl)
    Else
        context.Response.Write(Services.Localization.Localization.GetString("FilePermission.Error"))
    End If
End Sub&lt;/PRE&gt;
&lt;H3&gt;Wiring it Up&lt;/H3&gt;
&lt;P&gt;Now, without changing the original code much, we can replace a couple lines of code, set a few break points, and try this thing out. &lt;/P&gt;&lt;PRE class=vb name="code"&gt;' serve the file
If TabId = Null.NullInteger Then
    If Not FileSystemUtils.DownloadFile(_portalSettings.PortalId, Integer.Parse(UrlUtils.GetParameterValue(URL)), blnClientCache, blnForceDownload) Then
        RedirectToLoginOrDisplayErrorMessage(context, _portalSettings)
    End If
Else
    If Not FileSystemUtils.DownloadFile(_portalSettings, Integer.Parse(UrlUtils.GetParameterValue(URL)), blnClientCache, blnForceDownload) Then
        RedirectToLoginOrDisplayErrorMessage(context, _portalSettings)
    End If
End If&lt;/PRE&gt;
&lt;H3&gt;Summary&lt;/H3&gt;
&lt;P&gt;Mission accomplished. Now to see if it gets picked up anywhere along the way. Let me know what you think of this enhancement or the process in general. Also, I did enjoy picking apart this one trivial thing, so if you have any suggestions for future experiments, please let me know.&lt;/P&gt;
&lt;SCRIPT language=javascript&gt;
dp.SyntaxHighlighter.ClipboardSwf = 'http://www.engagesoftware.com/portals/2/syntaxhighlighter/flash/clipboard.swf';
dp.SyntaxHighlighter.HighlightAll('code');
&lt;/SCRIPT&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6768941" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/ianrobinson/archive/tags/DotNetNuke/default.aspx">DotNetNuke</category><category domain="http://weblogs.asp.net/ianrobinson/archive/tags/DNN/default.aspx">DNN</category><category domain="http://weblogs.asp.net/ianrobinson/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://weblogs.asp.net/ianrobinson/archive/tags/Secured+File+Management/default.aspx">Secured File Management</category><category domain="http://weblogs.asp.net/ianrobinson/archive/tags/UX/default.aspx">UX</category><category domain="http://weblogs.asp.net/ianrobinson/archive/tags/Visual+Basic/default.aspx">Visual Basic</category></item><item><title>Signs your DNN Module Development skills could use some sharpening</title><link>http://weblogs.asp.net/ianrobinson/archive/2008/11/03/signs-your-dnn-module-development-skills-could-use-some-sharpening.aspx</link><pubDate>Mon, 03 Nov 2008 23:32:00 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6718650</guid><dc:creator>irobinson</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/ianrobinson/rsscomments.aspx?PostID=6718650</wfw:commentRss><comments>http://weblogs.asp.net/ianrobinson/archive/2008/11/03/signs-your-dnn-module-development-skills-could-use-some-sharpening.aspx#comments</comments><description>&lt;OL&gt;
&lt;LI&gt;Your latest DNN module utilizes in-line CSS like &lt;I&gt;it&lt;/I&gt; was going out of style.&lt;/LI&gt;
&lt;LI&gt;You frequently need to release new versions due to changes that consist solely of updates to hard coded strings in your code-behind.&lt;/LI&gt;
&lt;LI&gt;Every time you implement your modules as part of a new solution, you spend the first few days just getting them to display correctly in the new skin&lt;/LI&gt;
&lt;LI&gt;You FTP into the production DNN site to upgrade your module.&lt;/LI&gt;
&lt;LI&gt;You spend more than 30 seconds packaging a new version of your module&lt;/LI&gt;
&lt;LI&gt;When you get a SQL script from source control you open up your favorite file comparison utility to see what’s changed so that you can run only those changes.&lt;/LI&gt;
&lt;LI&gt;You’ve been curious, for the last two years, about how to use the DNN UrlControl, but you never have the source readily available, so you still don’t know.&lt;/LI&gt;
&lt;LI&gt;The last time you upgraded your production environment you were up until 4AM trying to piece together outdated versions of your database.&lt;/LI&gt;
&lt;LI&gt;When creating your latest DNN module, you opened up the first DNN module you ever created to grab that same piece of code that you always grab when creating a new module.&lt;/LI&gt;&lt;/OL&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6718650" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/ianrobinson/archive/tags/DotNetNuke/default.aspx">DotNetNuke</category><category domain="http://weblogs.asp.net/ianrobinson/archive/tags/.NET/default.aspx">.NET</category><category domain="http://weblogs.asp.net/ianrobinson/archive/tags/DNN/default.aspx">DNN</category><category domain="http://weblogs.asp.net/ianrobinson/archive/tags/Open+Source/default.aspx">Open Source</category><category domain="http://weblogs.asp.net/ianrobinson/archive/tags/Worst+Practices/default.aspx">Worst Practices</category><category domain="http://weblogs.asp.net/ianrobinson/archive/tags/ASP.NET/default.aspx">ASP.NET</category></item><item><title>Creating DotNetNuke Modules using a Web Application Project (WAP)</title><link>http://weblogs.asp.net/ianrobinson/archive/2008/10/15/creating-dotnetnuke-modules-using-a-web-application-project-wap.aspx</link><pubDate>Wed, 15 Oct 2008 17:03:47 GMT</pubDate><guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:6682170</guid><dc:creator>irobinson</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/ianrobinson/rsscomments.aspx?PostID=6682170</wfw:commentRss><comments>http://weblogs.asp.net/ianrobinson/archive/2008/10/15/creating-dotnetnuke-modules-using-a-web-application-project-wap.aspx#comments</comments><description>&lt;p&gt;When creating a DNN module, you have the option to choose from the web site project (WSP), or a web application project (WAP). I prefer the web application project approach, and I'll explain it for developers who are new to module development, or unfamiliar with the WAP approach for developing modules.&lt;/p&gt;  &lt;p&gt;There are two major reasons why I feel WAP is generally better than WSP for DNN module development.&lt;/p&gt;  &lt;p&gt;1. Conciseness - the project contains only your module's code, nothing more. Separating your module from the DNN web site itself in Visual Studio really helps with performance and maintainability.&lt;/p&gt;  &lt;p&gt;2. Compiled - I like having the code compiled into an assembly. This makes it easier for me to build a module package and distribute. I like to use a &lt;a href="http://www.chrishammond.com/tabid/54/itemid/865/Packaging-your-DotNetNuke-Module-using-NANT.aspx"&gt;NAnt script&lt;/a&gt; for each module that creates the packages for me. The WAP project style lends itself to this approach.&lt;/p&gt;  &lt;p&gt;If you'd like to give it a try - here are the steps involved.&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Create a new WAP project, or alternatively, use a &lt;a href="http://weblogs.asp.net/briandukes/archive/2008/10/07/c-compiled-dotnetnuke-module-template.aspx"&gt;C# DNN Module WAP Template&lt;/a&gt;      &lt;br /&gt;      &lt;br /&gt;&lt;a href="http://weblogs.asp.net/blogs/ianrobinson/WindowsLiveWriter/DotNetNukeWAPprojectsforyourmodules_A431/image_10.png"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="83" alt="Template: ASP.NET Web Application" src="http://weblogs.asp.net/blogs/ianrobinson/WindowsLiveWriter/DotNetNukeWAPprojectsforyourmodules_A431/image_thumb_4.png" width="209" border="0" /&gt;&lt;/a&gt;       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;Place the root of the project in the DesktopModules directory of your DNN development web site's file system (e.g. ..\DesktopModules\MyModule\)     &lt;br /&gt;      &lt;br /&gt;&lt;a href="http://weblogs.asp.net/blogs/ianrobinson/WindowsLiveWriter/DotNetNukeWAPprojectsforyourmodules_A431/image_12.png"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="34" alt="Location: C:\inetpub\wwwroot\DotNetNuke\Website\DesktopModules" src="http://weblogs.asp.net/blogs/ianrobinson/WindowsLiveWriter/DotNetNukeWAPprojectsforyourmodules_A431/image_thumb_5.png" width="470" border="0" /&gt;&lt;/a&gt;       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;Set the project to build the assembly and place it in the &amp;quot;bin&amp;quot; directory of the DNN web site     &lt;br /&gt;      &lt;br /&gt;&lt;a href="http://weblogs.asp.net/blogs/ianrobinson/WindowsLiveWriter/DotNetNukeWAPprojectsforyourmodules_A431/image_8.png"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="35" alt="Output path: ..\..\Bin\" src="http://weblogs.asp.net/blogs/ianrobinson/WindowsLiveWriter/DotNetNukeWAPprojectsforyourmodules_A431/image_thumb_3.png" width="267" border="0" /&gt;&lt;/a&gt;       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;Set the project to launch your DNN web site (e.g. &lt;a href="http://localhost/DotNetNuke"&gt;http://localhost/DotNetNuke&lt;/a&gt;) when you F5/run.      &lt;br /&gt;      &lt;br /&gt;&lt;a href="http://weblogs.asp.net/blogs/ianrobinson/WindowsLiveWriter/DotNetNukeWAPprojectsforyourmodules_A431/image_6.png"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="33" alt="Start URL: http://localhost/dotnetnuke" src="http://weblogs.asp.net/blogs/ianrobinson/WindowsLiveWriter/DotNetNukeWAPprojectsforyourmodules_A431/image_thumb_2.png" width="415" border="0" /&gt;&lt;/a&gt;&amp;#160;&lt;a href="http://weblogs.asp.net/blogs/ianrobinson/WindowsLiveWriter/DotNetNukeWAPprojectsforyourmodules_A431/image_2.png"&gt;       &lt;br /&gt;&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;In order for Visual Studio to know about your development web site, make sure to set your project to use your IIS server, and set the Override application root URL to the root of your web site.&amp;#160; When you do this, Visual Studio knows that paths starting with &amp;quot;~&amp;quot; are really pointing to the DNN web site, and not just to your project.&amp;#160; This makes it possible to include controls from within the DNN site, like the DNN Label or URL control.     &lt;br /&gt;      &lt;br /&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="124" alt="Project Url: http://localhost/DotNetNuke/desktopmodules/MyModule,  Override application root URL: http://localhost/DotNetNuke" src="http://weblogs.asp.net/blogs/ianrobinson/WindowsLiveWriter/DotNetNukeWAPprojectsforyourmodules_A431/image_thumb.png" width="457" border="0" /&gt; &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;After your module is set up, you can import it (by importing the .dnn manifest file) into your development site, and run any database scripts that the module may have. Your module is now fully integrated into your DNN site.&lt;/p&gt;  &lt;p&gt;TIP: to debug the module, you may find it helpful to &amp;quot;&lt;a href="http://msdn.microsoft.com/en-us/library/c6wf8e4z.aspx"&gt;attach to process&lt;/a&gt;&amp;quot; in Visual Studio while you already have the web site running in a browser. This saves you from having to launch the web site multiple times.&lt;/p&gt;&lt;img src="http://weblogs.asp.net/aggbug.aspx?PostID=6682170" width="1" height="1"&gt;</description><category domain="http://weblogs.asp.net/ianrobinson/archive/tags/DotNetNuke/default.aspx">DotNetNuke</category><category domain="http://weblogs.asp.net/ianrobinson/archive/tags/.NET/default.aspx">.NET</category><category domain="http://weblogs.asp.net/ianrobinson/archive/tags/DNN/default.aspx">DNN</category></item></channel></rss>