레이블이 api인 게시물을 표시합니다. 모든 게시물 표시
레이블이 api인 게시물을 표시합니다. 모든 게시물 표시

2019년 3월 22일 금요일

API를 사용하여 Datasource를 생성하고 페이지 레이아웃 업데이트하기 [Part 2]

이전 포스트의 API를 사용하여 데이타소스(DS) 아이템을 만드는 방법에 이어서 이번에는 어떻게 API를 컴포넌트에 적용시키는지 알아보도록 하자. 필자는 아주 심플한 RichText Editor 컴포넌트를 만들었고, 컴포넌트 내에 API를 XMLHttpRequest를 통하여 Ajax Call을 실행하였다.

페이지 아이템의 xEditor에서 API를 이용하여 DS를 생성할시 아이템은 아래의 스크린샷처럼 하위경로의 DS폴더안에 생성된다.


해당 DS를 다른 페이지에서 재사용 할 경우 페이지 구조상으로 문제가 될수 있지만, 필자는 API를 컴포넌트에서 쉽게 사용하는 방법의 목적의 예제이므로 컨턴츠 구조 문제는 다루지 않겠다. 아래는 Controller Rendering의 RichText Editor 컴포넌트이다.

CustomComponentController.cs (Controller)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using Sitecore.Mvc.Controllers;
using Sitecore.Mvc.Presentation;
using System;
using System.Web.Mvc;
using Sitecore.Data;

namespace Sitecore.Custom.Component.RichTextEditor
{
    public class CustomComponentController : SitecoreController
    {
        /// <summary>
        /// RichText Editor의 Action Method
        /// </summary>
        /// <returns>ActionResult</returns>
        public ActionResult RichTextEditor()
        {
            Models.RichTextEditor rte = new Models.RichTextEditor();
            rte.PageItem = PageContext.Current.Item;
            rte.Rendering = RenderingContext.CurrentOrNull.Rendering;
            rte.RenderingDatasource = rte.Rendering.DataSource;

            // 레이아웃에 적용되어진 RTE 컨트롤에 데이타소스가 적용되었는지 확인한다.
            rte.IsDatasourceSet = (!String.IsNullOrEmpty(rte.RenderingDatasource) ? true : false);

            // 새로운 데이타소스 아이템의 이름을 선업한다.
            rte.NewItemName = rte.PageItem.Name + "_" + rte.DatasourceItemSuffix;

            // RTE DS의 템플릿을 선언한다.
            rte.RteTemplate = rte.PageItem.Database.GetTemplate(new ID("{0B57D07C-F406-4700-9CAC-D4C4B84D1B78}"));

            return PartialView(rte);
        }
    }
}

RichTextEditor.cs (Model)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
using Sitecore.Data.Items;
using Sitecore.Mvc.Presentation;

namespace Sitecore.Custom.Component.Models
{
    public class RichTextEditor
    {
        // Initialize component's rendering info
        public Item Item { get; set; }
        public Item ResourceFolder { get; set; }
        public Rendering Rendering { get; set; }
        public TemplateItem RteTemplate { get; set; }
        public string NewItemName { get; set; }
        public string DatasourceItemSuffix { get { return "RTE"; } }
        public string RenderingDatasource { get; set; }
        public Item PageItem { get; set; }
        public bool IsDatasourceSet { get; set; }
    }
}

