com.aurel.track.item.ItemDetailBL.java Source code

Java tutorial

Introduction

Here is the source code for com.aurel.track.item.ItemDetailBL.java

Source

/**
 * Genji Scrum Tool and Issue Tracker
 * Copyright (C) 2015 Steinbeis GmbH & Co. KG Task Management Solutions
    
 * <a href="http://www.trackplus.com">Genji Scrum Tool</a>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/* $Id:$ */

package com.aurel.track.item;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;

import com.aurel.track.beans.*;
import com.aurel.track.fieldType.constants.ValueType;
import com.aurel.track.fieldType.runtime.base.IFieldTypeRT;
import com.aurel.track.fieldType.types.FieldTypeManager;
import com.aurel.track.item.history.HistoryBean;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.apache.struts2.ServletActionContext;

import com.aurel.track.Constants;
import com.aurel.track.accessControl.AccessBeans;
import com.aurel.track.admin.customize.role.FieldsRestrictionsToRoleBL;
import com.aurel.track.admin.project.ProjectAccountingTO;
import com.aurel.track.admin.project.ProjectBL;
import com.aurel.track.admin.user.person.PersonPickerJSON;
import com.aurel.track.attachment.AttachBL;
import com.aurel.track.dao.DAOFactory;
import com.aurel.track.errors.ErrorData;
import com.aurel.track.fieldType.constants.BooleanFields;
import com.aurel.track.fieldType.constants.SystemFields;
import com.aurel.track.fieldType.runtime.base.LookupContainer;
import com.aurel.track.fieldType.runtime.base.WorkItemContext;
import com.aurel.track.item.consInf.ConsInfBL;
import com.aurel.track.item.consInf.ConsInfEdit;
import com.aurel.track.item.consInf.ConsInfShow;
import com.aurel.track.item.consInf.RaciRole;
import com.aurel.track.item.history.FieldChangeBL;
import com.aurel.track.item.history.HistoryValues;
import com.aurel.track.item.link.ItemLinkBL;
import com.aurel.track.json.JSONUtility;
import com.aurel.track.json.JSONUtility.JSON_FIELDS;
import com.aurel.track.linkType.LinkTypeBL;
import com.aurel.track.prop.ApplicationBean;
import com.aurel.track.resources.LocalizeUtil;
import com.aurel.track.screen.SystemActions;
import com.aurel.track.util.HTMLDiff;
import com.aurel.track.util.HTMLSanitiser;
import com.aurel.track.util.PropertiesHelper;
import com.aurel.track.util.TagReplacer;
import com.aurel.track.util.emailHandling.Html2Text;
import com.aurel.track.vc.Revision;
import com.aurel.track.vc.VersionControlMap;

public class ItemDetailBL {
    public static final int TAB_HISTORY = 1;
    public static final int TAB_COMMENTS = 2;
    public static final int TAB_ATTACHMENTS = 3;
    public static final int TAB_WORKLOG = 4;
    public static final int TAB_WATCHERS = 5;
    public static final int TAB_VERSION_CONTROL = 6;
    public static final int TAB_LINKS = 7;
    public static final int TAB_MIGRATIONS = 8;

    private static final Logger LOGGER = LogManager.getLogger(ItemDetailBL.class);

    private ItemDetailBL() {
    }

    public static void appendGridLayoutData(StringBuilder sb, Integer tabID, Integer personID, Locale locale,
            boolean last) {
        sb.append("gridLayoutData:{");
        List<ItemGridLayout> layout = ItemGridLayoutBL.loadLayout(tabID, personID, null, locale);
        //String[] sortInfo=ItemGridLayoutBL.getSortInfo(tabID,layout);
        sb.append("layout:").append(ItemDetailBL.encodeJSON_Layout(layout, locale));
        sb.append("}");
        if (!last) {
            sb.append(",");
        }
    }

    public static void appendGridLayoutData(StringBuilder sb, Integer tabID, Integer personID, Locale locale) {
        appendGridLayoutData(sb, tabID, personID, locale, false);
    }

    public static String encodeJSON_ItemDetailTabList(List<ItemDetailTab> list) {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        if (list != null) {
            ItemDetailTab itemDetailTab;
            for (int i = 0; i < list.size(); i++) {
                itemDetailTab = list.get(i);
                if (i > 0) {
                    sb.append(",");
                }
                sb.append("{");
                JSONUtility.appendStringValue(sb, "extClassName", itemDetailTab.getExtClassName());
                JSONUtility.appendJSONValue(sb, "jsonData", itemDetailTab.getJsonData());
                JSONUtility.appendIntegerValue(sb, "id", itemDetailTab.getId(), true);
                sb.append("}");
            }
        }
        sb.append("]");
        return sb.toString();
    }

