添加图表插件管理、上传、下载功能

This commit is contained in:
datagear 2019-12-29 21:38:17 +08:00
parent b5d3389d5e
commit 51143ad430
19 changed files with 1019 additions and 216 deletions

View File

@ -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}

View File

@ -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 <T extends RenderContext> List<ChartPlugin<T>> findChartPlugins(
Class<? extends T> renderContextType)
protected <T extends RenderContext> List<ChartPlugin<T>> findChartPlugins(Class<? extends T> renderContextType)
{
List<ChartPlugin<T>> reChartPlugins = new ArrayList<ChartPlugin<T>>();
@ -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;
}

View File

@ -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
{

View File

@ -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

View File

@ -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<HtmlChartPlugin<?>> ids, int depth) throws IOException
protected void upload(File file, Set<HtmlChartPlugin<?>> 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 <T extends RenderContext> ChartPlugin<T> 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;

View File

@ -345,7 +345,7 @@ public class HtmlChartPluginLoader
{
try
{
File tmpDirectory = FileUtil.createTempDirectory("dgcpl");
File tmpDirectory = FileUtil.createTempDirectory();
IOUtil.unzip(in, tmpDirectory);

View File

@ -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);
}
}

View File

@ -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);
}
}
}
}

View File

@ -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<HtmlChartPluginInfo> findHtmlChartPluginInfos(HttpServletRequest request, String keyword)
{
List<HtmlChartPluginInfo> pluginInfos = new ArrayList<HtmlChartPluginInfo>();
List<ChartPlugin<HtmlRenderContext>> plugins = getDirectoryHtmlChartPluginManager()
.getAll(HtmlRenderContext.class);
if (plugins != null)
{
Locale locale = WebUtils.getLocale(request);
RenderStyle renderStyle = resolveRenderStyle(request);
for (ChartPlugin<HtmlRenderContext> plugin : plugins)
{
if (plugin instanceof HtmlChartPlugin<?>)
{
HtmlChartPlugin<HtmlRenderContext> htmlChartPlugin = (HtmlChartPlugin<HtmlRenderContext>) plugin;
pluginInfos.add(toHtmlChartPluginInfo(htmlChartPlugin, renderStyle, locale));
}
}
}
return KeywordMatcher.<HtmlChartPluginInfo> match(pluginInfos, keyword,
new KeywordMatcher.MatchValue<HtmlChartPluginInfo>()
{
@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;
}
}
}

View File

@ -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<HtmlChartPluginInfo> pluginInfos = getAllHtmlChartPluginInfos(request);
List<HtmlChartPluginInfo> pluginInfos = findHtmlChartPluginInfos(request, null);
if (pluginInfos.size() > 0)
chart.setChartPlugin(pluginInfos.get(0).getChartPlugin());
{
String defaultChartPluginId = pluginInfos.get(0).getId();
chart.setHtmlChartPlugin((HtmlChartPlugin<HtmlRenderContext>) this.chartPluginManager
.<HtmlRenderContext> 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<HtmlChartPluginInfo> pluginInfos = getAllHtmlChartPluginInfos(request);
List<HtmlChartPluginInfo> 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<HtmlChartPluginInfo> pluginInfos = getAllHtmlChartPluginInfos(request);
List<HtmlChartPluginInfo> 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<HtmlChartPluginInfo> getAllHtmlChartPluginInfos(HttpServletRequest request)
{
List<HtmlChartPluginInfo> pluginInfos = new ArrayList<HtmlChartPluginInfo>();
List<ChartPlugin<HtmlRenderContext>> plugins = this.chartPluginManager.getAll(HtmlRenderContext.class);
if (plugins != null)
{
Locale locale = WebUtils.getLocale(request);
RenderStyle renderStyle = resolveRenderStyle(request);
for (ChartPlugin<HtmlRenderContext> plugin : plugins)
{
if (plugin instanceof HtmlChartPlugin<?>)
{
HtmlChartPluginInfo pluginInfo = new HtmlChartPluginInfo();
pluginInfo.setChartPlugin((HtmlChartPlugin<HtmlRenderContext>) 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<HtmlRenderContext> chartPlugin;
private String name;
private String desc;
private String manual;
private boolean hasIcon;
public HtmlChartPluginInfo()
{
super();
}
public HtmlChartPluginInfo(HtmlChartPlugin<HtmlRenderContext> chartPlugin, String name)
{
super();
this.chartPlugin = chartPlugin;
this.name = name;
}
public HtmlChartPlugin<HtmlRenderContext> getChartPlugin()
{
return chartPlugin;
}
public void setChartPlugin(HtmlChartPlugin<HtmlRenderContext> 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;
}
}
}

View File

@ -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<String, Object> 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<HtmlChartPluginInfo> pluginInfos = new ArrayList<HtmlChartPluginInfo>();
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<HtmlChartPlugin<?>> 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<String, Object> results = new HashMap<String, Object>();
results.put("pluginFileName", pluginFileName);
results.put("pluginInfos", pluginInfos);
return results;
}
@RequestMapping(value = "/saveUpload", produces = CONTENT_TYPE_JSON)
@ResponseBody
public ResponseEntity<OperationMessage> 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<OperationMessage> 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<HtmlChartPluginInfo> 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);
}
}
}

