Пробуем Orchard CMS для корпоративного сайта

от автора

Привет!

У нас есть корпоративный сайт на Liferay, и он требует довольно кропотливой работы на этапе создания layout. Мы решили посмотреть, чем отличается создание кастомного раздела в Liferay от того же процесса в Orchard CMS в рамках такой небольшой задачки как создание корпоративных новостей.

Что у нас из этого вышло:

Настройка Orchard:

Реализация раздела «Новости» состоит из следующих частей:

  1. 1. Тип содержимого.
  2. 2. Настройки типа содержимого.
  3. 3. Само содержимое.
  4. 4. Запрос.
  5. 5. Проекция – то, как всё будет выглядеть на сайте. Кастомизация внешних элементов.
Создание типа «News» и запроса (для показа на главной странице):

1. Создаём ContentType:


2. Добавляем в Новости картинку:

3. Создаём запрос
3.1.

3.2. Теперь надо настроить фильтры для запроса:

3.3. Определим порядок сортировки:

3.4. Т.к. результат запроса надо где-то показывать, создаём Projection:

Теперь на главной странице будет проекция. В dashboard можно создать несколько новостей.

Кастомизация внешнего вида

Создаём тему

Необходимо скачать и включить модуль Code generation: http://docs.orchardproject.net/Documentation/Command-line-scaffolding

Далее открываем папку с установленным Orchard -> bin -> orchard.exe, ждём, пока она инициализируется, пишем: codegen theme <theme-name> /BasedOn:TheThemeMachine /CreateProject:true

Открываем проект с темой:

И не можем создать Razor template… Для того, чтобы мы могли создавать .cshtml файлы из интерфейса студии, необходимо выгрузить проект, открыть файл проекта каким-либо текстовым редактором и изменить ProjectTypeGuids, добавив в него {E53F8FEA-EAE0-44A6-8774-FFD645390401}. У нас результат получился таким: {E53F8FEA-EAE0-44A6-8774-FFD645390401};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}. Теперь можно сохранить файл проекта и загрузить его в студии.

Все файлы темы, не найденные в текущей, берутся из базовой, если она определена, или из темы «SafeMode». Нам необходимо изменить фавиконку и подключить скрипты, фавиконка подключается только в темплейте «Document.cshtml», скрипты, связанные с темой, принято подключать здесь же.

Теперь мы можем создать Document.cshtml в папке Views.
Внутри этого файла мы добавляем ссылки на скрипты, регистрируем фавиконку, а остальной код взят из аналогичного файла в теме SafeMode
Views\Document.cshtml:

@using Orchard.Mvc.Html; @using Orchard.UI.Resources; @{     RegisterLink(new LinkEntry { Type = "image/x-icon", Rel = "shortcut icon", Href = Url.Content("~/Themes/ETRTheme/Content/images/favicon-201209261628.ico") });     Script.Include("html5.js").UseCondition("lt IE 9").AtHead();     Script.Include("jquery.min.js").AtHead();     Script.Include("jquery-ui.min.js").AtHead();     Script.Include("knockout-2.2.1.js").AtHead();     Script.Include("jquery.lightbox.js").AtHead();     Script.Include("jquery.cycle.all.js").AtHead();     Script.Include("date.format.js").AtHead();     Script.Include("script.js").AtHead();       string title = Convert.ToString(Model.Title);     string siteName = Convert.ToString(WorkContext.CurrentSite.SiteName); } <!DOCTYPE html> <html lang="@WorkContext.CurrentCulture" class="static @Html.ClassForPage()">     <head>         <meta charset="utf-8" />         <title>@Html.Title(title, siteName)</title>         @Display(Model.Head)         <script>        (function (d) { d.className = "dyn" + d.className.substring(6, d.className.length); })(document.documentElement);</script>     </head>     <body>         @Display(Model.Body)         @Display(Model.Tail)     </body> </html> 

Этот файл отвечает за отрисовку страницы, в строке «@Display(Model.Body)» он вызывает Layout.cshtml.

Layout содержит в себе разметку страницы, подключает css и «показывает» разные зоны.

Изменение шейпов для кастомизации UI

