diff --git a/datagear-analysis/src/main/java/org/datagear/analysis/ChartPluginManager.java b/datagear-analysis/src/main/java/org/datagear/analysis/ChartPluginManager.java index ee020a33..f420efde 100644 --- a/datagear-analysis/src/main/java/org/datagear/analysis/ChartPluginManager.java +++ b/datagear-analysis/src/main/java/org/datagear/analysis/ChartPluginManager.java @@ -24,10 +24,10 @@ public interface ChartPluginManager /** * 移除指定ID的{@linkplain ChartPlugin}。 * - * @param id + * @param ids * @return 被移除的{@linkplain ChartPlugin}或者{@code null}。 */ - ChartPlugin remove(String id); + ChartPlugin[] remove(String... ids); /** * 获取指定ID的{@linkplain ChartPlugin}。 diff --git a/datagear-analysis/src/main/java/org/datagear/analysis/support/AbstractChartPluginManager.java b/datagear-analysis/src/main/java/org/datagear/analysis/support/AbstractChartPluginManager.java index 7a8dbe91..0cdbb627 100644 --- a/datagear-analysis/src/main/java/org/datagear/analysis/support/AbstractChartPluginManager.java +++ b/datagear-analysis/src/main/java/org/datagear/analysis/support/AbstractChartPluginManager.java @@ -86,6 +86,21 @@ public abstract class AbstractChartPluginManager implements ChartPluginManager return put; } + /** + * 移除{@linkplain ChartPlugin}。 + * + * @param id + */ + protected ChartPlugin[] removeChartPlugins(String[] ids) + { + ChartPlugin[] removed = new ChartPlugin[ids.length]; + + for (int i = 0; i < ids.length; i++) + removed[i] = removeChartPlugin(ids[i]); + + return removed; + } + /** * 移除{@linkplain ChartPlugin}。 * @@ -128,8 +143,7 @@ public abstract class AbstractChartPluginManager implements ChartPluginManager * @return */ @SuppressWarnings("unchecked") - protected List> findChartPlugins( - Class renderContextType) + protected List> findChartPlugins(Class renderContextType) { List> reChartPlugins = new ArrayList>(); @@ -232,23 +246,27 @@ public abstract class AbstractChartPluginManager implements ChartPluginManager Version myVersion = null; Version oldVersion = null; - - if(!StringUtil.isEmpty(my.getVersion())) + + if (!StringUtil.isEmpty(my.getVersion())) { try { myVersion = Version.valueOf(my.getVersion()); } - catch(Exception e) {} + catch (Exception e) + { + } } - if(!StringUtil.isEmpty(old.getVersion())) + if (!StringUtil.isEmpty(old.getVersion())) { try { oldVersion = Version.valueOf(old.getVersion()); } - catch(Exception e) {} + catch (Exception e) + { + } } if (StringUtil.isEmpty(oldVersion) && StringUtil.isEmpty(myVersion)) @@ -258,7 +276,7 @@ public abstract class AbstractChartPluginManager implements ChartPluginManager else if (StringUtil.isEmpty(myVersion)) replace = false; else - replace = myVersion.isHigherThan(oldVersion); + replace = !myVersion.isLowerThan(oldVersion); return replace; } diff --git a/datagear-analysis/src/main/java/org/datagear/analysis/support/ConcurrentChartPluginManager.java b/datagear-analysis/src/main/java/org/datagear/analysis/support/ConcurrentChartPluginManager.java index 7740e40f..7a6e9735 100644 --- a/datagear-analysis/src/main/java/org/datagear/analysis/support/ConcurrentChartPluginManager.java +++ b/datagear-analysis/src/main/java/org/datagear/analysis/support/ConcurrentChartPluginManager.java @@ -52,13 +52,13 @@ public class ConcurrentChartPluginManager extends AbstractChartPluginManager } @Override - public ChartPlugin remove(String id) + public ChartPlugin[] remove(String... ids) { WriteLock writeLock = this.lock.writeLock(); try { - return removeChartPlugin(id); + return removeChartPlugins(ids); } finally { diff --git a/datagear-analysis/src/main/java/org/datagear/analysis/support/SimpleChartPluginManager.java b/datagear-analysis/src/main/java/org/datagear/analysis/support/SimpleChartPluginManager.java index 692a88fa..1445b795 100644 --- a/datagear-analysis/src/main/java/org/datagear/analysis/support/SimpleChartPluginManager.java +++ b/datagear-analysis/src/main/java/org/datagear/analysis/support/SimpleChartPluginManager.java @@ -36,9 +36,9 @@ public class SimpleChartPluginManager extends AbstractChartPluginManager } @Override - public ChartPlugin remove(String id) + public ChartPlugin[] remove(String... ids) { - return removeChartPlugin(id); + return removeChartPlugins(ids); } @Override diff --git a/datagear-analysis/src/main/java/org/datagear/analysis/support/html/DirectoryHtmlChartPluginManager.java b/datagear-analysis/src/main/java/org/datagear/analysis/support/html/DirectoryHtmlChartPluginManager.java index d0725891..9cdbb695 100644 --- a/datagear-analysis/src/main/java/org/datagear/analysis/support/html/DirectoryHtmlChartPluginManager.java +++ b/datagear-analysis/src/main/java/org/datagear/analysis/support/html/DirectoryHtmlChartPluginManager.java @@ -10,6 +10,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; +import java.util.zip.ZipOutputStream; import org.datagear.analysis.ChartPlugin; import org.datagear.analysis.ChartPluginManager; @@ -138,7 +139,7 @@ public class DirectoryHtmlChartPluginManager extends ConcurrentChartPluginManage return ids; } - protected void upload(File file, Set> ids, int depth) throws IOException + protected void upload(File file, Set> plugins, int depth) throws IOException { if (depth > 1 || !file.exists()) return; @@ -149,7 +150,11 @@ public class DirectoryHtmlChartPluginManager extends ConcurrentChartPluginManage { String name = generateUniquePluginFileName(file.getName()); File pluginFile = FileUtil.getFile(this.directory, name); - IOUtil.copy(file, pluginFile); + IOUtil.copy(file, pluginFile, false); + + HtmlChartPlugin plugin = loadAndRegister(pluginFile); + if (plugin != null) + plugins.add(plugin); } else { @@ -157,7 +162,7 @@ public class DirectoryHtmlChartPluginManager extends ConcurrentChartPluginManage if (children != null) { for (File child : children) - upload(child, ids, depth + 1); + upload(child, plugins, depth + 1); } } } @@ -165,15 +170,19 @@ public class DirectoryHtmlChartPluginManager extends ConcurrentChartPluginManage { String name = generateUniquePluginFileName(file.getName()); File pluginFile = FileUtil.getFile(this.directory, name); - IOUtil.copy(file, pluginFile); + IOUtil.copy(file, pluginFile, false); + + HtmlChartPlugin plugin = loadAndRegister(pluginFile); + if (plugin != null) + plugins.add(plugin); } else if (file.getName().toLowerCase().endsWith(".zip")) { - File tmpDirectory = FileUtil.createTempDirectory("dgcp"); + File tmpDirectory = FileUtil.createTempDirectory(); IOUtil.unzip(IOUtil.getZipInputStream(file), tmpDirectory); - upload(tmpDirectory, ids, depth + 1); + upload(tmpDirectory, plugins, depth + 1); FileUtil.deleteFile(tmpDirectory); } @@ -210,6 +219,35 @@ public class DirectoryHtmlChartPluginManager extends ConcurrentChartPluginManage return name; } + /** + * 下载指定ID的{@linkplain ChartPlugin}。 + * + * @throws IOException + */ + public void download(ZipOutputStream out, String... ids) throws IOException + { + ReadLock readLock = this.lock.readLock(); + + try + { + readLock.lock(); + + File tmpDirectory = FileUtil.createTempDirectory(); + + for (String id : ids) + { + PluginFileInfo pluginFileInfo = this.chartPluginFileInfoMap.get(id); + IOUtil.copy(pluginFileInfo.getFile(), tmpDirectory, true); + } + + IOUtil.writeFileToZipOutputStream(out, tmpDirectory, ""); + } + finally + { + readLock.unlock(); + } + } + @Override public ChartPlugin get(String id) { @@ -312,20 +350,17 @@ public class DirectoryHtmlChartPluginManager extends ConcurrentChartPluginManage { ChartPlugin plugin = this.htmlChartPluginLoader.loadFile(unload); + String pluginId = (plugin == null ? IDUtil.uuid() : plugin.getId()); + // 无论是否加载成功,都应该存入PluginFileInfo,避免每次checkForReload()时都会将其当做是未加载的 - this.chartPluginFileInfoMap.put(IDUtil.uuid(), new PluginFileInfo(unload)); + this.chartPluginFileInfoMap.put(pluginId, new PluginFileInfo(unload)); if (plugin != null) registerChartPlugin(plugin); } for (PluginFileInfo reload : reloads) - { - ChartPlugin plugin = this.htmlChartPluginLoader.loadFile(reload.getFile()); - - if (plugin != null) - registerChartPlugin(plugin); - } + loadAndRegister(reload.getFile()); } finally { @@ -333,6 +368,25 @@ public class DirectoryHtmlChartPluginManager extends ConcurrentChartPluginManage } } + /** + * 加载并注册插件,如果注册失败,将返回{@code null}。 + * + * @param pluginFile + * @return + */ + protected HtmlChartPlugin loadAndRegister(File pluginFile) + { + HtmlChartPlugin plugin = this.htmlChartPluginLoader.loadFile(pluginFile); + + if (plugin != null) + { + boolean reg = registerChartPlugin(plugin); + return (reg ? plugin : null); + } + else + return plugin; + } + protected static class PluginFileInfo { private File file; diff --git a/datagear-analysis/src/main/java/org/datagear/analysis/support/html/HtmlChartPluginLoader.java b/datagear-analysis/src/main/java/org/datagear/analysis/support/html/HtmlChartPluginLoader.java index 98f09872..e14582b6 100644 --- a/datagear-analysis/src/main/java/org/datagear/analysis/support/html/HtmlChartPluginLoader.java +++ b/datagear-analysis/src/main/java/org/datagear/analysis/support/html/HtmlChartPluginLoader.java @@ -345,7 +345,7 @@ public class HtmlChartPluginLoader { try { - File tmpDirectory = FileUtil.createTempDirectory("dgcpl"); + File tmpDirectory = FileUtil.createTempDirectory(); IOUtil.unzip(in, tmpDirectory); diff --git a/datagear-util/src/main/java/org/datagear/util/FileUtil.java b/datagear-util/src/main/java/org/datagear/util/FileUtil.java index 4821d8cd..2afa7923 100644 --- a/datagear-util/src/main/java/org/datagear/util/FileUtil.java +++ b/datagear-util/src/main/java/org/datagear/util/FileUtil.java @@ -322,6 +322,21 @@ public class FileUtil return null; } + /** + * 是否为指定后缀的文件名。 + * + * @param fileName + * @param extension + * @return + */ + public static boolean isExtension(String fileName, String extension) + { + if (StringUtil.isEmpty(fileName)) + return false; + + return fileName.toLowerCase().endsWith(extension.toLowerCase()); + } + /** * 连接路径。 * @@ -457,12 +472,13 @@ public class FileUtil /** * 创建临时文件夹。 * - * @param prefix * @return * @throws IOException */ - public static File createTempDirectory(String prefix) throws IOException + public static File createTempDirectory() throws IOException { + String prefix = Global.PRODUCT_NAME_EN.toUpperCase() + "_TMP_DIR"; + File tmp = File.createTempFile(prefix, null); if (!tmp.delete()) @@ -473,4 +489,29 @@ public class FileUtil return tmp; } + + /** + * 创建临时文件。 + * + * @return + * @throws IOException + */ + public static File createTempFile() throws IOException + { + String prefix = Global.PRODUCT_NAME_EN.toUpperCase() + "_TMP_FILE"; + return File.createTempFile(prefix, null); + } + + /** + * 创建临时文件。 + * + * @param extension + * @return + * @throws IOException + */ + public static File createTempFile(String extension) throws IOException + { + String prefix = Global.PRODUCT_NAME_EN.toUpperCase() + "_TMP_FILE"; + return File.createTempFile(prefix, extension); + } } diff --git a/datagear-util/src/main/java/org/datagear/util/IOUtil.java b/datagear-util/src/main/java/org/datagear/util/IOUtil.java index d163aee9..d42e32ee 100644 --- a/datagear-util/src/main/java/org/datagear/util/IOUtil.java +++ b/datagear-util/src/main/java/org/datagear/util/IOUtil.java @@ -574,9 +574,10 @@ public class IOUtil * * @param src * @param dest + * @param srcAsSubDirectory * @throws IOException */ - public static void copy(File src, File dest) throws IOException + public static void copy(File src, File dest, boolean srcAsSubDirectory) throws IOException { if (src.isDirectory()) { @@ -585,25 +586,37 @@ public class IOUtil else if (!dest.isDirectory()) throw new IllegalArgumentException("[dest] must be directory"); + File targetDirectory = dest; + if (srcAsSubDirectory) + targetDirectory = FileUtil.getDirectory(dest, src.getName()); + File[] children = src.listFiles(); if (children != null) { for (File child : children) - copy(child, new File(dest, child.getName())); + copy(child, new File(targetDirectory, child.getName()), false); } } else { - OutputStream out = null; - - try + if (dest.isDirectory()) { - out = IOUtil.getOutputStream(dest); - IOUtil.write(src, out); + File destFile = FileUtil.getFile(dest, src.getName()); + copy(src, destFile, false); } - finally + else { - IOUtil.close(out); + OutputStream out = null; + + try + { + out = IOUtil.getOutputStream(dest); + IOUtil.write(src, out); + } + finally + { + IOUtil.close(out); + } } } } diff --git a/datagear-web/src/main/java/org/datagear/web/controller/AbstractChartPluginAwareController.java b/datagear-web/src/main/java/org/datagear/web/controller/AbstractChartPluginAwareController.java new file mode 100644 index 00000000..1f935ed0 --- /dev/null +++ b/datagear-web/src/main/java/org/datagear/web/controller/AbstractChartPluginAwareController.java @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2018 datagear.tech. All Rights Reserved. + */ + +/** + * + */ +package org.datagear.web.controller; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import javax.servlet.http.HttpServletRequest; + +import org.datagear.analysis.ChartPlugin; +import org.datagear.analysis.Icon; +import org.datagear.analysis.RenderStyle; +import org.datagear.analysis.support.html.DirectoryHtmlChartPluginManager; +import org.datagear.analysis.support.html.HtmlChartPlugin; +import org.datagear.analysis.support.html.HtmlRenderContext; +import org.datagear.util.i18n.Label; +import org.datagear.web.convert.ClassDataConverter; +import org.datagear.web.util.KeywordMatcher; +import org.datagear.web.util.WebUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; + +/** + * 抽象插件相关的控制器。 + * + * @author datagear@163.com + * + */ +public class AbstractChartPluginAwareController extends AbstractDataAnalysisController +{ + @Autowired + private DirectoryHtmlChartPluginManager directoryHtmlChartPluginManager; + + public AbstractChartPluginAwareController() + { + super(); + } + + public AbstractChartPluginAwareController(MessageSource messageSource, ClassDataConverter classDataConverter, + DirectoryHtmlChartPluginManager directoryHtmlChartPluginManager) + { + super(messageSource, classDataConverter); + this.directoryHtmlChartPluginManager = directoryHtmlChartPluginManager; + } + + public DirectoryHtmlChartPluginManager getDirectoryHtmlChartPluginManager() + { + return directoryHtmlChartPluginManager; + } + + public void setDirectoryHtmlChartPluginManager(DirectoryHtmlChartPluginManager directoryHtmlChartPluginManager) + { + this.directoryHtmlChartPluginManager = directoryHtmlChartPluginManager; + } + + /** + * 查找插件信息列表。 + * + * @param request + * @param keyword + * @return + */ + protected List findHtmlChartPluginInfos(HttpServletRequest request, String keyword) + { + List pluginInfos = new ArrayList(); + + List> plugins = getDirectoryHtmlChartPluginManager() + .getAll(HtmlRenderContext.class); + + if (plugins != null) + { + Locale locale = WebUtils.getLocale(request); + RenderStyle renderStyle = resolveRenderStyle(request); + + for (ChartPlugin plugin : plugins) + { + if (plugin instanceof HtmlChartPlugin) + { + HtmlChartPlugin htmlChartPlugin = (HtmlChartPlugin) plugin; + pluginInfos.add(toHtmlChartPluginInfo(htmlChartPlugin, renderStyle, locale)); + } + } + } + + return KeywordMatcher. match(pluginInfos, keyword, + new KeywordMatcher.MatchValue() + { + @Override + public String[] get(HtmlChartPluginInfo t) + { + return new String[] { t.getName(), t.getDesc() }; + } + }); + } + + protected HtmlChartPluginInfo toHtmlChartPluginInfo(HtmlChartPlugin chartPlugin, RenderStyle renderStyle, + Locale locale) + { + HtmlChartPluginInfo pluginInfo = new HtmlChartPluginInfo(); + + pluginInfo.setId(chartPlugin.getId()); + + Label nameLabel = chartPlugin.getNameLabel(); + if (nameLabel != null) + pluginInfo.setName(nameLabel.getValue(locale)); + + Label descLabel = chartPlugin.getDescLabel(); + if (descLabel != null) + pluginInfo.setDesc(descLabel.getValue(locale)); + + Label manualLabel = chartPlugin.getManualLabel(); + if (manualLabel != null) + pluginInfo.setManual(manualLabel.getValue(locale)); + + Icon icon = chartPlugin.getIcon(renderStyle); + pluginInfo.setHasIcon(icon != null); + if (pluginInfo.isHasIcon()) + pluginInfo.setIconUrl(resolveIconUrl(chartPlugin)); + + pluginInfo.setVersion(chartPlugin.getVersion()); + + return pluginInfo; + } + + protected String resolveIconUrl(HtmlChartPlugin plugin) + { + return "/analysis/chartPlugin/icon/" + plugin.getId(); + } + + public static class HtmlChartPluginInfo implements Serializable + { + private static final long serialVersionUID = 1L; + + private String id; + + private String name; + + private String desc; + + private String manual; + + private boolean hasIcon; + + private String iconUrl; + + private String version; + + public HtmlChartPluginInfo() + { + super(); + } + + public HtmlChartPluginInfo(String id, String name) + { + super(); + this.id = id; + this.name = name; + } + + public String getId() + { + return id; + } + + public void setId(String id) + { + this.id = id; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getDesc() + { + return desc; + } + + public void setDesc(String desc) + { + this.desc = desc; + } + + public String getManual() + { + return manual; + } + + public void setManual(String manual) + { + this.manual = manual; + } + + public boolean isHasIcon() + { + return hasIcon; + } + + public void setHasIcon(boolean hasIcon) + { + this.hasIcon = hasIcon; + } + + public String getIconUrl() + { + return iconUrl; + } + + public void setIconUrl(String iconUrl) + { + this.iconUrl = iconUrl; + } + + public String getVersion() + { + return version; + } + + public void setVersion(String version) + { + this.version = version; + } + } +} diff --git a/datagear-web/src/main/java/org/datagear/web/controller/ChartController.java b/datagear-web/src/main/java/org/datagear/web/controller/ChartController.java index 9f671228..63d9c775 100644 --- a/datagear-web/src/main/java/org/datagear/web/controller/ChartController.java +++ b/datagear-web/src/main/java/org/datagear/web/controller/ChartController.java @@ -4,21 +4,12 @@ package org.datagear.web.controller; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Serializable; -import java.util.ArrayList; import java.util.List; -import java.util.Locale; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.datagear.analysis.ChartPlugin; import org.datagear.analysis.ChartPluginManager; -import org.datagear.analysis.Icon; -import org.datagear.analysis.RenderStyle; import org.datagear.analysis.support.html.HtmlChartPlugin; import org.datagear.analysis.support.html.HtmlRenderContext; import org.datagear.management.domain.HtmlChartWidgetEntity; @@ -28,28 +19,24 @@ import org.datagear.management.service.HtmlChartWidgetEntityService; import org.datagear.persistence.PagingData; import org.datagear.persistence.PagingQuery; import org.datagear.util.IDUtil; -import org.datagear.util.IOUtil; -import org.datagear.util.i18n.Label; import org.datagear.web.OperationMessage; import org.datagear.web.util.WebUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.context.request.WebRequest; /** - * 数据集控制器。 + * 图表控制器。 * * @author datagear@163.com * */ @Controller @RequestMapping("/analysis/chart") -public class ChartController extends AbstractDataAnalysisController +public class ChartController extends AbstractChartPluginAwareController { @Autowired private HtmlChartWidgetEntityService htmlChartWidgetEntityService; @@ -95,10 +82,14 @@ public class ChartController extends AbstractDataAnalysisController { HtmlChartWidgetEntity chart = new HtmlChartWidgetEntity(); - List pluginInfos = getAllHtmlChartPluginInfos(request); + List pluginInfos = findHtmlChartPluginInfos(request, null); if (pluginInfos.size() > 0) - chart.setChartPlugin(pluginInfos.get(0).getChartPlugin()); + { + String defaultChartPluginId = pluginInfos.get(0).getId(); + chart.setHtmlChartPlugin((HtmlChartPlugin) this.chartPluginManager + . get(defaultChartPluginId)); + } model.addAttribute("chart", chart); model.addAttribute("pluginInfos", pluginInfos); @@ -137,7 +128,7 @@ public class ChartController extends AbstractDataAnalysisController if (chart == null) throw new RecordNotFoundException(); - List pluginInfos = getAllHtmlChartPluginInfos(request); + List pluginInfos = findHtmlChartPluginInfos(request, null); model.addAttribute("chart", chart); model.addAttribute("pluginInfos", pluginInfos); @@ -174,7 +165,7 @@ public class ChartController extends AbstractDataAnalysisController if (chart == null) throw new RecordNotFoundException(); - List pluginInfos = getAllHtmlChartPluginInfos(request); + List pluginInfos = findHtmlChartPluginInfos(request, null); model.addAttribute("chart", chart); model.addAttribute("pluginInfos", pluginInfos); @@ -231,41 +222,6 @@ public class ChartController extends AbstractDataAnalysisController return pagingData; } - @RequestMapping(value = "/pluginicon/{pluginId}", produces = CONTENT_TYPE_JSON) - public void getPluginIcon(HttpServletRequest request, HttpServletResponse response, WebRequest webRequest, - @PathVariable("pluginId") String pluginId) throws Exception - { - ChartPlugin chartPlugin = this.chartPluginManager.get(pluginId); - - if (chartPlugin == null) - throw new FileNotFoundException(); - - RenderStyle renderStyle = resolveRenderStyle(request); - Icon icon = chartPlugin.getIcon(renderStyle); - - if (icon == null) - throw new FileNotFoundException(); - - long lastModified = icon.getLastModified(); - if (webRequest.checkNotModified(lastModified)) - return; - - response.setContentType("image/" + icon.getType()); - - OutputStream out = response.getOutputStream(); - InputStream iconIn = null; - - try - { - iconIn = icon.getInputStream(); - IOUtil.write(iconIn, out); - } - finally - { - IOUtil.close(iconIn); - } - } - protected SqlDataSetFactoryEntity[] getSqlDataSetFactoryEntityParams(HttpServletRequest request) { String[] dataSetIds = request.getParameterValues("dataSetId"); @@ -294,123 +250,4 @@ public class ChartController extends AbstractDataAnalysisController if (isEmpty(chart.getChartPlugin())) throw new IllegalInputException(); } - - protected List getAllHtmlChartPluginInfos(HttpServletRequest request) - { - List pluginInfos = new ArrayList(); - - List> plugins = this.chartPluginManager.getAll(HtmlRenderContext.class); - - if (plugins != null) - { - Locale locale = WebUtils.getLocale(request); - RenderStyle renderStyle = resolveRenderStyle(request); - - for (ChartPlugin plugin : plugins) - { - if (plugin instanceof HtmlChartPlugin) - { - HtmlChartPluginInfo pluginInfo = new HtmlChartPluginInfo(); - pluginInfo.setChartPlugin((HtmlChartPlugin) plugin); - - Label nameLabel = plugin.getNameLabel(); - if (nameLabel != null) - pluginInfo.setName(nameLabel.getValue(locale)); - - Label descLabel = plugin.getDescLabel(); - if (descLabel != null) - pluginInfo.setDesc(descLabel.getValue(locale)); - - Label manualLabel = plugin.getManualLabel(); - if (manualLabel != null) - pluginInfo.setManual(manualLabel.getValue(locale)); - - Icon icon = plugin.getIcon(renderStyle); - pluginInfo.setHasIcon(icon != null); - - pluginInfos.add(pluginInfo); - } - } - } - - return pluginInfos; - } - - public static class HtmlChartPluginInfo implements Serializable - { - private static final long serialVersionUID = 1L; - - private HtmlChartPlugin chartPlugin; - private String name; - private String desc; - private String manual; - private boolean hasIcon; - - public HtmlChartPluginInfo() - { - super(); - } - - public HtmlChartPluginInfo(HtmlChartPlugin chartPlugin, String name) - { - super(); - this.chartPlugin = chartPlugin; - this.name = name; - } - - public HtmlChartPlugin getChartPlugin() - { - return chartPlugin; - } - - public void setChartPlugin(HtmlChartPlugin chartPlugin) - { - this.chartPlugin = chartPlugin; - } - - public String getId() - { - return this.chartPlugin.getId(); - } - - public String getName() - { - return name; - } - - public void setName(String name) - { - this.name = name; - } - - public String getDesc() - { - return desc; - } - - public void setDesc(String desc) - { - this.desc = desc; - } - - public String getManual() - { - return manual; - } - - public void setManual(String manual) - { - this.manual = manual; - } - - public boolean isHasIcon() - { - return hasIcon; - } - - public void setHasIcon(boolean hasIcon) - { - this.hasIcon = hasIcon; - } - } } diff --git a/datagear-web/src/main/java/org/datagear/web/controller/ChartPluginController.java b/datagear-web/src/main/java/org/datagear/web/controller/ChartPluginController.java new file mode 100644 index 00000000..b517fabd --- /dev/null +++ b/datagear-web/src/main/java/org/datagear/web/controller/ChartPluginController.java @@ -0,0 +1,269 @@ +/* + * Copyright 2018 datagear.tech. All Rights Reserved. + */ + +package org.datagear.web.controller; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.datagear.analysis.ChartPlugin; +import org.datagear.analysis.Icon; +import org.datagear.analysis.RenderStyle; +import org.datagear.analysis.support.html.DirectoryHtmlChartPluginManager; +import org.datagear.analysis.support.html.HtmlChartPlugin; +import org.datagear.analysis.support.html.HtmlChartPluginLoadException; +import org.datagear.analysis.support.html.HtmlChartPluginLoader; +import org.datagear.util.FileUtil; +import org.datagear.util.IOUtil; +import org.datagear.util.StringUtil; +import org.datagear.web.OperationMessage; +import org.datagear.web.convert.ClassDataConverter; +import org.datagear.web.util.WebUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.multipart.MultipartFile; + +/** + * 图表插件控制器。 + * + * @author datagear@163.com + * + */ +@Controller +@RequestMapping("/analysis/chartPlugin") +public class ChartPluginController extends AbstractChartPluginAwareController +{ + @Autowired + private File tempDirectory; + + public ChartPluginController() + { + super(); + } + + public ChartPluginController(MessageSource messageSource, ClassDataConverter classDataConverter, + DirectoryHtmlChartPluginManager directoryHtmlChartPluginManager, File tempDirectory) + { + super(messageSource, classDataConverter, directoryHtmlChartPluginManager); + } + + public File getTempDirectory() + { + return tempDirectory; + } + + public void setTempDirectory(File tempDirectory) + { + this.tempDirectory = tempDirectory; + } + + @RequestMapping("/upload") + public String upload(HttpServletRequest request, org.springframework.ui.Model model) + { + return "/analysis/chartPlugin/chartPlugin_upload"; + } + + @RequestMapping(value = "/uploadFile", produces = CONTENT_TYPE_JSON) + @ResponseBody + public Map uploadFile(HttpServletRequest request, HttpServletResponse response, + @RequestParam("file") MultipartFile multipartFile) throws Exception + { + String pluginFileName = ""; + + File tmpDirectory = FileUtil.generateUniqueDirectory(this.tempDirectory); + + String zipFileName = multipartFile.getOriginalFilename(); + if (StringUtil.isEmpty(zipFileName)) + zipFileName = "plugin.zip"; + + if (!FileUtil.isExtension(zipFileName, ".zip")) + zipFileName += ".zip"; + + File zipFile = FileUtil.getFile(tmpDirectory, zipFileName); + + InputStream in = null; + OutputStream out = null; + try + { + in = multipartFile.getInputStream(); + out = IOUtil.getOutputStream(zipFile); + IOUtil.write(in, out); + } + finally + { + IOUtil.close(in); + IOUtil.close(out); + } + + List pluginInfos = new ArrayList(); + + HtmlChartPluginLoader loader = getDirectoryHtmlChartPluginManager().getHtmlChartPluginLoader(); + + Locale locale = WebUtils.getLocale(request); + RenderStyle renderStyle = resolveRenderStyle(request); + + try + { + if (loader.isHtmlChartPluginZip(zipFile)) + { + HtmlChartPlugin chartPlugin = loader.loadZip(zipFile); + pluginInfos.add(toHtmlChartPluginInfo(chartPlugin, renderStyle, locale)); + pluginFileName = tmpDirectory.getName(); + } + else + { + ZipInputStream zin = null; + try + { + zin = IOUtil.getZipInputStream(zipFile); + IOUtil.unzip(zin, tmpDirectory); + + Set> loaded = loader.loads(tmpDirectory); + + if (!loaded.isEmpty()) + { + for (HtmlChartPlugin chartPlugin : loaded) + pluginInfos.add(toHtmlChartPluginInfo(chartPlugin, renderStyle, locale)); + + pluginFileName = tmpDirectory.getName(); + } + } + finally + { + IOUtil.close(zin); + } + } + } + catch (IOException e) + { + } + catch (HtmlChartPluginLoadException e) + { + } + + Map results = new HashMap(); + results.put("pluginFileName", pluginFileName); + results.put("pluginInfos", pluginInfos); + + return results; + } + + @RequestMapping(value = "/saveUpload", produces = CONTENT_TYPE_JSON) + @ResponseBody + public ResponseEntity saveUpload(HttpServletRequest request, HttpServletResponse response, + @RequestParam("pluginFileName") String pluginFileName) throws Exception + { + if (StringUtil.isEmpty(pluginFileName)) + throw new IllegalInputException(); + + File tmpFile = FileUtil.getFile(this.tempDirectory, pluginFileName); + + getDirectoryHtmlChartPluginManager().upload(tmpFile); + + return buildOperationMessageSaveSuccessResponseEntity(request); + } + + @RequestMapping("/download") + public void download(HttpServletRequest request, HttpServletResponse response, org.springframework.ui.Model model, + @RequestParam("id") String[] ids) throws Exception + { + response.addHeader("Content-Disposition", "attachment;filename=chartPlugins.zip"); + response.setContentType("application/octet-stream"); + + ZipOutputStream zout = IOUtil.getZipOutputStream(response.getOutputStream()); + + try + { + getDirectoryHtmlChartPluginManager().download(zout, ids); + } + finally + { + zout.flush(); + zout.close(); + } + } + + @RequestMapping(value = "/delete", produces = CONTENT_TYPE_JSON) + @ResponseBody + public ResponseEntity delete(HttpServletRequest request, HttpServletResponse response, + @RequestParam("id") String[] ids) + { + getDirectoryHtmlChartPluginManager().remove(ids); + + return buildOperationMessageDeleteSuccessResponseEntity(request); + } + + @RequestMapping("/query") + public String query(HttpServletRequest request, org.springframework.ui.Model model) + { + model.addAttribute(KEY_TITLE_MESSAGE_KEY, "chartPlugin.manageChartPlugin"); + + return "/analysis/chartPlugin/chartPlugin_grid"; + } + + @RequestMapping(value = "/queryData", produces = CONTENT_TYPE_JSON) + @ResponseBody + public List queryData(HttpServletRequest request, HttpServletResponse response, + final org.springframework.ui.Model springModel, + @RequestParam(value = "keyword", required = false) String keyword) throws Exception + { + return findHtmlChartPluginInfos(request, keyword); + } + + @RequestMapping(value = "/icon/{pluginId}", produces = CONTENT_TYPE_JSON) + public void getPluginIcon(HttpServletRequest request, HttpServletResponse response, WebRequest webRequest, + @PathVariable("pluginId") String pluginId) throws Exception + { + ChartPlugin chartPlugin = getDirectoryHtmlChartPluginManager().get(pluginId); + + if (chartPlugin == null) + throw new FileNotFoundException(); + + RenderStyle renderStyle = resolveRenderStyle(request); + Icon icon = chartPlugin.getIcon(renderStyle); + + if (icon == null) + throw new FileNotFoundException(); + + long lastModified = icon.getLastModified(); + if (webRequest.checkNotModified(lastModified)) + return; + + response.setContentType("image/" + icon.getType()); + + OutputStream out = response.getOutputStream(); + InputStream iconIn = null; + + try + { + iconIn = icon.getInputStream(); + IOUtil.write(iconIn, out); + } + finally + { + IOUtil.close(iconIn); + } + } +} diff --git a/datagear-web/src/main/resources/datagear-applicationContext.xml b/datagear-web/src/main/resources/datagear-applicationContext.xml index 1522cdd1..b040cd4f 100644 --- a/datagear-web/src/main/resources/datagear-applicationContext.xml +++ b/datagear-web/src/main/resources/datagear-applicationContext.xml @@ -321,13 +321,13 @@ - + - + diff --git a/datagear-web/src/main/resources/datagear-security.xml b/datagear-web/src/main/resources/datagear-security.xml index b978dc19..ff892fec 100644 --- a/datagear-web/src/main/resources/datagear-security.xml +++ b/datagear-web/src/main/resources/datagear-security.xml @@ -55,6 +55,10 @@ + + + + diff --git a/datagear-web/src/main/resources/locales/datagear.properties b/datagear-web/src/main/resources/locales/datagear.properties index 8732c937..9e5d198c 100644 --- a/datagear-web/src/main/resources/locales/datagear.properties +++ b/datagear-web/src/main/resources/locales/datagear.properties @@ -227,6 +227,8 @@ main.manageUser=\u7BA1\u7406\u7528\u6237 main.addUser=\u6DFB\u52A0\u7528\u6237 main.manageRole=\u7BA1\u7406\u7528\u6237\u7EC4 main.manageAuthorization=\u7BA1\u7406\u6388\u6743 +main.manageChartPlugin=\u7BA1\u7406\u56FE\u8868\u63D2\u4EF6 +main.uploadChartPlugin=\u4E0A\u4F20\u56FE\u8868\u63D2\u4EF6 main.personalSet=\u4E2A\u4EBA\u8BBE\u7F6E main.changeTheme=\u5207\u6362\u80A4\u8272 main.changeTheme.lightness=\u6D45\u8272 @@ -639,4 +641,16 @@ dashboard.name=\u540D\u79F0 dashboard.template=\u6A21\u677F dashboard.createUser=\u521B\u5EFA\u7528\u6237 dashboard.createTime=\u521B\u5EFA\u65F6\u95F4 -dashboard.show=\u5C55\u793A \ No newline at end of file +dashboard.show=\u5C55\u793A + +#Chart plugin +chartPlugin.manageChartPlugin=\u7BA1\u7406\u56FE\u8868\u63D2\u4EF6 +chartPlugin.uploadChartPlugin=\u4E0A\u4F20\u56FE\u8868\u63D2\u4EF6 +chartPlugin.name=\u540D\u79F0 +chartPlugin.desc=\u63CF\u8FF0 +chartPlugin.icon=\u56FE\u6807 +chartPlugin.version=\u7248\u672C +chartPlugin.upload.selectFile=\u9009\u62E9\u6587\u4EF6 +chartPlugin.upload.desc=\u6587\u4EF6\u683C\u5F0F\u5E94\u4E3A\uFF1A*.zip +chartPlugin.upload.review=\u4E0A\u4F20\u9884\u89C8 +chartPlugin.upload.validation.uploadChartPluginFileRequired=\u8BF7\u9009\u62E9\u5408\u6CD5\u7684\u63D2\u4EF6\u6587\u4EF6 \ No newline at end of file diff --git a/datagear-web/src/main/webapp/WEB-INF/view/analysis/chart/chart_form.ftl b/datagear-web/src/main/webapp/WEB-INF/view/analysis/chart/chart_form.ftl index feb3eccf..d58fcc54 100644 --- a/datagear-web/src/main/webapp/WEB-INF/view/analysis/chart/chart_form.ftl +++ b/datagear-web/src/main/webapp/WEB-INF/view/analysis/chart/chart_form.ftl @@ -36,7 +36,7 @@ readonly 是否只读操作,允许为null <#list pluginInfos as pi>
  • <#if pi.hasIcon> -   +   <#else> ${pi.name?html} diff --git a/datagear-web/src/main/webapp/WEB-INF/view/analysis/chartPlugin/chartPlugin_grid.ftl b/datagear-web/src/main/webapp/WEB-INF/view/analysis/chartPlugin/chartPlugin_grid.ftl new file mode 100644 index 00000000..9ba55308 --- /dev/null +++ b/datagear-web/src/main/webapp/WEB-INF/view/analysis/chartPlugin/chartPlugin_grid.ftl @@ -0,0 +1,128 @@ +<#include "../../include/import_global.ftl"> +<#include "../../include/html_doctype.ftl"> +<#-- +titleMessageKey 标题标签I18N关键字,不允许null +selectonly 是否选择操作,允许为null +--> +<#assign selectonly=(selectonly!false)> + + +<#include "../../include/html_head.ftl"> +<#include "../../include/html_title_app_name.ftl"><@spring.message code='${titleMessageKey}' /> + + +<#if !isAjaxRequest> +
    + +
    +
    + +
    + <#if selectonly> + + + <#else> + + + + +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +<#if !isAjaxRequest> +
    + +<#include "../../include/page_js_obj.ftl"> +<#include "../../include/page_obj_searchform_js.ftl"> +<#include "../../include/page_obj_grid.ftl"> + + + diff --git a/datagear-web/src/main/webapp/WEB-INF/view/analysis/chartPlugin/chartPlugin_upload.ftl b/datagear-web/src/main/webapp/WEB-INF/view/analysis/chartPlugin/chartPlugin_upload.ftl new file mode 100644 index 00000000..98a94ef2 --- /dev/null +++ b/datagear-web/src/main/webapp/WEB-INF/view/analysis/chartPlugin/chartPlugin_upload.ftl @@ -0,0 +1,142 @@ +<#include "../../include/import_global.ftl"> +<#include "../../include/html_doctype.ftl"> + + +<#include "../../include/html_head.ftl"> +<#include "../../include/html_title_app_name.ftl"><@spring.message code='chartPlugin.uploadChartPlugin' /> + + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    + <@spring.message code='select' /> +
    +
    +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +<#include "../../include/page_js_obj.ftl" > +<#include "../../include/page_obj_form.ftl"> + + + \ No newline at end of file diff --git a/datagear-web/src/main/webapp/WEB-INF/view/main.ftl b/datagear-web/src/main/webapp/WEB-INF/view/main.ftl index acbb2835..6a2e47ee 100644 --- a/datagear-web/src/main/webapp/WEB-INF/view/main.ftl +++ b/datagear-web/src/main/webapp/WEB-INF/view/main.ftl @@ -595,6 +595,16 @@ $.setGridPageHeightOption(options); po.open(contextPath+"/authorization/${statics['org.datagear.management.domain.Schema'].AUTHORIZATION_RESOURCE_TYPE}/query", options); } + else if($item.hasClass("system-set-chartPlugin-manage")) + { + var options = {}; + $.setGridPageHeightOption(options); + po.open(contextPath+"/analysis/chartPlugin/query", options); + } + else if($item.hasClass("system-set-chartPlugin-upload")) + { + po.open(contextPath+"/analysis/chartPlugin/upload"); + } else if($item.hasClass("system-set-personal-set")) { po.open(contextPath+"/user/personalSet"); @@ -1174,6 +1184,9 @@
  • <@spring.message code='main.manageRole' />
  • <@spring.message code='main.manageAuthorization' />
  • +
  • <@spring.message code='main.manageChartPlugin' />
  • +
  • <@spring.message code='main.uploadChartPlugin' />
  • +
  • <@spring.message code='main.personalSet' />
  • <#if currentUser.admin> diff --git a/datagear-web/src/main/webapp/static/css/common.css b/datagear-web/src/main/webapp/static/css/common.css index b1563981..0e6468de 100644 --- a/datagear-web/src/main/webapp/static/css/common.css +++ b/datagear-web/src/main/webapp/static/css/common.css @@ -1971,4 +1971,37 @@ table.dataTable tbody tr .column-check .row-data-state .ui-icon{ } .page-form-chart .add-data-set-button{ vertical-align: top; +} + +.page-grid-chartPlugin{ +} +.page-grid-chartPlugin a.plugin-icon{ + display: inline-block; + width: 20px; + background-repeat: no-repeat; + background-size: 100% 100%; +} +.page-form-uploadChartPlugin .chart-plugin-infos{ + display: inline-block; + height: 8em; + overflow: auto; + padding: 3px 2px; +} +.page-form-uploadChartPlugin .chart-plugin-infos .chart-plugin-item{ + margin-bottom: 5px; + padding: 4px 2px; + white-space: nowrap; + overflow: hidden; +} +.page-form-uploadChartPlugin .chart-plugin-infos .chart-plugin-item a.plugin-icon{ + display: inline-block; + width: 20px; + background-repeat: no-repeat; + background-size: 100% 100%; +} +.page-form-uploadChartPlugin .chart-plugin-infos .chart-plugin-item .name{ + margin-left: 0.5em; +} +.page-form-uploadChartPlugin .chart-plugin-infos .chart-plugin-item .version{ + margin-left: 0.3em; } \ No newline at end of file