Changes for page Activity Stream
Last modified by superadmin on 2017/11/28 14:52
From empty
To version 1.1
edited by superadmin
on 2017/11/28 14:52
on 2017/11/28 14:52
Change comment: Install extension [org.xwiki.platform:xwiki-platform-activitystream-ui/9.9]
Summary
-
Page properties (6 modified, 0 added, 0 removed)
-
Objects (0 modified, 12 added, 0 removed)
- XWiki.JavaScriptExtension[0]
- XWiki.RequiredRightClass[0]
- XWiki.StyleSheetExtension[0]
- XWiki.WikiMacroClass[0]
- XWiki.WikiMacroParameterClass[0]
- XWiki.WikiMacroParameterClass[1]
- XWiki.WikiMacroParameterClass[2]
- XWiki.WikiMacroParameterClass[3]
- XWiki.WikiMacroParameterClass[4]
- XWiki.WikiMacroParameterClass[5]
- XWiki.WikiMacroParameterClass[6]
- XWiki.WikiMacroParameterClass[7]
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.XWikiGuest1 +xwiki:XWiki.superadmin - Syntax
-
... ... @@ -1,1 +1,1 @@ 1 -XWiki 2. 11 +XWiki 2.0 - Hidden
-
... ... @@ -1,1 +1,1 @@ 1 - false1 +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'>▼</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}&xaction=deleteMessage&id=$eventID&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 + · <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