From empty
To version 1.1
edited by superadmin
on 2017/11/28 14:52
Change comment: Install extension [org.xwiki.platform:xwiki-platform-activitystream-ui/9.9]

Summary

Details

Page properties
Title
... ... @@ -1,0 +1,1 @@
1 +$services.localization.render('xe.activity')
Parent
... ... @@ -1,0 +1,1 @@
1 +Main.WebHome
Author
... ... @@ -1,1 +1,1 @@
1 -XWiki.XWikiGuest
1 +xwiki:XWiki.superadmin
Syntax
... ... @@ -1,1 +1,1 @@
1 -XWiki 2.1
1 +XWiki 2.0
Hidden
... ... @@ -1,1 +1,1 @@
1 -false
1 +true
Content
... ... @@ -1,0 +1,19 @@
1 +{{velocity}}
2 +##
3 +## Activity Macro
4 +##
5 +## Optional parameters :
6 +##
7 +## - entries (String set to a numerical value): number of entries to display the activity for.
8 +## - subentries (String set to a numerical value): number of activities to show for each entry.
9 +## - wikis (List of string[s]): comma separated list of wikis to display activity for.
10 +## - spaces (List of string[s]): comma separated list of local serialized references of the spaces to display the activity for.
11 +## - authors (List of string[s]): comma separated list of authors whose modifications to show.
12 +## - tags (List of string[s]): comma separated list of tags to display activity for.
13 +## - minor (String set to "true" or "false"): whether to show modifications that create minor versions or not.
14 +## - rss (String set to "true" or "false"): whether to show activity rss link or not.
15 +## - messageSender (String set to "true" or "false"): whether or not to display the message sender input.
16 +##
17 +{{/velocity}}
18 +
19 +{{activity /}}
XWiki.JavaScriptExtension[0]
Caching policy
... ... @@ -1,0 +1,1 @@
1 +long
Code
... ... @@ -1,0 +1,125 @@
1 +var XWiki = (function (XWiki) {
2 +// Start XWiki augmentation.
3 +XWiki.Activity = Class.create({
4 + initialize : function() {
5 + this.initHandlers();
6 + this.attachDeleteHandlers();
7 + },
8 + initHandlers: function() {
9 + $$('.activityHeader').each(function(activityHeader){
10 + if(activityHeader.up().hasClassName('collapsed')){
11 + activityHeader.observe('click', this.toggle.bind(this, activityHeader.up()));
12 + }
13 + }.bind(this));
14 +
15 + document.observe('xwiki:activity:newActivity', function(event) {
16 + // Update all activity streams to display the new message
17 + $$('.activity').each(this.updateActivity.bind(this));
18 + }.bind(this));
19 + },
20 + toggle: function() {
21 + var collapsable = arguments[0];
22 + if (collapsable.hasClassName('collapsed')) {
23 + collapsable.removeClassName('collapsed');
24 + collapsable.down('.toolExpand').update('▲');
25 + } else {
26 + collapsable.addClassName('collapsed');
27 + collapsable.down('.toolExpand').update('▼');
28 + }
29 + },
30 + updateActivity: function(activityContainer) {
31 + var updateURL = activityContainer.down('.updateURL').value;
32 + new Ajax.Updater(
33 + {success: activityContainer},
34 + updateURL, {
35 + onCreate: function() {
36 + // NOTE: Do we need any progress/success notification at this level or do we do it only in the "calling" component?
37 + //activityContainer._notification = new XWiki.widgets.Notification("$services.localization.render('core.widgets.confirmationBox.notification.inProgress')", 'inprogress');
38 + },
39 + onSuccess: function() {
40 + if (activityContainer && activityContainer._notification) {
41 + activityContainer._notification.hide();
42 + }
43 + },
44 + onFailure: function(response) {
45 + var failureReason = '';
46 + if (response.statusText == '' /* No response */ || response.status == 12031 /* In IE */) {
47 + failureReason = 'Server not responding';
48 + } else {
49 + failureReason = response.statusText;
50 + }
51 + if (activityContainer) {
52 + if (activityContainer._notification) {
53 + activityContainer._notification.hide();
54 + }
55 + activityContainer._notification = new XWiki.widgets.Notification("$services.localization.render('core.widgets.confirmationBox.notification.failed')" + failureReason, 'error');
56 + }
57 + },
58 + on1223 : function(response) { /*SUCCESS*/
59 + response.request.options.onSuccess(response);
60 + },
61 + on0 : function(response) { /*FAILURE*/
62 + response.request.options.onFailure(response);
63 + },
64 + onComplete: function(response) {
65 + // The newly retrieved activity element is inserted after onSuccess is called, so we must handle it here instead of in onSuccess.
66 + if (response.request.success()) {
67 + // The remplacement activity has a default updateURL. We discard that and replace it with the updateURL of the original activity so that, at each update, the URL stays the same.
68 + var newActivity = activityContainer.next();
69 + if (newActivity) {
70 + newActivity.down('.updateURL').value = updateURL;
71 + }
72 +
73 + // Remove the old activity element.
74 + activityContainer.remove();
75 +
76 + this.attachDeleteHandlers();
77 + }
78 + }.bind(this),
79 + insertion: 'after'
80 + }
81 + );
82 + },
83 + attachDeleteHandlers : function () {
84 + $$('.activity .activityItem .toolDelete').invoke('observe', 'click', function (event) {
85 + event.stop();
86 + var trigger = event.element();
87 + trigger.blur();
88 + if (trigger.disabled) {
89 + // Do nothing if the button was already clicked and it's waiting for a response from the server.
90 + return;
91 + } else {
92 + new XWiki.widgets.ConfirmedAjaxRequest(
93 + /* Ajax request URL */
94 + trigger.href.replace(/xredirect=.*$/, 'xpage=plain&confirm=1'),
95 + /* Ajax request parameters */
96 + {
97 + onCreate : function() {
98 + // Disable the button, to avoid a cascade of clicks from impatient users
99 + trigger.disabled = true;
100 + },
101 + onSuccess : function() {
102 + // Remove the corresponding HTML element from the UI
103 + trigger.up("li").remove();
104 + },
105 + onComplete : function() {
106 + // In the end: re-enable the button
107 + trigger.disabled = false;
108 + }
109 + },
110 + /* Interaction parameters */
111 + {
112 + confirmationText: "$services.localization.render('xe.activity.messages.delete.confirm')"
113 + }
114 + );
115 + }
116 + });
117 + }
118 +});
119 +// End XWiki augmentation.
120 +return XWiki;
121 +}(XWiki || {}));
122 +
123 +document.observe('xwiki:dom:loaded', function() {
124 + new XWiki.Activity();
125 +});
Name
... ... @@ -1,0 +1,1 @@
1 +Expand and Collapse
Parse content
... ... @@ -1,0 +1,1 @@
1 +Yes
Use this extension
... ... @@ -1,0 +1,1 @@
1 +onDemand
XWiki.RequiredRightClass[0]
level
... ... @@ -1,0 +1,1 @@
1 +programming
XWiki.StyleSheetExtension[0]
Caching policy
... ... @@ -1,0 +1,1 @@
1 +long
Code
... ... @@ -1,0 +1,280 @@
1 +#template('colorThemeInit.vm')
2 +.rssURL{
3 + padding-top: 5px;
4 + text-align: right;
5 +}
6 +
7 +.rssURL img {
8 + margin-right: 5px;
9 +}
10 +
11 +ul.activityList {
12 + list-style: none;
13 + margin: 0;
14 + padding: 0;
15 +}
16 +
17 +.activityItem {
18 + margin: 0 0 5px;
19 + padding: 5px 0;
20 + position: relative;
21 +}
22 +
23 +.activityItem .activityItem {
24 + margin: 0;
25 +}
26 +
27 +.activityPage:hover , .activityApplication:hover , .activityUser:hover {
28 + background: $theme.highlightColor;
29 +}
30 +
31 +.activityApplication, .activityUser, .activityPage .activityItem {
32 + display: block;
33 + padding-left: 75px;
34 + position: relative;
35 +}
36 +
37 +.activityWiki {
38 + color: $theme.textSecondaryColor;
39 + font-size: .875em;
40 + padding-right: .2em;
41 +}
42 +
43 +.activityUser {
44 + padding-left: 0;
45 + width: 100%;
46 +}
47 +.activityUser .activityContent {
48 + padding: 0.5em 0 0.5em 75px;
49 +}
50 +
51 +.activitySummary {
52 + min-height: 35px;
53 + padding-top: 5px;
54 +}
55 +
56 +.activitySummary , .activityPage .activityAction {
57 + padding-left: 25px;
58 +}
59 +
60 +.activityPage ul .activityAction {
61 + padding-left: 0;
62 +}
63 +
64 +.activityAuthor , .activityAction{
65 + display: block;
66 +}
67 +
68 +.activityAuthor {
69 + padding-right: 8em;
70 +}
71 +
72 +.activityPage .activityAuthor {
73 + padding-left: 25px;
74 + text-indent: -25px;
75 +}
76 +
77 +.activityPage .activityContent .activityAuthor {
78 + padding-left: 0;
79 + text-indent: 0;
80 +}
81 +
82 +.activityAction, .activityTime {
83 + color: $theme.textSecondaryColor;
84 + font-size: .8em;
85 +}
86 +
87 +.activityTime {
88 + padding: 5px 15px 0 0;
89 + position: absolute;
90 + right: 0;
91 + top: 0;
92 +}
93 +
94 +.activitySnapshot {
95 + height: 30px;
96 + left: 0;
97 + margin: 5px 0 0 25px;
98 + position: absolute;
99 +}
100 +
101 +.activitySummary .activitySnapshot {
102 + display: inline-block;
103 + margin: 0 17px 0 0;
104 + position: relative;
105 +}
106 +
107 +.activityItem .activityItem .activitySnapshot {
108 + margin-top: 2px;
109 +}
110 +
111 +.activitySnapshot img {
112 + border-radius: 4px;
113 + height: 30px;
114 + width: 30px;
115 +}
116 +
117 +.activitySnapshot .activityActionAvatar {
118 + background: $theme.pageContentBackgroundColor;
119 + border-color: $theme.pageContentBackgroundColor;
120 + border-style: solid;
121 + border-width: 4px 1px 1px 3px;
122 + box-sizing: content-box;
123 + bottom: -5px;
124 + height: 16px;
125 + position: absolute;
126 + right: -10px;
127 + width: 16px;
128 +}
129 +
130 +/** <start> Inside activityContent **/
131 +.activityApplication .activityContent,
132 +.activityUser .activityContent,
133 +.activityList .annotated,
134 +.activityList .annotation,
135 +.activityList .attachment,
136 +.activityList .comment {
137 + font-size: .9em;
138 + padding-right: 15px;
139 +}
140 +
141 +.collapsed .activityContent, .activitySummary {
142 + display: none;
143 +}
144 +
145 +.collapsed .activitySummary {
146 + display: block;
147 +}
148 +
149 +.activityList .annotated {
150 + background-color: #FFEE99;
151 + padding: 3px 15px 3px 5px;
152 +}
153 +
154 +.activityList .attachment {
155 + margin: 0;
156 + padding: 3px 0 0;
157 +}
158 +
159 +.activityList .message {
160 + /* Preserve the white-space because messages are written in plain text. */
161 + white-space: pre-wrap;
162 +}
163 +/** <end> **/
164 +
165 +/** <start> Tools **/
166 +/** <start> Expand Tool **/
167 +.toolExpand{
168 + color: $theme.textColor;
169 + cursor: pointer;
170 + font-size: .6em;
171 + padding: 0 3px;
172 + position: absolute;
173 + right: 0;
174 + top: 7px;
175 + visibility: hidden;
176 +}
177 +
178 +.toolExpand:hover {
179 + text-decoration: none;
180 +}
181 +
182 +.activityList:hover .toolExpand {
183 + visibility: visible;
184 +}
185 +/** </end> Expand Tool **/
186 +/** <start> Filter Tool **/
187 +.toolFilter {
188 + cursor: pointer;
189 + float: right;
190 + padding-right: 13px;
191 + position: relative;
192 +}
193 +
194 +.toolFilter .toolExpand{
195 + color: transparent;
196 +}
197 +
198 +h1:hover .toolFilter .toolExpand {
199 + color: $theme.textColor;
200 +}
201 +
202 +.toolFilter .toolExpand {
203 + font-size: 0.3em;
204 + line-height: 4em;
205 + top: 5px;
206 +}
207 +/** </end> Filter Tool **/
208 +/** <start> Delete Tool **/
209 +.toolDelete {
210 + color: $theme.notificationErrorColor !important;
211 + cursor: pointer;
212 + font-size: .9em;
213 + font-weight: bold;
214 + padding: 0 4px;
215 + position: absolute;
216 + right: 0;
217 + top: 2px;
218 + visibility: hidden;
219 + z-index: 1;
220 +}
221 +
222 +.activityItem:hover .toolDelete {
223 + visibility: visible;
224 +}
225 +
226 +.toolDelete:hover {
227 + text-decoration: none;
228 +}
229 +/** </end> Delete Tool **/
230 +/** </end> Tools **/
231 +
232 +/** <start> Action Types **/
233 +.type {
234 + background: none no-repeat scroll 4px 0 transparent;
235 + padding-left: 25px;
236 +}
237 +
238 +.typePage {
239 + background-image:url("$xwiki.getSkinFile('icons/silk/page_white_text.png')");
240 +}
241 +
242 +/** </end> Action Types **/
243 +
244 +/** CSS 3**/
245 +.activitySnapshot .activityActionAvatar {
246 + border-radius: 10px 10px 0 10px;
247 + box-shadow: -1px 1px 1px rgba(128, 128, 128, 0.6);
248 +}
249 +
250 +.activitySnapshot img {
251 + box-shadow: 0 0 2px 1px rgba(128, 128, 128, 0.6);
252 +}
253 +
254 +/** IE7 **/
255 +* html .activityItem , * html .activityHeader, * html .activityContent ul{
256 + display: inline-block;
257 +}
258 +
259 +* html .activityAuthor {
260 + float: left;
261 +}
262 +*+html .activityAuthor {
263 + float: left;
264 +}
265 +
266 +* html .activityTime {
267 + float: right;
268 +}
269 +
270 +* html .activityItem {
271 + float: right;
272 +}
273 +
274 +* html .activityContent {
275 + line-height: 1%;
276 +}
277 +
278 +* html .activityAction {
279 + clear: left;
280 +}
Content Type
... ... @@ -1,0 +1,1 @@
1 +CSS
Name
... ... @@ -1,0 +1,1 @@
1 +Activity CSS
Parse content
... ... @@ -1,0 +1,1 @@
1 +Yes
Use this extension
... ... @@ -1,0 +1,1 @@
1 +onDemand
XWiki.WikiMacroClass[0]
Macro code
... ... @@ -1,0 +1,996 @@
1 +{{template name="hierarchy_macros.vm" /}}
2 +
3 +{{velocity output=false}}
4 +##
5 +##
6 +## Skin Extensions
7 +##--------------------------------------------------------------
8 +$xwiki.jsx.use('Main.Activity')##
9 +$xwiki.ssx.use('Main.Activity')##
10 +##
11 +## Icons
12 +##--------------------------------------------------------------
13 +## Some events are not implemented yet in the activitystream plugin: TODO: application | activityApplication, user | activityUser
14 +#set ($activityActionsMap = {
15 + 'create': 'icons/silk/page_white_add.png',
16 + 'delete': 'icons/silk/page_white_delete.png',
17 + 'update': 'icons/silk/page_white_edit.png',
18 + 'addAnnotation': 'icons/silk/note_add.png',
19 + 'deleteAnnotation': 'icons/silk/note_delete.png',
20 + 'updateAnnotation': 'icons/silk/note_edit.png',
21 + 'addAttachment': 'icons/xwiki/attach_add.png',
22 + 'deleteAttachment': 'icons/xwiki/attach_delete.png',
23 + 'updateAttachment': 'icons/xwiki/attach_edit.png',
24 + 'addComment': 'icons/silk/comment_add.png',
25 + 'deleteComment': 'icons/silk/comment_delete.png',
26 + 'updateComment': 'icons/silk/comment_edit.png',
27 + 'publicMessage': 'icons/silk/user_comment.png',
28 + 'personalMessage': 'icons/silk/user_comment.png',
29 + 'directMessage': 'icons/silk/user_go.png',
30 + 'groupMessage': 'icons/silk/group_go.png'
31 +})
32 +#macro (initDateVariables)
33 +##
34 +## Date Time
35 +##--------------------------------------------------------------
36 +#set ($currentTime = $xwiki.jodatime.getDateTime())
37 +#set ($now = $datetool.date)
38 +#set ($todayYear = $datetool.getYear($now))
39 +#set ($todayMonth = $datetool.getMonth($now) + 1)
40 +#set ($todayMidnight = $currentTime.withTimeAtStartOfDay())
41 +#set ($yesterdayMidnight = $todayMidnight.minusDays(1))
42 +#end
43 +##
44 +## Formatters
45 +##--------------------------------------------------------------
46 +#set ($eventDateFormatter = 'dd MMM, HH:mm')
47 +#set ($eventFullDateFormatter = 'yyyy/MM/dd HH:mm')
48 +#set ($activityListDateFormatter = 'dd MMM')
49 +##
50 +## User status configuration
51 +## -------------------------------------------------------------
52 +#set ($isMessageStreamActive = $services.messageStream.isActive())
53 +## TODO: filter out user statuses from the activity stream if inactive
54 +##
55 +##
56 +#set ($currentUser = $services.model.serialize($xcontext.userReference, 'default'))
57 +##
58 +## Filter clause
59 +##--------------------------------------------------------------
60 +#set ($filterClause = '1=1')
61 +#set ($filterParameters = [])
62 +##
63 +## Get values from request/macro param
64 +##--------------------------------------------------------------
65 +## Number of entries to display
66 +#readVariableFromRequest('aEntries' 'entries' 20)
67 +#if ($aEntries == 20 && $xcontext.macro.params.get('entries'))
68 + #set ($aEntries = $mathtool.toInteger($xcontext.macro.params.get('entries')))
69 +#end
70 +## Number of activities to display for each entry
71 +#readVariableFromRequest('aSubEntries' 'subentries' 10)
72 +#if ($aSubEntries == 10 && $xcontext.macro.params.get('subentries'))
73 + #set ($aSubEntries = $mathtool.toInteger($xcontext.macro.params.get('subentries')))
74 +#end
75 +## Wikis
76 +#readVariableFromRequest('aWikis' 'wikis' [])
77 +#if ($aWikis.size() == 0)
78 + #set ($aWikisString = $xcontext.macro.params.get('wikis'))
79 + #asList('aWikis' $aWikisString)
80 + #if ($aWikis.size() == 0)
81 + ## if no wikis has been set and we are in the main wiki, then we should try to list every wikis where the user has view right
82 + #if ("$!services.wiki" != '' && $services.wiki.mainWikiId == $services.wiki.currentWikiId)
83 + #set ($allWikis = $services.wiki.getAll())
84 + #foreach ($wiki in $allWikis)
85 + #if ($xwiki.hasAccessLevel('view', $xcontext.userReference, $wiki.mainPageReference))
86 + $aWikis.add($wiki.id)
87 + #end
88 + #end
89 + #else
90 + ## else, only check the current wiki
91 + $aWikis.add($xcontext.database)
92 + #end
93 + #end
94 +#end
95 +## Spaces
96 +#readVariableFromRequest('aSpaces' 'spaces' [])
97 +#if ($aSpaces.size() == 0)
98 + #set ($aSpacesString = $xcontext.macro.params.get('spaces'))
99 + #asList('aSpaces' $aSpacesString)
100 +#end
101 +## Authors
102 +#readVariableFromRequest('aAuthors' 'authors' [])
103 +#if ($aAuthors.size() == 0)
104 + #set ($aAuthorsString = $xcontext.macro.params.get('authors'))
105 + #asList('aAuthors' $aAuthorsString)
106 +#end
107 +## Tags
108 +#readVariableFromRequest('aTags' 'tags' [])
109 +#if ($aTags.size() == 0)
110 + #set ($aTagsString = $xcontext.macro.params.get('tags'))
111 + #asList('aTags' $aTagsString)
112 +#end
113 +## Minor versions
114 +#readVariableFromRequest('aMinor' 'minor' false)
115 +#if (!$aMinor && $xcontext.macro.params.get('minor'))
116 + #set ($aMinor = $xcontext.macro.params.get('minor'))
117 +#end
118 +## RSS link
119 +#readVariableFromRequest('aRSS' 'rss' false)
120 +#if (!$aRSS && $xcontext.macro.params.get('rss'))
121 + #set ($aRSS = $xcontext.macro.params.get('rss'))
122 +#end
123 +##
124 +## Filter by
125 +## ------------------------------------------------------------
126 +## Authors
127 +#if ($aAuthors.size() > 0)
128 + #set ($toUsersParameters = [])
129 + #set ($fromUsersParameters = [])
130 + #set ($usersQuery = '')
131 + #set ($hasDifferentUsers = false)
132 + #foreach ($u in $aAuthors)
133 + #if ("$!u" != '')
134 + #set ($usersQuery = $usersQuery.concat(',?'))
135 + #set ($resolvedUser = $services.model.serialize($services.model.resolveDocument($u), 'default'))
136 + #set ($discard = $fromUsersParameters.add($resolvedUser))
137 + #set ($discard = $toUsersParameters.add("${resolvedUser}^XWiki.XWikiUsers"))
138 + #if ($resolvedUser != $currentUser)
139 + #set ($hasDifferentUsers = true)
140 + #end
141 + #end
142 + #end
143 + #if ($usersQuery.length() > 1)
144 + ## Remove the first comma
145 + #set ($usersQuery = $usersQuery.substring(1))
146 + #if (!$isGuest)
147 + #if ($hasDifferentUsers)
148 + ## Direct messages from the current user to the filtered users
149 + #set ($filterClause = $filterClause + " and ((act.application = 'MessageStream' and act.type = 'directMessage' and act.user = ? and act.param2 in (${usersQuery})) or act.user in (${usersQuery}))")
150 + #set ($discard = $filterParameters.add($currentUser))
151 + #set ($discard = $filterParameters.addAll($toUsersParameters))
152 + #else
153 + ## All direct messages to the current user
154 + #set ($filterClause = $filterClause + " and ((act.application = 'MessageStream' and act.type = 'directMessage' and act.param2 = ?) or act.user in (${usersQuery}))")
155 + #set ($discard = $filterParameters.add("${currentUser}^XWiki.XWikiUsers"))
156 + #end
157 + #else
158 + #set ($filterClause = $filterClause + " and act.user in (${usersQuery})")
159 + #end
160 + #set ($discard = $filterParameters.addAll($fromUsersParameters))
161 + #end
162 +#end
163 +## Spaces
164 +#updateFilterClause('space' $aSpaces false)
165 +## BLACKLISTED SPACES - exclude them
166 +#if ($blacklistedSpaces && $blacklistedSpaces.size() > 0)
167 + #set ($filterClause = $filterClause + ' AND (act.application = ''MessageStream'' OR 1=1 ')
168 + #updateFilterClause('space' $blacklistedSpaces true)
169 + #set ($filterClause = $filterClause + ')')
170 +#end
171 +## Wikis
172 +#updateFilterClause('wiki' $aWikis false)
173 +## Tags
174 +#if ($xwiki.getPlugin('tag') && $aTags.size() > 0)
175 + #set ($docsWithTag = [])
176 + #foreach ($item in $aTags)
177 + #set ($discard = $docsWithTag.addAll($xwiki.tag.getDocumentsWithTag($item)))
178 + #end
179 + ## Since there is no 'tag' field in the database schema, we can only use the page field in order to filter the query. In order to get no results when there are no documents with the requested tags, we use a fake document name
180 + #if ($docsWithTag.size() == 0)
181 + #set ($discard = $docsWithTag.add("XWiki.FakeDocumentToMakeQueryReturnNoResults${mathtool.random(1,999)}"))
182 + #end
183 + #updateFilterClause('page' $docsWithTag false)
184 +#end
185 +## Minor versions
186 +#if ("$!aMinor" == 'false')
187 + #set ($filterClause = $filterClause + " and not (act.version not like '%.1' and act.type = 'update')")
188 +#end
189 +##
190 +## MessageStream filtering
191 +## ------------------------------------------------------------
192 +## The scope filtering is already handled by the "Filter by" section: specific space, specific user...
193 +##
194 +## Public messages should be visible to everyone
195 +#set ($messageFilter = "act.type = 'publicMessage'")
196 +#if ($isMessageStreamActive)
197 + #if (!$isGuest)
198 + ##
199 + ## A user should see all his messages
200 + #set ($messageFilter = "$messageFilter or act.user = ?")
201 + #set ($discard = $filterParameters.add(${currentUser}))
202 + ##
203 + ## A user should see direct messages for him
204 + #set ($messageFilter = "$messageFilter or (act.type = 'directMessage' and act.param2 = ?)")
205 + #set ($discard = $filterParameters.add("${currentUser}^XWiki.XWikiUsers"))
206 + ##
207 + ## A user should see personal messages from followed users
208 + #set ($followedUsers = $services.watchlist.getWatchedElements("USER"))
209 + #if ($followedUsers.size() > 0)
210 + #set ($messageFilter = "$messageFilter or (act.type = 'personalMessage' and act.user in (")
211 + #foreach ($u in $followedUsers)
212 + #if ($velocityCount > 1)
213 + #set ($messageFilter = $messageFilter.concat(','))
214 + #end
215 + #set ($messageFilter = $messageFilter.concat('?'))
216 + #set ($discard = $filterParameters.add($u))
217 + #end
218 + #set ($messageFilter = $messageFilter.concat('))'))
219 + #end
220 + ##
221 + ## A user should see messages for his groups
222 + #set ($usersGroups = $xwiki.rightsmanager.getAllGroupsNamesForMember($xcontext.user))
223 + #if ($usersGroups.size() > 0)
224 + #set ($messageFilter = "$messageFilter or (act.type = 'groupMessage' and act.param2 in (")
225 + #foreach ($g in $usersGroups)
226 + #if ($velocityCount > 1)
227 + #set ($messageFilter = $messageFilter.concat(','))
228 + #end
229 + #set ($messageFilter = $messageFilter.concat('?'))
230 + #set ($discard = $filterParameters.add("${services.model.serialize($services.model.resolveDocument($g), 'default')}^XWiki.XWikiGroups"))
231 + #end
232 + #set ($messageFilter = $messageFilter.concat('))'))
233 + #end
234 + #end
235 + #set ($filterClause = $filterClause + " and (act.application <> 'MessageStream' or (act.application = 'MessageStream' and (${messageFilter})))")
236 +#else
237 + #set ($filterClause = $filterClause + " and act.application <> 'MessageStream'")
238 +#end
239 +##
240 +## Macros
241 +## -------------------------------------------------------------
242 +#*
243 + * Gets the list (java.util.List) of elements in $arrayAsString, and puts them in the $list.
244 + *
245 + * @param $arrayAsString the string form of the array, similar to the java syntax for a macro ({elt1, elt2, etc}), or without the {}
246 + * @param $resultVariableName the name of the variable to put the resulted list into.
247 + * FIXME: this is gonna fail for lists with '{' in the string or ','. Need to escape
248 + *#
249 +#macro(asList $resultVariableName $arrayAsString)
250 + #set ($list = [])
251 + #if ("$!arrayAsString" != '')
252 + ## if the string is surrounded by {}, eliminate and then parse as list
253 + #if ($arrayAsString.startsWith('{') && $arrayAsString.endWith('}'))
254 + #set ($endIndex = $arrayAsString.length() - 1)
255 + #set ($arrayAsString = $arrayAsString.substring(1, $endIndex))
256 + #end
257 + ## parse the array serialized as string and fill in the list
258 + #set ($valuesArray = $arrayAsString.split(','))
259 + #foreach ($value in $valuesArray)
260 + #if ($value.trim().length())
261 + #set ($dispose = $list.add($value.trim()))
262 + #end
263 + #end
264 + #end
265 + #setVariable("$resultVariableName", $list)
266 +#end
267 +#**
268 + * Build query filter clause
269 + * @colName The name of the collumn in the database table
270 + * @colValues The values to match with
271 + * @exclude If true, exclude the given column values
272 + *#
273 +#macro(updateFilterClause $colName $colValues $exclude)
274 + #if ($colValues.class == 'class java.lang.String')
275 + ## Transform the input to an array to handle it uniformly, below.
276 + #set ($colValues = [$colValues])
277 + #end
278 +
279 + #if ($colValues.size() > 0)
280 + ## Filter out invalid values.
281 + #set ($validColValues = [])
282 + #foreach ($colValue in $colValues)
283 + #if ("$!colValue" != '')
284 + #set ($discard = $validColValues.add($colValue))
285 + #end
286 + #end
287 +
288 + ## Work with the remaining valid values.
289 + #if ($validColValues.size() > 0)
290 + ## Open wrapping subclause.
291 + #set ($filterClause = $filterClause + ' AND (')
292 +
293 + ## Determine the sign to use for the filterCondition.
294 + #set ($maybeNegated = '')
295 + #if ($exclude)
296 + #set ($maybeNegated = 'NOT ')
297 + #end
298 +
299 + ## Exact match clause for the $validColValues.
300 + #set ($filterCondition = "${maybeNegated}IN")
301 + #set ($filterClause = $filterClause + "act.${colName} ${filterCondition} (${stringtool.repeat('?,', $mathtool.sub($validColValues.size(),1))}?)")
302 + #set ($discard = $filterParameters.addAll($validColValues))
303 +
304 + ## Extra clauses for the 'space' colName to match any nested spaces inside the specified spaces.
305 + #if ($colName == 'space')
306 + ## In an inclusion clause, we use OR to join the prefix queries, since we want at leas one match.
307 + #set ($booleanOperation = 'OR')
308 + #if ($exclude)
309 + ## In an exclusion clause, we to use AND to join the prefix queries, since we don`t want any match.
310 + #set ($booleanOperation = 'AND')
311 + #end
312 +
313 + ## Prefix match clauses for the $validColValues.
314 + #set ($filterCondition = "${maybeNegated}LIKE")
315 + #foreach ($validColValue in $validColValues)
316 + #set ($filterClause = $filterClause + " ${booleanOperation} act.${colName} ${filterCondition} ?")
317 + #set ($discard = $filterParameters.add("${validColValue}.%"))
318 + #end
319 + #end
320 +
321 + ## Close wrapping subclause.
322 + #set ($filterClause = $filterClause + ')')
323 + #end
324 + #end
325 +#end
326 +#**
327 + * Check if the document version exists
328 + *#
329 +#macro(isDocumentVersionValid $event)
330 + #set ($isDocumentVersionValid = false)
331 + #set ($pageDoc = $xwiki.getDocument($event.page))
332 + #set ($versions = $pageDoc.getRevisions())
333 + #foreach ($version in $versions)
334 + #if ($event.version == $version)
335 + #set ($isDocumentVersionValid = true)
336 + #end
337 + #end
338 +#end
339 +#**
340 + * Check if the current page event list entry is expandable (there are at least 2 events for the current page) or not.
341 + *#
342 +#macro(isPageEntryExpandable)
343 + #set ($isPageEntryExpandable = false)
344 + #if ($events.size() > 1)
345 + #set ($isPageEntryExpandable = true)
346 + #end
347 +#end
348 +#**
349 + * Get unique page event users
350 + * -> used to display the message: 'X changes by Y user(s)'
351 + *#
352 +#macro (getUniqueEventUsers)
353 + #set ($eventUsers = [])
354 + #foreach ($event in $events)
355 + #if (!$eventUsers.contains($xwiki.getUserName($event.user)))
356 + #set ($discard = $eventUsers.add($xwiki.getUserName($event.user)))
357 + #end
358 + #end
359 +#end
360 +#**
361 + * Check if the current page event happen today
362 + *#
363 +#macro(isToday)
364 + #set ($isToday = false)
365 + #set ($timeAgoMsg = '')
366 + #if ($mathtool.sub($eventTimeDiff.getDayOfYear(), 1) == 0 &&
367 + ($eventDateTime.isAfter($todayMidnight) ||
368 + $eventDateTime.isEqual($todayMidnight)))
369 + #if ($eventTimeDiff.getHourOfDay() == 0)
370 + ##
371 + ## Few seconds ago
372 + ## ---------------------------------------------------------------
373 + #set ($timeAgoMsg = $services.localization.render('timeAgo.minutesAgo', [$eventTimeDiff.getMinuteOfHour()]))
374 + #else
375 + ##
376 + ## Number of minutes ago
377 + ## ---------------------------------------------------------------
378 + #set ($hoursAgo = $eventTimeDiff.getHourOfDay())
379 + #set ($timeAgoMsg = $services.localization.render('timeAgo.hoursAgo', [$hoursAgo]))
380 + #end
381 + #set ($isToday = true)
382 + #end
383 +#end
384 +#**
385 + * Check if the current page event happen yesterday
386 + *#
387 +#macro(isYesterday)
388 + #set ($isYesterday = false)
389 + #if (($mathtool.sub($eventTimeDiff.getDayOfYear(), 1) == 0 &&
390 + $eventDateTime.isBefore($todayMidnight)
391 + ) ||
392 + ($mathtool.sub($eventTimeDiff.getDayOfYear(), 2) == 0 &&
393 + ($eventDateTime.isAfter($yesterdayMidnight) ||
394 + $eventDateTime.isEqual($yesterdayMidnight)
395 + )))
396 + #set ($isYesterday = true)
397 + #end
398 +#end
399 +#**
400 + * Check if the current event is of type 'addAttachment' or 'updateAttachment' or 'deleteAttachment'
401 + * @param event The current event
402 + *#
403 +#macro(isAttachmentEvent $event)
404 + #set ($isAttachmentEvent = false)
405 + #if ($event.type == 'addAttachment' || $event.type == 'updateAttachment' || $event.type == 'deleteAttachment')
406 + #set ($isAttachmentEvent = true)
407 + #end
408 +#end
409 +#**
410 + * Check if the current event is of type 'addComment' or 'updateComment' or 'deleteComment'
411 + *#
412 +#macro(isCommentEvent)
413 + #set ($isCommentEvent = false)
414 + #if ($event.type == 'addComment' || $event.type == 'updateComment' || $event.type == 'deleteComment')
415 + #set ($isCommentEvent = true)
416 + #end
417 +#end
418 +#**
419 + * Check if the current event is of type 'addAnnotation' or 'updateAnnotation' or 'deleteAnnotation'
420 + *#
421 +#macro(isAnnotationEvent)
422 + #set ($isAnnotationEvent = false)
423 + #if ($event.type == 'addAnnotation' || $event.type == 'updateAnnotation' || $event.type == 'deleteAnnotation')
424 + #set ($isAnnotationEvent = true)
425 + #end
426 +#end
427 +#**
428 + * Check if the current event is a message in the MessageStream application
429 + *#
430 +#macro(isMessageEvent)
431 + #set ($isMessageEvent = false)
432 + #if ($event.application == 'MessageStream')
433 + #set ($isMessageEvent = true)
434 + #end
435 +#end
436 +#**
437 + * Get comment content
438 + *#
439 +#macro(getCommentContent $event $commentContent)
440 + #set ($commentContent = '')
441 + #isDocumentVersionValid($event)
442 + #if ($isDocumentVersionValid)
443 + #set ($pageDocRevision = $xwiki.getDocument($xwiki.getDocument($event.page), $event.version))
444 + #set ($commentContent = $!pageDocRevision.getObject('XWiki.XWikiComments', $mathtool.toInteger($event.param2)).getProperty('comment').value)
445 + #end
446 +#end
447 +#**
448 + * Get annotation text and selection
449 + *#
450 +#macro(getAnnotation $event $annotationText $annotationSelection $annotationTextSyntaxId)
451 + #set ($annotationText = '')
452 + #set ($annotationSelection = '')
453 + #set ($annotationTextSyntaxId = '')
454 + #isDocumentVersionValid($event)
455 + #if ($isDocumentVersionValid)
456 + #set ($pageDocRevision = $xwiki.getDocument($xwiki.getDocument($event.page), $event.version))
457 + ## FIXME: Should we query the Annotations application's' configuration for the configured annotation class instead of hardcoding the default one? What about custom annotations?
458 + #set ($annotationObject = $pageDocRevision.getObject('XWiki.XWikiComments', $mathtool.toInteger($event.param2)))
459 + #set ($annotationSelection = $annotationObject.getProperty('selection').value)
460 + #set ($annotationText = $annotationObject.getProperty('comment').value)
461 + #set ($annotationTextSyntaxId = $pageDocRevision.getSyntax().toIdString())
462 + #end
463 +#end
464 +#**
465 + * Remove duplicated 'update' and 'addAttachment' events for the current page entry
466 + * NOTE: each time an event != 'update' (like 'addAttachment, updateComment' ...) is fired, an extra 'update' event is fired too (for backward compatibility reasons)
467 + *#
468 +#macro(removeDuplicatedEvents)
469 + ## TO DO - fix XWIKI-5632
470 + ## Because of this issue, related 'updateAnnotation' events are not included in the list (for flooding reasons)
471 + #set ($newList = [])
472 + #set ($previousEventInOldList = $NULL)
473 + #set ($previousEventInNewList = $NULL)
474 + #foreach ($event in $events)
475 + #set ($isUpdateRelatedEvent = false)
476 + #set ($isConsecutiveUpdateEvent = false)
477 + #set ($isCreateOrDeleteRelatedEvent = false)
478 + ##
479 + ## Get consecutive 'update' events having the same user (-> only the latest such event will be taken in account)
480 + ## ---------------------------------------------------------------
481 + #if ($event.type == 'update'
482 + && $event.requestId != $previousEventInNewList.requestId
483 + && $event.type == $previousEventInNewList.type
484 + && $event.user == $previousEventInNewList.user)
485 + #set ($isConsecutiveUpdateEvent = true)
486 + #end
487 + #if (!$isConsecutiveUpdateEvent)
488 + #checkRelatedEvents()
489 + ##
490 + ## Build the clean event list
491 + ## ---------------------------------------------------------------
492 + #if (!$isUpdateRelatedEvent
493 + && !($isCreateOrDeleteRelatedEvent && $event.type != 'create' && $event.type != 'delete')
494 + && $newList.size() < $aSubEntries)
495 + #set ($discard = $newList.add($event))
496 + #set ($previousEventInNewList = $event)
497 + #end
498 + #if ($newList.size() >= $aSubEntries)
499 + ## number of subentries reached
500 + #break
501 + #end
502 + #end
503 + #set ($previousEventInOldList = $event)
504 + #end
505 + ## Set the $events reference to the new list
506 + #set ($events = $newList)
507 +#end
508 +#**
509 + * checks the condition of the related events
510 + *#
511 +#macro(checkRelatedEvents)
512 + ## load related events
513 + #set ($relatedEvents = $xwiki.activitystream.getRelatedEvents($event))
514 + #if ($relatedEvents.size() > 1)
515 + #if ($event.type == 'update')
516 + #foreach ($relatedEvent in $relatedEvents)
517 + #if ($relatedEvent.type != 'update')
518 + #set ($isUpdateRelatedEvent = true)
519 + #break
520 + #end
521 + #end
522 + #end
523 + ## Get 'create' and 'delete' related event (-> in order to remove duplicated events in the case of import, rename, delete page)
524 + ## ---------------------------------------------------------------
525 + #if ($event.type != 'create' && $event.type != 'delete')
526 + #foreach ($relatedEvent in $relatedEvents)
527 + #if ($relatedEvent.type == 'create' || $relatedEvent.type == 'delete')
528 + #set ($isCreateOrDeleteRelatedEvent = true)
529 + #break
530 + #end
531 + #end
532 + #end
533 + #end
534 +#end
535 +#**
536 + * Initialize event variables
537 + *#
538 +#macro(initializeEvent)
539 + #set ($yearMsg = '')
540 + #set ($timeAgoMsg = '')
541 + #set ($eventDateTime = $xwiki.jodatime.getDateTime($event.date.getTime()))
542 + #set ($eventTimeDiff = $currentTime.minus($eventDateTime.millis).withZone($xwiki.jodatime.getUTCTimezone()))
543 + #set ($eventYear = $eventDateTime.year)
544 + #if ($eventYear != $todayYear)
545 + #set ($yearMsg = $eventYear)
546 + #end
547 + #isToday()
548 + #isYesterday()
549 + #isMessageEvent()
550 + #isCommentEvent()
551 + #isAnnotationEvent()
552 + #if("$!timeAgoMsg" == '')
553 + #set ($timeAgoMsg = $xwiki.formatDate($event.date, $eventDateFormatter) + " $!yearMsg")
554 + #end
555 + #set ($iterationDate = $xwiki.formatDate($event.date, $activityListDateFormatter) + " $!yearMsg")
556 + #set ($eventFullDate = $xwiki.formatDate($event.date, $eventFullDateFormatter) + " $!yearMsg")
557 +#end
558 +#**
559 + * Display the arrow used to expand the event list
560 + *#
561 +#macro(toolExpand)
562 + <a class='toolExpand'>&#9660;</a>
563 +#end
564 +#**
565 + * Display the delete tool for own event messages
566 + *#
567 +#macro(toolDelete $eventID)
568 + <a class='toolDelete' href="$doc.getURL($xcontext.action, "$!{request.queryString}&amp;xaction=deleteMessage&amp;id=$eventID&amp;xredirect=$escapetool.url($doc.getURL($xcontext.action, $request.queryString))")" title="$services.localization.render('xe.activity.messages.delete')">x</a>
569 +#end
570 +#**
571 + * Display RSS URL
572 + *#
573 +#macro(displayRSSURL)
574 + #set ($rssURL = '')
575 + #set ($parameters = 'xpage=plain&outputSyntax=plain')
576 + #foreach ($tag in $aTags)
577 + #set ($parameters = "${parameters}&tag=$escapetool.url($tag)")
578 + #end
579 + #foreach ($space in $aSpaces)
580 + #set ($parameters = "${parameters}&space=$escapetool.url($space)")
581 + #end
582 + #set ($rssURL = $xwiki.getURL('Main.WebRss', 'view', $parameters))
583 + <div class="rssURL">
584 + <a href="${rssURL}"><img src="$xwiki.getSkinFile('icons/silk/feed.png')" alt="RSS icon"/>$services.localization.render('xe.activity.rssfeed')</a>
585 + </div>
586 +#end
587 +#**
588 + * Display mimetype and name for the given event of type attachment
589 + *#
590 +#macro(displayAttachment $event)
591 + #set ($pageDoc = $xwiki.getDocument($event.page))
592 + #set ($attach = $pageDoc.getAttachment($event.param2))
593 + <div class="attachment">
594 + #mimetypeimg($attach.getMimeType().toLowerCase() $event.param2)
595 + #if ($attach.version)
596 + <a href="$pageDoc.getAttachmentURL($event.param2)">$!{escapetool.xml($event.param2)}</a>
597 + #else
598 + $!{escapetool.xml($event.param2)}
599 + #end
600 + </div>
601 +#end
602 +#**
603 + * Display a user message
604 + *#
605 +#macro(displayMessage $event)
606 + <div class="message">$!{escapetool.xml($event.getBody())}</div>
607 +#end
608 +#**
609 + * Display comment
610 + *#
611 +#macro(displayComment $event)
612 + #getCommentContent($event $commentContent)
613 + #if ("$!commentContent" != '')
614 + <div class="comment">
615 + $doc.getRenderedContentRestricted($commentContent, $doc.getSyntax().toIdString())
616 + </div>
617 + #end
618 +#end
619 +#**
620 + * Display annotation
621 + *#
622 +#macro(displayAnnotation $event)
623 + #getAnnotation($event $annotationText $annotationSelection)
624 + #if ("$!annotationText" != '' || "$!annotationSelection" != '')
625 + <div class="annotated">$!{escapetool.xml($annotationSelection)}</div>
626 + <div class="annotation">$!{doc.getRenderedContentRestricted($annotationText, $annotationTextSyntaxId)}</div>
627 + #end
628 +#end
629 +#**
630 + * Display user avatar and action message
631 + *#
632 +#macro(activitySnapshot $eventUser $eventType)
633 + <span class="activitySnapshot">
634 + #if ($eventUser != 'XWiki.XWikiGuest')
635 + <a href="$xwiki.getURL($eventUser)">#mediumUserAvatar("$!eventUser")</a>
636 + #else
637 + #mediumUserAvatar("$!eventUser")
638 + #end
639 + <img class="activityActionAvatar" src="$xwiki.getSkinFile($activityActionsMap.get($eventType), true)" alt="$services.localization.render("xe.activity.action.${eventType}", [$relatedEventsNo])" title="$services.localization.render("xe.activity.action.${eventType}", [$relatedEventsNo])" />
640 + </span>
641 +#end
642 +#**
643 + * Display the summary (avatars with action icon) for the current entry
644 + *#
645 +#macro(displayActivityListItemSummary)
646 + <div class="activitySummary">
647 + #foreach ($event in $events)
648 + #set ($relatedEventsNo = $xwiki.activitystream.getRelatedEvents($event).size() / 2)
649 + #activitySnapshot($event.user $event.type)
650 + #end
651 + </div>
652 +#end
653 +#**
654 + * Check if the current subentry is expandable
655 + *#
656 +#macro(isEventEntryExpandable $event)
657 + #set ($isEventEntryExpandable = false)
658 + #if ($isAnnotationEvent)
659 + #getAnnotation($event $annotationText $annotationSelection)
660 + #if ("$!annotationText" != '' || "$!annotationSelection" != '')
661 + #set ($isEventEntryExpandable = true)
662 + #end
663 + #elseif ($isAttachmentEvent && "$!event.param2" != '')
664 + #set ($isEventEntryExpandable = true)
665 + #elseif ($isCommentEvent)
666 + #getCommentContent($event $commentContent)
667 + #if ("$!commentContent" != '')
668 + #set ($isEventEntryExpandable = true)
669 + #end
670 + #end
671 +#end
672 +#**
673 + * Display the content for the current event
674 + *#
675 +#macro(displayEventContent)
676 + #set ($isEventEntryExpandable = false)
677 + #isAttachmentEvent($event)
678 + #isEventEntryExpandable($event)
679 + <li class="activityItem #if($isEventEntryExpandable) collapsed #end">
680 + #displayActivityListItemHeader(false false $isEventEntryExpandable)
681 + #if ($isEventEntryExpandable || $isMessageEvent)
682 + <div class="activityContent">
683 + #if ($isAttachmentEvent)
684 + #foreach ($event in $xwiki.activitystream.getRelatedEvents($event))
685 + #isAttachmentEvent($event)
686 + #if ($isAttachmentEvent)
687 + #displayAttachment($event)
688 + #end
689 + #end
690 + #end
691 + #if ($isMessageEvent)
692 + #displayMessage($event)
693 + #elseif ($isCommentEvent)
694 + #displayComment($event)
695 + #elseif ($isAnnotationEvent && $isEventEntryExpandable)
696 + #displayAnnotation($event)
697 + #end
698 + </div>
699 + #end
700 + </li>
701 + #set ($previousEventType = $event.type)
702 + #set ($previousEventUser = $event.user)
703 +#end
704 +#**
705 + * Display the header structure for the given event
706 + * @param isPageEntryExpandable if true, the curent item is expandable
707 + * @param isFirstEntry if true, the current item is the first entry of a list formed by a single event -> do not display user avatar
708 + *#
709 +#macro(displayActivityListItemHeader $isPageEntryExpandable $isFirstEntry $isEventEntryExpandable)
710 + #set ($relatedEventsNo = $xwiki.activitystream.getRelatedEvents($event).size() / 2)
711 + <div class="activityHeader">
712 + #if (!$isPageEntryExpandable && !$isFirstEntry)
713 + #activitySnapshot($event.user $event.type)
714 + #end
715 + <span class="activityAuthor">
716 + #if ($isFirstEntry)
717 + #if ($event.application != 'MessageStream')
718 + #set ($pageFullName = "$!{event.wiki}:$!{pageName}")
719 + #set ($document = $xwiki.getDocument($pageFullName))
720 + #set ($pageTitle = "$!{document.plainTitle}")
721 + #if ($pageTitle == '')
722 + #set ($pageTitle = $pageName)
723 + #end
724 +
725 + ##
726 + ## Build the page's location
727 + ##
728 + #set ($pageLocation = "#hierarchy($document.documentReference, {'plain' : true})")
729 + ## Trim the result since the macro's output contains unwanted spaces from the velocity code's indentation.
730 + #set ($pageLocation = $pageLocation.trim())
731 +
732 + <a href="$xwiki.getURL(${pageFullName})" class="typePage type" title="$!{escapetool.xml($services.localization.render('activity.page.link.title', [$pageLocation]))}">$!{escapetool.xml($pageTitle)}</a>
733 + #if ($event.wiki != $services.wiki.currentWikiId)
734 + #set($wikiPrettyName = $services.wiki.getById($event.wiki).prettyName)
735 + <span class="activityWiki">($!{escapetool.xml($wikiPrettyName)})</span>
736 + #end
737 + #end
738 + #else
739 + $xwiki.getUserName($!event.user)
740 + #end
741 + </span>
742 + #if(!$isFirstEntry || $isPageEntryExpandable)
743 + <span class="activityAction">
744 + #isAttachmentEvent($event)
745 + #if ($isPageEntryExpandable)## display entry summary
746 + $!services.localization.render('xe.activity.action.summary', [$events.size(), $eventUsers.size()])
747 + #else
748 + $!services.localization.render("xe.activity.action.${event.type}", [$relatedEventsNo])
749 + #if ($event.type == 'update' && $event.version != '1.1')
750 + #set ($pageFullName = "$!{event.wiki}:$!{pageName}")
751 + &#183; <a href="$xwiki.getDocument($pageFullName).getURL('view', "viewer=changes&rev2=${event.version}")">$services.localization.render('xe.activity.action.seechanges')</a>
752 + #end
753 + #end
754 + </span>
755 + #end
756 + #if ("$!timeAgoMsg" != '')
757 + <span class='activityTime' title="$eventFullDate">$!timeAgoMsg</span>
758 + #end
759 + #if ($isPageEntryExpandable)
760 + #toolExpand('')
761 + #displayActivityListItemSummary()
762 + #end
763 + #if ($isEventEntryExpandable)
764 + #toolExpand('')
765 + #end
766 + ## Users can delete their own messages from the stream
767 + #if (($event.application == 'MessageStream') && $isFirstEntry && ($xwiki.getDocument("$!{event.user}").prefixedFullName == $xwiki.getDocument("$!{xcontext.user}").prefixedFullName))
768 + #toolDelete($event.getEventId())
769 + #end
770 + </div>
771 +#end
772 +#**
773 + * Display the list of events for the current page
774 + *#
775 +#macro(displayEvents)
776 + ##
777 + ## Display events for the current page
778 + ## -------------------------------------------------------------------
779 + #foreach ($event in $events)
780 + ##
781 + ## Event variables
782 + ## -------------------------------------------------------------------
783 + #initializeEvent()
784 + ##
785 + ## Date header + Start list
786 + ## -------------------------------------------------------------------
787 + #if ($isToday && $displayToday)
788 + #if ($closeList)## there are events from the future
789 + </ul>
790 + #else
791 + #set ($closeList = true)## a new list will be opened
792 + #end
793 + <h2>$services.localization.render('timeAgo.today')</h2><ul class='activityList'> ## display 'Today' message
794 + #set ($displayToday = false)
795 + #elseif ($isYesterday && $displayYesterday)## display 'Yesterday' message
796 + #if ($closeList)## there are events today
797 + </ul>
798 + #else
799 + #set ($closeList = true)## a new list will be opened
800 + #end
801 + <h2>$services.localization.render('timeAgo.yesterday')</h2><ul class='activityList'>
802 + #set ($displayYesterday = false)
803 + #elseif (!$isToday && !$isYesterday && $iterationDate != $displayDate)## display date message
804 + #set ($displayDate = $iterationDate)
805 + #if ($closeList)## there are events today or/and yesterday
806 + </ul>
807 + #else
808 + #set ($closeList = true)## a new list will be opened
809 + #end
810 + <h2>$!iterationDate</h2><ul class='activityList'>
811 + #end
812 + ##
813 + ## Open event list + Display first event
814 + ## -------------------------------------------------------------------
815 + #if ($velocityCount == 1)
816 + <li class="activityItem activityPage #if($isPageEntryExpandable)collapsed#end">
817 + #displayActivityListItemHeader($isPageEntryExpandable true false)
818 + #if (!$isPageEntryExpandable)
819 + ## single entry
820 + #set($timeAgoMsg = '')
821 + #end
822 + <div class="activityContent">
823 + <ul class="activityList">
824 + #end
825 + ##
826 + ## Display other events (inside the expanded list)
827 + ## -------------------------------------------------------------------
828 + #displayEventContent()
829 + ##
830 + ## Close page event list
831 + ## -------------------------------------------------------------------
832 + #if ($velocityCount == $events.size())
833 + </ul>
834 + </div>
835 + </li>
836 + #end
837 + #end
838 +#end
839 +#**
840 + * Display activity event list
841 + *#
842 +#macro (displayActivity)
843 + #set ($iterationDate = '')
844 + #set ($displayDate = '')
845 + #set ($iterationDocuments = [])
846 + #set ($displayToday = true)
847 + #set ($displayYesterday = true)
848 + #set ($closeList = false)
849 + ##
850 + ## Get distinct pages with activity
851 + ## -------------------------------------------------------------------
852 + #set ($noEventsShown = true)
853 + #set ($pages = $xwiki.activitystream.searchDailyPages("$!filterClause", $filterParameters, $aEntries, 0))
854 + #if ($pages.size() > 0)
855 + #foreach ($page in $pages)
856 + ##
857 + ## Extract page name and event date
858 + ## -------------------------------------------------------------------
859 + #set ($pageName = $page.get(0))
860 + #set ($pageDate = $page.get(1))
861 + #set ($pageWiki = $page.get(2))
862 + ## If the affected document name matches ".*:XWiki[.][0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}" then it is a GUID corresponding to message stream events, and not a real document.
863 + ## Always assume access to messages. For the rest of the documents, check access.
864 + #if ($pageName.matches(".*:XWiki[.][0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}") || ($xwiki.hasAccessLevel('view', $xcontext.user, "${xcontext.database}:${pageName}") && !$iterationDocuments.contains($pageName)))
865 + ##
866 + ## Get event list for the current page
867 + ## -------------------------------------------------------------------
868 + #set ($whereClause = " AND $!filterClause AND day(date)=" + ${xwiki.formatDate($pageDate, 'd')})
869 + #set ($whereClause = $whereClause + " AND month(date)=" + ${xwiki.formatDate($pageDate, 'M')})
870 + #set ($whereClause = $whereClause + " AND year(date)=" + ${xwiki.formatDate($pageDate, 'yyyy')})
871 + #set ($whereClause = "act.wiki=? AND act.page=? AND act.space<>'Scheduler'" + $!whereClause)
872 + #set ($subqueryParameters = [$pageWiki, $pageName])
873 + #set ($discard = $subqueryParameters.addAll($filterParameters))
874 + #set ($events= $xwiki.activitystream.searchEvents("$!whereClause", false, 0, 0, $subqueryParameters))
875 + #if ($events.size() > 0)
876 + ##
877 + ## Initialize page event vars
878 + ## -------------------------------------------------------------------
879 + #removeDuplicatedEvents()
880 + #isPageEntryExpandable()
881 + #getUniqueEventUsers()
882 + #displayEvents()
883 + #set($noEventsShown = false)
884 + #end
885 + #end
886 + #end
887 + #if ($closeList)## close the last event list
888 + </ul>
889 + #end
890 + #end
891 + #if ($noEventsShown)
892 + <span class="noitems">$services.localization.render('xe.activity.noentries')</span>
893 + #end
894 + ##
895 + ## Display RSS URL
896 + ## -------------------------------------------------------------------
897 + #if ("$!aRSS" == 'true')
898 + #displayRSSURL()
899 + #end
900 +#end
901 +#**
902 + * Handle a message (delete) request if the MessageStream module is active.
903 + * @param displayUI output parameter that is true if the Activity Stream UI should be displayed, false otherwise.
904 + * Deppending on the current request, handling the message action could not require displaying the UI (and making all the Activity Stream queries).
905 + *#
906 +#macro(handleMessageAction $displayUI)
907 + #set ($displayUI = $NULL)
908 + #setVariable("$displayUI", true)
909 + #if ($isMessageStreamActive)
910 + #set ($xredirect = $doc.getURL($xcontext.action, $!{request.queryString}))
911 + #set ($errorMessageKeyPrefix = "activity.deleteMessage.error.")
912 + #set ($errorMessage = $request.session.getAttribute("${errorMessageKeyPrefix}${xredirect}"))
913 + #if ("$!errorMessage" != '')
914 + ## Clean the error and display the message.
915 + #set ($discard = $request.session.removeAttribute("${errorMessageKeyPrefix}${xredirect}"))
916 + {{error}}$errorMessage{{/error}}
917 +
918 + #elseif ("$!{request.xaction}" == 'deleteMessage')
919 + #if ($request.confirm == '1')
920 + #if (!$services.messageStream.deleteMessage(${request.id}))
921 + #if ($request.xpage == 'plain')
922 + $response.setStatus(403)## Forbidden
923 + {{translation key="xe.activity.messages.delete.failed"/}}
924 + #elseif ("$!{request.xredirect}" != '')
925 + ## Pass the error message in the xredirect so that we display it in the calling page and not in the macro page.
926 + $request.session.setAttribute("${errorMessageKeyPrefix}${request.xredirect}", $services.localization.render('xe.activity.messages.delete.failed'))
927 + $response.sendRedirect($request.xredirect)
928 + #else
929 + {{error}}{{translation key="xe.activity.messages.delete.failed"/}}{{/error}}
930 +
931 + #end
932 + #elseif ("$!{request.xredirect}" != '')
933 + $response.sendRedirect(${request.xredirect})
934 + #elseif ($request.xpage == 'plain')
935 + ## Just print something for confirmation.
936 + {{translation key="xe.activity.messages.delete.success"/}}
937 + #end
938 + ## Suppress the UI to avoid useless DB or network access.
939 + #if ($request.xpage == 'plain' || "$!{request.xredirect}" != '')
940 + #set ($displayUI = $NULL)
941 + #setVariable("$displayUI", false)
942 + #end
943 + #else
944 + {{html}}
945 + <form action="$doc.getURL($xcontext.action, $!request.queryString))">
946 + <div class='xdialog-box xdialog-box-confirmation'>
947 + <div class='xdialog-content'>
948 + <div class='question'>$services.localization.render('xe.activity.messages.delete.confirm')</div>
949 + <div class='buttons'>
950 + <input type='hidden' name='confirm' value='1'/>
951 + <input type='hidden' name='xaction' value='deleteMessage'/>
952 + <input type='hidden' name='id' value="$!request.id"/>
953 + <input type='hidden' name='xredirect' value="$!request.xredirect"/>
954 + <span class='xbuttonwrapper'><input type='submit' value="$services.localization.render('core.widgets.confirmationBox.button.yes')" class='button'/></span>
955 + <a class='secondary button' href="$!request.xredirect"/>$services.localization.render('core.widgets.confirmationBox.button.no')</a>
956 + </div>
957 + </div>
958 + </div>
959 + </form>
960 + {{/html}}
961 +
962 + #end
963 + #end
964 + #end
965 +#end
966 +#**
967 + * Preserves Activity Stream's state (values of macro parameters) in the updateURL that is to be used if the ActivityStream needs to be refreshed.
968 + **#
969 +#macro (addStateHiddenInput)
970 + #set ($extraQueryString = '')
971 + #foreach ($param in $xcontext.macro.params.getParameterNames())
972 + #set ($value = "$!{xcontext.macro.params.get($param)}")
973 + #if ($value != '')
974 + #foreach($piece in $value.split('\s*,\s*'))
975 + #set($extraQueryString = "${param}=$!{escapetool.url($piece)}&$!{extraQueryString}")
976 + #end
977 + #end
978 + #end
979 + #set ($updateURL = $xwiki.getURL('Main.Activity', 'view', "$!{extraQueryString}&xpage=plain"))
980 + <input type='hidden' class='updateURL' value="$updateURL" />
981 +#end
982 +{{/velocity}}
983 +
984 +{{velocity}}
985 +#set ($displayUI = true)
986 +#handleMessageAction($displayUI)
987 +#if ($displayUI)
988 + {{html}}
989 + <div class='activity'>
990 + #initDateVariables()
991 + #addStateHiddenInput()
992 + #displayActivity()
993 + </div>
994 + {{/html}}
995 +#end
996 +{{/velocity}}
Macro content type
... ... @@ -1,0 +1,1 @@
1 +No content
Default category
... ... @@ -1,0 +1,1 @@
1 +Content
Macro description
... ... @@ -1,0 +1,1 @@
1 +The Activity Macro provides information about recent activities done by the users inside the XWiki instance. It lists the create, edit and delete events for pages, as well as comments, attachments and annotations.
Macro id
... ... @@ -1,0 +1,1 @@
1 +activity
Macro name
... ... @@ -1,0 +1,1 @@
1 +Activity
Supports inline mode
... ... @@ -1,0 +1,1 @@
1 +No
Macro visibility
... ... @@ -1,0 +1,1 @@
1 +Current Wiki
XWiki.WikiMacroParameterClass[0]
Parameter default value
... ... @@ -1,0 +1,1 @@
1 +20
Parameter description
... ... @@ -1,0 +1,1 @@
1 +Number of entries to display the activity for.
Parameter mandatory
... ... @@ -1,0 +1,1 @@
1 +No
Parameter name
... ... @@ -1,0 +1,1 @@
1 +entries
XWiki.WikiMacroParameterClass[1]
Parameter default value
... ... @@ -1,0 +1,1 @@
1 +10
Parameter description
... ... @@ -1,0 +1,1 @@
1 +Number of activities to show for each entry.
Parameter mandatory
... ... @@ -1,0 +1,1 @@
1 +No
Parameter name
... ... @@ -1,0 +1,1 @@
1 +subentries
XWiki.WikiMacroParameterClass[2]
Parameter description
... ... @@ -1,0 +1,1 @@
1 +Comma separated list of wikis to display activity for.
Parameter mandatory
... ... @@ -1,0 +1,1 @@
1 +No
Parameter name
... ... @@ -1,0 +1,1 @@
1 +wikis
XWiki.WikiMacroParameterClass[3]
Parameter description
... ... @@ -1,0 +1,1 @@
1 +Comma separated list of local serialized references of the spaces to display the activity for.
Parameter mandatory
... ... @@ -1,0 +1,1 @@
1 +No
Parameter name
... ... @@ -1,0 +1,1 @@
1 +spaces
XWiki.WikiMacroParameterClass[4]
Parameter description
... ... @@ -1,0 +1,1 @@
1 +Comma separated list of authors whose modifications to show.
Parameter mandatory
... ... @@ -1,0 +1,1 @@
1 +No
Parameter name
... ... @@ -1,0 +1,1 @@
1 +authors
XWiki.WikiMacroParameterClass[5]
Parameter description
... ... @@ -1,0 +1,1 @@
1 +Comma separated list of tags to display activity for.
Parameter mandatory
... ... @@ -1,0 +1,1 @@
1 +No
Parameter name
... ... @@ -1,0 +1,1 @@
1 +tags
XWiki.WikiMacroParameterClass[6]
Parameter default value
... ... @@ -1,0 +1,1 @@
1 +false
Parameter description
... ... @@ -1,0 +1,1 @@
1 +Whether to show modifications that create minor versions or not.
Parameter mandatory
... ... @@ -1,0 +1,1 @@
1 +No
Parameter name
... ... @@ -1,0 +1,1 @@
1 +minor
XWiki.WikiMacroParameterClass[7]
Parameter default value
... ... @@ -1,0 +1,1 @@
1 +false
Parameter description
... ... @@ -1,0 +1,1 @@
1 +Whether to show activity RSS link or not.
Parameter mandatory
... ... @@ -1,0 +1,1 @@
1 +No
Parameter name
... ... @@ -1,0 +1,1 @@
1 +rss