2019년 11월 16일 토요일

Sitecore Symposium 2019 올랜도 - 후기

사이트코어 심포지엄 2019 (Sitecore Symposium 2019)에서의 메인 Keynote는 Sitecore SaaS (Software as a Service) 발표이다. 공개적으로 어떻게 SaaS의 아키텍쳐 및 SaaS가 Microsoft Azure의 기준으로 개발되는지 아니면 AWS에서도 똑같은 기준으로 서포트를 하고 이용할 수 있는지는 발표되어지 않았다. 아래는 이번 Symposium에서의 Keynote 및 기준으로 리스트를 만들었다.

Sitecore 9.3 Winter 2019 Release

  • ContentHub 3.3 새로운 버전 출시 및 CMP (Content Marketing Platform) 은 Salesforce Marketing Cloud와 연동되어 쉽게 마케팅 컨텐츠를 관리하고 매니지 할수있다.
  • 3rd Party 서치엔진인 Coveo와 부분적으로 연동하기 시작한다.
  • Horizon이라는 새로운 에디터 인터페이스를 적용한다. 이는 기존의 Content Editor와 Experience Editor의 구분없이 한 UI에서 컨테츠를 관리하며 디자인 뷰를 제공한다.

  • SXA (Sitecore Experience Accelerator) 9.3 은 Front-End 개발 Workflow를 강화하는 동시에 추가적으로 새로운 2개의 Search Facet Filter를 추가한다.
  • 기존 SXA Theme은 Sitecore의 UI로만 제공되었으나, SXA 9.3에서부터 SXA CLI를 이용하여 Site Theme을 생성할 수가 있다. 
  • SXA 9.3는 Scriban 이라는 Text Templating Engine을 Rendering Variants를 적용하는데 이용할 수 있도록 한다.

  • Auto Personalization이라는 새로운 옵션을 적용하여 개별적인 아이템 Personalization 설정없이 Sitecore AI가 컨테츠를 체크하고 자동으로 적용을 시킨다.

Summer 2020

  • Sitecore SaaS 릴리즈 
  • ContentHub – DAM (Digital Asset Management)에 Video AI 적용시키며 Open API도 제공한다.
  • Experience Platform에 Container (예, Dockers)를 추가하고 쉽게 Instance 및 설정을 이미지화 할수있다. 이는 SaaS 이용과 동시에 큰 변화라 예상한다.
  • 새로운 개발 및 컨텐츠 에디터의 환경 변화가 있을꺼라 예상한다.

내년 Sitecore Symposium 2020은 시카고에서 10월 26일부터 10월 29일까지 진행되며, Sitecore 9.3의 Update 버전과 동시에 SaaS대한 많은 설명이 있을거라 예상한다.







2019년 9월 25일 수요일

SXA에서 새로운 사이트를 생성하고 스타일 업데이트하기

이번 포스트에서 Sitecore Experience Accelerator (SXA)에 대하여 소개하고 Creative Exchange Live를 통하여 웹사이트 Theme을 로컬 컴퓨터에서 수정하도록 하겠다.
필자는 "My Tenant"라는 영역에 "New Site"라는 SXA 사이트를 생성하였다. 아래의 스크린샷처럼 새로운 Theme 생성을 선택하고 사이트를 생성하면 새로운 Theme이 Media Library에 생성이 된다.




SXA Theme은 SASS, CSS, Javascript, Images 그리고 Fonts로 구성되어져 있으며, 새로 생성된 Theme은 SXA의 "Basic2" theme으로부터 복사 되어진 것이다. 이제는 Node.js 을 설치하여 Gulp를 사용하도록 하겠다. Gulp는 프론트엔드 개발자를 위한 Javascript 툴킷으로써 리얼타임 스트리밍 기능은 소스파일을 쉽게 수정하고 개발할 수 있도록 도와준다.