    public static String encodeJSON_Layout(List<ItemGridLayout> list, Locale locale) {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        if (list != null) {
            ItemGridLayout layout;
            for (int i = 0; i < list.size(); i++) {
                layout = list.get(i);
                if (i > 0) {
                    sb.append(",");
                }
                sb.append("{");
                JSONUtility.appendStringValue(sb, "header",
                        LocalizeUtil.getLocalizedTextFromApplicationResources(layout.getHeader(), locale));
                JSONUtility.appendIntegerValue(sb, "width", layout.getWidth());
                JSONUtility.appendStringValue(sb, "dataIndex", layout.getDataIndex());
                JSONUtility.appendIntegerValue(sb, "id", layout.getId());
                JSONUtility.appendIntegerValue(sb, "type", layout.getType());
                JSONUtility.appendBooleanValue(sb, "sortable", layout.isSortable());
                JSONUtility.appendBooleanValue(sb, "hidden", layout.isHidden(), true);
                sb.append("}");
            }
        }
        sb.append("]");
        return sb.toString();
    }

    public static String encodeJSON_HistoryList(List<HistoryValues> list, boolean historyFull, Locale locale) {
        return encodeJSON_HistoryList(list, historyFull, locale, false);
    }

    public static String encodeJSON_HistoryList(List<HistoryValues> list, boolean historyFull, Locale locale,
            boolean formatted) {

        StringBuilder sb = new StringBuilder();
        sb.append("[");
        if (list != null) {
            HistoryValues history;
            for (int i = 0; i < list.size(); i++) {
                history = list.get(i);
                if (i > 0) {
                    sb.append(",");
                }
                sb.append("{");

                String newShowValueFull = history.getNewShowValue();
                String newValueStr;
                if (historyFull) {
                    newShowValueFull = formatDescription(newShowValueFull, locale);
                    newValueStr = newShowValueFull;
                } else {
                    newValueStr = extractShortDescription(newShowValueFull);
                    newShowValueFull = formatDescription(newShowValueFull, locale);
                    newValueStr = formatDescription(newValueStr, locale);
                }
                JSONUtility.appendStringValue(sb, "newValues", newValueStr);
                JSONUtility.appendStringValue(sb, "newValuesFull", newShowValueFull);

                String oldShowValueFull = history.getOldShowValue();
                String oldValueStr;
                if (historyFull) {
                    oldShowValueFull = formatDescription(oldShowValueFull, locale);
                    oldValueStr = oldShowValueFull;
                } else {
                    oldValueStr = extractShortDescription(oldShowValueFull);
                    oldShowValueFull = formatDescription(oldShowValueFull, locale);
                    oldValueStr = formatDescription(oldValueStr, locale);
                }

                String commentFull = history.getTransactionComment();
                String commentStr;
                if (historyFull) {
                    commentFull = formatDescription(commentFull, locale);
                    commentStr = commentFull;
                } else {
                    commentStr = extractShortDescription(commentFull);
                    commentFull = formatDescription(commentFull, locale);
                    commentStr = formatDescription(commentStr, locale);
                }

                boolean isText = isTextHistory(history.getFieldID());
                String diffFull = null;
                if (isText) {
                    diffFull = makeHtmlDif(newShowValueFull, oldShowValueFull, locale);
                } else {
                    diffFull = makeSimpleHtmlDif(newShowValueFull, oldShowValueFull);
                }
                String diffStr = null;
                //diffFull=formatDescription(diffFull,locale);
                if (historyFull) {
                    diffStr = diffFull;
                } else {
                    if (isText) {
                        diffStr = makeHtmlDif(newValueStr, oldValueStr, locale);
                    } else {
                        diffStr = makeSimpleHtmlDif(newValueStr, oldValueStr);
                    }
                }

                JSONUtility.appendDateTimeValue(sb, "date", history.getLastEdit());
                JSONUtility.appendStringValue(sb, "author", history.getChangedByName());
                JSONUtility.appendStringValue(sb, "authorID", history.getChangedByID() + "");
                JSONUtility.appendStringValue(sb, "typeOfChange", history.getFieldName() + "");

                JSONUtility.appendStringValue(sb, "oldValues", oldValueStr);
                JSONUtility.appendStringValue(sb, "oldValuesFull", oldShowValueFull);

                JSONUtility.appendStringValue(sb, "comment", commentStr);
                JSONUtility.appendStringValue(sb, "commentFull", commentFull);

                JSONUtility.appendStringValue(sb, "diff", diffStr);
                JSONUtility.appendStringValue(sb, "diffFull", diffFull);
                JSONUtility.appendIntegerValue(sb, "id", i, true);
                sb.append("}");
            }
        }
        sb.append("]");
        return sb.toString();
    }

    private static boolean isTextHistory(Integer fieldID) {
        boolean result = false;
        if (fieldID == null) {
            return false;
        }
        if (fieldID.intValue() == TFieldChangeBean.COMPOUND_HISTORY_FIELD) {
            result = true;
        } else {
            if (fieldID > 0) {
                IFieldTypeRT fieldTypeRT = FieldTypeManager.getFieldTypeRT(fieldID);
                if (fieldTypeRT != null && (fieldTypeRT.getValueType() == ValueType.TEXT
                        || fieldTypeRT.getValueType() == ValueType.TEXT)) {
                    result = true;
                }
            }
        }
        return result;
    }

