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-logging1.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 @@
+
+
+
+
+
+
+
#
+
Package
+
Date
+
Size
+
Install
+
Delete
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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.
+