Joget DX 8 Stable Released
The stable release for Joget DX 8 is now available, with a focus on UX and Governance.
Table of Contents |
---|
In this tutorial, we will following the guideline of developing a plugin to develop our Gantt Chart Userview Menu plugin. Please also refer to the very first tutorial 如何开发一个Bean Shell Hash Variable插件 for more details steps.
I want to display collected form data in a gantt chart view. I can choose to use Jasper Reports Userview Menu to achieve it but It does not look nice and lack of interactive control. Then, I found this Jquery Gantt Chart library developed by Tait Brown under MIT license which look nicer and work better than viewing a gantt chart using Jasper Report. I want to use it to display my collected form data.
We can develop a Userview Menu Plugin to use the Jquery Gantt Chart library to display the collected form data.
To develop a Gantt Chart Userview Menu plugin, we can consider to provide the following as input.
By referring to the library demo, we can quickly come out a static HTML page like the picture below. As this is a Joget plugin tutorial, we will not go into the detail on coding the static HTML page. You can refer to static.zip. We will expect our userview page can display our collected data as the static HTML page.
If you are not familiar with FreeMaker syntax, you should have a look on their document before proceed.
We need to always have our Joget Workflow Source Code ready and builded by following this guideline.
The following of this tutorial is prepared with a Macbook Pro and Joget Source Code version 5.0.0. Please refer to 如何开发插件 for other platform command.
在本教程中,我们将遵循开发插件的 指导方针 来开发我们的甘特图用户视图菜单插件。 有关更多详细信息步骤,请参阅第一个教程 如何开发一个Bean Shell哈希变量插件。
我想在甘特图视图中显示收集的表单数据。我可以选择使用 贾斯珀报告用户视图菜单来实现它,但它看起来不错,缺乏交互控制。然后,我发现这个 由Tait Brown 根据MIT许可开发的Jquery Gantt Chart库 ,看起来比使用Jasper Report的甘特图更好看,效果更好。我想用它来显示我收集的表单数据。
我们可以开发一个 Userview菜单插件 来使用 Jquery甘特图库来显示收集的表单数据。
要开发一个甘特图Userview菜单插件,我们可以考虑提供以下作为输入。
通过参考库演示,我们可以快速地出来一个静态的HTML页面,如下图所示。由于这是一个Joget插件教程,所以我们不会详细介绍静态HTML页面的编码。你可以参考 static.zip。我们预计我们的用户视图页面可以将我们收集的数据显示为静态的HTML页面。
如果您不熟悉 FreeMaker 语法,则在继续之前,您应该查看其文档。
我们需要始终准备好Joget Workflow Source Code,并按照这个指导方针建立起来 。
本教程的以下内容是使用Macbook Pro和Joget源代码5.0.0版编写的。 其他平台命令请参考 如何开发插件。
让我们说我们的文件夹目录如下。 Let said our folder directory as following.
Code Block |
---|
- Home - joget - plugins - jw-community -5.0.0 |
The "plugins" directory is the folder we will create and store all our plugins and the "jw-community" directory is where the Joget Workflow Source code stored.
“plugins”目录是我们要创建和存储我们所有插件的文件夹,“jw-community”目录是Joget Workflow源代码存储的地方。
运行以下命令在“plugins”目录下创建一个maven项目。Run the following command to create a maven project in "plugins" directory.
Code Block | ||
---|---|---|
| ||
cd joget/plugins/
~/joget/jw-community/5.0.0/wflow-plugin-archetype/create-plugin.sh org.joget.tutorial gantt_chart_menu 5.0.0 |
Then, the shell script will ask us to key in a version for your plugin and ask us for confirmation before generate the maven project.
5.0.0 |
然后,shell脚本将要求我们为您的插件输入一个版本,并在生成maven项目之前要求我们确认。
Code Block | ||
---|---|---|
| ||
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: gantt_chart_menu version: 5.0.0 package: org.joget.tutorial Y: : y |
We should get "BUILD SUCCESS" message shown in our terminal and a "gantt我们应该在终端上显示“BUILD SUCCESS”消息,并在“plugins”文件夹中创建一个“gantt_chart_menu" folder created in "plugins" folder.menu”文件夹。
用你最喜欢的IDE打开maven项目。我将使用 NetBeans。 Open the maven project with your favourite IDE. I will be using NetBeans.
...
在“org.joget.tutorial”包下创建一个“GanttChartMenu”类。然后,使用Create a "GanttChartMenu" class under "org.joget.tutorial" package. Then, extend the class with org.joget.apps.userview.model.UserviewMenu abstract class. Please refer to Userview Menu Plugin. 抽象类来扩展 该类。请参阅用户 视图菜单插件。
...
像往常一样,我们必须执行所有的抽象方法。我们将使用AppPluginUtil.getMessage方法来支持i18n,并使用常量变量MESSAGE_PATH作为消息资源包目录。As usual, we have to implement all the abstract methods. We will using AppPluginUtil.getMessage method to support i18n and using constant variable MESSAGE_PATH for message resource bundle directory.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
package org.joget.tutorial; import org.joget.apps.app.service.AppPluginUtil; import org.joget.apps.userview.model.UserviewMenu; public class GanttChartMenu extends UserviewMenu { private final static String MESSAGE_PATH = "messages/GanttChartMenu"; public String getName() { return "Gantt Chart Menu"; } public String getVersion() { return "5.0.0"; } public String getClassName() { return getClass().getName(); } public String getLabel() { //support i18n return AppPluginUtil.getMessage("org.joget.tutorial.GanttChartMenu.pluginLabel", getClassName(), MESSAGE_PATH); } public String getDescription() { //support i18n return AppPluginUtil.getMessage("org.joget.tutorial.GanttChartMenu.pluginDesc", getClassName(), MESSAGE_PATH); } public String getPropertyOptions() { return AppUtil.readPluginResource(getClassName(), "/properties/ganttChartMenu.json", null, true, MESSAGE_PATH); } @Override public String getCategory() { return "Marketplace"; } @Override public String getIcon() { //sorry, i am reuse the icon of other plugin here return "/plugin/org.joget.apps.userview.lib.HtmlPage/images/grid_icon.gif"; } @Override public boolean isHomePageSupported() { return true; // Can use as first page of the userview } @Override public String getDecoratedMenu() { return null; // using default } @Override public String getRenderPage() { throw new UnsupportedOperationException("Not supported yet."); } } |
Then, we have to do a UI for admin user to provide inputs for our plugin. In getPropertyOptions method, we have already specifed our 插件属性选项与配置 definition file is locate at "然后,我们必须为管理员用户提供一个UI来为我们的插件提供输入。在getPropertyOptions方法中,我们已经指定了我们的 插件属性选项和配置 定义文件位于“/properties/ganttChartMenu.json". Let us create a directory "resources/properties" under "ganttjson”。让我们在“gantt_chart_menu / src / main" directory. After create the directory, create a file named "ganttChartMenu.json" in the "properties" folder.In the properties definition options file, we will need to provide options as below. Please note that we can use "@@message.key@@" syntax to support i18n in our properties options.main”目录下创建一个目录“resources / properties”。创建目录后,在“properties”文件夹中创建一个名为“ganttChartMenu.json”的文件。
在属性定义选项文件中,我们需要提供如下的选项。请注意,我们可以在我们的属性选项中使用“@@ message.key @@”语法来支持i18n。
Code Block | ||
---|---|---|
| ||
[{ title : '@@userview.ganttchart.config@@', properties : [{ name : 'id', label : 'Id', type : 'hidden' }, { name : 'customId', label : '@@userview.ganttchart.customId@@', type : 'textfield', regex_validation : '^[a-zA-Z0-9_]+$', validation_message : '@@userview.ganttchart.invalidId@@' }, { name : 'label', label : '@@userview.ganttchart.label@@', type : 'textfield', required : 'True' }, { name : 'title', label : '@@userview.ganttchart.title@@', type : 'textfield' }, { name : 'binder', label : '@@userview.ganttchart.binder@@', type : 'elementselect', required : 'True', options_ajax : '[CONTEXT_PATH]/web/property/json/getElements?classname=org.joget.apps.datalist.model.DataListBinderDefault', url : '[CONTEXT_PATH]/web/property/json[APP_PATH]/getPropertyOptions' }, { label : '@@userview.ganttchart.mapping@@', type : 'header' }, { name : 'category', label : '@@userview.ganttchart.mapping.category@@', type : 'textfield', required : 'True' }, { name : 'task', label : '@@userview.ganttchart.mapping.task@@', type : 'textfield', required : 'True' }, { name : 'activity', label : '@@userview.ganttchart.mapping.activity@@', type : 'textfield', required : 'True' }, { name : 'fromDate', label : '@@userview.ganttchart.mapping.fromDate@@', type : 'textfield', required : 'True' }, { name : 'toDate', label : '@@userview.ganttchart.mapping.toDate@@', type : 'textfield', required : 'True' }, { name : 'dateFormat', label : '@@userview.ganttchart.mapping.dateFormat@@', type : 'textfield', required : 'True', value : 'yyyy-MM-dd' }, { name : 'taskId', label : '@@userview.ganttchart.mapping.taskId@@', type : 'textfield' }, { name : 'cssClass', label : '@@userview.ganttchart.mapping.cssClass@@', type : 'textfield' }] }, { title : '@@userview.ganttchart.advanced@@', properties : [ { name : 'itemsPerPage', label : '@@userview.ganttchart.itemsPerPage@@', type : 'textfield', required : 'True', value : '20' }, { name : 'navigate', label : '@@userview.ganttchart.navigate@@', type : 'selectbox', required : 'True', value : 'scroll', options : [{ value : 'buttons', label : '@@userview.ganttchart.navigate.buttons@@' }, { value : 'scroll', label : '@@userview.ganttchart.navigate.scroll@@' }] }, { name : 'scale', label : '@@userview.ganttchart.scale@@', type : 'selectbox', required : 'True', value : 'days', options : [{ value : 'hours', label : '@@userview.ganttchart.scale.hours@@' }, { value : 'days', label : '@@userview.ganttchart.scale.days@@' }, { value : 'weeks', label : '@@userview.ganttchart.scale.weeks@@' }, { value : 'months', label : '@@userview.ganttchart.scale.months@@' }] }, { name : 'maxScale', label : '@@userview.ganttchart.maxScale@@', type : 'selectbox', required : 'True', value : 'months', options : [{ value : 'hours', label : '@@userview.ganttchart.scale.hours@@' }, { value : 'days', label : '@@userview.ganttchart.scale.days@@' }, { value : 'weeks', label : '@@userview.ganttchart.scale.weeks@@' }, { value : 'months', label : '@@userview.ganttchart.scale.months@@' }] }, { name : 'minScale', label : '@@userview.ganttchart.minScale@@', type : 'selectbox', required : 'True', value : 'hours', options : [{ value : 'hours', label : '@@userview.ganttchart.scale.hours@@' }, { value : 'days', label : '@@userview.ganttchart.scale.days@@' }, { value : 'weeks', label : '@@userview.ganttchart.scale.weeks@@' }, { value : 'months', label : '@@userview.ganttchart.scale.months@@' }] }, { name : 'useCookie', label : '@@userview.ganttchart.useCookie@@', type : 'checkbox', options : [{ value : 'true', label : '' }] }, { name : 'scrollToToday', label : '@@userview.ganttchart.scrollToToday@@', type : 'checkbox', options : [{ value : 'true', label : '' }] }, { name : 'onItemClick', label : '@@userview.ganttchart.onItemClick@@', type : 'codeeditor', mode : 'javascript', value : '//console.log(data); //data obj hold all the columns value of a row' }, { name : 'onAddClick', label : '@@userview.ganttchart.onAddClick@@', type : 'codeeditor', mode : 'javascript', value : '//console.log(datetime); //the DateTime in ms for the clicked Cell\n//console.log(rowId); //the row ID of clicked Cell' }, { name : 'onRender', label : '@@userview.ganttchart.onRender@@', type : 'codeeditor', mode : 'javascript', value : '//console.log("chart rendered");' }, { name : 'customHeader', label : '@@userview.ganttchart.customHeader@@', type : 'codeeditor', mode : 'html' }, { name : 'customFooter', label : '@@userview.ganttchart.customFooter@@', type : 'codeeditor', mode : 'html' }] }] type : 'codeeditor', mode : 'html' }] }] |
完成收集输入的属性选项后,我们可以使用getRenderPage方法的插件的主要方法。通常情况下,我会先详细地将数据填充到视图中,首先将getRenderPage的静态内容先构建并测试插件。这一切都很好,只有我们试图添加数据到视图。After done the properties option to collect input, we can work on the main method of the plugin which is getRenderPage method. Normally, what I will do before go into detail to populate the data to the view, I will first put the static content for the getRenderPage to build and test the plugin first. It everything is fine, then only we try to add data to the view.
Code Block | ||
---|---|---|
| ||
@Override public String getRenderPage() { Map model = new HashMap(); model.put("request", getRequestParameters()); model.put("element", this); PluginManager pluginManager = (PluginManager)AppUtil.getApplicationContext().getBean("pluginManager"); String content = pluginManager.getPluginFreeMarkerTemplate(model, getClass().getName(), "/templates/ganttChart.ftl", null); return content; } |
Then, let us create a template file locate at "然后,让我们创建一个位于“/templates/ganttChart.ftl". Let us create a directory "resources/templates" under "ganttftl”的模板文件。让我们在“gantt_chart_menu / src/main" directory. After create the directory, create a file named "ganttChartMenu.json" in the "templates" folder.src / main”目录下创建一个目录“resources / templates”。创建目录后,在“模板”文件夹中创建一个名为“ganttChartMenu.json”的文件。
将我们之前创建的静态HTML放到模板文件中,如下所示。记得把所有的依赖关系的JavaScript库和图像放在“ganttPut the static HTML we create previously into the template file as below. Remember to put all the dependencies javascript library and images under "gantt_chart_menu / src / main / resources / resources" and change the url as below accordingly. You project directory should look like the image below now.resources”下,并相应地更改url。你的项目目录应该看起来像下面的图片。
Code Block | ||
---|---|---|
| ||
<link href="${request.contextPath}/plugin/org.joget.tutorial.GanttChartMenu/lib/jquery/gantt/css/style.css" rel="stylesheet" type="text/css" /> <script src="${request.contextPath}/plugin/org.joget.tutorial.GanttChartMenu/lib/jquery/gantt/js/jquery.fn.gantt.min.js"></script> <div class="gantt_chart_menu_body"> <h3>Plugin Development</h3> <div class="gantt"></div> <script> $(function() { "use strict"; $(".gantt").gantt({ source: [{ name: "Sprint 0", desc: "Analysis", values: [{ from: "/Date(1320192000000)/", to: "/Date(1322401600000)/", label: "Requirement Gathering", customClass: "ganttRed" }] },{ name: " ", desc: "Scoping", values: [{ from: "/Date(1322611200000)/", to: "/Date(1323302400000)/", label: "Scoping", customClass: "ganttRed" }] },{ name: "Sprint 1", desc: "Development", values: [{ from: "/Date(1323802400000)/", to: "/Date(1325685200000)/", label: "Development", customClass: "ganttGreen" }] },{ name: " ", desc: "Showcasing", values: [{ from: "/Date(1325685200000)/", to: "/Date(1325695200000)/", label: "Showcasing", customClass: "ganttBlue" }] },{ name: "Sprint 2", desc: "Development", values: [{ from: "/Date(1326785200000)/", to: "/Date(1325785200000)/", label: "Development", customClass: "ganttGreen" }] },{ name: " ", desc: "Showcasing", values: [{ from: "/Date(1328785200000)/", to: "/Date(1328905200000)/", label: "Showcasing", customClass: "ganttBlue" }] },{ name: "Release Stage", desc: "Training", values: [{ from: "/Date(1330011200000)/", to: "/Date(1336611200000)/", label: "Training", customClass: "ganttOrange" }] },{ name: " ", desc: "Deployment", values: [{ from: "/Date(1336611200000)/", to: "/Date(1338711200000)/", label: "Deployment", customClass: "ganttOrange" }] },{ name: " ", desc: "Warranty Period", values: [{ from: "/Date(1336611200000)/", to: "/Date(1349711200000)/", label: "Warranty Period", customClass: "ganttOrange" }] }], navigate: "scroll", maxScale: "hours", itemsPerPage: 10 }); }); </script> </div> |
Now, for testing purpose, we can skip to c. Manage the dependency libraries of your plugin, d. Make your plugin internationalization (i18n) ready, e. Register your plugin to Felix Framework and f. Build it and testing then continue the below after testing it. You will get something similar to below in your userview.
现在,为了测试目的,我们可以跳到 c。管理你的插件的依赖库,d。让你的插件国际化(国际化), 例如。将您的插件注册到Felix Framework和 f。构建它并测试, 然后在测试之后继续下面的内容。你会在你的用户视图中看到类似于下面的内容。
在验证静态HTML在我们的插件中工作之后,我们可以通过向视图添加数据来进一步增强它。现在,修改您的getRenderPage方法和ganttChart.ftl模板如下。After verify the static HTML is working in our plugin, we can further enhance it by adding data to the view. Now, modify your getRenderPage method and ganttChart.ftl template as below.
Code Block | ||
---|---|---|
| ||
@Override public String getRenderPage() { Map model = new HashMap(); model.put("request", getRequestParameters()); model.put("element", this); //populate data in JSON format model.put("data", getData()); PluginManager pluginManager = (PluginManager)AppUtil.getApplicationContext().getBean("pluginManager"); String content = pluginManager.getPluginFreeMarkerTemplate(model, getClass().getName(), "/templates/ganttChart.ftl", MESSAGE_PATH); return content; } protected String getData() { String json = "[]"; try { DataListCollection data = null; String idColumn = getPropertyString("taskId"); //get the binder Object binderData = getProperty("binder"); if (binderData != null && binderData instanceof Map) { Map bdMap = (Map) binderData; if (bdMap != null && bdMap.containsKey("className") && !bdMap.get("className").toString().isEmpty()) { PluginManager pluginManager = (PluginManager) AppUtil.getApplicationContext().getBean("pluginManager"); DataListBinder binder = (DataListBinder) pluginManager.getPlugin(bdMap.get("className").toString()); if (binder != null) { Map bdProps = (Map) bdMap.get("properties"); binder.setProperties(bdProps); data = binder.getData(null, bdProps, new DataListFilterQueryObject[0], null, null, null, null); if (idColumn.isEmpty()) { idColumn = binder.getPrimaryKeyColumnName(); } } } } JSONArray dataArry = new JSONArray(); String dateFormat = getPropertyString("dateFormat"); SimpleDateFormat sdf = new SimpleDateFormat(dateFormat); if (data != null && !data.isEmpty()) { String currentCategory = null; int cat_count = 0; String currentTask = null; int task_count = 0; int act_count = 0; JSONObject taskObj = null; JSONArray taskValuesArray = null; for (Object r : data) { String id = getValue(r, idColumn); String cat = getValue(r, getPropertyString("category")); String task = getValue(r, getPropertyString("task")); String act = getValue(r, getPropertyString("activity")); String formDate = getValue(r, getPropertyString("fromDate")); String toDate = getValue(r, getPropertyString("toDate")); String status = getValue(r, getPropertyString("cssClass")); if (currentTask == null || !currentTask.equals(task) || (currentTask.equals(task) && !cat.equals(currentCategory))) { currentTask = task; if (taskObj != null) { taskObj.put("values", taskValuesArray); dataArry.put(taskObj); } taskObj = new JSONObject(); taskValuesArray = new JSONArray(); taskObj.put("desc", task); taskObj.put("id", id); task_count++; } if (currentCategory == null || !currentCategory.equals(cat)) { currentCategory = cat; taskObj.put("name", cat); cat_count++; } JSONObject valueObj = new JSONObject(); JSONObject actObj = new JSONObject(); act_count++; actObj.put("taskId", id); actObj.put("category", cat); actObj.put("task", task); actObj.put("activity", act); actObj.put("formDate", formDate); actObj.put("toDate", toDate); actObj.put("status", status); valueObj.put("dataObj", actObj); valueObj.put("label", act); valueObj.put("from", "/Date("+sdf.parse(formDate).getTime()+")/"); valueObj.put("to", "/Date("+sdf.parse(toDate).getTime()+")/"); valueObj.put("customClass", "cat_"+cat_count+" task_"+task_count+" act_"+act_count+" "+status.replace(" ", "_")); taskValuesArray.put(valueObj); } if (taskObj != null) { taskObj.put("values", taskValuesArray); dataArry.put(taskObj); } } return dataArry.toString(); } catch (Exception e) { LogUtil.error(GanttChartMenu.class.getName(), e, json); } return json; } protected String getValue(Object o, String name) { String value = ""; try { Object v = LookupUtil.getBeanProperty(o, name); if (v != null) { return v.toString(); } } catch (Exception e) { LogUtil.error(GanttChartMenu.class.getName(), e, name); } return value; } |
Code Block |
---|
<link href="${request.contextPath}/plugin/org.joget.tutorial.GanttChartMenu/lib/jquery.gantt/css/style.css" rel="stylesheet" type="text/css" /> <script src="${request.contextPath}/plugin/org.joget.tutorial.GanttChartMenu/lib/jquery.gantt/js/jquery.fn.gantt.min.js"></script> <div class="gantt_chart_menu_body"> <#if element.properties.title?? ><h3>${element.properties.title!}</h3></#if> ${element.properties.customHeader!} <div class="gantt"></div> <script> $(function() { "use strict"; $(".gantt").gantt({ source: ${data!}, months: [@@userview.ganttChart.months.label@@], dow: [@@userview.ganttChart.dow.label@@], itemsPerPage: ${element.properties.itemsPerPage!}, navigate: "${element.properties.navigate!}", scale: "${element.properties.scale!}", maxScale: "${element.properties.maxScale!}", minScale: "${element.properties.minScale!}", waitText: "@@userview.ganttChart.waitText@@", onItemClick: function (data) { ${element.properties.onItemClick!} }, onAddClick: function(datetime, rowId) { ${element.properties.onAddClick!} }, onRender: function() { ${element.properties.onRender!} }, useCookie: <#if element.properties.useCookie! == 'true'>true<#else>false</#if>, scrollToToday: <#if element.properties.scrollToToday! == 'true'>true<#else>false</#if> }); }); </script> ${element.properties.customFooter!} </div> |
...
我们的插件正在使用一些库,我们必须将它们全部添加到我们的POM文件中。Our plugin is using some libraries, we have to add all of them to our POM file.
Code Block | ||
---|---|---|
| ||
<!-- Change plugin specific dependencies here -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20080701</version>
</dependency>
<dependency>
<groupId>displaytag</groupId>
<artifactId>displaytag</artifactId>
<version>1.2</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>jcl104-over-slf4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- End change plugin specific dependencies here --> |
We are using i18n message key in getLabel and getDescription method. We also used i18n message key in our properties options definition as well. So, we will need to create a message resource bundle properties file for our plugin.
--> |
我们在getLabel和getDescription方法中使用i18n消息密钥。我们还在我们的属性选项定义中使用了i18n消息密钥。所以,我们需要为我们的插件创建一个消息资源包属性文件。
在“ganttCreate directory "resources/messages" under "gantt_chart_menu / src / main”目录下创建目录“resources / main" directory. Then, create a "GanttChartMenu.properties" file in the folder. In the properties file, let add all the message keys and its label as below.messages”。然后,在文件夹中创建一个“GanttChartMenu.properties”文件。在属性文件中,让我们添加所有的消息键和它的标签如下。
Code Block |
---|
org.joget.tutorial.GanttChartMenu.pluginLabel=Gantt Chart Menu org.joget.tutorial.GanttChartMenu.pluginDesc=Display data in a Gantt Chart view userview.ganttchart.config=Configure Gantt Chart Menu userview.ganttchart.customId=Custom ID userview.ganttchart.invalidId=Only alpha-numeric and underscore characters allowed userview.ganttchart.label=Label userview.ganttchart.title=Page Title userview.ganttchart.binder=Data Binder userview.ganttchart.mapping=Column to Data Mappings userview.ganttchart.mapping.category=Category (column ID) userview.ganttchart.mapping.task=Task (column ID) userview.ganttchart.mapping.activity=Activity (column ID) userview.ganttchart.mapping.fromDate=Activity From Date (column ID) userview.ganttchart.mapping.toDate=Activity To Date (column ID) userview.ganttchart.mapping.dateFormat=Date format for Activity From/To Date userview.ganttchart.mapping.taskId=Task Id (column ID) userview.ganttchart.mapping.cssClass=Status (column ID, use as CSS class for styling) userview.ganttchart.advanced=Advanced userview.ganttchart.itemsPerPage=Item pre page userview.ganttchart.navigate=Navigator userview.ganttchart.navigate.buttons=Buttons userview.ganttchart.navigate.scroll=Scroll userview.ganttchart.scale=Default Scale userview.ganttchart.scale.hours=Hours userview.ganttchart.scale.days=Days userview.ganttchart.scale.weeks=Weeks userview.ganttchart.scale.months=Months userview.ganttchart.maxScale=Maximum Scale userview.ganttchart.minScale=Minimum Scale userview.ganttchart.useCookie=Use cookie for storing chart states userview.ganttchart.scrollToToday=Auto scroll to today after rendered userview.ganttchart.onItemClick=On Item Clicked Event (Javascript) userview.ganttchart.onAddClick=On Empty Space Clicked Event (Javascript) userview.ganttchart.onRender=On Rendered Event (Javascript) userview.ganttchart.customHeader=Custom Header (HTML) userview.ganttchart.customFooter=Custom Footer (HTML) userview.ganttChart.months.label="January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" userview.ganttChart.dow.label="S", "M", "T", "W", "T", "F", "S" userview.ganttChart.waitText=Loading... |
...
我们将不得不在Activator类(在同一个类包中自动生成)中注册我们的插件类,以告诉Felix框架这是一个插件。We will have to register our plugin class in Activator class (Auto generated in the same class package) to tell Felix Framework that this is a plugin.
Code Block | ||
---|---|---|
| ||
public void start(BundleContext context) { registrationList = new ArrayList<ServiceRegistration>(); //Register plugin here registrationList.add(context.registerService(GanttChartMenu.class.getName(), new GanttChartMenu(), null)); } |
...
让我们建立我们的插件。一旦构建过程完成,我们将在“gantt_chart_menu / target”目录下创建一个“ganttLet build our plugin. Once the building process is done, we will found a "gantt_chart_menu-5.0.0.jar" file is created under "gantt_chart_menu/target" directory.
Then, let upload the plugin jar to Manage Plugins. After upload the jar file, double check the plugin is uploaded and activated correctly.
Open a userview, you will see the new plugin is added under "Marketplace". Drag it to one of your Userview Category.
Edit the properties of the Gantt Chart Menu.
I selected "Form Data Binder" as "Data Binder" for testing. Fill all the mappings to corresponding Field Id/Column Id.
Configure binder.
Advanced setting to configure the gantt chart.
Writing some css styling in "Custom Footer (HTML)" option to give different colors for different status.
Populate some data to the form for testing.
The end result.
jar”文件。
然后,让插件jar上传到 管理插件。上传jar文件后,仔细检查插件是否正确上传和激活。
打开一个用户视图,你会看到新的插件被添加到“市场”下。将其拖到您的一个Userview类别.
编辑甘特图菜单的属性。
我选择“表单数据包装器”作为“数据包装器”进行测试。将所有映射填充到相应的字段标识/列标识。
配置选项。
高级设置来配置甘特图。
在“自定义页脚(HTML)”选项中编写一些css样式,为不同的状态提供不同的颜色。
将一些数据填充到表单中进行测试。
最终的结果。
您可以从You can download the source code from gantt_chart_menu.zip.下载源代码 。
本教程的测试应用程序是 The test app for this tutorial is APP_testGanttChart-1-20151106194035.jwa.。
要下载现成的插件jar,请在To download the ready-to-use plugin jar, please find it in http://marketplace.joget.org/. 上找到它 。