65.9K
CodeProject 正在变化。 阅读更多。
Home

SharePoint 最受欢迎内容 Web 部件

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2009年1月15日

CPOL

5分钟阅读

viewsIcon

144176

downloadIcon

463

创建一个显示最受欢迎内容的 Web 部件

引言

我目前在一家有线电视公司工作,该公司希望设置一个公共 SharePoint 网站,允许客户查看有助于他们设置家庭系统(网络、Xbox 等)的内容。一位同事提出的一个好主意是在默认页面上显示一个 Web 部件,以显示我们客户查看次数最多的内容。这将使我们的客户能够轻松访问其他客户最常见问题的解决方案。

背景

如果您对 SharePoint 足够了解,那么您可能已经知道使用情况报告。您需要做的第一件事是为您的网站集设置使用情况报告。一篇很好的教程可以帮助您完成此操作,请在此处查看:此处。如果您使用过它们,那么您知道它们在功能方面非常有限。因此,我深入研究(反射 SharePoint DLL)发现,默认每晚运行的使用情况分析作业会填充位于您的 SharedServicesDB 中的 ANL<suffix> 表。

Anl_Tables.png

  • ANLResource - 一行代表 SharePoint 中的任何给定内容项,无论是 PDF、ASPX 页面等。此表中没有重复项。
  • ANLHit - 一行代表用户对特定资源的单个点击。

ANLResource 表非常有用,因为它包含两个非常重要的字段:WebGuidSiteGuid。这将允许我们将查询范围限定为用户当前正在浏览的网站。另外,另一个重要字段是 ANLHit.UserId,因为它允许我们将查询范围限定为特定用户。然后,我们将只能显示用户点击过的内容。太棒了!!是吧?

请记住,使用情况报告每晚运行一次。因此,请注意,即使您点击链接一千次,在作业运行之前,Web 部件中也不会反映出任何更改。

安装

希望大多数人都知道如何安装 Web 部件,但如果您需要帮助,请按照以下步骤操作

  1. Mullivan.Shared.dllMullivan.SharePoint.WebParts.dll 添加到 GAC。
  2. 在 SharePoint Web Config 中注册 Mullivan.SharePoint.WebParts.dll

    转到 C:\inetpub\wwwroot\wss\VirtualDirectories\<Port Number>\web.config。在 SafeControls 节点之间添加以下内容

    <SafeControl 
     Assembly="Mullivan.SharePoint.WebParts, Version=1.0.0.0, 
               Culture=neutral, PublicKeyToken=c37a514ec27d3057"
     Namespace="Mullivan.SharePoint.WebParts" TypeName="*" Safe="True" />
  3. 转到网站设置 -> Web 部件 -> 点击菜单上的“新建”。

    滚动到底部并勾选 Mullivan.SharePoint.WebParts.MostViewedWebPart,然后滚动回顶部并点击“填充库”。

完成!您应该会在您的 Web 部件集合中看到它。

配置

Most_Viewed_Content_Config.png

上面显示的是此 Web 部件的属性窗格。我将在下面解释每个字段

  • 仅用户 - 如果选中,则将查询范围限定为仅显示当前用户点击过的内容。
  • 范围 - 将查询设置为从所有网站中提取内容,或仅从 Web 部件所在的网站中提取内容。
  • 返回计数 - 查询应返回的最大项目数。
  • 跨度 - 查询应从现在开始追溯的天数,并据此确定点击次数。
  • 扩展名 - 文件扩展名的逗号分隔列表,将查询限制为特定文件类型。因此,如果您只想显示 PDF 和 Word 文档,那么您可以使用“pdf, doc, docx”。

代码

您准备好了吗??我有点恶作剧,并且花很多时间反射 DLL。我不喜欢花时间阅读书本上的文字,而是喜欢直接上手弄清楚一切是如何工作的。:P

