diff --git a/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-filters/xwiki-platform-notifications-filters-api/src/main/java/org/xwiki/notifications/filters/internal/scope/ScopeNotificationFilterPreference.java b/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-filters/xwiki-platform-notifications-filters-api/src/main/java/org/xwiki/notifications/filters/internal/scope/ScopeNotificationFilterPreference.java index e65673d3294feaebf07343b0349aa57207791ae1..465736a63305fb1b7a330dee6f3562b507e7cc70 100644 --- a/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-filters/xwiki-platform-notifications-filters-api/src/main/java/org/xwiki/notifications/filters/internal/scope/ScopeNotificationFilterPreference.java +++ b/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-filters/xwiki-platform-notifications-filters-api/src/main/java/org/xwiki/notifications/filters/internal/scope/ScopeNotificationFilterPreference.java @@ -26,6 +26,7 @@ import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; import org.xwiki.model.EntityType; import org.xwiki.model.reference.EntityReference; import org.xwiki.model.reference.EntityReferenceResolver; @@ -248,4 +249,10 @@ public int hashCode() .append(children) .toHashCode(); } + + @Override + public String toString() + { + return ToStringBuilder.reflectionToString(this); + } } diff --git a/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-macro/xwiki-platform-notifications-macro-ui/src/main/resources/XWiki/Notifications/Code/Macro/NotificationsMacro.xml b/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-macro/xwiki-platform-notifications-macro-ui/src/main/resources/XWiki/Notifications/Code/Macro/NotificationsMacro.xml index 562713df8e5881f75abcac0ee9b93ce5571a4325..efd1e8e944e0d849c23f80c230c7cd04724e0e86 100644 --- a/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-macro/xwiki-platform-notifications-macro-ui/src/main/resources/XWiki/Notifications/Code/Macro/NotificationsMacro.xml +++ b/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-macro/xwiki-platform-notifications-macro-ui/src/main/resources/XWiki/Notifications/Code/Macro/NotificationsMacro.xml @@ -1127,6 +1127,7 @@ "spaces": "$!escapetool.xml($xcontext.macro.params.spaces)", "pages": "$!escapetool.xml($xcontext.macro.params.pages)", "users": "$!escapetool.xml($users)", + "currentWiki": "$!escapetool.xml($services.wiki.currentWikiId)", "tags": "$!escapetool.xml($xcontext.macro.params.tags)" }) #set ($dataParameters = []) diff --git a/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-sources/src/main/java/org/xwiki/notifications/sources/NotificationParameters.java b/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-sources/src/main/java/org/xwiki/notifications/sources/NotificationParameters.java index 0a5531cf9e58925e47b4f8e91dab59cfe9b3e3c8..fdfcb4c98356c2db1c0462ae32ba7331c3b766cd 100644 --- a/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-sources/src/main/java/org/xwiki/notifications/sources/NotificationParameters.java +++ b/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-sources/src/main/java/org/xwiki/notifications/sources/NotificationParameters.java @@ -23,6 +23,7 @@ import java.util.Collection; import java.util.Date; +import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.xwiki.model.reference.DocumentReference; @@ -132,4 +133,10 @@ public int hashCode() .append(filters) .toHashCode(); } + + @Override + public String toString() + { + return ToStringBuilder.reflectionToString(this); + } } diff --git a/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-sources/src/main/java/org/xwiki/notifications/sources/internal/DefaultNotificationParametersFactory.java b/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-sources/src/main/java/org/xwiki/notifications/sources/internal/DefaultNotificationParametersFactory.java index f47f98a21f87b97b747af672f879d8ba72b2469c..127cefd3dafea51348668e24a242be27b81d802b 100644 --- a/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-sources/src/main/java/org/xwiki/notifications/sources/internal/DefaultNotificationParametersFactory.java +++ b/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-sources/src/main/java/org/xwiki/notifications/sources/internal/DefaultNotificationParametersFactory.java @@ -30,6 +30,7 @@ import java.util.stream.Collectors; import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Singleton; import org.apache.commons.lang3.StringUtils; @@ -39,8 +40,11 @@ import org.xwiki.eventstream.EventStreamException; import org.xwiki.eventstream.RecordableEventDescriptor; import org.xwiki.eventstream.RecordableEventDescriptorManager; +import org.xwiki.model.EntityType; import org.xwiki.model.reference.DocumentReferenceResolver; +import org.xwiki.model.reference.EntityReference; import org.xwiki.model.reference.EntityReferenceResolver; +import org.xwiki.model.reference.EntityReferenceSerializer; import org.xwiki.notifications.NotificationConfiguration; import org.xwiki.notifications.NotificationException; import org.xwiki.notifications.NotificationFormat; @@ -58,7 +62,6 @@ import org.xwiki.notifications.filters.internal.user.OwnEventFilter; import org.xwiki.notifications.preferences.NotificationPreferenceManager; import org.xwiki.notifications.sources.NotificationParameters; -import org.xwiki.wiki.descriptor.WikiDescriptorManager; /** * This component aims at producing {@link org.xwiki.notifications.sources.NotificationParameters} instances @@ -81,6 +84,13 @@ public class DefaultNotificationParametersFactory @Inject private EntityReferenceResolver<String> entityReferenceResolver; + @Inject + @Named("relative") + private EntityReferenceResolver<String> relativeEntityReferenceResolver; + + @Inject + private EntityReferenceSerializer<String> entityReferenceSerializer; + @Inject private NotificationConfiguration configuration; @@ -96,9 +106,6 @@ public class DefaultNotificationParametersFactory @Inject private NotificationFilterPreferenceManager notificationFilterPreferenceManager; - @Inject - private WikiDescriptorManager wikiDescriptorManager; - @Inject private UsersParameterHandler usersParameterHandler; @@ -217,6 +224,7 @@ public enum ParametersKey { * This is used in case of multiple wikis. */ // TODO: not sure we should keep it, I put it since it was already in the REST API. + // if we are sure we can always get the current wiki from the context, we should do it. CURRENT_WIKI(false); private boolean isDirectlyUsed; @@ -266,8 +274,8 @@ static ParametersKey valueOfIgnoreCase(String name) /** * Create a notification parameters with a map of parameters indexed by strings. * The keys of the map should be any variant of {@link ParametersKey} names: camel case is accepted. - * The {@link NotificationParameters} is created then using {@link #createNotificationParameters(Map)} i - * mplementation. This method is mainly provided as a helper in order to be allowed to use this factory with + * The {@link NotificationParameters} is created then using {@link #createNotificationParameters(Map)} + * implementation. This method is mainly provided as a helper in order to be allowed to use this factory with * velocity scripts easily. * * @param parameters the map of parameters with String as indexes. @@ -402,24 +410,14 @@ private void dontUseUserPreferences(NotificationParameters notificationParameter .filter(filter -> !excludedFilters.contains(filter.getName())).collect(Collectors.toList()); enableAllEventTypes(notificationParameters); + + final String currentWiki = parameters.get(ParametersKey.CURRENT_WIKI); handleLocationParameter(parameters.get(ParametersKey.PAGES), - notificationParameters, NotificationFilterProperty.PAGE); + notificationParameters, NotificationFilterProperty.PAGE, currentWiki); handleLocationParameter(parameters.get(ParametersKey.SPACES), - notificationParameters, NotificationFilterProperty.SPACE); - - String wikis = parameters.get(ParametersKey.WIKIS); - String currentWiki = parameters.get(ParametersKey.CURRENT_WIKI); - handleLocationParameter(wikis, notificationParameters, NotificationFilterProperty.WIKI); - // When the notifications are displayed in a macro in a subwiki, we assume they should not contain events from - // other wikis (except if the "wikis" parameter is set). - // The concept of the subwiki is to restrict a given domain of interest into a given wiki, this is why it does - // not make sense to show events from other wikis in a "timeline" such as the notifications macro. - // TODO: add a "handleAllWikis" parameter to disable this behaviour - // Note that on the main wiki, which is often a "portal" for all the others wikis, we assure it's OK to display - // events from other wikis. - if (StringUtils.isBlank(wikis) && !StringUtils.equals(currentWiki, wikiDescriptorManager.getMainWikiId())) { - handleLocationParameter(currentWiki, notificationParameters, NotificationFilterProperty.WIKI); - } + notificationParameters, NotificationFilterProperty.SPACE, currentWiki); + handleLocationParameter(parameters.get(ParametersKey.WIKIS), + notificationParameters, NotificationFilterProperty.WIKI, currentWiki); usersParameterHandler.handleUsersParameter(parameters.get(ParametersKey.USERS), notificationParameters); @@ -437,7 +435,7 @@ private void handleTagsParameter(NotificationParameters parameters, String tags, } private void handleLocationParameter(String locations, NotificationParameters parameters, - NotificationFilterProperty property) + NotificationFilterProperty property, String currentWiki) { if (StringUtils.isNotBlank(locations)) { Set<NotificationFormat> formats = new HashSet<>(); @@ -457,10 +455,10 @@ private void handleLocationParameter(String locations, NotificationParameters pa pref.setWiki(locationArray[i]); break; case SPACE: - pref.setPage(locationArray[i]); + pref.setPage(makeReferenceAbsolute(locationArray[i], EntityType.SPACE, currentWiki)); break; case PAGE: - pref.setPageOnly(locationArray[i]); + pref.setPageOnly(makeReferenceAbsolute(locationArray[i], EntityType.PAGE, currentWiki)); break; default: break; @@ -470,6 +468,28 @@ private void handleLocationParameter(String locations, NotificationParameters pa } } + /** + * add the current wiki to the reference if it is missing an explicit wiki reference. + * @param entityRefStr the reference to check + * @param entityType the (expected) type of the reference + * @param currentWiki the wiki to add to the reference, if missing + * @return a string representation if a reference with an explicit wiki + */ + private String makeReferenceAbsolute(String entityRefStr, EntityType entityType, String currentWiki) + { + if (relativeEntityReferenceResolver == null) { + throw new NullPointerException("relativeEntityReferenceResolver"); + } + EntityReference entityRef = relativeEntityReferenceResolver.resolve(entityRefStr, entityType); + if (entityRef == null) { + throw new NullPointerException("relativeEntityReferenceResolver.resolve(" + entityRefStr + "," + entityType+")"); + } + if (entityRef.extractReference(EntityType.WIKI) == null) { + entityRef = entityReferenceResolver.resolve(entityRefStr, entityType, new EntityReference(currentWiki, EntityType.WIKI)); + } + return entityReferenceSerializer.serialize(entityRef); + } + private void enableAllEventTypes(NotificationParameters parameters) throws EventStreamException { parameters.preferences.clear(); diff --git a/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-sources/src/test/java/org/xwiki/notifications/sources/internal/DefaultNotificationParametersFactoryTest.java b/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-sources/src/test/java/org/xwiki/notifications/sources/internal/DefaultNotificationParametersFactoryTest.java index 2adcdc1b82d84b2c8d373607e7c646c6beaecd73..3d678ba7900f71934b78a8854da9522b0d0bab19 100644 --- a/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-sources/src/test/java/org/xwiki/notifications/sources/internal/DefaultNotificationParametersFactoryTest.java +++ b/xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-sources/src/test/java/org/xwiki/notifications/sources/internal/DefaultNotificationParametersFactoryTest.java @@ -27,13 +27,20 @@ import java.util.List; import java.util.Map; +import javax.inject.Named; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.xwiki.eventstream.RecordableEventDescriptor; import org.xwiki.eventstream.RecordableEventDescriptorManager; +import org.xwiki.model.EntityType; import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.DocumentReferenceResolver; +import org.xwiki.model.reference.EntityReference; import org.xwiki.model.reference.EntityReferenceResolver; +import org.xwiki.model.reference.EntityReferenceSerializer; import org.xwiki.notifications.NotificationConfiguration; import org.xwiki.notifications.NotificationException; import org.xwiki.notifications.NotificationFormat; @@ -64,6 +71,7 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.isNotNull; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -104,6 +112,13 @@ public class DefaultNotificationParametersFactoryTest @MockComponent private EntityReferenceResolver<String> entityReferenceResolver; + @MockComponent + @Named("relative") + private EntityReferenceResolver<String> relativeEntityReferenceResolver; + + @MockComponent + private EntityReferenceSerializer<String> entityReferenceSerializer; + @MockComponent private UsersParameterHandler usersParameterHandler; @@ -168,6 +183,38 @@ public void setup(MockitoComponentManager componentManager) throws Exception when(recordableEventDescriptorManager.getRecordableEventDescriptors(true)) .thenReturn(recordableEventDescriptors); + + when(relativeEntityReferenceResolver.resolve(isNotNull(), isNotNull())) + .thenAnswer(new Answer<EntityReference>() { + public EntityReference answer(InvocationOnMock invocation) throws Throwable { + String pageName = invocation.getArgument(0, String.class); + EntityType type = invocation.getArgument(1, EntityType.class); + return new EntityReference(pageName, type); + } + }); + + when(entityReferenceResolver.resolve(isNotNull(), isNotNull(), isNotNull())) + .thenAnswer(new Answer<EntityReference>() { + public EntityReference answer(InvocationOnMock invocation) throws Throwable { + String pageName = invocation.getArgument(0, String.class); + EntityType type = invocation.getArgument(1, EntityType.class); + EntityReference parent = invocation.getArgument(2, EntityReference.class); + return new EntityReference(pageName, type, parent); + } + }); + + when(entityReferenceSerializer.serialize(isNotNull())) + .thenAnswer(new Answer<String>() { + public String answer(InvocationOnMock invocation) throws Throwable { + EntityReference param = invocation.getArgument(0, EntityReference.class); + EntityReference parent = param.getParent(); + if (parent != null) { + assertEquals(EntityType.WIKI, parent.getType()); + return parent.getName() + "@@" + param.getName(); + } + return param.getName(); + } + }); } @Test @@ -251,27 +298,27 @@ public void createNotificationParameters() throws NotificationException List<NotificationFilterPreference> notificationFilterPreferences = new ArrayList<>(); DefaultNotificationFilterPreference filterPref = getFilterPreference("PAGE", 0); - filterPref.setPageOnly("a"); + filterPref.setPageOnly("mywiki@@a"); notificationFilterPreferences.add(new ScopeNotificationFilterPreference(filterPref, this.entityReferenceResolver)); filterPref = getFilterPreference("PAGE", 1); - filterPref.setPageOnly("b"); + filterPref.setPageOnly("mywiki@@b"); notificationFilterPreferences.add(new ScopeNotificationFilterPreference(filterPref, this.entityReferenceResolver)); filterPref = getFilterPreference("PAGE", 2); - filterPref.setPageOnly("c"); + filterPref.setPageOnly("mywiki@@c"); notificationFilterPreferences.add(new ScopeNotificationFilterPreference(filterPref, this.entityReferenceResolver)); filterPref = getFilterPreference("SPACE", 0); - filterPref.setPage("space1"); + filterPref.setPage("mywiki@@space1"); notificationFilterPreferences.add(new ScopeNotificationFilterPreference(filterPref, this.entityReferenceResolver)); filterPref = getFilterPreference("SPACE", 1); - filterPref.setPage("space2"); + filterPref.setPage("mywiki@@space2"); notificationFilterPreferences.add(new ScopeNotificationFilterPreference(filterPref, this.entityReferenceResolver));