Интеграция ASP.NET MVC c Sharepoint 2013. Part 2: Взаимодействие с SharePoint

    В предыдущей статье Интеграция ASP.NET MVC c Sharepoint 2013. Part 1: High-Trusted provider-hosted APP было разобрано, как настроить SharePoint 2013 для SharePoint Apps (теперь Microsoft называет это SharePoint Add-in) и сделать базовую интеграцию приложения ASP.NET MVC с provider-hosted APP. В этой статье я покажу, как мы реализовали: поиск элементов SharePoint Site в MVC приложении, передачу элементов из SharePoint Site, App-parts и локализацию элементов SharePoint.

    Поиск элементов SharePoint через ASP.NET MVC приложение

    Допустим, что где-то в интерфейсе вашего приложения есть обычное поисковое поле, которое в результате вызывает action SharepointSearch:
        public JsonResult SharepointSearch(string search)
        {
            var sharepointItems = GetSharePointSearchResult(search);
            var json = JsonHelper.ConvertToJsonResponse(sharepointItems);
    
            return Json(json);
        }
    
        private ResponseModel<List<SharePointDocumentModel>> GetSharePointSearchResult(string query)
        {
            var responseModel = new ResponseModel<List<SharePointDocumentModel>>();
            var searchResult = new List<SharePointDocumentModel>();
    
            var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
    
            if (spContext == null)
            {
                responseModel.Initialize(searchResult, Resources.SharepointContextNull, ResponseStatusEnum.Fail);
                return responseModel;
            }                
    
            using (var clientContext = spContext.CreateUserClientContextForSpHost())
            {
                clientContext.ExecuteQuery();
    
                var keywordQuery = new KeywordQuery(clientContext);
                keywordQuery.QueryText = query;
    
                var searchExecutor = new SearchExecutor(clientContext);
    
                var results = searchExecutor.ExecuteQuery(keywordQuery);
                clientContext.ExecuteQuery();
                foreach (var resultRow in results.Value[0].ResultRows)
                {
                    clientContext.ExecuteQuery();
                    var spDocument = new SharePointDocumentModel();
                    DateTime createdDateTime;
                    DateTime.TryParse(GetDictonaryStringValueByKey(resultRow, "Write"), out createdDateTime);
    
                    var contentTypeId = spDocument.DocumentName = GetDictonaryStringValueByKey(resultRow, "ContentTypeId");
    
                    if (contentTypeId.StartsWith("0x01"))
                    {
                        spDocument.DocumentName = GetDictonaryStringValueByKey(resultRow, "Title");
                        spDocument.DocumentUrl = GetDictonaryStringValueByKey(resultRow, "Path");
                        spDocument.Id = GetDictonaryStringValueByKey(resultRow, "WorkId");
                        spDocument.Author = GetDictonaryStringValueByKey(resultRow, "Author");
                        
                        spDocument.CreateDate = createdDateTime.ToShortDateString();
    
                        searchResult.Add(spDocument);
                    }
                }
            }
    
            responseModel.Initialize(searchResult);
    
            return responseModel;
        }
    

    В нашем случае необходимо было получить все возможные ссылки на любые SP-объекты (по идентификатору "0x01"). Однако вы можете детализировать отбор элементов, используя другие идентификаторы контента SharePoint

    Присоединение элементов SharePoint через CustomActions

    Через SharePoint APP вы можете «внедрить» в SharePoint свои элементы CustomActions (кнопки в риббоне, элементы в контекстном меню). Подробней о том, как это делать, можно прочитать на MSDN.

    Попробуем добавить два элемента: кнопку в риббон и в пункт контекстное меню.

    Начнем с контекстного меню. Для этого нам потребуется добавить в наш SharePoint APP приложение новый Menu Item Custom Action. В визарде добавления элементов Visual Studio будет предложено выбрать, где и в каких списках должен появляться пункт контекстного меню. Если взглянуть на созданный элемент, то станет понятно, что это обычный xml файл следующего содержания:
    <?xml version="1.0" encoding="utf-8"?>
    <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
      <CustomAction Id="6d471c89-41cc-4b81-b794-ea7664dc4c38.AttachToNewDocument"
                    RegistrationType="ContentType"
                    RegistrationId="0x0101"
                    Location="EditControlBlock"
                    Sequence="10001"
                    Title="$Resources:ContextMenu_AttachToNewDocument">
    
        <UrlAction Url="~remoteAppUrl/Navigator/SharepointAction/?SPHostUrl={HostUrl}&SPListId={ListId}&SPListItemsId={ItemId}" />
      </CustomAction>
    </Elements>
    

    Разберем по порядку основные элементы:
    RegistrationType & RegistrationId — указывают комбинацию, при которой пункт меню будет вызываться (то, что было указано в визарде; можно переопределить на больший спектр действия)
    Location — где элемент содержится (контекстное меню)
    Sequence — порядок в меню
    Title — Видимое название. Здесь можно увидеть, что уже применена локализация. Именно так можно локализовать элементы из Resources (Host web)
    UrlAction — основа CustomActions. Здесь указывается url, куда будет перенаправлен пользователь после клика. В данном случае указано специальное системное обозначение для provider hosted app ~remoteAppUrl.
    Также важно отметить параметры SPListId, SPListItemsId — это идентификаторы текущего листа и выбранных в нем элементов соответственно. Как значения подставлены маркеры. Полную информацию по формированию таких ссылок можно найти здесь

    Теперь перейдем к принимающему action нашего MVC приложения:
        public ActionResult SharepointAction(SharepointActionModel actionModel)
        {
            var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
    
             GetSharepointListItemsAndPerformAction(spContext, actionModel.SpListId.Value, actionModel.SpListItemsId,
                (listItems, ids, context, spList) =>
                {
                    if (spList.BaseType == BaseType.DocumentLibrary)
                    {
                        richCardId = AttachSharepointDocumentsToDocumentCard(listItems, ids, context);
                    }
                }); 
    
            return Redirect(model.UrlToNavigate);
        }
    
        public class SharepointActionModel
        {
            public Guid? SpListId { get; set; }
    
            public string SpListItemsId { get; set; }
    
            public string SpHostUrl { get; set; }
        }
    
        private const string DocumentCamlQuery = @"<QueryOptions><ViewAttributes Scope='All'/></QueryOptions>
                                                    <Where>
                                                        <In>
                                                            <FieldRef Name='ID' />
                                                            <Values>
                                                                {0}
                                                            </Values>
                                                        </In>
                                                    </Where>";
    
        private void GetSharepointListItemsAndPerformAction(SharePointContext spContext, Guid listId, string listItemIds, Action<ListItemCollection, List<int>, ClientContext, List> fileAction)
        {
            try
            {
                if (spContext == null)
                    throw Error.SharepointIntergration();
    
                using (var clientContext = spContext.CreateUserClientContextForSPHost())
                {
                    var spList = clientContext.Web.Lists.GetById(listId);
                    clientContext.Load(spList);
                    clientContext.ExecuteQuery();
                    
                    if (spList != null && spList.ItemCount > 0)
                    {
                        var camlQuery = new CamlQuery();
                        camlQuery.ViewXml = String.Format(DocumentCamlQuery, GetFilterValues(listItemIds));
    
                        var listItems = spList.GetItems(camlQuery);
    
                        clientContext.Load(listItems);
                        clientContext.ExecuteQuery();
    
                        var stringIds = listItemIds.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
                        var ids = ConvertToIntFromStringIds(stringIds);
    
                        fileAction(listItems, ids, clientContext, spList);
                    }
                }
            }
            catch (Exception ex)
            {
                Trace.TraceError(ex);
                throw;
            }
        }
    

    Далее, с кнопками в риббоне необходимо сделать все то же самое, добавив в проект элемент Ribbon Custom Action. Причем можно направить url в то же самое место, заранее предусмотрев, что в SPListItemsId может быть несколько элементов, разделенных запятой. В коде выше это уже предусмотрено.

    SharePoint AppParts

    AppPart — еще один элемент, который можно установить в SharePoint через App. Но в данном случае пользователь SharePoint сам решает, где и куда добавить этот элемент на странице. Основная идея AppPart — показать часть вашего high-trusted приложения, используя технологию iframe. В предыдущих версиях SharePoint это же было реализовано через WebParts.

    Итак, как добавлять App-part подробно описано тоже на MSDN.

    Добавить его проект можно снова, используя визард Visual Studio и представляет из себя тоже xml файл примерно следующего содержания:
    <?xml version="1.0" encoding="utf-8"?>
    <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
      <ClientWebPart Name="Approvals" Title="$Resources:AppPart_Approvals_Title" Description="$Resources:AppPart_Approvals_Description" DefaultWidth="850" DefaultHeight="150">
        <Content Type="html" Src="~remoteAppUrl/Navigator/ApprovalAppPart?{StandardTokens}" />
      </ClientWebPart>
    </Elements>
    

    Наверняка потребуется менять размеры app part при изменении размера контента. Я оставлю ниже javascript код, который необходимо вызывать вручную при изменении размера контента на стороне ASP.NET MVC приложения:
        function ResizeIFrame() {
            if (!IsSharepointAppPart())
                return;
    
            var oBody = document.body;
            var innerHeight = $(".app-part-content", oBody).height();
    
            var dheight = innerHeight + (oBody.offsetHeight - oBody.clientHeight);
            var dwidth = oBody.scrollWidth + (oBody.offsetWidth - oBody.clientWidth);
    
            var message = "<Message senderId=" + senderId + " >"
                + "resize(" + dwidth + "," + dheight + ")</Message>";
            window.parent.postMessage(message, document.referrer);
        }
    
        function IsSharepointAppPart() {
            return IsInsideIframe();
        }
    
        function IsInsideIframe() {
            return window.self !== window.top;
        }
    

    Пожалуй, на этом можно закончить. В следующий раз я напишу, как мы создали свой Ribbon Tab, SharePoint Solution и связали его c SharePoint App.
    ДоксВижн
    45.02
    Company
    Share post

    Comments 0

    Only users with full accounts can post comments. Log in, please.