Versions Compared

Key

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

Hosting

Status
titleDATA CENTER

Problems and solutions

Here are some scripts that address common Projectrak Data Center (DC) problems. These scripts should be executed in the Projectrak console, which can be accessed from the Manage Apps section and is only accessible to Jira Administrator.
Click here to learn more about the Projectrak Script Console console.

Note

Most of the scripts make irreversible changes that can impact the proper functioning of the application. They should be used with caution.

Please contact our support team before using them.

Table of Contents
minLevel2
maxLevel5
includeScripts.

...

Tip

Values

Problem / Suggestion

*
outlinefalse
typelist
separatorpipe
printablefalse

Scripts for values

Problem: You need to delete values by their identifiers

...

Solution

...

: The introduced identifiers are being traversed, and if they exist, they are deleted from the database.

...

Code Block
languagegroovy
import com.atlassian.jira.component.ComponentAccessor
import com.deiser.jira.profields.ao.v2.projectvalue.ProjectValueMgr
 
 
def VALUES_IDS = [x,x]
def projectValueMgr = ComponentAccessor.getOSGiComponentInstanceOfType(ProjectValueMgr.class)
def value = null
def println = ""
 
VALUES_IDS.each {
     
    println += "*** *** *** <br/>"
    println += "Value ID: " + it + "<br/>"
     
    value = projectValueMgr.get(it)
     
    if(value != null){
        projectValueMgr.delete(value)
        println += "Deleted!! <br/>"
    }
    else{
        println += "Can not found this Value <br/>"
    }
     
    println += "<br/><br/>"
}
 
 
return println

Problem

...

: Delete all values from Projectrak within a set of projects.

Solution

...

: All the introduced project identifiers are being traversed, and for each of them, all their values are being iterated to subsequently delete them.

...

titleScript
Code Block
languagegroovy
import com.atlassian.jira.component.ComponentAccessor
import com.deiser.jira.profields.ao.v2.projectvalue.ProjectValueMgr
 
 
def PROJECTS_IDS = [x, x]
def projectValueMgr = ComponentAccessor.getOSGiComponentInstanceOfType(ProjectValueMgr.class)
def value = null
def println = ""
 
PROJECTS_IDS.each {
 
    println += "*** *** *** <br/>"
    println += "Project Value for Project: " + it + "<br/>"
 
    value = projectValueMgr.getByProject((Long) it)
 
    if(value != null){
        projectValueMgr.delete(value)
        println += "Deleted!! <br/>"
    }
    else{
        println += "Can not found this Value <br/>"
    }
 
    println += "<br/><br/>"
}
 
 
return println

Problem

...

: The values in the columns where the value is stored in lowercase do not have the same equivalent value as those where the value is stored as raw data.

Solution

...

: All existing values are verified. Each field type has a different validation check.

...

Code Block

...

language

...

groovy
import com.atlassian.jira.component.ComponentAccessor
import com.deiser.jira.profields.ao.v2.projectvalue.ProjectValueMgr
import com.deiser.jira.profields.api.field.FieldService
import com.deiser.jira.profields.api.field.FieldType
 
 
def projectValueMgr = ComponentAccessor.getOSGiComponentInstanceOfType(ProjectValueMgr.class)
def fieldService = ComponentAccessor.getOSGiComponentInstanceOfType(FieldService.class)
def VALUES_IDS = projectValueMgr.get()
def println = "*** Values different *** <br />"
VALUES_IDS.each {
    def change = false
 
    def field = fieldService.get(it.field.ID)
 
    if(!(field.type.isCalculatedType() || field.type.isEquals("number") || field.type.isEquals("date") ||field.type.isEquals("duration"))) {
 
        if (it.value != null && !it.value.toLowerCase().equals(it.lowerValue)) {
            println += "VALUE: " + it.ID + "<br />"
            println += "Changing LowerValue <br />"
            it.setLowerValue(it.value.toLowerCase())
            change = true
        }
 
        if (it.display != null && !it.display.toLowerCase().equals(it.lowerDisplay)) {
            println += "VALUE: " + it.ID + "<br />"
            println += "Changing LowerDisplay <br />"
            it.setLowerDisplay(it.display.toLowerCase())
            change = true
        } else if (it.display == null && it.value != null) {
            println += "VALUE: " + it.ID + "<br />"
            println += "Set Display equals to Value <br />"
            it.setDisplay(it.value)
            it.setLowerDisplay(it.value.toLowerCase())
            change = true
        }
 
        if(change){
            projectValueMgr.save(it)
            println += "Change it! <br />"
        }
    }
 
 
}
 
 
return println

Problem

...

: Information of values still exists in Projectrak for projects that no longer exist in Jira.

Solution

...

: All value records are being traversed, and those where the project does not exist are being deleted.

...

Code Block

...

language

...

groovy

...

import com.atlassian.jira.bc.project.ProjectService
import com.atlassian.jira.component.ComponentAccessor
import com.deiser.jira.profields.ao.v2.projectvalue.AOProjectValue
import com.deiser.jira.profields.ao.v2.projectvalue.ProjectValueMgr
 
 
def projectValueMgr = ComponentAccessor.getOSGiComponentInstanceOfType(ProjectValueMgr.class)
def projectManager = ComponentAccessor.getProjectManager()
def println = ""
 
def allProjectValues = projectValueMgr.get()
if(allProjectValues != null){
    allProjectValues.each {
 
        def haveToDelete = it == null || projectManager.getProjectObj(it.getProjectId()) == null
        if(haveToDelete){
 
            println += "*** *** *** <br/>"
            println += (it == null || it.getProjectId() == null) ? "Project Value is empty" : "Project Value for Project: " + it.getProjectId() + "<br/>"
 
            projectValueMgr.delete(it)
            println += "<br/>"
        }
    }
     
}
 
return println

Problem

...

: There are projects with duplicate values for the same field.

Solution

...

: Traverse all values and delete the duplicates, leaving the oldest value in the database.

...

Code Block
languagegroovy

