2021년 4월 27일 화요일

사이트코어 10.1.0 한글버전 패치 - Korean Language Patch

이번 포스트는 이전과 같이 사이트코어 10.1.0버전의 언어팩을 한글화하였다. 이전버전과는 다르게 Core, Master, Web 데이타베이스의 Data파일을 따로 관리하고 기존의 한 파일에 많은 언어를 분리하여 파일 사이즈도 줄이며 조금더 Organize 된 느낌이 든다.

테스트 해보진 못했지만 Data파일을 파일 시스템 폴더에 옮겨 언어팩이 패치가 될때 파일을 불러들이고 Back-End에서 XML파일로 변환을 하여 언어팩을 적용시키는것으로 보인다. 고로, 필자는 데이타파일을 변환하는데 어려움이 있었으며, 이를 Debug하기위해서는 소스 코드 및 Assembly를 De-Compile을 해야만 했다. 차후 이 부분에 대해서는 추가적으로 업데이트 하겠으며, 기존 방식의 메인 언어팩 파일 ("texts.ja-JP.xml")만을 변경하였다. 파일을 GitHub에 업로드 되어있으며, 추가적인 설치사항은 사이트코어 언어팩 설치방법을 참고 하거나 이전 포스트를 확인하기 바란다.

https://github.com/udt1106/Sitecore-Client-Translations-Korean/tree/Sitecore-10.1.0



2021년 3월 26일 금요일

사이트코어 페이지 렌더링 최소화 하기 (Performance)

긴 휴가를 마치고 오랜만에 포스트를 작성해 본다. 이번 포스트에서는 웹개발 및 플랫폼 관리에 있어서 가장 기본적이면서도 놓치기 쉬운 요소에 대하여 알아보기로 하자. 알다시피, 사이트코어는 무궁무진(?)하게 플랫폼을 비지니스 컨셉 또는 사용자에 맞게 확장 및 개발 구조를 변경할 수가 있다. 예를 들어, 하나의 플래폼에 많은 개발 에이전시들이 이용하고 있으면 이에 맞게 개발 구조를 변경해야 할 뿐더러 관리자 입장에서는 각각 에이전시 개발자들이 플랫폼의 기준을 얼마나 잘 준수하며 개발을 하는지에 대하여 관리를 해야한다. 

개발 준수사항을 따르지 않고 웹사이트를 만들고 설계할 시 소수의 웹사이트 관리는 조그만한(?) 노력으로 충분히 보완하고 수정할 수 있지만, 사이트의 수 및 사이즈에 따라 보수하는데에 있어서 하나의 프로젝트로 진행할만큼의 시간, 노력, 그리고 비용이 든다. 특히나, 컴포넌트 및 모듈을 많은 웹사이트들과 공유하는데 있어서는 더욱 그러하다.

그 중의 또 하나의 가장 큰 문제점은 웹사이트 Performance 이다. 겉보기에는 웹사이트가 이쁘게 만들어졌고, 잘 돌아가는것처럼 보이나 그 뒤에 서버, 플랫폼, 데이타베이스 등은 개발 준수사항을 따르지 않음으로써 많은 리소스를 소비할뿐더러 요즘첨럼 클라우드를 기반으로 Infrastructure가 짜여져 있는 환경에서는 비용까지 고려해야한다. 또한, 고객 및 웹사이트 방문자의 입장에서도 웹사이트의 느린 렌더링 그리고 페이지를 이동하고 관련된 미디어를 노출하는데에도 시간이 소비되는 부분에 있어서 마케팅 및 비지니스에 영향을 끼친다.