RichTextEditor.cshtml (Views)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@model Sitecore.Custom.Component.Models.RichTextEditor
@if (!Model.IsDatasourceSet && Sitecore.Context.PageMode.IsExperienceEditor)
{
    <div style="padding:20px 0;" class="border border-success">
        <h4>RichText Editor Added</h4>
        <button type="button" class="btn btn-success" onclick="SendAjaxPOST(); return false;">Create Rich Text Editor Datasource</button>
        <script>
            function SendAjaxPOST() {
                // 현재 페이지에서 다른 아이템 및 필드가 수정되었는지 확인한다.
                if (Sitecore.PageModes.PageEditor.isModified()) {
                    alert("There are unsaved items on this page.\rPlease save the page, then create a new datasource.");
                    return false;
                }

                // XHR 시작
                var xhr = new XMLHttpRequest();
                xhr.open("POST", "http://sc910.sc/customapi/createitem/@Model.PageItem.ID.Guid.ToString()");
                xhr.setRequestHeader("Content-Type", "application/json");
                xhr.send("{\"itemName\": \"@Model.NewItemName\", \"templateGuid\": \"@Model.RteTemplate.ID.Guid.ToString()\", \"isDatasourceItem\": \"1\", \"renderingUid\": \"@Model.Rendering.UniqueId.ToString()\"}");
                xhr.onreadystatechange = function () {
                    if (this.readyState == 4) {
                        if (this.status === 200) {
                            window.onbeforeunload = null;
                            this.returnValue = true;

                            setTimeout(function () { location.reload() }, 500);
                        } else {
                            console.log('Error Status: ' + this.status + '\nHeaders: ' + JSON.stringify(this.getAllResponseHeaders()) + '\nBody: ' + this.responseText);
                        }
                    }
                };
            }
        </script>
    </div>
}
else
{
    <div>@Html.Sitecore().Field("Content")</div>
}

해당 컴포넌트를 사이트코어 페이지에 적용하고 실행하면 ContentEditor 와 xEditor의 페이지 이동 및 확인없이 쉽게 데이타소스를 생성할수있다. 이는 Non-Technical 유저 입장에서 쉽게 페이지를 생성하고 데이터소스를 다이나믹하게 생성하는데 도움이된다.





2019년 2월 26일 화요일

API를 사용하여 Datasource를 생성하고 페이지 레이아웃 업데이트하기 [Part 1]

이번 포스트에서는 Non-Technical 사이트코어 Author 또는 Contributor를 타켓으로 하여 어떻게 xEditor에서 아이템을 생성하고 페이지에 적용을 시키는지에 알아보도록 하자. 필자는 새로운 API를 생성하여 XMLHttpRequest를 통하여 아이템을 생성하는 방법을 택하였다.

새로운 API를 생성하기 전, 페이지 아이템 및 데이터소스 구조를 정하여야 한다. 각 컴퍼니 또는 비지니스의 컨셉, 그리고 웹사이트의 수에 따라 아이템 스트럭쳐는 다양하게 변경되어있을 수 있으므로 이번 예제에서는 어떻게 사이크코어의 사용자가 렌더링 아이템을 적용 시키고 그에 맞는 데이타소스를 손쉽게(?) 적용할수 있도록 포커스를 두었다. 이는 경우에 따라 언제든지 확장 및 수정이 가능할수 있다.

아래는 기본적으로 유저가 렌더링 아이템을 적용하고 데이타소스를 생성하여 페이지 레이아웃에 적용하는 순서이다.
  1. 업데이트를 하고자 하는 페이지 아이템으로 이동
  2. Content Editor에 원하는 경로에 컴포넌트의 데이터소스 생성한다.
  3. xEditor로 이동 후, 적용되어진 Placeholder에 컴포넌트를 Insert한다.
  4. 적용 되어진 컴포넌트의 속성을 열어, 데이터소스 아이템을 Datasource 필드에 적용을 시킨다.
아래의 API를 통하여 컴포넌트와 관련된 아이템을 페이지에 적용하는 방법이다.
  1. 업데이트 하고자 하는 아이템의 xEditor 오픈
  2. Placeholder에 원하는 컴포넌트 Insert
  3. 생성되어진 Input 버튼 (One-Click)을 통하여 컴포넌트의 데이터소소를 정해진 경로에 생성
필자의 방법처럼 API를 사용하여 아이템을 생성과 동시에 Layout에 데이터소스를 적용하면 Content Editor 와 xEditor의 변경 없이 손쉽게 누구나 페이지에 컴포넌트 및 그에 맞는 데이터소스를 적용시킬수가 있다. 좀 더 자세히 알아보기 위하여 필자는 아래의 샘플 소스를 작성하였다.  

먼저 새로운 Route을 적용 시키고 "CreateItem" 이라는 API 액션을 생성하도록 하자.


필자는 API라는 새로운 프로젝트를 만든 후, RouteMap을 "{controller}/{action}/{id}" 으로 설정하고 CustomApiController와 Api 모델을 생성하였다. Controller에는 새로운 아이템을 생성하는  ActionResult와 페이지 아이템 하위의 데이터소스폴더 및 생성되어진 데이타소스의 키를 업데이트할수 있는 레이아웃 업데이트 Method도 같이 생성하였다.