import com.atlassian.jira.component.ComponentAccessor
import com.deiser.jira.profields.ao.v2.projectvalue.AOProjectValue
import com.deiser.jira.profields.ao.v2.projectvalue.ProjectValueMgr
import com.deiser.jira.profields.api.configuration.ConfigurationService
import com.deiser.jira.profields.api.field.FieldService
import com.deiser.jira.profields.api.field.FieldType
import groovy.transform.Field
import org.apache.commons.collections.CollectionUtils
 
@Field ProjectValueMgr projectValueMgr = ComponentAccessor.getOSGiComponentInstanceOfType(ProjectValueMgr.class)
 
/* ***** execution ***** */
@Field HashMap<String, List<AOProjectValue>> allValuesByHash = new HashMap<>()
@Field List println = ["<b>*** REMOVED ***</b> <br/>"]
removeAllDuplicatedValues()
return println
 
/* ***** FUNCTIONS ***** */
def removeAllDuplicatedValues(){
    projectValueMgr.get().each{addInMap(it)}
    getAllDuplicatedValues().each {list -> removeDuplicated(list)}
}
 
def getAllDuplicatedValues(){
    return allValuesByHash.entrySet().stream()
        .filter{it -> it.value.size() > 1}
        .map{it -> it.value}
        .collect()
}
 
def addInMap(AOProjectValue value){
    String hash = getHash(value)
    List currentValues = allValuesByHash.remove(hash)
    currentValues = currentValues == null ? new ArrayList() : currentValues;
    currentValues.add(value)
    allValuesByHash.put(hash, currentValues)
}
 
def getHash(AOProjectValue value){
    def fieldType = FieldType.getFieldTypeByKey(value.getField().getType())
    return fieldType.isMultiple() ? getHashForMultipleField(value) : getHashForSimpleField(value)
}
 
def getHashForMultipleField(AOProjectValue value){
    return "PROJECT("+ value.projectId +"); FIELD_ID("+ value.field.ID +"); VALUE("+ value.value +"); DISPLAY("+ value.display +"); PARENT_ID("+ value.parentId +");".toLowerCase()
}
 
def getHashForSimpleField(AOProjectValue value){
    return "PROJECT("+ value.projectId +"); FIELD_ID("+ value.field.ID +");".toLowerCase()
}
 
def getValueInString(AOProjectValue value){
    return "<br/> ID(<b>"+ value.ID +"</b>); PROJECT(<b>"+ value.projectId +"</b>); FIELD_ID(<b>"+ value.field.ID +"</b>); FIELD_TYPE(<b>"+ value.field.type +"</b>); VALUE(<b>"+ value.value +"</b>); DISPLAY(<b>"+ value.display +"</b>); PARENT-VALUE(<b>"+ value.parentValue +"</b>) PARENT-ID(<b>"+ value.parentId +"</b>); CHILDREN(<b>"+ getNumberOfChildren(value) +"</b>);"
}
 
def removeDuplicated(List<AOProjectValue> duplicated){
    List<AOProjectValue> withChildren = new ArrayList<>();
    List<AOProjectValue> withoutChildren = new ArrayList<>();
    duplicated.sort{a,b -> (a.ID <=> b.ID) }
    duplicated.each{ fillByNumberOfChildren(it, withChildren, withoutChildren)}
    def listToRemove = getListToRemove(withChildren, withoutChildren)
    listToRemove.each{
        println.add(getValueInString(it))
        projectValueMgr.delete(it)
    }
}
 
def fillByNumberOfChildren(AOProjectValue value, List withChildren, List withoutChildren){
    def numberOfChildren = getNumberOfChildren(value)
    if(numberOfChildren == 0) withoutChildren.add(value)
    else withChildren.add(value)
}
 
def getNumberOfChildren(AOProjectValue value){
    def parent = projectValueMgr.getByParentCustom(value.value)
    return parent == null ? null : parent.size();
}
 
def getListToRemove(List<AOProjectValue> withChildren, List<AOProjectValue> withoutChildren){
    if(withChildren.isEmpty()) return withoutChildren.subList(1, withoutChildren.size())
    if(withChildren.size() == 1) return withoutChildren
    List listToRemove = withChildren.size(1, withChildren.size())
    listToRemove.addAll(withoutChildren)
    return listToRemove
}

...

Scripts for layouts

...

Layouts

Problem

...

: We have found non-existent fields in the structure of a layout.

Solution

...

: Traverse the structure of all layouts to check if their fields exist, and if not, we delete them.

...

Code Block

...

language

...

groovy
import com.atlassian.jira.component.ComponentAccessor
import com.deiser.jira.profields.ao.v2.viewitem.AOViewItem
import com.deiser.jira.profields.ao.v2.viewitem.ViewItemMgr
import com.deiser.jira.profields.api.field.FieldService

def viewItemMgr = ComponentAccessor.getOSGiComponentInstanceOfType(ViewItemMgr.class)
def fieldService = ComponentAccessor.getOSGiComponentInstanceOfType(FieldService.class)
def allViewItems = viewItemMgr.get()

def println = ""

allViewItems.each {

    if (it.fieldId != null) {

        def field = fieldService.get(it.fieldId)
        if(field == null){
            println += "Delete Field (" + it.fieldId + ") in Layout (" + it.layout.name + ") <br/>"
            viewItemMgr.delete(it)
        }
    }
}

println

Problem

...

: Within a layout, there is duplicated information and orphaned structures.

Solution

...

: Traverse all the layouts and verify their internal structure. If they have duplicated or orphaned structures, we delete them.

...

titleScript
Code Block
languagegroovy
import com.atlassian.jira.component.ComponentAccessor
import com.deiser.jira.profields.ao.v2.layout.LayoutMgr
import com.deiser.jira.profields.ao.v2.viewitem.AOViewItem
import com.deiser.jira.profields.ao.v2.viewitem.ViewItemMgr
import com.deiser.jira.profields.api.layout.view.SectionView
import com.deiser.jira.profields.api.layout.view.ViewItem
import com.google.common.collect.Lists
import groovy.transform.Field
import org.apache.commons.collections.ListUtils

import java.util.stream.Collectors

