This Blog is not active anymore, further posts will be available on my company website. Read new posts here
Yesterday I gave a demo about events in Umbraco V4. I did this by describing a few "Business problems" that could be solved using events. A few facts about events in Umbraco V4:
- You find events on every Component in Umbraco.
- To uses events, you need references to the businesslogic, cms and interfaces assemlies.
- To use events, you have to create a class that uses umbraco.BusinessLogic.ApplicationBase as the base class. In the constructor you can wire up the event handler. These classes will be automaticly picked up when you put the file in the App_Code folder of Umbraco or put the compiled DLL in the bin folder of Umbraco. I prefer the last one.
- Most of the event args derive from CancelEventArgs, so we can cancel the operation.
- Because an event executes in the background you should always document that you're using events and make sure you will write a logmessage when you're event handler gets executed.
For my demo's, I've used an Umbraco V4 installation with the Creative Website Wizard installed.
Demo 1 Auto Expire news
In the first demo.I've showed how the document.BeforePublish can be used to check if a news item is allready expired, if so cancel the publish event, otherwise check if the expire date is set and if not, set the expire date to 14 days from now.
1: using System;
2: using System.Collections.Generic;
3: using System.Text;
4: using umbraco.BusinessLogic;
5: using umbraco.cms.businesslogic.web;
6:
7: namespace AutoExpire
8: {
9: /// <summary>
10: ///
11: /// </summary>
12: public class Expire :ApplicationBase
13: {
14: /// <summary>
15: /// Constructor used to wire up the event
16: /// </summary>
17: public Expire()
18: {
19: Document.BeforePublish += new Document.PublishEventHandler(Document_BeforePublish);
20: }
21:
22: /// <summary>
23: /// Check if the news item is expired, or check if we need to set the expire date
24: /// </summary>
25: void Document_BeforePublish(Document sender, umbraco.cms.businesslogic.PublishEventArgs e)
26: {
27: //Always LOG
28: Log.Add(LogTypes.Custom, sender.Id, "Event raised");
29: //Check if the doctype is news
30: if (sender.ContentType.Alias == "News")
31: {
32: //Check if the expiredate is filled and the value is not allready expired
33: if (sender.ExpireDate != DateTime.MinValue && sender.ExpireDate < DateTime.Now)
34: {
35: //Item is expired, cancel the publish
36: e.Cancel = true;
37: }
38: else
39: {
40: //Date is not set set it now and save the doc. Probably better to to this in the before save event
41: //since this is a demo it's allowed to do it here
42: sender.ExpireDate = DateTime.Now.AddDays(14);
43: sender.Save();
44:
45: }
46: }
47: }
48: }
49: }
During this demo someone asked if it's possible to show a custom message in the speechbubble event when an event is canceled. This is not possible (tested it this morning), I've created an item on CodePlex.
Demo 2 Auto archive news
In this Demo I've created some functionality to automaticly move a news item to the archive folder when a content manager sets the archive boolean to true. First an archive boolean must be added to the news document and a new Archive folder must be created where we can store the archived News items in.
1: using umbraco.BusinessLogic;
2: using umbraco.cms.businesslogic.web;
3:
4: namespace AutoMoveToArchive
5: {
6: /// <summary>
7: /// Auto move demo
8: /// </summary>
9: public class AutoMoveToArchive : ApplicationBase
10: {
11: /// <summary>
12: /// Wire up the event handler
13: /// </summary>
14: public AutoMoveToArchive()
15: {
16: Document.BeforeSave += new Document.SaveEventHandler(Document_BeforeSave);
17: }
18:
19: /// <summary>
20: /// Before a documents get saved we wil check if the archived proerty is.
21: /// When it's set move it to the archive folder.
22: /// Again it;s demo code, normally you should also check if it's not allready IN the archived foder 
23: /// </summary>
24: void Document_BeforeSave(Document sender, umbraco.cms.businesslogic.SaveEventArgs e)
25: {
26: // Set Arrchive folder id, no excuse to use hard coded values in Umbraco 
27: int archiveId = 1123;
28: //Log that we are doing something
29: Log.Add(LogTypes.Custom, sender.Id, "Document After save Raised");
30:
31: //Check if the item is news and must be archived
32: if (sender.ContentType.Alias == "News" && sender.getProperty("archived").Value.Equals(1))
33: {
34: //Yes, move to archive
35: sender.Move(archiveId);
36:
37: //Unpublish from current Node
38: umbraco.library.UnPublishSingleNode(sender.Id);
39: sender.UnPublish();
40: //Publish will get called if the user selected Save and publish
41: }
42: }
43: }
44: }
Demo 3 Add Unpublish menu Item to the Context menu
In this demo I've showed that we can use events to modify the context menu. I'm missing a unpublish item in the context menu. This example is a little bit harder because we we need to create a menu item also. A menu item can be created to create a class that derives from the IAction interface. I will leave the explanation of this interface for a future blogpost.
1: using System;
2: using umbraco.BusinessLogic;
3: using umbraco.cms.presentation.Trees;
4: using umbraco.interfaces;
5:
6: namespace UnpublishAction
7: {
8: /// <summary>
9: /// Add unpublish to the menu item
10: /// </summary>
11: public class AddUnpublishActionEvent :ApplicationBase
12: {
13: public AddUnpublishActionEvent()
14: {
15: BaseContentTree.BeforeNodeRender += new BaseTree.BeforeNodeRenderEventHandler(BaseTree_BeforeNodeRender);
16: }
17:
18: /// <summary>
19: /// Before a menu item gets rendered we will add the unpublish action if the document is published
20: /// </summary>
21: private void BaseTree_BeforeNodeRender(ref XmlTree sender, ref XmlTreeNode node, EventArgs e)
22: {
23: ///Only unpublish when published
24: if (node.Menu!= null && !node.NotPublished.GetValueOrDefault(true))
25: {
26: //Find the publish action and add 1 for the index, so the position of the ubpublish is direct after the publish menu item
27: int index = node.Menu.FindIndex(delegate(IAction a) { return a.Alias == "publish"; })+1;
28:
29: //Insert unpublish action
30: node.Menu.Insert(index, UnpublishAction.Instance);
31: }
32: }
33: }
34: }
Download the complete solution for this demo here.
Demo 4 Invisible for Writer Usertype
In the last demo I've showed how to use the AfterNodeRender event to make protected nodes invisble for the Writer UserType
1: using System;
2: using umbraco.BusinessLogic;
3: using umbraco.cms.presentation.Trees;
4:
5: namespace OnlyForAdmins
6: {
7: /// <summary>
8: /// Makes a document invisible for writers
9: /// </summary>
10: public class MenuIsNotForWriters : ApplicationBase
11: {
12: /// <summary>
13: /// Wire up the event handler
14: /// </summary>
15: public MenuIsNotForWriters()
16: {
17: BaseContentTree.AfterNodeRender += new BaseTree.AfterNodeRenderEventHandler(BaseContentTree_AfterNodeRender);
18: }
19:
20: /// <summary>
21: /// Removes node from menu tree if page is protected and the user is a writer
22: /// </summary>
23: void BaseContentTree_AfterNodeRender(ref XmlTree sender, ref XmlTreeNode node, EventArgs e)
24: {
25: //check if page is protecetd and the usertype is writer
26: if (node.IsProtected.GetValueOrDefault(false) && umbraco.helper.GetCurrentUmbracoUser().UserType.Alias == "writer")
27: {
28: //Writers cannot see protected pages
29: sender.Remove(node);
30: }
31: }
32: }
33: }
Overview of all Events
Class |
Events |
Access |
|
|
BeforeSave AfterSave BeforeAddProtection AfterAddProtection BeforeRemoveProtection AfterRemoveProtection BeforeAddMemberShipRoleToDocument AfterAddMemberShipRoleToDocument BeforeRemoveMemberShipRoleToDocument AfterRemoveMemberShipRoleToDocument BeforeRemoveMembershipUserFromDocument AfterRemoveMembershipUserFromDocument BeforeAddMembershipUserToDocument AfterAddMembershipUserToDocument |
BaseTree |
|
|
BeforeNodeRender AfterNodeRender |
CMSNode |
|
|
BeforeSave AfterSave AfterNew BeforeDelete AfterDelete BeforeMove AfterMove |
content |
|
|
BeforeUpdateDocumentCache AfterUpdateDocumentCache BeforeClearDocumentCache AfterClearDocumentCache BeforeRefreshContent AfterRefreshContent |
CreatedPackage |
|
|
BeforeSave AfterSave New BeforeDelete AfterDelete BeforePublish AfterPublish |
DataType |
|
|
Saving New Deleting |
Dictionary |
|
|
Saving New Deleting |
Document |
|
|
BeforeSave AfterSave New BeforeDelete AfterDelete BeforePublish AfterPublish BeforeSendToPublish AfterSendToPublish BeforeUnPublish AfterUnPublish BeforeCopy AfterCopy BeforeRollBack AfterRollBack BeforeAddToIndex AfterAddToIndex |
DocumentType |
|
|
BeforeSave AfterSave New BeforeDelete AfterDelete |
Domain |
|
|
BeforeSave AfterSave New BeforeDelete AfterDelete |
InstalledPackage |
|
|
BeforeSave AfterSave New BeforeDelete AfterDelete |
Language |
|
|
BeforeSave AfterSave New BeforeDelete AfterDelete |
Macro |
|
|
BeforeSave AfterSave New BeforeDelete AfterDelete |
Media |
|
|
BeforeSave AfterSave New BeforeDelete AfterDelete |
MediaType |
|
|
BeforeSave AfterSave New BeforeDelete AfterDelete |
Member |
|
|
BeforeSave AfterSave New BeforeAddGroup AfterAddGroup BeforeRemoveGroup AfterRemoveGroup BeforeAddToCache AfterAddToCache BeforeDelete AfterDelete |
MemberGroup |
|
|
BeforeSave AfterSave New BeforeDelete AfterDelete |
MemberType |
|
|
BeforeSave AfterSave New BeforeDelete AfterDelete |
StyleSheet |
|
|
BeforeSave AfterSave New BeforeDelete AfterDelete |
StylesheetProperty |
|
|
BeforeSave AfterSave New BeforeDelete AfterDelete |
Template |
|
|
BeforeSave AfterSave New BeforeDelete AfterDelete |
User |
|
|
Saving New Disabling Deleting FlushingFromCache BeforeSave AfterSave New BeforeDelete AfterDelete BeforePublish AfterPublish |