CustomApiController.cs


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
using System.Web.Mvc;
using Sitecore.Data.Items;
using Sitecore.Data;
using Sitecore.Globalization;
using Sitecore.SecurityModel;
using Sitecore.Layouts;

namespace Sitecore.Custom.Component.API.Controllers
{
    public class CustomApiController : Controller
    {
        Database masterDb = Configuration.Factory.GetDatabase("master");
        Database webDb = Configuration.Factory.GetDatabase("web");

        /// <summary>
        /// 새로운 ResourceFolder 페이지 아이템 하위에 생성한다.
        /// 해당 페이지 아이템과 관련된 모든 컴포넌트의 데이타소스는 
        /// 페이지 하위 ResourceFolder에 모두 생성된다.
        /// </summary>
        /// <param name="api">api</param>
        /// <param name="parentItem">ResouceFolder가 만들어진 상위의 페이지 아이템</param>
        /// <returns></returns>
        public Item CreateResourceFolder(Models.Api api, Item parentItem)
        {
            var localResourceFolder = parentItem.Database.GetItem(parentItem.Paths.FullPath + "/" + api.localResourceFolderName);
            using (new SecurityDisabler())
            {
                TemplateItem template = parentItem.Database.GetTemplate("User Defined/Local Resource Folder");
                if (localResourceFolder == null)
                {
                    localResourceFolder = parentItem.Add(api.localResourceFolderName, template);
                }
            }
            return localResourceFolder;
        }

        /// <summary>
        /// 생성되어진 Datasource Item의 ID아이디를 페이지 레이아웃에 업데이트한다.
        /// </summary>
        /// <param name="pageItem">현재 페이지 아이템</param>
        /// <param name="datasourceItem">새로운 데이타소스 아이템</param>
        /// <param name="renderingUid">렌더링 아이템의 유니크 아이디</param>
        public void UpdateDatasourceKey(Item pageItem, Item datasourceItem, string renderingUid)
        {
            using (new SecurityDisabler())
            {
                // Get the layout definitions and the device
                string deviceId = Context.Device.ID.ToString();
                DeviceItem device = pageItem.Database.Resources.Devices[deviceId];
                Data.Fields.LayoutField layoutField = new Sitecore.Data.Fields.LayoutField(pageItem.Fields[Sitecore.FieldIDs.FinalLayoutField]);
                LayoutDefinition layoutDefinition = LayoutDefinition.Parse(layoutField.Value);
                DeviceDefinition deviceDefinition = layoutDefinition.GetDevice(device.ID.ToString());
                foreach (RenderingDefinition rd in deviceDefinition.Renderings)
                {
                    // Update the renderings datasource value accordingly 
                    if (rd.UniqueId == "{" + renderingUid.ToUpper() + "}")
                    {
                        rd.Datasource = datasourceItem.ID.ToString();
                        // Save the layout changes
                        pageItem.Editing.BeginEdit();
                        layoutField.Value = layoutDefinition.ToXml();
                        pageItem.Editing.EndEdit();
                    }
                }
            }
        }

        /// <summary>
        /// 새로운 아이템을 정해진 템플릿에 맞게 생성한다
        /// </summary>
        /// <param name="itemName">새로운 아이템을 이름</param>
        /// <param name="id">{id}에 생성될 다이나믹 아이디</param>
        /// <param name="templateGuid">새로운 아이템의 템플릿 Guid</param>
        /// <param name="isDatasourceItem">현재 생성될 아이템이 데이타소스인지 확인 (Default: false)</param>
        /// <param name="renderingUid">레이아웃에 업데이트 되어진 렌더링 아이템의 유니크 아이디</param>
        /// <returns></returns>
        public ActionResult CreateItem(string itemName, string id, string templateGuid, string isDatasourceItem = "0", string renderingUid = null)
        {
            Models.Api api = new Models.Api();

            using (new LanguageSwitcher("en"))
            {
                Item pageItem = masterDb.GetItem(ID.Parse(id));
                // 새로운 데이타소스가 생성되었다면, 레이아웃을 업데이트한다

                if (isDatasourceItem == "1")
                {
                    api.item = CreateResourceFolder(api, pageItem);
                }
                api.templateItem = masterDb.GetTemplate(ID.Parse(templateGuid));
                Item newDatasource = api.item.Add(itemName, api.templateItem);

                // 새로운 데이타소스가 생성되었다면, 레이아웃을 업데이트한다
                if (isDatasourceItem == "1" && newDatasource != null)
                {
                    UpdateDatasourceKey(pageItem, newDatasource, renderingUid);
                }
            }
            return new EmptyResult();
        }
    }
}


