[ELMA3] Реализация возможности закрепления объекта на странице
В статье приведен пример реализации возможности закрепления объекта на странице. Существует множество вариантов использования данного функционала. В данной статье приведено два примера возможного использования:
- Открытие конкретного экземпляра объекта IPaperPinExampleObject при переходе в модуль. IPaperPinExampleObject – простой объект-справочник со стандартным набором полей: Наименование, Автор, Дата создания, Дата Изменения, Автор изменения.
- Запись прикрепленных объектов (в примере используются документы) в портлет.
Пример отображения данных
Рис. 1. Отображение «булавки» на форме объекта
Рис. 2. Портлет, отображающий прикрепленные документы
Методы расширения (интерфейса)
Точка расширения (интерфейс) IPaperPinProvider имеет следующие методы:
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 | /// <summary> /// Доступна ли булавка для данного объекта /// </summary> /// <param name="typeUid">GUID типа объекта</param> /// <param name="entityId">Идентификатор объекта</param> /// <returns><c>true</c>, если для указанного типа объекта будет отображаться булавка</returns> bool IsAvailable(Guid typeUid, object entityId = null ); /// <summary> /// Все поддерживаемые типы. /// </summary> /// <returns>Список поддерживаемых типов</returns> List<Guid> AvailableTypes(); /// <summary> /// Использовать для закрепления только один экземпляр для данного типа. Все остальные экземпляры открепляются. /// </summary> /// <returns><c>true</c>, если использовать для закрепления только один экземпляр данного типа</returns> bool OnlyOneInstance(); /// <summary> /// Описание на кнопке, когда объект не закреплён. /// </summary> string TooltipText(Guid typeUid, object entityId = null ); /// <summary> /// Описание на кнопке, когда объект закреплён. /// </summary> string TooltipTextPin(Guid typeUid, object entityId = null ); /// <summary> /// Текст предупреждения при закреплении объекта /// </summary> /// <param name="typeUid">GUID типа объекта</param> /// <param name="entityId">Идентификатор объекта</param> string ConfirmText(Guid typeUid, object entityId = null ); |
Данная точка расширения реализована при помощи базового класса BasePaperPinProvider
BasePaperPinProvider имеет следующие методы:
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 | /// <summary> /// Доступна ли булавка для данного объекта /// </summary> /// <param name="typeUid">GUID типа объекта</param> /// <param name="entityId">Идентификатор объекта</param> /// <returns><c>true</c>, если для указанного типа объекта будет отображаться булавка</returns> public virtual bool IsAvailable(Guid typeUid, object entityId = null ) { var availableTypes = AvailableTypes(); return availableTypes.Any(uid => uid == typeUid); } /// <summary> /// Все поддерживаемые типы. /// </summary> /// <returns>Список поддерживаемых типов</returns> public abstract List<Guid> AvailableTypes(); /// <summary> /// Использовать для закрепления только один экземпляр для данного типа. Все остальные экземпляры открепляются /// </summary> /// <returns><c>true</c>, если использовать для закрепления только один экземпляр данного типа</returns> public virtual bool OnlyOneInstance() { return false ; } /// <summary> /// Описание на кнопке, когда объект не закреплён. /// </summary> /// <param name="typeUid">GUID типа объекта</param> /// <param name="entityId">Идентификатор объекта</param> public abstract string TooltipText(Guid typeUid, object entityId = null ); /// <summary> /// Описание на кнопке, когда объект закреплён. /// </summary> /// <param name="typeUid">GUID типа объекта</param> /// <param name="entityId">Идентификатор объекта</param> public abstract string TooltipTextPin(Guid typeUid, object entityId = null ); /// <summary> /// Текст предупреждения при закреплении объекта /// </summary> /// <param name="typeUid">GUID типа объекта</param> /// <param name="entityId">Идентификатор объекта</param> public abstract string ConfirmText(Guid typeUid, object entityId = null ); |
Пример 1. Открытие конкретного экземпляра объекта IPaperPinExampleObject
Пример класса точки расширения
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 | [Component] public class ObjectPaperPinExample : BasePaperPinProvider { public IEntityActionHandler EntityActionHandler { get ; set ; } /// <summary> /// Условие при котором "булавка" будет доступна /// </summary> /// <param name="typeUid"></param> /// <param name="entityId"></param> /// <returns></returns> public override bool IsAvailable(Guid typeUid, object entityId = null ) { if (! base .IsAvailable(typeUid, entityId)) return false ; if (entityId == null ) return false ; return typeUid == InterfaceActivator.UID<IPaperPinExampleObject>(); } /// <summary> /// Все поддерживаемые типы. /// </summary> /// <returns></returns> public override List<Guid> AvailableTypes() { return AllObjectTypes(); } public static List<Guid> AllObjectTypes() { return new List<Guid> { InterfaceActivator.UID<IPaperPinExampleObject>() }; } /// <summary> /// Использовать для закрепления только один экземпляр для данного типа. Все остальные экземпляры открепляются /// </summary> /// <returns></returns> public override bool OnlyOneInstance() { return true ; } /// <summary> /// Описание на кнопке, когда объект не закреплён. /// </summary> public override string TooltipText(Guid typeUid, object entityId = null ) { return SR.T( "После нажатия на кнопку, данный объект будет открываться сразу при выборе пункта меню \"Мои объекты\"" ); } /// <summary> /// Описание на кнопке, когда объект закреплён. /// </summary> public override string TooltipTextPin(Guid typeUid, object entityId = null ) { return SR.T( "После нажатия на кнопку, при выборе пункта меню \"Мои объекты\", будет открываться главная страница модуля \"Мои объекты\"" ); } /// <summary> /// Текст предупреждения при закреплении объекта /// </summary> /// <param name="typeUid"></param> /// <param name="entityId"></param> public override string ConfirmText(Guid typeUid, object entityId = null ) { return SR.T( "Объекты будет открываться сразу при выборе пункта меню \"Мои объекты\"" ); } } |
В данном примере был создан пункт меню, который переводит пользователя на список объектов:
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 | protected IUser CurrentUser { get { return AuthenticationService.GetCurrentUser<IUser>(); } } [ContentItem] public ActionResult Grid() { // если есть закреплённый объект, то открываем сразу его var paperPinManager = PaperPinManager.Instance; var allObjectTypes = ObjectPaperPinExample.AllObjectTypes(); var pin = paperPinManager.FirstPaperPinByUser(CurrentUser, allObjectTypes); if (pin != null && ! string .IsNullOrEmpty(pin.EntityId)) { long entityId; if ( long .TryParse(pin.EntityId, out entityId)) return RedirectToAction( "ViewItem" , new { id = entityId }); } var filter = CreateFilter(); var data = CreateGridData( new GridCommand(), filter); return View(data); } public ActionResult ViewItem( long id) { var model = PaperPinExampleObjectManager.Instance.Load(id); return View(model); } |
Как можно заметить, при выборе пункта меню, если будет найден первый объект, который прикреплен, то будет совершен переход на запись этого объекта.
О том, как создать свой пункт меню, можно подробнее прочитать в данной статье.
1 2 3 4 | @{ var title = SR.T( "Объект - {0}" , Model.Name); Html.Header(title, Model); } |
Благодаря данному коду на странице появится булавка.
Пример 2. Запись прикрепленных объектов (в примере используются документы) в портлет
Пример класса точки расширения
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 | private static List<Guid> _documentAvailableTypes; public static List<Guid> DocumentAvailableTypes { get { if (_documentAvailableTypes == null ) { _documentAvailableTypes = Locator.GetServiceNotNull<IMetadataRuntimeService>() .GetMetadataList() .OfType<DocumentMetadata>() .Select(documentMetadata => documentMetadata.Uid).ToList(); } return _documentAvailableTypes; } } [Component] public class DocumentPaperPinExample : BasePaperPinProvider { public IEntityActionHandler EntityActionHandler { get ; set ; } /// <summary> /// Условие при котором "булавка" будет доступна /// </summary> /// <param name="typeUid"></param> /// <param name="entityId"></param> /// <returns></returns> public override bool IsAvailable(Guid typeUid, object entityId = null ) { if (! base .IsAvailable(typeUid, entityId)) return false ; if (entityId == null ) return false ; return DocumentAvailableTypes.FirstOrDefault(a => a == typeUid) != null ; } /// <summary> /// Все поддерживаемые типы. /// </summary> /// <returns></returns> public override List<Guid> AvailableTypes() { return DocumentAvailableTypes; } /// <summary> /// Описание на кнопке, когда объект не закреплён. /// </summary> public override string TooltipText(Guid typeUid, object entityId = null ) { return SR.T( "После нажатия на кнопку, документ добавится в портлет \"Список прикрепленных документов\"" ); } /// <summary> /// Описание на кнопке, когда объект закреплён. /// </summary> public override string TooltipTextPin(Guid typeUid, object entityId = null ) { return SR.T( "Документ отображается в портлете \"Список прикрепленных документов\"" ); } /// <summary> /// Текст предупреждения при закреплении объекта /// </summary> /// <param name="typeUid"></param> /// <param name="entityId"></param> public override string ConfirmText(Guid typeUid, object entityId = null ) { return SR.T( "Документ будет добавлен в портлет \"Список прикрепленных документов\"" ); } } |
Создадим портлет в модуле. Подробнее о том, как создавать портлеты в модуле, Вы можете прочитать здесь.
Представление портлета имеет следующий код:
1 2 3 4 | @model PaperPinExampleModule.Web.Portlets.PaperPinDocumentsPersonalization @ using PaperPinExampleModule.Web @Html.Action( "PortletView" , "Home" , new {area = RouteProvider.AreaName}) |
В представлении вызывается метод PortletView контроллера HomeController
Код метода контроллера:
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 | protected IUser CurrentUser { get { return AuthenticationService.GetCurrentUser<IUser>(); } } public ActionResult PortletView() { var documentManager = DocumentManager.Instance; //Получаем все идентификаторы документов, у которых прицеплена булавка для текущего пользователя var paperPinManager = PaperPinManager.Instance; var allObjectTypes = DocumentPaperPinExample.DocumentAvailableTypes; var pins = paperPinManager.GetAllPaperPinsByUser(CurrentUser, allObjectTypes); List< long > documentIdsArray = new List< long >(); foreach (var pin in pins) { long documentId; if ( long .TryParse(pin.EntityId, out documentId)) documentIdsArray.Add(documentId); } var docFilter = InterfaceActivator.Create<IDocumentFilter>(); docFilter.Ids = documentIdsArray; var fetchOptions = new FetchOptions { SelectColumns = new List< string >{ "Id" , "Name" } }; var documents = documentManager.Find(docFilter, fetchOptions).Select(doc => new DocumentPortletModel() { Name = doc.Name, Id = doc.Id }).ToList(); return PartialView( "PortletView" , documents); } |
Где DocumentPortletModel - это класс, представляющий простую модель со свойствами Name, Id:
1 2 3 4 5 6 7 8 9 10 11 12 | public class DocumentPortletModel { /// <summary> /// Наименование документа /// </summary> public string Name { get ; set ; } /// <summary> /// Идентификатор документа /// </summary> public long Id { get ; set ; } } |
Как можно заметить, в список документов добавляются только те документы, которые прикреплены текущим пользователем, соответственно, в портлете появятся прикрепленные документы только для текущего пользователя. Далее контроллер возвращает частичное представление PortletView. Код частичного представления:
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 | @model System.Collections.Generic.List<PaperPinExampleModule.Web.Models.DocumentPortletModel> <style> .tableTd { padding: 3px 5px !important; border-bottom: 1px solid #e6e6e6 !important; } </style> <table class = "t-grid-table" style= "width: 100%" > <thead class = "t-grid-header" > <tr> <th scope= "col" class = "t-header " ><span class = "t-header-content" >@SR.T( "Название документа" )</span></th> </tr> </thead> @ if (Model.Count > 0) { foreach (var doc in (dynamic)Model) { if (doc != null ) { <tr> <td class = "tableTd" > <a href= "@Url.Action(" View ", " Document ", new {area = " EleWise.ELMA.Documents.Web ", id = doc.Id})" >@doc.Name</a> </td> </tr> } } } </table> |