@Field ViewItemMgr viewItemMgr = ComponentAccessor.getOSGiComponentInstanceOfType(ViewItemMgr.class)
@Field LayoutMgr layoutMgr = ComponentAccessor.getOSGiComponentInstanceOfType(LayoutMgr.class)
@Field List println = []

execute()


//EXECUTE
def execute(){
    def layouts = layoutMgr.get()
    layouts.each {
        iterateByLayout(it)
    }
    return println
}

//LAYOUTS
def iterateByLayout(layout){
    def sourceItems = viewItemMgr.getByLayout(layout)
    checkAndFix(layout, sourceItems)
}

def checkAndFix(layout, sourceItems){
    //Get all structure
    def structure = getAllSections(sourceItems)
    addToPrint(layout, structure)

    //Remove all duplicated sections
    def duplicatedSections = removeAllDuplicatedSections(structure)
    addToPrintDeletedDuplicatedSections(duplicatedSections)

    //Remove all orphan
    def deletedOrphans = removeUntilThereAreNoOrphans()
    addToPrintDeletedOrphans(deletedOrphans)

    //Split
    addToPrintSplit()
}

//SECTIONS
def getAllSections(sourceItems){
    return sourceItems.stream()
            .filter{it -> isSection(it)}
            .map{it -> new ViewNode(item: it, children: getAllColumns(it, sourceItems), type: "Section")}
            .collect(Collectors.toList())
}

boolean isSection(viewItem){
    return viewItem.parentId == null
}

int compareSection(viewNode1, viewNode2){
    boolean comparative = viewNode1.item.name == viewNode2.item.name &&
            viewNode1.item.position == viewNode2.item.position &&
            ((viewNode1.children == null && viewNode2.children == null) ||
                    (viewNode1.children.size() == viewNode2.children.size()))
    return comparative ? 0 : 1
}

def removeAllDuplicatedSections(structure){
    def sectionsUniques = structure.toUnique{ sect1, sect2 -> compareSection(sect1, sect2) }
    def thereAreDuplicatedSections = structure.size() != sectionsUniques.size();
    if(thereAreDuplicatedSections){
        def repeatedSections = ListUtils.removeAll(structure, sectionsUniques)
        def repeatedViewItemSections = repeatedSections.stream().map { it -> it.item }.collect(Collectors.toList())

        this.viewItemMgr.delete(repeatedViewItemSections)
        return repeatedViewItemSections
    }
    return [];
}



//COLUMNS
def getAllColumns(section, sourceItems){
    return columns = sourceItems.stream()
            .filter{ it -> isColumn(section, it) }
            .map{it-> new ViewNode(item: it, children: getAllContainer(it, sourceItems), type: "Column")}
            .collect(Collectors.toList())
            .sort{ a -> a.item.position}
}

boolean isColumn(section, viewItem){
    return viewItem.parentId != null &&
            viewItem.parentId == section.ID &&
            viewItem.name == null &&
            viewItem.fieldId == null
}



//CONTAINERS
def getAllContainer(column, sourceItems){
    return sourceItems.stream()
            .filter{it -> isContainer(column, it)}
            .map{it -> new ViewNode(item: it, children: getAllFields(it, sourceItems), type: "Container")}
            .collect(Collectors.toList())
            .sort{ a -> a.item.position}
}

boolean isContainer(column, viewItem){
    return viewItem.parentId != null &&
            viewItem.parentId == column.ID &&
            viewItem.name
}

//FIELDS
def getAllFields(container, sourceItems){
    return sourceItems.stream()
            .filter{it ->  isField(container, it)}
            .map{it -> new ViewNode(item: it, children: null, type: "Field")}
            .collect(Collectors.toList())
            .sort{ a -> a.item.position}
}

def isField(container, viewItem){
    return viewItem.parentId != null &&
            viewItem.parentId == container.ID &&
            viewItem.getFieldId() != null
}


class ViewNode {
    AOViewItem item
    List<ViewNode> children;
    String type;

    def compare(ViewNode node){
        return item.name == node.item.name &&
                item.position == node.item.position &&
                item.fieldId == node.item.fieldId &&
                item.required == node.item.required &&
                item.layout.ID == node.item.layout.ID &&
                compareChildren(node.children)
    }

    def compareChildren(List<AOViewItem> nodeChildren){
        if(children == null && nodeChildren == null) return true;

        if(children.size() !=  nodeChildren.size()) return false;

        def compareResult = true
        children.eachWithIndex { ViewNode item, int index ->
            compareResult &= item.compare(nodeChildren.get(index))
            if(compareResult == false) return false
        }
        return compareResult
    }

    def printToString(){
        def itemString = item.position + " ID["+ item.ID +"] NAME["+ item.name +"] PARENT-ID["+ item.parentId +"] FIELD-ID["+ item.fieldId +"] <br/>"
        def childrenString = this.children == null
                ? ""
                : this.children.stream()
                .map{it -> it.printToString()}
                .collect(Collectors.joining(""))

        def tab  = ""
        switch(type){
            case "Section":
                break;
            case "Column":
                tab += "        "
                break;
            case "Container":
                tab += "        " + "        "
                break;
            case "Field":
                tab += "        " + "        " + "        " + "    "
                break;
        }

        return tab + type + " --> " +  itemString + childrenString
    }
}

//REMOVE ORPHAN
def removeUntilThereAreNoOrphans(){
    def hasOrphan = true
    def printlnToOrphans = []

    while(hasOrphan){
        hasOrphan = removeOrphan(printlnToOrphans)
    }

    return printlnToOrphans
}

def removeOrphan(println){
    def hasRemoved = false
    viewItemMgr.get().each {
        def parentId = it.getParentId()
        if(parentId != null && parentId >= 0){
            if(viewItemMgr.get(parentId) == null){
                println.add( "ID: "+ it.ID +", Name: "+ it.name +" / ")
                viewItemMgr.delete(it)
                hasRemoved = true
            }
        }
    }
    return hasRemoved
}


//PRINTS
def addToPrint(layout, structure){
    println.add("Layout ("+ layout.ID +")  <b>"+ layout.name +"</b><br/>Structure:<br/>")
    println.add(structure.stream().map{it -> it.printToString()}.collect(Collectors.toList()))
}