필자는 이미 Node.js 를 설치하였으므로 Creative Exchange를 통하여 새로 생성한 Theme을 내려받고 Visual Code에서 수정하도록 하겠다.
  1. 새로 생성한 Theme을 내려받기 전 "Site -> Presentation -> Page Designs"으로 이동 후 기본 Theme을 설정한다.

  2. 사용자가 Gulp로 Remote 엑세스할 수 있다록 "\App_Config\Include\z.Feature.Overrides\z.SPE.Sync.Enabler.Gulp.config.disabled" 파일 이름의 ".disabled" 지우고 "z.SPE.Sync.Enabler.Gulp.config" 이름으로 변경한다.
  3. 사용자자의 Account가 Powershell API를 사용할수 있도록 아래의 세팅을 Patch를 한다.

    <configuration>
      <sitecore>
        <powershell>
          <services>
            <restfulv2>
              <authorization>
                <add Permission="Allow" IdentityType="User" Identity="sitecore\YourUserName" />
              </authorization>
            </restfulv2>
        </powershell>
      </sitecore>
    </configuration>
    
  4. Content Editor에서 새로 생성한 사이트를 클릭 후, "Home" 탭에서 "Export" 버턴을 누른다.


  5. 아래의 세팅처럼 필자는 패키지를 서버로 저장하고 추후 패키지를 다시 "Import" 할 수있다록 "Agent" 모드를 사용하였다.
    1. 패키지의 기본 저장경로는 파일 시스템의 "/App_Data/packages/CreativeExchange" 이다.
    2. Agent Mode: 사이트 및 페이지의 모든 프리뷰 코드를 제공하며 Theme을 Import하는 필수 파일들이 포함되어있다.
      End-User Mode: 사이트 및 페이지의 모든 프리뷰 코드를 제공하며 Theme을 Import하는 필수 파일들이 포함되어 있지 않다.

  6. Visual Code에서 "/App_Data/packages/CreativeExchange/FileStorage/{Site Name}" 경로를 열어 Gulp를 사용하도록 하자.
  7. 먼저 경로의 "{Site Name}/{Site Theme Name}/gulp/config.js" 파일을 열어 "Server Options"의 "server"를 CMS URL로 변경한다.
    예) server: 'http://sc92u0sc.dev.local',
  8. "{Site Theme Name}"의 경로에서 Visual Code 터미널을 열어준다.
  9. "npm install" 커맨드를 통하여 Gulp 커맨드가 사용되어질수있는 환경을 생성한다.
    *Node Framework을 정상적으로 설치를 하였다면 아래처럼 Gulp 유틸이 설치되는것을 볼수있다.


  10. 설치가 완료되면 "gulp" 커맨드를 실행하고 Access가 주어진 Username과 Password를 입력 후, Gulp Watch가 실행되는지 확인한다.



  11. 새로 생성한 사이트로 이동하여 Home 페이지를 열어 기본적인 페이지를 생성한다.
  12. 필자는 "Home" 아이템에서 Accordion 컴포넌트를 추가하였다. Accordion 스타일은 Theme의 SXA "component-accordion.css"에서 지정되는데 Creative Exchange Live를 통하여 SASS 파일을 수정하고 Live 업데이트 과정을 보도록 하자.





필자는 Gulp를 통하여 Creative Exchange Live를 컨넥하고 SASS Pre-Processor를 이용하여 CSS file을 생성하였다. 새로운 CSS 파일이 생성되고 업로드되면 Sitecore SXA는 Theme 안의 CSS 파일들을 모아 Optimized CSS 파일을 생성하여 페이지 레이아웃에 적용한다.




2019년 8월 24일 토요일

사이트코어 심포지엄 - Sitecore Symposium 2019, 올랜도 플리리다

매년 사이트코어는 Sitecore Symposium을 개최함으로써 개발자들간의 정보를 공유할 뿐 아니라, 사이트코어의 현재 또는 차후 출시 될 새로운 기능을 소개하며 간력한 플랫폼 로드맵을 공유한다.

이번 Symposium은 작년과 동일하게 플로리다 올랜도에서 개최된다. 최근 사이트코어는 새로운 버전 9.2.0을 릴리즈 하였으며, 필자의 생각으로는 새로운 버전 소개보다 9.2의 새로운 기능 및 보완된 부분을 중점을 다룰것이라 예상된다. 9.2버전에서는 Content Hub 와 Cortex 기능을 이용한 머신러닝을 비롯하여 .NET Core을 사용하는 Universal Tracker 을 집중적으로 소개할것이라 생각한다.

