Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

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.

1. What is the problem?

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.

2. What is your idea to solve the problem?

We can develop a Userview Menu Plugin to use the Jquery Gantt Chart library to display the collected form data.

3. What is the input needed for your plugin?

To develop a Gantt Chart Userview Menu plugin, we can consider to provide the following as input.

  1. Data Binder : We can reuse datalist binder to retrieve data
  2. Data Mapping : Map the retrieve data from datalist binder to the data format of the library
  3. Formatting options : Options to format and customise the gantt chart. 

4. What is the output and expected outcome of your plugin?

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.

Image Removed

5. Is there any resources/API that can be reuse?

If you are not familiar with FreeMaker syntax, you should have a look on their document before proceed.

6. Prepare your development environment

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哈希变量插件

1.什么问题?

我想在甘特图视图中显示收集的表单数据。我可以选择使用  贾斯珀报告用户视图菜单来实现它,但它看起来不错,缺乏交互控制。然后,我发现这个  由Tait Brown  根据MIT许可开发的Jquery Gantt Chart库  ,看起来比使用Jasper Report的甘特图更好看,效果更好。我想用它来显示我收集的表单数据。  

2.你有什么想法来解决这个问题?

我们可以开发一个  Userview菜单插件  来使用  Jquery甘特图库来显示收集的表单数据。

3.你的插件需要什么输入?

要开发一个甘特图Userview菜单插件,我们可以考虑提供以下作为输入。

  1. 数据绑定器:我们可以重用数据绑定器来检索数据
  2. 数据映射:将检索数据从数据列表绑定到映射库的数据格式
  3. 格式化选项:用于格式化和自定义甘特图的选项。 

4.你的插件的输出和预期结果是什么?

通过参考库演示,我们可以快速地出来一个静态的HTML页面,如下图所示。由于这是一个Joget插件教程,所以我们不会详细介绍静态HTML页面的编码。你可以参考  static.zip。我们预计我们的用户视图页面可以将我们收集的数据显示为静态的HTML页面。

Image Added

5.有没有可重用的资源/ API?

如果您不熟悉  FreeMaker  语法,则在继续之前,您应该查看其文档。

6.准备你的开发环境

我们需要始终准备好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
languagebash
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
languagebash
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.  

7. Just code it!

a.

...

扩展插件类型的抽象类

在“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.  抽象类来扩展  该类。请参阅用户  视图菜单插件

b.

...

实现所有的抽象方法

像往常一样,我们必须执行所有的抽象方法。我们将使用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
languagejava
titleImplementation of all basic abstract methods
collapsetrue
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
languagejs
 [{
    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
languagejava
    @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
languagexml
<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) readye. 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.

Image Removed

现在,为了测试目的,我们可以跳到  c。管理你的插件的依赖库d。让你的插件国际化(国际化),  例如。将您的插件注册到Felix Framework和  f。构建它并测试,  然后在测试之后继续下面的内容。你会在你的用户视图中看到类似于下面的内容。

Image Added

在验证静态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
languagejava
    @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>

c.

...

管理你的插件的依赖库

我们的插件正在使用一些库,我们必须将它们全部添加到我们的POM文件中。Our plugin is using some libraries, we have to add all of them to our POM file.

Code Block
languagexml
        <!-- 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 -->

d. Make your plugin internationalization (i18n) ready

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.

-->

d. 让你的插件国际化(国际化)

我们在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...

e.

...

注册你的插件Felix框架

我们将不得不在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
languagejava
    public void start(BundleContext context) {
        registrationList = new ArrayList<ServiceRegistration>();
        //Register plugin here
        registrationList.add(context.registerService(GanttChartMenu.class.getName(), new GanttChartMenu(), null));
    }

f.

...

建设和测试 

让我们建立我们的插件。一旦构建过程完成,我们将在“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.

Image Removed

Open a userview, you will see the new plugin is added under "Marketplace". Drag it to one of your Userview Category.

Image Removed

Edit the properties of the Gantt Chart Menu.

Image Removed

I selected "Form Data Binder" as "Data Binder" for testing. Fill all the mappings to corresponding Field Id/Column Id.

Image Removed

Configure binder.

Image Removed

Advanced setting to configure the gantt chart.

Image Removed

Writing some css styling in "Custom Footer (HTML)" option to give different colors for different status.

Image Removed

Populate some data to the form for testing.

Image Removed

The end result.

8. Take a step further, share it or sell it

jar”文件。

然后,让插件jar上传到  管理插件。上传jar文件后,仔细检查插件是否正确上传和激活。

Image Added

打开一个用户视图,你会看到新的插件被添加到“市场”下。将其拖到您的一个Userview类别.

Image Added

编辑甘特图菜单的属性。

Image Added

我选择“表单数据包装器”作为“数据包装器”进行测试。将所有映射填充到相应的字段标识/列标识。

Image Added

配置选项。

Image Added

高级设置来配置甘特图。

Image Added

在“自定义页脚(HTML)”选项中编写一些css样式,为不同的状态提供不同的颜色。

Image Added

将一些数据填充到表单中进行测试。

Image Added

最终的结果。

8. 再进一步,分享或出售

您可以从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/找到它  。