def addToPrintDeletedDuplicatedSections(duplicatedSections){
    if(duplicatedSections){
        def prefix = "<br/> <b>Deleted Sections:</b>";
        def duplicatedString = duplicatedSections.stream().map{it -> "ID: "+ it.ID +", Name: "+ it.name +" / "}.collect(Collectors.joining(" / "))
        println.add(prefix + duplicatedString + "<br/>")
    }
}

def addToPrintSplit(){
    println.add("   ------------    ------------    ------------   ------------    ------------    ------------   <br/>")
}

def addToPrintDeletedOrphans(deletedOrphans){
    if(!deletedOrphans.isEmpty()){
        println.add("<b>Orphans Deleted:</b> " + deletedOrphans + "<br/>")
    }
}

Problem

...

: We have found duplicate associations between layouts and projects.

Solution

...

: Traverse all existing associations and remove the duplicates.

...

Code Block

...

language

...

groovy

...

import com.atlassian.jira.component.ComponentAccessor
import com.deiser.jira.profields.ao.v2.projectlayout.AOProjectLayout
import com.deiser.jira.profields.ao.v2.projectlayout.ProjectLayoutMgr


def projectLayoutMgr = ComponentAccessor.getOSGiComponentInstanceOfType(ProjectLayoutMgr.class)

def store = [:]
def listToDelete = []


def allProjectLayout = projectLayoutMgr.get()
allProjectLayout.sort(true){a,b -> a.ID <=> b.ID}
if(allProjectLayout != null && allProjectLayout.size() > 0){
    allProjectLayout.each {

        def key = "LAYOUT: " + it.layout.ID + "; PROJECT: " + it.projectId + ";"
        def value = store.get(key)
        if(value == null){
            store.put(key, it)
        }
        else{
            listToDelete.add(it)
        }
    }
}


def println = ""
listToDelete.each {
    println += "ID: "+ it.ID +"LAYOUT: " + it.layout.ID + "; PROJECT: " + it.projectId + ";"
    projectLayoutMgr.delete(it)
    println += "Deleted! </br>"
}
println

Problem

...

: We have found associations of layouts with non-existent projects.

Solution

...

: Traverse all associations and delete those that are associated with non-existent projects.

...

titleScript
Code Block
languagegroovy
import com.atlassian.jira.component.ComponentAccessor
import com.deiser.jira.profields.ao.v2.projectlayout.AOProjectLayout
import com.deiser.jira.profields.ao.v2.projectlayout.ProjectLayoutMgr


def projectLayoutMgr = ComponentAccessor.getOSGiComponentInstanceOfType(ProjectLayoutMgr.class)
def projectManager = ComponentAccessor.getProjectManager()
def println = ""

def allProjectLayout = projectLayoutMgr.get()
if(allProjectLayout != null && allProjectLayout.length > 0){
    allProjectLayout.each {
        def haveToDelete = it == null || projectManager.getProjectObj(it.getProjectId()) == null
        if(haveToDelete){

            println += "<br/>"
            println += (it == null || it.getProjectId() == null) ? "Layout-Project is empty. " : "Layout-Project for Project: " + it.getProjectId() + "  "

            projectLayoutMgr.delete(it)
            println += "Deleted!!"
        }
    }
}

return println

...

Scripts for items of list fields

Problem

...

: We are experiencing issues with the items in the list fields, but we are uncertain about the root cause. We need to identify where the problem originates.

Solution

...

: Display the information of the problematic items.

...

titleScript
Code Block
languagegroovy
import com.atlassian.jira.component.ComponentAccessor
import com.deiser.jira.profields.ao.v2.field.FieldMgr
import com.deiser.jira.profields.ao.v2.listitem.AOListItem
import com.deiser.jira.profields.ao.v2.listitem.ListItemMgr
import com.deiser.jira.profields.ao.v2.listitem.ListItemType
import groovy.transform.Field;

@Field ListItemMgr listItemMgr = ComponentAccessor.getOSGiComponentInstanceOfType(ListItemMgr.class)
@Field FieldMgr fieldMgr = ComponentAccessor.getOSGiComponentInstanceOfType(FieldMgr.class)




String getMessageError(AOListItem listItem){
    String error = ""
    error += getValidField(listItem)
    error += getValidType(listItem)
    error += getValidCustomId(listItem)
    error += getValidData(listItem)
    error += getValidOrder(listItem)
    return error
}

String getValidField(AOListItem listItem){
    String error = ""
    error += listItem.getFieldId() == null ? "FieldId NULL; " : ""
    error += (error == "") && listItem.getFieldId() <= 0 ? "FieldId-0; " : ""
    error += (error == "") && fieldMgr.get(listItem.getFieldId()) == null ? "Field Not Exist; " : ""
    return error
}

String getValidType(AOListItem listItem){
    String error = ""
    error += listItem.getListItemType() == null ? "Type NULL; " : ""
    error += (error == "") && !ListItemType.isValidKey(listItem.getListItemType()) ? "Type Not Valid Value; " : ""
    return error
}

String getValidCustomId(AOListItem listItem){
    String error = ""
    error += listItem.getCustomId() == null ? "CustomId NULL" : ""
    error += (error == "") && listItem.getCustomId() == "" ? "CustomId is EMPTY;" : ""
    return error
}

String getValidData(AOListItem listItem){
    String error = ""
    error += listItem.getData() == null ? "Data NULL; " : ""
    error += (error == "") && listItem.getData() == "" ? "Data is EMPTY;" : ""
    return error
}

String getValidOrder(AOListItem listItem){
    String error = ""
    error += listItem.getOrder() == null ? "Order NULL; " : ""
    error += (error == "") && listItem.getOrder() <= 0 ? "Order-0" : ""
    return error
}



def print = ""
listItemMgr.get().each {
    def error = getMessageError(it)
    if(error != ""){
        print += "ID " + it.ID + ": " + error + " <br/> "
    }
}
return "<b>List Items With ERRORS:</b> <br/>" + print

Problem

...

