A social plugin for EPiServer
The background
Since the release of EPiServer 6 i haven´t been engaged in any pure EPiServer projects and therefor haven´t got to use the new and shiny features that comes with version 6. Because of that (and the fact that my EPiServer developer certificate soon will expire again) I started playing around with the Dynamic Data Store recently to get a better grip on it.
The goal
The goal of this, other than me getting to know DDS, was to create a plugin for EPiServer that provided some basic “social” features like comments and ratings. There has been numerous blogposts and demos on how to implement this kind of functionality using DDS, but I thought I would take it a step further and create a ready-to-use plugin.
Besides that, I wanted the use of it to be as simple as possible. No configuration, no setup, no extra files. Just drop an assembly in the bin-folder and get busy coding.
The result
The result of all this became a plugin that provides a simple API for creating comments and ratings for EPiServer pages. It also contains a simple editmode plugin for managing these for each page.
This is to be considered in an alpha stage. There still remains some work before it is ready and additional features will be added along the way. Feel free to try it out.
This post focuses on how to use the EPiSocial plugin rather than how DDS works.
Single assembly deployment
Well, almost. The plugin itself is just a single assembly, but it depends on both AutoMapper and StructureMap.
So the deployment consists of adding the following three assemblies to your bin-folder:
- EPiSocial.dll
- AutoMapper.dll
- StructureMap.dll
Then add a reference to EPiSocial and you´re done.
POCO objects
The comments and ratings are simple POCO objects and looks like this:
PageComment class:
1: public class PageComment
2: {
3: public string Id { get; set; }
4: public string Text { get; set; }
5: public int PageId { get; set; }
6: public string PageLanguage { get; set; }
7: public Author Author { get; set; }
8: public DateTime Created { get; set; }
9: }
PageRating class:
1: public class PageRating
2: {
3: public string Id { get; set; }
4: public int Rating { get; set; }
5: public int PageId { get; set; }
6: public string PageLanguage { get; set; }
7: public Author Author { get; set; }
8: public DateTime Created { get; set; }
9: }
Author class:
1: public class Author
2: {
3: public string Email { get; set; }
4: public string Name { get; set; }
5: }
Usage - The easy way
The EPiSocial plugin provides a EPiSocialService class that is the entrypoint for using the API. It has a singleton instance, EPiSocialService.Instance, to use.
The EPiSocialService itself has two properties, a CommentRepository and a RatingRepository, that provides methods for adding, retrieving and removing comments/ratings.
Add a comment:
1: var pageComment = new PageComment
2: {
3: Text = "Some text",
4: PageId = 3,
5: PageLanguage = "sv",
6: Created = DateTime.Now,
7: Author = new Author { Name = "Some name" }
8: };
9:
10: EPiSocialService.Instance.CommentRepository.AddComment(pageComment);
Get comments for a page:
1: var commentsForPage = EPiSocialService.Instance.CommentRepository.GetCommentsForPage(3);
Languagespecific overload:
1: var commentsForPage = EPiSocialService.Instance.CommentRepository.GetCommentsForPage(3, "sv");
Usage - The recommended way
Even though the above example is really quick and easy to use, you really should not depend directly on the EPiSocialService class. As the good developer you are you should instead have dependencies on the interface IEPiSocialService or any of the repository interfaces ICommentRepository or IRatingRepository.
Editmode plugin
When adding the EPiSocial assembly you also get an editmode plugin added to EPiServer. It is a tab in the editpanel of each page named “EPiSocial”. The plugin contains some statistics for the current page and the ability to reset ratings and remove unwanted comments.
This has been an introduction to EPiSocial. The project is just in it´s first stumbling steps and any feedback is appreciated.
The plugin can be downloaded here for now. Feel free to try it out.
Hi.
Two things:
1) If the PageComment and PageRating classes are stored in the DDS then your Id properties should be of type Guid or Identity. In fact the DDS should be throwing an exception saying this.
See the Identity Management section of http://world.episerver.com/Documentation/Items/Tech-Notes/EPiServer-CMS-6/EPiServer-CMS-60/Dynamic-Data-Store/
2) Entities that are related to CMS pages are much better stored through the PageObjectManager (which uses DDS underneath) as this will give you free import/export and
Mirroring support for your ratings and comments.
See http://world.episerver.com/Documentation/Items/Tech-Notes/EPiServer-CMS-6/EPiServer-CMS-60/Page-Objects/ for more information.
/Paul.
Paul, is there a way to use PageObjects and still be able to easily query the store for a class, say Ratings, to create aggregates over all pages. I mean like calculating the average rating for all pages and getting the top rated pages?
Thanks for the quick feedback.
1. The PageComment/PageRating objects are mapped to DDS comment/rating objects in the background, and then the Identity is created. I didn´t want to have a hard coupling to EPiServer/DDS. With this approach the CommentRepository can be exchanged to any other storage; XML, document database etc. This makes the whole plugin a lot more flexible.
2. I´ll have to take a look at that.
// Niklas
@Magnus
When using the PageObjectManager the actual entity objects are stored in the DDS in the normal way. Johan Olofsson has give an example in the forum entry here of how to get average ratings:
http://world.episerver.com/Templates/Forum/Pages/thread.aspx?id=37962
although I don't really agree with him that you can't use page objects to do this. The trick is to make sure your Rating class has a property to capture the page id. This may seem to defeat the point of using page objects but don't forget the free import/export/mirroring support which you would have to do yourself otherwise.
/Paul.
Thanks for the response Paul. I was clear on how to do the actual aggregation, if I could only get a queryable object containing all the ratings. Does the PageObjectManager use a class-specific store for each object type passed in so I can get PageRatings stored as PageObjects by calling GetStore(typeof(PageRating))? Is it acheived by using a EPiServer.Data.Dynamic.EPiServerDataStoreAttribute to map the PageRating class to a specific store, or do PageObjects work that way OOTB?
@Magnus.
Objects stored via the PageObjectManager work exactly the same as if stored directly via the DDS API's. So if your class is called PageRating then you can get a store like this:
var store = DynamicDataStoreFactory.Instance.GetStore(typeof(PageRating));
This call takes account of any Type to Store mapping either via the EPiServerDataStoreAttribute or via the GlobalTypeToStoreMap API.
/Paul.
Thank you for your input.
I´m not convinced though, seems to me that using PO might get messier and the only real benefit is support for mirroring and import/export. And to do aggregations for multiple pages I still have to use plain DDS, right? Please correct me if I´m wrong.
To add a comment for example, using DDS I can just insert a new object to the store. Using PO I have to do a lot of things:
- Load the PO
- If exists, add to PO
- If not exists, create a new PO
- Save PO
For now, I can actually live with not having mirroring and import/export.
// Niklas
@Niklas
Everything you say is correct but I must take you up on one point. You say that _you_ can live without Mirroring/Import/Export but these features aren't for developers, they're for editors and site owners.
It could be very frustrating for a site owner using your components for page comments, rating etc to see that those items do not follow the pages when exported / mirrored.
/Paul.
You have a point in that, of course.
I´m actually working on a PageObject implementation of the repositories. I´ll try to add a post on that soon.
If you're interested in allowing access to the source and taking contributions I'd gladly help out on this. It would be great to have a commonly used commenting method on episerver projects.
@Seth
I´m thinking of doing this further on, but first I will focus on getting the plugin in a good enough shape. At the moment it is still in a alpha/beta stage.
I will try to keep posting about the progress here, stay tuned.