Для того чтобы кастомизировать внешний вид элементов, необходимо создать несколько темплейтов с определёнными названиями (по одному на каждую кастомизацию, Orchard сам подберёт, каким темплейтом какой элемент ему рэндерить).

http://docs.orchardproject.net/Documentation/Accessing-and-rendering-shapes#NamingShapesandTemplates

В нашем случае будут, например, такие темплейты:

  • Content-News.Summary.cshtml – отвечает за отображение сокращённого варианта новости.
  • SliderGrid-ProjectionPage.cshtml – отвечает за отображение projection.

Темплейт для отображения сокращённой новости:
Views\Content-News.Summary.cshtml:

@using Orchard.Utility.Extensions; @using Orchard.Fields.Fields; @using Orchard.Core.Common.Models; @using Orchard.ContentManagement; @using System.Text.RegularExpressions; @using Orchard.Tags.Models; @using System.Text; @using Orchard.MediaLibrary.Fields; @using Orchard.ContentManagement  @{     ContentItem contentItem = Model.ContentItem;          var contentTypeClassName = ((string)contentItem.ContentType).HtmlClassify();     var news = ((dynamic)contentItem).News;          IEnumerable<ContentField> fields = news.Fields;      var common = contentItem.As<CommonPart>();     var publishedDate = "";     if (common.PublishedUtc.HasValue)     {         var date = common.PublishedUtc.Value;         publishedDate = date.ToString("d MMMM yyyy");     }      MediaLibraryPickerField newsMainImage = null;     bool newsHasImage = false;     string imageUrl = null;     string imageAlternateText = null;     try     {         newsMainImage = (MediaLibraryPickerField)fields.First(f => f.Name == "NewsMainImage");         imageUrl = newsMainImage.MediaParts.First().MediaUrl;         imageAlternateText = newsMainImage.MediaParts.First().AlternateText;         newsHasImage = true;     }     catch (InvalidOperationException e)     {         newsHasImage = false;     }      var body = contentItem.As<BodyPart>();     var summaryBodyText = body.Text.RemoveTags().Trim().Replace(Environment.NewLine, " ");     summaryBodyText = Regex.Replace(summaryBodyText, @"\s+", " ");     var textLength = newsHasImage ? 400 : 500;     textLength = textLength > summaryBodyText.Length ? summaryBodyText.Length : textLength;     summaryBodyText = summaryBodyText.Substring(0, textLength);      var tags = contentItem.As<TagsPart>().CurrentTags;     var tagsHtml = new List<IHtmlString>();     foreach (var t in tags)     {         if (tagsHtml.Any())         {             tagsHtml.Add(new HtmlString(", "));         }         tagsHtml.Add(Html.ActionLink((string)t.TagName, "Search", "Home", new { area = "Orchard.Tags", tagName = (string)t.TagName }, new { }));     }      <div          @if (newsHasImage)         {             <text>class="b-newsroll-item-inner"</text>         }         else         {             <text>class="b-newsroll-item-inner-no-image"</text>         }>         @if (common.PublishedUtc.HasValue)         {             <div class="b-newsroll-date">                 @publishedDate             </div>         }         @if (newsHasImage)         {             <div class="b-newsroll-image-wrapper">                 <img class="b-newsroll-image" src="@Url.Content(@imageUrl)"                     @if (!String.IsNullOrWhiteSpace(imageAlternateText))                     {                         <text>alt="@imageAlternateText"</text>                     }/>             </div>         }         <div class="b-newsroll-content">             <div class="b-newsroll-text">                 <p>@Html.Raw(summaryBodyText)</p>             </div>             <span class="b-newsroll-content-crop"></span>         </div>         <div class="b-newsroll-more">             @Html.ItemDisplayLink(T("Read more").ToString(), contentItem)         </div>           @if (tagsHtml.Any())         {             <div class="b-newsroll-tags">                 <span>                     @T("Tags:")                 </span>                 @foreach (var htmlString in tagsHtml)                 {                     @htmlString                 }                 </div>         }     </div> } 
Создание грида для расположения новостей