: There are issues with the sequential order of items in list fields. They maintain their order, but it is not sequential.

Solution

...

: Establish a new sequential order through the script.

...

...

Code Block
languagegroovy
import com.atlassian.jira.component.ComponentAccessor
import com.deiser.jira.profields.ao.v2.field.FieldMgr
import com.deiser.jira.profields.ao.v2.listitem.AOListItem
import com.deiser.jira.profields.ao.v2.listitem.ListItemMgr
import com.deiser.jira.profields.api.configuration.ConfigurationService
import groovy.transform.Field
@Field ListItemMgr listItemMgr = ComponentAccessor.getOSGiComponentInstanceOfType(ListItemMgr.class)
@Field boolean UPDATE = true
main()
/* **** METHODS **** */
def main(){
    def itemsByFieldID = getAllItemsByFieldID()
    def itemsToChange = checkOrder(itemsByFieldID)
    def println = printItems(itemsToChange)
    if(UPDATE){
        listItemMgr.save(itemsToChange)
        println.add(" UPDATED ALL <br/>")
    }
    return println
}
def getAllItemsByFieldID(){
    def fieldWithItems = [:]
    listItemMgr.get().each {
        def values = fieldWithItems[it.fieldId]
        if(values == null){
            fieldWithItems[it.fieldId] = [it]
        }
        else{
            values.add(it)
            fieldWithItems[it.fieldId] = values
        }
    }
    return fieldWithItems
}
def checkOrder(HashMap map){
    def itemsToChange = []
    def keys = map.keySet()
    keys.each{
        def values = map[it]
        orderItems(values, itemsToChange)
    }
    return itemsToChange
}
def printItems(List<AOListItem> items){
    def println = [" *** ITEMS TO UPDATE *** <br/>"]
    items.each { println.add(itemToString(it))}
    return println
}
def orderItems(List<AOListItem> items, List<AOListItem> itemsToChange){
    def sortedItems = items.sort{ a, b -> a.order <=> b.order}
    sortedItems.eachWithIndex { AOListItem entry, int i ->
        if(entry.order != (i +1)){
            entry.order = i + 1
            itemsToChange.add(entry)
        }
    }
}
String itemToString(AOListItem item){
    return String.format("ID(%d); ORDER(%d) FIELD_ID(%d); TYPE(%s); <br/>",
            item.ID,
            item.order,
            item.fieldId,
            item.listItemType
    )
}

...

Scripts for connections

Problem

...

: Establishing a connection to a pre-existing database is required for certain fields of Projectrak's external list.

Solution

...

: Through a set of IDs of external list fields, they are traversed to establish the selected connection using their respective ID.

...

...

Code Block
languagegroovy

import com.atlassian.jira.component.ComponentAccessor
import com.deiser.jira.profields.ao.v2.field.AOField
import com.deiser.jira.profields.ao.v2.field.FieldMgr
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
 
def fieldMgr = ComponentAccessor.getOSGiComponentInstanceOfType(FieldMgr.class)
 
def FIELD_IDs = [X, X];
def CONNECTION_ID = Y
 
def println = "Start <br/>"
 
 
 
FIELD_IDs.each {
 
    def fieldList = fieldMgr.getById(it)
 
    if(fieldList.length == 1){
 
        def field = fieldList[0]
        def data = field.getData()
 
        println += "     Field: Id(" + field.ID + ") Name(" + field.name + ") Data(" + field.data + ") <br/>"
 
        def jsonSlurper = new JsonSlurper()
        def objectJson = jsonSlurper.parseText(data)
 
        objectJson.connectionId = CONNECTION_ID
        def newData = JsonOutput.toJson(objectJson)
 
        field.setData(newData)
 
        println += "     Before Save: Id(" + field.ID + ") Name(" + field.name + ") Data(" + field.data + ") <br/>"
 
        fieldMgr.save(field)
 
        println += "     Saved! <br/>"
    }
}
 
println

...

Scripts for subscriptions

Problem

...

: Know which subscriptions the instance has and who their owner is.

Solution

...

: Retrieve all subscriptions stored in Projectrak along with their respective users.

...

titleScript
Code Block
languagegroovy
import com.deiser.jira.profields.api.subscription.SubscriptionService
import com.atlassian.jira.component.ComponentAccessor

ComponentAccessor.getOSGiComponentInstanceOfType(SubscriptionService.class)
    .get()
    .collect{" - ${it.user}: ${it.name}"}
    .join("<br/>")

...

Scripts for mapping to Jira custom fields

Problem

...

: We have encountered Jira custom fields that have lost their association with Projectrak fields.

Solution

...

: All Jira custom fields that belong to Projectrak are traversed to verify if they are related to a Projectrak field. If not, the Jira custom field is deleted.

...

...

titleScript
Code Block
languagegroovy
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.config.managedconfiguration.ManagedConfigurationItem
import com.atlassian.jira.config.managedconfiguration.ManagedConfigurationItemService
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.fields.CustomField
import com.deiser.jira.profields.ao.v2.field.FieldMgr
 
CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager()
ManagedConfigurationItemService managedConfigurationItemService = ComponentAccessor.getComponent(ManagedConfigurationItemService.class);
 
FieldMgr fieldMgr = ComponentAccessor.getOSGiComponentInstanceOfType(FieldMgr.class)
 
def objects = customFieldManager.getCustomFieldObjects()
print = "*** CustomFields of Profields *** <br/>"
 
def each = objects.each {
    def customType = it.getCustomFieldType().toString()
    if (customType.contains("com.deiser.jira.profields")) {
 
        print += "CustomField ID: " + it.idAsLong + "<br/>"
        print += "Type: " + it.getCustomFieldType().toString() + "<br/>"
        print += "Name: " + it.name + "<br/>"
 
        def field = fieldMgr.getFieldByCustomField(it.idAsLong)
        if(field != null){
            print += "Oh Yeah! It's correct! <br/>"
        }
        else{
            print += "This customfield doesn't exist in Profields <br/>"
 
            ManagedConfigurationItem managedCreatedCustomField = managedConfigurationItemService.getManagedCustomField(it);
            managedConfigurationItemService.removeManagedConfigurationItem(managedCreatedCustomField)
            customFieldManager.removeCustomField(it)
 
            print += "Deleted!! <br/>"
        }
 
        print += "<br/><br/>"
    }
}
 
