Joget DX 8 Stable Released
The stable release for Joget DX 8 is now available, with a focus on UX and Governance.
ในบทเรียนนี้เราจะติดตาม guideline for developing a plugin เพื่อพัฒนาปลั๊กอินดาวน์โหลด PDF Datalist Action ของเรา โปรดอ้างอิงถึงบทช่วยสอนแรกสุด วิธีการพัฒนา Bean Shell Hash Variable สำหรับขั้นตอนรายละเอียดเพิ่มเติม
ราต้องการความสามารถในการดาวน์โหลดข้อมูลแบบฟอร์มเป็นไฟล์ PDF จากดาต้าลิสต์
เราจะพัฒนา Datalist Action Plugin เพื่อแสดงปุ่มเพื่อสร้างไฟล์ PDF ของแบบฟอร์ม
เพื่อพัฒนาปลั๊กอิน PDF Download Datalist Action เราจะพิจารณาให้สิ่งต่อไปนี้เป็นอินพุต
เมื่อ PDF Download Datalist Action ถูกใช้เป็นแอ็คชันแถวของ datalist หรือคอลัมน์แอ็คชัน ผู้ใช้ปกติจะเห็นลิงค์เพื่อดาวน์โหลดไฟล์ PDF ในทุก ๆ แถวของดาต้าลิสต์ เมื่อคลิกที่ลิงค์ ไฟล์ PDF จะถูกถามให้ดาวน์โหลดสำหรับแถวที่ระบุ
เมื่อมีการใช้ปลั๊กอินสำหรับดาต้าลิสต์หลายแถว (การดำเนินการรายการทั้งหมด) ไฟล์ซิปที่มีไฟล์ PDF ที่สร้างขึ้นทั้งหมดของทุกแถวที่เลือกจะได้รับแจ้งให้ดาวน์โหลดเมื่อมีการคลิกปุ่ม
เพื่อพัฒนาปลั๊กอิน PDF Download Datalist Action. เราสามารถใช้วิธีการใน FormPdfUtil เพื่อสร้างแบบฟอร์ม PDF. เรายังสามารถอ้างถึง source code ของปลั๊กอินการดำเนินการลบแบบฟอร์มข้อมูลดาต้าลิสต์ นอกจากนั้นเรายังสามารถอ้างถึง เครื่องมือส่งออกแบบฟอร์มอีเมล์ (Export Form Email Tool) ในตัวเลือกคุณสมบัติปลั๊กอินที่เราสามารถให้ในปลั๊กอินเนื่องจากเครื่องมือส่งออกฟอร์มอีเมลใช้วิธีการในฟอร์ม PdfUtil ด้วยเช่นกัน
เราจำเป็นต้องให้ซอร์สโค้ด Joget Workflow ของเราพร้อมและสร้างโดยทำตาม this guideline.
บทช่วยสอนต่อไปนี้จัดทำขึ้นด้วย Macbook Pro และ Joget Source Code is version 5.0.0. โปรดดูที่ แนวทางสำหรับการพัฒนาปลั๊กอิน บทความสำหรับคำสั่งแพลตฟอร์มอื่น ๆ
สมมติว่าไดเรกทอรีโฟลเดอร์ของเรามีดังนี้
- Home - joget - plugins - jw-community -5.0.0
The "plugins" directory เป็นโฟลเดอร์ที่เราจะสร้างและจัดเก็บปลั๊กอินของเราและ "jw-community" directory เป็นที่เก็บ Source code Joget Workflow.
เรียกใช้คำสั่งต่อไปนี้เพื่อสร้างโครงการ maven ใน "plugins" directory.
cd joget/plugins/ ~/joget/jw-community/5.0.0/wflow-plugin-archetype/create-plugin.sh org.joget.tutorial download_pdf_datalist_action 5.0-SNAPSHOT
Then, the shell script will ask us to key in a version number for the plugin and ask us for a confirmation before it generates the maven project.
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" ที่ปรากฏในเครื่องของเราและโฟลเดอร์ "download_pdf_datalist_action" ที่สร้างในโฟลเดอร์ "plugins"
เปิดโครงการ maven ด้วย IDE ที่คุณชื่นชอบ เราจะใช้ NetBeans.
สร้าง "DownloadPdfDatalistAction" ภายใต้ "org.joget.tutorial" package. จากนั้น ขยายคลาสด้วย org.joget.apps.datalist.model.DataListActionDefault abstract class. โปรดดูที่ Datalist Action Plugin.
ตามปกติเราต้องใช้วิธีนามธรรมทั้งหมด เราจะใช้วิธี AppPluginUtil.getMessage เพื่อสนับสนุน i18n และการใช้ตัวแปร MESSAGE_PATH คงที่สำหรับ message resource bundle directory.
ตอนนี้เราต้องสร้าง UI สำหรับผู้ใช้ผู้ดูแลระบบเพื่อให้อินพุตสำหรับปลั๊กอินของเรา. ในวิธีการ getPropertyOptions เราได้ระบุไว้แล้วที่ ตัวเลือกคุณสมบัติปลั๊กอิน ไฟล์อยู่ที่ "/properties/downloadPdfDatalistAction.json". Let us create a directory "resources/properties" ภายใต้ "download_pdf_datalist_action/src/main" directory. หลังจากสร้าง directory, สร้างไฟล์ชื่อ "downloadPdfDatalistAction.json" ในโฟลเดอร์ "properties"
ในไฟล์ตัวเลือกคุณสมบัติ เราจะต้องให้ตัวเลือกดังต่อไปนี้ โปรดทราบว่าเราสามารถใช้ "@@message.key@@" syntax เพื่อสนับสนุน 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); } }
ปลั๊กอินของเรากำลังใช้ javax.servlet.http.HttpServletRequest และ javax.servlet.http.HttpServletResponse class, ดังนั้นเราต้องเพิ่ม jsp-api library ไปที่ไฟล์ 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 -->
We are using i18n message key in getLabel and getDescription method. We will use i18n message key in our properties options definition as well. Then, we will need to create a message resource bundle properties file for our plugin.
สร้าง directory, "resources/messages", ภายใต้ "download_pdf_datalist_action/src/main" directory. จากนั้นสร้างไฟล์ "DownloadPdfDatalistAction.properties" ในโฟลเดอร์ ,ในไฟล์ properties เพิ่ม message keys และ its label as below.
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?
ต่อไปเราจะต้องลงทะเบียนคลาสปลั๊กอินของเราในคลาส Activator (สร้างอัตโนมัติในแพ็คเกจคลาสเดียวกัน) เพื่อบอก Felix Framework ว่านี่เป็นปลั๊กอิน
public void start(BundleContext context) { registrationList = new ArrayList<ServiceRegistration>(); //Register plugin here registrationList.add(context.registerService(DownloadPdfDatalistAction.class.getName(), new DownloadPdfDatalistAction(), null)); }
มาสร้างปลั๊กอินของเรากัน เมื่อกระบวนการสร้างเสร็จสิ้นเราจะพบไฟล์ "download_pdf_datalist_action-5.0.0.jar" ที่สร้างขึ้นภายใต้ไดเรกทอรี "download_pdf_datalist_action / target"
จากนั้นลองอัปโหลดปลั๊กอินไปที่ Manage Plugins. หลังจากอัปโหลดไฟล์ jar ตรวจสอบอีกครั้งว่ามีการอัปโหลดและเปิดใช้งานปลั๊กอินอย่างถูกต้อง
จากนั้นมาลองในหนึ่งในนักดาต้า คุณสามารถดูปลั๊กอินใหม่ของเราที่แสดงใน "การทำงาน" ใน ตัวสร้างดาตาลิสต์ (Datalist Builder).
เมื่อเราลากและวางการกระทำ "ดาวน์โหลด PDF" ลงในผืนผ้าใบตัวสร้างดาต้าลิสต์เราสามารถแก้ไขการกระทำได้ หน้าการกำหนดค่าต่อไปนี้จะแสดงตามคำนิยามตัวเลือกคุณสมบัติของเรา
ลองเพิ่มการกระทำ "ดาวน์โหลด PDF" เป็นการกระทำแถวและการกระทำแบบรายการทั้งหมดสำหรับการทดสอบ เราสามารถเห็นปุ่ม "ดาวน์โหลด" แสดงอย่างถูกต้องในหน้าจอ userview ด้านล่าง
มื่อคลิกการกระทำแถวจะมีการดาวน์โหลดไฟล์ PDF
เมื่อมีการคลิกการกระทำทั้งรายการไฟล์ซิปจะถูกดาวน์โหลด
คุณสามารถดาวน์โหลด source code จาก download_pdf_datalist_action.zip.
หากต้องการดาวน์โหลด jar ปลั๊กอินที่พร้อมใช้งานโปรดค้นหาที่ http://marketplace.joget.org/.