Api.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
using Sitecore.Data.Items;
using Sitecore.Data;

namespace Sitecore.Custom.Component.API.Models
{
    public class Api
    {
        public string actionType { get; set; }
        public Item item { get; set; }
        public Database sourceDb { get; set; }
        public Database targetDb { get; set; }
        public TemplateItem templateItem { get; set; }
        public string localResourceFolderName {
            get
            {
                return "Local Resource";
            }
        }
    }
}


API는 준비되었으므로 다음 포스트에서 심플한 컴포넌트를 만들어 API를 xEditor에서호출하여 보자.



2018년 11월 19일 월요일

사이트코어의 새로운 MVC Route 적용하기

이번 블로그에서는 새로운 MVC Route를 사이트코어에 적용하는 방법을 알아보자.
사이트코어의 기본 MVC Route는 "/api/sitecore/{controller}/{action}"으로써 Sitecore 9 이상의 버전에서는 Sitecore.Mvc.config, 그리고 Sitecore 9 하위 버전에서는 Sitecore.Speak.Mvc.Config 파일에서 확인할 수가 있다.
Sitecore의 자체적인 MVC Application의 컨트롤는 "/sitecore/layout/controllers" 아이템 트리에 등록되어진 Application 렌더링 아이템을 작동시키지만, 간혹 아이템의 등록없이 새로운 API를 만들어 외부 소스를 Ajax Call로 불러들여 원하는 기능을 작동시킬수가 있다.