View File

@ -321,13 +321,13 @@
<property name="authorizationService" ref="authorizationService" />
</bean>
<bean id="chartPluginManager" class="org.datagear.analysis.support.html.DirectoryHtmlChartPluginManager" init-method="init">
<bean id="directoryHtmlChartPluginManager" class="org.datagear.analysis.support.html.DirectoryHtmlChartPluginManager" init-method="init">
<property name="directory" ref="chartPluginRootDirectory" />
</bean>
<bean id="htmlChartWidgetEntityService" class="org.datagear.management.service.impl.HtmlChartWidgetEntityServiceImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
<property name="chartPluginManager" ref="chartPluginManager" />
<property name="chartPluginManager" ref="directoryHtmlChartPluginManager" />
<property name="sqlDataSetFactoryEntityService" ref="sqlDataSetFactoryEntityService" />
<property name="authorizationService" ref="authorizationService" />
</bean>

View File

@ -55,6 +55,10 @@
<intercept-url pattern="/schemaUrlBuilder/saveScriptCode" access="ROLE_ADMIN" />
<intercept-url pattern="/schemaUrlBuilder/previewScriptCode" access="ROLE_ADMIN" />
<!-- 图表插件管理 -->
<intercept-url pattern="/analysis/chartPlugin/icon/*" access="ROLE_USER, IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/analysis/chartPlugin/**" access="ROLE_ADMIN" />
<intercept-url pattern="/login/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/register/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/resetPassword/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />

View File

@ -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
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

View File

@ -36,7 +36,7 @@ readonly 是否只读操作允许为null
<#list pluginInfos as pi>
<li class="ui-state-default ui-corner-all" chart-plugin-id="${pi.id?html}" title="${pi.name?html}">
<#if pi.hasIcon>
<a class="plugin-icon" style="background-image: url(${contextPath}/analysis/chart/pluginicon/${pi.id?html})">&nbsp;</a>
<a class="plugin-icon" style="background-image: url(${contextPath}/${pi.iconUrl})">&nbsp;</a>
<#else>
<a class="plugin-name">${pi.name?html}</a>
</#if>

View File

