One of the things that we'll be doing in our XMOD PRO blog posts is sharing some of the different solutions that we've built that help extend the functions of other modules or bridge the gap where there are functions or features that are missing in a DotNetNuke installation.
Today's blog post falls under the category of DNN Enhancements. These are the type of items where we think, "Gee, I wish that DNN could to X or that we had Y feature," to which I immediately think, "Why not build it in XMOD PRO!"
Back in 2001, working with Classic ASP pages and our own home-built simple management systems, I built for our search engine marketing person, a single ASP include page that had a case statement that listed out all of the meta content for every page of the website. Having this one page as a tool made it very easy for them to review the entire site's Page Titles, Meta Keywords, and Meta Description in on place. This complete view allowed them to quickly see any holes where they had neglected to setup individualized titles or to vary the keywords and check that they were rich keywords compared to the other pages of the site.
The Admin PAGES section of DNN is a great tool that allows you to quickly review the order of the site and see all the pages that are created. In the new DNN 6 updates that are coming soon, there are even more new features that help make the PAGES section even more useful, but there's nothing that lets you quickly see all of the other important Meta-related information.
Even though today, Meta Keywords are less important than they used to be, setting up good Titles and Descriptions and rich, accurate Keywords all play a role in good SEO practices. This is a page that we drop into almost every site for ourselves and our clients to use.
So in today's post, we'll show you how to build a manager that we call the PAGES META editor which allows you to manage, from one location, all of the meta tags/titles for every DNN page of your site.
Benefits:
- Easy to work on and see any holes instantly
- Often faster than loading pages individually or working with "pages" page
- Easy to spot consistency and format/keywords across all
When done, you'll have a page that looks like this:
First - Make A Page & Add XMOD PRO Module
Begin by making a new page called "Pages Meta" and if you like, put it under the parent of Admin and order it after the core Pages tab. Next add the main XMOD PRO module to the page and we'll get into the Form and Template.
XMOD Part One - XMOD Form "_dnn_TabMetaEditor"
Whenever we're making forms or templates that are for internal use or are system type things, we'll name them with a leading underscore to help make sure that the name goes to the end of the list of XMOD forms and templates. That helps group the system and bottom items when you get into 20 and 40 templates in a complex site.
This form we've titled as "_dnn_TabMetaEditor", and you'll only be needing the EditForm.
For the Form, we've used the XMOD Form Builder wizard to produce a simple form that edits the Tabs table: TabName, Title, Description, KeyWords, and SiteMapPriority fields. We find that on quick forms that we build for our own internal use, or quick forms for simple tables for client's use, we love using the form builder. Many times, our more complex forms will start out from a quick build from the Form Builder and then we'll enhance it further with js and changes to make the XMOD tags more complex.
FORM:
<EditForm>
<ScriptBlock BlockType="HeadScript" RegisterOnce="True" ScriptId="KBXM_Theme_/DesktopModules/XModPro/styles/ui-cupertino/jquery-ui-1.7.2.custom.css_TabMetaEditor">
<Link rel="stylesheet" type="text/css" href="/DesktopModules/XModPro/styles/ui-cupertino/jquery-ui-1.7.2.custom.css"/>
</ScriptBlock>
<ScriptBlock BlockType="HeadScript" RegisterOnce="True" ScriptId="KBXM_Style_TabMetaEditor">
<style type ="text/css">
.xmp-TabMetaEditor {
padding: 10px 5px 5px 5px;
}
.xmp-TabMetaEditor .xmp-form-row {
margin: 3px;
padding: 3px;
clear:left;
}
.xmp-TabMetaEditor label.xmp-form-label, .xmp-TabMetaEditor span.xmp-form-label {
display:block;
float:left;
width: 120px;
text-align: right;
white-space: nowrap;
margin-right: 5px;
padding-right:5px;
}
.xmp-TabMetaEditor .xmp-button {
margin-right: 5px;
}
.xmp-TabMetaEditor label.xmp-form-label, .xmp-TabMetaEditor span.xmp-form-label {
display:block;
float:left;
width: 120px;
text-align: right;
white-space: nowrap;
margin-right: 5px;
padding-right:5px;
}
</style>
</ScriptBlock>
<SelectCommand CommandText="SELECT [TabName], [Title], [Description], [KeyWords], [SiteMapPriority], [TabID] FROM Tabs WHERE [TabID]=@TabID"/>
<SubmitCommand CommandText="UPDATE [Tabs] SET [TabName]=@TabName, [Title]=@Title, [Description]=@Description, [KeyWords]=@KeyWords, [SiteMapPriority]=@SiteMapPriority WHERE [TabID]=@TabID"/>
<div class="ui-widget ui-widget-content xmp-TabMetaEditor xmp-form">
<div class="xmp-form-row">
<Label For="TabName" Text="Tab Name" CssClass="xmp-form-label" />
<TextBox id="TabName" DataField="TabName" DataType="string" MaxLength="50" Width="200" />
</div>
<div class="xmp-form-row">
<Label For="Title" Text="Title" CssClass="xmp-form-label" />
<TextBox id="Title" DataField="Title" DataType="string" MaxLength="200" Width="250" Nullable="True" />
</div>
<div class="xmp-form-row">
<Label For="Description" Text="Description" CssClass="xmp-form-label" />
<TextArea id="Description" DataField="Description" DataType="string" Height="200" Width="400" Nullable="True" />
</div>
<div class="xmp-form-row">
<Label For="KeyWords" Text="Key Words" CssClass="xmp-form-label" />
<TextArea id="KeyWords" DataField="KeyWords" DataType="string" Height="200" Width="400" Nullable="True" />
</div>
<div class="xmp-form-row">
<Label For="SiteMapPriority" Text="Site Map Priority" CssClass="xmp-form-label" />
<TextBox id="SiteMapPriority" DataField="SiteMapPriority" DataType="double" />
<Validate Type="Compare" Operator="DataTypeCheck" DataType="Double" Target="SiteMapPriority" Text="*" Message="Site Map Priority is not a valid Float" CssClass="xmp-validation ui-state-error" />
</div>
<div class="kbxmFormRow">
<span class="xmp-form-label"></span>
<UpdateButton Text="Update" CssClass="xmp-button" />
<CancelButton Text="Cancel" CssClass="xmp-button" />
</div>
<ValidationSummary DisplayMode="BulletList" CssClass="xmp-validation-summary ui-state-error" />
<TextBox id="TabID" DataField="TabID" DataType="int32" Visible<="False" />
</div>
</EditForm>
XMOD Part Two - XMOD Template "_dnn_TabList"
For the template that we've named "_dnn_TabList", we have a basic table structure that presents each page/tab in a row, with column breakdown of Tab Name, Meta Info, PageRank and Links.
SQL - We have two versions of the SQL Query for the main template... the one I’ve presented here is a simple query that you can insert in the ListDataSource select statement. The other version that we use on occasion is one in a stored procedure. It has a little bit more logic to it and will order the pages a bit differently and suppress the Admin menu from the list returned. If anyone is interested in that version, LMK and I’ll post it too. For now, this query gets the job done.
Some of the things you'll see used in the template:
xmod:ScriptBlock
- we have a general set of css styles that we use in our generic table listings. the set specify things like alternating colors, error highlight colors, etc.
ItemTemplate / AlternatingItemTemplate
- these have the same code in them, just with a different class to setup our alternating row styles
xmod:Select
- TitleFlag | DescriptionFlag | KeywordsFlagh - we use XMOD case statements to allow us to display/highlight items differently based on whether the content is present or blank/null. It lets us quickly set a different style, and a message such as "- description missing -"
xmod:Format type="regex"
- You'll notice that I still have in here the older workaround for displaying a truncated amount of text. The format tag will do that on it's own if you specify type=text, however in older versions of XMOD 2x that had an issue, so the type=regex is the one that we used back then, and still works in any newer installation. Having this version also means that we can put this into any site regardless of age of the XMOD that was installed there.
xmod:ToggleLink
- we've used plenty of jQuery things to do popups, tool tips, graybox and more for displaying additional details, but the built-in xmod toggle is a favorite to quickly show/hide additional contents. Here we use it to hid the long/full version of the Meta Description and Meta Keywords.
SearchSort
- Although not required, i never build an XMOD list without the search/sort at the top! This one lets us quickly search for a tab by name, and to sort by all the fields that you might want to sort.
<xmod:ScriptBlock ScriptId="xmodQuickStyle" BlockType="HeadScript" RegisterOnce="True">
<style>
.sm-font {font-size:8px;color:gray;}
.generic-table {width:100%;background:#efeded;border:0 solid #c0c0c0;border-collapse:separate}
.generic-table td{font-family:Arial,Helvetica,sans-serif;font-size:10px;line-height:18px;padding-left:10px}
.generic-headers{font-weight:bold;padding-left:6px;background:#efeded}
.generic-headers th{font-weight:bold;color:#333;padding-top:4px;padding-bottom:4px}
.generic-headers td{font-weight:bold;color:#444;padding-top:4px;padding-bottom:4px}
.generic-items{background:#FFFFFF}
.generic-items-alt{background:#f1f6fa;border-top:1px solid #EFEFEF;border-bottom:1px solid #EFEFEF}
.highlight{background-color:#FFF0F5; border-left:2px solid red; border-bottom:1px solid #EAD4DC; color:#EAD4DC;}
</style>
</xmod:ScriptBlock>
<xmod:Template>
<ListDataSource CommandText=SELECT TabID, replace(TabPath,'//','/') AS TabPath, ISNULL(ParentId, 0) AS SortOrderVal, ParentId, TabOrder, Level, TabName, ISNULL(NULLIF (Title, ''), '1') AS TitleFlag, Title, ISNULL(NULLIF (Description, ''),
'1') AS DescriptionFlag, Description, ISNULL(NULLIF (KeyWords, ''), '1') AS KeyWordsFlag, KeyWords, SiteMapPriority
FROM Tabs
WHERE (PortalID = @passedPortalID) AND (IsDeleted = 0)
ORDER BY SortOrderVal, Level, TabOrder
">
<Parameter Name="passedPortalID" Value='[[Portal:ID]]' DataType="Int32" />
</ListDataSource>
<HeaderTemplate>
<table class="generic-table">
<tr class="generic-headers">
<td width="30"></td>
<td><strong>TabName</strong></td>
<td colspan="2"><strong>Meta Info</strong></td>
<td><strong>SiteMap Priority</strong></td>
<td><strong>Resources</strong></td>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr class="generic-items">
<td rowspan="2">
<xmod:EditImage imageurl="~/images/edit.gif" alternativetext="Edit" tooltip="Edit">
<Parameter name="TabID" value='[[TabID]]'/>
</xmod:EditImage>
</td>
<td rowspan="2"> valign="top">[[TabName]]<br /><span class="sm-font">([[TabId]])</span>
</td>
<xmod:Select>>
<Case CompareType="text" Value='[[TitleFlag]]' Operator="=" Expression="1">
<td colspan="2" class=highlight>
- title missing -
</td>
</Case>
<Else>
<td colspan="2">
[[Title]]
</td>
</Else>
</xmod:Select>
<td rowspan="2">[[SiteMapPriority]]</td>
<td rowspan="2">
<xmod:Redirect Display="linkbutton" Target="http://www.webconfs.com/keyword-density-checker.php" Method="post" Text="chk kwds" ToolTip="check the page's content keyword density">
<Field Name="url" Value='[[Join("{0}{1}/TabID/{2}/Default.aspx", [[Portal:Alias]],[[TabPath]],[[TabID]])]]' />
</xmod:Redirect>
</td>
</tr>
<tr class="generic-items">
<xmod:Select>
<Case CompareType="text" Value='[[DescriptionFlag]]' Operator="=" Expression="1">
<td class="highlight">
- description missing -
</td>
</Case>
<Else>
<td>
<nobr>
<span class="sm-font"><
<xmod:Format Type="regex" Value='[[Description]]' Pattern="(.*)" Replacement="$1" Maxlength="70" />
</span>
<xmod:ToggleLink text="+" target='[[Join("#divMoreInfo_{0}a",[[TabId]])]]' speed="Fast" />
</nobr>
<div id="divMoreInfo_[[TabId]]a" style="display:none;">
<br/>
<span class="sm-font">[[Description]]</span>
</div>
<td>
</Else>
</xmod:Select>
<xmod:Select>
<Case comparetype="text" value='[[KeywordsFlag]]' operator="=" expression="1">
<td class="highlight">
- keywords missing -
</td>
</Case>
<Else>
<td>
<nobr>
<span class="sm-font">
<xmod:Format type="regex" value='[[Keywords]]' pattern="(.*)" replacement="$1" maxlength="70" />
</span>
<xmod:ToggleLink text="+" target='[[Join("#divMoreInfo_{0}b",[[TabId]])]]' speed="Fast" />
</nobr>
<div id="divMoreInfo_[[TabId]]b" style="display:none;">
<br/>
<span class="sm-font">[[Keywords]]</span>
</div>
</td>
</Else>
</xmod:Select>
</tr>
<ItemTemplate>
<AlternatingItemTemplate>
<tr class="generic-items-alt">
<td rowspan="2">
<xmod:EditImage imageurl="~/images/edit.gif" alternativetext="Edit" tooltip=""Edit">
<Parameter name="TabID" value='[[TabID]]'/>
</xmod:EditImage>
</td>
<td rowspan="2" valign="top">[[TabName]]>
<br/>
<span class="sm-font">([[TabId]])</span>
</td>
<xmod:Select>
<Case comparetype="text" value='[[TitleFlag]]' operator="=" expression="1">
<td colspan="2" class="highlight">
- missing title -
</td>
</Case>
<Else>
<td colspan="2">
[[Title]]
</td>
</Else>
</xmod:Select>
<td rowspan="2">[[SiteMapPriority]]</td>
<td rowspan="2">
<xmod:Redirect Display="linkbutton" Target="http://www.webconfs.com/keyword-density-checker.php" Method="post" Text="chk kwds" ToolTip="check the page's content keyword density">
<Field name="url" value='[[Join("{0}{1}/TabID/{2}/Default.aspx", [[Portal:Alias]],[[TabPath]],[[TabID]])]]' />
</xmod:Redirect>
</td>
</tr>
<tr class="generic-items-alt">
<xmod:Select>
<Case comparetype="text" value='[[DescriptionFlag]]' operator="=" expression="1">
<td class="highlight">
- description missing -
</td>
</Case>
<Else>
<td>
<nobr>
<span class="sm-font">
<xmod:Format type="regex" value='[[Description]]' pattern="(.*)" replacement="$1" maxlength="70" />
</span>
<xmod:ToggleLink text="+" target='[[Join("#divMoreInfo_{0}a",[[TabId]])]]' speed="Fast" />
</nobr>
<div id="divMoreInfo_[[TabId]]a" style="display:none;">
<br/>
<span class="sm-font">[[Description]]</span>
</div>
</td>
</Else>
</xmod:Select>
<xmod:Select>
<Case comparetype="text" value='[[KeywordsFlag]]' operator="=" expression="1">
<td class=""highlight">
- keywords missing -
</td>
</Case>
<Else>
<td>
<nobr>
<span class="sm-font">
<xmod:Format type="regex" value='[[Keywords]]' pattern="(.*)" replacement="$1" maxlength="70" />
</span>
<xmod:ToggleLink text="+" target='[[Join("#divMoreInfo_{0}b",[[TabId]])]]' speed="Fast" />
</nobr>
<div id="divMoreInfo_[[TabId]]b" style="display:none;">
<br/>
<span class="sm-font">[[Keywords]]</span>
</div>
</td>
</Else>
</xmod:Select>
</tr>
</AlternatingItemTemplate>
<NoItemsTemplate>
Seriously? no pages in a DNN instance, in this portal? not possible is it?
</NoItemsTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
<Pager PageSize="60" PageNumCssClass="CommandButton" FirstPageCaption="[First]" LastPageCaption="[Last]">
<table>
<tr>
<td>Page <strong>{PageNumber}</strong> of {PageCount}</td>
<td align="right">{Pager}</td>
</tr>
</table>
</Pager>
<SearchSort FilterExpression="TabName LIKE '%{0}%'"
SearchLabelText="Search By TabName:" SearchButtonText="GO"
SortFieldNames="TabId,TabName,Title,Description,Site Map Priority"
SortFieldLabels="TabId,TabName,Title,Description,SiteMapPriority"
ReverseSortText="Reverse Sort Order">
<table>
<tr>
<td style="padding-right:20px; border-right:1px solid silver;" class="sm-font"><strong>{SearchLabel}</strong> {SearchBox} {SearchButton}
</td>
<td align="right" style="padding-left:20px;" class="sm-font"><strong>{SortLabel}</strong> {SortFieldList} {SortButton} - {ReverseSort}
</td>
</tr>
</table>
</SearchSort>
</xmod:Template>
Enjoy The Results
Now, at a glance, you can review all of the tabs of the website on one page. Here are some of the features of the page:
- Any pages that are missing either the Title or Meta Keywords or Meta Description will highlight that table cell in red.
- Each cell for Keywords and Description allow you to expand/collapse to view all of the details
- At the far right is a link to a web tool that we use that does a simple processing of the page to return a word count for all the content of the page. This can be used to help you evaluate the text content that exists on a page. You can use this to further improve the keywords that are listed on your page, or to suggest them when where you have blanks. Just look at the list (sorted by most used words to least used words) under the Keyword Density title.
Some notes for further development:
I think you'll find that when we're preparing an XMOD item like this, we're putting together the quick and dirty code to get it working fast. There are always improvements and additions that can be made to things like this... we've used it for a while in this state and haven't desperately needed new items, but we have had a few ideas. Would love to see your thoughts and ideas for additions too!
- We could add an additional template at the top that shows the Portal Settings main Title, Description and Keywords since, obviously, any pages not specified will show that info as backup/default.
- One of our crew had discussed making a js for "expand/collapse all"
Thanks for reading everyone, I hope you try out the tool and get some use out of it!
Ryan - MooreCreative
P.S. - We later put this together as a Module Kit available for free in the DNN Store:
http://store.dnnsoftware.com/home/product-details/dnn-meta-manager-for-xmod-pro
MooreCreative XMOD Development Blog
In the MooreCreative XMOD PRO Development Blog, I'll try to share some of the thoughts and ideas that we've come up with on a day-to-day basis as my development company uses DNN + XMOD PRO to tackle client projects. I'll include items such as tips/tricks, did-you-know articles, troubleshooting/testing examples as well as posting samples solutions and explanations behind our experience working with XMOD and jQuery functions, PageBlaster, ZLDNN Article, and other modules/elements.
If you would like to see us tackle a topic, especially anything out of the forums that could use further examples, please, don't hesitate to email me, and I'll look into preparing it for a blog post.