Symposium은 약 3일간 진행되며, 본 Keynote 또는 세션은 화요일부터 보격적으로 시작될것이다. 아래는 이번 2019년도 Symposium 정보이며, 아래의 링크를 통하여 등록할수가 있다.

혹시, 궁금한점 또는 추가정보가 필요하다면 오른쪽은 "이메일 보내기"를 통하여 필자에게 메세지를 보낼수가 있다.


Sitecore Symposium: 2019년 11월 4일 ~ 7일 
Location: Walt Disney World Swan & Dolphin Resort, 올랜도 플로리다 

Registration: 100불 할인 및 등록하기

2019년 7월 26일 금요일

새로운 아이템 생성시 Display Name 업데이트 하기

사이트코어 CMS를 처음 사용하다 보면 Item Name과 Display Name에 대하여 혼돈이 되는 경우가 있다. Item Name은 웹페이지의 URL이 되며 아이템의 고유 이름을 뜻 한다. Display Name은 Content Editor에서 선택적으로 사용되어 Item의 닉네임이라고 생각하면 된다.

Content Editor 또는 xEditor에서 새로운 아이템 (페이지)를 추가하면 사이트코어는 새로운 Dialog 팝업을 오픈하고 Item Name을 입력하면 새로운 페이지가 형성된다. 만약 사용자가 Display Name을 추가하고자 한다면 새로 생성된 아이템으로 이동 후, Display Name 필드를 업데이트 하여야한다. 

이번 포스트에서는 Item을 생성할때 Item Name을 지정함과 동시에 Display Name도 업데이트할 수 있도록 Dialog Layout을 업데이트하고, Layout의 CodeBeside를 업데이트하여 아이템의 Display Name을 업데이트 하도록 하자.

아이템의 지정된 Insert Option은 기본적으로 Message Dialog Popup을 사용하지만 필자는 Insert Layout을 사용하여 Dialog Popup 레이아웃을 업데이트하기로 하였다.

아래는 필자가 업데이트 한 "Add New Item"의 레이아웃이다. 그리고 이 업데이트된 레이아웃을 "/sitecore/shell/override/" 폴더에 저장한다.

- /sitecore/shell/override/AddMasterContentManager.xml

 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
<?xml version="1.0" encoding="utf-8" ?>
<control xmlns:def="Definition" xmlns="http://schemas.sitecore.net/Visual-Studio-Intellisense">
  <AddMasterContentManager>
    <Stylesheet Src="SelectItemWithThumbnails.css" DeviceDependant="true" />
    
    <FormDialog ID="Dialog" Icon="Applications/32x32/document_new.png" Header="Add New Item" 
      Text="Enter an item name and display name for the new item" OKButton="Insert">
      <CodeBeside Type="Sitecore.Custom.Component.Core.Library.AddMasterForm,Sitecore.Custom.Component.Core.Library"/>
      <Script>
        Event.observe(window, "load", function() { 
          var focus = function() { $('ItemName').focus(); };
          
          $('Items').observe("click", focus);
          
          $$('#Items a.scItemThumbnail').each(function(thumb) {
            thumb.observe("click", focus);
          });
        });
      </Script>

      <div class="scStretch addMasterContainer">
        <Border Padding="4px 0px 0px 0px">
          <GridPanel Width="100%" Columns="2">
            <Border Padding="0px 4px 0px 0px" Style="white-space:nowrap;" GridPanel.Align="right" NoWrap="true">
              <Literal Text="Item Name:"/>
            </Border>
            <Edit ID="ItemName" GridPanel.Width="100%"/>
            <Border Padding="0px 4px 0px 0px" Style="white-space:nowrap;" GridPanel.Align="right" NoWrap="true">
              <Literal Text="Display Name:"/>
            </Border>
            <Edit ID="DisplayName" GridPanel.Width="100%"/>
          </GridPanel>
        </Border>
      </div>
    </FormDialog>
  </AddMasterContentManager>
</control>

아래는 Insert Option에 지정된 템플릿을 클릭하였을시 새로 생성한 레이아웃을 호출하고 Dialog 필드 값을 더블 파이프 라인 ("||")을 사용하여 Item Name과 Display Name의 값을 구분한다.

