Страницы

Tuesday, March 13, 2012

Голосование своими руками в SharePoint 2010

Рис. 1.
Доброго времени суток, дорогие коллеги и просто читатели. Как часто, работая с SharePoint 2010 вам приходится, слышать просьбы, о создании голосований с изображениями? Для тех кому ("хватит это терпеть" (c) Жириновский) надоело, представляю прототип решения для SharePoint 2010 в виде веб части, подключаемой к любой библиотеке документов с изображениями.

Данная веб часть, подключается к любой библиотеке документов, и выводит список изображений для голосования (см. Рис. 1). Результаты голосования записываются в автоматически создаваемое поле SPListItem-a. В представленной версии отсутвует AJAX кнопка, и механизм запоминания проголосовавшего (он сделан на основе coockies, в полной версии).

Отрисовка списка, выполнена по статье сайта artlebedev.ru - "Блоки картинок, с подписями выровненныe по центру".

Еще одним из интересных моментов, является способ получения данных из библиотеки документов, и приведения SPListItemCollection к списку строго типизированных объектов. Методика выполнена в виде Extension методов для SPListItemCollection (см. код ниже).
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.SharePoint;

namespace ImageVotingWebPart
{
    public static class SPListItemCollectionExtension
    {
        /// 
        /// Преобразование элементов коллекции  к 
        /// 
        /// Тип результирующей коллекции
        /// 


 Исходная коллекция
        /// 
        public static IEnumerable<T> Select<T>(this SPListItemCollection collection)
            where T : new()
        {
            return collection
                .Cast<SPListItem>()
                .Select(item => item.Cast<T>());
        }

        /// 
        /// Извлечение из SPListItem полей класса  и создание его экземпляра
        /// 
        /// Тип результата
        /// 


 Преобразуемый элемент
        /// Возвращает объект класса  с теми заполнеными полями, которые были в исходном SPListItem
        public static T Cast<T>(this SPListItem item) 
            where T : new()
        {
            var result = new T();
            Type type = typeof(T);
            PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);

            foreach (PropertyInfo property in properties)
            {
                try
                {
                    object value = item[property.Name];
                    property.SetValue(result, value, null);
                }
                catch (Exception exc)
                {
                    Debug.WriteLine(exc);
                }
            }

            return result;
        }
    }
}
Модель:
namespace ImageVotingWebPart.ImageVotingWebPart
{
 public class ImageVotingItem
 {
  public int ID { get; set; }
  public string EncodedAbsThumbnailUrl { get; set; }
  public string FileLeafRef { get; set; }
  public string FileRef { get; set; }
  public int Rating { get; set; }
  public string Title { get; set; }

  public string TitleOrName
  {
   get { return !string.IsNullOrEmpty(Title) ? Title : FileLeafRef; }
  }
 }
}
Код веб части:
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
namespace ImageVotingWebPart.ImageVotingWebPart
{
    [ToolboxItemAttribute(false)]
    public class ImageVotingWebPart : WebPart
    {
        // Visual Studio might automatically update this path when you change the Visual Web Part project item.
        private const string _ascxPath = @"~/_CONTROLTEMPLATES/ImageVotingWebPart/ImageVotingWebPart/ImageVotingWebPartUserControl.ascx";

        protected override void CreateChildControls()
        {
            Control control = Page.LoadControl(_ascxPath);
            Controls.Add(control);
        }

        [Personalizable]
        [WebBrowsable]
        [WebDisplayName("Адрес библиотеки")]
        public string LibraryUrl { get; set; }
    }
}
Код контрола ImageVotingWebPartUserControl:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using ImageVotingWebPart.Properties;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;

namespace ImageVotingWebPart.ImageVotingWebPart
{
    public partial class ImageVotingWebPartUserControl : UserControl
    {
        #region Constants

        private const string c_ratingFieldName = "Rating";
        protected const string c_voteCommandName = "Vote";

        #endregion

        protected ImageVotingWebPart WebPart
        {
            get { return (ImageVotingWebPart)Parent; }
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            if (string.IsNullOrEmpty((WebPart.LibraryUrl ?? string.Empty).Trim()))
            {
                return;
            }

            bindImageList();
        }

        protected void OnImageListItemCommand(object sender, RepeaterCommandEventArgs e)
        {
            if (e.CommandName != c_voteCommandName)
            {
                return;
            }

            int id;

            if (!int.TryParse((string)e.CommandArgument, out id))
            {
                return;
            }

            if (SPUtility.ValidateFormDigest())
            {
                SPSecurity.RunWithElevatedPrivileges(() => increaseImageRating(id));
            }
        }

        private void increaseImageRating(int id)
        {
            try
            {
                using (SPSite site = new SPSite(WebPart.LibraryUrl))
                {
                    string webRelativeUrl = WebPart.LibraryUrl.Replace(site.Url, String.Empty);
                    using (SPWeb web = site.OpenWeb(webRelativeUrl))
                    {
                        SPList list = web.GetList(WebPart.LibraryUrl);

                        if (!list.Fields.ContainsField(c_ratingFieldName))
                        {
                            list.Fields.Add(c_ratingFieldName, SPFieldType.Integer, true);
                        }
                        else
                        {
                            SPField field = list.Fields.GetField(c_ratingFieldName);

                            if (field.Type != SPFieldType.Integer)
                            {
                                throw new ApplicationException(ImageVotingResources.RatingFieldWrongTypeError);
                            }
                        }

                        SPItem item = list.GetItemById(id);
                        int rating = (int)(item[c_ratingFieldName] ?? 0);
                        item[c_ratingFieldName] = rating + 1;
                        item.Update();
                    }
                }
            }
            catch (Exception e)
            {
                setError(e.Message);
            }
        }

