diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..c804e17 --- /dev/null +++ b/NOTICE @@ -0,0 +1,4 @@ +Public Sling + Sightly blog engine + +This product contains code derived from Apache Sling from https://sling.apache.org/. +Copyright The Apache Software Foundation \ No newline at end of file diff --git a/core/pom.xml b/core/pom.xml index a4546bc..8ede1ea 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -254,6 +254,36 @@ ${project.build.directory}/dependency/httpclient-osgi-4.5.jar + + install-distribution + + install-file + + install + + ${project.build.directory}/dependency/org.apache.sling.distribution.core-0.1.6.jar + + + + install-hc + + install-file + + install + + ${project.build.directory}/dependency/org.apache.sling.hc.core-1.2.2.jar + + + + install-vault + + install-file + + install + + ${project.build.directory}/dependency/org.apache.jackrabbit.vault-3.1.18.jar + + @@ -448,6 +478,21 @@ commons-logging 1.2 + + org.apache.sling + org.apache.sling.distribution.core + 0.1.6 + + + org.apache.sling + org.apache.sling.hc.core + 1.2.2 + + + org.apache.jackrabbit.vault + org.apache.jackrabbit.vault + 3.1.18 + diff --git a/core/src/main/java/com/nateyolles/sling/publick/components/admin/SystemConfig.java b/core/src/main/java/com/nateyolles/sling/publick/components/admin/SystemConfig.java index 96a234f..d414b1d 100644 --- a/core/src/main/java/com/nateyolles/sling/publick/components/admin/SystemConfig.java +++ b/core/src/main/java/com/nateyolles/sling/publick/components/admin/SystemConfig.java @@ -14,39 +14,28 @@ */ public class SystemConfig extends WCMUse { - /** - * The Sling Script Helper to get services. - */ + /** The Sling Script Helper to get services. */ private SlingScriptHelper scriptHelper; - /** - * The current resource. - */ + /** The current resource. */ private Resource resource; - /** - * The name of the blog. - */ + /** The name of the blog. */ private String blogName; - /** - * Setting for extensionless URLs. - */ + /** The file system temporary directory */ + private String temporaryDirectory; + + /** Setting for extensionless URLs. */ private boolean extensionlessUrls; - /** - * The separator between blog name and page title. - */ + /** The separator between blog name and page title. */ private static final String TITLE_SEPARATOR = " - "; - /** - * The title property of the resource. - */ + /** The title property of the resource. */ private static final String TITLE_PROPERTY = "title"; - /** - * Initialize the Sightly component. - */ + /** Initialize the Sightly component. */ @Override public void activate() { scriptHelper = getSlingScriptHelper(); @@ -57,6 +46,7 @@ public void activate() { if (systemSettingsService != null) { blogName = systemSettingsService.getBlogName(); extensionlessUrls = systemSettingsService.getExtensionlessUrls(); + temporaryDirectory = systemSettingsService.getTemporaryDirectory(); } } @@ -69,6 +59,15 @@ public String getBlogName() { return blogName; } + /** + * Get the temporary directory. + * + * @return The temporary directory. + */ + public String getTemporaryDirectory() { + return temporaryDirectory; + } + /** * Get the setting for extensionless URLs. * diff --git a/core/src/main/java/com/nateyolles/sling/publick/services/PackageService.java b/core/src/main/java/com/nateyolles/sling/publick/services/PackageService.java new file mode 100644 index 0000000..1884d2d --- /dev/null +++ b/core/src/main/java/com/nateyolles/sling/publick/services/PackageService.java @@ -0,0 +1,42 @@ +package com.nateyolles.sling.publick.services; + +import java.util.List; + +import org.apache.jackrabbit.vault.packaging.JcrPackage; +import org.apache.sling.api.SlingHttpServletRequest; + +/** + * API's to get, create, upload, install and delete packages. + */ +public interface PackageService { + + /** + * Get the list of all packages in order of newest to oldest. + * + * @param request The Sling HTTP servlet request + * @return List of all JCR Packages + */ + List getPackageList(final SlingHttpServletRequest request); + + /** + * Create a JCR package and store it under /etc/packages/group_name/package_name-version.zip. + * + * @param request The Sling HTTP servlet request + * @param groupName The name of the package group + * @param packageName The name of the package + * @param version The version of the package + * @param paths The JCR paths to include in the package + * @return true if package is created successfully + */ + boolean createPackage(final SlingHttpServletRequest request, final String groupName, + final String packageName, final String version, final String[] paths); + + /** + * Create a Publick backup package. + * + * @param request The Sling HTTP servlet request + * @param packageName The name of the package + * @return true if package is created successfully + */ + boolean createBackupPackage(final SlingHttpServletRequest request, final String packageName); +} \ No newline at end of file diff --git a/core/src/main/java/com/nateyolles/sling/publick/services/SystemSettingsService.java b/core/src/main/java/com/nateyolles/sling/publick/services/SystemSettingsService.java index c0b5238..9bf160a 100644 --- a/core/src/main/java/com/nateyolles/sling/publick/services/SystemSettingsService.java +++ b/core/src/main/java/com/nateyolles/sling/publick/services/SystemSettingsService.java @@ -14,6 +14,9 @@ public interface SystemSettingsService { /** OSGi property name for extensionless URLs */ public static final String SYSTEM_EXTENSIONLESS_URLS = "system.extentionlessUrls"; + /** OSGi property name for the temporary directory */ + public static final String SYSTEM_TEMPORARY_DIRECTORY = "system.temporaryDirectory"; + /** * Set multiple properties for the System Settings service. * @@ -55,4 +58,19 @@ public interface SystemSettingsService { * @return true if the save was successful. */ boolean setExtensionlessUrls(final boolean value); + + /** + * Get the setting for temporary directory. + * + * @return The setting for temporary directory. + */ + String getTemporaryDirectory(); + + /** + * Set the value for temporary directory. + * + * @param directory The setting for the temporary directory. + * @return true if the save was successful. + */ + boolean setTemporaryDirectory(final String directory); } \ No newline at end of file diff --git a/core/src/main/java/com/nateyolles/sling/publick/services/impl/PackageServiceImpl.java b/core/src/main/java/com/nateyolles/sling/publick/services/impl/PackageServiceImpl.java new file mode 100644 index 0000000..e7410f0 --- /dev/null +++ b/core/src/main/java/com/nateyolles/sling/publick/services/impl/PackageServiceImpl.java @@ -0,0 +1,151 @@ +package com.nateyolles.sling.publick.services.impl; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import javax.jcr.Session; +import javax.jcr.RepositoryException; + +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.Service; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.nateyolles.sling.publick.utils.VltUtils; +import com.nateyolles.sling.publick.services.PackageService; +import com.nateyolles.sling.publick.services.SystemSettingsService; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter; +import org.apache.jackrabbit.vault.packaging.ExportOptions; +import org.apache.jackrabbit.vault.packaging.JcrPackage; +import org.apache.jackrabbit.vault.packaging.JcrPackageManager; +import org.apache.jackrabbit.vault.packaging.Packaging; +import org.apache.jackrabbit.vault.packaging.VaultPackage; +import org.apache.sling.api.SlingHttpServletRequest; + +/** + * Service to get, create, upload, install and delete packages. + */ +@Service( value = PackageService.class ) +@Component( immediate = true ) +public class PackageServiceImpl implements PackageService { + + /** Service to get the JCR Package Manager */ + @Reference + private Packaging packaging; + + /** Service to get the system temporary folder for creating packages */ + @Reference + private SystemSettingsService settingsService; + + /** The logger */ + private static final Logger LOGGER = LoggerFactory.getLogger(PackageServiceImpl.class); + + /** Default package group name for Publick backups */ + private static final String BACKUP_GROUP = "publick_backup"; + + /** Default package version for Publick backups */ + private static final String BACKUP_VERSION = "1.0"; + + /** Default package paths for Publick backups */ + private static final String[] BACKUP_PATHS = {"/content/assets", "/content/blog", "/content/comments"}; + + /** + * Get the list of all packages in order of newest to oldest. + * + * @param request The Sling HTTP servlet request + * @return List of all JCR Packages + */ + public List getPackageList(final SlingHttpServletRequest request) { + Session session = request.getResourceResolver().adaptTo(Session.class); + JcrPackageManager packageManager = packaging.getPackageManager(session); + List packages = null; + + try { + packages = packageManager.listPackages(); + } catch (RepositoryException e) { + LOGGER.error("Could not get package list", e); + } + + return packages; + } + + /** + * Create a JCR package and store it under /etc/packages/group_name/package_name-version.zip. + * {@link org.apache.sling.distribution.serialization.impl.vlt.JcrVaultDistributionPackageBuilder#createPackageForAdd} + * + * @param request The Sling HTTP servlet request + * @param groupName The name of the package group + * @param packageName The name of the package + * @param version The version of the package + * @param paths The JCR paths to include in the package + * @return true if package is created successfully + */ + public boolean createPackage(final SlingHttpServletRequest request, final String groupName, + final String packageName, final String version, final String[] paths) { + + Session session = null; + VaultPackage vaultPackage = null; + File tempDirectory = VltUtils.getTempFolder(settingsService.getTemporaryDirectory()); + + try { + session = request.getResourceResolver().adaptTo(Session.class); + + WorkspaceFilter filter = VltUtils.createFilter(paths, true); + ExportOptions opts = VltUtils.getExportOptions(filter, paths, groupName, packageName, version); + + vaultPackage = VltUtils.createPackage(packaging.getPackageManager(), session, opts, tempDirectory); + + uploadPackage(session, vaultPackage); + session.save(); + } catch (Exception e) { + VltUtils.deletePackage(vaultPackage); + } finally { + if (session != null && session.isLive()) { + session.logout(); + session = null; + } + } + + return true; + } + + /** + * Create a Publick backup package. + * + * @param request The Sling HTTP servlet request + * @param packageName The name of the package + * @return true if package is created successfully + */ + public boolean createBackupPackage(final SlingHttpServletRequest request, final String packageName) { + return createPackage(request, BACKUP_GROUP, packageName, BACKUP_VERSION, BACKUP_PATHS); + } + + /** + * Create package in the JCR under /etc/packages/group_name. + * {@link org.apache.sling.distribution.serialization.impl.vlt.JcrVaultDistributionPackageBuilder#uploadPackage} + * + * @param session The current session + * @param pack the Vault Package to upload + * @return the JCR Package from the uploaded file + * @throws IOException + * @throws RepositoryException + */ + private JcrPackage uploadPackage(Session session, VaultPackage pack) throws IOException, RepositoryException { + JcrPackageManager packageManager = packaging.getPackageManager(session); + + InputStream in = FileUtils.openInputStream(pack.getFile()); + + try { + JcrPackage jcrPackage = packageManager.upload(in, true); + return jcrPackage; + } finally { + IOUtils.closeQuietly(in); + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/nateyolles/sling/publick/services/impl/SystemSettingsServiceImpl.java b/core/src/main/java/com/nateyolles/sling/publick/services/impl/SystemSettingsServiceImpl.java index 6fd39f6..18cbda1 100644 --- a/core/src/main/java/com/nateyolles/sling/publick/services/impl/SystemSettingsServiceImpl.java +++ b/core/src/main/java/com/nateyolles/sling/publick/services/impl/SystemSettingsServiceImpl.java @@ -114,4 +114,23 @@ public boolean getExtensionlessUrls() { public boolean setExtensionlessUrls(final boolean value) { return osgiService.setProperty(COMPONENT_PID, SYSTEM_EXTENSIONLESS_URLS, value); } + + /** + * Get the setting for temporary directory. + * + * @return The setting for temporary directory. + */ + public String getTemporaryDirectory() { + return osgiService.getStringProperty(COMPONENT_PID, SYSTEM_TEMPORARY_DIRECTORY, null); + } + + /** + * Set the value for temporary directory. + * + * @param value The setting for the temporary directory. + * @return true if the save was successful. + */ + public boolean setTemporaryDirectory(final String directory) { + return osgiService.setProperty(COMPONENT_PID, SYSTEM_TEMPORARY_DIRECTORY, directory); + } } \ No newline at end of file diff --git a/core/src/main/java/com/nateyolles/sling/publick/servlets/SystemConfigServlet.java b/core/src/main/java/com/nateyolles/sling/publick/servlets/SystemConfigServlet.java index 1d117f1..174cb7a 100644 --- a/core/src/main/java/com/nateyolles/sling/publick/servlets/SystemConfigServlet.java +++ b/core/src/main/java/com/nateyolles/sling/publick/servlets/SystemConfigServlet.java @@ -44,6 +44,9 @@ public class SystemConfigServlet extends AdminServlet { /** The extensionless URLs request parameter */ private static final String EXTENSIONLESS_URLS_PROPERTY = "extensionlessUrls"; + /** The temporary directory request parameter */ + private static final String TEMPORARY_DIRECTORY_PROPERTY = "temporaryDirectory"; + /** * Save system properties on POST. * @@ -63,11 +66,13 @@ protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse if (allowWrite) { final String blogName = request.getParameter(BLOG_NAME_PROPERTY); final boolean extensionlessUrls = Boolean.parseBoolean(request.getParameter(EXTENSIONLESS_URLS_PROPERTY)); + final String tempDir = request.getParameter(TEMPORARY_DIRECTORY_PROPERTY); final Map properties = new HashMap(); properties.put(SystemSettingsService.SYSTEM_BLOG_NAME, blogName); properties.put(SystemSettingsService.SYSTEM_EXTENSIONLESS_URLS, extensionlessUrls); + properties.put(SystemSettingsService.SYSTEM_TEMPORARY_DIRECTORY, tempDir); boolean result = systemSettingsService.setProperties(properties); diff --git a/core/src/main/java/com/nateyolles/sling/publick/servlets/admin/BackupServlet.java b/core/src/main/java/com/nateyolles/sling/publick/servlets/admin/BackupServlet.java new file mode 100644 index 0000000..d69098b --- /dev/null +++ b/core/src/main/java/com/nateyolles/sling/publick/servlets/admin/BackupServlet.java @@ -0,0 +1,107 @@ +package com.nateyolles.sling.publick.servlets.admin; + +import com.nateyolles.sling.publick.PublickConstants; +import com.nateyolles.sling.publick.services.PackageService; +import com.nateyolles.sling.publick.services.UserService; +import com.nateyolles.sling.publick.servlets.AdminServlet; + +import org.apache.commons.lang.CharEncoding; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.sling.SlingServlet; +import org.apache.jackrabbit.vault.packaging.JcrPackage; +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.SlingHttpServletResponse; +import org.apache.sling.commons.json.JSONArray; +import org.apache.sling.commons.json.JSONException; +import org.apache.sling.commons.json.JSONObject; + +import javax.jcr.RepositoryException; +import javax.servlet.ServletException; + +import java.io.IOException; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Servlet for getting, creating, uploading, installing and deleting + * backup packages. A get request will provide a list of all packages. + * Post requests will perform functions on an individual package. + */ +@SlingServlet(paths = PublickConstants.SERVLET_PATH_ADMIN + "/backup") +public class BackupServlet extends AdminServlet { + + /** The logger. */ + private static final Logger LOGGER = LoggerFactory.getLogger(BackupServlet.class); + + /** Service to determine if the current user has write permissions. */ + @Reference + private UserService userService; + + /** Service to get, install, upload, create and delete packages. */ + @Reference + private PackageService packageService; + + /** Display format for the package creation date */ + private static final String DATE_FORMAT = "yyyy-MM-dd hh:mm:ss"; + + /** + * Return all packages on a GET request in order of newest to oldest. + */ + @Override + protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) + throws ServletException, IOException { + + final PrintWriter writer = response.getWriter(); + final SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT); + response.setCharacterEncoding(CharEncoding.UTF_8); + response.setContentType("application/json"); + + List packages = packageService.getPackageList(request); + + try { + JSONArray jsonArray = new JSONArray(); + + for (JcrPackage jcrPackage : packages) { + final JSONObject json = new JSONObject(); + + json.put("size", getSize(jcrPackage.getPackage().getSize())); + json.put("date", dateFormat.format(jcrPackage.getPackage().getCreated().getTime())); + json.put("name", jcrPackage.getDefinition().getId().getName()); + json.put("path", jcrPackage.getNode().getPath()); + json.put("id", jcrPackage.getDefinition().getId().toString()); + + jsonArray.put(json); + } + + response.setStatus(SlingHttpServletResponse.SC_OK); + writer.write(jsonArray.toString()); + } catch (JSONException | RepositoryException e) { + LOGGER.error("Could not write JSON", e); + response.setStatus(SlingHttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + + /** + * Convert package bytes to kilobytes. + * + * @param size Size in bytes; -1 if not found. + * @return Size in kilobytes with extension + */ + private String getSize(final long size) { + String value; + + if (size == -1) { + value = "N/A"; + } else if (size <= 1000) { + value = "1kb"; + } else { + value = size / 1000 + "kb"; + } + + return value; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/nateyolles/sling/publick/utils/VltUtils.java b/core/src/main/java/com/nateyolles/sling/publick/utils/VltUtils.java new file mode 100644 index 0000000..a4c1a76 --- /dev/null +++ b/core/src/main/java/com/nateyolles/sling/publick/utils/VltUtils.java @@ -0,0 +1,259 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * CHANGE: Make #createFilter take a String array as opposed + * to a DistributionRequest. + */ +package com.nateyolles.sling.publick.utils; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.jackrabbit.vault.fs.api.ImportMode; +import org.apache.jackrabbit.vault.fs.api.PathFilterSet; +import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter; +import org.apache.jackrabbit.vault.fs.config.DefaultMetaInf; +import org.apache.jackrabbit.vault.fs.config.DefaultWorkspaceFilter; +import org.apache.jackrabbit.vault.fs.config.MetaInf; +import org.apache.jackrabbit.vault.fs.filter.DefaultPathFilter; +import org.apache.jackrabbit.vault.fs.io.AccessControlHandling; +import org.apache.jackrabbit.vault.fs.io.ImportOptions; +import org.apache.jackrabbit.vault.packaging.ExportOptions; +import org.apache.jackrabbit.vault.packaging.JcrPackage; +import org.apache.jackrabbit.vault.packaging.PackageManager; +import org.apache.jackrabbit.vault.packaging.VaultPackage; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; +import java.util.Properties; + +/** + * Utility class for creating vlt filters and import/export options + */ +public class VltUtils { + + /* Changed to accept String[] instead of DistributionRequest. */ + public static WorkspaceFilter createFilter(final String[] paths, final boolean deep) { + DefaultWorkspaceFilter filter = new DefaultWorkspaceFilter(); + + for (String path : paths) { + PathFilterSet filterSet = createFilterSet(path, deep); + filter.add(filterSet); + } + + return filter; + } + + public static String[] getPaths(MetaInf metaInf) { + if (metaInf == null) { + return null; + } + + WorkspaceFilter filter = metaInf.getFilter(); + if (filter == null) { + filter = new DefaultWorkspaceFilter(); + } + List filterSets = filter.getFilterSets(); + String[] paths = new String[filterSets.size()]; + for (int i = 0; i < paths.length; i++) { + paths[i] = filterSets.get(i).getRoot(); + } + + return paths; + } + + private static PathFilterSet createFilterSet(String path, boolean deep) { + PathFilterSet filterSet = new PathFilterSet(path); + + if (!deep) { + filterSet.addInclude(new DefaultPathFilter(path)); + } + return filterSet; + } + + public static ExportOptions getExportOptions(WorkspaceFilter filter, String[] packageRoots, + String packageGroup, + String packageName, + String packageVersion) { + DefaultMetaInf inf = new DefaultMetaInf(); + ExportOptions opts = new ExportOptions(); + inf.setFilter(filter); + + Properties props = new Properties(); + props.setProperty(VaultPackage.NAME_GROUP, packageGroup); + props.setProperty(VaultPackage.NAME_NAME, packageName); + props.setProperty(VaultPackage.NAME_VERSION, packageVersion); + inf.setProperties(props); + + opts.setMetaInf(inf); + + String root = getPackageRoot(filter.getFilterSets(), packageRoots); + opts.setRootPath(root); + opts.setMountPath(root); + + return opts; + } + + + /** + * Picks a package root that dominates all filter sets. If there is none then "/" is returned. + */ + private static String getPackageRoot(List filterSets, String[] packageRoots) { + + String packageRoot = null; + + if (packageRoots != null && packageRoots.length > 0) { + for (String currentRoot : packageRoots) { + boolean filtersHaveCommonRoot = true; + + + for (PathFilterSet filterSet : filterSets) { + String filterSetRoot = filterSet.getRoot(); + + if (!filterSetRoot.startsWith(currentRoot)) { + filtersHaveCommonRoot = false; + } + } + + if (filtersHaveCommonRoot) { + packageRoot = currentRoot; + break; + } + } + + } + + + + if (packageRoot == null || !packageRoot.startsWith("/")) { + packageRoot = "/"; + } + + return packageRoot; + + } + + public static ImportOptions getImportOptions(AccessControlHandling aclHandling, ImportMode importMode) { + ImportOptions opts = new ImportOptions(); + if (aclHandling != null) { + opts.setAccessControlHandling(aclHandling); + } + else { + // default to overwrite + opts.setAccessControlHandling(AccessControlHandling.OVERWRITE); + } + if (importMode != null) { + opts.setImportMode(importMode); + } + else { + // default to update + opts.setImportMode(ImportMode.UPDATE); + } + + return opts; + } + + public static VaultPackage createPackage(PackageManager packageManager, Session session, ExportOptions options, File tempFolder) throws IOException, RepositoryException { + File file = File.createTempFile("distr-vault-create-" + System.nanoTime(), ".zip", tempFolder); + + try { + VaultPackage vaultPackage = packageManager.assemble(session, options, file); + return vaultPackage; + } catch (RepositoryException e) { + FileUtils.deleteQuietly(file); + throw e; + } + } + + public static VaultPackage readPackage(PackageManager packageManager, InputStream stream, File tempFolder) throws IOException { + File file = File.createTempFile("distr-vault-read-" + System.nanoTime(), ".zip", tempFolder); + OutputStream out = FileUtils.openOutputStream(file); + try { + IOUtils.copy(stream, out); + return packageManager.open(file); + } catch (IOException e) { + FileUtils.deleteQuietly(file); + throw e; + } finally { + IOUtils.closeQuietly(stream); + IOUtils.closeQuietly(out); + } + } + + public static void deletePackage(VaultPackage vaultPackage) { + if (vaultPackage == null) { + return; + } + + File file = vaultPackage.getFile(); + vaultPackage.close(); + + FileUtils.deleteQuietly(file); + } + + public static void deletePackage(JcrPackage jcrPackage) { + if (jcrPackage == null) { + return; + } + + Node node = jcrPackage.getNode(); + jcrPackage.close(); + + try { + if (node != null) { + node.remove(); + } + } catch (RepositoryException e) { + // do nothing + } + } + + public static File getTempFolder(String tempFolderPath) { + File directory = null; + try { + directory = new File(tempFolderPath); + if (!directory.exists() || !directory.isDirectory()) { + directory = null; + } + } catch (Throwable e) { + directory = null; + } + + return directory; + } + + + public static String findParent(String path, String nodeName) { + path = path.endsWith("/") ? path : path + "/"; + + nodeName = "/" + nodeName + "/"; + + int idx = path.indexOf(nodeName); + + if (idx < 0) { + return null; + } + + return path.substring(0, idx); + } +} \ No newline at end of file diff --git a/ui/Gruntfile.js b/ui/Gruntfile.js index bef0c12..66e765b 100644 --- a/ui/Gruntfile.js +++ b/ui/Gruntfile.js @@ -33,7 +33,9 @@ module.exports = function(grunt) { '<%=conf.dist%>/js/userService.js', '<%=conf.dist%>/js/settingsService.js', '<%=conf.dist%>/js/commentService.js', - '<%=conf.dist%>/js/commentModalController.js' + '<%=conf.dist%>/js/commentModalController.js', + '<%=conf.dist%>/js/backupController.js', + '<%=conf.dist%>/js/backupService.js' ], dest: '<%=conf.src%>/js/admin.js' } diff --git a/ui/src/main/resources/jcr_root/content/admin/backups.json b/ui/src/main/resources/jcr_root/content/admin/backups.json new file mode 100644 index 0000000..4f46e84 --- /dev/null +++ b/ui/src/main/resources/jcr_root/content/admin/backups.json @@ -0,0 +1,15 @@ +{ + "jcr:primaryType": "publick:page", + "sling:resourceType" : "publick/pages/adminSecurePage", + "title" : "Backups", + "jcr:content" : { + "mainContent" : { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType" : "publick/components/foundation/parsys", + "backupList" : { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType" : "publick/components/admin/backupList" + } + } + } +} \ No newline at end of file diff --git a/ui/src/main/resources/jcr_root/etc/clientlibs/admin/js/backupController.js b/ui/src/main/resources/jcr_root/etc/clientlibs/admin/js/backupController.js new file mode 100644 index 0000000..fac1fdb --- /dev/null +++ b/ui/src/main/resources/jcr_root/etc/clientlibs/admin/js/backupController.js @@ -0,0 +1,29 @@ +/** + * Angular controller to upload, create, install, delete and list backup + * packages. + */ +app.controller('BackupController', function($scope, $modal, BackupService) { + + $scope.packages = []; + + $scope.install = function(index) { + alert('TODO: install'); + }; + + $scope.delete = function(index) { + alert('TODO: delete'); + }; + + $scope.upload = function() { + alert('TODO: upload'); + }; + + $scope.create = function() { + alert('TODO: create'); + }; + + /* Get all packages on load */ + BackupService.getPackages().success(function(data){ + $scope.packages = data; + }); +}); \ No newline at end of file diff --git a/ui/src/main/resources/jcr_root/etc/clientlibs/admin/js/backupService.js b/ui/src/main/resources/jcr_root/etc/clientlibs/admin/js/backupService.js new file mode 100644 index 0000000..f6f022f --- /dev/null +++ b/ui/src/main/resources/jcr_root/etc/clientlibs/admin/js/backupService.js @@ -0,0 +1,30 @@ +/** + * Angular service to communicate with the Backup Admin Servlet. This service + * will get all packages, create packages, delete packages, install packages, + * and upload packages. + */ +app.factory('BackupService', function($http, formDataObject) { + var backupFactory = {}, + PATH = '/bin/admin/backup'; + + /** + * @private + */ + function post(data) { + return $http({ + method: 'POST', + url: PATH, + data: data, + transformRequest: formDataObject + }); + } + + backupFactory.getPackages = function() { + return $http({ + method: 'GET', + url: PATH + }); + }; + + return backupFactory; +}); \ No newline at end of file diff --git a/ui/src/main/resources/jcr_root/libs/publick/components/admin/backupList/backupList.html b/ui/src/main/resources/jcr_root/libs/publick/components/admin/backupList/backupList.html new file mode 100644 index 0000000..0bb52c3 --- /dev/null +++ b/ui/src/main/resources/jcr_root/libs/publick/components/admin/backupList/backupList.html @@ -0,0 +1,28 @@ +
+
+
+ + + + + + + + + + + + + + + + + + + + + +
#PackageDateSizeInstallDelete
+
+
+
\ No newline at end of file diff --git a/ui/src/main/resources/jcr_root/libs/publick/components/admin/systemConfig/systemConfig.html b/ui/src/main/resources/jcr_root/libs/publick/components/admin/systemConfig/systemConfig.html index 41e26b6..df31bec 100644 --- a/ui/src/main/resources/jcr_root/libs/publick/components/admin/systemConfig/systemConfig.html +++ b/ui/src/main/resources/jcr_root/libs/publick/components/admin/systemConfig/systemConfig.html @@ -25,6 +25,15 @@

Settings

+
+
+
+ + + The file system temporary directory where packages are created. +
+
+
diff --git a/ui/src/main/resources/jcr_root/libs/publick/pages/adminSecurePage/sidebar.html b/ui/src/main/resources/jcr_root/libs/publick/pages/adminSecurePage/sidebar.html index 79e139c..cd1dd41 100644 --- a/ui/src/main/resources/jcr_root/libs/publick/pages/adminSecurePage/sidebar.html +++ b/ui/src/main/resources/jcr_root/libs/publick/pages/adminSecurePage/sidebar.html @@ -24,6 +24,9 @@ +