About

As system evolves and gets a lot of changes, this section contains referencing code examples. Snippets below gets extracted from an actual project code, so these are always reliable. Use IDE, if needed, in order to search for specific classes or files.

Documentation

Recommended approaches and samples may be always found here.

DrawIO diagrams:

Other samples:

Code

Saving latest/last user request parameters

Let’s take interface filters, as example - use restoreRequestParams method in org.bgerp.action.base.BaseAction

Saving a value:

// PzdcDoc snippet of: 'org.bgerp.action.MessageAction', lines: 498 - 498

        restoreRequestParams(conSet.getConnection(), form, true, false, "messageTypeAdd");

Restoring a value:

// PzdcDoc snippet of: 'org.bgerp.action.MessageAction', lines: 530 - 537

public ActionForward processMessageUpdate(DynActionForm form, Connection con) throws Exception {
    var type = getType(form.getParamInt("typeId"));

    // preserving message type for choosing in next usage of editor
    if (form.getId() <= 0) {
        form.setParam("messageTypeAdd", String.valueOf(type.getId()));
        restoreRequestParams(con, form, false, true, "messageTypeAdd");
    }

Saving and restoring a value at once:

// PzdcDoc snippet of: 'ru.bgcrm.struts.action.ProcessLinkAction', lines: 48 - 52

// процессы, к которым привязана сущность
public ActionForward linkedProcessList(DynActionForm form, Connection con) throws Exception {
    ProcessLinkDAO processLinkDAO = new ProcessLinkDAO(con, form);

    restoreRequestParams(con, form, true, true, "open");

Feature flag for UI

Option stored in user personalization map. UI dropbox available in user profile settings.

<%-- PzdcDoc snippet of: 'webapps/WEB-INF/jspf/user/profile/default.jsp', lines: 39 - 45 --%>

<c:set var="key" value="iface.buffer.behavior"/>
<ui:combo-single hiddenName="${key}" value="${ctxUser.personalizationMap.get(key, '1')}" widthTextValue="200px">
        <jsp:attribute name="valuesHtml">
                <li value="1">${l.l('Последний объект сверху')}</li>
                <li value="2">${l.l('Сохранять порядок объектов')}</li>
        </jsp:attribute>
</ui:combo-single>

Stored values may be read in JSP pages using in the same ctxUser.personalizationMap way, but also available in JS like for this case.

// PzdcDoc snippet of: 'webapps/js/kernel.shell.js', lines: 458 - 459

if (($$.pers["iface.buffer.openOnLongPress"] || 0) === 1) {
        const debug = $$.debug("buffer");

Depicting element count on a tab

For example, showing associated processes count. Gets saved on the first call. Use ru.bgcrm.model.IfaceState class.

tab related process counts

Show on a tab’s header:

<%-- PzdcDoc snippet of: 'webapps/WEB-INF/jspf/user/process/process/process_editor.jsp', lines: 77 - 88 --%>

        <c:if test="${processType.properties.configMap.getSok('1', false, 'show.tab.links.process', 'processShowProcessLinks') eq '1'}">
                <c:set var="ifaceId" value="link_process"/>
                <c:set var="ifaceState" value="${ifaceStateMap[ifaceId]}"/>

                <c:url var="url" value="/user/process/link/process.do">
                        <c:param name="id" value="${process.id}"/>
                        <c:param name="ifaceId" value="${ifaceId}"/>
                        <c:param name="ifaceState" value="${ifaceState.state}"/>
                </c:url>

                $tabs.tabs("add", "${url}", "${l.l('Related Processes')}<span class='iface-state'>${ifaceState.formattedState}</span>");
        </c:if>

Update value in DB:

// PzdcDoc snippet of: 'org.bgerp.action.ProcessLinkProcessAction', lines: 53 - 61

IfaceState currentState = new IfaceState(form);
if (Utils.notBlankString(currentState.getIfaceId())) {
    Pair<Integer, Integer> counts = new ProcessLinkDAO(conSet.getSlaveConnection()).getLinkedProcessesCounts(form.getId());

    IfaceState newState = new IfaceState(Process.OBJECT_TYPE, form.getId(), form,
            String.valueOf(counts.getFirst()),
            String.valueOf(counts.getSecond()));
    new IfaceStateDAO(conSet.getConnection()).compareAndUpdateState(currentState, newState, form);
}

Refresh on a tab’s header:

<%-- PzdcDoc snippet of: 'webapps/WEB-INF/jspf/user/process/process/link/process/default.jsp', lines: 6 - 10 --%>

<script>
        $(function () {
                $$.ui.ifaceStateTabUpdate('${uiid}', '${ifaceState.formattedState}');
        })
</script>

Date and time format

When formatting date and time values in Java or JSP , use the following formats in order to keep unified approach and maintain independence from the current locale:

  • ymd - year, month, date;

  • ymdh - year, month, day, hour;

  • ymdhm - year, month, day, hour, minute;

  • ymdhms - year, month, day, hour, minute, second.

Java code uses ru.bgcrm.util.TimeUtils class for date formatting, this class also contains format constants.

The same functions may be used in JSP using tu prefix:

<%-- PzdcDoc snippet of: 'webapps/WEB-INF/jspf/user/parameter/edit.jsp', lines: 168 - 168 --%>

<input type="text" name="value" value="${tu.format(data.value, type)}" id="${focusFieldUiid}" ${changeAttrs} onclick="${getCommand}"/>

Configuration Java Beans

In order to speed up parsing and validation, use Java class objects inherited from org.bgerp.app.cfg.Config For example: org.bgerp.model.process.config.IsolationConfig. This configuration supports constructor with a validation flag, which allows to check syntax when saving.

Configuration bin can be also obtained in JSP:

<%-- PzdcDoc snippet of: 'webapps/WEB-INF/jspf/user/plugin/task/task_list.jsp', lines: 4 - 4 --%>

<c:set var="config" value="${ctxSetup.getConfig('ru.bgcrm.plugin.task.Config')}"/>

Pagination

process status table

Putting results into JSP and rendering a form:

<%-- PzdcDoc snippet of: 'webapps/WEB-INF/jspf/admin/process/status/list.jsp', lines: 4 - 18 --%>

<html:form action="${form.requestURI}" styleClass="in-mr1">
        <input type="hidden" name="method" value="statusList"/>
        <input type="hidden" name="id" value="-1"/>
        <input type="hidden" name="returnUrl" value="${form.requestUrl}"/>

        <c:url var="url" value="${form.requestURI}">
                <c:param name="method" value="statusGet"/>
                <c:param name="id" value="-1"/>
                <c:param name="returnUrl" value="${form.requestUrl}"/>
        </c:url>

        <ui:button type="add" onclick="$$.ajax.loadContent('${url}', this)"/>

        <ui:page-control/>
</html:form>

Java action:

// PzdcDoc snippet of: 'ru.bgcrm.struts.action.admin.ProcessAction', lines: 52 - 56

    public ActionForward statusList(DynActionForm form, Connection con) throws Exception {
        new StatusDAO(con).searchStatus(new Pageable<>(form));

        return html(con, form, PATH_JSP + "/status/list.jsp");
    }

File upload

JSP page:

<%-- PzdcDoc snippet of: 'webapps/WEB-INF/jspf/admin/license/license.jsp', lines: 9 - 16 --%>

        <form action="${form.requestURI}" method="POST" enctype="multipart/form-data">
                <input type="hidden" name="method" value="upload"/>
                <input type="hidden" name="responseType" value="json"/>
                <input type="file" name="file" style="visibility: hidden; display: none;"/>
                <button type="button" class="btn-grey w100p mt1" onclick="$$.ajax.fileUpload(this.form).done(() => {
                        $$.ajax.loadContent('${form.requestUrl}', this);
                })">${l.l('Загрузить файл лицензии')}</button>
        </form>

Put an attention to the ${form.requestURI} expression, which gets action URL of the current action and prevents duplication of code.

Action:

// PzdcDoc snippet of: 'org.bgerp.action.admin.LicenseAction', lines: 29 - 42

public ActionForward upload(DynActionForm form, ConnectionSet conSet) throws Exception {
    var file = form.getFile();

    byte[] data = IOUtils.toByteArray(file.getInputStream());

    String error = new License(new String(data, StandardCharsets.UTF_8)).getError();
    if (Utils.notBlankString(error))
        throw new BGMessageException(error);

    IOUtils.write(data, new FileOutputStream(License.FILE_NAME));
    AppLicense.init();

    return json(conSet, form);
}

JSP + HTML / CSS + JS

A simple dictionary with pagination, AJAX editor invocation: webapps/WEB-INF/jspf/admin/process/status/list.jsp

Sending AJAX for sending, exiting or restoring data in process type properties editor:

<%-- PzdcDoc snippet of: 'webapps/WEB-INF/jspf/admin/process/type/properties.jsp', lines: 153 - 160 --%>

        <div class="mt1">
                <ui:form-ok-cancel/>
                <span style="float: right;">
                        <button type="button" class="btn-grey mr1" onclick="${saveCommand}"
                                title="${l.l('Save without leaving editor')}">${l.l('Save')}</button>
                        <button type="button" class="btn-grey" onclick="$$.ajax.load('${editUrl}', $('#${formUiid}').parent())">${l.l('Restore')}</button>
                </span>
        </div>

Recursive include:

<%-- PzdcDoc snippet of: 'webapps/WEB-INF/jspf/admin/user/perm_check_tree_item.jsp', lines: 39 - 46 --%>

                <c:if test="${not empty node.children}">
                        <ul>
                                <c:forEach var="child" items="${node.children}">
<c:set var="node" value="${child}" scope="request"/>
<jsp:include page="perm_check_tree_item.jsp" />
                                </c:forEach>
                        </ul>
                </c:if>

Action execution result include:

<%-- PzdcDoc snippet of: 'webapps/WEB-INF/jspf/admin/user/user/update.jsp', lines: 161 - 167 --%>

        <c:url var="url" value="/user/parameter.do">
                <c:param name="method" value="parameterList" />
                <c:param name="id" value="${form.id}" />
                <c:param name="objectType" value="user" />
                <c:param name="header" value="Доп. параметры"/>
        </c:url>
        <c:import url="${url}" />

Flex layout, using constants from Java classes (defined in process_search_constants.jsp), print button close to a field:

<%-- PzdcDoc snippet of: 'webapps/WEB-INF/jspf/user/search/search.jsp', lines: 65 - 79 --%>

<div style="display: flex;">
        <u:sc>
                <%@ include file="process_search_constants.jsp"%>
                <ui:combo-single hiddenName="mode" styleClass="w100p">
                        <jsp:attribute name="valuesHtml">
                                <li value="${MODE_USER_CREATED}">${l.l('Cозданные мной')}</li>
                                <li value="${MODE_USER_CLOSED}">${l.l('Закрытые мной')}</li>
                                <li value="${MODE_USER_STATUS_CHANGED}">${l.l('Статус изменён мной')}</li>
                        </jsp:attribute>
                </ui:combo-single>
        </u:sc>
        <div class="pl05">
                <ui:button type="out" onclick="this.form.searchBy.value='userId'; $$.ajax.load(this.form, '#searchResult');"/>
        </div>
</div>
For some reason a class, containing included constant must not contain config word in package path.

Snap-in refresh upon moving back to it, onShow:

<%-- PzdcDoc snippet of: 'webapps/WEB-INF/jspf/user/log/log.jsp', lines: 17 - 24 --%>

<script>
        $(function () {
                const $content = $('#${uiid}').parent();
                $content.data('onShow', function () {
                        $$.ajax.load("${form.requestUrl}", $content);
                });
        });
</script>

Sending AJAX request and showing progress indicator on button during execution:

<%-- PzdcDoc snippet of: 'webapps/WEB-INF/jspf/admin/run/run.jsp', lines: 20 - 24 --%>

                <button class="btn-grey ml1 icon" type="button" onclick="
                        $$.ajax.post(this)
                                .done(() => {
                                        alert(this.form.sync.value ? '${l.l('Класс выполнен, проверьте логи')}' : '${l.l('Класс запущен в отдельном потоке,\\nвывод в логах.')}')
                                })"><i class="ti-control-play"></i></button>

The same but for $$.ajax.load function:

<%-- PzdcDoc snippet of: 'webapps/WEB-INF/jspf/user/process/queue/filter.jsp', lines: 616 - 616 --%>

<ui:button type="out" styleClass="out" onclick="const $form = $('${selectorForm}'); processQueueMarkFilledFilters($form); $$.ajax.load($form, $('#processQueueData'), {control: this});"/>

Restore form parameter values:

<%-- PzdcDoc snippet of: 'webapps/WEB-INF/jspf/admin/directory/parameter/group/update.jsp', lines: 8 - 10 --%>

<html:form action="/admin/directory" styleClass="center500">
        <input type="hidden" name="method" value="parameterGroupUpdate"/>
        <html:hidden property="directoryId"/>

Toggle button:

examples toggle button
<%-- PzdcDoc snippet of: 'webapps/WEB-INF/jspf/user/process/message_possible_process_list.jsp', lines: 15 - 17 --%>

<button type="button"
                onclick="this.form.open.value = this.form.open.value ? '' : 'true'; ${sendCommand}"
                class="mr1 btn-small ${form.param.open eq 'true' ? 'btn-blue' : 'btn-white'}">${l.l('Open only')}</button>