- AddMasterForm.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
using System;
using Sitecore.Configuration;
using Sitecore.Diagnostics;
using Sitecore.Web.UI.HtmlControls;
using Sitecore.Web.UI.Pages;
using Sitecore.Web.UI.Sheer;
using System.Text.RegularExpressions;

namespace Sitecore.Custom.Component.Core.Library
{
    public class AddMasterForm : DialogForm
    {
        protected Edit ItemName;
        protected Edit DisplayName;

        protected override void OnLoad(EventArgs e)
        {
            Assert.ArgumentNotNull((object)e, "e");
            base.OnLoad(e);
            if (Context.ClientPage.IsEvent)
                return;
        }

        protected void OK_Click()
        {
            if (this.ItemName.Value.Length == 0)
                SheerResponse.Alert("You must specify a name for the new item.");
            else if (!Regex.IsMatch(this.ItemName.Value, Settings.ItemNameValidation, RegexOptions.ECMAScript))
                SheerResponse.Alert("The name contains invalid characters.");
            else if (this.ItemName.Value.Length > Settings.MaxItemNameLength)
            {
                SheerResponse.Alert("The name is too long.");
            }
            else
            {
                SheerResponse.SetDialogValue(this.ItemName.Value + "||" + this.DisplayName.Value);
                SheerResponse.CloseWindow();
            }
        }

        protected override void OnOK(object sender, EventArgs args)
        {
            Assert.ArgumentNotNull(sender, "sender");
            Assert.ArgumentNotNull((object)args, "args");
            this.OK_Click();
        }
    }
}

아래 코드는 새로운 "item:addmaster" 커맨드로써 "control:AddMasterContentManager" 라는 새로운 레이아웃을 불러들이고 "SetItemDisplayName" method를 통하여 새로 생성된 아이템 "obj2"의 Display Name을 업데이트 한다. 해당 코드는 "Sitecore.Shell.Framework.Commands.AddMaster" 클래스를 수정한것이다.

- AddMasterCommand.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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
using Sitecore.Data.Events;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Exceptions;
using Sitecore.Globalization;
using Sitecore.Resources;
using Sitecore.Shell.Framework.Commands;
using Sitecore.Text;
using Sitecore.Web.UI.Sheer;
using System;
using System.Collections.Specialized;

namespace Sitecore.Custom.Component.Core.Library
{
    /// <summary>Represents the AddMaster command.</summary>
    [Serializable]
    public class AddMasterCommand : Command
    {
        /// <summary>Occurs when item created.</summary>
        protected event ItemCreatedDelegate ItemCreated;

        /// <summary>Executes the command in the specified context.</summary>
        /// <param name="context">The context.</param>
        public override void Execute(CommandContext context)
        {
            if (context.Items.Length != 1 || !context.Items[0].Access.CanCreate())
                return;
            Item obj = context.Items[0];
            Context.ClientPage.Start((object)this, "Add", new NameValueCollection()
            {
                ["Master"] = context.Parameters["master"],
                ["ItemID"] = obj.ID.ToString(),
                ["Language"] = obj.Language.ToString(),
                ["Version"] = obj.Version.ToString()
            });
        }

        /// <summary>Queries the state of the command.</summary>
        /// <param name="context">The context.</param>
        /// <returns>The state of the command.</returns>
        public override CommandState QueryState(CommandContext context)
        {
            Error.AssertObject((object)context, nameof(context));
            if (context.Items.Length != 1)
                return CommandState.Hidden;
            if (!context.Items[0].Access.CanCreate())
                return CommandState.Disabled;
            return base.QueryState(context);
        }