필자는 지금까지의 경험을 토대로 어떻게 사이트코어의 Performance를 최적화 할 수 있는지에 대한 요소를 정리해 보았다.

  • Roslyn Compiler - 로슬린 컴파일러  오픈소스 컴파일러이면서 API를 통하여 .NET 코드를 분석한다. 고로, 로슬린 .NET 코드 분석툴이라고 설명할 수가 있다. 먼저, Visual Studio에서 사이트코어 컴포넌트 작업을 할때 NuGet Compiler를 설치하고 개발을 하다보면 어디에서 코드 문제가 발생하는지 바로 알수있다. 로슬린 컴파일러는 사이트코어가 Views 로드하는데 미리 컴파일을 함으로써 로딩 시간을 더욱 빠르게 해준다. 사이트코어는 이미 DLLs파일을 포함하고 있으며, 이를 Web.Config 파일에서 활성화만 시켜주면 된다.

    <system.codedom>
        <compilers>
          <compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:1659;1699;1701" />
          <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:41008 /define:_MYTYPE=\&quot;Web\&quot; /optionInfer+" />
        </compilers>
    </system.codedom>

  • HTML Cache Setting - 이는 웹사이트 방문자 관점에서 페이지 렌더링 속도를 향상시킬수 있다. 특히나 SXA를 사용하고 있을때에는 각 사이트 세팅마다 컴포넌트에 대하여 HTML Cache 레이어를 쉽게 설정할 수가 있다.

  • Data Cache - HTML Cache 처럼 사이트코어처럼 데이타소스를 많이 이용하는 플랫폼에서는 특히나 중요하다. 기본적으로 초기 세팅이 되어있는데 이는 새로운 사이트코어를 셋업할때의 기본 세팅이므로 사이트에 맞게 설정이 중요하다.

  • Asset Optimizer - 이는 SXA의 기반의 웹사이트를 개발할때 특히나 중요하다. 만약 이를 세팅하지 않고 웹사이트를 렌더링할시 하나하나의 CSS/JS 파일을 Request/Response 하는데 있어서 많이 리소스가 필요하 수 있고, 이는 사이트 Performance에 영향을 미친다. Optimizer를 사용함으로써 Asset의 요소를 모두 모아 Minify 하여 리소스 소비를 간략 시킬수 있다.

  • IIS Dynamic Content Compression - 이는 HTTP Compression으로써 IIS를 설치할때 추가적인 기능중의 하나다. 이 Compression은 IIS와 사용자 브라우져간에 Transmission 시간을 빠르게 해줌으로써 컨텐츠를 빠르게 렌더링하는데 도움이 된다.

  • The Limited Number of Child Item - 싱글 노드 아래 많은 많은 Child 아이템이 있다면 CMS내에 에디팅을 하는데 Performance 문제가 있을 수 있다. 고로, 100개의 Child 아이템으로 제한하는것을 권한다.

  • The Limited Number of Rendering Variants - SXA기반으로 웹사이트를 개발하다보면 기존의 SXA 컴포턴트를 이용하여 추가적으로 새로운 스타일 및 레이아웃의 렌더링 옵션을 만들수가 있다. 하지만, 너무 많은 옵셥은 오히려 Performance 또는 에디팅에 있어서 모듈을 렌터링하는데 시간이 걸린다. 고로, 15개의 옵션으로 제한을 두는것을 권한다.

  • Image Parameter - 요즘은 데스크탑 및 모바일뷰를 Responsive Design으로 개발을 한다. 하나의 고정된 이미지를 사용하기 보단 모바일뷰일때는 Image Parameter를 이용하여 서버에서 이미지를 re-size할수가 있다. 또한, 수정된 이미지는 파일시스템내의 데이타 Cache 폴더에 저장되므로 다음 Request가 있을시 반복적인 렌터링 및 수정 작업을 필할수있다.

  • The Limited Number of Components on Page - 한 페이지의 들어가 컴포넌트수는 제한을 두어 페이지 렌더링 및 수정작업을 하는데 리소스 소비를 줄일수 있다. SXA 기반의 웹사이트일 경우 일반적이며 많은 페이지에 반복적으로 들어가는 컴포넌트라면 Partial Design을 통하여 우선적으로 세팅을 하고, 각각의 페이지에는 그 페이지에 맞는 컴포넌트를 추가하도록 하자. 이는 CMS내의 작업뿐만 아니라, 서버의 CPU 및 메모리 소비에도 영향을 주므로 명심하자.



2020년 10월 24일 토요일

사이트코어 10.0.0 한글버전 패치 - Korean Language Patch

