Implementing the possibility to pin an object to a page
This article provides an example of how to implement the possibility to pin an object to a page. There are many ways to use this capability. This article describes two use cases:
- Opening a specific instance of the IPaperPinExampleObject object when going to a module. IPaperPinExampleObject – is a simple object with standard fields: Name, Author, Date Created, Date Modified, Modified By.
- Displaying pinned objects (documents, in this example) to a portlet.
Example
Fig. 1. Pin icon on the object form
Fig. 2. Portlet displaying pinned document
Extension (interface) methods
IPaperPinProvider extension point (interface) has the following methods:
/// <summary>
/// Availability of the pin for this object
/// </summary>
/// <param name="typeUid">Object type GUID</param>
/// <param name="entityId">Object ID</param>
/// <returns><c>true</c>, if pin will be displayed for this object type</returns>
bool IsAvailable(Guid typeUid, object entityId = null);
/// <summary>
/// All supported types.
/// </summary>
/// <returns>List of supported types</returns>
List<Guid> AvailableTypes();
/// <summary>
/// Use to pin only one instance of this type. All the other instances are unpinned.
/// </summary>
/// <returns><c>true</c>, if it is used to pin only one instance of this type</returns>
bool OnlyOneInstance();
/// <summary>
/// Description on the button, when the object is not pinned.
/// </summary>
string TooltipText(Guid typeUid, object entityId = null);
/// <summary>
/// Description on the button, when the object is pinned.
/// </summary>
string TooltipTextPin(Guid typeUid, object entityId = null);
/// <summary>
/// Warning text displayed when pinning the object
</summary>
/// <param name="typeUid">Object type GUID</param>
/// <param name="entityId">Object ID</param>
string ConfirmText(Guid typeUid, object entityId = null);
This extension point is implemented using the BasePaperPinProvider base class
BasePaperPinProvider has the following methods:
/// <summary>
/// Availability of the pin for this object
/// </summary>
/// <param name="typeUid"></param>
/// <param name="entityId">Object ID</param>
/// <returns><c>true</c>, if pin will be displayed for the specified object type</returns>
public virtual bool IsAvailable(Guid typeUid, object entityId = null)
{
var availableTypes = AvailableTypes();
return availableTypes.Any(uid => uid == typeUid);
}
/// <summary>
/// All the supported types.
/// </summary>
/// <returns>List of supported types</returns>
public abstract List<Guid> AvailableTypes();
/// <summary>
/// Use to pin only one instance of this type. All the other instances are unpinned
/// </summary>
/// <returns><c>true</c>, if it is used to pin only one instance of this type</returns>
public virtual bool OnlyOneInstance()
{
return false;
}
/// <summary>
/// Description on the button, when the object is unpinned.
/// </summary>
/// <param name="typeUid">Object type GUID</param>
/// <param name="entityId">Object ID</param>
public abstract string TooltipText(Guid typeUid, object entityId = null);
/// <summary>
/// Description on the button, when the object is pinned.
/// </summary>
/// <param name="typeUid">Object type GUID</param>
/// <param name="entityId">Object ID</param>
public abstract string TooltipTextPin(Guid typeUid, object entityId = null);
/// <summary>
/// Warning text when pinning the object
/// </summary>
/// <param name="typeUid">Object type GUID</param>
/// <param name="entityId">Object ID</param>
public abstract string ConfirmText(Guid typeUid, object entityId = null);
Example 1. Opening a specific instance of the IPaperPinExampleObject object
Extension point class example
[Component]
public class ObjectPaperPinExample : BasePaperPinProvider
{
public IEntityActionHandler EntityActionHandler { get; set; }
/// <summary>
/// Condition, under which the "pin" is available
/// </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>
/// All the supported types.
/// </summary>
/// <returns></returns>
public override List<Guid> AvailableTypes()
{
return AllObjectTypes();
}
public static List<Guid> AllObjectTypes()
{
return new List<Guid> { InterfaceActivator.UID<IPaperPinExampleObject>() };
}
/// <summary>
/// Use to pin only one instance for this type. All the other instances are unpinned
/// </summary>
/// <returns></returns>
public override bool OnlyOneInstance()
{
return true;
}
/// <summary>
/// Description on the button, when the object is unpinned.
/// </summary>
public override string TooltipText(Guid typeUid, object entityId = null)
{
return SR.T("After clicking on the button, this object will open after selecting the \"My Objects\" menu item");
}
/// <summary>
/// Description on the button, when the object is pinned.
/// </summary>
public override string TooltipTextPin(Guid typeUid, object entityId = null)
{
return SR.T("After clicking on the button, when selecting the \"My Objects\" menu item, the main page of the \"My Objects\" module will open");
}
/// <summary>
/// Warning text when pinning the object
/// </summary>
/// <param name="typeUid"></param>
/// <param name="entityId"></param>
public override string ConfirmText(Guid typeUid, object entityId = null)
{
return SR.T("The object will open after selecting the \"My Objects\" menu item");
}
}
In this example, a menu item is created, which opens a list of objects for the user:
protected IUser CurrentUser
{
get
{
return AuthenticationService.GetCurrentUser<IUser>();
}
}
[ContentItem]
public ActionResult Grid()
{
// if an object is pinned, open it
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);
}
As you can see, when selecting the menu item, if the first pinned object is found, its instance will open.
To learn more about creating a menu item, read this article.
@{
var title = SR.T("Object - {0}", Model.Name);
Html.Header(title, Model);
}
This code will add a pin to the page.
Example 2. Writing pinned objects (documents in the example) to a portlet
Extension point class example
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>
/// Condition, under which the "pin" is available
/// </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>
/// All the supported types.
/// </summary>
/// <returns></returns>
public override List<Guid> AvailableTypes()
{
return DocumentAvailableTypes;
}
/// <summary>
/// Description on the button, when the object is unpinned.
/// </summary>
public override string TooltipText(Guid typeUid, object entityId = null)
{
return SR.T("After clicking on the button, the document will be added to the \"Pinned documents list\" portlet");
}
/// <summary>
/// Description on the button, when the object is pinned.
/// </summary>
public override string TooltipTextPin(Guid typeUid, object entityId = null)
{
return SR.T("The document is displayed in the \"Pinned documents list\" portlet");
}
/// <summary>
/// Warning text when pinning the object
/// </summary>
/// <param name="typeUid"></param>
/// <param name="entityId"></param>
public override string ConfirmText(Guid typeUid, object entityId = null)
{
return SR.T("The document will be added to the \"Pinned documents list\" portlet");
}
}
Create a portlet in the module. To learn more about creating portlets in a module, read this article.
The portlet view has the following code:
@model PaperPinExampleModule.Web.Portlets.PaperPinDocumentsPersonalization
@using PaperPinExampleModule.Web
@Html.Action("PortletView", "Home", new {area = RouteProvider.AreaName})
The PortletView method of the HomeController controller is called in the view
Controller method code:
protected IUser CurrentUser
{
get
{
return AuthenticationService.GetCurrentUser<IUser>();
}
}
public ActionResult PortletView()
{
var documentManager = DocumentManager.Instance;
//Get all the IDs of the documents, pinned for the current user
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);
}
Where DocumentPortletModel - is a class, representing a simple model with Name and Id properties:
public class DocumentPortletModel
{
/// <summary>
/// Document name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Document ID
/// </summary>
public long Id { get; set; }
}
As you can see, only the documents, pinned by the current users, are added to the document list, therefore the portlet will display pinned documents only for the current user. Next, the controller returns a partial view PortletView. The partial view code:
@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>