将 Telerik RadControls 与 DotNetNuke 结合使用。完整的评分和评论解决方案






4.77/5 (8投票s)
本教程展示了在 DotNetNuke 中使用 Telerik 控件。它将引导您完成为整个 DotNetNuke 页面或任何其他由 URL 标识的内容创建评分/评论解决方案的过程。

引言
DotNetNuke 5.2+ 附带功能强大的 Telerik 控件库。鉴于 Telerik 控件是典型的 ASP.NET 库,您可能会认为需要 Visual Studio 来创建使用出色的 Telerik 控件和功能的模块。在这里,我将向您展示如何在不开发和部署任何模块的情况下将 Telerik 控件添加到您的 DotNetNuke 网站页面。您需要的所有工具都是免费且开源的。您需要 DotNetNuke 社区版 5.2 或更高版本以及 XsltDb 01.01.24 或更高版本。
对于这个 Telerik-DotNetNuke 教程,一个非常好的任务是创建一个页面评分/评论系统。对于评分,我使用了两个 Telerik 控件:RadRating 和 RadChart,并实现了一个类似于 codeproject 文章评分的评分系统。评分子系统必须支持以下功能:
- 显示当前页面评分,
- 显示投票柱状图:投票如何按值分布,
- 允许访问者投票,
- 使用 cookie 控制访问者投票,如果访问者已经投票,系统将删除之前的投票。这允许访问者更正/更改他们的投票,但可以防止投票重复。
对于评论,我使用 RadEditor 来输入评论文本,并使用 RadComboBox 来输入姓名/电子邮件/网站。为什么选择 Combo?RadTextBox 不支持圆角。但是,可以设置一个组合框来输入文本而不使用下拉功能。我相信 Telerik 在未来的版本中将支持 RadTextBox 和 RadEditor 的圆角。评论子系统必须支持以下功能:
- 按时间顺序排列的评论列表,
- 评论提交表单,
- 评论者头像,
- 网站管理员删除评论,
- 新评论电子邮件通知。
还有一件事需要定义。评分/评论的对象是什么?实际上,我们不需要知道对象是什么,只需要它的 ID。在 Web 中,我们可以可靠地使用 URL 作为内容 ID。实际 URL 可能包含其他不标识内容的参数,因此在构建要评分/评论的内容 ID 时必须将其过滤掉。
内容识别
有两种方法可以获取当前 URL
- URL 重写后:mdo:aspnet('Request.Url.PathAndQuery')
- URL 重写前:mdo:aspnet('Request.RawUrl')
您必须决定哪种方法适合您。我在这里使用原始客户端 URL。因此,PageID 的构建如下:
<xsl:variable name="page-id" select="mdo:aspnet('Request.RawUrl')" />
为了提供可靠的门户识别,我们使用相对 URL 和 XsltDb 的数据隔离功能。因此,用于已识别内容的数据库表将如下所示:
create table {databaseOwner}MDO_PageRating
(
PageID int identity primary key,
PortalID int,
PageGuid nvarchar(256)
)
为了简化内容识别,我们使用以下组合存储过程,该过程检查页面是否已识别,如果未识别,则插入新记录并返回其 ID:
create procedure {databaseOwner}MDO_PageRating_EnsurePage
@PortalID int,
@PageGuid nvarchar(max),
@PageID int out
as
begin
select @PageID = PageID from MDO_PageRating
where PageGuid = @PageGuid and PortalID = @PortalID
if @PageID is null begin
insert {databaseOwner}MDO_PageRating(PortalID, PageGuid)
values(@PortalID, @PageGuid);
set @PageID = SCOPE_IDENTITY();
end;
end;
)
现在我们可以开始评分了。
评分子系统
数据库对象
首先,我们必须创建一个存储每次投票的表。
create table {databaseOwner}MDO_PageRating_Rate
(
RateID int identity primary key,
PageID int constraint MDO_PageOfRating foreign key references MDO_PageRating(PageID)
on delete cascade on update cascade,
Value float,
Cookie uniqueidentifier
)
我们需要 2 个存储过程:用于注册新投票和检索有关投票的信息。新投票注册
create procedure {databaseOwner}[{objectQualifier}mdo_xslt_rate_page]
@PortalID int,
@PageGuid nvarchar(max),
@Value float,
@Cookie nvarchar(max)
as
begin
declare @PageID int;
exec {databaseOwner}MDO_PageRating_EnsurePage @PortalID, @PageGuid, @PageID out;
delete MDO_PageRating_Rate
where PageID = @PageID
and Cookie = @Cookie;
insert {databaseOwner}MDO_PageRating_Rate(PageID, Value, Cookie)
values(@PageID, @Value, @Cookie);
end;
投票和评分查询
create procedure {databaseOwner}[{objectQualifier}mdo_xslt_page_rating] @PortalID int, @PageGuid nvarchar(max) as begin select pr.* from {databaseOwner}MDO_PageRating_Rate pr join {databaseOwner}MDO_PageRating p on p.PageID = pr.PageID where p.PageGuid = @PageGuid and p.PortalID = @PortalID; select SUM(StatCount) TotalVotes, MAX(StatCount) Height, SUM(StatValue * StatCount) / SUM(StatCount) Average from ( select pr.Value StatValue, COUNT(*) StatCount from {databaseOwner}MDO_PageRating_Rate pr join {databaseOwner}MDO_PageRating p on p.PageID = pr.PageID where p.PageGuid = @PageGuid and p.PortalID = @PortalID group by pr.Value) t; end;
此存储过程返回 2 个结果集:投票列表和柱状图的评分摘要。在评分摘要中,我们有投票总数、最大柱状图高度和当前平均评分。
显示当前评分
为了显示当前内容的评分,我们创建了 2 个对象 RadRating – 显示页面的平均评分,以及 RadChart – 显示投票分布。所以我们进行 3 个步骤:
1. 从数据库读取当前页面评分信息
<xsl:variable name="rating" select="mdo:xml('page-rating', 'rating, total', $page-id)" />
2. 创建一个 RadRating 控件来显示当前评分
<telerik:RadRating ID="PageRating" runat="server" Value="{mdo:coalesce($rating//Average, 0)}" ReadOnly="True" Skin="Office2007" />
3. 创建 RadChart 控件来显示投票分布
<telerik:RadChart runat="server" ID="RadChart2" ChartTitle-Visible="false" Width="100" Height="50" Skin="Office2007" >
<Series>
<telerik:ChartSeries Type="Bar">
<Items>
<xsl:for-each select="mdo:sequence(1,5,1)">
<telerik:ChartSeriesItem XValue="{current()}"
YValue="{count($rating//rating[Value=current()])+0.3}"
Label-TextBlock-Text="{count($rating//rating[Value=current()])}"/>
</xsl:for-each>
</Items>
</telerik:ChartSeries>
</Series>
<PlotArea Appearance-Dimensions-Margins="0,0,0,0">
<XAxis AutoScale="False" MinValue="0.5" MaxValue="5.5" Step="1" />
<YAxis AutoScale="False" MinValue="0"
MaxValue="{mdo:coalesce($rating//Height,0) + 1}"
Visible="False" />
</PlotArea>
<Legend Visible="false"/>
</telerik:RadChart>
上面的一些列表片段需要澄清。
- <xsl:for-each select="mdo:sequence(1,5,1)"> 执行从 1 到 5 的循环。XSL 不支持循环变量,因此 XsltDb 提供了一个 mdo:sequence 函数,它返回 XML 节点,这些节点实际上是循环变量的值列表。当前值可以使用标准 xsl 函数 current() 捕获。
- YValue="{count($rating//rating[Value=current()])+0.3}" 允许我们在没有此类投票的情况下创建高度为 0.3 的条形。
- MaxValue="{mdo:coalesce($rating//Height,0) + 1}" 分配一个垂直刻度以在条形上方留出数字空间。
评分输入控件
为了让访问者投票,我们创建了另一个 RadRating 控件,如下所示:
<telerik:RadRating ID="PageRating2" runat="server" Value="{mdo:coalesce($rating//Average, 0)}" OnClientRated="onClientRated" Skin="Office2007"
>
<Items>
<telerik:RadRatingItem Value="1" ToolTip="Poor" />
<telerik:RadRatingItem Value="2" ToolTip="Fair" />
<telerik:RadRatingItem Value="3" ToolTip="Good" />
<telerik:RadRatingItem Value="4" ToolTip="Excellent" />
<telerik:RadRatingItem Value="5" ToolTip="Awesome" />
</Items>
</telerik:RadRating>
为了更好地理解星级的含义,我为 RadRating 控件中的每个星级分配了一个有意义的标签。最初,它显示了页面的当前评分 (Value="{mdo:coalesce($rating//Average, 0)}") 并订阅了一个评分事件 (OnClientRated="onClientRated")。JavaScript 函数 onClientRated 非常简单:
function onClientRated(sender, args)
{
{{mdo:submit('@rate', '@sender.get_value()')}}
}
它只是获取访问者提供的评分值并将其提交到服务器。请注意,@rate 中的 @ 符号表示 XsltDb 只保留 @rate 的值一个请求(类似命令的行为)提交值中的 @ 符号表示这是 javascript 代码,必须在提交之前对其进行评估。
在评分提交到服务器后,我们必须将其保存到数据库。代码很简单:
<xsl:if test="mdo:param('@rate')">
<xsl:if test="not(mdo:cookie('rater'))">
<xsl:if test="mdo:set-cookie('rater', mdo:newid(), '2020-01-01')" />
</xsl:if>
<xsl:if test="mdo:xml('rate-page', '$script', $page-id, mdo:param('@rate'), mdo:cookie('rater'))" />
{{mdo:redirect(mdo:aspnet('Request.RawUrl'))}}
</xsl:if>
您可以看到,只需要 3 个步骤即可将评分值发送到数据库。
首先,检查访问者是否已识别。如果没有,则创建一个 cookie。我使用 mdo:newid() 作为 cookie 值。mdo:newid() 生成一个 guid,因此访问者是唯一标识的。
其次,将新的评分值发送到数据库。DB 将检查访问者是否是新访客并插入新的评分,或者访问者是否已对该内容评分,DB 将替换之前的投票。
第三,我们重定向到当前页面。这并非必需,但我建议进行此重定向,因为:
- 您结束了页面生命周期并开始了新的页面生命周期,因此您的标准 ASP.NET 控件和 Telerik 控件将恢复到初始状态。这使我们无需编写清理代码即可将控件值重置为初始状态,而不会破坏 ASP.NET 页面生命周期。
- 当访问者按 F5 时,他不会看到是否要重新发送数据到服务器的难看的消息框。它还防止数据向服务器重复提交。
评分子系统已基本完成,切换到评论子系统
评论子系统
数据库对象
与评分一样,这里我们需要一个表来存储评论,以及 3 个存储过程来检索评论列表、将新评论保存到数据库以及按管理员删除评论。
create table {databaseOwner}MDO_PageRating_Comment
(
CommentID int identity primary key,
ParentID int,
PageID int constraint MDO_PageOfComment foreign key references MDO_PageRating(PageID)
on delete cascade on update cascade,
Comment nvarchar(max),
EMail nvarchar(128),
URL nvarchar(128),
Name nvarchar(128),
DTCreated datetime default getdate()
)
create procedure {databaseOwner}[{objectQualifier}mdo_xslt_comment_page]
@PortalID int,
@PageGuid nvarchar(max),
@ParentID int,
@Comment nvarchar(max),
@Name nvarchar(max),
@EMail nvarchar(max),
@URL nvarchar(max)
as
begin
declare @PageID int;
exec {databaseOwner}MDO_PageRating_EnsurePage @PortalID, @PageGuid, @PageID out;
if @ParentID < 1 set @ParentID = null;
insert {databaseOwner}MDO_PageRating_Comment(ParentID,PageID,Comment,EMail,URL,Name)
values(@ParentID,@PageID,@Comment,@EMail,@URL,@Name);
end;
create procedure {databaseOwner}[{objectQualifier}mdo_xslt_page_comments]
@PortalID int,
@PageGuid nvarchar(max)
as
begin
select c.* from {databaseOwner}MDO_PageRating_Comment c
join {databaseOwner}MDO_PageRating p on p.PageID = c.PageID
where p.PageGuid = @PageGuid
and p.PortalID = @PortalID;
end;
create procedure {databaseOwner}[{objectQualifier}mdo_xslt_delete_page_comment]
@PortalID int,
@CommentID int
as
begin
delete {databaseOwner}MDO_PageRating_Comment
where CommentID = @CommentID
and PageID in (select PageID from {databaseOwner} MDO_PageRating where PortalID = @PortalID);
end;
评论输入表单
我喜欢圆角,所以我想使用 Telerik 控件来创建圆角输入框。问题是 RadTextBox 不提供圆角选项。因此,我改用 RadComboBox。以下代码使用 RadComboBox 创建了一个具有圆角的输入框:
<telerik:RadComboBox runat="server" ShowToggleImage="False" ShowDropDownOnTextboxClick="False" AllowCustomText="True"/>
这种方法适用于评论者的姓名、电子邮件和网站字段。我还希望我的评论易于阅读,所以我为评论本身使用了 RadEditor。我只允许评论者使用基本格式,因此我必须列出评论窗口中允许的工具:
<telerik:RadEditor runat="server" ID="Message" EditModes="Design" Width="491px" Height="200px">
<Tools>
<telerik:EditorToolGroup >
<telerik:EditorTool Name="Bold" />
<telerik:EditorTool Name="Italic" />
<telerik:EditorTool Name="Underline" />
</telerik:EditorToolGroup>
<telerik:EditorToolGroup >
<telerik:EditorTool Name="InsertUnorderedList" />
<telerik:EditorTool Name="InsertOrderedList" />
</telerik:EditorToolGroup>
<telerik:EditorToolGroup >
<telerik:EditorTool Name="JustifyLeft" />
<telerik:EditorTool Name="JustifyCenter" />
<telerik:EditorTool Name="JustifyRight" />
<telerik:EditorTool Name="JustifyFull" />
</telerik:EditorToolGroup>
<telerik:EditorToolGroup >
<telerik:EditorTool Name="Cut" />
<telerik:EditorTool Name="Copy" />
<telerik:EditorTool Name="Paste" />
</telerik:EditorToolGroup>
</Tools>
</telerik:RadEditor>
在 RadEditor 下面,我放了一个提交评论表单的按钮:
<input type="button" onclick="{mdo:jsubmit('@comment', 'yes')}" value="Save Comment" />
我想让我的“发送评论”按钮像其他控件一样使用 Office2007 皮肤进行样式设置。为此,我使用 RadFormDecorator,它允许我将皮肤应用于页面的特定区域。
<telerik:RadFormDecorator runat="server" DecorationZoneID="rating-and-comments" Skin="Office2007"/>
<!-- All controls go here -->
<div id="rating-and-comments">
</div>
服务器端评论处理
评论提交后,我们必须将其存入数据库,并通知网站/页面所有者内容已被评论。代码如下:
<xsl:if test="mdo:param('@comment')">
<xsl:if test="mdo:xml('comment-page', '$script', $page-id, -1,
mdo:url-decode(mdo:request(mdo:client-name('Message'))),
mdo:request(mdo:client-name('Name')),
mdo:request(mdo:client-name('EMail')),
mdo:request(mdo:client-name('Url'))
)" />
<xsl:variable name="mail">
<p>
New comment added to the site http://xsltdb.com.
<a href="http://xsltdb.com{mdo:aspnet('Request.RawUrl')}">Click here to read comments.</a>
</p>
</xsl:variable>
<xsl:if test="mdo:mail('admin@xsltdb.com', 'admin@xsltdb.com', 'New comments added', mdo:text($mail))" />
{{mdo:redirect(mdo:aspnet('Request.RawUrl'))}}
</xsl:if>
在这里,我们进行 3 个步骤:
1. mdo:xml('comment-page', '$script'…) 将新评论保存到数据库。这里我想澄清如何检索 ASP.NET 控件的值。通常,ASP.NET 控件有一个相关的隐藏输入来存储值。您可以使用 mdo:client-name() 函数获取控件的名称。此函数获取 ASP.NET 控件的本地 ID 并返回控件隐藏输入的全局客户端名称。之后,您可以使用 mdo:request() 函数获取值。RadEditor 在此输入中存储 URL 编码的文本,因此在将其发送到数据库之前,我们必须使用 mdo:url-decode() 函数对其进行解码。
2. 评论保存到数据库后,我们使用 mdo:mail() 函数创建并发送一封电子邮件给网站管理员。此函数发送我们构建为 XSL 变量并使用 mdo:text() 函数转换为字符串的 HTML 消息。
3. 处理完成后,我们使用 mdo:redirect() 重新启动 ASP.NET 页面生命周期,正如我们对评分所做的那样。
显示评论
为了显示页面的评论列表,我们首先从数据库读取评论列表:
<xsl:variable name="comments" select="mdo:xml('page-comments', 'comment', $page-id)" />
以下代码显示评论列表:
<xsl:for-each select="$comments//comment">
<div style="width:100px;float:left;">
<img src="http://www.gravatar.com/avatar/{mdo:md5(EMail)}" />
<div style="color:grey">
<a rel="external nofollow" style="font-size:smaller" href="{URL}">{{Name}}</a>,
{{mdo:fmt-date(DTCreated, 'd')}}
</div>
</div>
<div style="float:left;background-color:white;width:375px;padding:10px;border:1px solid silver;">
{h{Comment}}
<div style="padding-top:45px;">
<xsl:if test="mdo:isinrole('Administrators')">
<a href="{mdo:jsubmit('@delete-comment', CommentID)}" class="CommandButton">Delete</a>
</xsl:if>
</div>
</div>
<div style="clear:both"/>
</xsl:for-each>
此代码为每条评论显示以下内容:
- 评论者图片。这非常简单,因为我们有 gravatar 服务。只需获取电子邮件并创建一个评论者图片 URL:src="http://www.gravatar.com/avatar/{mdo:md5(EMail)}"
- 评论文本以 HTML 显示。{h{Comment}} 由于我们使用 RadEditor,我们可以确保 HTML 不包含脚本部分。
- 如果当前用户是门户管理员,他必须能够删除评论。因此,我们在 <xsl:if test="mdo:isinrole('Administrators')"> 之后有一个代码块来执行此操作。
结论
本教程演示了如何创建具有平面评论的简单评分/评论系统。在 实时演示 中,我们可以看到稍微复杂一些的带回复的评论。这不包含在文章文本中,但文章附件包含来自 xsltdb.com 网站的真实代码,该代码支持评论回复。我将此功能排除在文章之外是为了减小文章的篇幅,因为它没有展示 Telerik Controls 或 XsltDb Module 的独特功能。
使用文章附带的代码
要使用此代码,您需要 DotNetBuke 5.2 或更高版本,推荐版本为 5.4.2。部署非常简单:
- 以超级用户身份登录您的 DotNetNuke 网站。
- 安装 XsltDb 模块,在此处下载
- 创建数据库对象。转到 Host/SQL 选项卡,将 DatabaseObject.sql 粘贴到文本框中,选择 RunAsScript,然后单击 Execute。
- 导航到您想要评分/评论的页面。将新的 XsltDb 模块添加到页面。点击 EditXSLT 链接。将 ModuleCode.xslt 粘贴到文本区域。查看代码并更改您的电子邮件和 URL。点击 Update & Publish。
- 享受评分/评论。
用于创建评论/评分模块的工具
- Windows Server 2008 / IIS 7.5 / .NET 3.5
- DotNetNuke 5.4.2 社区版,内置 Telerik RadControls
- XsltDb DotNetNuke 集成模块