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에서호출하여 보자.