print

Problem

...

: The Jira custom fields related to Projectrak cannot be edited or deleted as they are currently locked.

Solution

...

: All Jira custom fields that belong to Projectrak are traversed to unlock.

...

titleScript
Code Block
languagegroovy
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.config.managedconfiguration.ConfigurationItemAccessLevel
import com.atlassian.jira.config.managedconfiguration.ManagedConfigurationItem
import com.atlassian.jira.config.managedconfiguration.ManagedConfigurationItemBuilder
import com.atlassian.jira.config.managedconfiguration.ManagedConfigurationItemService
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.fields.CustomField
import com.deiser.jira.profields.ao.v2.field.FieldMgr
 
 
String PLUGIN_NAME = "com.deiser.jira.profields";
 
CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager()
ManagedConfigurationItemService managedConfigurationItemService = ComponentAccessor.getComponent(ManagedConfigurationItemService.class);
 
def objects = customFieldManager.getCustomFieldObjects()
print = "*** CustomFields of Profields *** <br/>"
 
objects.each {
    def customType = it.getCustomFieldType().toString()
    if (customType.contains(PLUGIN_NAME)) {
 
        print += "CustomField ID: " + it.idAsLong + "<br/>"
        print += "Type: " + it.getCustomFieldType().toString() + "<br/>"
        print += "Name: " + it.name + "<br/>"
 
        ManagedConfigurationItem managedCreatedCustomField = managedConfigurationItemService.getManagedCustomField(it);
        ManagedConfigurationItemBuilder managedConfigurationItemBuilder = managedCreatedCustomField.newBuilder();
        ManagedConfigurationItem managedConfigurationItem = managedConfigurationItemBuilder
                .setManaged(false)
                .setConfigurationItemAccessLevel(ConfigurationItemAccessLevel.ADMIN)
                .build();
 
        managedConfigurationItemService.updateManagedConfigurationItem(managedConfigurationItem);
        print += 'Unlock CustomField <br/><br/>'
    }
}
 
print

Problem

...

: We want to regenerate the mapping of all Projectrak fields with the Jira custom fields.

Solution

...

: Traverse the Projectrak fields that have mapping enabled to remove the current Jira custom field and generate a new one.

...

Code Block

...

language

...

groovy
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.CustomFieldManager
import com.deiser.jira.profields.ao.v2.field.FieldMgr
import com.deiser.jira.profields.api.field.FieldService
 
CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager()
FieldMgr fieldMgr = ComponentAccessor.getOSGiComponentInstanceOfType(FieldMgr.class)
FieldService fieldService = ComponentAccessor.getOSGiComponentInstanceOfType(FieldService.class)
 
def println = "Delete ID CustomField: <br/>"
 
fieldMgr.get().each {
    def customFieldId = it.getCustomFieldId()
    if(customFieldId != null){
 
        def customField = customFieldManager.getCustomFieldObject(customFieldId)
        if(customField == null){
            println += "customField(" + it.customFieldId + ") in the field(" + it.ID + "): " + it.name + " "
            it.customFieldId = null
            fieldMgr.save(it)
 
            def field = fieldService.get(it.ID)
            field.customField = true
            fieldService.update(field)
            println += "-> setted new custom field; <br/>"
        }
 
    }
}
 
println
Tip

Gadgets

Problem / Suggestion

Scripts for gadgets

Problem: There are issues with data migrations related to the gadgets, and we want to identify the problematic gadget.

Solution

...

: All users of the instance are being traversed to check the Projectrak gadgets. Both correct and erroneous gadgets are displayed on the screen.

...

Code Block

...

language

...

groovy
import org.apache.commons.codec.binary.Hex
import java.util.stream.*
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.portal.PortalPage
import com.atlassian.jira.portal.PortletConfiguration
import com.atlassian.jira.portal.PortletConfigurationManager
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.bc.portal.PortalPageService
import org.apache.commons.lang.StringUtils
 
def GADGET_ONEFIELD_XML = "profields_gadget_onefield.xml"
def GADGET_SUMMARY_XML = "profields_gadget_summary.xml"
def GADGET_TIMESHEET_XML = "profields_gadget_timesheet.xml"
def GADGET_TWOFIELDS_XML = "profields_gadget_twofields.xml"
 
def GADGETS_PROFIELDS = [GADGET_ONEFIELD_XML, GADGET_SUMMARY_XML, GADGET_TIMESHEET_XML, GADGET_TWOFIELDS_XML]
 
def userManager = ComponentAccessor.getUserManager()
def portalPageService = ComponentAccessor.getOSGiComponentInstanceOfType(PortalPageService.class)
def portletConfigurationManager = ComponentAccessor.getOSGiComponentInstanceOfType(PortletConfigurationManager.class)
 
def errorPortalPage = ""
def printValue = ""
def amountOfGadgetsMigrated = 0
 
