カスタムレポートを作成するには、QueryBuilder の REST インターフェイスを使用するか、または QueryBuilder API で OSGi サービスを作成します。

カスタムレポートを作成するための一般的な手順

カスタムレポートを追加する前に、次のテンプレート手順を実行してください。  

  1. カスタムレポートで使用するデータは、プロセスレポートで使用可能なデータである必要があります。 データを確実に使用可能にするには、cron ジョブをスケジュールするか、プロセスレポート UI の「同期」オプションを使用します。

  2. 必要なクエリーをカプセル化した URL リクエストは、該当するクエリー結果オブジェクトを返す必要があります。 クエリーを作成するには、QueryBuilder の REST インターフェイスを使用し、QueryBuilder API で OSGi サービスを作成します。動的または静的なクエリーを作成できます。

  3. 結果を表示するカスタムユーザーインターフェイスを作成します。 スタンドアロンのユーザーインターフェイスを作成するか、または結果を既存のプロセスレポート UI と統合します。

QueryBuilder の REST インターフェイスの使用

CRX QueryBuilder REST インターフェイスは Java API および REST API 経由で Asset Share Query Builder の機能を公開します。 CRX QueryBuilder REST インターフェイスの使用方法を確認してから、次の手順を実行してください。

  1. http://[server]:[port]/lc/bin/querybuilder.json にアクセスします。

  2. プロセスレポートのストレージノードの構造およびプロパティに基づいてクエリーを作成します。

    オプションのパラメーターでオフセット、制限、アクセス数、プロパティを指定できます。 静的レポートの場合は引数をハードコードし、動的レポートの場合はパラメーターを UI から取得します。  

    すべてのプロセス名を取得するためのクエリーは次のように記述します。

    http://[Server]:[Port]/lc/bin/querybuilder.json?exact=false&p.hits=selective&p.properties=pmProcessTitle&path=%2fcontent%2freporting%2fpm&property=pmNodeType&property.operation=equals&property.value=ProcessType&type=sling%3aFolder

    注意:

    どのクエリーでも、path パラメーターで crx 保存場所を指定し、URL 標準に従って文字をエスケープします。  

Query Builder API を使用したサービスの作成 