所以,我仔细思考(在我看来,就是 5 分钟)如何与共享服务数据库建立连接。好吧,使用反射,我发现 SPUsageSite.aspx 上的 TopPages 控件使用 PortalContext 对象上的一个名为“AnalyticsSqlSession”的内部属性。所以,我决定使用反射将其提取出来并获得访问权限。您可能会觉得这很疯狂,但它避免了我存储到该数据库的某种连接字符串。

private SqlDataReader GetSqlReader(SqlCommand cmd)
{
    PortalContext pContext = PortalApplication.GetContext();
    Type tContext = pContext.GetType();
    PropertyInfo pSqlSession = tContext.GetProperty("AnalyticsSqlSession", 
                 BindingFlags.NonPublic | BindingFlags.Instance);
    object sqlSession = pSqlSession.GetValue(pContext, null);

    Type tSqlSession = sqlSession.GetType();
    MethodInfo mExecute = tSqlSession.GetMethod("ExecuteReader", 
                          new Type[1] { typeof(SqlCommand) });
    return mExecute.Invoke(sqlSession, new object[1] { cmd }) as SqlDataReader;
}

所以现在,我们将要生成查询字符串。我们要确保可能频繁更改的值作为 SQL 参数构建到查询中。所以,我们将使用开始日期、用户名和网站 GUID 作为 SQL 参数。TOP 和文件扩展名不会改变,因此查询应该被缓存并在 SQL Server 数据库中重用。

private string GetQueryText(bool isUserQuery, int returnCount, 
               MostViewedScope scope, string[] extensions)
{
    string query = @"
        SELECT TOP {0} ANLResource.ResourceId AS ResourceId, 
              ANLResource.DocName AS DocName, 
              ANLResource.FullUrl AS FullUrl, 
              ANLResource.WebGuid AS WebGuid,
              COUNT_BIG(*) AS HitCount 
        FROM ANLHit 
        INNER JOIN ANLResource 
        ON ANLHit.ResourceId = ANLResource.ResourceId";
                    if (isUserQuery)
                    {
                        query += @"
                            INNER JOIN ANLUser
                            ON ANLHit.UserId = ANLUser.UserId";
                    }

    query += @"
        INNER JOIN ANLDay
        ON ANLHit.DayId = ANLDay.DayId

        WHERE 

        ANLDay.FullDate > @StartDate";

    if (scope == MostViewedScope.CurrentSite)
        query += "AND ANLResource.WebGuid = @WebGuid";
    else
        query += "AND ANLResource.SiteGuid = @SiteGuid";

    if (isUserQuery)
    {
        query += @"AND ANLUser.UserName = @UserName";
    }

    if (extensions != null && extensions.Length > 0)
    {
        query += @"AND (";
        for (int i = 0; i < extensions.Length; i++)
        {
            if (i != 0)
                query += " OR ";

            query += string.Format("(CHARINDEX('.{0}', ANLResource.DocName) > 0)", 
                                   extensions[i].Trim());
        }

        query += ") ";
    }

    query += @"
        GROUP BY ANLResource.ResourceId,
                 ANLResource.DocName,
                 ANLResource.FullUrl,
                 ANLResource.WebGuid
 
        ORDER BY HitCount DESC";

    return string.Format(query, returnCount); ;
}

我知道这看起来有点混乱,但这才是需要做到的一步。下面是生成查询后的示例

SELECT TOP 10 ANLResource.ResourceId AS ResourceId, 
              ANLResource.DocName AS DocName, 
              ANLResource.FullUrl AS FullUrl, 
              ANLResource.WebGuid AS WebGuid,
              COUNT_BIG(*) AS HitCount 
FROM ANLHit 
INNER JOIN ANLResource 
ON ANLHit.ResourceId = ANLResource.ResourceId
INNER JOIN ANLUser
ON ANLHit.UserId = ANLUser.UserId
INNER JOIN ANLDay
ON ANLHit.DayId = ANLDay.DayId

