Hide last authors
Danilo Oliveira 5.1 1 {{box cssClass="floatinginfobox" title="**Contents**"}}
2 {{toc/}}
3 {{/box}}
Vincent Massol 1.1 4
5 XWiki supports notifications (a.k.a Observation Events) and it's possible to do some action when a document is modified, when a document's objects are modified, when a wiki is created, etc. See the full documentation in the [[Observation reference documentation>>extensions:Extension.Observation Module]].
6
7 There are several ways to write an Event Listener to react to some events:
Danilo Oliveira 5.1 8
Vincent Massol 1.1 9 * In a wiki page, as a [[Wiki Component>>extensions:Extension.WikiComponent Module]]
Vincent Massol 11.5 10 * In a wiki page, using Groovy, by writing a Component and manually registering it against the Component Manager
Vincent Massol 11.6 11 * In Java, as a [[Component>>extensions:Extension.Component Module]]
Vincent Massol 1.1 12
13 This tutorial will demonstrate all techniques on several various examples.
14
15 = Adding content to pages on save =
16
17 The goal is to listen to ##DocumentCreatingEvent## and ##DocumentUpdatingEvent## events (note that these events are fired **before** the page is saved and thus our code doesn't need to perform the save itself since this is done automatically).
18
19 Let's implement this example using a [[Wiki Component>>extensions:Extension.WikiComponent Module]].
20
21 {{warning}}
Vincent Massol 1.2 22 This example uses the [[~~{~~{groovy}} macro>>extensions:Extension.Groovy Macro]] and thus requires having Programming Rights. If you're in a wiki in a farm this means having Programming Rights on the main wiki.
Vincent Massol 1.1 23 {{/warning}}
24
25 Follow these steps:
Danilo Oliveira 5.1 26
Vincent Massol 1.1 27 * Create a page, for example ##EventListeners.DocumentSaveListener##
28 * Add a ##XWiki.ComponentClass## XObject in it
29 ** Component Role Type: ##org.xwiki.observation.EventListener##
30 ** Component Role Hint: ##mytest## (you can use whatever name you want, it's the technical id of your listener that you won't be using in this tutorial)
31 ** Component Scope: ##Current Wiki## this means this listener will be active only in the current wiki). You'll need Admin rights for ##Current Wiki## and you'd need Programming Rights for ##Global## which would make the listener available in all the farm (i.e. all the wikis). For ##Current User## you don't need any special permission but the listener will only be active for your user.
32 * Add a ##XWiki.ComponentMethodClass## XObject for implementing the ##getEvents()## method:
33 ** Method Name: ##getEvents##
34 ** Method body code:(((
35 {{code}}
36 {{groovy}}
37 import org.xwiki.bridge.event.*
38
39 xcontext.method.output.value = [new DocumentCreatingEvent(), new DocumentUpdatingEvent()]
40 {{/groovy}}
41 {{/code}}
42 )))
43 * Add another ##XWiki.ComponentMethodClass## XObject for implementing the ##getName()## method:
44 ** Method Name: ##getName##
45 ** Method body code:(((
46 {{code}}
47 {{groovy}}
48 xcontext.method.output.value = "mytest"
49 {{/groovy}}
50 {{/code}}
51
52 Note that ##mytest## should be the same technical id that you've used above.
53 )))
54 * Add another ##XWiki.ComponentMethodClass## XObject for implementing the ##onEvent()## method:
55 ** Method Name: ##onEvent##
56 ** Method body code:(((
57 {{code}}
58 {{groovy}}
59 def docSource = xcontext.method.input.get(1)
60 if (docSource.space != "EventListeners") {
61 docSource.setContent(docSource.content + "\n\nSome extra content...")
62 }
Danilo Oliveira 5.1 63 {{/groovy}}
64 {{/code}}
Vincent Massol 1.1 65 )))
66 * Save!
67
68 When you save the ##EventListeners.DocumentSaveListener## page, the component you've defined (your Event Listener) is automatically registered and active.
69
70 You can verify it works by creating a new page or editing an existing page and you should see the text ##Some extra content...## added at the bottom of your pages when you save them.
71
Vincent Massol 2.2 72 = Log when a document is modified =
Vincent Massol 1.1 73
74 In this example we want to log all document changes by adding a line in a page named ##Main.Logs##.
75
76 We'll implement this use case using Groovy in a wiki page.
77
78 {{warning}}
Vincent Massol 1.2 79 This example uses the [[~~{~~{groovy}} macro>>extensions:Extension.Groovy Macro]] and thus requires having Programming Rights. If you're in a wiki in a farm this means having Programming Rights on the main wiki.
Vincent Massol 1.1 80 {{/warning}}
81
82 {{code language="java"}}
83 {{groovy}}
84 import org.xwiki.observation.*
85 import org.xwiki.observation.event.*
86 import org.xwiki.bridge.event.*
Vincent Massol 6.1 87 import org.xwiki.model.reference.*
88 import org.xwiki.model.*
Vincent Massol 1.1 89 import com.xpn.xwiki.web.*
90 import com.xpn.xwiki.*
91
92 class LoggingEventListener implements EventListener
93 {
Vincent Massol 6.1 94 def logReference = new EntityReference("Logs", EntityType.DOCUMENT,
95 new EntityReference("Main", EntityType.SPACE))
96
Vincent Massol 1.1 97 String getName()
98 {
99 // The unique name of this event listener
100 return "logging"
101 }
102
103 List<Event> getEvents()
104 {
105 // The list of events this listener listens to
106 return [new DocumentUpdatedEvent()]
107 }
108
109 // Called by the Observation Manager when an event matches the list of events returned
110 // by getEvents()
Vincent Massol 6.1 111 void onEvent(Event event, Object source, Object context)
Vincent Massol 1.1 112 {
113 // Prevent infinite recursion since in this example we log to wiki page which
114 // triggers a document change... :)
115 if (source.fullName != "Main.Logs") {
Vincent Massol 6.1 116 def xwiki = context.getWiki()
117 def document = xwiki.getDocument(logReference, context)
Vincent Massol 1.1 118 document.setContent("${document.getContent()}\n* ${source.fullName} has been modified!")
Vincent Massol 6.1 119 xwiki.saveDocument(document, "Logging event", true, context)
Vincent Massol 1.1 120 }
121 }
122 }
123
124 // Register against the Observation Manager
125 def observation = Utils.getComponent(ObservationManager.class)
126 observation.removeListener("logging")
Marius Dumitru Florea 4.1 127 def listener = new LoggingEventListener()
Vincent Massol 1.1 128 observation.addListener(listener)
129 {{/groovy}}
130 {{/code}}
131
132 You can add other events to the ##getEvents## returned list if you wish to listener to other events, for example:
133
134 {{code}}
135 ...
Vincent Massol 6.1 136 return [new DocumentUpdateEvent(), new DocumentSaveEvent(), new DocumentDeleteEvent()]
Vincent Massol 1.1 137 ...
138 {{/code}}
139
Vincent Massol 9.1 140 == Activating the listener ==
141
Vincent Massol 11.3 142 Contrary to the Wiki Component-based solution above, this solution (Groovy script) requires that the page be viewed/rendered for the listener to be registered against the Observation Manager. Thus if you restart your wiki for example, the page won't be rendered unless someone navigates to it.
Vincent Massol 9.1 143
slauriere 11.11 144 If you need your script to always be executed in your wiki, even when it restarts, please check the [[tutorial to write a Script Service in Groovy>>doc:extensions:Extension.Script Module.GroovyScriptServiceTutorial]] which demonstrates a generic case based on Wiki Components to ensure that a script is always executed at least once in the wiki.
Vincent Massol 9.1 145
Vincent Massol 2.2 146 = Send a mail whenever a comment is added =
Vincent Massol 1.1 147
148 Let's implement this use case in Java this time!
149
Vincent Massol 11.7 150 Note: We're going to listen to ##CommentAddedEvent## but in general if you wish to be notified when an XObject has been updated you should listen to ##XObjectUpdatedEvent## (or ##XObjectPropertyUpdatedEvent## when an XProperty has been updated).
Vincent Massol 1.1 151
Vincent Massol 11.7 152 {{warning}}
153 The code below uses the old Mail Sender Plugin. This no longer the canonical way to send mails from script. This example needs to be rewritten using the new [[Mail Sender API>>doc:extensions:Extension.Mail Sender API]].
154 {{/warning}}
155
Vincent Massol 8.3 156 * Let's use the XWiki Maven Archetype for creating a skeleton project, by following [[this tutorial>>platform:DevGuide.WritingComponents]].
Vincent Massol 11.7 157 * Make sure you have the following 2 dependencies in your ##pom.xml## file (adjust the version to use some recent XWiki version):(((
Vincent Massol 2.3 158 {{code language="xml"}}
Vincent Massol 1.1 159 ...
160 <dependency>
161 <groupId>org.xwiki.platform</groupId>
162 <artifactId>xwiki-platform-oldcore</artifactId>
163 <version>5.4.5</version>
164 </dependency>
165 <dependency>
166 <groupId>org.xwiki.platform</groupId>
167 <artifactId>xwiki-platform-mailsender</artifactId>
168 <version>5.4.5</version>
169 </dependency>
170 ...
Vincent Massol 2.3 171 {{/code}}
Vincent Massol 1.1 172 )))
173 * Create a ##CommentListener## class in package ##org.xwiki.contrib.internal## (pick the package name you wish!) with the following content:(((
174 {{code language="java"}}
175 package org.xwiki.contrib.internal;
176
177 import java.util.Arrays;
178 import java.util.List;
179
180 import javax.inject.Inject;
181 import javax.inject.Named;
182 import javax.inject.Singleton;
183
184 import org.slf4j.Logger;
185 import org.xwiki.component.annotation.Component;
186 import org.xwiki.model.EntityType;
187 import org.xwiki.model.reference.EntityReference;
188 import org.xwiki.observation.EventListener;
189 import org.xwiki.observation.event.Event;
190
191 import com.xpn.xwiki.XWikiContext;
192 import com.xpn.xwiki.doc.XWikiDocument;
193 import com.xpn.xwiki.internal.event.XObjectAddedEvent;
194 import com.xpn.xwiki.objects.BaseObject;
195 import com.xpn.xwiki.plugin.mailsender.MailSenderPluginApi;
196
197 @Component
198 @Named("CommentEventListener")
199 @Singleton
200 public class CommentEventListener implements EventListener
201 {
202 @Inject
203 private Logger logger;
204
205 private EntityReference commentClassReference = new EntityReference("XWikiComments", EntityType.DOCUMENT,
206 new EntityReference("XWiki", EntityType.SPACE));
207
208 @Override public String getName()
209 {
210 return "CommentEventListener";
211 }
212
213 @Override public List<Event> getEvents()
214 {
215 return Arrays.<Event>asList(new XObjectAddedEvent());
216 }
217
218 @Override public void onEvent(Event event, Object source, Object data)
219 {
220 XWikiDocument document = (XWikiDocument) source;
221 BaseObject commentObject = document.getXObject(this.commentClassReference);
222 if (commentObject != null) {
223 try {
224 // Get comment
225 String comment = commentObject.getStringValue("comment");
226 // Send email
227 XWikiContext xcontext = (XWikiContext) data;
228 MailSenderPluginApi mailSender = (MailSenderPluginApi) xcontext.getWiki().getPluginApi("mailsender", xcontext);
229 mailSender.sendTextMessage("XWiki <xwiki@no-reply>", "john@doe.com",
230 "[XWiki] Comment added to " + document.toString(), comment);
231 } catch (Exception e) {
232 this.logger.error("Failure in comment listener", e);
233 }
234
235 }
236 }
237 }
238 {{/code}}
239 )))
240 * Don't forget to register your component in the ##META-INF/components.txt## file in your project:(((
241 {{code}}
242 org.xwiki.contrib.internal.CommentEventListener
243 {{/code}}
244 )))
Vincent Massol 11.8 245 * Build the project with maven: ##mvn clean install## and copy the JAR generated in the ##target## directory in your XWiki's ##WEB-INF/lib## directory, and restart XWiki.(((
Danilo Oliveira 5.1 246 {{info}}
Vincent Massol 11.8 247 If you are experiencing problems trying to build it, refer to the [[Building documentation>>dev:Community.Building]].
Danilo Oliveira 5.1 248 {{/info}}
Vincent Massol 11.8 249 )))
Danilo Oliveira 5.1 250
Vincent Massol 11.9 251 Before trying it, go to your wiki's administration page and make sure you've configured the Mail Server settings. Try it by adding a new comment on a page. You should receive an email!
Vincent Massol 1.1 252
253 {{warning}}
Vincent Massol 11.10 254 This implementation is not very good since the mail is sent synchronously on page saves, and sending an email takes some time. Using the newer [[Mail Sender API>>doc:extensions:Extension.Mail Sender API]] would allow to do that easily.
Vincent Massol 1.1 255 {{/warning}}
256
Vincent Massol 3.1 257 = Old Notifications Tutorial =
258
Vincent Massol 8.3 259 If you're using an old version of XWiki (prior to 2.0) you should check this [[old Notifications Tutorial>>platform:DevGuide.GroovyNotificationTutorial]].

Get Connected