이를 위하여, 새로운 MVC Route를 만듬으로써 기본 API 경로에 구애받지 않고 사이트코어 내에 다른 어플리케이션을 독립적으로 수행할수가 있다. 지금부터 어플리케이션을 만들고 적용시키는 방법을 알아보자.

  1. Visual Studio에서 새로운 Empty MVC 프로젝트를 만든다.

  2. App_Start 폴더에 RouteConfig.cs을 새로운 Route를 레지스터하기 위하여 수정한다.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    using System.Web.Mvc;
    using System.Web.Routing;
    
    namespace SitecoreSpaceNewRoute
    {
        public class RouteConfig
        {
            public static void RegisterRoutes(RouteCollection routes)
            {
                //// 
                //// 기본 MVC Route
                ////
                //routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
                //routes.MapRoute(
                //    name: "Default",
                //    url: "{controller}/{action}/{id}",
                //    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
                //);
    
    
                ////
                //// 사이트코어 스페이스의 새로운 MVC Route
                ////
                routes.MapRoute(
                    name: "SitecpreSpaceRouting",
                    url: "new/sitecore/space/routing/{controller}/{action}/{id}",
                    defaults: new { controller = "SitecoreSpace", action = "MyView", id = UrlParameter.Optional }
                );
            }
        }
    }
    


  3. 새로운 폴더 "CustomRouting" 이라는 폴더를 만들고, Sitecore.Kernel assembly를 Reference에 추가하고 사이트코어의 Bin 폴더의 System.Web.Mvc를 사용하도록 프로젝트에 업데이트한다. 레지스터한 Route를 Sitecore.Pipeline의 프로세스에 적용을 시킨다.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    using System.Web.Routing;
    using Sitecore.Pipelines;
    
    namespace SitecoreSpaceNewRoute.CustomRouting
    {
        public class InitializeRoutes
        {
            /// <summary>
            /// 새로 등록한 Route는 Sitecore Pipeline에 적용시킨다.
            /// </summary>
            /// <param name="args"></param>
            public void Process(PipelineArgs args)
            {
                RouteConfig.RegisterRoutes(RouteTable.Routes);
            }
        }
    }
    


  4. 간단하게 새로운 Route의 레지스트를 마쳤으며, 이젠 Simple MVC을 만들자.

    Controller (SitecoreSpaceController.cs)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    
    namespace SitecoreSpaceNewRoute.Controllers
    {
        public class SitecoreSpaceController : Controller
        {
            // GET: SitecoreSpace
            public ActionResult MyView()
            {
                Models.SitecoreSpace ssnr = new Models.SitecoreSpace();
    
                // Valuable 값을 다른 소스 (예, 데이타베이스)에서 불러들일수 있지만,
                // 이번은 예제이므로, Static 값을 메뉴얼로 적용하겠다.
                ssnr.Subject = "사이트코어의 새로운 MVC Routing";
                
                ssnr.Description = "사이트코어 스페이스의 새로운 MVC Routing으로써, 기존 (Default)의 Routing 제한을 받지않고, 임의의 새로운 Route를 적용시켜 독립적인 MVC 어플리케이션을 Sitecore XP내에서 작동시킬수 있다.";
                ssnr.CurrentDateTime = DateTime.UtcNow;
    
                return View(ssnr);
            }
    
            // Redirect가 아니 다른 View rendering으로 사용할수가 있다.
            public ActionResult RedirectToBlog()
            {
                Models.SitecoreSpace ssnr = new Models.SitecoreSpace();
                ssnr.BlogUrl = "https://SitecoreSpace.Blogspot.com";
    
                return Redirect(ssnr.BlogUrl);
            }
        }
    }
    


    Model (SitecoreSpace.cs)
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    using System;
    namespace SitecoreSpaceNewRoute.Models
    {
        public class SitecoreSpace
        {
            public string FullBlogName { get; set; }
            public string BlogUrl { get; set; }
            public string Subject { get; set; }
            public string Description { get; set; }
            public DateTime CurrentDateTime { get; set; }
        }
    }
    


    View (MyView.cshtml)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    @model SitecoreSpaceNewRoute.Models.SitecoreSpace
    
    <!DOCTYPE html>
    <html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title>MyView</title>
    </head>
    <body>
        <div class="form-horizontal">
            <h4>SitecoreSpace</h4>
            <hr />
            <div class="col-md-10">
                @Html.DisplayTextFor(model => model.Subject)
            </div>
            <br />
            <div class="col-md-10">
                @Html.DisplayTextFor(model => model.FullBlogName)
            </div>
            <br />
            <div class="col-md-10">
                @Html.DisplayTextFor(model => model.Description)
            </div>
            <br />
            <div class="col-md-10">
                링크: @Html.ActionLink("Sitecore Space Blog", "RedirectToBlog", null, new { target = "_blank" })
            </div>
            <br />
            <div class="col-md-10">
                현재시간: @Model.CurrentDateTime.ToString("yyyy-MM-dd h:mm:ss tt")
            </div>
        </div>
    </body>
    </html>
    


    View (RedirectToBlog.cshtml)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    <!DOCTYPE html>
    <html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title>RedirectToBlog</title>
    </head>
    <body>
        <div> 
            추가 extra 페이지
        </div>
    </body>
    </html>
    


  5. 새로운 패치파일을 만들어 사이트코어 파일시스템의 "/App_config/Include/" 폴더에 z.Sitecore.Space.Route.config을 만든다.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    <?xml version="1.0" encoding="utf-8"?>
    <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/">
      <sitecore>
        <pipelines>
          <initialize>
            <processor type="SitecoreSpaceNewRoute.CustomRouting.InitializeRoutes, SitecoreSpaceNewRoute" 
                       patch:after="processor[@type='Sitecore.Pipelines.Loader.EnsureAnonymousUsers, Sitecore.Kernel']" />
          </initialize>
        </pipelines>
      </sitecore>
    </configuration>
    


이젠 브라우저에서 패치되어진 API 경로를 이동을 해본다.








2017년 8월 8일 화요일

사이트코어와 Slack (슬랙) API 연동하기

이번에는 사이트코어와 Slack API 연동에 대하여 알아보도록 하겠다.

