View Javadoc

1   /**
2    * This file Copyright (c) 2003-2012 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.admininterface.pages;
35  
36  import info.magnolia.cms.beans.config.ContentRepository;
37  import info.magnolia.cms.core.Content;
38  import info.magnolia.cms.core.Content.ContentFilter;
39  import info.magnolia.cms.core.HierarchyManager;
40  import info.magnolia.cms.core.ItemType;
41  import info.magnolia.cms.core.Path;
42  import info.magnolia.cms.i18n.MessagesManager;
43  import info.magnolia.cms.security.AccessDeniedException;
44  import info.magnolia.cms.util.AlertUtil;
45  import info.magnolia.cms.util.ContentUtil;
46  import info.magnolia.cms.util.NodeDataUtil;
47  import info.magnolia.context.MgnlContext;
48  import info.magnolia.importexport.DataTransporter;
49  import info.magnolia.module.ModuleRegistry;
50  import info.magnolia.module.admininterface.TemplatedMVCHandler;
51  import info.magnolia.module.files.BasicFileExtractor;
52  import info.magnolia.module.files.FileExtractor;
53  import info.magnolia.module.files.ModuleFileExtractorTransformer;
54  import info.magnolia.repository.RepositoryConstants;
55  
56  import java.io.File;
57  import java.io.FileNotFoundException;
58  import java.io.FileOutputStream;
59  import java.io.IOException;
60  import java.util.HashSet;
61  import java.util.Iterator;
62  import java.util.Set;
63  
64  import javax.jcr.PathNotFoundException;
65  import javax.jcr.RepositoryException;
66  import javax.jcr.Session;
67  import javax.servlet.http.HttpServletRequest;
68  import javax.servlet.http.HttpServletResponse;
69  
70  import org.apache.commons.io.IOUtils;
71  import org.apache.commons.lang.StringEscapeUtils;
72  import org.apache.commons.lang.StringUtils;
73  import org.slf4j.Logger;
74  import org.slf4j.LoggerFactory;
75  
76  
77  /**
78   * Utilities that can be used during development.
79   * @author Fabrizio Giustina
80   * @version $Revision$ ($Author$)
81   */
82  public class DevelopmentUtilsPage extends TemplatedMVCHandler {
83  
84      private boolean templates;
85  
86      private boolean paragraphs;
87  
88      private boolean dialogs;
89  
90      private boolean pages;
91  
92      private boolean website;
93  
94      private boolean users;
95  
96      private boolean groups;
97  
98      private boolean roles;
99  
100     private boolean virtualURIs;
101 
102     private String rootdir;
103 
104     private String parentpath;
105 
106     private String repository;
107 
108     private String module;
109 
110     private ContentFilter chieldFilter;
111 
112     /**
113      * Logger.
114      */
115     public static Logger log = LoggerFactory.getLogger(DevelopmentUtilsPage.class);
116 
117     /**
118      * @param name
119      * @param request
120      * @param response
121      */
122     public DevelopmentUtilsPage(String name, HttpServletRequest request, HttpServletResponse response) {
123         super(name, request, response);
124 
125         rootdir = StringUtils.defaultIfEmpty(NodeDataUtil.getString(
126             RepositoryConstants.CONFIG,
127             "/modules/adminInterface/config/developmentUtils/exportpath"), "WEB-INF/bootstrap/common");
128         module = StringUtils.defaultIfEmpty(NodeDataUtil.getString(
129             RepositoryConstants.CONFIG,
130             "/modules/adminInterface/config/developmentUtils/module"), "templating");
131     }
132 
133     /**
134      * Getter for <code>templates</code>.
135      * @return Returns the templates.
136      */
137     public boolean isTemplates() {
138         return this.templates;
139     }
140 
141     /**
142      * Getter for <code>paragraphs</code>.
143      * @return Returns the paragraphs.
144      */
145     public boolean isParagraphs() {
146         return this.paragraphs;
147     }
148 
149     /**
150      * Getter for <code>dialogs</code>.
151      * @return Returns the dialogs.
152      */
153     public boolean isDialogs() {
154         return this.dialogs;
155     }
156 
157     /**
158      * Getter for <code>pages</code>.
159      * @return Returns the pages.
160      */
161     public boolean isPages() {
162         return this.pages;
163     }
164 
165     /**
166      * Setter for <code>chieldFilter</code>.
167      * This filter is used in the backupChildren method. Get all children of the current node that pass the current filter.
168      * If no filter is set, ALL_NODES_EXCEPT_JCR_CONTENT_FILTER is then used.
169      */
170     public void setChieldFilter(ContentFilter chieldFilter) {
171         this.chieldFilter = chieldFilter;
172     }
173 
174     /**
175      * Setter for <code>pages</code>.
176      * @param pages The pages to set.
177      */
178     public void setPages(boolean pages) {
179         this.pages = pages;
180     }
181 
182     /**
183      * Getter for <code>website</code>.
184      * @return Returns the website.
185      */
186     public boolean isWebsite() {
187         return this.website;
188     }
189 
190     /**
191      * Getter for <code>users</code>.
192      * @return Returns the users.
193      */
194     public boolean isUsers() {
195         return this.users;
196     }
197 
198     /**
199      * Getter for <code>groups</code>.
200      * @return Returns the groups.
201      */
202     public boolean isGroups() {
203         return this.groups;
204     }
205 
206     /**
207      * Getter for <code>roles</code>.
208      * @return Returns the roles.
209      */
210     public boolean isRoles() {
211         return this.roles;
212     }
213 
214     /**
215      * Getter for <code>rootdir</code>.
216      * @return Returns the rootdir.
217      */
218     public String getRootdir() {
219         return this.rootdir;
220     }
221 
222     /**
223      * Getter for <code>parentpath</code>.
224      * @return Returns the parentpath.
225      */
226     public String getParentpath() {
227         return this.parentpath;
228     }
229 
230     /**
231      * Getter for <code>repository</code>.
232      * @return Returns the repository.
233      */
234     public String getRepository() {
235         return this.repository;
236     }
237 
238     /**
239      * Setter for <code>dialogs</code>.
240      * @param dialogs The dialogs to set.
241      */
242     public void setDialogs(boolean dialogs) {
243         this.dialogs = dialogs;
244     }
245 
246     /**
247      * Setter for <code>paragraphs</code>.
248      * @param paragraphs The paragraphs to set.
249      */
250     public void setParagraphs(boolean paragraphs) {
251         this.paragraphs = paragraphs;
252     }
253 
254     /**
255      * Setter for <code>templates</code>.
256      * @param templates The templates to set.
257      */
258     public void setTemplates(boolean templates) {
259         this.templates = templates;
260     }
261 
262     /**
263      * Setter for <code>rootdir</code>.
264      * @param rootdir The rootdir to set.
265      */
266     public void setRootdir(String rootdir) {
267         this.rootdir = StringEscapeUtils.escapeHtml(rootdir); //escape to prevent XSS attack
268     }
269 
270     /**
271      * Setter for <code>website</code>.
272      * @param website The website to set.
273      */
274     public void setWebsite(boolean website) {
275         this.website = website;
276     }
277 
278     /**
279      * Setter for <code>parentpath</code>.
280      * @param parentpath The parentpath to set.
281      */
282     public void setParentpath(String parentpath) {
283         this.parentpath = parentpath;
284     }
285 
286     /**
287      * Setter for <code>groups</code>.
288      * @param groups The groups to set.
289      */
290     public void setGroups(boolean groups) {
291         this.groups = groups;
292     }
293 
294     /**
295      * Setter for <code>roles</code>.
296      * @param roles The roles to set.
297      */
298     public void setRoles(boolean roles) {
299         this.roles = roles;
300     }
301 
302     /**
303      * Setter for <code>users</code>.
304      * @param users The users to set.
305      */
306     public void setUsers(boolean users) {
307         this.users = users;
308     }
309 
310     /**
311      * Getter for <code>module</code>.
312      * @return Returns the module.
313      */
314     public String getModule() {
315         return this.module;
316     }
317 
318     /**
319      * Setter for <code>module</code>.
320      * @param module The module to set.
321      */
322     public void setModule(String module) {
323         this.module = module;
324     }
325 
326     /**
327      * Setter for <code>repository</code>.
328      * @param repository The repository to set.
329      */
330     public void setRepository(String repository) {
331         this.repository = repository;
332     }
333 
334     /**
335      * Getter for <code>virtualURIs</code>.
336      * @return Returns the virtualURIs.
337      */
338     public boolean isVirtualURIs() {
339         return this.virtualURIs;
340     }
341 
342     /**
343      * Setter for <code>virtualURIs</code>.
344      * @param virtualURIs The virtualURIs to set.
345      */
346     public void setVirtualURIs(boolean virtualURIs) {
347         this.virtualURIs = virtualURIs;
348     }
349 
350     public Iterator getRepositories() {
351         return ContentRepository.getAllRepositoryNames();
352     }
353 
354     public Set getModules() {
355         return ModuleRegistry.Factory.getInstance().getModuleNames();
356     }
357 
358     // ---- operations ----
359     public String extractModuleFiles() {
360         final FileExtractor extractor = new BasicFileExtractor();
361         try {
362             extractor.extractFiles(new ModuleFileExtractorTransformer(module));
363             AlertUtil.setMessage("Files extracted");
364         }
365         catch (IOException e) {
366             AlertUtil.setMessage("Could not extract files for module " + module + ": " + e.getMessage(), e);
367         }
368 
369         return this.show();
370     }
371 
372     public String reloadI18nMessages() {
373         try {
374             MessagesManager.getInstance().reload();
375             AlertUtil.setMessage("Messages reloaded.");
376         }
377         catch (Exception e) {
378             e.printStackTrace();
379             AlertUtil.setMessage("Can't reload: " + e.getMessage(), e);
380         }
381 
382         return this.show();
383     }
384 
385     public String backup() {
386         HierarchyManager hm = MgnlContext.getHierarchyManager(RepositoryConstants.CONFIG);
387         Session session = hm.getWorkspace().getSession();
388 
389         try {
390             Content moduleroot = hm.getContent("/modules/" + module);
391             if (templates) {
392                 exportChildren(RepositoryConstants.CONFIG, session, moduleroot, "templates", new ItemType[]{
393                     ItemType.CONTENT,
394                     ItemType.CONTENTNODE}, false);
395             }
396             if (paragraphs) {
397                 exportChildren(RepositoryConstants.CONFIG, session, moduleroot, "paragraphs", new ItemType[]{
398                     ItemType.CONTENT,
399                     ItemType.CONTENTNODE}, false);
400             }
401             if (pages) {
402                 exportChildren(RepositoryConstants.CONFIG, session, moduleroot, "pages", new ItemType[]{
403                     ItemType.CONTENT,
404                     ItemType.CONTENTNODE}, false);
405             }
406             if (dialogs) {
407                 exportChildren(RepositoryConstants.CONFIG, session, moduleroot, "dialogs", new ItemType[]{
408                     ItemType.CONTENT,
409                     ItemType.CONTENTNODE}, true);
410             }
411             if (virtualURIs) {
412                 exportChildren(
413                     RepositoryConstants.CONFIG,
414                     session,
415                     moduleroot,
416                     "virtualURIMapping",
417                     new ItemType[]{ItemType.CONTENTNODE},
418                     true);
419             }
420             AlertUtil.setMessage("Backup done to "
421                 + new File(Path.getAbsoluteFileSystemPath(rootdir)).getCanonicalPath());
422         }
423         catch (Exception e) {
424             log.error(e.getMessage(), e);
425             AlertUtil.setMessage("Error while processing module " + module, e);
426         }
427 
428         if (website) {
429             extractWorkspaceRoots(RepositoryConstants.WEBSITE);
430         }
431 
432         if (users) {
433             backupChildren(RepositoryConstants.USERS, "/admin");
434         }
435 
436         if (groups) {
437             extractWorkspaceRoots(RepositoryConstants.USER_GROUPS);
438         }
439 
440         if (roles) {
441             extractWorkspaceRoots(RepositoryConstants.USER_ROLES);
442         }
443 
444         return this.show();
445     }
446 
447     /**
448      * @param repositoryName
449      */
450     private void extractWorkspaceRoots(String repositoryName) {
451         try {
452             HierarchyManager hm = MgnlContext.getHierarchyManager(repositoryName);
453             Content wesiteRoot = hm.getRoot();
454 
455             Iterator children = wesiteRoot.getChildren(ContentUtil.MAGNOLIA_FILTER).iterator();
456             while (children.hasNext()) {
457                 Content exported = (Content) children.next();
458                 exportNode(repositoryName, hm.getWorkspace().getSession(), exported);
459             }
460         }
461         catch (Exception e) {
462             log.error(e.getMessage(), e);
463             AlertUtil.setMessage("Error while processing " + repositoryName + " repository", e);
464         }
465     }
466 
467     public String backupChildren() {
468         backupChildren(this.repository, this.parentpath);
469 
470         String path = Path.getAbsoluteFileSystemPath(rootdir);
471 
472         try {
473             path = new File(path).getCanonicalPath();
474         }
475         catch (IOException e) {
476             // should never happen
477         }
478 
479         AlertUtil.setMessage("Backup done to " + path);
480 
481         return this.show();
482     }
483 
484     private void backupChildren(String repository, String parentpath) {
485         HierarchyManager hm = MgnlContext.getHierarchyManager(repository);
486 
487         Content parentNode = null;
488         try {
489             parentNode = hm.getContent(parentpath);
490         }
491         catch (RepositoryException e) {
492             // ignore
493             return;
494         }
495         try {
496             this.chieldFilter = this.chieldFilter!=null?this.chieldFilter:ContentUtil.ALL_NODES_EXCEPT_JCR_CONTENT_FILTER;
497             Iterator children = parentNode.getChildren(this.chieldFilter).iterator();
498             while (children.hasNext()) {
499                 Content exported = (Content) children.next();
500                 exportNode(repository, hm.getWorkspace().getSession(), exported);
501             }
502 
503         }
504         catch (Exception e) {
505             log.error(e.getMessage(), e);
506             AlertUtil.setMessage("Error while processing actions", e);
507         }
508 
509     }
510 
511     /**
512      * @param session
513      * @param moduleroot
514      * @param exportContentContainingContentNodes
515      * @throws PathNotFoundException
516      * @throws RepositoryException
517      * @throws AccessDeniedException
518      * @throws FileNotFoundException
519      * @throws IOException
520      */
521     private void exportChildren(String repository, Session session, Content moduleroot, String path,
522         ItemType[] itemTypes, boolean exportContentContainingContentNodes) throws PathNotFoundException,
523         RepositoryException, AccessDeniedException, FileNotFoundException, IOException {
524         Content templateRoot = null;
525         try {
526             templateRoot = moduleroot.getContent(path);
527         }
528         catch (PathNotFoundException e) {
529             // ignore
530             return;
531         }
532 
533         // we need to track exported paths, or it will export any single control for dialogs
534         Set alreadyExported = new HashSet();
535 
536         Iterator children = ContentUtil.collectAllChildren(templateRoot, itemTypes).iterator();
537         while (children.hasNext()) {
538             Content exported = (Content) children.next();
539             if (!exported.getNodeDataCollection().isEmpty() // ignore "directories"
540                 || (exportContentContainingContentNodes && exported.hasChildren(ItemType.CONTENTNODE.getSystemName()))) {
541 
542                 String current = exported.getHandle();
543                 boolean dontexport = false;
544 
545                 for (Iterator iterator = alreadyExported.iterator(); iterator.hasNext();) {
546                     String already = (String) iterator.next();
547                     if (current.startsWith(already)) {
548                         dontexport = true;
549                         break;
550                     }
551                 }
552 
553                 if (!dontexport) {
554                     alreadyExported.add(exported.getHandle() + "/");
555                     exportNode(repository, session, exported);
556                 }
557             }
558         }
559     }
560 
561     /**
562      * @param session
563      * @param exported
564      * @throws FileNotFoundException
565      * @throws IOException
566      */
567     private void exportNode(String repository, Session session, Content exported) throws FileNotFoundException,
568         IOException {
569         String handle = exported.getHandle();
570         String xmlName = repository + StringUtils.replace(handle, "/", ".") + ".xml";
571         xmlName = DataTransporter.encodePath(xmlName, ".", "UTF-8");
572         // create necessary parent directories
573         File folder = new File(Path.getAbsoluteFileSystemPath(rootdir));
574         folder.mkdirs();
575         File xmlFile = new File(folder.getAbsoluteFile(), xmlName);
576         FileOutputStream fos = new FileOutputStream(xmlFile);
577 
578         try {
579             DataTransporter.executeExport(fos, false, true, session, handle, repository, DataTransporter.XML);
580         }
581         finally {
582             IOUtils.closeQuietly(fos);
583         }
584     }
585 }