def applicationUserCollection = userManager.getAllApplicationUsers()
for(ApplicationUser user: applicationUserCollection){
 
    def portalPageCollection = portalPageService.getOwnedPortalPages(user)
    for(PortalPage portalPage: portalPageCollection){
 
        try {
 
            def listPortletConfiguration = portletConfigurationManager.getByPortalPage(portalPage.getId())
            for (PortletConfiguration portletConfiguration : listPortletConfiguration) {
 
                def uri = portletConfiguration.getOpenSocialSpecUri().getOrNull()
                if (uri != null) {
 
                    GADGETS_PROFIELDS.each {
 
                        if (uri.toString().contains(it)) {
                            printValue += " *************************** <br/>"
                            printValue += " ID: " + portalPage.id
                            printValue += "<br/>"
                            printValue += " UserName: " + user.getName()
                            printValue += "<br/>"
                            printValue += " Dashboard: " + portalPage.getName()
                            printValue += "<br/>"
                            printValue += "Configuration:"
                            printValue += "<br/>"
 
                            def filter = false
                            portletConfiguration.getUserPrefs()
                                    .each {
                                key, value ->
 
                                    if (key == "overrideTitle") {
                                        printValue += "Gadget Name: ${value}"
                                        printValue += "<br/>"
                                    } else if (key == "pqlPickerSource") {
                                        if (value == "filter") {
                                            filter = true
                                            printValue += "Has FILTER"
                                            printValue += "<br/>"
                                        } else if (value == "pql") {
                                            filter = false
                                            printValue += "Has PQL"
                                            printValue += "<br/>"
                                        }
                                    } else if (key == "pqlPickerData") {
                                        if (filter) {
                                            printValue += "ID FILTER: ${value}"
                                            printValue += "<br/>"
                                        } else {
                                            if (value != null) {
                                                value = new String(Hex.decodeHex(value.toCharArray()))
                                            }
                                            printValue += "PQL: ${value}"
                                            printValue += "<br/>"
                                        }
                                    }
 
                            }
 
                            printValue += " *************************** <br/>"
                            amountOfGadgetsMigrated += 1
                        }
                    }
 
                }
            }
        }
        catch(Exception ex){
            printValue += "<br/> -- ERROR --> amount before: " + amountOfGadgetsMigrated + "<br/>"
            errorPortalPage += "<br/> **** **** **** <br/>"
            errorPortalPage += "ID: " + portalPage.id + "/ Name: " + portalPage.name + "/ Owner: " + portalPage.ownerUserName + " <br/>"
            errorPortalPage += "Before: " + amountOfGadgetsMigrated + "<br/>"
            errorPortalPage += "ERROR: " + ex.getMessage() + "<br/> <br/>"
        }
    }
}
 
return printValue + "<br/>" + errorPortalPage

...

Script for Projectrak

...

cache

Problem

...

: Need to enable or disable the Projectrak cache for testing purposes.

Solution

...

: Set the desired value in the Projectrak configuration related to the cache.

...

...

Enable cache:

Code Block
languagegroovy
import com.atlassian.jira.component.ComponentAccessor
import com.deiser.jira.profields.api.configuration.ConfigurationService
import static com.atlassian.jira.component.ComponentAccessor.getOSGiComponentInstanceOfType

def configurationService = ComponentAccessor.getOSGiComponentInstanceOfType(ConfigurationService.class)
configurationService.setCache(true)

...

Disable cache:

Code Block
languagegroovy
import com.atlassian.jira.component.ComponentAccessor
import com.deiser.jira.profields.api.configuration.ConfigurationService
import static com.atlassian.jira.component.ComponentAccessor.getOSGiComponentInstanceOfType

def configurationService = ComponentAccessor.getOSGiComponentInstanceOfType(ConfigurationService.class)
configurationService.setCache(false)

...

Scripts for Projectrak database version

Problem

...

: We want to know the latest Projectrak migration registered in Jira that has been performed. Please note that there may be other types of migrations that are not recorded in Jira.

Solution

...

: Accessing the Jira tables where the migration code is stored.

...

...

titleScript
Code Block
languagegroovy
import com.atlassian.jira.component.ComponentAccessor
import org.apache.commons.lang3.exception.ExceptionUtils
import org.ofbiz.core.entity.ConnectionFactory
import org.ofbiz.core.entity.DelegatorInterface
 
import java.sql.ResultSet
import java.sql.Statement
 
def delegator = ComponentAccessor.getComponentOfType(DelegatorInterface.class)
def helperName = delegator.getGroupHelperName("default")
def connection = ConnectionFactory.getConnection(helperName)
 
def result = ""
try {
 
    def stmt = connection.createStatement()
     
     ResultSet rs = stmt.executeQuery(
            "select propertystring.id, propertystring.propertyvalue " +
            "from propertyentry, propertystring " +
            "where propertyentry.id = propertystring.id " +
            "and propertyentry.property_key = 'AO_00B950_#'"
     );
     
        while (rs.next()) {
            def id = rs.getString("id");
            def value = rs.getString("propertyvalue");
 
            result += "Version of Profields Database : " + value + "<br/>"
        }
 
} catch (def e) {
    result += "<br/> Error: <br/>" + ExceptionUtils.getStackTrace(e)
}
 
return result

...

...

Oracle database

Problem / Suggestion

When making a change in the database properties, we encountered Clob-type columns in the Projectrak tables, which is causing errors in certain functionalities.

Solution

...

Problem: We want to reset the latest Projectrak migration registered in Jira that has been performed. Please note that there may be other types of migrations that are not recorded in Jira.

Solution: Reset the record in the Jira tables where the migration code is stored.

Code Block
languagegroovy
import com.atlassian.jira.component.ComponentAccessor
import org.apache.commons.lang3.exception.ExceptionUtils
import org.ofbiz.core.entity.ConnectionFactory
import org.ofbiz.core.entity.DelegatorInterface
 
import java.sql.ResultSet
import java.sql.Statement
 
def delegator = ComponentAccessor.getComponentOfType(DelegatorInterface.class)
def helperName = delegator.getGroupHelperName("default")
def connection = ConnectionFactory.getConnection(helperName)
 
def result = ""
try {
    def NEW_VERSION = 26
 
    def stmt = connection.createStatement()
    def isOk = stmt.execute(
            "update propertystring " +
                     "set propertyvalue = ('"+ NEW_VERSION +"') " +
                    "where id = ( " +
                        "select propertystring.id " +
                            " from propertyentry, propertystring " +
                            " where propertyentry.id = propertystring.id " +
                        "     and propertyentry.property_key = 'AO_00B950_#'" +
                    ")"
    )
 
    if(isOk != true){
        result += " Update the database version of Profields to (" + NEW_VERSION + ")"
    }
    else{
        result += " Error: Can not update the database version of Profields to (" + NEW_VERSION + ")"
    }
 
} catch (def e) {
    result += "<br/> Error: <br/>" + ExceptionUtils.getStackTrace(e)
}
 
return result

Scripts for Oracle database

Problem: When making a change in the database properties, we encountered Clob-type columns in the Projectrak tables, which is causing errors in certain functionalities.

