浏览代码

fix #2217 add theme

Shinsuke Sugaya 5 年之前
父节点
当前提交
c36d96bb4f

+ 2 - 3
src/main/java/org/codelibs/fess/app/web/admin/plugin/AdminPluginAction.java

@@ -25,7 +25,6 @@ import java.util.stream.Collectors;
 import org.codelibs.fess.app.web.base.FessAdminAction;
 import org.codelibs.fess.helper.PluginHelper;
 import org.codelibs.fess.helper.PluginHelper.Artifact;
-import org.codelibs.fess.helper.PluginHelper.ArtifactType;
 import org.codelibs.fess.util.ComponentUtil;
 import org.codelibs.fess.util.RenderDataUtil;
 import org.lastaflute.web.Execute;
@@ -108,7 +107,7 @@ public class AdminPluginAction extends FessAdminAction {
 
     public static Map<String, String> beanToMap(final Artifact artifact) {
         final Map<String, String> item = new HashMap<>();
-        item.put("type", ArtifactType.getType(artifact.getName()).getId());
+        item.put("type", artifact.getType().getId());
         item.put("id", artifact.getName() + ":" + artifact.getVersion());
         item.put("name", artifact.getName());
         item.put("version", artifact.getVersion());
@@ -124,7 +123,7 @@ public class AdminPluginAction extends FessAdminAction {
     public static void installArtifact(final Artifact artifact) {
         new Thread(() -> {
             final PluginHelper pluginHelper = ComponentUtil.getPluginHelper();
-            final Artifact[] artifacts = pluginHelper.getInstalledArtifacts(ArtifactType.getType(artifact.getName()));
+            final Artifact[] artifacts = pluginHelper.getInstalledArtifacts(artifact.getType());
             try {
                 pluginHelper.installArtifact(artifact);
             } catch (final Exception e) {

+ 30 - 0
src/main/java/org/codelibs/fess/exception/ThemeException.java

@@ -0,0 +1,30 @@
+/*
+ * Copyright 2012-2019 CodeLibs Project and the Others.
+ *
+ * Licensed 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.
+ */
+package org.codelibs.fess.exception;
+
+public class ThemeException extends FessSystemException {
+
+    private static final long serialVersionUID = 1L;
+
+    public ThemeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public ThemeException(String message) {
+        super(message);
+    }
+
+}

+ 30 - 8
src/main/java/org/codelibs/fess/helper/PluginHelper.java

@@ -30,7 +30,6 @@ import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import javax.servlet.ServletContext;
 import javax.xml.XMLConstants;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
@@ -193,14 +192,35 @@ public class PluginHelper {
         } catch (final Exception e) {
             throw new PluginException("Failed to install the artifact " + artifact.getName(), e);
         }
+
+        switch (artifact.getType()) {
+        case DATA_STORE:
+            break;
+        case THEME:
+            ComponentUtil.getThemeHelper().install(artifact);
+            break;
+        default:
+            break;
+        }
     }
 
     public void deleteInstalledArtifact(final Artifact artifact) {
         final String fileName = artifact.getFileName();
-        final Path jarPath = Paths.get(getPluginPath().toString(), fileName);
+        final Path jarPath = Paths.get(ResourceUtil.getPluginPath().toString(), fileName);
         if (!Files.exists(jarPath)) {
             throw new PluginException(fileName + " does not exist.");
         }
+
+        switch (artifact.getType()) {
+        case DATA_STORE:
+            break;
+        case THEME:
+            ComponentUtil.getThemeHelper().uninstall(artifact);
+            break;
+        default:
+            break;
+        }
+
         try {
             Files.delete(jarPath);
         } catch (final IOException e) {
@@ -220,10 +240,6 @@ public class PluginHelper {
         return null;
     }
 
-    protected Path getPluginPath() {
-        return Paths.get(ComponentUtil.getComponent(ServletContext.class).getRealPath("/WEB-INF/plugin"));
-    }
-
     public static class Artifact {
         protected final String name;
         protected final String version;
@@ -255,14 +271,18 @@ public class PluginHelper {
             return url;
         }
 
+        public ArtifactType getType() {
+            return ArtifactType.getType(name);
+        }
+
         @Override
         public String toString() {
-            return "Artifact [name=" + name + ", version=" + version + "]";
+            return name + ":" + version;
         }
     }
 
     public enum ArtifactType {
-        DATA_STORE("fess-ds"), UNKNOWN("unknown");
+        DATA_STORE("fess-ds"), THEME("fess-theme"), UNKNOWN("unknown");
 
         private final String id;
 
@@ -277,6 +297,8 @@ public class PluginHelper {
         public static ArtifactType getType(final String name) {
             if (name.startsWith(DATA_STORE.getId())) {
                 return DATA_STORE;
+            } else if (name.startsWith(THEME.getId())) {
+                return THEME;
             }
             return UNKNOWN;
         }

+ 130 - 0
src/main/java/org/codelibs/fess/helper/ThemeHelper.java

@@ -0,0 +1,130 @@
+/*
+ * Copyright 2012-2019 CodeLibs Project and the Others.
+ *
+ * Licensed 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.
+ */
+package org.codelibs.fess.helper;
+
+import java.io.IOException;
+import java.nio.file.FileVisitOption;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Comparator;
+import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.codelibs.core.lang.StringUtil;
+import org.codelibs.fess.exception.ThemeException;
+import org.codelibs.fess.helper.PluginHelper.Artifact;
+import org.codelibs.fess.helper.PluginHelper.ArtifactType;
+import org.codelibs.fess.util.ResourceUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ThemeHelper {
+    private static final Logger logger = LoggerFactory.getLogger(ThemeHelper.class);
+
+    public void install(Artifact artifact) {
+        Path jarPath = getJarFile(artifact);
+        String themeName = getThemeName(artifact);
+        if (logger.isDebugEnabled()) {
+            logger.debug("Theme: " + themeName);
+        }
+        try (ZipInputStream zis = new ZipInputStream(Files.newInputStream(jarPath))) {
+            ZipEntry entry;
+            while ((entry = zis.getNextEntry()) != null) {
+                if (!entry.isDirectory()) {
+                    String[] names = entry.getName().split("/");
+                    if (names.length < 2) {
+                        continue;
+                    }
+                    if (logger.isDebugEnabled()) {
+                        logger.debug("Loading " + entry.getName());
+                    }
+                    if ("view".equals(names[0])) {
+                        names[0] = themeName;
+                        Path path = ResourceUtil.getViewTemplatePath(names);
+                        Files.createDirectories(path.getParent());
+                        Files.copy(zis, path);
+                    } else if ("css".equals(names[0])) {
+                        names[0] = themeName;
+                        Path path = ResourceUtil.getCssPath(names);
+                        Files.createDirectories(path.getParent());
+                        Files.copy(zis, path);
+                    } else if ("js".equals(names[0])) {
+                        names[0] = themeName;
+                        Path path = ResourceUtil.getJavaScriptPath(names);
+                        Files.createDirectories(path.getParent());
+                        Files.copy(zis, path);
+                    } else if ("images".equals(names[0])) {
+                        names[0] = themeName;
+                        Path path = ResourceUtil.getImagePath(names);
+                        Files.createDirectories(path.getParent());
+                        Files.copy(zis, path);
+                    }
+                }
+            }
+        } catch (IOException e) {
+            throw new ThemeException("Failed to install " + artifact);
+        }
+    }
+
+    public void uninstall(Artifact artifact) {
+        String themeName = getThemeName(artifact);
+
+        Path viewPath = ResourceUtil.getViewTemplatePath(themeName);
+        closeQuietly(viewPath);
+        Path imagePath = ResourceUtil.getImagePath(themeName);
+        closeQuietly(imagePath);
+        Path cssPath = ResourceUtil.getCssPath(themeName);
+        closeQuietly(cssPath);
+        Path jsPath = ResourceUtil.getJavaScriptPath(themeName);
+        closeQuietly(jsPath);
+    }
+
+    protected String getThemeName(Artifact artifact) {
+        String themeName = artifact.getName().substring(ArtifactType.THEME.getId().length() + 1);
+        if (StringUtil.isBlank(themeName)) {
+            throw new ThemeException("Theme name is empty: " + artifact);
+        }
+        return themeName;
+    }
+
+    protected void closeQuietly(Path dir) {
+        try (Stream<Path> walk = Files.walk(dir, FileVisitOption.FOLLOW_LINKS)) {
+            walk.sorted(Comparator.reverseOrder()).forEach(f -> {
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Deleting " + f);
+                }
+                try {
+                    Files.delete(f);
+                } catch (IOException e) {
+                    logger.warn("Failed to delete " + f, e);
+                }
+            });
+            Files.deleteIfExists(dir);
+        } catch (IOException e) {
+            logger.warn("Failed to delete " + dir, e);
+        }
+    }
+
+    protected Path getJarFile(Artifact artifact) {
+        Path jarPath = ResourceUtil.getPluginPath(artifact.getFileName());
+        if (!Files.exists(jarPath)) {
+            throw new ThemeException(artifact.getFileName() + " does not exist.");
+        }
+        return jarPath;
+    }
+
+}

+ 7 - 0
src/main/java/org/codelibs/fess/util/ComponentUtil.java

@@ -61,6 +61,7 @@ import org.codelibs.fess.helper.SambaHelper;
 import org.codelibs.fess.helper.SearchLogHelper;
 import org.codelibs.fess.helper.SuggestHelper;
 import org.codelibs.fess.helper.SystemHelper;
+import org.codelibs.fess.helper.ThemeHelper;
 import org.codelibs.fess.helper.UserAgentHelper;
 import org.codelibs.fess.helper.UserInfoHelper;
 import org.codelibs.fess.helper.ViewHelper;
@@ -90,6 +91,8 @@ public final class ComponentUtil {
 
     private static Map<String, Object> componentMap = new HashMap<>();
 
+    private static final String THEME_HELPER = "themeHelper";
+
     private static final String PLUGIN_HELPER = "pluginHelper";
 
     private static final String LANGUAGE_HELPER = "languageHelper";
@@ -448,6 +451,10 @@ public final class ComponentUtil {
         return getComponent(PLUGIN_HELPER);
     }
 
+    public static ThemeHelper getThemeHelper() {
+        return getComponent(THEME_HELPER);
+    }
+
     public static <T> T getComponent(final Class<T> clazz) {
         try {
             return SingletonLaContainer.getComponent(clazz);

+ 33 - 15
src/main/java/org/codelibs/fess/util/ResourceUtil.java

@@ -80,50 +80,62 @@ public class ResourceUtil {
         if (StringUtil.isNotBlank(confPath)) {
             return Paths.get(confPath, names);
         }
-        return getPath("conf", names);
+        return getPath("WEB-INF/", "conf", names);
     }
 
     public static Path getClassesPath(final String... names) {
-        return getPath("classes", names);
+        return getPath("WEB-INF/", "classes", names);
     }
 
     public static Path getOrigPath(final String... names) {
-        return getPath("orig", names);
+        return getPath("WEB-INF/", "orig", names);
     }
 
     public static Path getMailTemplatePath(final String... names) {
-        return getPath("mail", names);
+        return getPath("WEB-INF/", "mail", names);
     }
 
     public static Path getViewTemplatePath(final String... names) {
-        return getPath("view", names);
+        return getPath("WEB-INF/", "view", names);
     }
 
     public static Path getDictionaryPath(final String... names) {
-        return getPath("dict", names);
+        return getPath("WEB-INF/", "dict", names);
     }
 
     public static Path getThumbnailPath(final String... names) {
-        return getPath("thumbnails", names);
+        return getPath("WEB-INF/", "thumbnails", names);
     }
 
     public static Path getSitePath(final String... names) {
-        return getPath("site", names);
+        return getPath("WEB-INF/", "site", names);
     }
 
     public static Path getPluginPath(final String... names) {
-        return getPath("plugin", names);
+        return getPath("WEB-INF/", "plugin", names);
     }
 
     public static Path getProjectPropertiesFile() {
-        return getPath("", "project.properties");
+        return getPath("WEB-INF/", StringUtil.EMPTY, "project.properties");
     }
 
-    protected static Path getPath(final String base, final String... names) {
+    public static Path getImagePath(final String... names) {
+        return getPath(StringUtil.EMPTY, "images", names);
+    }
+
+    public static Path getCssPath(final String... names) {
+        return getPath(StringUtil.EMPTY, "css", names);
+    }
+
+    public static Path getJavaScriptPath(final String... names) {
+        return getPath(StringUtil.EMPTY, "js", names);
+    }
+
+    protected static Path getPath(final String root, final String base, final String... names) {
 
         try {
             final ServletContext servletContext = ComponentUtil.getComponent(ServletContext.class);
-            final String webinfPath = servletContext.getRealPath("/WEB-INF/" + base);
+            final String webinfPath = servletContext.getRealPath("/" + root + base);
             if (webinfPath != null) {
                 if (Files.exists(Paths.get(webinfPath))) {
                     return Paths.get(webinfPath, names);
@@ -132,15 +144,15 @@ public class ResourceUtil {
         } catch (final Throwable e) {
             // ignore
         }
-        final String webinfBase = "WEB-INF/" + base;
+        final String webinfBase = root + base;
         if (Files.exists(Paths.get(webinfBase))) {
             return Paths.get(webinfBase, names);
         }
-        final String srcWebInfBase = "src/main/webapps/WEB-INF/" + base;
+        final String srcWebInfBase = "src/main/webapps" + root + base;
         if (Files.exists(Paths.get(srcWebInfBase))) {
             return Paths.get(srcWebInfBase, names);
         }
-        final String targetWebInfBase = "target/fess/WEB-INF/" + base;
+        final String targetWebInfBase = "target/fess/" + root + base;
         if (Files.exists(Paths.get(targetWebInfBase))) {
             return Paths.get(targetWebInfBase, names);
         }
@@ -157,6 +169,9 @@ public class ResourceUtil {
             return new File[0];
         }
         final File libDir = new File(libPath);
+        if (!libDir.exists()) {
+            return new File[0];
+        }
         return libDir.listFiles((FilenameFilter) (file, name) -> name.startsWith(namePrefix));
     }
 
@@ -170,6 +185,9 @@ public class ResourceUtil {
             return new File[0];
         }
         final File libDir = new File(libPath);
+        if (!libDir.exists()) {
+            return new File[0];
+        }
         return libDir.listFiles((FilenameFilter) (file, name) -> name.startsWith(namePrefix));
     }
 

+ 2 - 0
src/main/resources/app.xml

@@ -34,6 +34,8 @@
 	</component>
 	<component name="pluginHelper" class="org.codelibs.fess.helper.PluginHelper">
 	</component>
+	<component name="themeHelper" class="org.codelibs.fess.helper.ThemeHelper">
+	</component>
 	<component name="queryStringBuilder" class="org.codelibs.fess.util.QueryStringBuilder" instance="prototype">
 	</component>
 	<component name="queryParser" class="org.apache.lucene.queryparser.ext.ExtendableQueryParser" instance="prototype">