在本教程中,我们将遵循 guideline for developing a plugin 来开发一个下载PDF 数据列表插件.有关更多详细信息步骤,请参阅第一个教程 如何开发一个Bean Shell Hash Variable插件.
1.什么问题?
我们需要能够从datalist下载PDF格式的表格数据。
2.如何解决问题?
我们将开发一个 Datalist Action插件 来显示一个按钮来生成一个表单PDF文件。
3.你的插件需要什么输入?
要开发一个PDF下载Datalist Action插件,我们将考虑提供以下内容作为输入。
- 表单ID:将用于生成PDF文件的表单。
- 记录标识列:使用数据列行的标识或列值来加载记录。
- 格式化选项:用于格式化和自定义PDF输出的选项。
4.你的插件的输出和预期结果是什么?
当“PDF Download Datalist Action”用作数据列行操作或列操作时,普通用户将在数据列表的每一行中看到链接以下载PDF文件。一旦链接被点击,一个PDF文件将被提示下载该特定的行。
当插件用于多个datalist行(整个列表操作)时,包含所有生成的每个选定行的PDF文件的zip文件将被点击按钮时提示下载。
5.是否有任何资源/ API可以重复使用?
要开发PDF下载Datalist Action插件,我们可以重用FormPdfUtil中的方法 来生成PDF格式的表单。我们也可以参考 Datalist Form Data Delete Action插件的 源代码。除此之外,我们可以参考 导出表单电子邮件工具 ,了解我们可以在插件中提供哪种插件属性选项,因为导出表单电子邮件工具也使用FormPdfUtil中的方法。
6.准备你的开发环境
我们需要始终准备好Joget Workflow Source Code,并按照这个指导方针建立起来 。
以下教程是用Macbook Pro编写的,Joget源代码是5.0.0版。其他平台命令请参阅 如何开发插件文章。
假设我们的文件夹目录如下所示。
- Home
- joget
- plugins
- jw-community
-5.0.0
“插件”目录是我们将创建和存储我们所有插件的文件夹,“jw-community”目录是Joget Workflow源代码的存储位置。
运行以下命令在“plugins”目录下创建一个maven项目。
cd joget/plugins/ ~/joget/jw-community/5.0.0/wflow-plugin-archetype/create-plugin.sh org.joget.tutorial download_pdf_datalist_action 5.0.0
然后,shell脚本会要求我们输入插件的版本号,并在生成maven项目之前要求我们确认。
Define value for property 'version': 1.0-SNAPSHOT: : 5.0.0 [INFO] Using property: package = org.joget.tutorial Confirm properties configuration: groupId: org.joget.tutorial artifactId: download_pdf_datalist_action version: 5.0.0 package: org.joget.tutorial Y: : y
我们应该在终端上显示“BUILD SUCCESS”消息,并在“plugins”文件夹中创建一个“download_pdf_datalist_action”文件夹。
用你最喜欢的IDE打开maven项目。我将使用 NetBeans。
7. 开始编码吧!
a. 继承插件抽象类
在“org.joget.tutorial”包下创建一个“DownloadPdfDatalistAction”类。然后,使用org.joget.apps.datalist.model.DataListActionDefault抽象类来扩展 该类。请参阅 Datalist Action插件。
b. 实现所有的抽象方法
像往常一样,我们必须执行所有的抽象方法。我们将使用AppPluginUtil.getMessage方法来支持i18n,并使用常量变量MESSAGE_PATH作为消息资源包目录。
现在,我们必须为管理员用户创建一个UI,为我们的插件提供输入。在getPropertyOptions方法中,我们已经指定了我们的 插件属性选项和配置定义文件位于“/properties/downloadPdfDatalistAction.json”。让我们在“download_pdf_datalist_action / src / main”目录下创建一个目录“resources / properties”。创建目录后,在“properties”文件夹中创建一个名为“downloadPdfDatalistAction.json”的文件。
在属性定义选项文件中,我们需要提供如下的选项。请注意,我们可以在我们的属性选项中使用“@@ message.key @@”语法来支持i18n。
[{
title : '@@datalist.downloadPdf.config@@',
properties : [{
name : 'label',
label : '@@datalist.downloadPdf.label@@',
type : 'textfield',
value : '@@datalist.downloadPdf.download@@'
},
{
name : 'formDefId',
label : '@@datalist.downloadPdf.form@@',
type : 'selectbox',
options_ajax : '[CONTEXT_PATH]/web/json/console/app[APP_PATH]/forms/options',
required : 'True'
},
{
name : 'recordIdColumn',
label : '@@datalist.downloadPdf.recordIdColumn@@',
description : '@@datalist.downloadPdf.recordIdColumn.desc@@',
type : 'textfield'
},
{
name : 'confirmation',
label : '@@datalist.downloadPdf.confirmationMessage@@',
type : 'textfield'
}]
},
{
title : '@@datalist.downloadPdf.advanced@@',
properties : [{
name : 'formatting',
label : '@@datalist.downloadPdf.formatting@@',
type : 'codeeditor',
mode : 'css'
},
{
name : 'headerHtml',
label : '@@datalist.downloadPdf.headerHtml@@',
type : 'codeeditor',
mode : 'html'
},
{
name : 'repeatHeader',
label : '@@datalist.downloadPdf.repeatHeader@@',
type : 'checkbox',
options : [{
value : 'true',
label : ''
}]
},
{
name : 'footerHtml',
label : '@@datalist.downloadPdf.footerHtml@@',
type : 'codeeditor',
mode : 'html'
},
{
name : 'repeatFooter',
label : '@@datalist.downloadPdf.repeatFooter@@',
type : 'checkbox',
options : [{
value : 'true',
label : ''
}]
},
{
name : 'hideEmptyValueField',
label : '@@datalist.downloadPdf.hideEmptyValueField@@',
type : 'checkbox',
options : [{
value : 'true',
label : ''
}]
},
{
name : 'showNotSelectedOptions',
label : '@@datalist.downloadPdf.showNotSelectedOptions@@',
type : 'checkbox',
options : [{
value : 'true',
label : ''
}]
}]
}]
完成属性选项以收集输入后,我们可以对插件的主要方法(即executeAction方法)进行操作。
public DataListActionResult executeAction(DataList dataList, String[] rowKeys) {
// only allow POST
HttpServletRequest request = WorkflowUtil.getHttpServletRequest();
if (request != null && !"POST".equalsIgnoreCase(request.getMethod())) {
return null;
}
// check for submited rows
if (rowKeys != null && rowKeys.length > 0) {
try {
//get the HTTP Response
HttpServletResponse response = WorkflowUtil.getHttpServletResponse();
if (rowKeys.length == 1) {
//generate a pdf for download
singlePdf(request, response, rowKeys[0]);
} else {
//generate a zip of all pdfs
multiplePdfs(request, response, rowKeys);
}
} catch (Exception e) {
LogUtil.error(getClassName(), e, "Fail to generate PDF for " + ArrayUtils.toString(rowKeys));
}
}
//return null to do nothing
return null;
}
/**
* Handles for single pdf file
* @param request
* @param response
* @param rowKey
* @throws IOException
* @throws javax.servlet.ServletException
*/
protected void singlePdf(HttpServletRequest request, HttpServletResponse response, String rowKey) throws IOException, ServletException {
byte[] pdf = getPdf(rowKey);
writeResponse(request, response, pdf, rowKey+".pdf", "application/pdf");
}
/**
* Handles for multiple files download. Put all pdfs in zip.
* @param request
* @param response
* @param rowKeys
* @throws java.io.IOException
* @throws javax.servlet.ServletException
*/
protected void multiplePdfs(HttpServletRequest request, HttpServletResponse response, String[] rowKeys) throws IOException, ServletException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zip = new ZipOutputStream(baos);
try {
//create pdf and put in zip
for (String id : rowKeys) {
byte[] pdf = getPdf(id);
zip.putNextEntry(new ZipEntry(id+".pdf"));
zip.write(pdf);
zip.closeEntry();
}
zip.finish();
writeResponse(request, response, baos.toByteArray(), getLinkLabel() +".zip", "application/zip");
} finally {
baos.close();
zip.flush();
}
}
/**
* Generate PDF using FormPdfUtil
* @param id
* @return
*/
protected byte[] getPdf(String id) {
AppDefinition appDef = AppUtil.getCurrentAppDefinition();
String formDefId = getPropertyString("formDefId");
Boolean hideEmptyValueField = null;
if (getPropertyString("hideEmptyValueField").equals("true")) {
hideEmptyValueField = true;
}
Boolean showNotSelectedOptions = null;
if (getPropertyString("showNotSelectedOptions").equals("true")) {
showNotSelectedOptions = true;
}
Boolean repeatHeader = null;
if ("true".equals(getPropertyString("repeatHeader"))) {
repeatHeader = true;
}
Boolean repeatFooter = null;
if ("true".equals(getPropertyString("repeatFooter"))) {
repeatFooter = true;
}
String css = null;
if (!getPropertyString("formatting").isEmpty()) {
css = getPropertyString("formatting");
}
String header = null;
if (!getPropertyString("headerHtml").isEmpty()) {
header = getPropertyString("headerHtml");
header = AppUtil.processHashVariable(header, null, null, null);
}
String footer = null;
if (!getPropertyString("footerHtml").isEmpty()) {
footer = getPropertyString("footerHtml");
footer = AppUtil.processHashVariable(footer, null, null, null);
}
return FormPdfUtil.createPdf(formDefId, id, appDef, null, hideEmptyValueField, header, footer, css, showNotSelectedOptions, repeatHeader, repeatFooter);
}
/**
* Write to response for download
* @param response
* @param bytes
* @param filename
* @param contentType
* @throws IOException
*/
protected void writeResponse(HttpServletRequest request, HttpServletResponse response, byte[] bytes, String filename, String contentType) throws IOException, ServletException {
OutputStream out = response.getOutputStream();
try {
String name = URLEncoder.encode(filename, "UTF8").replaceAll("\\+", "%20");
response.setHeader("Content-Disposition", "attachment; filename="+name+"; filename*=UTF-8''" + name);
response.setContentType(contentType+"; charset=UTF-8");
if (bytes.length > 0) {
response.setContentLength(bytes.length);
out.write(bytes);
}
} finally {
out.flush();
out.close();
//simply foward to a
request.getRequestDispatcher(filename).forward(request, response);
}
}
c. 管理你的插件的依赖库
我们的插件使用javax.servlet.http.HttpServletRequest和javax.servlet.http.HttpServletResponse类,因此我们需要将jsp-api库添加到我们的POM文件中。
<!-- Change plugin specific dependencies here -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
</dependency>
<!-- End change plugin specific dependencies here -->
d. 让你的插件国际化(国际化)准备就绪
我们在getLabel和getDescription方法中使用i18n消息密钥。我们将在我们的属性选项定义中使用i18n消息密钥。然后,我们将需要为我们的插件创建一个消息资源包属性文件。
在“download_pdf_datalist_action / src / main”目录下创建一个目录“resources / messages”。然后,在文件夹中创建一个“DownloadPdfDatalistAction.properties”文件。在属性文件中,添加所有消息密钥及其标签,如下所示。
org.joget.tutorial.DownloadPdfDatalistAction.pluginLabel=Download PDF org.joget.tutorial.DownloadPdfDatalistAction.pluginDesc=Support to download form PDF from datalist datalist.downloadPdf.download=Download datalist.downloadPdf.config=Configure Download PDF Action datalist.downloadPdf.label=Label datalist.downloadPdf.form=Form datalist.downloadPdf.recordIdColumn=Record Id Column datalist.downloadPdf.recordIdColumn.desc=Default to the primary key of the configured binder datalist.downloadPdf.confirmationMessage=Confirmation Message datalist.downloadPdf.hideEmptyValueField=Hide field that without value datalist.downloadPdf.showNotSelectedOptions=Show unselected options for multi options field datalist.downloadPdf.advanced=Advanced datalist.downloadPdf.formatting=Formatting (CSS) datalist.downloadPdf.headerHtml=Header (HTML) datalist.downloadPdf.repeatHeader=Repeat header on every page? datalist.downloadPdf.footerHtml=Footer (HTML) datalist.downloadPdf.repeatFooter=Repeat footer on every page?
e.注册你的插件到Felix框架
接下来,我们将需要在Activator类(在同一个类包中自动生成)中注册我们的插件类,以告诉Felix框架这是一个插件。
public void start(BundleContext context) {
registrationList = new ArrayList<ServiceRegistration>();
//Register plugin here
registrationList.add(context.registerService(DownloadPdfDatalistAction.class.getName(), new DownloadPdfDatalistAction(), null));
}
f. 构建并测试
让我们建立我们的插件。一旦构建过程完成,我们将在“download_pdf_datalist_action / target”目录下找到一个“download_pdf_datalist_action-5.0.0.jar”文件。
然后,让我们上传插件jar到 管理插件。上传jar文件后,再次检查插件是否正确上传并激活。
那么,让我们来试一下datalist。您可以在Datalist Builder的 “操作”下看到我们的新插件。
一旦我们将“下载PDF”操作拖放到数据生成器画布中,我们就可以编辑该操作。以下配置页面将根据我们的属性选项定义显示。
我们添加“下载PDF”操作作为行操作,也是整个列表操作进行测试。我们可以在下面的userview截图中看到正确显示的“下载”按钮。
当点击时,下载pdf。
当整个列表动作被点击时,一个zip文件被下载。
8. 再进一步,分享或出售
您可以从download_pdf_datalist_action.zip下载源代码 。
要下载现成的插件jar,请在http://marketplace.joget.org/上找到它 。