WHERE 

ANLDay.FullDate > @StartDate
AND ANLResource.WebGuid = @WebGuid
AND ANLUser.UserName = @UserName
AND ((CHARINDEX('.aspx', ANLResource.DocName) > 0) 
OR (CHARINDEX('.pdf', ANLResource.DocName) > 0) 
OR (CHARINDEX('.docx', ANLResource.DocName) > 0) 
OR (CHARINDEX('.doc', ANLResource.DocName) > 0)) 

GROUP BY ANLResource.ResourceId,
         ANLResource.DocName,
         ANLResource.FullUrl,
         ANLResource.WebGuid
         
ORDER BY HitCount DESC

好的……现在,通过调用 GetReportData() 方法来执行我们的查询。

private DataTable GetReportData()
{
    string currentUser = null;

    if (this.IsUserQuery
        && this.Page.User != null
        && this.Page.User.Identity != null)
        currentUser = this.Page.User.Identity.Name;

    DataTable table = null;
    using (SqlCommand command = new SqlCommand())
    {
        SqlDataReader reader = null;
        command.CommandText = GetQueryText(!string.IsNullOrEmpty(currentUser), 
                              this.ReturnCount, this.Scope, this.Extensions);
        command.CommandType = CommandType.Text;

        if (!string.IsNullOrEmpty(currentUser))
        {
            SqlParameter spUserName = 
              new SqlParameter("@UserName", SqlDbType.NVarChar, 50);
            spUserName.Value = currentUser;
            command.Parameters.Add(spUserName);
        }

        SqlParameter spScope = null;
        if (this.Scope == MostViewedScope.CurrentSite)
        {
            spScope = new SqlParameter("@WebGuid", SqlDbType.UniqueIdentifier);
            spScope.Value = SPControl.GetContextWeb(this.Context).ID;
        }
        else
        {
            spScope = new SqlParameter("@SiteGuid", SqlDbType.UniqueIdentifier);
            spScope.Value = SPControl.GetContextSite(this.Context).ID;
        }

        command.Parameters.Add(spScope);

        SqlParameter spStartDate = new SqlParameter("@StartDate", SqlDbType.DateTime);
        spStartDate.Value = DateTime.Today.Subtract(
                              TimeSpan.FromDays(Convert.ToDouble(this.SpanDays)));
        command.Parameters.Add(spStartDate);

        table = new DataTable();
        table.Locale = CultureInfo.InvariantCulture;
        reader = GetSqlReader(command);
        try
        {
            table.Load(reader);
            return table;
        }
        finally
        {
            if (reader != null)
            {
                reader.Dispose();
            }
        }
    }
}

好了,现在,让我们在部分中渲染我们的数据。

protected override void OnLoad(EventArgs e)
{
    try
    {
        _data = GetReportData();
    }
    catch (Exception ex)
    {
        _errorMessage = ex.ToString();
    }

    base.OnLoad(e);
}

protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
    if (!string.IsNullOrEmpty(_errorMessage))
    {
        writer.Write(HttpUtility.HtmlEncode(_errorMessage).Replace("\n", "<br/>"));
        return;
    }

    StringBuilder sb = new StringBuilder();

    #region "Html"
    string htmlItem = @"
        <tr>
            <td class=""ms-propertysheet"" style=""padding-left:1px"">
                <table cellspacing=""0"" cellpadding=""0"" width=""100%"" border=""0"">
                    <tr>
                        <td valign=""top"" nowrap=""nowrap"" 
                                    class=""ms-descriptiontext"" 
                                    width=8px style=""padding-top:5px;"">
                            <IMG src=""{2}"" width=16px height=16px alt='' > 
                        </td>
                        <td valign=top class=""ms-descriptiontext"" 
                          style=""padding-top:7px;padding-left: 3px;"">
                            <a href=""{0}"" title=""{3}"">{1}</a>
                        </td>
                    </tr>
                </table>
            </td>
        </tr>
        ";

    string htmlTable = @"
        <table width=100% cellpadding=0 cellspacing=0 border=0>
            <tr>
                <td nowrap class=""ms-linksectionheader"" 
                           style=""padding: 4px;"" width=""100%"">
                    <H3 class=""ms-standardheader"">
                        {0}
                    </H3>
                </td>
            </tr>
            <tr>
                <td height=""1px"">
                    <IMG SRC=""/_layouts/images/blank.gif"" width=1 height=1 alt="""">
               </td>
            </tr>
            <tr>
                <td width=""100%"" style=""padding: 0px 4px 4px 4px;"" colspan=""2"">
                    <table cellpadding=""0"" cellspacing=""0"" border=""0"">
                        <tr>
                            <td valign=""top"" class=""ms-descriptiontext"" 
                                style=""padding-top:5px"">
                            </td>
                        </tr> 
                        {1}
                    </table>
                </td>
            </tr>
            <tr>
                <td height=""15px"">
                 <IMG SRC=""/_layouts/images/blank.gif"" 
                      width=1 height=15 alt="""">
                </td>
            </tr>
        </table>
        ";
    #endregion "Html"

    //DataRow dr1 = _data.NewRow();
    //dr1["DocName"] = "Pages/mypage1.aspx";
    //dr1["FullUrl"] = "http://sullyserver2008/sites/mullivan/Pages/mypage1.aspx";
    //dr1["HitCount"] = 2345L;
    //dr1["WebGuid"] = new Guid("{db7e891b-18c4-4c00-8c99-160ecbcee67f}");
    //_data.Rows.Add(dr1);
    foreach (DataRow dr in _data.Rows)
    {
        string docName = dr["DocName"].ToString();
        string fullUrl = dr["FullUrl"].ToString();
        long hitCount = (long)dr["HitCount"];
        string desc = string.Format("This file has been requested {0} " + 
                      "times in the past {1} days.", hitCount, this.SpanDays);
        Guid webGuid = (Guid)dr["WebGuid"];
        string extUrl = "/_layouts/images/icgen.gif";

        try
        {
            SPSite site = SPControl.GetContextSite(this.Context);
            using (SPWeb web = site.AllWebs[webGuid])
            {
                string relativeUrl = fullUrl;
                int idx = relativeUrl.IndexOf(web.ServerRelativeUrl,
                    StringComparison.InvariantCultureIgnoreCase);

                if (idx > -1)
                    relativeUrl = 
                       relativeUrl.Substring(idx, relativeUrl.Length - idx);

                SPListItem spListItem = web.GetListItem(relativeUrl);
                docName = spListItem.Title;

                object icon = spListItem["DocIcon"];
                if (icon != null)
                    extUrl = string.Format("/_layouts/images/ic{0}.gif", 
                                           Convert.ToString(icon));
            }
        }
        catch
        {
            //Item doesn't exist
            continue;
        }

        sb.Append(string.Format(htmlItem, fullUrl, docName, extUrl, desc));
    }

    writer.Write(string.Format(htmlTable, this.Title, sb.ToString()));
}

好的……基本上就是这样。此 Web 部件附带一个用于配置的编辑器,但我不会深入介绍。有很多博客可以帮助您了解它是如何工作的。

关注点

该项目还包括另外两个 Web 部件。天气 Web 部件来自 Mossman 的博客,您可以在此处找到:此处。另一个是我昨天博客中介绍的 Web 部件。它是一个非常棒的 Web 部件,可以显示导航栏,请在此处查看:此处

结论

请告诉我您的想法。如果您有任何想法,我会仔细考虑并看看是否可以提供更新。我希望您发现这个 Web 部件很有用。

历史

  • 2009年1月15日:初始帖子
  • 2009年3月19日:更新了源代码
  • 2009 年 3 月 24 日:已更新源代码
© . All rights reserved.