Solution: Execute three scripts to modify the column type. This involves creating a new column for data dumping and subsequently deleting the erroneous column. No information is lost during the execution of the scripts.

...

Project Values (Table: AO_00B950_AOPROJECT_VALUE

...

)

Code Block
languagegroovy
import com.atlassian.jira.component.ComponentAccessor
import org.apache.commons.lang3.StringUtils
import org.apache.commons.lang3.exception.ExceptionUtils
import org.ofbiz.core.entity.ConnectionFactory
import org.ofbiz.core.entity.DelegatorInterface
import java.sql.Statement
 
def delegator = ComponentAccessor.getComponentOfType(DelegatorInterface.class)
def helperName = delegator.getGroupHelperName("default")
def connection = ConnectionFactory.getConnection(helperName)
 
def arrayColumns = ["DISPLAY", "LOWER_DISPLAY", "VALUE", "LOWER_VALUE", "PARENT_VALUE"] as String[]
 
def result = ""
try {
    def statement = connection.createStatement()
 
    arrayColumns.each {
 
    statement.execute("alter table AO_00B950_AOPROJECT_VALUE add " + it +"2 clob")
        statement.execute("update AO_00B950_AOPROJECT_VALUE set " + it +"2 = " + it)
        statement.execute("update AO_00B950_AOPROJECT_VALUE set " + it +" = null")
        statement.execute("alter table AO_00B950_AOPROJECT_VALUE drop column " + it)
 
        statement.execute("alter table AO_00B950_AOPROJECT_VALUE add " + it +" varchar2(4000)")
        statement.execute("update AO_00B950_AOPROJECT_VALUE set " + it +" = " + it +"2")
        statement.execute("alter table AO_00B950_AOPROJECT_VALUE drop column " + it +"2")
 
        result += "Completed " + it + "! <br/>"
 
    }
     
 
} catch (def e) {
    result += "<br/> Error: <br/>" + ExceptionUtils.getStackTrace(e)
}
 
return result

...

Historical (Table: AO_00B950_AOHISTORICAL_FIELD

...

)

Code Block
languagegroovy
import com.atlassian.jira.component.ComponentAccessor
import org.apache.commons.lang3.StringUtils
import org.apache.commons.lang3.exception.ExceptionUtils
import org.ofbiz.core.entity.ConnectionFactory
import org.ofbiz.core.entity.DelegatorInterface
import java.sql.Statement
 
def delegator = ComponentAccessor.getComponentOfType(DelegatorInterface.class)
def helperName = delegator.getGroupHelperName("default")
def connection = ConnectionFactory.getConnection(helperName)
 
def arrayColumns = ["NEW_VALUE", "OLD_VALUE", "NEW_DISPLAY", "OLD_DISPLAY"] as String[]
 
 
def result = ""
try {
    def statement = connection.createStatement()
 
    arrayColumns.each {
 
    statement.execute("alter table AO_00B950_AOHISTORICAL_FIELD add " + it +"2 clob")
        statement.execute("update AO_00B950_AOHISTORICAL_FIELD set " + it +"2 = " + it)
        statement.execute("update AO_00B950_AOHISTORICAL_FIELD set " + it +" = null")
        statement.execute("alter table AO_00B950_AOHISTORICAL_FIELD drop column " + it)
 
        statement.execute("alter table AO_00B950_AOHISTORICAL_FIELD add " + it +" varchar2(4000)")
        statement.execute("update AO_00B950_AOHISTORICAL_FIELD set " + it +" = " + it +"2")
        statement.execute("alter table AO_00B950_AOHISTORICAL_FIELD drop column " + it +"2")
 
        result += "Completed " + it + "! <br/>"
 
    }
     
 
} catch (def e) {
    result += "<br/>Error:<br/>" + ExceptionUtils.getStackTrace(e)
}
 
return result

...

...

List items (Table: AO_00B950_AOLIST_ITEM

...

)

Code Block
languagegroovy
import com.atlassian.jira.component.ComponentAccessor
import org.apache.commons.lang3.StringUtils
import org.apache.commons.lang3.exception.ExceptionUtils
import org.ofbiz.core.entity.ConnectionFactory
import org.ofbiz.core.entity.DelegatorInterface
import java.sql.Statement
 
def delegator = ComponentAccessor.getComponentOfType(DelegatorInterface.class)
def helperName = delegator.getGroupHelperName("default")
def connection = ConnectionFactory.getConnection(helperName)
 
def arrayColumns = ["DATA", "LOWER_DATA"] as String[]
 
 
def result = ""
try {
    def statement = connection.createStatement()
 
    statement.execute("alter table AO_00B950_AOLIST_ITEM modify DATA null")
 
    arrayColumns.each {
 
        statement.execute("alter table AO_00B950_AOLIST_ITEM add " + it +"2 clob")
        statement.execute("update AO_00B950_AOLIST_ITEM set " + it +"2 = " + it)
        statement.execute("update AO_00B950_AOLIST_ITEM set " + it +" = null")
        statement.execute("alter table AO_00B950_AOLIST_ITEM drop column " + it)
 
        statement.execute("alter table AO_00B950_AOLIST_ITEM add " + it +" varchar2(4000)")
        statement.execute("update AO_00B950_AOLIST_ITEM set " + it +" = " + it +"2")
        statement.execute("alter table AO_00B950_AOLIST_ITEM drop column " + it +"2")
 
        result += "Completed " + it + "! <br/>"
 
    }
     
     statement.execute("alter table AO_00B950_AOLIST_ITEM modify DATA not null")
 
} catch (def e) {
    result += "<br/>Error:<br/>" + ExceptionUtils.getStackTrace(e)
}
 
return result

Filter by label (Content by label)
showLabelsfalse
max5
spacescom.atlassian.confluence.content.render.xhtml.model.resource.identifiers.SpaceResourceIdentifier@140ea
showSpacefalse
sortmodified
reversetrue
typepage
cqllabel in ( "script" , "profields" , "scriptrunner" , "exporter" ) and type = "page" and space = "SKB"
labelsprofields script scriptrunner


Page Properties
hiddentrue

Related issues