forked from p85126437/datagear
[web]重构数据库URL构建器功能,添加管理员编辑构建器脚本代码功能,并且由加载脚本文件改为写入脚本内容
This commit is contained in:
parent
3c5203d2f2
commit
de3d2c5f39
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* Copyright 2018 datagear.tech. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package org.datagear.web.controller;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.datagear.connection.IOUtil;
|
||||
import org.datagear.web.OperationMessage;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
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.ServletContextAware;
|
||||
import org.springframework.web.context.support.ServletContextResource;
|
||||
|
||||
/**
|
||||
* 模式JDBC连接URL构建器控制器。
|
||||
*
|
||||
* @author datagear@163.com
|
||||
*
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/schemaUrlBuilder")
|
||||
public class SchemaUrlBuilderController extends AbstractSchemaModelController implements ServletContextAware
|
||||
{
|
||||
protected static final String LINE_SEPARATOR = System.getProperty("line.separator");
|
||||
|
||||
public static final String BUILT_IN_DB_URL_BUILDER_PATH = "/WEB-INF/builtInDbUrlBuilder.js";
|
||||
|
||||
public static final String DB_URL_BUILDER_ENCODING = "UTF-8";
|
||||
|
||||
private ServletContext servletContext;
|
||||
|
||||
private File schemaUrlBuilderScriptFile;
|
||||
|
||||
private volatile String _builtInDbUrlBuilderScript = null;
|
||||
|
||||
public SchemaUrlBuilderController()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public SchemaUrlBuilderController(ServletContext servletContext, File schemaUrlBuilderScriptFile)
|
||||
{
|
||||
super();
|
||||
this.servletContext = servletContext;
|
||||
this.schemaUrlBuilderScriptFile = schemaUrlBuilderScriptFile;
|
||||
}
|
||||
|
||||
public ServletContext getServletContext()
|
||||
{
|
||||
return servletContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServletContext(ServletContext servletContext)
|
||||
{
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
public File getSchemaUrlBuilderScriptFile()
|
||||
{
|
||||
return schemaUrlBuilderScriptFile;
|
||||
}
|
||||
|
||||
public void setSchemaUrlBuilderScriptFile(File schemaUrlBuilderScriptFile)
|
||||
{
|
||||
this.schemaUrlBuilderScriptFile = schemaUrlBuilderScriptFile;
|
||||
}
|
||||
|
||||
@Value("${schemaUrlBuilderScriptFile}")
|
||||
public void setSchemaUrlBuilderScriptFileString(String schemaUrlBuilderScriptFile)
|
||||
{
|
||||
this.schemaUrlBuilderScriptFile = IOUtil.getFile(schemaUrlBuilderScriptFile);
|
||||
}
|
||||
|
||||
@RequestMapping("/editScriptCode")
|
||||
public String editScriptCode(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
request.setAttribute("scriptCode", getUrlBuilderScript());
|
||||
|
||||
return "/schema_url_builder";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/saveScriptCode", produces = CONTENT_TYPE_JSON)
|
||||
@ResponseBody
|
||||
public ResponseEntity<OperationMessage> saveScriptCode(HttpServletRequest request, HttpServletResponse response,
|
||||
@RequestParam(value = "scriptCode", required = false) String scriptCode) throws IOException
|
||||
{
|
||||
saveCustomScript(scriptCode);
|
||||
|
||||
return buildOperationMessageSaveSuccessResponseEntity(request);
|
||||
}
|
||||
|
||||
@RequestMapping("/buildUrl")
|
||||
public String buildSchemaUrl(HttpServletRequest request, HttpServletResponse response,
|
||||
org.springframework.ui.Model springModel) throws IOException
|
||||
{
|
||||
request.setAttribute("scriptCode", getUrlBuilderScript());
|
||||
|
||||
return "/schema/schema_build_url";
|
||||
}
|
||||
|
||||
protected void saveCustomScript(String scriptCode) throws IOException
|
||||
{
|
||||
if (scriptCode == null)
|
||||
scriptCode = "";
|
||||
|
||||
Writer out = IOUtil.getWriter(this.schemaUrlBuilderScriptFile, DB_URL_BUILDER_ENCODING);
|
||||
|
||||
try
|
||||
{
|
||||
out.write(scriptCode);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtil.close(out);
|
||||
}
|
||||
}
|
||||
|
||||
protected String getUrlBuilderScript() throws IOException
|
||||
{
|
||||
String script = getCustomUrlBuilderScript();
|
||||
|
||||
if (script == null || script.isEmpty())
|
||||
script = getBuiltInUrlBuilderScript();
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义脚本。
|
||||
*
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
protected String getCustomUrlBuilderScript() throws IOException
|
||||
{
|
||||
if (this.schemaUrlBuilderScriptFile == null || !this.schemaUrlBuilderScriptFile.exists())
|
||||
return "";
|
||||
|
||||
Reader reader = IOUtil.getReader(this.schemaUrlBuilderScriptFile, DB_URL_BUILDER_ENCODING);
|
||||
|
||||
BufferedReader bufferedReader = null;
|
||||
|
||||
if (reader instanceof BufferedReader)
|
||||
bufferedReader = (BufferedReader) reader;
|
||||
else
|
||||
bufferedReader = new BufferedReader(reader);
|
||||
|
||||
try
|
||||
{
|
||||
return getUrlBuilderScript(bufferedReader);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtil.close(bufferedReader);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取内置脚本。
|
||||
*
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
protected String getBuiltInUrlBuilderScript() throws IOException
|
||||
{
|
||||
if (this._builtInDbUrlBuilderScript == null)
|
||||
{
|
||||
ServletContextResource resource = new ServletContextResource(this.servletContext,
|
||||
BUILT_IN_DB_URL_BUILDER_PATH);
|
||||
|
||||
InputStream in = resource.getInputStream();
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in, DB_URL_BUILDER_ENCODING));
|
||||
|
||||
try
|
||||
{
|
||||
this._builtInDbUrlBuilderScript = getUrlBuilderScript(bufferedReader);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtil.close(bufferedReader);
|
||||
}
|
||||
}
|
||||
|
||||
return this._builtInDbUrlBuilderScript;
|
||||
}
|
||||
|
||||
protected String getUrlBuilderScript(BufferedReader reader) throws IOException
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
String line = null;
|
||||
|
||||
while ((line = reader.readLine()) != null)
|
||||
{
|
||||
sb.append(line);
|
||||
sb.append(LINE_SEPARATOR);
|
||||
}
|
||||
|
||||
return sb.toString().trim();
|
||||
}
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
/*
|
||||
* Copyright 2018 datagear.tech. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package org.datagear.web.controller;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Writer;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
|
||||
/**
|
||||
* 模式JDBC连接URL构建器JS资源控制器。
|
||||
*
|
||||
* @author datagear@163.com
|
||||
*
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/schemaUrlBuilder")
|
||||
public class SchemaUrlBuilderResourceController extends AbstractSchemaModelController
|
||||
{
|
||||
@Autowired
|
||||
@Qualifier("schemaUrlBuilderResourceDirectory")
|
||||
private File schemaUrlBuilderResourceDirectory;
|
||||
|
||||
public SchemaUrlBuilderResourceController()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public SchemaUrlBuilderResourceController(File schemaUrlBuilderResourceDirectory)
|
||||
{
|
||||
super();
|
||||
this.schemaUrlBuilderResourceDirectory = schemaUrlBuilderResourceDirectory;
|
||||
}
|
||||
|
||||
public File getSchemaUrlBuilderResourceDirectory()
|
||||
{
|
||||
return schemaUrlBuilderResourceDirectory;
|
||||
}
|
||||
|
||||
public void setSchemaUrlBuilderResourceDirectory(File schemaUrlBuilderResourceDirectory)
|
||||
{
|
||||
this.schemaUrlBuilderResourceDirectory = schemaUrlBuilderResourceDirectory;
|
||||
}
|
||||
|
||||
@RequestMapping("buildUrl")
|
||||
public String buildSchemaUrl(HttpServletRequest request, HttpServletResponse response,
|
||||
org.springframework.ui.Model springModel)
|
||||
{
|
||||
String[] jsFileNames = listJsResourceFileNamesNoSuffix();
|
||||
|
||||
springModel.addAttribute("schemaUrlBuilderResources", jsFileNames);
|
||||
|
||||
return "/schema/schema_build_url";
|
||||
}
|
||||
|
||||
@RequestMapping("script/{name}")
|
||||
public void getScript(WebRequest request, HttpServletResponse response, @PathVariable("name") String scriptFileName)
|
||||
throws IOException
|
||||
{
|
||||
File file = new File(this.schemaUrlBuilderResourceDirectory, scriptFileName + ".js");
|
||||
|
||||
if (!file.exists())
|
||||
file = new File(this.schemaUrlBuilderResourceDirectory, scriptFileName + ".JS");
|
||||
|
||||
if (!file.exists())
|
||||
file = new File(this.schemaUrlBuilderResourceDirectory, scriptFileName + ".jS");
|
||||
|
||||
if (!file.exists())
|
||||
file = new File(this.schemaUrlBuilderResourceDirectory, scriptFileName + ".Js");
|
||||
|
||||
if (!file.exists())
|
||||
return;
|
||||
|
||||
if (!isJsFile(file))
|
||||
return;
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
|
||||
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setContentType("text/javascript;charset=UTF-8");
|
||||
|
||||
Writer out = response.getWriter();
|
||||
|
||||
String line = null;
|
||||
|
||||
try
|
||||
{
|
||||
while ((line = reader.readLine()) != null)
|
||||
{
|
||||
out.write(line);
|
||||
out.write("\r\n");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected String[] listJsResourceFileNamesNoSuffix()
|
||||
{
|
||||
File[] files = this.schemaUrlBuilderResourceDirectory.listFiles(new FileFilter()
|
||||
{
|
||||
@Override
|
||||
public boolean accept(File pathname)
|
||||
{
|
||||
if (pathname.isDirectory())
|
||||
return false;
|
||||
|
||||
return isJsFile(pathname);
|
||||
}
|
||||
});
|
||||
|
||||
String[] names = new String[files.length];
|
||||
|
||||
for (int i = 0; i < files.length; i++)
|
||||
{
|
||||
String name = files[i].getName();
|
||||
name = name.substring(0, name.lastIndexOf('.'));
|
||||
|
||||
names[i] = name;
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
protected boolean isJsFile(File file)
|
||||
{
|
||||
return file.getName().toLowerCase().endsWith(".js");
|
||||
}
|
||||
}
|
|
@ -59,11 +59,6 @@
|
|||
<property name="createIfInexistence" value="false" />
|
||||
</bean>
|
||||
|
||||
<bean id="schemaUrlBuilderResourceDirectoryFactory" class="org.datagear.web.util.DirectoryFactory" init-method="init">
|
||||
<property name="directoryName" value="${directory.url_builder}" />
|
||||
</bean>
|
||||
<bean id="schemaUrlBuilderResourceDirectory" factory-bean="schemaUrlBuilderResourceDirectoryFactory" factory-method="getDirectory" />
|
||||
|
||||
<bean id="tempDriverLibraryRootDirectoryFactory" class="org.datagear.web.util.DirectoryFactory" init-method="init">
|
||||
<property name="directoryName" value="${directory.temp.driverEntity}" />
|
||||
</bean>
|
||||
|
|
|
@ -44,6 +44,10 @@
|
|||
<!-- 全局设置 -->
|
||||
<intercept-url pattern="/globalSetting/*" access="ROLE_ADMIN" />
|
||||
|
||||
<!-- 设置数据库URL构建器脚本 -->
|
||||
<intercept-url pattern="/schemaUrlBuilder/editScriptCode" access="ROLE_ADMIN" />
|
||||
<intercept-url pattern="/schemaUrlBuilder/saveScriptCode" access="ROLE_ADMIN" />
|
||||
|
||||
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY, ROLE_USER, ROLE_ADMIN" />
|
||||
|
||||
<!-- 表单登录页登录 -->
|
||||
|
|
|
@ -11,12 +11,12 @@ directory.driver=${directory.root}/driver
|
|||
#系统使用的derby数据库主目录
|
||||
directory.derby=${directory.root}/derby
|
||||
|
||||
#数据编辑界面自定义URL构建器主目录
|
||||
directory.url_builder=${directory.root}/url_builder
|
||||
|
||||
#添加驱动程序操作时临时驱动程序文件目录
|
||||
directory.temp.driverEntity=${directory.root}/temp/driverEntity
|
||||
|
||||
#数据编辑界面自定义URL构建器脚本文件
|
||||
schemaUrlBuilderScriptFile=${directory.root}/db_url_builder.js
|
||||
|
||||
#重置密码请求被处理的时延小时数
|
||||
resetPasswordRequestDelayHours=24
|
||||
|
||||
|
|
|
@ -228,10 +228,15 @@ schema.driverEntity=\u6570\u636E\u5E93\u9A71\u52A8\u7A0B\u5E8F
|
|||
schema.shared=\u662F\u5426\u516C\u5F00
|
||||
schema.urlHelp=\u6570\u636E\u5E93\u7684JDBC\u8FDE\u63A5URL\uFF0C\u70B9\u51FB\u53EF\u6253\u5F00\u8BBE\u7F6E\u5E2E\u52A9\u9875\u9762
|
||||
schema.schemaBuildUrl=\u8BBE\u7F6E\u6570\u636E\u5E93URL
|
||||
schema.url.dbType=\u6570\u636E\u5E93\u7C7B\u578B
|
||||
schema.url.dbName=\u6570\u636E\u5E93\u540D\u79F0
|
||||
schema.url.host=\u4E3B\u673A\u540D/IP
|
||||
schema.url.port=\u7AEF\u53E3
|
||||
schema.url.name=\u6570\u636E\u5E93\u540D\u79F0
|
||||
schema.loadUrlBuilderScriptError=\u52A0\u8F7D\u6570\u636E\u5E93URL\u6784\u5EFA\u5668\u4EE3\u7801\u51FA\u9519
|
||||
|
||||
#schemaUrlBuilder
|
||||
schemaUrlBuilder.schemaUrlBuilder=\u6570\u636E\u5E93URL\u6784\u5EFA\u5668
|
||||
schemaUrlBuilder.scriptCode=\u6784\u5EFA\u5668\u4EE3\u7801
|
||||
|
||||
#user
|
||||
user.addUser=\u6DFB\u52A0\u7528\u6237
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
dbName : "MySQL",
|
||||
template : "jdbc:mysql://{host}:{port}/{name}",
|
||||
defaultValue :
|
||||
{
|
||||
host : "",
|
||||
port : 3306,
|
||||
name : ""
|
||||
}
|
||||
},
|
||||
{
|
||||
dbName : "PostgreSQL",
|
||||
template : "jdbc:postgresql://{host}:{port}/{name}",
|
||||
defaultValue :
|
||||
{
|
||||
host : "",
|
||||
port : 5432,
|
||||
name : ""
|
||||
}
|
||||
},
|
||||
{
|
||||
dbName : "Oracle",
|
||||
template : "jdbc:oracle:thin:@{host}:{port}/{name}",
|
||||
defaultValue :
|
||||
{
|
||||
host : "",
|
||||
port : 1521,
|
||||
name : ""
|
||||
}
|
||||
},
|
||||
{
|
||||
dbName : "SQL Server",
|
||||
template : "jdbc:sqlserver://{host}:{port};DatabaseName={name}",
|
||||
defaultValue :
|
||||
{
|
||||
host : "",
|
||||
port : 1433,
|
||||
name : ""
|
||||
}
|
||||
},
|
||||
{
|
||||
dbName : "DB2",
|
||||
template : "jdbc:db2://{host}:{port}/{name}",
|
||||
defaultValue :
|
||||
{
|
||||
host : "",
|
||||
port : 50000,
|
||||
name : ""
|
||||
}
|
||||
},
|
||||
{
|
||||
dbName : "Sybase",
|
||||
template : "jdbc:sybase:Tds:{host}:{port}/{name}",
|
||||
defaultValue :
|
||||
{
|
||||
host : "",
|
||||
port : 5000,
|
||||
name : ""
|
||||
}
|
||||
}
|
|
@ -14,82 +14,79 @@
|
|||
<html>
|
||||
<head>
|
||||
<%@ include file="include/html_head.jsp" %>
|
||||
<title><%@ include file="include/html_title_app_name.jsp" %><fmt:message key='globalSetting.globalSetting' /></title>
|
||||
<title><%@ include file="include/html_title_app_name.jsp" %><fmt:message key='globalSetting.smtpSetting' /></title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="${pageId}" class="page-data-form page-data-form-globalSetting">
|
||||
<form id="${pageId}-form" action="<%=request.getContextPath()%>/globalSetting/save" method="POST">
|
||||
<div class="form-head"></div>
|
||||
<div class="form-content">
|
||||
<fieldset class="ui-widget ui-widget-content ui-corner-all">
|
||||
<legend><fmt:message key='globalSetting.smtpSetting' /></legend>
|
||||
<div class="form-item">
|
||||
<div class="form-item-label">
|
||||
<label><fmt:message key='globalSetting.smtpSetting.host' /></label>
|
||||
</div>
|
||||
<div class="form-item-value">
|
||||
<input type="text" name="smtpSetting.host" value="<c:out value='${globalSetting.smtpSetting.host}' />" class="ui-widget ui-widget-content" />
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<div class="form-item-label">
|
||||
<label><fmt:message key='globalSetting.smtpSetting.host' /></label>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<div class="form-item-label">
|
||||
<label><fmt:message key='globalSetting.smtpSetting.port' /></label>
|
||||
</div>
|
||||
<div class="form-item-value">
|
||||
<input type="text" name="smtpSetting.port" value="<c:out value='${globalSetting.smtpSetting.port}' />" class="ui-widget ui-widget-content" />
|
||||
</div>
|
||||
<div class="form-item-value">
|
||||
<input type="text" name="smtpSetting.host" value="<c:out value='${globalSetting.smtpSetting.host}' />" class="ui-widget ui-widget-content" />
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<div class="form-item-label">
|
||||
<label><fmt:message key='globalSetting.smtpSetting.username' /></label>
|
||||
</div>
|
||||
<div class="form-item-value">
|
||||
<input type="text" name="smtpSetting.username" value="<c:out value='${globalSetting.smtpSetting.username}' />" class="ui-widget ui-widget-content" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<div class="form-item-label">
|
||||
<label><fmt:message key='globalSetting.smtpSetting.port' /></label>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<div class="form-item-label">
|
||||
<label><fmt:message key='globalSetting.smtpSetting.password' /></label>
|
||||
</div>
|
||||
<div class="form-item-value">
|
||||
<input type="password" name="smtpSetting.password" value="<c:out value='${globalSetting.smtpSetting.password}' />" class="ui-widget ui-widget-content" />
|
||||
</div>
|
||||
<div class="form-item-value">
|
||||
<input type="text" name="smtpSetting.port" value="<c:out value='${globalSetting.smtpSetting.port}' />" class="ui-widget ui-widget-content" />
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<div class="form-item-label">
|
||||
<label><fmt:message key='globalSetting.smtpSetting.connectionType' /></label>
|
||||
</div>
|
||||
<div class="form-item-value">
|
||||
<select name="smtpSetting.connectionType">
|
||||
<option value="<%=ConnectionType.PLAIN%>"><fmt:message key='globalSetting.smtpSetting.connectionType.PLAIN' /></option>
|
||||
<option value="<%=ConnectionType.SSL%>"><fmt:message key='globalSetting.smtpSetting.connectionType.SSL' /></option>
|
||||
<option value="<%=ConnectionType.TLS%>"><fmt:message key='globalSetting.smtpSetting.connectionType.TLS' /></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<div class="form-item-label">
|
||||
<label><fmt:message key='globalSetting.smtpSetting.username' /></label>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<div class="form-item-label">
|
||||
<label><fmt:message key='globalSetting.smtpSetting.systemEmail' /></label>
|
||||
</div>
|
||||
<div class="form-item-value">
|
||||
<input type="text" name="smtpSetting.systemEmail" value="<c:out value='${globalSetting.smtpSetting.systemEmail}' />" class="ui-widget ui-widget-content" />
|
||||
</div>
|
||||
<div class="form-item-value">
|
||||
<input type="text" name="smtpSetting.username" value="<c:out value='${globalSetting.smtpSetting.username}' />" class="ui-widget ui-widget-content" />
|
||||
</div>
|
||||
<div class="form-item" style="height:3em;">
|
||||
<div class="form-item-label">
|
||||
<label> </label>
|
||||
</div>
|
||||
<div class="form-item-value">
|
||||
<button type="button" id="testSmtpButton"><fmt:message key='globalSetting.testSmtp' /></button>
|
||||
|
||||
<span id="testSmtpPanel" style="display: none;">
|
||||
<fmt:message key='globalSetting.testSmtp.recevierEmail' />
|
||||
<input type="text" name="testSmtpRecevierEmail" value="" class="ui-widget ui-widget-content" />
|
||||
<button type="button" id="testSmtpSendButton" style="vertical-align:baseline;"><fmt:message key='globalSetting.testSmtp.send' /></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<div class="form-item-label">
|
||||
<label><fmt:message key='globalSetting.smtpSetting.password' /></label>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="form-item-value">
|
||||
<input type="password" name="smtpSetting.password" value="<c:out value='${globalSetting.smtpSetting.password}' />" class="ui-widget ui-widget-content" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<div class="form-item-label">
|
||||
<label><fmt:message key='globalSetting.smtpSetting.connectionType' /></label>
|
||||
</div>
|
||||
<div class="form-item-value">
|
||||
<select name="smtpSetting.connectionType">
|
||||
<option value="<%=ConnectionType.PLAIN%>"><fmt:message key='globalSetting.smtpSetting.connectionType.PLAIN' /></option>
|
||||
<option value="<%=ConnectionType.SSL%>"><fmt:message key='globalSetting.smtpSetting.connectionType.SSL' /></option>
|
||||
<option value="<%=ConnectionType.TLS%>"><fmt:message key='globalSetting.smtpSetting.connectionType.TLS' /></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<div class="form-item-label">
|
||||
<label><fmt:message key='globalSetting.smtpSetting.systemEmail' /></label>
|
||||
</div>
|
||||
<div class="form-item-value">
|
||||
<input type="text" name="smtpSetting.systemEmail" value="<c:out value='${globalSetting.smtpSetting.systemEmail}' />" class="ui-widget ui-widget-content" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-item" style="height:3em;">
|
||||
<div class="form-item-label">
|
||||
<label> </label>
|
||||
</div>
|
||||
<div class="form-item-value">
|
||||
<button type="button" id="testSmtpButton"><fmt:message key='globalSetting.testSmtp' /></button>
|
||||
|
||||
<span id="testSmtpPanel" style="display: none;">
|
||||
<fmt:message key='globalSetting.testSmtp.recevierEmail' />
|
||||
<input type="text" name="testSmtpRecevierEmail" value="" class="ui-widget ui-widget-content" />
|
||||
<button type="button" id="testSmtpSendButton" style="vertical-align:baseline;"><fmt:message key='globalSetting.testSmtp.send' /></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-foot" style="text-align:center;">
|
||||
<input type="submit" value="<fmt:message key='save' />" class="recommended" />
|
||||
|
|
|
@ -285,6 +285,10 @@
|
|||
{
|
||||
pageObj.open(contextPath+"/globalSetting");
|
||||
}
|
||||
else if($item.hasClass("system-set-schema-url-builder"))
|
||||
{
|
||||
pageObj.open(contextPath+"/schemaUrlBuilder/editScriptCode");
|
||||
}
|
||||
else if($item.hasClass("system-set-driverEntity-add"))
|
||||
{
|
||||
pageObj.open(contextPath+"/driverEntity/add");
|
||||
|
@ -1024,8 +1028,6 @@
|
|||
<ul style="display:none;">
|
||||
<%if(!user.isAnonymous()){ %>
|
||||
<%if(user.isAdmin()){ %>
|
||||
<li class="system-set-global-setting"><a href="javascript:void(0);"><fmt:message key='main.globalSetting' /></a></li>
|
||||
<li class="ui-widget-header"></li>
|
||||
<li class="system-set-driverEntity-manage"><a href="javascript:void(0);"><fmt:message key='main.manageDriverEntity' /></a></li>
|
||||
<li class="system-set-driverEntity-add"><a href="javascript:void(0);"><fmt:message key='main.addDriverEntity' /></a></li>
|
||||
<li class="ui-widget-header"></li>
|
||||
|
@ -1034,6 +1036,14 @@
|
|||
<li class="ui-widget-header"></li>
|
||||
<%}%>
|
||||
<li class="system-set-personal-set"><a href="javascript:void(0);"><fmt:message key='main.personalSet' /></a></li>
|
||||
<%if(user.isAdmin()){ %>
|
||||
<li class=""><a href="javascript:void(0);"><fmt:message key='main.globalSetting' /></a>
|
||||
<ul>
|
||||
<li class="system-set-global-setting"><a href="javascript:void(0);"><fmt:message key='globalSetting.smtpSetting' /></a></li>
|
||||
<li class="system-set-schema-url-builder"><a href="javascript:void(0);"><fmt:message key='schemaUrlBuilder.schemaUrlBuilder' /></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<%}%>
|
||||
<li class="ui-widget-header"></li>
|
||||
<%}%>
|
||||
<li class=""><a href="javascript:void(0);"><fmt:message key='main.changeTheme' /></a>
|
||||
|
|
|
@ -13,22 +13,18 @@
|
|||
<head>
|
||||
<%@ include file="../include/html_head.jsp" %>
|
||||
<script type="text/javascript">
|
||||
$.schemaUrlBuilder.removeCustom();
|
||||
</script>
|
||||
<%
|
||||
String[] urlBuilderJsFileNames = (String[])request.getAttribute("schemaUrlBuilderResources");
|
||||
if(urlBuilderJsFileNames != null)
|
||||
$.schemaUrlBuilder.clear();
|
||||
try
|
||||
{
|
||||
long notCacheCode = new java.util.Date().getTime();
|
||||
for(int i=0; i<urlBuilderJsFileNames.length; i++)
|
||||
{
|
||||
String fileName = urlBuilderJsFileNames[i];
|
||||
%>
|
||||
<script src="<%=request.getContextPath()%>/schemaUrlBuilder/script/<%=fileName%>?_=<%=notCacheCode%>" type="text/javascript"></script>
|
||||
<%
|
||||
}
|
||||
$.schemaUrlBuilder.add(
|
||||
<%=request.getAttribute("scriptCode")%>
|
||||
);
|
||||
}
|
||||
%>
|
||||
catch(e)
|
||||
{
|
||||
$.tipError("<fmt:message key='schema.loadUrlBuilderScriptError' />");
|
||||
}
|
||||
</script>
|
||||
<title><%@ include file="../include/html_title_app_name.jsp" %><fmt:message key='schema.schemaBuildUrl' /></title>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -38,10 +34,10 @@ if(urlBuilderJsFileNames != null)
|
|||
<div class="form-content">
|
||||
<div class="form-item">
|
||||
<div class="form-item-label">
|
||||
<label><fmt:message key='schema.url.dbType' /></label>
|
||||
<label><fmt:message key='schema.url.dbName' /></label>
|
||||
</div>
|
||||
<div class="form-item-value">
|
||||
<select name="dbType">
|
||||
<select name="dbName">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -80,7 +76,7 @@ if(urlBuilderJsFileNames != null)
|
|||
(function(pageObj)
|
||||
{
|
||||
pageObj.form = pageObj.element("#${pageId}-form");
|
||||
pageObj.dbTypeSelect = pageObj.element("select[name='dbType']");
|
||||
pageObj.dbNameSelect = pageObj.element("select[name='dbName']");
|
||||
|
||||
var pageParam = pageObj.pageParam();
|
||||
|
||||
|
@ -92,17 +88,17 @@ if(urlBuilderJsFileNames != null)
|
|||
for(var i=0; i<builderInfos.length; i++)
|
||||
{
|
||||
var builderInfo = builderInfos[i];
|
||||
$("<option>").attr("value", builderInfo.dbType).html(builderInfo.dbName).appendTo(pageObj.dbTypeSelect);
|
||||
$("<option>").attr("value", builderInfo.dbName).html(builderInfo.dbDesc).appendTo(pageObj.dbNameSelect);
|
||||
}
|
||||
|
||||
pageObj.dbTypeSelect.selectmenu(
|
||||
pageObj.dbNameSelect.selectmenu(
|
||||
{
|
||||
"classes" : { "ui-selectmenu-button" : "schema-build-url-dbtype-select" },
|
||||
change : function(event, ui)
|
||||
{
|
||||
var dbType = ui.item.value;
|
||||
var dbName = ui.item.value;
|
||||
|
||||
var defaultUrlInfo = $.schemaUrlBuilder.defaultValue(dbType);
|
||||
var defaultUrlInfo = $.schemaUrlBuilder.defaultValue(dbName);
|
||||
pageObj.setFormUrlValue(defaultUrlInfo);
|
||||
}
|
||||
});
|
||||
|
@ -123,7 +119,7 @@ if(urlBuilderJsFileNames != null)
|
|||
|
||||
pageObj.buildFormUrl = function()
|
||||
{
|
||||
var dbType = pageObj.dbTypeSelect.val();
|
||||
var dbName = pageObj.dbNameSelect.val();
|
||||
|
||||
var value = {};
|
||||
|
||||
|
@ -134,7 +130,7 @@ if(urlBuilderJsFileNames != null)
|
|||
value[input.attr("name")] = input.val();
|
||||
}
|
||||
|
||||
return $.schemaUrlBuilder.build(dbType, value);
|
||||
return $.schemaUrlBuilder.build(dbName, value);
|
||||
};
|
||||
|
||||
pageObj.form.validate(
|
||||
|
@ -167,14 +163,14 @@ if(urlBuilderJsFileNames != null)
|
|||
|
||||
if(urlInfo != null)
|
||||
{
|
||||
pageObj.dbTypeSelect.val(urlInfo.dbType);
|
||||
pageObj.dbTypeSelect.selectmenu("refresh");
|
||||
pageObj.dbNameSelect.val(urlInfo.dbName);
|
||||
pageObj.dbNameSelect.selectmenu("refresh");
|
||||
initUrlValue = urlInfo.value;
|
||||
}
|
||||
}
|
||||
|
||||
if(!initUrlValue)
|
||||
initUrlValue = $.schemaUrlBuilder.defaultValue(pageObj.dbTypeSelect.val());
|
||||
initUrlValue = $.schemaUrlBuilder.defaultValue(pageObj.dbNameSelect.val());
|
||||
|
||||
pageObj.setFormUrlValue(initUrlValue);
|
||||
})
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
<%--
|
||||
/*
|
||||
* Copyright 2018 datagear.tech. All Rights Reserved.
|
||||
*/
|
||||
--%>
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
||||
<%@ include file="include/jsp_import.jsp" %>
|
||||
<%@ include file="include/jsp_ajax_request.jsp" %>
|
||||
<%@ include file="include/jsp_jstl.jsp" %>
|
||||
<%@ include file="include/jsp_page_id.jsp" %>
|
||||
<%@ include file="include/jsp_method_get_string_value.jsp" %>
|
||||
<%@ include file="include/html_doctype.jsp" %>
|
||||
<html>
|
||||
<head>
|
||||
<%@ include file="include/html_head.jsp" %>
|
||||
<title><%@ include file="include/html_title_app_name.jsp" %><fmt:message key='schemaUrlBuilder.schemaUrlBuilder' /></title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="${pageId}" class="page-data-form page-data-form-schemaUrlBuilder">
|
||||
<form id="${pageId}-form" action="<%=request.getContextPath()%>/schemaUrlBuilder/saveScriptCode" method="POST">
|
||||
<div class="form-head"></div>
|
||||
<div class="form-content">
|
||||
<div class="form-item">
|
||||
<div class="form-item-label">
|
||||
<label><fmt:message key='schemaUrlBuilder.scriptCode' /></label>
|
||||
</div>
|
||||
<div class="form-item-value">
|
||||
<textarea name="scriptCode" style="width:30em; height: 20em;"><c:out value='${scriptCode}' /></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-foot" style="text-align:center;">
|
||||
<input type="submit" value="<fmt:message key='save' />" class="recommended" />
|
||||
|
||||
<input type="reset" value="<fmt:message key='reset' />" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<%@ include file="include/page_js_obj.jsp" %>
|
||||
<script type="text/javascript">
|
||||
(function(pageObj)
|
||||
{
|
||||
$.initButtons(pageObj.element());
|
||||
|
||||
pageObj.form = pageObj.element("#${pageId}-form");
|
||||
|
||||
pageObj.form.validate(
|
||||
{
|
||||
submitHandler : function(form)
|
||||
{
|
||||
$(form).ajaxSubmit(
|
||||
{
|
||||
success : function(response)
|
||||
{
|
||||
var pageParam = pageObj.pageParam();
|
||||
|
||||
var close = false;
|
||||
|
||||
if(pageParam && pageParam.afterSave)
|
||||
close = (pageParam.afterSave() != false);
|
||||
|
||||
if(close)
|
||||
pageObj.close();
|
||||
}
|
||||
});
|
||||
},
|
||||
errorPlacement : function(error, element)
|
||||
{
|
||||
error.appendTo(element.closest(".form-item"));
|
||||
}
|
||||
});
|
||||
})
|
||||
(${pageId});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -27,11 +27,11 @@
|
|||
|
||||
var builders = $.schemaUrlBuilder.builders;
|
||||
|
||||
for(var dbType in builders)
|
||||
for(var dbName in builders)
|
||||
{
|
||||
var builder = builders[dbType];
|
||||
var builder = builders[dbName];
|
||||
|
||||
infoArray.push({ "dbType" : dbType, "dbName" : (builder.dbName || dbType), "order" : builder.order });
|
||||
infoArray.push({ "dbName" : dbName, "dbDesc" : (builder.dbDesc || dbName), "order" : builder.order });
|
||||
}
|
||||
|
||||
infoArray.sort(function(a, b)
|
||||
|
@ -50,12 +50,12 @@
|
|||
/**
|
||||
* 构建JDBC连接URL。
|
||||
*
|
||||
* @param dbType 数据库类型标识
|
||||
* @param dbName 数据库类型标识
|
||||
* @param value URL值对象
|
||||
*/
|
||||
schemaUrlBuilder.build = function(dbType, value)
|
||||
schemaUrlBuilder.build = function(dbName, value)
|
||||
{
|
||||
var builder = $.schemaUrlBuilder.builders[dbType];
|
||||
var builder = $.schemaUrlBuilder.builders[dbName];
|
||||
|
||||
if(!builder)
|
||||
return "";
|
||||
|
@ -70,7 +70,7 @@
|
|||
|
||||
/**
|
||||
* 由JDBC连接URL解析连接信息。
|
||||
* 返回对象格式:{ dbType : "", value : { host : "", port : "", name : "" } }。
|
||||
* 返回对象格式:{ dbName : "", value : { host : "", port : "", name : "" } }。
|
||||
* 如果无法解析,返回null。
|
||||
*
|
||||
* @param url JDBC连接URL
|
||||
|
@ -79,9 +79,9 @@
|
|||
{
|
||||
var builders = $.schemaUrlBuilder.builders;
|
||||
|
||||
for(var dbType in builders)
|
||||
for(var dbName in builders)
|
||||
{
|
||||
var builder = builders[dbType];
|
||||
var builder = builders[dbName];
|
||||
|
||||
var value = null;
|
||||
|
||||
|
@ -91,7 +91,7 @@
|
|||
value = $.schemaUrlBuilder._resolveValue(builder.template, url);
|
||||
|
||||
if(value != null)
|
||||
return { dbType : dbType, value : value };
|
||||
return { dbName : dbName, value : value };
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -100,11 +100,11 @@
|
|||
/**
|
||||
* 获取数据库的默认URL值对象。
|
||||
*
|
||||
* @param dbType 数据库类型标识
|
||||
* @param dbName 数据库类型标识
|
||||
*/
|
||||
schemaUrlBuilder.defaultValue = function(dbType)
|
||||
schemaUrlBuilder.defaultValue = function(dbName)
|
||||
{
|
||||
var builder = $.schemaUrlBuilder.builders[dbType];
|
||||
var builder = $.schemaUrlBuilder.builders[dbName];
|
||||
|
||||
if(!builder)
|
||||
return {};
|
||||
|
@ -115,32 +115,40 @@
|
|||
/**
|
||||
* 是否包含指定数据库类型标识的构建器。
|
||||
*
|
||||
* @param dbType 数据库类型标识
|
||||
* @param dbName 数据库类型标识
|
||||
*/
|
||||
schemaUrlBuilder.contains = function(dbType)
|
||||
schemaUrlBuilder.contains = function(dbName)
|
||||
{
|
||||
return ($.schemaUrlBuilder.builders[dbType] != undefined);
|
||||
return ($.schemaUrlBuilder.builders[dbName] != undefined);
|
||||
};
|
||||
|
||||
/**
|
||||
* 添加一个构建器。
|
||||
*
|
||||
* @param dbType 数据库类型标识
|
||||
* @param builder 构建器,可以有两种格式:
|
||||
* 1.
|
||||
* {
|
||||
* //必选,数据库名称
|
||||
* dbName : "...",
|
||||
*
|
||||
* //必选,模板
|
||||
* template : "...{host}...{port}...{name}...",
|
||||
*
|
||||
* //可选,默认值
|
||||
* defaultValue : { host : "...", port : "...", name : "" },
|
||||
*
|
||||
* //可选,数据库描述
|
||||
* dbDesc : "...",
|
||||
*
|
||||
* //可选,展示排序
|
||||
* order : 9
|
||||
* }
|
||||
*
|
||||
* 2.
|
||||
* {
|
||||
* //必选,数据库名称
|
||||
* dbName : "...",
|
||||
*
|
||||
* //必选,由{ host : "...", port : "...", name : "" }值对象构建URL的函数
|
||||
* build : function(value){ ... },
|
||||
*
|
||||
|
@ -150,44 +158,52 @@
|
|||
* //可选,默认值
|
||||
* defaultValue : { host : "...", port : "...", name : "" },
|
||||
*
|
||||
* //可选,数据库描述
|
||||
* dbDesc : "...",
|
||||
*
|
||||
* //可选,展示排序
|
||||
* order : 9
|
||||
* }
|
||||
*/
|
||||
schemaUrlBuilder.add = function(dbType, builder)
|
||||
schemaUrlBuilder.add = function(builder)
|
||||
{
|
||||
$.schemaUrlBuilder.builders[dbType] = builder;
|
||||
var order = 0;
|
||||
|
||||
for(var i= 0; i<arguments.length; i++)
|
||||
{
|
||||
var ele = arguments[i];
|
||||
|
||||
if(!$.isArray(ele))
|
||||
ele = [ ele ];
|
||||
|
||||
for(var j=0; j<ele.length; j++)
|
||||
{
|
||||
var myBuilder = ele[j];
|
||||
|
||||
if(myBuilder.dbName)
|
||||
{
|
||||
if(myBuilder.order == undefined)
|
||||
myBuilder.order = order;
|
||||
|
||||
$.schemaUrlBuilder.builders[myBuilder.dbName] = myBuilder;
|
||||
|
||||
order++;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除所有自定义构建器。
|
||||
* 删除所有构建器。
|
||||
*/
|
||||
schemaUrlBuilder.removeCustom = function()
|
||||
schemaUrlBuilder.clear = function()
|
||||
{
|
||||
var builders = $.schemaUrlBuilder.builders;
|
||||
|
||||
var removed = [];
|
||||
|
||||
for(var dbType in builders)
|
||||
{
|
||||
var remove = true;
|
||||
|
||||
var builtInDbTypes = $.schemaUrlBuilder.builtInDbTypes;
|
||||
if(builtInDbTypes)
|
||||
{
|
||||
for(var i = 0; i<builtInDbTypes.length; i++)
|
||||
{
|
||||
if(dbType == builtInDbTypes[i])
|
||||
{
|
||||
remove = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(remove)
|
||||
removed.push(dbType);
|
||||
}
|
||||
for(var dbName in builders)
|
||||
removed.push(dbName);
|
||||
|
||||
for(var i=0; i< removed.length; i++)
|
||||
delete builders[removed[i]];
|
||||
|
@ -335,106 +351,5 @@
|
|||
|
||||
return varInfo;
|
||||
};
|
||||
|
||||
/**
|
||||
* 将已添加的构建起记录为内置构建器。
|
||||
*/
|
||||
schemaUrlBuilder._asBuiltInDbTypes = function()
|
||||
{
|
||||
var builtInDbTypes = [];
|
||||
|
||||
var builders = $.schemaUrlBuilder.builders;
|
||||
|
||||
for(var dbType in builders)
|
||||
builtInDbTypes.push(dbType);
|
||||
|
||||
$.schemaUrlBuilder.builtInDbTypes = builtInDbTypes;
|
||||
};
|
||||
|
||||
schemaUrlBuilder.add("MySQL",
|
||||
{
|
||||
template : "jdbc:mysql://{host}:{port}/{name}",
|
||||
defaultValue :
|
||||
{
|
||||
host : "",
|
||||
port : 3306,
|
||||
name : ""
|
||||
},
|
||||
order : 0
|
||||
});
|
||||
|
||||
schemaUrlBuilder.add("PostgreSQL",
|
||||
{
|
||||
template : "jdbc:postgresql://{host}:{port}/{name}",
|
||||
defaultValue :
|
||||
{
|
||||
host : "",
|
||||
port : 5432,
|
||||
name : ""
|
||||
},
|
||||
order : 1
|
||||
});
|
||||
|
||||
schemaUrlBuilder.add("Oracle",
|
||||
{
|
||||
template : "jdbc:oracle:thin:@{host}:{port}/{name}",
|
||||
defaultValue :
|
||||
{
|
||||
host : "",
|
||||
port : 1521,
|
||||
name : ""
|
||||
},
|
||||
order : 2
|
||||
});
|
||||
|
||||
schemaUrlBuilder.add("SQL Server",
|
||||
{
|
||||
template : "jdbc:sqlserver://{host}:{port};DatabaseName={name}",
|
||||
defaultValue :
|
||||
{
|
||||
host : "",
|
||||
port : 1433,
|
||||
name : ""
|
||||
},
|
||||
order : 3
|
||||
});
|
||||
|
||||
schemaUrlBuilder.add("DB2",
|
||||
{
|
||||
template : "jdbc:db2://{host}:{port}/{name}",
|
||||
defaultValue :
|
||||
{
|
||||
host : "",
|
||||
port : 50000,
|
||||
name : ""
|
||||
},
|
||||
order : 4
|
||||
});
|
||||
|
||||
schemaUrlBuilder.add("Sybase",
|
||||
{
|
||||
template : "jdbc:sybase:Tds:{host}:{port}/{name}",
|
||||
defaultValue :
|
||||
{
|
||||
host : "",
|
||||
port : 5000,
|
||||
name : ""
|
||||
},
|
||||
order : 5
|
||||
});
|
||||
|
||||
schemaUrlBuilder._asBuiltInDbTypes();
|
||||
})
|
||||
(jQuery);
|
||||
|
||||
/**
|
||||
* $.schemaUrlBuilder.add的简写函数,便于自定义调用。
|
||||
*
|
||||
* @param dbType
|
||||
* @param builder
|
||||
* @returns
|
||||
*/
|
||||
function addDbURLBuilder(dbType, builder)
|
||||
{
|
||||
$.schemaUrlBuilder.add(dbType, builder)
|
||||
}
|
||||
(jQuery);
|
Loading…
Reference in New Issue