May
7
Posted by:
Shaun Walker
5/7/2008
When it comes to software, a component or service which survives for an extended period of time without substantial refactoring is generally considered to be an anomoly. However, there are cases when a solution fulfills the business requirements so completely that you have the luxury of leaving it on autopilot for quite some time. Generally, this only happens in situations where a broad set of use cases were examined up front, the primary stakeholders were accurately identified, clear goals were established, multiple prototypes were developed, user participation resulted in continuous feedback loops, and the architecture and implementation were properly abstracted from external factors. The Skinning Engine in DotNetNuke is an example of a solution which has stood the test of time and has served the community well.
Originally developed in 2003 and released with DotNetNuke 2.0 in 2004, the skinning engine had some very clear goals in mind - the most important being that it needed to be focussed on designers rather than developers. To accomplish this goal, the ultimate solution needed to abstract the inner workings of DotNetNuke and ASP.NET application framework from the designer. It also needed to leverage standard web design elements and allow designers to use their design tools of choice. Finally, it needed to encourage creativity, and allow for portability so that skins could easily be installed in sites without developer intervention.
Clearly, the initial release of the skinning engine was a large success and it helped accelerate the growth of the platform in the early stages. Designers appreciated the flexibility of the solution as well as the clean abstraction of form and function. An entire commercial ecosystem developed around skins and there are now thousands of professional skin offerings available in the community.
When ASP.NET 2.0 came onto the scene in 2005 there was some trepidation that that heavily marketed "Master Pages" concept would replace the DotNetNuke skinning engine. However, it did not take long to realize that a Master Page is not a designer solution - it is a developer solution. Therefore, it did not fulfill the same set of business requirements, or cater to the same stakeholders. Combined with the fact that Master Pages are not portable, it was fairly clear that the DotNetNuke skinning engine was the superior architecture for the future.
Over the years, some minor enhancements have been made to the original skinning engine to add more flexibility and provide better abstraction; however, for the most part the basic architecture has remained relatively unchanged. This has been a real benefit to the community as it means that skins created for DotNetNuke 2.0 and 3.0 and 4.0 continue to be compatible with the most recent versions of the platform.
With DotNetNuke 5.0, a number of new enhancements have been introduced to the skinning engine which provide additional power and flexibility to designers. In order to maintain compatibility with previous versions of the platform, the new features are completely optional so that they can be leveraged at the discretion of designers.
1. Packaging and Distribution
As I mentioned earlier, the portability aspect has long been one of the greatest benefits of the DotNetNuke skinning architecture. Originally a skin "package" was simply a group of files ( ie. HTML, CSS, images, etc... ) comprising a single skin, compressed into a standard ZIP file. Later, we modified the definition of a skin package so that a designer could create a more comprehensive design in a single ZIP file which internally included both a skin package ( skins.zip ) and container package( containers.zip ). This format represents the vast majority of skin packages today.
Although there is nothing inherently wrong with the current model, there are some limitations which have been identified. From a high level, the lack of a consistent packaging model across all DotNetNuke features has resulted in additional complexity in the application and a less than optimal user experience. In addition, uninstall and dependency management is not handled elegantly and there is too much redundancy in terms of maintaining multiple independent package formats. As a result, a new common packaging model has been introduced in DNN 5.0 which is designed to address some of these limitations.
There is now a "manifest" for all package types. A manifest is used to describe attributes about a package as well as identify all resources included in the package. Skins never supported the concept of a manifest or versioning in the past, but in the new model there is now the ability for a designer to include additional information to describe the content and creator of the package. Attributes include the owner of the package, the organization name, a website URL for further information, an email address for support inquiries, and a version identifier for identifying the specific release of the package. There is also the ability to include a license agreement which an end-user must accept during installation of the package, as well as release notes which can provide a summary of fixes or enhancements included in each version.
In DNN 5.0, packages also have the ability to contain additional resources. So why is this relevant for skins? Well consider the situation where a skin relies on a custom skin object. This effectively means that both the skin and skin object need to be installed at the same time in order for the skin to function correctly - a concept which was not possible in the past. In DNN 5.0 it is now possible to bundle skins, containers, and skin objects in the same package for distribution.
And since most people are apprehensive about adopting new features due to the perceived learning curve, we have included the ability to create the manifest file and skin package very simply using a new wizard-based Skin Packager option which is now included in the core platform. In most cases, taking advantage of the new skin package format is as simple as stepping through the wizard to produce a new ZIP file.
2. Skin Object Definition and Attributes
Skin objects are a critical part of the DotNetNuke skinning architecture. They basically represent controls which are included in a skin to provide improved functionality and usability. In order to abstract the details of skin objects from designers, DotNetNuke has always supported a notion of [TOKENS] within HTML skin files. The idea behind tokens is that a designer could include them within their design and then the system would perform the necessary substitution with the actual server-side control when the skin was installed. This worked well; however, there was one significant flaw. Most skin objects have a variety of attributes which affect their behavior and there was no way to elegantly include these attribute specifications within the HTML skin file. As a result, a second XML file needed to be handcrafted by designers to include the attributes for the skin. Now XML is not a designer-friendly format and is not well supported by most design tools. So basically one of the primary goals of the skinning solution was somewhat compromised by this situation.
Not wanting a let a problem go unsolved, Nik Kalyani began experimenting with a number of approaches for dealing with this issue and ultimately made a proposal which was so simple, yet elegant, that I am surprised it took this long to be revealed.
With the proliferation of Flash on the web, the support for <object> tags has grown substantially over the years. <object> tags are really intended for client-side control specifications; however, there is nothing stopping us from leveraging them for our own purposes. And the great thing about <object> tags is that they already provide support for an arbitrary list of parameters which can be managed in most design tools. So lets look at an example:
Instead of having an HTML file with a token:
[SEARCH]
and an XML file with:
<Object>
<Token>[SEARCH]</Token>
<Settings>
<Setting>
<Name>showWeb</Name>
<Value>True</Value>
</Setting>
<Setting>
<Name>ShowSite</Name>
<Value>True</Value>
</Setting>
</Settings>
</Object>
I can rewrite this within a single HTML file as:
<object id="SEARCH">
<param name="ShowWeb" value="True" />
<param name="ShowSite" value="True" />
</object>
When the skin is installed, the system will automatically convert the <object> tag and parameters into an ASP.NET user control declaration:
<dnn:SEARCH runat="server" id="dnnSEARCH" showWeb="True" showSite="True" />
This is a great simplication for designers and should make the DotNetNuke skinning engine even more designer-friendly.
3. [TEXT] Skin Object / Localization
While the DotNetNuke framework has supported localization since DNN 3.0, there has never been a capability to support localization in skins. At Sebastian Leupold's insistence, a new skin object was introduced into DNN 5.0 which provides both localization support as well as a number of other very useful features. So lets look at an example:
In the ASCX skin file, you could include the following declaration:
<dnn:TEXT runat="server" id="dnnTEXT" Text="Test" resourceKey="Test" />
Effectively what this means is that the system should display the default text of "Test" in the skin at the location where the skin object is specified. But what about the second parameter named "resourceKey" ? Well, basically this is telling the system to look in a related resource file for a key named "Test" and if it exists, use the string value which is included. Resource files for skins follow the same model as that for Modules; with any RESX files residing in an App_LocalResources folder beneath the Skin folder.
Now, this feature is not only useful for localization, but also for custom configuration. For example you could include a [TEXT] skin object in your skin for a third party service such as Google Adsense and then include the necessary javascript in the RESX file. In this way, the Administrator does not need to tamper with the actual skin file to make modifications.
And for even more power consider the following:
<dnn:TEXT runat="server" id="dnnTEXT" Text="Test" resourceKey="Test" replaceTokens="True" />
The "replaceTokens" attribute indicates that the system should use the token replace functionality to dynamically substitute tokens with environment values at run-time. This can be very powerful if you would like to include items like page name, site name, etc... in your skin.
4. Parser Optimization
The skin parser has always supported a concept referred to as 'named instances'. This allowed you to have multiple definitions for the same type of skin object in a skin or container. This was done in HTML skins by using a notation such as [ACTIONBUTTON:1] and [ACTIONBUTTON:2]. When the skin was parsed, the system created the ASCX file and included user control registrations and substitutions for each named instance:
<%@ Register TagPrefix="dnn" TagName="ACTIONBUTTON1" Src="~/Admin/Containers/ActionButton.ascx" %>
<%@ Register TagPrefix="dnn" TagName="ACTIONBUTTON2" Src="~/Admin/Containers/ActionButton.ascx" %>
<dnn:ACTIONBUTTON1 runat="server" id="dnnACTIONBUTTON1" CommandName="AddContent.Action" DisplayIcon="True" DisplayLink="True" />
<dnn:ACTIONBUTTON2 runat="server" id="dnnACTIONBUTTON2" CommandName="SyndicateModule.Action" DisplayIcon="True" DisplayLink="True" />
Although this works, it is not very efficient. There is no reason why each named instance should have its own individual control registration. Since each named instance is related to the same skin object, there only needs to be a single control registration:
<%@ Register TagPrefix="dnn" TagName="ACTIONBUTTON" Src="~/Admin/Containers/ActionButton.ascx" %>
<dnn:ACTIONBUTTON runat="server" id="dnnACTIONBUTTON1" CommandName="AddContent.Action" DisplayIcon="True" DisplayLink="True" />
<dnn:ACTIONBUTTON runat="server" id="dnnACTIONBUTTON2" CommandName="SyndicateModule.Action" DisplayIcon="True" DisplayLink="True" />
In DNN 5.0 the parser was modified to include this optimization.
20 comment(s) so far...
Re: Skinning Enhancements
Shaun, great news for all skinners, I guess and thanks for the dnn:TEXT enhancements :-)). A few questions though: 1. should the object tag not contain a type="DotNetNuke" to prevent other multimedia objects being affected? 2. for dnn:TEXT, as far as I understand, the default resource file can be included in the skin package and the text param omitted, right? 3. for dnn:TEXT, does replaceTokens require a resourceKey being used or can the token be included in the Text like < dnn:TEXT runat="server" id="dnnTEXT" Text="[Portal:Portalname]" replaceTokens="True" />
By leupold on
5/7/2008
|
Re: Skinning Enhancements
Beautiful enhancements! Thanks for the update, Shaun. And thanks to Nik Kalyani for what is really a very elegant solution to the XML separation that occured for token attributes. Perhaps someone will create a "converter" to repackage older style skins to the new model's for DNN 5.0. I'm really looking forward to seeing what DNN 5.0 is like. :)
By dagilleland on
5/7/2008
|
Re: Skinning Enhancements
This is great news!! I can't wait for it to come out.
By cdang on
5/7/2008
|
Re: Skinning Enhancements
Localization of skin through TEXT skin objects takes multilanguage support one step ahead, I think they will be very appreciated. Along with token replacement this could be used to show different chunks of HTML for different locales (flags, company addresses, etc.). Now I wonder if in future we are going to expect the opportunity of choosing an entire different skin for each different language.
By gnogna82 on
5/7/2008
|
Re: Skinning Enhancements
Answers:
1. should the object tag not contain a type="DotNetNuke" to prevent other multimedia objects being affected?
>> Note that we are not actually using the object tag for its normal purpose as a client-side control. Instead we are converting it to a server-side user control reference when the skin is installed. The ID attribute is being used to match the object name to a valid registered skin object. I suppose there is a limited chance for a naming collision with a true client-side multi-media object ( if you are using both types in your skin and are not aware of ID naming convention ). I believe in Niks proposal there was a provision for a mandatory CodeType="server" attribute which would also have satisfied this case. I am not entirely sure if this is a high probability use case though.
2. for dnn:TEXT, as far as I understand, the default resource file can be included in the skin package and the text param omitted, right?
>> Correct - the Text attribute is not required ( it is really a fallback if no resource key is included ).
3. for dnn:TEXT, does replaceTokens require a resourceKey being used or can the token be included in the Text like < dnn:TEXT runat="server" id="dnnTEXT" Text="[Portal:Portalname]" replaceTokens="True" />
>> Good suggestion. I have now checked in a modification to allow for use of token replace without a resource key.
By sbwalker on
5/7/2008
|
Re: Skinning Enhancements
Hi Shaun, thanks for the reply and the enhancement for #3. Regarding #1 I'm still of the opinion, that we should avoid any possible naming conflicts, IMO a type attribute would enhance readibility and prevent any other objet being affected just because of same ID.
By leupold on
5/7/2008
|
Re: Skinning Enhancements
Shaun, one question: If I combine 2) and 3), I am allowed to write < object id="TEXT"> < param name="Text" value="Test" /> < param name="resourceKey" value="Test" /> < /object> What happens if I want to use the TEXT Skin Object twice? Id would not be unique anymore.
By cshark on
5/7/2008
|
Re: Skinning Enhancements
Based on feeback from this blog, I will be adding a Type attribute for DNN 5.0 to eliminate naming collision issues with objects.
As far as unique IDs, the current solution requires you to use the same concept of named instances that we use for [TOKENS]. So if you had multiple TEXT objects the ID values would need to be "TEXT:1" and "TEXT:2". I believe we are going to change this and allow designers to make their own ID specifications to ensure uniqueness.
By sbwalker on
5/7/2008
|
Re: Skinning Enhancements
Great News, these changes will really push the ability of skinners to create "user friendly" skins / skin objects, and "complete" skin packages. The ability to manifest and more importantly include skin objects within the skin itself is superb. I like to use the house of nuke, CSS NavMenu, and a xflex skin objects in my skins. Trying to get my end users to install all of that before loading a skin can at times be troublesome. The removal of the xml file will also make it so much easier to document skin attributes and what to change when, allowing end users the ability to slightly tweak a skin to their liking. Without having to try and alter a "scary" xml file. Can't wait to get my hands on 5.0 you guys have me drooling for all the new additions on the way.
By keeperofstars on
5/8/2008
|
Re: Skinning Enhancements
These look like terrific enhancements. I like the elegance of the < object> idea particularly - has a MicroFormat ring to it.
Has there been any discussion of how skins and modules could handle support files? For instance, what would be the best way to avoid loading multiple copies of Jquery, Prototype, etc.? Could there be an abstracted registry for support files of all types? Once registered, modules and skins alike could request the files by name. Duplication is then handled by the core versus manually writing the include and link lines. If the entire list of support files is handled this way then skin designers could turn off default.css (for instance) rather then delete the contents. Further, conditions for roles could hide admin only css and js files making the public pages much lighter.
By lancelong on
5/10/2008
|
Re: Skinning Enhancements
This is great news, not only from the skinning engine enhancements, but also from the installation/package concept.
I think this will give not only skin designers but module developers much better tools to provide more usable products!
By mitchel.sellers@gmail.com on
5/10/2008
|
Re: Skinning Enhancements
These are excellent changes/enhancements, albeit some of the older ascx skins will most likely need modifying to work??? I'm not clear on that. It does reinforce my stand on why as a skinning professional, I choose the ascx method when explaining skinning to newcomers, for the simple reason that XML in it's own structure is also a learning step for some which was hard to overcome as well as grasping the methodologies of skinning. Particularly when you see those coming into DNN from a purely graphical environment, XML was often quite the stumbling block as they tried to do more advanced skinning techniques.
I may consider studying more of the html approaches in the future to give true upgrade options moving forward.
While I've had no issues in telling people to install skinobjects and the like, the integration of these and avoiding collisions and allowing duplication (as we're sometimes seeing in the web 2.0 environment) this is another step forward for an already remarkable skinning engine.
From it's initial release just a few short years ago, it's been the leader in this space and this step forward in making it easier again to deploy will grow it further.
I'm really looking forward to this coming out and pushing it's boundaries further.
I do have a question though - unless I'm wrong, I've found that there are quite a few scenarios with 'inline coding' as well as applying xml, which is another reason I tend to us ascx methods, as I can drop the code directly into the skin file, without having to wonder if the attributes will be correctly parsed through an xml file - this is mainly relating to the menu - with the NAV menu have more documentation and information to carry out these actions, or will there still be elements that need to be applied directly in the menu code within the file itself?
Thanks for this post - I have found it really interesting and informative.
Nina Meiers
By nina on
5/10/2008
|
Re: Skinning Enhancements
As I mentioned in the blog, all of these enhancements are *optional* - so full backward compatibility is maintained ( ie. older HTML and ASCX will continue to work fine ). And on the topic of 'inline coding', you have always been able to include script code inline in either HTML or ASCX skins - the skin parser does not affect inline script. The major benefit of HTML skins is that a designer, whether they are a newcomer or professional, really does not have to know anything about ASP.NET - which is really important as most designers are not developers.
By sbwalker on
5/10/2008
|
Re: Skinning Enhancements
So after much discussion, the embedded object tag syntax is going to be:
<object id="dnnSEARCH" codetype="dotnetnuke/server" type="SEARCH">
"id" must be unique and can be user defined ( this eliminates the need for 'named instances' ).
"codetype" must be "dotnetnuke/server" for Skin Objects or "dotnetnuke/client" for Skin Widgets. We have not discussed Skin Widgets yet, but Nik Kalyani has created a new feature in DNN 5.0 which allows for dynamic injection of client-side behavior in skin.
"type" must be the name of the Skin Object ( effectively the same name as what is currently specified in a [TOKEN] ).
By sbwalker on
5/10/2008
|
Re: Skinning Enhancements
Shawn,
As per support files. What I meant was a system that would have more intelligence for how and what files are loaded. By default the system would load the current set of files for backward support but skin designers and module coders alike would be able to include additional libraries or suppress the common ones.
Perhaps more "real world" - IE only style sheets are of vital importance in the post table layout world. I currently use Page Blaster to include the conditional css in the head and some VB scrip to include other files. This is bulky (PB doesn't run on 100% of the requests) and inaccessible to skin designers. If there was a general sub system for support files distribution then the object tag could be used to request them in the skin. In the IE6 case, the object tag would surround a code block to be added to the head section. I have no suggestion of what that would look like in the ascx, you and the team would be better to answer that.
Can you see the value of such a system?
By lancelong on
6/12/2008
|
Re: Skinning Enhancements
it is so greate that I can't wait for it to come out.
By sunwangji on
6/12/2008
|
Re: Skinning Enhancements
Wow! I am so pumped about these enhancements! Thank you so much. I am a speak at OpenForce Connect - Orlando. I will be e-mailing you about this.
By hismightiness on
6/12/2008
|
Re: Skinning Enhancements
Hi Shawn,
Great enhancements. But we are wondering if the rendering of the HTML by DotNetNuke will be enhanced? For instance the span class="Title" should rather be a H1 or even left blank so the designer can deside in the container how it should be rendered. What we do now is put the DotNetNuke title between H1 tags but than you get something like
Title here and in css you need to declare H1 .Title which is not so beautifull...
By vandenbroele on
12/6/2008
|
Re: Skinning Enhancements
This article is still EXTREMELY helpful a year later. :)
However, I am wondering where would I place the [TEXT] or for Text in my skin file if I wanted all modules or some specific modules to automatically use the replaceTokens command? Or is there another way of achieving what I am looking for?
By snatrott on
4/26/2009
|
Re: Skinning Enhancements
@Snatrott -- The best place to inquire about specific helps will be to post in the "Make it Hot!" forum... most skinners hang out there. Cheers.
By Scott Willhite on
4/26/2009
|