From b4911a2b2f7077234ce61a62921073ca54c13fbf Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Fri, 26 Feb 2010 16:58:01 +0000
Subject: [PATCH] support plugin themes

---
 .../net/i2p/router/web/ConfigUIHelper.java    | 10 ++++
 .../src/net/i2p/router/web/PluginStarter.java | 52 +++++++++++++++----
 apps/routerconsole/jsp/viewtheme.jsp          | 24 ++++++++-
 3 files changed, 75 insertions(+), 11 deletions(-)

diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUIHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUIHelper.java
index 50a949fe13..97f1a5254c 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUIHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUIHelper.java
@@ -1,6 +1,7 @@
 package net.i2p.router.web;
 
 import java.io.File;
+import java.util.Iterator;
 import java.util.TreeSet;
 import java.util.Set;
 
@@ -19,6 +20,8 @@ public class ConfigUIHelper extends HelperBase {
         return buf.toString();
     }
 
+    static final String PROP_THEME_PFX = "routerconsole.theme.";
+
     /** @return standard and user-installed themes, sorted (untranslated) */
     private Set<String> themeSet() {
          Set<String> rv = new TreeSet();
@@ -33,6 +36,13 @@ public class ConfigUIHelper extends HelperBase {
              if (files[i].isDirectory() && ! name.equals("images"))
                  rv.add(name);
          }
+         // user themes
+         Set props = _context.getPropertyNames();
+         for (Iterator iter = props.iterator(); iter.hasNext(); ) {
+              String prop = (String) iter.next();
+              if (prop.startsWith(PROP_THEME_PFX) && prop.length() > PROP_THEME_PFX.length())
+                  rv.add(prop.substring(PROP_THEME_PFX.length()));
+         }
          return rv;
     }
 
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java
index c844f936a3..030f7670b1 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java
@@ -6,6 +6,7 @@ import java.lang.reflect.Method;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -26,7 +27,9 @@ import org.mortbay.jetty.Server;
 
 
 /**
- *  Start plugins that are already installed
+ *  Start/stop/delete plugins that are already installed
+ *  Get properties of installed plugins
+ *  Get or change settings in plugins.config
  *
  *  @since 0.7.12
  *  @author zzz
@@ -35,6 +38,10 @@ public class PluginStarter implements Runnable {
     private RouterContext _context;
     static final String PREFIX = "plugin.";
     static final String ENABLED = ".startOnLoad";
+    private static final String[] STANDARD_WEBAPPS = { "i2psnark", "i2ptunnel", "susidns",
+                                                       "susimail", "addressbook", "routerconsole" };
+    private static final String[] STANDARD_THEMES = { "images", "light", "dark", "classic",
+                                                      "midnight" };
 
     public PluginStarter(RouterContext ctx) {
         _context = ctx;
@@ -81,6 +88,18 @@ public class PluginStarter implements Runnable {
         }
         //log.error("Starting plugin: " + appName);
 
+        // register themes
+        File dir = new File(pluginDir, "console/themes");
+        File[] tfiles = dir.listFiles();
+        if (tfiles != null) {
+            for (int i = 0; i < tfiles.length; i++) {
+                String name = tfiles[i].getName();
+                if (tfiles[i].isDirectory() && (!Arrays.asList(STANDARD_THEMES).contains(tfiles[i])))
+                    ctx.router().setConfigSetting(ConfigUIHelper.PROP_THEME_PFX + name, tfiles[i].getAbsolutePath());
+                    // we don't need to save
+            }
+        }
+
         // load and start things in clients.config
         File clientConfig = new File(pluginDir, "clients.config");
         if (clientConfig.exists()) {
@@ -103,9 +122,7 @@ public class PluginStarter implements Runnable {
                         String warName = fileNames[i].substring(0, fileNames[i].lastIndexOf(".war"));
                         //log.error("Found webapp: " + warName);
                         // check for duplicates in $I2P
-                        // easy way for now...
-                        if (warName.equals("i2psnark") || warName.equals("susidns") || warName.equals("i2ptunnel") ||
-                            warName.equals("susimail") || warName.equals("addressbook")) {
+                        if (Arrays.asList(STANDARD_WEBAPPS).contains(warName)) {
                             log.error("Skipping duplicate webapp " + warName + " in plugin " + appName);
                             continue;
                         }
@@ -147,8 +164,6 @@ public class PluginStarter implements Runnable {
             }
         }
 
-        // add themes in console/themes
-
         // add summary bar link
         Properties props = pluginProperties(ctx, appName);
         String name = ConfigClientsHelper.stripHTML(props, "consoleLinkName_" + Messages.getLanguage(ctx));
@@ -192,8 +207,7 @@ public class PluginStarter implements Runnable {
             if (fileNames != null) {
                 for (int i = 0; i < fileNames.length; i++) {
                     String warName = fileNames[i].substring(0, fileNames[i].lastIndexOf(".war"));
-                    if (warName.equals("i2psnark") || warName.equals("susidns") || warName.equals("i2ptunnel") ||
-                        warName.equals("susimail") || warName.equals("addressbook")) {
+                    if (Arrays.asList(STANDARD_WEBAPPS).contains(warName)) {
                         continue;
                     }
                     WebAppStarter.stopWebApp(server, warName);
@@ -213,12 +227,12 @@ public class PluginStarter implements Runnable {
         return true;
     }
 
-    /** @return true on success - call stopPlugin() first */
+    /** @return true on success - caller should call stopPlugin() first */
     static boolean deletePlugin(RouterContext ctx, String appName) throws IOException {
         Log log = ctx.logManager().getLog(PluginStarter.class);
         File pluginDir = new File(ctx.getAppDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName);
         if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) {
-            log.error("Cannot stop nonexistent plugin: " + appName);
+            log.error("Cannot delete nonexistent plugin: " + appName);
             return false;
         }
         // uninstall things in clients.config
@@ -229,6 +243,24 @@ public class PluginStarter implements Runnable {
             List<ClientAppConfig> clients = ClientAppConfig.getClientApps(clientConfig);
             runClientApps(ctx, pluginDir, clients, "uninstall");
         }
+
+        // unregister themes, and switch to default if we are unregistering the current theme
+        File dir = new File(pluginDir, "console/themes");
+        File[] tfiles = dir.listFiles();
+        if (tfiles != null) {
+            String current = ctx.getProperty(CSSHelper.PROP_THEME_NAME);
+            for (int i = 0; i < tfiles.length; i++) {
+                String name = tfiles[i].getName();
+                if (tfiles[i].isDirectory() && (!name.equals("images")) && (!name.equals("classic")) &&
+                    (!name.equals("dark")) && (!name.equals("light")) && (!name.equals("midnight"))) {
+                    ctx.router().removeConfigSetting(ConfigUIHelper.PROP_THEME_PFX + name);
+                    if (name.equals(current))
+                        ctx.router().setConfigSetting(CSSHelper.PROP_THEME_NAME, CSSHelper.DEFAULT_THEME);
+                }
+            }
+            ctx.router().saveConfig();
+        }
+
         FileUtil.rmdir(pluginDir, false);
         Properties props = pluginProperties();
         for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) {
diff --git a/apps/routerconsole/jsp/viewtheme.jsp b/apps/routerconsole/jsp/viewtheme.jsp
index 07096cd039..c095cc54e9 100644
--- a/apps/routerconsole/jsp/viewtheme.jsp
+++ b/apps/routerconsole/jsp/viewtheme.jsp
@@ -20,7 +20,29 @@ if (uri.endsWith(".css")) {
   response.setContentType("image/x-icon");
 }
 response.setHeader("Cache-Control", "max-age=86400");  // cache for a day
-String base = net.i2p.I2PAppContext.getGlobalContext().getBaseDir().getAbsolutePath() +
+/*
+ * User or plugin themes
+ * If the request is for /themes/console/foo/bar/baz,
+ * and the property routerconsole.theme.foo=/path/to/foo,
+ * get the file from /path/to/foo/bar/baz
+ */
+String themePath = null;
+final String PFX = "/themes/console/";
+if (uri.startsWith(PFX) && uri.length() > PFX.length() + 1) {
+    String theme = uri.substring(PFX.length());
+    int slash = theme.indexOf('/');
+    if (slash > 0) {
+        theme = theme.substring(0, slash);
+        themePath = net.i2p.I2PAppContext.getGlobalContext().getProperty("routerconsole.theme." + theme);
+        if (themePath != null)
+            uri = uri.substring(PFX.length() + theme.length()); // /bar/baz
+    }
+}
+String base;
+if (themePath != null)
+    base = themePath;
+else
+    base = net.i2p.I2PAppContext.getGlobalContext().getBaseDir().getAbsolutePath() +
               java.io.File.separatorChar + "docs";
 net.i2p.util.FileUtil.readFile(uri, base, response.getOutputStream());
 %>
\ No newline at end of file
-- 
GitLab