It's kind of tricky for the underlaying API to know when you actually View a page. As you probably notice "Loaded Content" might also mean that you load the content to list it, or just get property values from it.
Most of the time when this is done, we usually do that in the aspx or controller (depending on WebForms or MVC).
To simplify the implementation, make sure that you have a separate class with the stuff you want to do.
Such as
protected override void OnLoad(EventArgs e) { base.OnLoad(e); ContentView.ContentIsViewed(this.CurrentPage); }
or
public override ActionResult Index(MyPage currentPage) { ContentView.ContentIsViewed(currentPage); }
and in your class something like
public static class ContentView { public static void ContentIsViewed(PageData page) { // ... do something } }
Also look at how base classes of your templates and controllers can help you. Keep in mind that only because all of your pages want to do this, a base class might not be the best solution.
The thing is i want this to be like a plugin, added through nuget and it automaticly begins hooking up as events.
I dont want to have to manually need to change the code or add code to every possible page controller etc.
Hi Mattias,
Can you give us more details? What exactly are you trying to achieve? Can you use global filters?
We want to devolop a plugin where you log things on each page view. The thought is you install this plugin and configure it and it works out of the box. There shouldent be any need to rebuild or change the code for each page controller to get it working.
Global filters ? are this some base MVC thing ?
I was looking for a MVC solution but couldent think of the actionfilters that you could register them globaly. With an global action filter and resolving the url to a content and comparing the actionfilter context controller type agains the controller type of the current page i could managed to filter out other results like blocks and souch that are executed in the same page view.
Thanks for the tip about action filters.
Here is my code if anyone needs something similar.
public class PageViewActionFilterAttribute : ActionFilterAttribute { public override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); var content = UrlResolver.Current.Route(new UrlBuilder(filterContext.RequestContext.HttpContext.Request.Url.PathAndQuery)); if (content != null) { var contentTypeRepository = ServiceLocator.Current.GetInstance<IContentTypeRepository>(); var templateResolver = ServiceLocator.Current.GetInstance<TemplateResolver>(); var type = contentTypeRepository.Load(content.ContentTypeID); var template = templateResolver.ResolveMvcTemplate(filterContext.HttpContext, type); if (filterContext.Controller.GetType().Equals(template.TemplateType)) { Debug.WriteLine($"ACTION Executed: {filterContext.RequestContext.HttpContext.Request.Url.PathAndQuery}"); } } } }
You can also check if the current controller is PageController<T>.
public class PageViewActionFilterAttribute : ActionFilterAttribute { public override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); if (IsPageController(filterContext.Controller.GetType())) { Debug.WriteLine($"ACTION Executed: {filterContext.RequestContext.HttpContext.Request.Url.PathAndQuery}"); } } private bool IsPageController(Type type) { if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(PageController<>)) { return true; } return type.BaseType != null && IsPageController(type.BaseType); } }
Yes, that might have been a quicker aproach, but if there is somehow a link on the current page to another page causing this to be rendered to i might get duplicate page view logs.
But i just want to log the actual page being viewed, so i think my aproach of checking the filter context controller against the content controller is a more precise aproach.
I'm not sure what you mean. Debug.WriteLine will execute only once. Do you have an example where it executes more than once (in Alloy perhaps)?
What i mean is if a page is viewed and that page has linked content like a link to another page and on this page it is rendered as an action like @Html.Action(). Then that could result in a log hit for the original page and the linked page eg. Html.Action() if this page could be resolved as PageController<>.
Ok, I get what you mean now :)
@Html.Action on Index action is not that common, at least not on PageController<>.
In your approach, Debug.WriteLine can also be executed several times as nothing prevents a developer from calling @Html.Action on the same controller (doesn't even have to be index action).
To make sure Debug.WriteLine is called only once (for Index action, and no child actions), the following code can be used:
public class PageViewActionFilterAttribute : ActionFilterAttribute { public override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); if (IsPageController(filterContext.Controller.GetType()) && !filterContext.IsChildAction && filterContext.ActionDescriptor.ActionName.Equals("index", StringComparison.InvariantCultureIgnoreCase)) { Debug.WriteLine($"ACTION Executed: {filterContext.RequestContext.HttpContext.Request.Url.PathAndQuery}"); } } private bool IsPageController(Type type) { if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(PageController<>)) { return true; } return type.BaseType != null && IsPageController(type.BaseType); } }
Hope this helps.
Is there a way to register an event to a page view ?
For an example when an CMS content page is displayed/loaded i wanna trigger an event, is this possible.
I have already tried the DataFactory.Instance.LoadedContent event, but this is triggered to often. The page i view, and all other content that page is somehow using (blocks, images, etc.).
I just want to trigger this event once each time a page is loaded.