View Javadoc
1   /**
2    * This file Copyright (c) 2013-2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.module.groovy.setup;
35  
36  import static info.magnolia.module.groovy.GroovyModule.*;
37  import static info.magnolia.test.hamcrest.NodeMatchers.*;
38  import static org.hamcrest.CoreMatchers.*;
39  import static org.junit.Assert.*;
40  import static org.mockito.Mockito.mock;
41  
42  import info.magnolia.cms.security.MgnlGroupManager;
43  import info.magnolia.cms.security.MgnlRoleManager;
44  import info.magnolia.cms.security.MgnlUserManager;
45  import info.magnolia.cms.security.Permission;
46  import info.magnolia.cms.security.Realm;
47  import info.magnolia.cms.security.Role;
48  import info.magnolia.cms.security.SecuritySupport;
49  import info.magnolia.cms.security.SecuritySupportImpl;
50  import info.magnolia.context.MgnlContext;
51  import info.magnolia.jcr.predicate.NodeTypePredicate;
52  import info.magnolia.jcr.util.NodeTypeTemplateUtil;
53  import info.magnolia.jcr.util.NodeTypes;
54  import info.magnolia.jcr.util.NodeUtil;
55  import info.magnolia.jcr.util.PropertyUtil;
56  import info.magnolia.module.InstallContext;
57  import info.magnolia.module.ModuleManagementException;
58  import info.magnolia.module.ModuleVersionHandler;
59  import info.magnolia.module.ModuleVersionHandlerTestCase;
60  import info.magnolia.module.groovy.console.MgnlGroovyConsole;
61  import info.magnolia.module.model.Version;
62  import info.magnolia.repository.RepositoryConstants;
63  import info.magnolia.test.ComponentsTestUtil;
64  import info.magnolia.ui.form.field.definition.CodeFieldDefinition;
65  
66  import java.io.Writer;
67  import java.util.Arrays;
68  import java.util.List;
69  
70  import javax.jcr.Node;
71  import javax.jcr.NodeIterator;
72  import javax.jcr.RepositoryException;
73  import javax.jcr.Session;
74  import javax.jcr.nodetype.NodeType;
75  import javax.jcr.nodetype.NodeTypeManager;
76  import javax.jcr.nodetype.NodeTypeTemplate;
77  
78  import org.junit.Before;
79  import org.junit.Test;
80  
81  import com.vaadin.ui.UI;
82  
83  import groovy.lang.Binding;
84  
85  
86  public class GroovyModuleVersionHandlerTest extends ModuleVersionHandlerTestCase {
87  
88      private Node uiApp;
89      private Node uiConfig;
90      private Node groovyModule;
91      private MgnlRoleManager roleManager;
92  
93      private Session scriptsSession;
94  
95      @Override
96      protected String getModuleDescriptorPath() {
97          return "/META-INF/magnolia/groovy.xml";
98      }
99  
100     @Override
101     protected ModuleVersionHandler newModuleVersionHandlerForTests() {
102         return new GroovyModuleVersionHandler();
103     }
104 
105     @Override
106     protected List<String> getModuleDescriptorPathsForTests() {
107         return Arrays.asList("/META-INF/magnolia/core.xml");
108     }
109 
110     @Override
111     protected String[] getExtraWorkspaces() {
112         return new String[]{SCRIPTS_REPOSITORY_NAME, "contacts"};
113     }
114 
115     @Override
116     protected String getRepositoryConfigFileName() {
117         return "info/magnolia/module/groovy/setup/test-repositories.xml";
118     }
119 
120     @Override
121     @Before
122     public void setUp() throws Exception {
123         super.setUp();
124 
125         Session configSession = MgnlContext.getJCRSession(RepositoryConstants.CONFIG);
126         scriptsSession = MgnlContext.getJCRSession(SCRIPTS_REPOSITORY_NAME);
127 
128         uiApp = NodeUtil.createPath(configSession.getRootNode(), "/modules/ui-admincentral/apps", NodeTypes.ContentNode.NAME);
129         groovyModule = NodeUtil.createPath(configSession.getRootNode(), "/modules/groovy", NodeTypes.ContentNode.NAME);
130         uiConfig = NodeUtil.createPath(configSession.getRootNode(), "/modules/ui-admincentral/config/appLauncherLayout/groups/dev/apps", NodeTypes.ContentNode.NAME);
131         uiApp.getSession().save();
132 
133         // Security Support
134         final SecuritySupportImpl securitySupport = new SecuritySupportImpl();
135         securitySupport.addUserManager(Realm.REALM_ALL.getName(), new MgnlUserManager());
136         roleManager = new MgnlRoleManager();
137         securitySupport.setRoleManager(roleManager);
138         securitySupport.setGroupManager(new MgnlGroupManager());
139         ComponentsTestUtil.setInstance(SecuritySupport.class, securitySupport);
140 
141         setupConfigProperty("/modules/groovy/apps/groovy/subApps/browser/workbench", "dropConstraintClass", "info.magnolia.contacts.app.main.tree.ContactDropConstraint");
142     }
143 
144     @Test
145     public void updateFrom201WithExistingLegacyApp() throws ModuleManagementException, RepositoryException {
146         // GIVEN
147         groovyModule.addNode("trees", NodeTypes.Folder.NAME);
148         groovyModule.addNode("dialogs", NodeTypes.Folder.NAME).addNode("scriptsEdit", NodeTypes.ContentNode.NAME);
149         groovyModule.addNode("controls", NodeTypes.Folder.NAME);
150 
151         uiApp.addNode("groovyConsole", NodeTypes.ContentNode.NAME);
152         uiApp.addNode("groovyScripts", NodeTypes.ContentNode.NAME);
153 
154         uiConfig.addNode("groovyConsole", NodeTypes.ContentNode.NAME);
155         uiConfig.addNode("groovyScript", NodeTypes.ContentNode.NAME);
156 
157         // WHEN
158         executeUpdatesAsIfTheCurrentlyInstalledVersionWas(Version.parseVersion("2.0.1"));
159 
160         // THEN
161         assertFalse(groovyModule.hasNode("trees"));
162         assertFalse(groovyModule.hasNode("dialogs/scriptsEdit"));
163         assertFalse(groovyModule.hasNode("controls"));
164 
165         assertFalse(uiApp.hasNode("groovyConsole"));
166         assertFalse(uiApp.hasNode("groovyScripts"));
167 
168         assertFalse(uiConfig.hasNode("groovyConsole"));
169         assertFalse(uiConfig.hasNode("groovyScripts"));
170 
171         assertTrue(uiConfig.hasNode("groovy"));
172         assertTrue(groovyModule.hasNode("fieldTypes"));
173         assertTrue(groovyModule.hasNode("dialogs/renameScript"));
174     }
175 
176     @Test
177     public void updateFrom201WithNoExistingLegacyApp() throws ModuleManagementException, RepositoryException {
178         // GIVEN
179 
180         // WHEN
181         executeUpdatesAsIfTheCurrentlyInstalledVersionWas(Version.parseVersion("2.0.1"));
182 
183         // THEN
184         assertTrue(uiConfig.hasNode("groovy"));
185         assertTrue(groovyModule.hasNode("fieldTypes"));
186         assertTrue(groovyModule.hasNode("dialogs/renameScript"));
187     }
188 
189     @Test
190     public void upgradeFrom21SetsAppPermissions() throws Exception {
191         // GIVEN
192         final Session configSession = MgnlContext.getJCRSession(RepositoryConstants.CONFIG);
193 
194         // WHEN
195         executeUpdatesAsIfTheCurrentlyInstalledVersionWas(Version.parseVersion("2.1"));
196 
197         // THEN
198         assertEquals("scripter", configSession.getProperty("/modules/groovy/apps/groovy/permissions/roles/scripter").getString());
199     }
200 
201     @Test
202     public void updateFrom21ReordersGroovyInDevGroup() throws Exception {
203         // GIVEN
204         Session session = MgnlContext.getJCRSession(RepositoryConstants.CONFIG);
205         Node devApps = NodeUtil.createPath(session.getRootNode(), "/modules/ui-admincentral/config/appLauncherLayout/groups/dev/apps", NodeTypes.ContentNode.NAME);
206         NodeUtil.createPath(devApps, "jcrQueryUtils", NodeTypes.ContentNode.NAME);
207         NodeUtil.createPath(devApps, "activation", NodeTypes.ContentNode.NAME);
208 
209         // WHEN
210         executeUpdatesAsIfTheCurrentlyInstalledVersionWas(Version.parseVersion("2.1"));
211 
212         // THEN
213         NodeIterator it = devApps.getNodes();
214         assertTrue(devApps.hasNode("groovy"));
215         assertEquals("jcrQueryUtils", it.nextNode().getName());
216         assertEquals("groovy", it.nextNode().getName());
217         assertEquals("activation", it.nextNode().getName());
218     }
219 
220     @Test
221     public void updateFrom22AddsDuplicateAction() throws Exception {
222         // GIVEN
223 
224         // WHEN
225         executeUpdatesAsIfTheCurrentlyInstalledVersionWas(Version.parseVersion("2.2"));
226 
227         // THEN
228         assertTrue(groovyModule.hasNode("apps/groovy/subApps/browser/actions/duplicateScript"));
229         assertTrue(groovyModule.hasNode("apps/groovy/subApps/browser/actionbar/sections/script/groups/editActions/items/duplicateScript"));
230     }
231 
232     @Test
233     public void upgradeFrom10() throws Exception {
234         // WHEN
235         executeUpdatesAsIfTheCurrentlyInstalledVersionWas(Version.parseVersion("1.0"));
236 
237         // THEN no errors
238     }
239 
240     @Test
241     public void upgradeFrom222() throws Exception {
242         // GIVEN
243         setupConfigProperty("/modules/groovy/apps/groovy/subApps/detail/editor/form/tabs/script/fields/text", "class", "info.magnolia.ui.form.field.definition.BasicTextCodeFieldDefinition");
244         Session userRoles = MgnlContext.getJCRSession(RepositoryConstants.USER_ROLES);
245         Role role = roleManager.createRole(SCRIPTER_ROLE_NAME);
246 
247         roleManager.addPermission(role, RepositoryConstants.CONFIG, "/modules/adminInterface/config/menu/tools/groovyInteractiveShell", Permission.READ);
248         assertTrue(userRoles.propertyExists("/" + SCRIPTER_ROLE_NAME + "/acl_config/0/path"));
249         assertEquals("/modules/adminInterface/config/menu/tools/groovyInteractiveShell",
250                 userRoles.getProperty("/" + SCRIPTER_ROLE_NAME + "/acl_config/0/path").getString());
251 
252         roleManager.addPermission(role, RepositoryConstants.CONFIG, "/modules/adminInterface/config/menu/tools/scripts", Permission.READ);
253         assertTrue(userRoles.propertyExists("/" + SCRIPTER_ROLE_NAME + "/acl_config/0/path"));
254         assertEquals("/modules/adminInterface/config/menu/tools/scripts",
255                 userRoles.getProperty("/" + SCRIPTER_ROLE_NAME + "/acl_config/00/path").getString());
256 
257         roleManager.addPermission(role, RepositoryConstants.CONFIG, "/modules/adminInterface/config/menu/tools", Permission.READ);
258         assertTrue(userRoles.propertyExists("/" + SCRIPTER_ROLE_NAME + "/acl_config/01/path"));
259         assertEquals("/modules/adminInterface/config/menu/tools",
260                 userRoles.getProperty("/" + SCRIPTER_ROLE_NAME + "/acl_config/01/path").getString());
261 
262         // WHEN
263         InstallContext ctx = executeUpdatesAsIfTheCurrentlyInstalledVersionWas(Version.parseVersion("2.2.2"));
264 
265         // THEN
266         assertFalse(userRoles.nodeExists("/" + SCRIPTER_ROLE_NAME + "/acl_config/0"));
267         assertFalse(userRoles.nodeExists("/" + SCRIPTER_ROLE_NAME + "/acl_config/00"));
268         assertFalse(userRoles.nodeExists("/" + SCRIPTER_ROLE_NAME + "/acl_config/01"));
269         this.assertNoMessages(ctx);
270     }
271 
272     @Test
273     public void upgradeFrom23() throws Exception {
274         // GIVEN
275 
276         // WHEN
277         InstallContext ctx = executeUpdatesAsIfTheCurrentlyInstalledVersionWas(Version.parseVersion("2.3"));
278 
279         // THEN
280         assertThat(ctx.getConfigJCRSession().getNode("/modules/groovy/apps/groovy/subApps/detail/editor/form/tabs/script/fields/text/"), hasNode("validators"));
281         assertThat(ctx.getConfigJCRSession().getNode("/modules/groovy/apps/groovy/subApps/browser/workbench"), hasProperty("dropConstraintClass", "info.magnolia.module.groovy.app.drop.GroovyDropConstraint"));
282     }
283 
284     @Test
285     public void updateFrom232ReplaceBasicTextCodeFieldDefinition() throws Exception {
286         // GIVEN
287         String textPath = "/modules/groovy/apps/groovy/subApps/detail/editor/form/tabs/script/fields/text";
288         setupConfigProperty(textPath, "class", "info.magnolia.ui.form.field.definition.BasicTextCodeFieldDefinition");
289 
290         // WHEN
291         InstallContext ctx = executeUpdatesAsIfTheCurrentlyInstalledVersionWas(Version.parseVersion("2.3.2"));
292 
293         // THEN
294         assertThat(ctx.getConfigJCRSession().getNode(textPath), hasProperty("class", CodeFieldDefinition.class.getName()));
295     }
296 
297     @Test
298     public void updateFrom252RemovesWorkspaceFromCreateAppScriptEditorSection() throws Exception {
299         // GIVEN
300         String editorSectionOpeningLine = "editor {";
301         String setWorkspaceLine = "workspace(app_workspace)";
302         setupProperty(SCRIPTS_REPOSITORY_NAME, "/createAppScript", "text", editorSectionOpeningLine + "\n" + setWorkspaceLine, null);
303 
304         // WHEN
305         executeUpdatesAsIfTheCurrentlyInstalledVersionWas(Version.parseVersion("2.5.2"));
306 
307         // THEN
308         assertThat(scriptsSession.getNode("/createAppScript"), hasProperty("text"));
309         String scriptProperty = scriptsSession.getNode("/createAppScript").getProperty("text").getString();
310         // only the second occurrence of 'workspace(app_workspace)' needs to be deleted
311         int indexOfEditorSectionOpeningLine = scriptProperty.indexOf(editorSectionOpeningLine);
312         assertThat(scriptProperty.indexOf(setWorkspaceLine, indexOfEditorSectionOpeningLine), is(-1));
313     }
314 
315     @Test
316     public void updateFrom253ReplacesOldGroovyMailCommandSampleAndRemovesEmptyFolders() throws Exception {
317         // GIVEN
318         setupNode(SCRIPTS_REPOSITORY_NAME, "/my/commands/GroovyMailCommand");
319         setupConfigNode("modules/mail");
320 
321         // WHEN
322         executeUpdatesAsIfTheCurrentlyInstalledVersionWas(Version.parseVersion("2.5.3"));
323 
324         // THEN
325         assertThat(scriptsSession.nodeExists("/my"), is(false));
326         assertThat(scriptsSession.getRootNode(), hasNode("samples/commands/GroovyMailCommand"));
327     }
328 
329     @Test
330     public void updateFrom253ReplacesOldGroovyMailCommandSampleAndKeepsNonEmptyFolders() throws Exception {
331         // GIVEN
332         setupConfigNode("modules/mail");
333         setupNode(SCRIPTS_REPOSITORY_NAME, "/my/commands/GroovyMailCommand");
334         setupNode(SCRIPTS_REPOSITORY_NAME, "/my/commands/CustomCommand");
335         // WHEN
336         executeUpdatesAsIfTheCurrentlyInstalledVersionWas(Version.parseVersion("2.5.3"));
337 
338         // THEN
339         assertThat(scriptsSession.nodeExists("/my/commands/GroovyMailCommand"), is(false));
340         assertThat(scriptsSession.nodeExists("/my/commands/CustomCommand"), is(true));
341         assertThat(scriptsSession.getRootNode(), hasNode("samples/commands/GroovyMailCommand"));
342     }
343 
344     @Test
345     public void updateFrom260FixesCreateAppScript() throws Exception {
346         // GIVEN
347         setupNode(SCRIPTS_REPOSITORY_NAME, "/createAppScript");
348 
349         // WHEN
350         executeUpdatesAsIfTheCurrentlyInstalledVersionWas(Version.parseVersion("2.6"));
351 
352         // THEN
353         assertThat(scriptsSession.getNode("/createAppScript").getProperty("text").getString(), containsString("/modules/publishing-core/config/receivers"));
354         assertThat(scriptsSession.getNode("/createAppScript").getProperty("text").getString(), not(containsString("/server/activation/subscribers")));
355     }
356 
357     @Test
358     public void updateFrom261() throws Exception {
359         // GIVEN
360         setupConfigNode("modules/mail");
361         setupConfigNode("modules/contacts");
362         setupConfigNode("modules/travel-demo");
363 
364         setupNode(SCRIPTS_REPOSITORY_NAME, "/samples/commands/GroovyMailCommand");
365 
366         // WHEN
367         executeUpdatesAsIfTheCurrentlyInstalledVersionWas(Version.parseVersion("2.6.1"));
368 
369         // THEN
370         assertThat(scriptsSession.getRootNode(), allOf(
371                 hasNode("samples/commands/GroovyMailCommand"),
372                 hasNode("deleteSomeContacts"),
373                 hasNode("importContactsFromXml"),
374                 hasNode("simplifiedDataHierarchyNavigation")
375         ));
376     }
377 
378     @Test
379     public void cleanInstallBootstrapsSamples() throws Exception {
380         // GIVEN
381         setupConfigNode("modules/contacts");
382         setupConfigNode("modules/ui-admincentral");
383         setupConfigNode("modules/mail");
384 
385         setupConfigNode("modules/travel-demo");
386         setupNode(RepositoryConstants.WEBSITE, "travel/about");
387 
388         // THEN
389         executeUpdatesAsIfTheCurrentlyInstalledVersionWas(null);
390 
391         // THEN
392         assertThat(scriptsSession.getRootNode(), allOf(
393                 hasNode("deleteSomeContacts"),
394                 hasNode("importContactsFromXml"),
395                 hasNode("simplifiedDataHierarchyNavigation"),
396                 hasNode("createAppScript"),
397                 hasNode("samples/commands/GroovyMailCommand")
398         ));
399 
400         final NodeTypeManager nodeTypeManager = MgnlContext.getJCRSession("contacts").getWorkspace().getNodeTypeManager();
401         final NodeTypeTemplate type = NodeTypeTemplateUtil.createSimpleNodeType(nodeTypeManager, "mgnl:contact", Arrays.asList(NodeType.NT_HIERARCHY_NODE, NodeType.MIX_REFERENCEABLE, NodeTypes.Created.NAME, NodeTypes.Activatable.NAME, NodeTypes.LastModified.NAME, NodeTypes.Renderable.NAME));
402         nodeTypeManager.registerNodeType(type, true);
403 
404         final MgnlGroovyConsole shell = new MgnlGroovyConsole(new Binding(), null, null);
405 
406         for (Node script : NodeUtil.collectAllChildren(scriptsSession.getRootNode(), new NodeTypePredicate(NodeTypes.Content.NAME))) {
407             final String scriptText = PropertyUtil.getString(script, "text");
408 
409             if (PropertyUtil.getBoolean(script, "script", false)) {
410                 shell.runAsync(scriptText, mock(UI.class), new MgnlGroovyConsole.ScriptCallback() {
411                     @Override
412                     public void onSuccess(String result) {
413                     }
414 
415                     @Override
416                     public void onFailure(Throwable e) {
417                         fail(String.format("Exception in script %s\n%s", script, e.getMessage()));
418                     }
419 
420                     @Override
421                     public void onProgress(Writer out) {
422                     }
423 
424                     @Override
425                     public boolean requiresNotificationMessageUponCompletion() {
426                         return false;
427                     }
428                 });
429             } else { //it's not a script, e.g. a command and we don't know how to run it, so at least check for parsing errors
430                 shell.parse(scriptText);
431             }
432         }
433     }
434 
435 }