@ -0,0 +1,128 @@
<#include "../../include/import_global.ftl">
<#include "../../include/html_doctype.ftl">
<#--
titleMessageKey 标题标签I18N关键字不允许null
selectonly 是否选择操作允许为null
-->
<#assign selectonly=(selectonly!false)>
<html>
<head>
<#include "../../include/html_head.ftl">
<title><#include "../../include/html_title_app_name.ftl"><@spring.message code='${titleMessageKey}' /></title>
</head>
<body class="fill-parent">
<#if !isAjaxRequest>
<div class="fill-parent">
</#if>
<div id="${pageId}" class="page-grid page-grid-chartPlugin">
<div class="head">
<div class="search">
<#include "../../include/page_obj_searchform.html.ftl">
</div>
<div class="operation">
<#if selectonly>
<input name="confirmButton" type="button" class="recommended" value="<@spring.message code='confirm' />" />
<input name="viewButton" type="button" value="<@spring.message code='view' />" />
<#else>
<input name="uploadButton" type="button" value="<@spring.message code='upload' />" />
<input name="downloadButton" type="button" value="<@spring.message code='download' />" />
<input name="deleteButton" type="button" value="<@spring.message code='delete' />" />
</#if>
</div>
</div>
<div class="content">
<table id="${pageId}-table" width="100%" class="hover stripe">
</table>
</div>
<div class="foot">
<div class="pagination-wrapper">
<div id="${pageId}-pagination" class="pagination"></div>
</div>
</div>
</div>
<#if !isAjaxRequest>
</div>
</#if>
<#include "../../include/page_js_obj.ftl">
<#include "../../include/page_obj_searchform_js.ftl">
<#include "../../include/page_obj_grid.ftl">
<script type="text/javascript">
(function(po)
{
$.initButtons(po.element(".operation"));
po.url = function(action)
{
return "${contextPath}/analysis/chartPlugin/" + action;
};
<#if !selectonly>
po.element("input[name=uploadButton]").click(function()
{
po.open(po.url("upload"),
{
pageParam :
{
afterSave : function()
{
po.refresh();
}
}
});
});
po.element("input[name=downloadButton]").click(function()
{
var selectedDatas = po.getSelectedData();
var param = $.getPropertyParamString(selectedDatas, "id");
var options = {target : "_file"};
po.open(po.url("download?"+param), options);
});
po.element("input[name=deleteButton]").click(
function()
{
po.executeOnSelects(function(rows)
{
po.confirm("<@spring.message code='confirmDelete' />",
{
"confirm" : function()
{
var data = $.getPropertyParamString(rows, "id");
$.post(po.url("delete"), data, function()
{
po.refresh();
});
}
});
});
});
</#if>
var columnIcon = $.buildDataTablesColumnSimpleOption("<@spring.message code='chartPlugin.icon' />", "iconUrl");
columnIcon.render = function(data, type, row, meta)
{
if(data)
data = "<a class=\"plugin-icon\" style=\"background-image: url(${contextPath}/"+data+")\">&nbsp;</a>";
return data;
};
var tableColumns = [
$.buildDataTablesColumnSimpleOption("<@spring.message code='id' />", "id", true),
$.buildDataTablesColumnSimpleOption($.buildDataTablesColumnTitleSearchable("<@spring.message code='chartPlugin.name' />"), "name"),
$.buildDataTablesColumnSimpleOption($.buildDataTablesColumnTitleSearchable("<@spring.message code='chartPlugin.desc' />"), "desc"),
columnIcon,
$.buildDataTablesColumnSimpleOption("<@spring.message code='chartPlugin.version' />", "version")
];
var tableSettings = po.buildDataTableSettingsAjax(tableColumns, po.url("queryData"));
po.initDataTable(tableSettings);
})
(${pageId});
</script>
</body>
</html>

View File

