Show last authors
author | version | line-number | content |
---|---|---|---|
1 | {{box cssClass="floatinginfobox" title="**Contents**"}} | ||
2 | {{toc/}} | ||
3 | {{/box}} | ||
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: | ||
8 | |||
9 | * In a wiki page, as a [[Wiki Component>>extensions:Extension.WikiComponent Module]] | ||
10 | * In a wiki page, using Groovy, by writing a Component and manually registering it against the Component Manager | ||
11 | * In Java, as a [[Component>>extensions:Extension.Component Module]] | ||
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}} | ||
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. | ||
23 | {{/warning}} | ||
24 | |||
25 | Follow these steps: | ||
26 | |||
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 | } | ||
63 | {{/groovy}} | ||
64 | {{/code}} | ||
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 | |||
72 | = Log when a document is modified = | ||
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}} | ||
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. | ||
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.* | ||
87 | import org.xwiki.model.reference.* | ||
88 | import org.xwiki.model.* | ||
89 | import com.xpn.xwiki.web.* | ||
90 | import com.xpn.xwiki.* | ||
91 | |||
92 | class LoggingEventListener implements EventListener | ||
93 | { | ||
94 | def logReference = new EntityReference("Logs", EntityType.DOCUMENT, | ||
95 | new EntityReference("Main", EntityType.SPACE)) | ||
96 | |||
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() | ||
111 | void onEvent(Event event, Object source, Object context) | ||
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") { | ||
116 | def xwiki = context.getWiki() | ||
117 | def document = xwiki.getDocument(logReference, context) | ||
118 | document.setContent("${document.getContent()}\n* ${source.fullName} has been modified!") | ||
119 | xwiki.saveDocument(document, "Logging event", true, context) | ||
120 | } | ||
121 | } | ||
122 | } | ||
123 | |||
124 | // Register against the Observation Manager | ||
125 | def observation = Utils.getComponent(ObservationManager.class) | ||
126 | observation.removeListener("logging") | ||
127 | def listener = new LoggingEventListener() | ||
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 | ... | ||
136 | return [new DocumentUpdateEvent(), new DocumentSaveEvent(), new DocumentDeleteEvent()] | ||
137 | ... | ||
138 | {{/code}} | ||
139 | |||
140 | == Activating the listener == | ||
141 | |||
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. | ||
143 | |||
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. | ||
145 | |||
146 | = Send a mail whenever a comment is added = | ||
147 | |||
148 | Let's implement this use case in Java this time! | ||
149 | |||
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). | ||
151 | |||
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 | |||
156 | * Let's use the XWiki Maven Archetype for creating a skeleton project, by following [[this tutorial>>platform:DevGuide.WritingComponents]]. | ||
157 | * Make sure you have the following 2 dependencies in your ##pom.xml## file (adjust the version to use some recent XWiki version):((( | ||
158 | {{code language="xml"}} | ||
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 | ... | ||
171 | {{/code}} | ||
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>", "[email protected]", | ||
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 | ))) | ||
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.((( | ||
246 | {{info}} | ||
247 | If you are experiencing problems trying to build it, refer to the [[Building documentation>>dev:Community.Building]]. | ||
248 | {{/info}} | ||
249 | ))) | ||
250 | |||
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! | ||
252 | |||
253 | {{warning}} | ||
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. | ||
255 | {{/warning}} | ||
256 | |||
257 | = Old Notifications Tutorial = | ||
258 | |||
259 | If you're using an old version of XWiki (prior to 2.0) you should check this [[old Notifications Tutorial>>platform:DevGuide.GroovyNotificationTutorial]]. |