이미 Sitecore 10이 릴리즈되지 몇 개월이 지났지만, 회사 및 바쁜 스케쥴의 핑계로 미뤄두었던 사이트코어 10.0.0 한글 패치를 업데이트 하였다. 설치는 기존 사이트코어 9.3.0의 방법과 동일하며 패치파일은 GitHub에 "Sitecore-10.0.0" 브랜치에서 다운로드 할 수 있다.

패치 다운로드: https://github.com/udt1106/Sitecore-Client-Translations-Korean/tree/Sitecore-10.0.0
설치 방법: https://sitecorespace.blogspot.com/2020/04/sitecore-client-translations-korean.html


Sitecore Symposium 2020

추가적으로 다음주부터 Sitecore Symposium 2020 (Oct. 26-28, 2020)이 시작된다. 코로나 여파로 이번해는 온라인 디지털로 진행되며, 일반 참석자의 경우는 제한적이지만 무료 엑세스를 제공한다. 풀엑세스를 원한다면 $395로 세션 및 Virtual Network를 이용할 수 있다. 아래는 일반 엑세스와 풀 엑세스를 구분하였다.

General Access

  • 5 General session keynotes (Premium keynotes are not included) 
  • Partner roundtables 
  • Localized panel discussions 
  • Limited access to the Partner Pavilion 
  • Learn about Sitecore products in the CORE

Full Access ($395)

  • All 8 Premium and General Session keynotes
    • 3 Premium, including Leslie Odom Jr., Forrester’s Rusty Warner and Sitecore’s product roadmap
    • 5 General
  • 100+ Breakout sessions
    • Featured and On-demand (continue learning post event)
  • Virtual networking
    • Virtual Happy Hour
    • Full access to the Partner Pavilion
    • 1:1 chats*
    • Meeting requests*
    • Matchmaking with registrants and sponsors*
  • Access to Guru Bar
    • Get your Sitecore questions answered by an expert in real time
  • Exclusive training seminars
    • Led by Sitecore training team
  • Learn about Sitecore products in the CORE
  • Opportunities for giveaways and prizes
  • Partner roundtables
  • Localized panel discussions
  • Full access to activity breaks
  • Access to post-event experience




2020년 8월 14일 금요일

패키지 디자이너 (Sitecore - Package Designer) 에서 일반 유저 "Download" 버턴 활성화 하기

코로나 바이러스로 인하여 전 세계적으로 많은 변화가 진행되고 있고, 그 임팩트는 도무지 가라앉을 기색이 보이지 않는다. 필자 역시 3월 중순부터 재택근무를 시작하여 회사로부터의 공식적인 공지는 1월초까지 재택근무. 하루하루 끊임없는 Skype 및 WebEx 미팅으로 인하여 시간이 어떻게 지나가는지 잊을뿐더러 업무시간 외에는 논스톱으로 가정일을 도와야하는 일이 반복적으로 진행된다. 정말 일상적인 시간이 얼마나 소중하였는지 깨닫게 된다.

회사 업무의 핑계로 블로그를 몇달 동안 소홀히 하였다. 다시 본격적으로 마음가짐을 잡고 이번 글에서는 Package Design 기능에서의 문제점(?)에 대하여 알아보고 수정하는 방법을 살펴보도록 하자.

필자의 회사에서는 많은 컨트랙터 및 Vendor 그리고 개발자들이 회사 사이트코어에 접속하여 웹사이트를 개발하고 있다. 몰론 멀티플 스태이징을 통하여 DEV, UAT, PROD 등으로 나누어 관리하고 있으며. 또한 Service Delivery 팀(DevOps)이 사이트코어 플랫폼, Deployment, 시스템 및 서비스센터등을 관리한다. 고로 개발자들은 허용된 범위내에서 사이트를 관리하며 컨텐츠 및 컴포넌트를 개발 업데이트한다. 여기서 개발자들은 업데이트 된 정보를 상위 환경으로 Deploy하기 어떤 아이템이 수정되었는지 서비스팀에게 알려줘야하는데 수정된 항목 및 아이템이 많을경우 리스트하기가 쉽지가 않을뿐더러 불필요한 시간을 소비하게 된다. 고로, Package Designer를 사용하여 프로젝트를 저장하고 XML 파일을 공유하는 방법을 택하였다.