Теперь надо сделать нормальный грид для новостей, для этого создаём модуль «SliderGrid»: codegen module SliderGrid и описываем в нём шейпы:

Layouts\SliderGridLayoutForms.cs:

using System; using Orchard.DisplayManagement; using Orchard.Forms.Services; using Orchard.Localization;  namespace SliderGrid.Layouts { 	public class SliderGridLayoutForms : IFormProvider 	{ 		protected dynamic Shape { get; set; } 		public Localizer T { get; set; }  		public SliderGridLayoutForms( 			IShapeFactory shapeFactory) 		{ 			Shape = shapeFactory; 			T = NullLocalizer.Instance; 		}  		public void Describe(DescribeContext context) 		{ 			Func<IShapeFactory, object> form = 				shape => 				{ 					var f = Shape.Form( 						Id: "SliderGridLayout", 						_HtmlProperties: Shape.Fieldset() 						); 					return f; 				}; 			context.Form("SliderGridLayout", form); 		} 	} }  Layouts\SliderGridLayout.cs: using System.Collections.Generic; using System.Linq; using Orchard.ContentManagement; using Orchard.DisplayManagement; using Orchard.Localization; using Orchard.Projections.Descriptors.Layout; using Orchard.Projections.Models; using Orchard.Projections.Services;  namespace SliderGrid.Layouts { 	public class SliderGridLayout : ILayoutProvider 	{ 		public Localizer T { get; set; } 		private readonly IContentManager _contentManager; 		protected dynamic Shape { get; set; } 		public SliderGridLayout(IShapeFactory shapeFactory, IContentManager contentManager) 		{ 			_contentManager = contentManager; 			Shape = shapeFactory; 			T = NullLocalizer.Instance; 		}  		public void Describe(DescribeLayoutContext describe) 		{ 			describe.For("Html", T("Html"), T("Html Layouts")) 				.Element("SliderGrid", T("SliderGrid"), T("Renders multiPageGrid of elements + slider."), 					DisplayLayout, 					RenderLayout, 					"SliderGridLayout" 				); 		}  		public LocalizedString DisplayLayout(LayoutContext context) 		{ 			return T("Renders multiPageGrid + slider."); 		}  		public dynamic RenderLayout(LayoutContext context, IEnumerable<LayoutComponentResult> layoutComponentResults) 		{ 			IEnumerable<dynamic> shapes = 			   context.LayoutRecord.Display == (int)LayoutRecord.Displays.Content 				   ? layoutComponentResults.Select(x => _contentManager.BuildDisplay(x.ContentItem, context.LayoutRecord.DisplayType)) 				   : layoutComponentResults.Select(x => x.Properties);  			return Shape.SliderGrid(Items: shapes); 		} 	} } 
Включаем грид

Подключаем в «модулях» фичу SliderGrid, изменяем layout запроса LastNews на SliderGrid:

Изменяем в проекции запрос на только что исправленный.

Выводы

Для того, чтобы можно было через админку писать новости, нам надо реализовать ContentType, в нашем случае — News. Чтобы показать список новостей на главной странице, нам нужен Projection, а для него — Query. Чтобы мы могли кастомизировать внешний вид элементов, нам надо в текущей теме в папку Views положить шаблоны с определёнными именами, по которым Orchard сможет понять, к какому шейпу этот шаблон применять. Как доставать ContentPart и ContentTypeField из ContentType понятно из примера с короткими новостями.

На основании проведенного эксперимента мы пришли к выводу, что Orchard и Liferay на этапе подготовки инструмента для публикации новостей… примерно одинаковые. И там, и там есть кастомизация тем, изготовление нужных сущностей, возможность кастомизации этих сущностей, но имеется нюанс: на этапе, когда надо сделать блочный вывод (например, список новостей или статей) начинаются проблемы с Liferay – проблема падает на программиста, которому вытащить сущности самостоятельно, чтобы их отображать. Здесь требуется довольно высокая квалификация в java и в разработке под Liferay.

В Orchard же есть удобный механизм запроса — Query, — справиться с которым под силу всем.

ссылка на оригинал статьи http://habrahabr.ru/company/eastbanctech/blog/189314/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *