diff --git a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/pom.xml b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/pom.xml index 974eef64d12e..085480ca315b 100644 --- a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/pom.xml +++ b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/pom.xml @@ -170,5 +170,19 @@ ${project.version} test + + + org.xwiki.platform + xwiki-platform-date + ${project.version} + test + + + + org.xwiki.commons + xwiki-commons-diff-script + ${commons.version} + test + diff --git a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/diff_macros.vm b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/diff_macros.vm index 1e30d19761ff..affed54c19c8 100644 --- a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/diff_macros.vm +++ b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/diff_macros.vm @@ -333,14 +333,14 @@ #macro (createDocumentDiff $oldDoc $newDoc $return) #set ($objectDiffs = []) #set ($tagsDiff = $NULL) - #foreach ($objectDiff in $doc.getObjectDiff($oldDoc, $newDoc)) + #foreach ($objectDiff in $oldDoc.getObjectDiff($oldDoc, $newDoc)) #if ($objectDiff.get(0).className == 'XWiki.TagClass') #set ($tagsDiff = $objectDiff) #else #set ($discard = $objectDiffs.add($objectDiff)) #end #end - #set ($classDiff = $doc.getClassDiff($oldDoc, $newDoc)) + #set ($classDiff = $oldDoc.getClassDiff($oldDoc, $newDoc)) #if ($classDiff && $classDiff.size() > 0) #set ($classDiff = $classDiff.get(0)) #end @@ -353,9 +353,9 @@ 'documentReference': $documentReference, 'oldDoc': $oldDoc, 'newDoc': $newDoc, - 'metaData': $doc.getMetaDataDiff($oldDoc, $newDoc), + 'metaData': $oldDoc.getMetaDataDiff($oldDoc, $newDoc), 'tags': $tagsDiff, - 'attachments': $doc.getAttachmentDiff($oldDoc, $newDoc), + 'attachments': $oldDoc.getAttachmentDiff($oldDoc, $newDoc), 'objects': $objectDiffs, 'classProperties': $classDiff }) diff --git a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/test/java/org/xwiki/web/NotificationMailDefaultHtmlTest.java b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/test/java/org/xwiki/web/NotificationMailDefaultHtmlTest.java new file mode 100644 index 000000000000..a0a639cee12e --- /dev/null +++ b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/test/java/org/xwiki/web/NotificationMailDefaultHtmlTest.java @@ -0,0 +1,329 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.xwiki.web; + +import java.util.Collection; +import java.util.Date; +import java.util.List; + +import javax.script.ScriptContext; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.xwiki.diff.display.UnifiedDiffBlock; +import org.xwiki.diff.display.UnifiedDiffElement; +import org.xwiki.diff.script.DiffDisplayerScriptService; +import org.xwiki.diff.script.DiffScriptService; +import org.xwiki.eventstream.Event; +import org.xwiki.eventstream.RecordableEventDescriptor; +import org.xwiki.eventstream.internal.DefaultEvent; +import org.xwiki.eventstream.script.EventStreamScriptService; +import org.xwiki.icon.IconManagerScriptService; +import org.xwiki.localization.script.LocalizationScriptService; +import org.xwiki.model.reference.DocumentReference; +import org.xwiki.notifications.CompositeEvent; +import org.xwiki.platform.date.script.DateScriptService; +import org.xwiki.rendering.syntax.Syntax; +import org.xwiki.script.ScriptContextManager; +import org.xwiki.script.service.ScriptService; +import org.xwiki.template.TemplateManager; +import org.xwiki.template.script.TemplateScriptService; +import org.xwiki.test.annotation.ComponentList; +import org.xwiki.test.page.HTML50ComponentList; +import org.xwiki.test.page.PageTest; + +import com.xpn.xwiki.doc.XWikiDocument; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +/** + * Tests the {@code notification/email/default.html.vm} template. + * + * @version $Id$ + */ +@ComponentList({ + TemplateScriptService.class +}) +@HTML50ComponentList +class NotificationMailDefaultHtmlTest extends PageTest +{ + private static final DocumentReference TEST_DOCUMENT_REFERENCE = new DocumentReference("xwiki", "Test", "WebHome"); + + private static final DocumentReference USER_REFERENCE = new DocumentReference("xwiki", "XWiki", "User"); + + private static final String MAIL_NOTIF_TEMPLATE = "notification/email/default.html.vm"; + + @Mock + private EventStreamScriptService eventStreamScriptService; + + @Mock + private LocalizationScriptService localizationScriptService; + + @Mock + private DateScriptService dateScriptService; + + @Mock + private IconManagerScriptService iconManagerScriptService; + + @Mock + private DiffScriptService diffScriptService; + + @Mock + private DiffDisplayerScriptService diffDisplayerScriptService; + + private TemplateManager templateManager; + + private ScriptContext scriptContext; + + @BeforeEach + void setUp() throws Exception + { + this.scriptContext = this.oldcore.getMocker().getInstance(ScriptContextManager.class) + .getCurrentScriptContext(); + this.templateManager = this.oldcore.getMocker().getInstance(TemplateManager.class); + this.oldcore.getMocker().registerComponent(ScriptService.class, "eventstream", this.eventStreamScriptService); + this.oldcore.getMocker().registerComponent(ScriptService.class, "localization", this.localizationScriptService); + this.oldcore.getMocker().registerComponent(ScriptService.class, "date", this.dateScriptService); + this.oldcore.getMocker().registerComponent(ScriptService.class, "icon", this.iconManagerScriptService); + + this.oldcore.getMocker().registerComponent(ScriptService.class, "diff", this.diffScriptService); + when(this.diffScriptService.get("display")).thenReturn(this.diffDisplayerScriptService); + } + + @Test + void htmlEscaping() throws Exception + { + XWikiDocument testDocument = new XWikiDocument(TEST_DOCUMENT_REFERENCE); + testDocument.setTitle("Hello & Co"); + testDocument.setSyntax(Syntax.XWIKI_2_1); + this.oldcore.getSpyXWiki().saveDocument(testDocument, this.context); + XWikiDocument clone = testDocument.clone(); + testDocument.setContent("Content"); + this.oldcore.getSpyXWiki().saveDocument(testDocument, this.context); + testDocument.setOriginalDocument(clone); + + Event testEvent = new DefaultEvent(); + testEvent.setApplication("test&app"); + testEvent.setType("test&type"); + // Mock date formatting to avoid issues with timezones. + Date testDate = new Date(12); + when(this.dateScriptService.displayTimeAgo(testDate)).thenReturn("A few minutes ago"); + testEvent.setDate(testDate); + testEvent.setUser(USER_REFERENCE); + // Mock the user's name. + when(this.oldcore.getSpyXWiki().getPlainUserName(USER_REFERENCE, this.context)).thenReturn("First & Name"); + testEvent.setDocument(TEST_DOCUMENT_REFERENCE); + testEvent.setDocumentVersion(testDocument.getVersion()); + + String eventType = "test&type"; + RecordableEventDescriptor recordableEventDescriptor = mock(RecordableEventDescriptor.class); + when(this.eventStreamScriptService.getDescriptorForEventType(eventType, true)) + .thenReturn(recordableEventDescriptor); + when(recordableEventDescriptor.getApplicationName()).thenReturn("eventType.translationKey"); + when(this.localizationScriptService.render("eventType.translationKey")).thenReturn("Event Test&Type"); + + when(this.localizationScriptService.render(eq(List.of( + "test&type.description.by.1user", + "notifications.events.test&type.description.by.1user", + "test&type.description", + "notifications.events.test&type.description" + )), any(Collection.class))).then(invocationOnMock -> { + List parameters = invocationOnMock.getArgument(1); + assertEquals(1, parameters.size()); + + return "User information: "+ parameters.get(0); + }); + when(this.localizationScriptService.render("web.history.changes.summary.documentProperties")) + .thenReturn("Document properties"); + when(this.localizationScriptService.render("web.history.changes.document.content")) + .thenReturn("Content"); + + when(this.iconManagerScriptService.renderHTML("file-text")).thenReturn("Icon file text"); + + String expectedDiffLink = "2.1"; + when(this.localizationScriptService.render("notifications.rss.seeChanges", List.of(expectedDiffLink))) + .thenReturn(String.format("See changes: [%s]", expectedDiffLink)); + + when(this.diffDisplayerScriptService.unified(any(String.class), any(String.class), isNull())) + .then(invocationOnMock -> { + String previous = invocationOnMock.getArgument(0); + assertEquals("", previous); + + String next = invocationOnMock.getArgument(1); + assertEquals("Content", next); + UnifiedDiffBlock result = new UnifiedDiffBlock<>(); + result.add(0, new UnifiedDiffElement<>(0, UnifiedDiffElement.Type.ADDED, "Content")); + return List.of(result); + }); + + this.scriptContext.setAttribute("event", new CompositeEvent(testEvent), ScriptContext.GLOBAL_SCOPE); + + String result = this.templateManager.render(MAIL_NOTIF_TEMPLATE); + String expectedResult = "\n" + + " \n" + + " " + + "\n" + + " \n" + + " \n" + + "
\n" + + " Event Test&Type\n" + + "\n" + + " \n" + + "
\n" + // The hierarchy should go inside this div but it's not an easy one to mock. + + "
\n" + + " \n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + " xwiki / Hello & Co / Hello & Co\n" + + "
\n" + + " Hello & Co\n" + + "
\n" + + " User information: " + + "\"U\"\n" + + " First & Name\n" + + " \n" + + "
\n" + + " \n" + + " A few minutes ago\n" + + " \n" + + "
\n" + // No details as there's a single event + + " \n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + " " + + "
\n" + + "
\n" + + "
\n" + + " \n" + + " Icon file text\n" + + " \n" + + " Document properties\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + " Content\n" + + "
\n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
......@@ -1,0 +1,1 @@
1+Content
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + " \n" + + "
"; + + assertEquals(expectedResult, result.trim()); + } +} diff --git a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/test/java/org/xwiki/web/NotificationRssDefaultPageTest.java b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/test/java/org/xwiki/web/NotificationRssDefaultPageTest.java index 375aa0063596..65cb72917b5f 100644 --- a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/test/java/org/xwiki/web/NotificationRssDefaultPageTest.java +++ b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/test/java/org/xwiki/web/NotificationRssDefaultPageTest.java @@ -108,8 +108,6 @@ void htmlEscaping() throws Exception when(this.eventStreamScriptService.getDescriptorForEventType(eventType, true)) .thenReturn(recordableEventDescriptor); when(recordableEventDescriptor.getApplicationName()).thenReturn("eventType.translationKey"); - when(this.localizationScriptService.render("eventType.translationKey")).thenReturn("RSS Event Test&Type"); - when(this.localizationScriptService.render("eventType.translationKey")).thenReturn("RSS Event Test&Type"); when(this.localizationScriptService.render("notifications.events.by", Syntax.HTML_5_0, List.of("First & Name"))) .thenReturn("Event by: [First & Name]");