아래 사진처럼 프로젝트를 저장하고 "Open"경로를 열면 저장되어진 모든 프로젝트 파일이 나온다. XML파일은 팩키지에 추가되어진 모든 아이템 항목이 포함되어있어 쉽게 어떤 항목이 업데이트 되었는지 구분할뿐더러 서비스템에게 쉽게 전달할수가 있다.

사이트코어 패키지 디자이너

하지만 여기서 문제점은 Download. 사이트코어는 Admin 유저에게만 기본적으로 Download 버튼을 활성화 시킨다. 유저가 Developer 또는 Designer Role에 소속이 되었어도 해당 Download버턴을 보이지가 않는다. Core 데이타베이스에서 아무리 아이템정보를 찾아보아도 찾을수가 없었고, 이 Dialog 다운로드 버턴은 어프리케이션 폼 파일 (.xml)을 통하여 관리가 되고 UI가 형성되는것을 알수가 있었다. 고로 해당파일 "/sitecore/shell/Applications/Install/Dialogs/BrowseDialog.xml" 파일에서 "${Sitecore.Context.IsAdministrator.ToString()}" 부분을 "True"로 변경을 한다.

<Toolbutton Icon="Office/16x16/download.png" Tooltip="Download the selected file" Header="Download" Click="dialog:download" Visible='${Sitecore.Context.IsAdministrator.ToString()}'/>

<!-- 아래로 변경 -->

<Toolbutton Icon="Office/16x16/download.png" Tooltip="Download the selected file" Header="Download" Click="dialog:download" Visible='True'/>






2020년 4월 28일 화요일

사이크코어 한글버전 적용시키기

사실 예전부터 계획해 놓았던 작업중에 하나가 바로 사이트코어 한글화를 하는 작업이었다. 몇 번의 시도가 이미 있었지만 의도한대로 번역이 잘 이뤄지지 않았고, 몇 번의 재시도 끝에 드디어 한글 언어팩을 최근에 완료할 수 있었다. 사이트코어에서 기본적으로 제공하는 언어팩을 보면 분석 또는 번역을 해야하는 텍스트가 너무 방대하다. 고로, 이는 하나하나씩 번역을 하는것은 시간을 많이 소요할뿐더러 많은 인내가 필요하다.

필자는 네이버에서 제공하는 파파고 (Papago) AI 번역기를 사용해보았다. 하지만, 무료 버전의 경우 API는 하루 제한 양인 1만자의 글을 번역을 하고, 그 이상의 번역을 구입을 하여야한다. 사실 구입하는 비용은 큰 고려대상은 아니었지만, 번역 퀄리티의 문제가 많이 보였다. 필자는 기본 번역 소스를 사이트코어 일본어로 하였고, 일본어에서 한국어를 번역하는데는 오류가 많이 나타났었다. 그래서 다른 API를 찾아본것이 Google Translate API이다. 구글 번역 API는 무료 버전을 제공하지 않지만, 첫 Google Cloud 가입자에게는 1년안에 모든 API를 사용할 수 있는 $300 크레딧을 제공하였다. 또한, 파파고와 비교해보았는데 번역의 퀄리티는 훨씬 뛰어났다.

로컬 호스트에 WAMP를 설치하고, 그나마 필자에게 만만한(?) PHP를 선택하여 Google Translate API를 로컬에서 실행시켰다. 아무리 시대가 발전하고 새로운 기술이 나와도 디테일부분에서는 많은 부족함이 보였고, 직접 리뷰를 하고 확인을 하는 절차가 필요했다. 많은 매뉴얼 수정과 번역 스크립 시도를 통하여 드디어! 사이트코어 한국어 팩 (버전1)을 완성하였다.

파일 다운로드: https://github.com/udt1106/Sitecore-Client-Translations-Korean/blob/Sitecore-9.3.0/ko-KR.xml