        private void bindImageList()
        {
            string[] usingFields = new[]
            {
                "ID",
                "EncodedAbsThumbnailUrl",
                "FileRef",
                "FileLeafRef",
                c_ratingFieldName,
                "Title",
            };

            try
            {
                using (SPSite site = new SPSite(WebPart.LibraryUrl))
                {
                    string webRelativeUrl = WebPart.LibraryUrl.Replace(site.Url, String.Empty);
                    using (SPWeb web = site.OpenWeb(webRelativeUrl))
                    {
                        SPList list = web.GetList(WebPart.LibraryUrl);
                        SPQuery query = new SPQuery();
                        query.ViewFields = string.Join("", usingFields.Select(name => string.Format("<FieldRef Name='{0}' />", name)).ToArray());
                        query.ViewAttributes = "Scope='Recursive'";
                        ImageList.DataSource = list.GetItems(query).Select<ImageVotingItem>();
                        ImageList.DataBind();
                    }
                }
            }
            catch (Exception exc)
            {
                setError(string.Format(ImageVotingResources.LoadImagesError, exc.Message));
            }
        }

        private void setError(string error)
        {
            ErrorLabel.Text = error;
        }
    }
}
Разметка контрола ImageVotingWebPartUserControl.ascx:
<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>

<%@ Register TagPrefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ImageVotingWebPartUserControl.ascx.cs"

    Inherits="ImageVotingWebPart.ImageVotingWebPart.ImageVotingWebPartUserControl" %>

<%@ Import Namespace="ImageVotingWebPart.Properties" %>

<style type="text/css">

    .thumbnail

    {

        /* Убираем подчеркивание у элемента ins,

который был использован для совместимости со старыми версиями Internet Explorer */

        text-decoration: none;

        /* Следующее правило для Firefox 2 */

        display: -moz-inline-box;

        /* а это для остальных */

        display: inline-block;

        vertical-align: top;

        /* Отступы между блоками */

        margin: 2em 0 0 5px;

    }

    .thumbnail .r

    {

        background-color: #D6E4FE;

        height: 164px;

        width: 164px;

        text-align: center;

        /*layout magic*/

        display: table;

        /*# - is hack for ie7*/

        #position: relative;

        overflow: hidden;

    }

    .centered

    {

        /*# - is hack for ie7*/

        #position: absolute;

        #top: 50%;

        #left: 50%;

        display: table-cell;

        vertical-align: middle;

    }

</style>

<asp:Label ID="ErrorLabel" runat="server" ForeColor="Red" /><br />

<asp:Repeater ID="ImageList" runat="server" OnItemCommand="OnImageListItemCommand">

    <ItemTemplate>

        <ins class="thumbnail">

            <div class="r">

                <div class="centered">

                    <div style=" #position: relative; #top: -50%; #left: -50%;">

                        <a href='<%# Eval("FileRef") %>' target="_blank" title='<%# Eval("TitleOrName") %>'>

                            <img alt='' src='<%# Eval("EncodedAbsThumbnailUrl") %>' style="border-style: none;"

                                title='<%# Eval("TitleOrName") %>' />

                        </a>

                    </div>

                </div>

            </div>

            <br />

            <table width="164px">

                <col width="80%" />

                <col />

                <tr>

                    <td style="text-align: center">

                        <asp:Literal ID="Literal2" runat="server" Text='<%# Eval("TitleOrName") %>' />

                    </td>

                </tr>

                <tr>

                    <td style="text-align: center">

                        <asp:Literal ID="Literal3" runat="server" Text='<%# ImageVotingResources.RatingLabelText %>' />:&nbsp;

                        <asp:Literal ID="Literal4" runat="server" Text='<%# Eval("Rating") %>' />

                        <asp:ImageButton ID="ImageButton1" runat="server" CommandName='<%# c_voteCommandName %>'

                            CommandArgument='<%# Eval("ID") %>' ImageUrl="~/_layouts/images/ImageVotingWebPart/vote.png"

                            Width="20" Height="20" Style="vertical-align: middle;" Title='<%# ImageVotingResources.VoteButtonText %>' />

                    </td>

                </tr>

            </table>

        </ins>

    </ItemTemplate>

</asp:Repeater>

Пост написан, в ответ на многочисленные просьбы пользователей форума www.gotdotnet.ru, на тему - "КАК МНЕ СДЕЛАТЬ ГОЛОСОВАНИЕ ДЛЯ КАРТИНОК В SHAREPOINT"?

4 comments:

  1. Стандартный механизм голосования чем не устроил?

    ReplyDelete
  2. "Не красиво" (c) Отдел пиара.

    ReplyDelete
  3. А можно поподробнее, как подключить данный код?

    ReplyDelete
  4. Привет. Просто создаешь проект SharePoint в нем создаешь WebPart вставляешь туда код. Собираешь проект деплоишь wsp и все работает. Потом на сайте подключаешь webpart.

    ReplyDelete