QueryBuilder API を使用してサービスを作成するための前提条件は、CQ OSGI バンドルを作成してデプロイすること、および Query Builder API を使用することです。

  1. 適切なアノテーションを使用して OSGi サービスを作成します。 QueryBuilder にアクセスするには、次のように記述します。

    @Reference(referenceInterface = QueryBuilder.class)
    private QueryBuilder queryBuilder; 

  2. predicate グループを作成します。 predicate グループを作成するコードは次のとおりです。  

    PredicateGroup predicateGroup = new PredicateGroup();
    predicateGroup.setAllRequired(true);

  3. 新しく作成した predicateGroup に predicate を追加します。 役に立つ predicate 構造としては、JcrBoolPropertyPredicateEvaluatorJcrPropertyPredicateEvaluatorRangePropertyPredicateEvaluatorDateRangePredicateEvaluatorTypePredicateEvaluator などがあります。

    静的レポートの場合は predicate をハードコードし、動的レポートの場合はリクエストから predicate を取得します。

    プロセスのすべてのインスタンスを取得するサンプルコードは次のとおりです。  

    Predicate predicate;
     
      //Add the path Constraint
      predicate = new Predicate(PathPredicateEvaluator.PATH);
      predicate.set(PathPredicateEvaluator.PATH, "/content/reporting/pm"); // should point to the crx path being used to store data
      predicate.set(PathPredicateEvaluator.EXACT, "false");
      predicateGroup.add(predicate);
     
      //type nt:unstructured
      predicate = new Predicate(TypePredicateEvaluator.TYPE);
      predicate.set(TypePredicateEvaluator.TYPE, "nt:unstructured");
      predicateGroup.add(predicate);
     
      //NodeType: Process Instance
      predicate = new Predicate(JcrPropertyPredicateEvaluator.PROPERTY);
      predicate.set(JcrPropertyPredicateEvaluator.PROPERTY, "pmNodeType");
      predicate.set(JcrPropertyPredicateEvaluator.OPERATION, JcrPropertyPredicateEvaluator.OP_EQUALS);
      predicate.set(JcrPropertyPredicateEvaluator.VALUE, "ProcessInstance");
      predicateGroup.add(predicate);
     
      //processName
      predicate = new Predicate(JcrPropertyPredicateEvaluator.PROPERTY);
      predicate.set(JcrPropertyPredicateEvaluator.PROPERTY, "pmProcessName");
      predicate.set(JcrPropertyPredicateEvaluator.OPERATION, JcrPropertyPredicateEvaluator.OP_EQUALS);
      predicate.set(JcrPropertyPredicateEvaluator.VALUE, processName); //processName variable stores the name of the process whose instances need to be searched
      predicateGroup.add(predicate);
  4. predicateGroup を使用してクエリーを定義します。  

    Query query = queryBuilder.createQuery(predicateGroup, session);

  5. クエリーの結果を取得します。  

    query.setStart(offset); // hardcode or fetch from request
            if(hits == -1)         // hardcode or fetch from request
                hits = 0;
            query.setHitsPerPage(hits);
            SearchResult searchResult = query.getResult();
  6. 結果を反復し、必要な形式に変換します。 結果を CSV 形式で送信するためのコードは次のとおりです。  

    Iterator<Node> iter = searchResult.getNodes();
                    while(iter.hasNext()) {
                        Node node = iter.next();
                        row = new StringBuilder();
                        for (String property : includeProperties) { // the properties of the node which needs to be returned, or one can return all the properties too.
                            try {
                                row.append(node.getProperties(property).nextProperty().getString() + COMMA_SEPARATOR);
                            } catch (NoSuchElementException e) {
                                //Adding separator for no value
                                row.append(COMMA_SEPARATOR);
                            } catch (RepositoryException e) {
                                e.printStackTrace();
                            }
                        }
                        row.deleteCharAt(row.lastIndexOf(COMMA_SEPARATOR));
                        row.append(NEW_LINE);
                        out.write(row.toString().getBytes());
                    
  7. org.apache.felix maven-bundle-plugin を使用して、サーブレットの OSGi バンドルを作成します。

  8. CRX サーバーでバンドルをデプロイします。

サービスの例

次のサンプルコードは、月末、四半期末、年末の時点での状態が RUNNING および COMPLETE であるプロセスのインスタンスをカウントするサービスです。   

package custom.reporting.service;
 
import java.text.DateFormatSymbols;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
 
import javax.jcr.Node;
import javax.jcr.Session;
 
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
 
import com.day.cq.search.Predicate;
import com.day.cq.search.PredicateGroup;
import com.day.cq.search.Query;
import com.day.cq.search.QueryBuilder;
import com.day.cq.search.eval.JcrPropertyPredicateEvaluator;
import com.day.cq.search.eval.PathPredicateEvaluator;
import com.day.cq.search.eval.TypePredicateEvaluator;
import com.day.cq.search.result.SearchResult;
 
@Component(metatype = true, immediate = true, label = "PeriodicProcessVolume", description = "Service for supporting cutom reports pluggable to Process Reporting.")
@Service(value = PeriodicProcessVolume.class)
public class PeriodicProcessVolume {
 
    private static String[] monthNameList = new DateFormatSymbols().getMonths();
    private static String[] quaterNameList = { "I", "II", "III", "IV" };
 
    private final Map<Integer, Map<Integer, Long[]>> monthly = new HashMap<Integer, Map<Integer, Long[]>>();
    private final Map<Integer, Map<Integer, Long[]>> quaterly = new HashMap<Integer, Map<Integer, Long[]>>();
    private final Map<Integer, Long[]> yearly = new HashMap<Integer, Long[]>();
 
    @Reference(referenceInterface = QueryBuilder.class)
    private QueryBuilder queryBuilder;
 
    private void addConstraints(PredicateGroup predicateGroup, String processName) {
        Predicate predicate;
 
        //Add the path Constraint
        predicate = new Predicate(PathPredicateEvaluator.PATH);
        predicate.set(PathPredicateEvaluator.PATH, "/content/reporting/pm");
        predicate.set(PathPredicateEvaluator.EXACT, "false");
        predicateGroup.add(predicate);
 
        //type nt:unstructured
        predicate = new Predicate(TypePredicateEvaluator.TYPE);
        predicate.set(TypePredicateEvaluator.TYPE, "nt:unstructured");
        predicateGroup.add(predicate);
 
        //NodeType: Process Instance
        predicate = new Predicate(JcrPropertyPredicateEvaluator.PROPERTY);
        predicate.set(JcrPropertyPredicateEvaluator.PROPERTY, "pmNodeType");
        predicate.set(JcrPropertyPredicateEvaluator.OPERATION, JcrPropertyPredicateEvaluator.OP_EQUALS);
        predicate.set(JcrPropertyPredicateEvaluator.VALUE, "ProcessInstance");
        predicateGroup.add(predicate);
 
        //processName
        if (processName != null) {
            predicate = new Predicate(JcrPropertyPredicateEvaluator.PROPERTY);
            predicate.set(JcrPropertyPredicateEvaluator.PROPERTY, "pmProcessName");
            predicate.set(JcrPropertyPredicateEvaluator.OPERATION, JcrPropertyPredicateEvaluator.OP_EQUALS);
            predicate.set(JcrPropertyPredicateEvaluator.VALUE, processName);
            predicateGroup.add(predicate);
        }
    }
 
    private Long[] setFrequency(Long[] frequency, int index) {
        if (frequency == null) {
            frequency = new Long[2];
            frequency[0] = 0L;
            frequency[1] = 0L;
        }
        frequency[index] = frequency[index] + 1L;
        return frequency;
    }
 
    public void populateValues(Session session, String processName) {
        PredicateGroup predicateGroup = new PredicateGroup();
        predicateGroup.setAllRequired(true);
        try {
            addConstraints(predicateGroup, processName);
 
            long batchSize = 10000L;
            long start = 0l;
 
            while (true) {
                Query query = queryBuilder.createQuery(predicateGroup, session);
                query.setStart(start);
                query.setHitsPerPage(batchSize);
                SearchResult searchResult = query.getResult();
                Iterator<Node> itr = searchResult.getNodes();
                long length = 0;
                while (itr.hasNext()) {
                    length++;
                    Node n = itr.next();
                    Calendar calender = n.getProperty("pmCreateTime").getDate();
                    String status = n.getProperty("pmStatus").getString();
                    int index = 0;
                    if ("COMPLETE".equals(status)) {
                        index = 1;
                    } else if ("RUNNING".equals(status)) {
                        index = 0;
                    } else {
                        continue;
                    }
                    int month = calender.get(Calendar.MONTH);
                    int year = calender.get(Calendar.YEAR);
                    int quater;
                    if (month < 3) {
                        quater = 1;
                    } else if (month < 6) {
                        quater = 2;
                    } else if (month < 9) {
                        quater = 3;
                    } else {
                        quater = 4;
                    }
 
                    Long frequency[];
                    Map<Integer, Long[]> yearMonthMap = this.monthly.get(year);
                    if (yearMonthMap == null) {
                        yearMonthMap = new HashMap<Integer, Long[]>();
                    }
                    frequency = yearMonthMap.get(month);
                    frequency = setFrequency(frequency, index);
                    yearMonthMap.put(month, frequency);
                    this.monthly.put(year, yearMonthMap);
 
                    Map<Integer, Long[]> yearQuaterMap = this.quaterly.get(year);
                    if (yearQuaterMap == null) {
                        yearQuaterMap = new HashMap<Integer, Long[]>();
                    }
                    frequency = yearQuaterMap.get(quater);
                    frequency = setFrequency(frequency, index);
                    yearQuaterMap.put(quater, frequency);
                    this.quaterly.put(year, yearQuaterMap);
 
                    frequency = this.yearly.get(year);
                    frequency = setFrequency(frequency, index);
                    this.yearly.put(year, frequency);
                }
 
                if (length < batchSize) {
                    break;
                } else {
                    start = start + batchSize;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
 
    }
 
    public Map<String, Long[]> getMonthly() {
        Map<String, Long[]> result = new LinkedHashMap<String, Long[]>();
        SortedSet<Integer> years = new TreeSet<Integer>(monthly.keySet());
        for (Integer year : years) {
            Map<Integer, Long[]> yearMonthMap = monthly.get(year);
            SortedSet<Integer> months = new TreeSet<Integer>(yearMonthMap.keySet());
            for (Integer month : months) {
                String str = monthNameList[month] + " " + year;
                result.put(str, yearMonthMap.get(month));
            }
        }
        return result;
    }
 
    public Map<String, Long[]> getQuaterly() {
        Map<String, Long[]> result = new LinkedHashMap<String, Long[]>();
        SortedSet<Integer> years = new TreeSet<Integer>(quaterly.keySet());
        for (Integer year : years) {
            Map<Integer, Long[]> quaterMonthMap = quaterly.get(year);
            SortedSet<Integer> quaters = new TreeSet<Integer>(quaterMonthMap.keySet());
            for (Integer quater : quaters) {
                String str = quaterNameList[quater - 1] + " " + year;
                result.put(str, quaterMonthMap.get(quater));
            }
        }
        return result;
    }
 
    public Map<Integer, Long[]> getYearly() {
        return yearly;
    }
 
 
 
}

このサービス上にビルドする pom.xml ファイルのサンプルを次に示します。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <!-- ====================================================================== -->
    <!-- P R O J E C T  D E S C R I P T I O N                                   -->
    <!-- ====================================================================== -->
    <groupId>com.custom</groupId>
    <artifactId>sample-report-core</artifactId>
    <packaging>bundle</packaging>
    <name>PR Sample Report</name>
    <description>Bundle providing support for a custom report pluggable to process reporting.</description>
    <version>1</version>
 
 
    <!-- ====================================================================== -->
    <!-- B U I L D   D E F I N I T I O N                                        -->
    <!-- ====================================================================== -->
    <build>
        <plugins>
          <plugin>
              <groupId>org.apache.felix</groupId>
              <artifactId>maven-bundle-plugin</artifactId>
              <version>2.3.7</version>
              <extensions>true</extensions>
              <configuration>
                    <instructions>
                        <Bundle-Category>sample-report</Bundle-Category>
                        <Export-Package>
                            custom.reporting.service.*;
                        </Export-Package>
                     </instructions>
              </configuration>
          </plugin>
          <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-scr-plugin</artifactId>
                <version>1.11.0</version>
                <executions>
                    <execution>
                        <id>generate-scr-scrdescriptor</id>
                        <goals>
                            <goal>scr</goal>
                        </goals>
                        <configuration>
                            <!-- Private service properties for all services. -->
                            <properties>
                                <service.vendor>Sample Report</service.vendor>
                            </properties>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
 
    <!-- ====================================================================== -->
    <!-- D E P E N D E N C I E S                                                -->
    <!-- ====================================================================== -->
    <dependencies>
        <dependency>
          <groupId>com.day.cq</groupId>
          <artifactId>cq-search</artifactId>
          <version>5.6.4</version>
        </dependency>
 
        <dependency>
          <groupId>javax.jcr</groupId>
          <artifactId>jcr</artifactId>
          <version>2.0</version>
        </dependency>
 
        <dependency>
          <groupId>org.apache.felix</groupId>
          <artifactId>org.apache.felix.scr.annotations</artifactId>
          <version>1.9.0</version>
        </dependency>
    </dependencies>
</project>

別の UI の作成 

結果を表示するための UI を別途作成するための前提条件は、Sling の基礎知識があること、CRX ノードを作成すること、適切なアクセス権限を付与することです。

  1. /apps ノードで CRX ノードを作成し、適切なアクセス権限を付与します。 (PERM_PROCESS_REPORTING_USER)

  2. /content ノードでレンダラーを定義します。

  3. 手順 1 で作成したノードに JSP または HTML ファイルを追加します。 CSS ファイルも追加できます。  

    JSP および CSS ファイルを使用したサンプルノード
    JSP および CSS ファイルを使用したサンプルノード

  4. QueryBuilder REST API またはサービスの Ajax 呼び出しを開始するための JavaScript コードを追加します。 さらに、適切な引数を追加します。

  5. Ajax 呼び出しに適切なサクセスハンドラーを追加し、結果を解析して表示します。 結果を複数の形式(json、csv、ユーザー定義)で解析し、表形式またはその他の形式で表示します。

  6. (オプション)適切なエラーハンドラーを Ajax 呼び出しに追加します。

OSGi サービスと QueryBuilder API の両方を使用する JSP サンプルコードは次のとおりです。  

<%@taglib prefix="sling" uri="http://sling.apache.org/taglibs/sling/1.0"%>
<%request.setAttribute("silentAuthor", new Boolean(true));%>
<%@include file="/libs/foundation/global.jsp"%>
<%@ page import="java.util.Map,
java.util.Set,
com.adobe.idp.dsc.registry.service.ServiceRegistry,
javax.jcr.Session,
org.apache.sling.api.resource.ResourceResolver,
custom.reporting.service.PeriodicProcessVolume"%>
<%
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
%><!DOCTYPE HTML>
<html>
    <head>
        <meta charset="UTF-8">
 
        <link rel="stylesheet" href="/lc/apps/sample-report-process-reporting/custom-reports/periodicProcessVolume/style.css">
        <title>REPORT Monthly / Qaterly / Yearly</title>
        <script type="text/javascript">
 
            <%
                slingResponse.setCharacterEncoding("utf-8");
                ResourceResolver resolver = slingRequest.getResourceResolver();
                String processName = slingRequest.getParameter("processName");
                Session session = resolver.adaptTo(Session.class);
                custom.reporting.service.PeriodicProcessVolume periodicProcessVolume = sling.getService(custom.reporting.service.PeriodicProcessVolume.class);
                periodicProcessVolume.populateValues(session, processName);
                if (processName == null) {
                    processName = "All";
                }
            %>
            var lineSeprator = "<td class='seprator'>----------------</td>";
            var tableEnder = "<tr>" + lineSeprator + lineSeprator + lineSeprator + "</tr>";
 
            var tableColHeader = "<td class='colHead colNum'>Running</td>";
            tableColHeader += "<td class='colHead  colNum'>Complete</td></tr>";
            tableColHeader += tableEnder;
 
            var monthly = "<table><tr><td class='colHead colStr'>Month</td>";
            monthly += tableColHeader;
 
            <%
                Map<String, Long[]> monthlyMap = periodicProcessVolume.getMonthly();
                Set<String> monthKeys = monthlyMap.keySet();
                for (String key: monthKeys) {
                    Long[] frequencies = monthlyMap.get(key);
            %>
 
            monthly += "<tr><td class='colStr'> <%= key %> </td>";
            monthly += "<td class='colNum'> <%= frequencies[0] %> </td>";
            monthly += "<td class='colNum'> <%= frequencies[1] %> </td></tr>";
            <%
                }
            %>
 
            monthly += tableEnder;
 
            var quaterly = "<table><tr><td class='colHead colStr'>Quater</td>";
            quaterly += tableColHeader;
 
            <%
                Map<String, Long[]> quaterMap = periodicProcessVolume.getQuaterly();
                Set<String> quaterKeys = quaterMap.keySet();
                for (String key: quaterKeys) {
                    Long[] frequencies = quaterMap.get(key);
            %>
 
            quaterly += "<tr><td class='colStr'> <%= key %> </td>";
            quaterly += "<td class='colNum'> <%= frequencies[0] %> </td>";
            quaterly += "<td class='colNum'> <%= frequencies[1] %> </td></tr>";
            <%
                }
            %>
 
            quaterly += tableEnder;
 
            var yearly = "<table><tr><td class='colHead colStr'>Year</td>";
            yearly += tableColHeader;
 
            <%
                Map<Integer, Long[]> yearMap = periodicProcessVolume.getYearly();
                Set<Integer> yearKeys = yearMap.keySet();
                for (Integer key: yearKeys) {
                    Long[] frequencies = yearMap.get(key);
            %>
 
            yearly += "<tr><td class='colStr'> <%= key %> </td>";
            yearly += "<td class='colNum'> <%= frequencies[0] %> </td>";
            yearly += "<td class='colNum'> <%= frequencies[1] %> </td></tr>";
            <%
                }
            %>
 
            yearly += tableEnder;
 
            function reloadFrame(value) {
                if (value === '-1') {
                    window.location = "/lc/content/process-reporting-runtime/custom-reports/periodicProcessVolume.html";
                } else {
                    window.location = "/lc/content/process-reporting-runtime/custom-reports/periodicProcessVolume.html?processName=" + value;
                }
            }
 
            function populateTable(selection) {
                if (selection === 0) {
                    document.getElementById('tableHeading').innerHTML = 'Monthly';
                    document.getElementById('volumeTable').innerHTML = monthly;
                } else if (selection === 1) {
                    document.getElementById('tableHeading').innerHTML = 'Quaterly';
                    document.getElementById('volumeTable').innerHTML = quaterly;
                } else {
                    document.getElementById('tableHeading').innerHTML = 'Yearly';
                    document.getElementById('volumeTable').innerHTML = yearly;
                }
            }
 
            function fetchProcesses() {
                var xmlhttp = new XMLHttpRequest(),
                    request = '';
                xmlhttp.onreadystatechange = function() {
                   if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
                       var responseText,
                           response,
                           items,
                           hits = [],
                           responseSize = 0,
                           processName,
                           selectedIndex = 0,
                           comboBox;
                       responseText = xmlhttp.responseText;
                       if (responseText !== undefined && responseText !== null) {
                           response = JSON.parse(responseText);
                           responseSize = response.results;
                           hits = response.hits;
                       }
 
                       items = "<option value='-1'>All</option>";
 
                       for(var i = 0; i < responseSize; i++) {
                           processName = hits[i].pmProcessTitle;
                           if (processName === '<%= processName %>') {
                               selectedIndex = i + 1;
                           }
                           items += "<option value='" + processName + "'>" + processName + "</option>"
                       }
 
                       comboBox = document.getElementById('processSelection');
                       comboBox.innerHTML = items;
                       comboBox.selectedIndex = selectedIndex;
                   }
               };
               request = "/lc/bin/querybuilder.json?";
               request += "exact=false&";
               request += "p.hits=selective&";
               request += "p.properties=pmProcessTitle&";
               request += "path=%2fcontent%2freporting%2fpm&";
               request += "property=pmNodeType&";
               request += "property.operation=equals&";
               request += "property.value=ProcessType&";
               request += "type=sling%3aFolder";
 
 
               xmlhttp.open("POST", request, true);
               xmlhttp.setRequestHeader("Content-type","application/json");
               xmlhttp.send();
            }
 
        </script>
    </head>
    <body onLoad="fetchProcesses();populateTable(0);">
        Process:
        <select id="processSelection" onchange="reloadFrame(this.value);"></select>
        &nbsp &nbsp Period Interval:
        <select name="periodSelection" onchange="populateTable(this.selectedIndex);">
            <option value="1">Monthly</option>
            <option value="2">Quaterly</option>
            <option value="3">Yearly</option>
        </select>
        <br> <br> <br> <br>
        <div class="inline"> Process: &nbsp <b><%= processName %></b> &nbsp &nbsp Period: &nbsp </div> <b> <div id="tableHeading" class="inline"> </div> </b>
        <br><br>
        <div id="volumeTable"> </div>
 
    </body>
</html>

既存のプロセスレポート UI へのレポート UI の統合 

結果を表示するための UI を別途作成するための前提条件は、Sling の基礎知識があること、CRX ノードを作成すること、適切なアクセス権限を付与することです。

  1. 別の UI を作成」セクションの説明に従って別の UI を作成します。

  2. プラグ可能なレポートについてはその都度 /content/process-reporting-runtime/custom-reports ノードで子ノード nt:unstructured を作成します。

    • id - レポートの固有の ID 番号を指定します。
    • name - レポートの名前を指定します。 この名前が UI に表示されます。
    • link - 別の UI のレンダラーへの相対リンクを指定します。 このリンクは手順 1 で作成したものです。
    • description - レポートの説明を 1 行で指定します。 description フィールドは空欄のままでも構いません。
    • icon - レポートを表す画像を指定します。 icon フィールドは空欄のままでも構いません。  
    ノードのプロパティ
    ノードのプロパティ

  3. レポート UI がプロセスレポート UI に統合されます。 UI を統合すると、更新後に次のようになります。

    新たに追加したカスタムレポートのユーザーインターフェイス
    新たに追加したカスタムレポートのユーザーインターフェイス

    カスタムレポートの検索結果画面
    カスタムレポートの検索結果画面

サンプルパッケージ

sample-report-pkg-1.zip パッケージを読み込み、記事で説明しているカスタムレポートおよび UI を Process Management UI と統合します。  

ダウンロード

本作品は Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License によってライセンス許可を受けています。  Twitter™ および Facebook の投稿には、Creative Commons の規約内容は適用されません。

法律上の注意   |   プライバシーポリシー