        /// <summary>Adds the specified args.</summary>
        /// <param name="args">The arguments.</param>
        protected void Add(ClientPipelineArgs args)
        {
            if (!SheerResponse.CheckModified())
                return;
            Item obj1 = Context.ContentDatabase.GetItem(args.Parameters["Master"]);
            if (obj1 == null)
                SheerResponse.Alert(Translate.Text("Branch \"{0}\" not found.", (object)args.Parameters["Master"]));
            else if (obj1.TemplateID == TemplateIDs.CommandMaster)
            {
                string message = obj1["Command"];
                if (string.IsNullOrEmpty(message))
                    return;
                Context.ClientPage.SendMessage((object)this, message);
            }
            else if (args.IsPostBack)
            {
                if (!args.HasResult)
                    return;
                Item parent = Context.ContentDatabase.Items[StringUtil.GetString(args.Parameters["ItemID"]), Language.Parse(StringUtil.GetString(args.Parameters["Language"]))];
                if (parent == null)
                    SheerResponse.Alert("Parent item not found.");
                else if (!parent.Access.CanCreate())
                {
                    Context.ClientPage.ClientResponse.Alert("You do not have permission to create items here.");
                }
                else
                {
                    Item obj2 = (Item)null;
                    try
                    {
                        string[] inputValue = args.Result.Split(new[] { "||" }, StringSplitOptions.None);
                        string itemName = inputValue[0];
                        string displayName = inputValue[1];

                        if (obj1.TemplateID == TemplateIDs.BranchTemplate)
                        {
                            BranchItem branch = (BranchItem)obj1;
                            obj2 = Context.Workflow.AddItem(itemName, branch, parent);
                            Log.Audit((object)this, "Add from branch: {0}", AuditFormatter.FormatItem((Item)branch));
                        }
                        else
                        {
                            TemplateItem template = (TemplateItem)obj1;
                            obj2 = Context.Workflow.AddItem(itemName, template, parent);
                            Log.Audit((object)this, "Add from template: {0}", AuditFormatter.FormatItem((Item)template));
                        }
                        if (obj2 != null)
                        {
                            SetItemDisplayName(obj2, displayName);
                        }
                    }
                    catch (WorkflowException ex)
                    {
                        Log.Error("Workflow error: could not add item from master", (Exception)ex, (object)this);
                        SheerResponse.Alert(ex.Message);
                    }
                    if (obj2 == null || this.ItemCreated == null)
                        return;
                    this.ItemCreated((object)this, new ItemCreatedEventArgs(obj2));
                }
            }
            else
            {
                UrlString urlString = ResourceUri.Parse("control:AddMasterContentManager").ToUrlString();
                Item parent = Context.ContentDatabase.Items[StringUtil.GetString(args.Parameters["ItemID"]), Language.Parse(StringUtil.GetString(args.Parameters["Language"]))];
                parent.Uri.AddToUrlString(urlString);
                SheerResponse.ShowModalDialog(urlString.ToString(), "500px", "275px", string.Empty, true);

                args.WaitForPostBack();
            }
        }

        public void SetItemDisplayName(Item item, string displayName)
        {
            item.Editing.BeginEdit();
            item.Fields["__Display name"].Value = displayName;
            item.Editing.EndEdit();
            item.Editing.AcceptChanges();
        }
    }
}

그리고, 마지막으로 Patch를 통하여 새로운 업데이트 된 커맨드를 업데이트하여 Sitecore에 적용시킨다.

- z.Sitecore.Patch.config

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/">
  <sitecore>
    <commands>
      <command name="item:addmaster">
        <patch:attribute name="type">Sitecore.Custom.Component.Core.Library.AddMasterCommand, Sitecore.Custom.Component.Core.Library</patch:attribute>
      </command>
    </commands>
  </sitecore>
</configuration>











2019년 6월 29일 토요일

xEditor의 Navigation Bar 기본값 변경하기

현재 직장에는 Sitecore 플랫폼 서비스를 제공하면서 각 부서마다 Sitecore Author 및 Contributor 사용자가 있다. 이들은 저희 IT부서로부터 트레이닝을 마친 후, 사이트코어 접속 권한을 가지며 각 부서에 해당되는 컨텐츠만을 관리할 자격이 주어진다.

비록, 트레이닝을 잘 마쳤지만 Sitecore의 많은 기능들을 짧은 시간안에 숙달하기는 어려움이 있다. 최근 몇몇의 사용자로 부터 추가 요청이 되어진것이 바로 xEditor의 Navigation Bar은 기본값으로 Enable 하는것이었다.

사이트코어는 각 사용자의 User Profile을 데이타베이스에 저장하고, 유저 어카운트에 설정되어진 정보를 기억하기 때문에 다시 설정 할 필요가 없다. 고로, 유저가 Navigation Bar를 항상 xEditor 상단에 노출하기 위해서는 꼭 View 탬의 Navigation Bar의 체크박스를 체크하여야 한다. 사실 이런 기본적인 설정 하나하나는 Best Practice이므로 트래이닝 또는 사용자의 입장에서 기능을 인지하고 직접 설정을 하여야한다.