Slack API은 메신저와 같은 커뮤니케이션 공간으로써, 많은 유저들이 하나의 커뮤니티 공간에서 많은 채널 (채팅방)을 서로 공유하며, 정보를 주고 받을 수가 있다.

개발자들 및 IT 유저들 사이에는 이미 널리 알려진 웹어플리케이션으로써, 필자가 일하고 있는 회사에서도 역시 직원들의 커뮤니티 공간으로 잘 활용을 하고 있다.

이전에 포스트에서 올려놓았던 Workflow Related Item 업데이트 하기에서 Custom Workflow를 소개하였으며, Workflow가 업데이트 될때마다 관리자 및 검증자가 업데이트 되어진 정보를 이메일로 받는 동시에, Slack API를 통하여 Slack 채널에도 업데이트된 정보가 올려지도록 클래스를 많들었다.

우선, Real-Time message를 전송하기 위하여, Slack 플러그인 (Incoming WebHooks)을 추가적으로 설치한 후,  Webhook URL의 값을 WebClient의 UploadString 메쏘드 파라미터로 입력을 한다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public static class PostSlackMessage
{
    public static void PostMessage(Item pageItem, User user, bool isPublished, string comment = null)
    {
        if (pageItem == null) {
            Sitecore.Diagnostics.Assert.IsNull(null, "Page Item can't be found!");
        }
        if (user == null) {
            Sitecore.Diagnostics.Assert.IsNull(null, "User can't be found!");
        }

        string pagePath = pageItem.Paths.Path.ToString().ToLower();
        pagePath = pagePath.Replace(Sitecore.Context.Data.Site.RootPath.ToLower(), "");
        pagePath = pagePath.Replace(Sitecore.Context.Data.Site.StartItem.ToLower(), "");

        string requestedByEmail = user.Profile.Email;
        string requestedByFullName = user.Profile.FullName;
        string updatedByEmail = User.FromName(pageItem.Statistics.UpdatedBy, true).Profile.Email;
        string updatedByFullName = User.FromName(pageItem.Statistics.UpdatedBy, true).Profile.FullName;
        string requestedDate = DateTime.Now.ToString("g");
        
        string pretextMsg = "Approval Request :file_cabinet:";
        string fallbackMsg = "ReferenceError - UI is not defined";
        string titleMsg = "<https://yoursitecms.com" + pagePath + "|" + pageItem.Fields["Page Title"].Value + "> - " + pagePath;
        string commentMsg = comment;
        string colorMsg = "#FF8000";
        
        if (isPublished)
        {
            pretextMsg = "Approved and Published :white_check_mark:";
            titleMsg = "<http://yoursitecds.com" + pagePath + "|" + pageItem.Fields["Page Title"].Value + ">";
            colorMsg = "#36a64f";
        }
        var field = new[]{
                new {
                    title = (isPublished) ? "Approved by" : "Requested by",
                    value = "<mailto:" + requestedByEmail + "|" + requestedByFullName + ">",
                    @short = true
                },
                new {
                    title = "Updated by",
                    value = "<mailto:" + updatedByEmail + "|" + updatedByFullName + ">",
                    @short = true
                },
                new {
                    title = (isPublished) ? "Approved Date" : "Requested Date",
                    value = requestedDate,
                    @short = true
                },
                new {
                    title = "Page Version",
                    value = pageItem.Version.Number.ToString(),
                    @short = true
                }
        };

        var message = new
        {
            username = "Mr. Sitecore",
            channel = "G690XEDK4", // Private Channel "Sitcore Publishing"
            icon_emoji = ":sitecore:",
            attachments = new[]{ 
                new {
                    pretext = pretextMsg,
                    fallback = fallbackMsg,
           title = titleMsg,
           text = commentMsg,
                    color = colorMsg,
                    fields = field
                }
            }
        };

        var json = JsonConvert.SerializeObject(message);

        var webClient = new WebClient();
        webClient.Headers[HttpRequestHeader.ContentType] = "application/json";
        webClient.UploadString("https://hooks.slack.com/services/your/code/here", json);
    }
}



1
2
// Post Slack Message
PostSlackMessage.PostMessage(args.DataItem, User.Current, true, args.CommentFields["Comments"].ToString());