플랫폼 버전은 사이트코어 9.3.0이며, 아래는 한국어 팩을 설치하고 추가적으로 수정해야하는 부분을 설명하였다.

  1. 작업은 Core 데이터베이스에서 이루어지므로, 필요에 따라 Core 데이터베이스를 백업해둔다.
  2. 사이트코어 로그인 후, Core 데이터베이스로 이동을 한다.
  3. Core 데이타베이스에서 Control Panel로 이동한다.
  4. Control Panel에서 "Add a new language"로 이동 후, 한국어 코드를 리스트에서 선택한다.



  5. 입력을 완료하여, 한국어를 등록하고 "Import language"로 이동하여 다운반은 한글 XML파일을 로드한다. 필자는 사이트코어 루트의 "/App_Data/" 폴더에 파일을 저장하였다.



    다음 버턴을 눌러, 이전에 추가해놓은 한국어 버전을 선택하고,



    Core 데이터베이스를 선택 후, "Import"를 실행한다.


  6. "Import"를 진행하고 약 몇 분 또는 10분의 시간이 지나면 완료된 메세지를 볼수가 있다.



  7. 완료 후, Control Panel로 돌아가 "My Settings"의 "Region and language options"로 이동 후, 한국어로 변경을 한다.

  8. 한국어 적용 후, Control Panel 페이지를 Refresh하면 한국어로 변경 된것을 볼수가 있다.


하지만, 추가적으로 설정을 해야하는 부분이 있다. 하나는 Solr 서치엔지, 그리고 다른 하나는 Powershell이다. 두 기능은 사이트코어의 플랫폼 설치에 포함되어 있지 않는 기능 및 모듈이고 이는 추가적인 설정을 통하여 Solr에서 한글을 서치할수 있도록, 그리고 Powershell을 기복적으로 Language Fallback이 설정되었는 않는 관계로 Korean 버전을 등록하여한다. Powershell의 모듈 팩키지에는 영어, 덴마크어, 그리고 일본어 버전만 추가되어져 있다.

먼저, Solr 설정부터 알아보도록 하겠다. 간단하다. Solr 서치엔진은 한국어를 지원하지만 사이트코어 데이타베이스의 Index Schema에는 다이나믹 키가 설정 되어있지가 않다. 고로, Solr 파일 루트로 이동후 "\server\solr\" 폴더 안의 인덱스 폴더들의 "managed-schema" 파일을 오픈하여 아래의 다이나믹 필드 노드를 추가한다. 필자는 두개의 xDB 폴더는 제외하였다.


<dynamicField name="*_t_ko" type="text_ko" indexed="true" stored="true"/>


모든 인덱스 폴더 업데이후, Solr 서버를 재시작하고 사이트코어 Application Pool을 Recycle을 한다. Control Panel로 이동하여 데이터베이스 인텍스 재생성을 실행하면 오류없이 인덱싱이 완료될 뿐 더러, Content Editor에서 한글 서치가 가능하다.




두번째는 Powershell이다. 방금 전 얘기했듯이 한국어 버전이 모듈에 포함되어있지 않는 관계로 Powershell 메뉴와 관련된 아이템은 글을 보이지가 않는다. 고로, 필자는 한국버전을 추가하고 한국어의 이름을 추가하여 Powershell의 메뉴 아이템의 한국화 하도록 하였다. Powershell의 많은 메뉴 아이템과 기능이 있는 관계로 하나하나씩 찾아서 업데이트하는데 번거로움이 있을수있다. 고로, Unicorn, TDS 또는 아이톔 Serialize를 통하여 변경하는 방법도 있다.



이번 버전은 9.3.0에 초점을 두었을뿐더러 베타버전이다. 차후 업그레이드 버전이 나올때마다 추가적인 기능 및 텍스트에 대해서는 꾸준히 작업을 하여야한다. 혹, 필자와 같이 작업 또는 아이디어를 공유할 분이 있다면 언제든지 환영하며, 오른쪽은 메일보내기 폼을 통하여 필자에게 메세지를 보내주셨으면 한다.







2020년 2월 26일 수요일

사이트코어 SXA 사이트 - 페이지 디자인 및 부분 디자인

사이트코어 SXA의 큰 장점중의 하나는 프리젠테이션 레이어를 분리하여 웹사이트/페이지 디자인을 재사용할 수 있도록 구분하였다. 그 뿐만 아니라, 디자인 레이어를 Page Design과 Partial Design으로 구분하여 Partial Design 아이템이 Page Design에 상속되는 구조로 Partial Design의 상속여부에따라 Page Design이 다양하게 변경될수가 있다.

아래의 사진처럼 SXA 사이트 루트에 Presentation 노드가 있으며, 그 안에는 사이트/페이지 스타일 및 렌더링 아이템 설정과 관련된 아이템이 존재한다. Page Design과 Partial Design 노드를 오픈하면 디자인을 구성하는 아이템이 포함되어있다.




디자인 아이템은 웹페이지 아이템처럼 컴포넌트를 추가하고 페이지 형식으로 디자인 할 수 있으며 아래의 그림으로 디자인 스트럭쳐를 좀 더 쉽게 이해할수 있다.



사진에서 보듯이 Partial Design은 페이지 형식으로 컴포넌트를 추가하여 고정 컨텐츠 및 Placeholder를 구성할여 Page Design에 상속을 시킨다. 페이지 디자인에서도 같은 형식으로 컴포넌트를 추가여 페이지 디자인 레벨의 고정 컨턴츠를 정의 할수 있는며 이는 본 웹페이지 아이템으로 상속이 된다. Page Design은 하나 이상의 Partial Design을 상속할수 있지만 웹페이지 아이템은 오직 하나만의 Page Design을 상속할수 있다.

Page Design 아이템에서 각각 SXA 사이트 웹페이지마다의 디자인을 정의할수 있으며, 또는 Experience Editor의 Experience Accelerator 탭의 "Site Page Designs" 메뉴에서 페이지 디자인을 정의 할수가 있다.









2020년 1월 23일 목요일

사이트코어 SXA 구조

2020년도 첫 포스트는 SXA로 시작을 해보자. 사이트코어 플랫폼에서는 새로운 웹사이트를 쉽게 생성하고 관리하는 SXA의 비중이 점점(?) 중요해지고 있다. 이는 최근 업데이트 및 차후 업그레이드 될 플랫폼의 로드맵을 보면 SXA는 꾸준히 업그레이되고 있으며, Sitecore 9.3부터는 CLI를 통하여 새로운 사이트 생성 및 관리를 할 수 있도록 아주 진보(?)적이다.

그래서 필자는 이번 포스트에서 Habitat을 기준으로 SXA 사이트 생성 및 컨텐츠, Theme 그리고 템플릿 Structure에 대하여 설명하고자 한다.

Tenant:

SXA는 Tenant 아이템을 생성하고 그 하위 아이템으로 Site 및 Tenant Folder를 생성할 수 있다. Tenant 아이템 아래에 추가적으로 Tenant Folder를 생성하면 유저는 세부적으로 웹사이트의 카테고리를 구분하여 Multiple 웹사이트를 관리할 수 있다. 아래는 Multiple Tenant를 생성하였고, 각각 Tenant에 Global 사이트를 생성하고 해당 Tenant안의 많은 웹사이트를 Standardize 할 수 있도록 계획을 세웠다. 글로벌 사이트를 생성하는 것은 Option으로써, 만약 Tenant안에 소수의 웹사이트가 존재한다면, 굳이 글로버 사이트를 생성할 필요은 없으며, 사이트들 중 하나의 웹사이트가 표준이 될 수도 있다. 이는 개발자 및 설계자의 재량으로써 비지니스의 컨셉 및 플랜에 따라 언제든지 바꿔질 수 있다.




Template:

Template 스트럭쳐는 쉽게 생각할 수 있지만 많은 수의 및 다양한 옵션을 요구하는 웹사이트에서는 아주 중요하다고 할 수 있다. 기본적으로 SXA Template은 Tenant 아래 하나의 사이트를 생성함과 동시에 만들어 지는데 템플릿은 "template/{tenant name}"에 존재하며, 해당 Tenant에서 사이트를 생성할 때 마다 모든 사이트는 Tenant 템플릿을 사용되도록 세팅이 되어있다. SXA의 장점중의 하나로 웹사이트는 Page Design 및 Partial Design으로 쉽게 디자인을 변경하고 추가할 수 있도록 잘 짜여져 있지만, 어떤 특정 사이트를 개발하고 그 사이트가 템플릿에서 새로운 필드를 필요로 한다면 필드는 Tenant 템플릿에 추가되어야 한다.

새로운 필드는 컴포넌트와 함께 연동 될 수 있으며, 만약 많은 웹사이트가 Tenant 템플릿을 사용 중이라면 분명히 통합하는 과정에서 해당 필드 및 템플릿 업데이트가 다른 사이트에는 문제가 되지 않는지 짚고 넘어가야 한다. 이런 부분을 해소하기 위하여 아래처럼 Tenant 폴더에 Global 템플릿을 기본 템플릿으로 설정하고 특정한 사이트만의 템플릿을 만들어 Global 템플릿에 상속되도록 설정할 수가 있다.

모든 페이지 및 웹사이트가 하나의 템플릿에서 Page Design과 Partial Design에 의존을 하는지 아니면, 사이트만의 템플릿과 함께 Page Design 및 Partial Design이 연동되는지는 개발자 및 설개자의 재량이다. 만약 Sitecore 사용자가 사이트 특정의 Workflow를 사용할 계획이 있다면 사이트 특정 템플릿을 만드는것이 하나의 장점일뿐더러 차후 웹사이트만의 퍼브리슁을 관리하고 매니지하는 부분에 있어서는 좋은 옵션이라고도 볼 수 있다.




Theme:

SXA Theme은 SASS를 통하여 CSS를 생성하고 사이트코어에 업로드 된 CSS는 플랫폼 차체 프로세서를 통하여 Optimize/minify 된 CSS 파일을 생성한다. 사이트 Theme에는 사이트를 생성할 때 선택되어진 모듈/컨포넌트의 SASS/CSS를 포함하고 있는데 이 모든 CSS는 통합되어지고 SXA 사이트에서는 "optimized-min.css"만 불러들임으로써 스타일 및 다지인을 렌더링 할 수 있다. SXA 사이트를 생성할때 사이트만의 Theme를 새로 생성할 수 있으나 이미 똑같은 디자인 및 theme이 존재하다면 굳이 새로운 사이트 Theme를 생성할 필요는 없다. 아래처럼, 필요자는 몇 개의 Theme을 정하고 Theme이 재사용 되어지도록 리스트 하였다. Theme의 이름은 사이트 이름 및 Tenant 이름과 반드시 매치될 필요는 없다.




Media:

SXA 사이트 컨텐츠 트리는 웹 페이지 아이템뿐만 아니라 사이트 세팅, 컴포넌트 로컬 데이타소스의 Data폴더 및 디자인을 위한 프리젠테이션 등 하나의 패키지형식으로 사이트 아이템이 구성되어져있다. 여기서 추가적으로 Media 폴더가 있는데 폴더 하위에는 사이트 이름의 폴더와 "Shared" 라는 폴더가 기본으로 존재한다. 만약, 이미지 및 Digital Assets을 Tenant안의 다른 모든 사이트와 공유를 하고 싶다면 "shared" 폴더에 이미지 및 디지털 파일을 저장하고 그 Digital Assets이 오직 해당 사이트에서만 사용되어진다면 사이트 폴더에 저장하면 된다. 해당 Media 폴더에 업로드된 아이템은 사이트코어의 Media Library에 동등한 사이트 경로 "/sitecore/media library/Project/{tenant 이름}/{사이트 이름}/{파일 이름}" 에 저장이 되며, 사이트 내의 이미지들은 자동으로 매핑이 된다.




이번 포스트에서는 SXA의 세부(?) Structure에 대하여 알아봤으므로 다음에는 어떻게 Page Design 및 Partial Design을 페이지에 적용시키는지 알아보도록 하자.





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 되는것을 볼수가 있다.