하지만, 현 직장에서는 클라이언들의 맞춤형 서비스(?)를 아주 중요하게 생각하므로 해당 옵션의 기본값을 Enable로 설정하여 항상 Bar가 노출될수 있도록 하기로 결정하였다.

사이트코어는 기본적인 옵션 설정을 자바스크립 컨맨드를 사용하여 Library를 호출하는데 Navigation Bar 자바스크립트에 한 라인을 추가하여 쉽게 기본값을 변경할 수가 있다.

먼저, 해당 경로로 이동한다.
\sitecore\shell\client\Sitecore\ExperienceEditor\Commands\ShowNavigationBar.js

아래처럼 "context.button.set("isChecked", "1");"을 canExecute 함수의 상단에 추가한다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
......
Sitecore.Commands.ShowNavigationBar =
  {
    canExecute: function (context) {
      //// 추가부분 시작
      context.button.set("isChecked", "1");
      //// 추가부분 끝
      context.app.RibbonBreadcrumb.set("isVisible", context.button.get("isChecked") == "1");
      context.app.setHeight();
      return true;
......





2019년 5월 29일 수요일

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

이번 포스트는 API를 사용하여 렌더링 아이템의 Datasource를 생성한 Implementaion의 추가로써, xEditor에서 Datasource를 생성하고 페이지에 저장 및 Apply를 조금 더 효율적으로 할수있는 팁을 알아보자.
우린 Datasource 생성 버튼을 클릭하여 메인 소스를 만들었지만, 추가적으로 필드 에디터 버턴을 생성하여 Content Editor를 Dialog Popup 형식으로 xEditor 띄워 필드정보를 수정할수있다.
데이타소스의 필드정보 업데이트는 "webedit:fieldeditor" 커맨드를 통하여 실행할 수 있지만, "webedit:componentoptions"처럼 Content Editor 팝업에서 "OK" 버튼을 클릭할시 자동저장 (autosave) 기능이 없다.

"webedit:fieldeditor" 커맨드는 Sitecore의 Content Manager 어플리케이션의 FieldEditorPage 클래스를 호출하며 우리는 간단한게 "FieldEditor.aspx" 페이지에 Javascript을 추가하여 Auto-Saving을 적용할것이다. 추후 "componentoptions" 커맨드처럼 기능이 추가될지 모르겠으나, 이 업데이트는 Sitecore 버전 9.1 (현재 가장 최근 버전) 및 하위 버전까지 모두 적용되어질수 있다.

자, 이제 렌더링 아이템의 데이터소스 필드를 xEditor에서 손 쉽게(?) 수정하는 방법과 업데이트 되어진 Field값을 자동저장하는 방법에 대하여 알아보도록 하겠다.

우선, 해당 경로의 FieldEditor 파일을 오픈하고, 아래의 자바스크립을 추가한다.

"\sitecore\shell\Applications\Content Manager\FieldEditor.aspx"

 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
<script type="text/javascript">
    var p = parent.parent;
    var iframes;
    try {
        iframes = p.jQuery('#jqueryModalDialogsFrame').contents().find('.ui-dialog');
    } catch (e) { }
    if (p && typeof iframes != 'undefined' && iframes.length == 1) {
        var footerRow = document.getElementById("FooterRow");
        var inp = footerRow.getElementsByTagName("input");
        var btn;
        for (i = 0; i < inp.length; i++) {
            if (inp[i].value == "OK") {
                if (p.autoSaveItems != false) {
                    btn = inp[i];
                    btn.setAttribute("onclick", "javascript:return ClickAndSave()");
                }
                break;
            }
        }
    }
    function ClickAndSave() {
        if (p.Sitecore.PageModes.PageEditor.isModified()) {
            p.Sitecore.PageModes.PageEditor.postRequest("webedit:save", function() { p.onbeforeunload = null; }, false);
        }
        scForm.invoke("contenteditor:saveandclose");
        p.Sitecore.PageModes.PageEditor.postRequest("webedit:saved", function() { p.location.reload(); }, true);
    }
</script>


아래는 업데이트 되어진 VIEW (RichTextEditor.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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@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;
            }
            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 if (Sitecore.Context.PageMode.IsExperienceEditor) {
    <div style="border-bottom: solid 1px grey;">
        <button type="button" class="btn btn-warning" style="margin-bottom: 5px;" onClick="updateRTEStyle()">Update Style</button>
    </div>
    <div style='@Model.Rendering.Item.Fields["Custom CSS"].Value'>@Html.Sitecore().Field("Content")</div>
    <script>
        function updateRTEStyle() {
            Sitecore.PageModes.PageEditor.postRequest(
                "webedit:fieldeditor(" +
                    "command={11111111-1111-1111-1111-111111111111}, " + 
                    "fields=Custom CSS, " + 
                    "id=@(Model.RenderingDatasource))"
                );
        }
    </script>
} 
else
{
    <div style='@Model.Rendering.Item.Fields["Custom CSS"].Value'>@Html.Sitecore().Field("Content")</div>
}


아래는 최종적으로 업데이트 되어진 화면이며, ContentEditor 팝업박스의 OK 버턴을 누르면 자동으로 업데이트되고 xEditor가 Reload 되는것을 볼수가 있다.







2019년 4월 11일 목요일

사이트코어 대쉬보드의 Custom Analytics 패널 숨기기

사이트코어 Experience Analytics(xA)는 User Experience를 다이나믹하게 분석 및 수집을 할뿐 아니라 수집되어진 정보를 통하여 마케팅의 목족으로 다양하게 Customize하여 사용할수있다. xA의 대쉬보드를 엑세스하면 웹사이트 방문자들의 정보를 다양하게 분석하고 최적화하여 차트 통계를 제공한다.



xA의 접근은 아래 리스트(v9.1.0 기준) 처럼 마테팅 및 분석과 관련된 사이트코어 펄밋이 필요하다. 이는 Makerting Control Panel을 통하여 새로운 마케팅 캠페인, 목표 (Goal), Profile 및 Asset 등을 설정하여 다양하게 방문자 정보를 수집한다.

  • sitecore\Analytics Advanced Testing
  • sitecore\Analytics Content Profiling
  • sitecore\Analytics Maintaining
  • sitecore\Analytics Management Reporting
  • sitecore\Analytics Personalization
  • sitecore\Analytics Reporting
  • sitecore\Analytics Testing
  • sitecore\Marketing Automation Editors
  • sitecore\EXM Advanced Users (이메일 캠페인)
  • sitecore\EXM Users (이메일 캠페인)

사이트코어 관리자 입장에서 새로운 유저를 등록하고 유저타입에 상응하는 Permit을 주는데 있어서 굳이 Non-Marketing 유저에게 xA 정보 및 엑세스 권한을 부여할 필요는 없다. 이는 해당 유저에게 xA와 관련된 Role을 부여하지 않으면 되지만, 유저는 Sitecore 메인 대쉬보드에서 Custom Analytics라는 통계 프리뷰를 볼수가 있다. 이 패널의 뷰는 기본값으로써 권한에 관계없이 모든 사이트코어 CMS유저에 보여진다. 사이트코어 관리자 및 Stakeholder의 입장에서는 CMS유저 및 서비스를 제공하고 Client 유저에게 얼마나 많은 사용자가 웹사이트를 방문했고 그의 Value가 얼마나 되는지 정보를 제공할 필요는 없다. 이번 포스트에서는 Core DB에 접근하여 쉽게 Custom Analytics 패널을 Disable하도록 하자.
  1. CMS 로그인 후, Desktop 뷰 또는 QueryString을 업데이트하여 Core DB로 이동
  2. "/sitecore/client/Applications/ExperienceAnalytics/Shared/LaunchPad/Parameters" 이동
  3. Parameters 트리안에는 "Interactions by visits and value per visits Parameters" 와 "Top five campaigns by visits Parameters" 차트 렌더링 아이템이 존재한다.

  4. 해당 아이템에서 "IsVisible" 값을 Uncheck하고 저장한다.
  5. CMS의 Master DB로 이동 후, Dashboard를 Refresh하면 Custom Analyze의 패널이 없어진것을 확인할수있다.