    private static String makeSimpleHtmlDif(String newValue, String oldValue) {
        StringBuilder sb = new StringBuilder();
        if (oldValue != null && oldValue.length() > 0) {
            sb.append("<span class=\"diff-html-removed\">").append(oldValue).append("</span>");
        }
        if (newValue != null && newValue.length() > 0) {
            sb.append(" ").append("<span class=\"diff-html-added\">").append(newValue).append("</span>");
        }
        return sb.toString();
    }

    private static String makeHtmlDif(String newValue, String oldValue, Locale locale) {
        String diffStr = null;
        try {
            diffStr = HTMLDiff.makeDiff(newValue, oldValue, locale);
        } catch (Exception ex) {
            LOGGER.info("Making html diff failed with " + ex.getMessage());
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("New value " + newValue);
                LOGGER.debug("Old value " + oldValue);
                LOGGER.error(ExceptionUtils.getStackTrace(ex));
            }
        }
        return diffStr;
    }

    public static String encodeJSON_Comments(List<HistoryValues> list, Locale locale) {
        return encodeJSON_Comments(list, locale, null, false, false);
    }

    public static String encodeJSON_Comments(List<HistoryValues> list, Locale locale, Integer personID,
            boolean isProjectActive, boolean isProjectAdmin) {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        if (list != null) {
            HistoryValues comment;
            for (int i = 0; i < list.size(); i++) {
                comment = list.get(i);
                if (i > 0) {
                    sb.append(",");
                }
                sb.append("{");
                JSONUtility.appendDateTimeValue(sb, "date", comment.getLastEdit());
                JSONUtility.appendStringValue(sb, "author", comment.getChangedByName());

                JSONUtility.appendStringValue(sb, "authorID", comment.getChangedByID() + "");
                JSONUtility.appendStringValue(sb, "comment", formatDescription(comment.getNewShowValue(), locale));
                if (personID != null) {
                    sb.append("\"editable\":")
                            .append(editableComment(comment, personID, isProjectActive, isProjectAdmin))
                            .append(",");
                }

                sb.append("\"id\":").append(comment.getObjectID());

                sb.append("}");
            }
        }
        sb.append("]");
        return sb.toString();

    }

    public static String encodeJSON_Attachments(String sessionID, List<TAttachmentBean> list, Locale locale,
            Integer personID, boolean isProjectActive, boolean editable) {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        if (list != null) {
            TAttachmentBean attachBean;
            for (int i = 0; i < list.size(); i++) {
                attachBean = list.get(i);
                if (i > 0) {
                    sb.append(",");
                }
                sb.append("{");
                sb.append("id:").append(attachBean.getObjectID()).append(",");
                JSONUtility.appendStringValue(sb, "fileName", attachBean.getFileName());
                JSONUtility.appendBooleanValue(sb, "isURL",
                        BooleanFields.fromStringToBoolean(attachBean.getIsUrl()));
                JSONUtility.appendStringValue(sb, "fileSize", Long.toString(attachBean.getSize()));
                JSONUtility.appendDateTimeValue(sb, "date", attachBean.getLastEdit());
                JSONUtility.appendStringValue(sb, "author", attachBean.getChangedByName());
                JSONUtility.appendStringValue(sb, "description", attachBean.getDescription());

                sb.append("editable:").append(editableAttachemnt(attachBean, personID, isProjectActive, editable))
                        .append(",");
                boolean thumbnail = AttachBL.isImage(attachBean);
                sb.append("thumbnail:").append(thumbnail);
                sb.append("}");
            }
        }
        sb.append("]");
        return sb.toString();
    }

    public static String encodeJSON_AddRole(ConsInfEdit consInfEdit) {
        StringBuilder sb = new StringBuilder();
        sb.append("{success:true,");
        sb.append("persons:");
        if (consInfEdit.getPotentialPersons() != null) {
            List<TPersonBean> potentialPersons = consInfEdit.getPotentialPersons();
            sb.append(PersonPickerJSON.encodePersonList(potentialPersons, false));
        }
        sb.append(",");
        sb.append("groups:");
        if (consInfEdit.getPotentialGroups() != null) {
            List<TPersonBean> potentialGroups = consInfEdit.getPotentialGroups();
            sb.append(PersonPickerJSON.encodePersonList(potentialGroups, false));
        }
        sb.append("}");
        return sb.toString();
    }

    public static String encodeJSON_Watchers(ConsInfShow consInfShow, Integer personID, String consPrompt,
            String infPrompt) {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        List realConsultantGroups = consInfShow.getRealConsultantGroups();
        List realConsultantPersons = consInfShow.getRealConsultantPersons();
        List realInformantGroups = consInfShow.getRealInformantGroups();
        List realInformantPersons = consInfShow.getRealInformantPersons();

        int realConsultantGroupsSize = realConsultantGroups.size();
        int realConsultantPersonsSize = realConsultantPersons.size();
        int realInformantGroupsSize = realInformantGroups.size();
        int realInformantPersonsSize = realInformantPersons.size();

        boolean hasNext = false;
        if (realConsultantGroupsSize > 0) {
            sb.append(encodeJSON_ConsInfItem(realConsultantGroups, true, consPrompt, "c", personID));
            hasNext = (realConsultantPersonsSize + realInformantGroupsSize + realInformantPersonsSize > 0);
        }
        if (hasNext) {
            sb.append(",");
        }

        hasNext = false;
        if (realConsultantPersonsSize > 0) {
            sb.append(encodeJSON_ConsInfItem(realConsultantPersons, false, consPrompt, "c", personID));
            hasNext = (realInformantGroupsSize + realInformantPersonsSize > 0);
        }
        if (hasNext) {
            sb.append(",");
        }

        hasNext = false;
        if (realInformantGroupsSize > 0) {
            sb.append(encodeJSON_ConsInfItem(realInformantGroups, true, infPrompt, "i", personID));
            hasNext = (realInformantPersonsSize > 0);
        }
        if (hasNext) {
            sb.append(",");
        }

        if (realInformantPersonsSize > 0) {
            sb.append(encodeJSON_ConsInfItem(realInformantPersons, false, infPrompt, "i", personID));
        }
        sb.append("]");
        return sb.toString();
    }

    private static String encodeJSON_ConsInfItem(List consInfList, boolean isGroup, String type, String raciRole,
            Integer personID) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < consInfList.size(); i++) {
            TPersonBean person = (TPersonBean) consInfList.get(i);

            if (i > 0) {
                sb.append(",");
            }
            sb.append("{");
            JSONUtility.appendIntegerValue(sb, "objectID", person.getObjectID());
            JSONUtility.appendStringValue(sb, "type", type);
            JSONUtility.appendStringValue(sb, "raciRole", raciRole);
            JSONUtility.appendStringValue(sb, "person", person.getFullName());
            JSONUtility.appendBooleanValue(sb, "isGroup", isGroup);
            JSONUtility.appendBooleanValue(sb, "editable", editableWatcher(person, personID), true);
            sb.append("}");
        }
        return sb.toString();
    }

    /**
     * Get the integer array from the semicolon sparated string  
     * @param semicolonSeparatedValues
     * @return
     */
    public static Integer[] getIntegerTokens(String semicolonSeparatedValues) {
        Integer[] selectedItems = new Integer[0];
        if (semicolonSeparatedValues != null && semicolonSeparatedValues.length() > 0) {
            StringTokenizer st = new StringTokenizer(semicolonSeparatedValues, ";");
            List<Integer> intList = new ArrayList<Integer>();
            while (st.hasMoreTokens()) {
                String token = st.nextToken();
                try {
                    Integer personID = new Integer(token);
                    intList.add(personID);
                } catch (Exception e) {
                    LOGGER.warn("Converting the semicolon separated value  " + token + " to integer failed with "
                            + e.getMessage());
                }
            }
            selectedItems = new Integer[intList.size()];
            selectedItems = intList.toArray(selectedItems);
        }
        return selectedItems;
    }

    private static String extractShortDescription(String descriptionOriginal) {
        String description;
        try {
            description = Html2Text.getNewInstance().convert(descriptionOriginal);
        } catch (Exception e) {
            description = descriptionOriginal;
        }
        int maxSizeDescription = 120;
        if (description == null || description.length() <= maxSizeDescription) {
            return description;
        }
        return description.substring(0, maxSizeDescription) + "...";
    }

    private static boolean editableComment(HistoryValues comment, Integer personID, boolean isProjectActive,
            boolean isProjectAdmin) {
        return isProjectActive && (isProjectAdmin || comment.getChangedByID().intValue() == personID.intValue());
    }

    private static boolean editableAttachemnt(TAttachmentBean attachBean, Integer personID, boolean isProjectActive,
            boolean editable) {
        return (isProjectActive && editable)
                || (attachBean.getChangedBy() != null && attachBean.getChangedBy().equals(personID));
    }

    private static boolean editableExpense(TCostBean costBean, Integer personID, boolean isProjectActive,
            boolean personIsProjectAdmin) {
        return isProjectActive && (personIsProjectAdmin || personID.equals(costBean.getChangedByID()));
    }

    private static boolean editableWatcher(TPersonBean person, Integer personID) {
        return true;
    }

    public static String replacePattern(String pattern, String path, String revision) {
        String s = pattern;//.replaceAll("\\$\\{repo-name\\}", repoName);
        if (path != null) {
            String encodedPath = path.replaceAll("/", "%2F");
            s = s.replaceAll("\\$\\{path\\}", path);
            s = s.replaceAll("\\$\\{encodedPath\\}", encodedPath);
        }
        s = s.replaceAll("\\$\\{rev\\}", revision);
        //s=s.replaceAll("\\$\\{rev-1\\}", revision-1);
        return s;
    }

    public static String formatDescription(String s, Locale locale) {
        if (s == null) {
            return null;
        }
        TagReplacer replacer = new TagReplacer(locale);
        replacer.setContextPath(Constants.BaseURL);
        String html = HTMLSanitiser.stripHTML(s);
        return replacer.formatDescription(html);
    }

    /**
     * Whether the comments tabs content is read only
     * @return
     */
    public static boolean isCommentsReadOnly(boolean isProjectActive, boolean editable) {
        return !isProjectActive || !editable;
    }

    /**
     * Whether the attachments tabs content is read only
     * @return
     */
    public static boolean isAttachmentsReadOnly(boolean isProjectActive, boolean editable) {
        return !isProjectActive || !editable;
    }

    /* Enable watcher's add/delete buttons
    * @return
    */
    private static boolean isWatcherAddDeleteDisabled(boolean projectIsActive, boolean editable,
            Map<Integer, Integer> fieldRestrictions) {
        if (!projectIsActive || !editable) {
            return true;
        }
        if (fieldRestrictions != null
                && fieldRestrictions.containsKey(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.WATCHERS)) {
            return true;
        }
        return false;
    }

    /**
     * Whether the me (add/remove) consulted button is disabled
     * @return
     */
    public static boolean isWatcherMeConsultedDisabled(TWorkItemBean workItemBean, Integer personID,
            boolean projectIsActive, boolean editable) {
        if (workItemBean.getObjectID() == null) {
            //new workItem add/remove me as consulted is enabled
            return false;
        }
        //existing workItem
        if (!projectIsActive) {
            return true;
        }
        if (editable) {
            return false;
        }
        if (ConsInfBL.hasDirectRaciRole(workItemBean.getObjectID(), personID, RaciRole.CONSULTANT)) {
            //if he is set as consulted he can remove himself
            return false;
        } else {
            //he is not set as consulted
            return !ConsInfBL.isEnableAddMeConsulted(personID, workItemBean.getProjectID(),
                    workItemBean.getListTypeID());
        }
    }

    /**
     * Whether the me (add/remove) informed button is disabled
     * @return
     */
    public static boolean isWatcherMeInformedDisabled(TWorkItemBean workItemBean, Integer personID,
            boolean projectIsActive, boolean editable) {
        if (workItemBean.getObjectID() == null) {
            //new workItem add/remove me as informed is enabled
            return false;
        }
        //existing workItem
        if (!projectIsActive) {
            return true;
        }
        if (editable) {
            return false;
        }
        if (ConsInfBL.hasDirectRaciRole(workItemBean.getObjectID(), personID, RaciRole.INFORMANT)) {
            //if he is set as informed he can remove himself
            return false;
        } else {
            return !ConsInfBL.isEnableAddMeInformed(personID, workItemBean.getProjectID(),
                    workItemBean.getListTypeID());
        }
    }

    public static boolean isExpenseReadOnly(boolean projectIsActive, boolean editable) {
        if (!projectIsActive || !editable) {
            return true;
        }
        return false;
    }

    /*public boolean isExpenseAddDisabled(TWorkItemBean workItemBean,Integer personID,boolean projectIsActive, boolean editable) {
       if (!projectIsActive || !editable) {
     return true;
       }
       if (workItemBean!=null) {
     return !AccountingBL.isEnableExpenseAdd(personID, workItemBean.getProjectID(),
           workItemBean.getListTypeID());
       }
       return true;
    }
    public boolean isExpenseEditDeleteDisabled(boolean projectIsActive) {
       if (!projectIsActive) {
     return true;
       }
       return false;
    }*/

    public static void includeItemDetails(ApplicationBean applicationBean, StringBuilder sb, WorkItemContext ctx,
            TPersonBean personBean, Locale locale, Integer lastSelectedTab) {
        Integer personID = personBean.getObjectID();
        TWorkItemBean workItemBean = ctx.getWorkItemBean();
        if (workItemBean == null) {
            return; // we cannot do much here anymore
        }

        //TProjectBean project=ctx.getProject();
        Integer projectID = workItemBean.getProjectID();
        Integer issueTypeID = workItemBean.getListTypeID();
        Integer activeTab = 0;
        sb.append("\"itemDetail\":{");
        Integer workItemID = workItemBean.getObjectID();

        boolean isProjectActive = ItemBL.projectIsActive(projectID);
        boolean editable = true;
        ErrorData editPermissionErrorData = ItemBL.hasEditPermission(personID, workItemBean);
        if (editPermissionErrorData != null) {
            editable = false;
        }

        Integer versionControlSize = getVersionControlSize(workItemID);
        //tabsEnabled:
        Map<Integer, Integer> fieldRestrictions = ctx.getFieldRestrictions();
        boolean enabledHistory = (workItemID != null);
        boolean enabledComments = (workItemID != null
                && (fieldRestrictions == null || !fieldRestrictions.containsKey(SystemFields.INTEGER_COMMENT)));

        Integer accessFlagAttachment = null;
        if (fieldRestrictions != null) {
            accessFlagAttachment = fieldRestrictions.get(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.ATTACHMENTS);
        }
        boolean readonlyAttachment = false;
        boolean invisibleAttachment = false;
        if (accessFlagAttachment != null) {
            if (accessFlagAttachment.intValue() == TRoleFieldBean.ACCESSFLAG.NOACCESS) {
                readonlyAttachment = true;
                invisibleAttachment = true;
            } else {
                invisibleAttachment = false;
                readonlyAttachment = (accessFlagAttachment.intValue() == TRoleFieldBean.ACCESSFLAG.READ_ONLY);
            }
        }
        boolean enabledAttachments = !invisibleAttachment;

        boolean enabledWorklog = false;
        ProjectAccountingTO projectAccountingTO = null;
        if (!ApplicationBean.getInstance().isGenji()) {
            projectAccountingTO = ProjectBL.getProjectAccountingTO(workItemBean.getProjectID());
            if (projectAccountingTO.isWorkTracking() || projectAccountingTO.isCostTracking()) {
                enabledWorklog = AccessBeans.showAccountingTab(personID, projectID, issueTypeID);
            }
        }

        boolean enabledWatchersTab = AccessBeans.watchersTabAllowed(personID, workItemBean.getProjectID(),
                workItemBean.getListTypeID());
        boolean enabledVersionControl = (workItemID != null && versionControlSize > 0);
        boolean enabledLinks = isEnableLinks(workItemBean);

        //TODO implement migrations
        boolean enabledMigrations = false;//isEnableMigrations(actionID);

        List<ItemDetailTab> itemDetailTabs = new ArrayList<ItemDetailTab>();
        if (enabledHistory) {
            ItemDetailTab historyTab = createTabHistory(personBean);
            itemDetailTabs.add(historyTab);
        }
        if (enabledComments) {
            ItemDetailTab commentsTab = createTabComments(workItemBean, workItemID, isProjectActive, editable);
            itemDetailTabs.add(commentsTab);
        }
        if (enabledAttachments) {
            ItemDetailTab attachmentsTab = createTabAttachment(applicationBean, workItemID, isProjectActive,
                    editable, readonlyAttachment);
            itemDetailTabs.add(attachmentsTab);
        }
        if (enabledWorklog) {
            ItemDetailTab worklogTab = createTabWorklog(workItemID, isProjectActive, editable, fieldRestrictions,
                    projectAccountingTO);
            itemDetailTabs.add(worklogTab);
        }
        if (enabledWatchersTab) {
            ItemDetailTab watchersTab = createTabWatcher(personID, workItemBean, workItemID, isProjectActive,
                    editable, fieldRestrictions);
            itemDetailTabs.add(watchersTab);
        }
        if (enabledVersionControl) {
            ItemDetailTab versionControlTab = createTabVersionControl(personBean);
            itemDetailTabs.add(versionControlTab);
        }
        if (enabledLinks) {
            ItemDetailTab linksTab = createTabLinks(workItemID, isProjectActive, editable);
            itemDetailTabs.add(linksTab);
        }
        if (lastSelectedTab != null) {
            for (int i = 0; i < itemDetailTabs.size(); i++) {
                ItemDetailTab tab = itemDetailTabs.get(i);
                if (tab.getId() == lastSelectedTab.intValue()) {
                    activeTab = i;
                    break;
                }
            }
        }
        JSONUtility.appendIntegerValue(sb, "activeTab", activeTab);
        JSONUtility.appendJSONValue(sb, "tabs", ItemDetailBL.encodeJSON_ItemDetailTabList(itemDetailTabs), true);
        sb.append("}");
    }

    private static ItemDetailTab createTabLinks(Integer workItemID, boolean projectActive, boolean editable) {
        ItemDetailTab linksTab = new ItemDetailTab();
        linksTab.setId(ItemDetailBL.TAB_LINKS);
        linksTab.setExtClassName("com.aurel.trackplus.itemDetail.LinksTab");
        StringBuilder jsonData = new StringBuilder();
        jsonData.append("{");

        int linkNumber = 0;

        if (workItemID != null) {
            linkNumber = ItemLinkBL.countLinks(workItemID);
        }
        JSONUtility.appendBooleanValue(jsonData, "readOnly", !projectActive || !editable);
        JSONUtility.appendIntegerValue(jsonData, "linkNumber", linkNumber, true);
        jsonData.append("}");
        linksTab.setJsonData(jsonData.toString());
        return linksTab;
    }

    private static ItemDetailTab createTabVersionControl(TPersonBean personBean) {
        ItemDetailTab versionControlTab = new ItemDetailTab();
        versionControlTab.setId(ItemDetailBL.TAB_VERSION_CONTROL);
        versionControlTab.setExtClassName("com.aurel.trackplus.itemDetail.VersionControlTab");
        boolean showFlat;
        String showFlatStr = PropertiesHelper.getProperty(personBean.getMoreProperties(),
                TPersonBean.SHOW_FLAT_VERSION_CONTROL);
        if (showFlatStr != null && showFlatStr.equalsIgnoreCase("true")) {
            showFlat = true;
        } else {
            showFlat = false;
        }

        StringBuilder jsonData = new StringBuilder();
        jsonData.append("{");
        JSONUtility.appendBooleanValue(jsonData, "showFlat", showFlat, true);
        jsonData.append("}");
        versionControlTab.setJsonData(jsonData.toString());
        return versionControlTab;
    }

    private static ItemDetailTab createTabWatcher(Integer personID, TWorkItemBean workItemBean, Integer workItemID,
            boolean projectActive, boolean editable, Map<Integer, Integer> fieldRestrictions) {
        ItemDetailTab watchersTab = new ItemDetailTab();
        watchersTab.setId(ItemDetailBL.TAB_WATCHERS);
        watchersTab.setExtClassName("com.aurel.trackplus.itemDetail.WatchersTab");
        int watchersNumber = 0;
        if (workItemID != null) {
            watchersNumber = ConsInfBL.countDirectWatcherPersons(workItemID);
        }
        StringBuilder jsonData = new StringBuilder();
        jsonData.append("{");
        //ItemDetailBL.appendGridLayoutData(jsonData,ItemDetailBL.TAB_WATCHERS,personID,locale);
        JSONUtility.appendBooleanValue(jsonData, "watcherAddDeleteDisabled",
                isWatcherAddDeleteDisabled(projectActive, editable, fieldRestrictions));
        JSONUtility.appendBooleanValue(jsonData, "watcherMeConsultedDisabled",
                isWatcherMeConsultedDisabled(workItemBean, personID, projectActive, editable));
        JSONUtility.appendBooleanValue(jsonData, "watcherMeInformedDisabled",
                isWatcherMeInformedDisabled(workItemBean, personID, projectActive, editable));
        JSONUtility.appendIntegerValue(jsonData, "watchersNumber", watchersNumber);
        JSONUtility.appendIntegerValue(jsonData, "myID", personID, true);

        jsonData.append("}");
        watchersTab.setJsonData(jsonData.toString());
        return watchersTab;
    }

    private static ItemDetailTab createTabComments(TWorkItemBean workItemBean, Integer workItemID,
            boolean projectActive, boolean editable) {
        ItemDetailTab commentsTab = new ItemDetailTab();
        commentsTab.setId(ItemDetailBL.TAB_COMMENTS);
        commentsTab.setExtClassName("com.aurel.trackplus.itemDetail.CommentsTab");
        int commentNumber = 0;

        StringBuilder jsonData = new StringBuilder();
        jsonData.append("{");

        if (workItemID != null) {
            commentNumber = FieldChangeBL.countCommentsByWorkItemID(workItemBean.getObjectID());
        }
        JSONUtility.appendBooleanValue(jsonData, "readOnly", isCommentsReadOnly(projectActive, editable));
        JSONUtility.appendIntegerValue(jsonData, "commentNumber", commentNumber, true);
        jsonData.append("}");
        commentsTab.setJsonData(jsonData.toString());
        return commentsTab;
    }

    private static ItemDetailTab createTabHistory(TPersonBean personBean) {
        ItemDetailTab historyTab = new ItemDetailTab();
        historyTab.setId(ItemDetailBL.TAB_HISTORY);
        historyTab.setExtClassName("com.aurel.trackplus.itemDetail.HistoryTab");

        boolean historyFull;
        String historyFullStr = PropertiesHelper.getProperty(personBean.getMoreProperties(),
                TPersonBean.HISTORY_FULL);
        if (historyFullStr != null && historyFullStr.equalsIgnoreCase("false")) {
            historyFull = false;
        } else {
            historyFull = true;
        }
        boolean showCommentsHistory;
        String showCommentsHistoryStr = PropertiesHelper.getProperty(personBean.getMoreProperties(),
                TPersonBean.SHOW_COMMENTS_HISTORY);
        if (showCommentsHistoryStr != null && showCommentsHistoryStr.equalsIgnoreCase("true")) {
            showCommentsHistory = true;
        } else {
            showCommentsHistory = false;
        }
        boolean showFlatHistory;
        String showFlatHistoryStr = PropertiesHelper.getProperty(personBean.getMoreProperties(),
                TPersonBean.SHOW_FLAT_HISTORY);
        if (showFlatHistoryStr != null && showFlatHistoryStr.equalsIgnoreCase("true")) {
            showFlatHistory = true;
        } else {
            showFlatHistory = false;
        }

        StringBuilder jsonData = new StringBuilder();
        jsonData.append("{");
        JSONUtility.appendBooleanValue(jsonData, "historyFull", historyFull);
        JSONUtility.appendBooleanValue(jsonData, "showCommentsHistory", showCommentsHistory);
        JSONUtility.appendBooleanValue(jsonData, "showFlatHistory", showFlatHistory, true);
        jsonData.append("}");
        historyTab.setJsonData(jsonData.toString());
        return historyTab;
    }

    private static ItemDetailTab createTabWorklog(Integer workItemID, boolean projectActive, boolean editable,
            Map<Integer, Integer> fieldRestrictions, ProjectAccountingTO projectAccountingTO) {
        ItemDetailTab worklogTab = new ItemDetailTab();
        worklogTab.setId(ItemDetailBL.TAB_WORKLOG);
        worklogTab.setExtClassName("com.aurel.trackplus.itemDetail.WorklogTab");

        boolean readOnly = isExpenseReadOnly(projectActive, editable);
        boolean expenseAddDisabled = false;
        boolean expenseEditDeleteDisabled = false;
        if (readOnly) {
            expenseAddDisabled = true;
            expenseEditDeleteDisabled = true;
        } else {
            if (fieldRestrictions != null
                    && fieldRestrictions.containsKey(FieldsRestrictionsToRoleBL.PSEUDO_COLUMNS.MY_EXPENSES)) {
                expenseAddDisabled = true;
                expenseEditDeleteDisabled = true;
            } else {
                if (ApplicationBean.getInstance().getSiteBean().getSummaryItemsBehavior()) {
                    if (workItemID != null && ItemBL.hasChildren(workItemID)) {
                        expenseAddDisabled = true;
                        expenseEditDeleteDisabled = true;
                    }
                }
            }
        }
        StringBuilder jsonData = new StringBuilder();
        jsonData.append("{");
        JSONUtility.appendBooleanValue(jsonData, "readOnly", readOnly);
        JSONUtility.appendBooleanValue(jsonData, "expenseAddDisabled", expenseAddDisabled);
        JSONUtility.appendBooleanValue(jsonData, "expenseEditDeleteDisabled", expenseEditDeleteDisabled);
        JSONUtility.appendBooleanValue(jsonData, "workTracking", projectAccountingTO.isWorkTracking());
        JSONUtility.appendBooleanValue(jsonData, "costTracking", projectAccountingTO.isCostTracking(), true);
        jsonData.append("}");
        worklogTab.setJsonData(jsonData.toString());
        return worklogTab;
    }

    private static ItemDetailTab createTabAttachment(ApplicationBean applicationBean, Integer workItemID,
            boolean projectActive, boolean editable, boolean readonlyAttachment) {
        ItemDetailTab attachmentsTab = new ItemDetailTab();
        attachmentsTab.setId(ItemDetailBL.TAB_ATTACHMENTS);
        attachmentsTab.setExtClassName("com.aurel.trackplus.itemDetail.AttachmentsTab");
        StringBuilder jsonData = new StringBuilder();
        jsonData.append("{");
        JSONUtility.appendBooleanValue(jsonData, "readOnly",
                isAttachmentsReadOnly(projectActive, editable) || readonlyAttachment);
        int attachmentNumber = 0;

        if (workItemID != null) {
            attachmentNumber = AttachBL.countByWorkItemID(workItemID);
        }

        JSONUtility.appendIntegerValue(jsonData, "attachmentNumber", attachmentNumber);
        JSONUtility.appendStringValue(jsonData, "maxSize",
                AttachBL.getMaxAttachmentSizeInMb(applicationBean).toString(), true);
        jsonData.append("}");
        attachmentsTab.setJsonData(jsonData.toString());
        return attachmentsTab;
    }

    private static Integer getVersionControlSize(Integer workItemID) {
        Integer versionControlSize;
        if (workItemID == null) {
            versionControlSize = Integer.valueOf(0);
        } else {
            Map<Integer, List<Revision>> revisionsMap = VersionControlMap.getMap();
            List<Revision> revisions;
            if (revisionsMap == null) {
                revisions = new ArrayList<Revision>();
            } else {
                revisions = revisionsMap.get(workItemID);
            }
            if (revisions == null || revisions.isEmpty()) {
                versionControlSize = Integer.valueOf(0);
            } else {
                versionControlSize = revisions.size();
            }
        }
        return versionControlSize;
    }

    /**
     * Whether the link tab is enabled
     * @param workItemBean
     * @return
     */
    public static boolean isEnableLinks(TWorkItemBean workItemBean) {
        if (workItemBean != null) {
            Integer projectID = workItemBean.getProjectID();
            if (projectID != null) {
                TProjectBean projectBean = LookupContainer.getProjectBean(projectID);
                if (projectBean != null) {
                    return LinkTypeBL.isEnableTab(projectBean);
                }
            }
        }
        return false;
    }

    public static void encodeTabJSON(String itemsJSON, boolean includeLayout, Integer tabID, Integer personID,
            Locale locale) {
        String result;
        if (includeLayout) {
            StringBuilder sb = new StringBuilder();
            sb.append("{");
            JSONUtility.appendBooleanValue(sb, JSON_FIELDS.SUCCESS, true);
            JSONUtility.appendFieldName(sb, JSONUtility.JSON_FIELDS.DATA).append(":{");
            List<ItemGridLayout> layout = ItemGridLayoutBL.loadLayout(tabID, personID, null, locale);
            JSONUtility.appendFieldName(sb, "gridLayoutData").append(":{");
            JSONUtility.appendJSONValue(sb, "layout", ItemDetailBL.encodeJSON_Layout(layout, locale), true);
            sb.append("},");
            JSONUtility.appendJSONValue(sb, "items", itemsJSON, true);
            sb.append("}}");
            result = sb.toString();
        } else {
            result = itemsJSON;
        }
        try {
            JSONUtility.prepareServletResponseJSON(ServletActionContext.getResponse());
            PrintWriter out = ServletActionContext.getResponse().getWriter();
            out.println(result);
        } catch (IOException e) {
            LOGGER.error(ExceptionUtils.getStackTrace(e));
        }
    }
}