@ -0,0 +1,142 @@
<#include "../../include/import_global.ftl">
<#include "../../include/html_doctype.ftl">
<html>
<head>
<#include "../../include/html_head.ftl">
<title><#include "../../include/html_title_app_name.ftl"><@spring.message code='chartPlugin.uploadChartPlugin' /></title>
</head>
<body>
<div id="${pageId}" class="page-form page-form-uploadChartPlugin">
<form id="${pageId}-form" action="${contextPath}/analysis/chartPlugin/saveUpload" method="POST">
<div class="form-head"></div>
<div class="form-content">
<input type="hidden" name="pluginFileName" value="" />
<div class="form-item">
<div class="form-item-label">
<label><@spring.message code='chartPlugin.upload.selectFile' /></label>
</div>
<div class="form-item-value">
<div class="fileinput-button" title="<@spring.message code='chartPlugin.upload.desc' />">
<@spring.message code='select' /><input type="file" accept=".zip" class="ignore">
</div>
<div class="upload-file-info"></div>
</div>
</div>
<div class="form-item">
<div class="form-item-label">
<label><@spring.message code='chartPlugin.upload.review' /></label>
</div>
<div class="form-item-value">
<input type="hidden" name="inputForValidate" value="" />
<div class="ui-widget ui-widget-content input chart-plugin-infos"></div>
</div>
</div>
</div>
<div class="form-foot" style="text-align:center;">
<input type="submit" value="<@spring.message code='save' />" class="recommended" />
</div>
</form>
</div>
<#include "../../include/page_js_obj.ftl" >
<#include "../../include/page_obj_form.ftl">
<script type="text/javascript">
(function(po)
{
po.element("input:submit, input:button, input:reset, button, .fileinput-button").button();
po.url = function(action)
{
return "${contextPath}/analysis/chartPlugin/" + action;
};
po.chartPluginInfos = function(){ return this.element(".chart-plugin-infos"); };
po.fileUploadInfo = function(){ return this.element(".upload-file-info"); };
po.renderChartPluginInfos = function(uploadResult)
{
po.element("input[name='pluginFileName']").val(uploadResult.pluginFileName);
po.chartPluginInfos().empty();
var chartPluginInfos = uploadResult.pluginInfos;
for(var i=0; i<chartPluginInfos.length; i++)
{
var chartPluginInfo = chartPluginInfos[i];
var $item = $("<div class='ui-widget ui-widget-content ui-corner-all chart-plugin-item' />")
.appendTo(po.chartPluginInfos());
if(chartPluginInfo.hasIcon)
$("<a class=\"plugin-icon\" style=\"background-image: url(${contextPath}/"+chartPluginInfo.iconUrl+")\">&nbsp;</a>").appendTo($item);
$("<span class='name'></span>").text(chartPluginInfo.name).appendTo($item);
if(chartPluginInfo.version)
$("<span class='version'></span>").text("(" +chartPluginInfo.version+")").appendTo($item);
}
};
po.element(".fileinput-button").fileupload(
{
url : po.url("uploadFile"),
paramName : "file",
success : function(uploadResult, textStatus, jqXHR)
{
$.fileuploadsuccessHandlerForUploadInfo(po.fileUploadInfo(), false);
po.renderChartPluginInfos(uploadResult);
}
})
.bind('fileuploadadd', function (e, data)
{
po.element("input[name='pluginFileName']").val("");
po.form().validate().resetForm();
$.fileuploadaddHandlerForUploadInfo(e, data, po.fileUploadInfo());
})
.bind('fileuploadprogressall', function (e, data)
{
$.fileuploadprogressallHandlerForUploadInfo(e, data, po.fileUploadInfo());
});
$.validator.addMethod("uploadChartPluginFileRequired", function(value, element)
{
var thisForm = $(element).closest("form");
var $pluginFileName = $("input[name='pluginFileName']", thisForm).val();
return $pluginFileName.length > 0;
});
po.form().validate(
{
ignore : ".ignore",
rules :
{
inputForValidate : "uploadChartPluginFileRequired"
},
messages :
{
inputForValidate : "<@spring.message code='chartPlugin.upload.validation.uploadChartPluginFileRequired' />"
},
submitHandler : function(form)
{
$(form).ajaxSubmit(
{
success : function()
{
var close = (po.pageParamCall("afterSave") != false);
if(close)
po.close();
}
});
},
errorPlacement : function(error, element)
{
error.appendTo(element.closest(".form-item-value"));
}
});
})
(${pageId});
</script>
</body>
</html>

View File

@ -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 @@
<li class="system-set-rold-manage"><a href="javascript:void(0);"><@spring.message code='main.manageRole' /></a></li>
<li class="system-set-authorization-manage"><a href="javascript:void(0);"><@spring.message code='main.manageAuthorization' /></a></li>
<li class="ui-widget-header"></li>
<li class="system-set-chartPlugin-manage"><a href="javascript:void(0);"><@spring.message code='main.manageChartPlugin' /></a></li>
<li class="system-set-chartPlugin-upload"><a href="javascript:void(0);"><@spring.message code='main.uploadChartPlugin' /></a></li>
<li class="ui-widget-header"></li>
</#if>
<li class="system-set-personal-set"><a href="javascript:void(0);"><@spring.message code='main.personalSet' /></a></li>
<#if currentUser.admin>

View File

@ -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;
}