Javascript not loading in Issue search

Hey community,

I’ve made a plugin that displays some information inside an issuetabpanel. To act upon that info, I’ve added some javascript that manipulates and extracts data from the dom and passes it to a rest module with ajax.on a normal issue page this works fine (e.g. protocol://host/browse/ISS-1). However, whenever I view issues from search results (e.g. e.g. protocol://host/ISS-1?jql=), the javascript does not get loaded (checked this from the network tab in my browsers dev tools).

The files have been declared as web resources in the atlassian-plugin.xml, they are required in the velocity template through the web resource manager and the js functions are triggerd to execute only after page load.
On a whim I tried disabling batched mode, but that did not resolve the problem.
Any suggestions?

CodeSnippets:

atlassian-plugin.xml

<web-resource key="jira-upsource-connectivity-plugin-resources-general" name="jira-upsource-connectivity-plugin General Web Resources">
    <dependency>com.atlassian.auiplugin:ajs</dependency>
    <dependency>com.atlassian.auiplugin:dialog2</dependency>
    <resource type="download" name="GeneralScript.js" location="/js/GeneralScript.js"/>
    <resource type="download" name="jira-upsource-connectivity-plugin.css" location="/css/jira-upsource-connectivity-plugin.css"/>
    <resource type="download" name="images/" location="/images"/>
    <context>jira-upsource-connectivity-plugin-general</context>
    <!--<context>atl.general</context>-->
  </web-resource>
  <web-resource key="jira-upsource-connectivity-plugin-resources-issuetab" name="jira-upsource-connectivity-plugin IssueTab Web Resources">
    <resource type="download" name="IssueTabPanelScript.js" location="/js/IssueTabPanelScript.js"/>
    <context>jira-upsource-connectivity-plugin-issuetab</context>
  </web-resource>
  <web-resource key="jira-upsource-connectivity-plugin-resources-accordionfold" name="jira-upsource-connectivity-plugin AccordionFold Web Resources">
    <resource type="download" name="AccordionFoldScript.js" location="/js/AccordionFoldScript.js"/>
    <context>jira-upsource-connectivity-plugin-accordionfold</context>
  </web-resource>

Velocity templage

$webResourceManager.requireResourcesForContext("jira-upsource-connectivity-plugin-general")
$webResourceManager.requireResourcesForContext("jira-upsource-connectivity-plugin-issuetab")
<div class="mod-header">
    <h3>Code Reviews</h3>
    #if( !$exceptionList.isEmpty() )
        <h3>Error</h3>
        #foreach( $exception in $exceptionList )
            <p>$exception.toString()</p>
        #end
    #end 
    #if($reviewsDTO.reviews.isEmpty())
        <p>No relevant reviews available.</p>
    #else
    #foreach( $review in $reviewsDTO.reviews )
    #set( $count = $velocityCount - 1)
        <button class="accordion">
            <h4>$review.reviewId.reviewId</h4>
        </button>
        <div class="panel">
        <p>State: #if($review.state == 1)
            Open
            #else
            Closed
            #end
        </p>
        <br/>
        #if($review.state == 1)
            <button class="aui-button accshowbutton" id="Acc_dialog_show_button_$count">Accept</button>
            <button class="aui-button decshowbutton" id="Dec_dialog_show_button_$count">Raise Concern</button>
            <section role="dialog" id="Acc_dialog_$count" class="aui-layer aui-dialog2 aui-dialog2-medium" aria-hidden="true">
                <!-- Dialog header -->
                <header class="aui-dialog2-header">
                    <!-- The dialog's title -->
                    <h2 class="aui-dialog2-header-main">Authorize action: <div id="actiontype_$count"></div></h2>
                    <!-- Actions to render on the right of the header -->
                    <div class="aui-dialog2-header-secondary">
                    </div>
                    <!-- Close icon -->
                    <a class="aui-dialog2-header-close">
                        <span class="aui-icon aui-icon-small aui-iconfont-close-dialog">Close</span>
                    </a>
                </header>
                <!-- Main dialog content -->
                <div class="aui-dialog2-content">
                <form id="Acc_input_${count}" class="aui acc">
                      <div class="field-group">
                        <label for="Acc_user_$count">${i18n.getText("upsource-connectivity-panel.username.label")}</label>
                        <input type="text" id="Acc_user_$count" name="Acc_user_$count" class="text">
                      </div>
					  <div class="field-group">
                        <label for="Acc_pass_$count">${i18n.getText("upsource-connectivity-panel.password.label")}</label>
                        <input type="password" id="Acc_pass_$count" name="Acc_pass_$count" class="text">
                      </div>
                      <div class="field-group">
					    <input type="hidden" value="$review.reviewId.reviewId" id="review_$count">
						<input type="hidden" value="$review.reviewId.projectId" id="upsource_project_$count">
						<input type="hidden" value="$action.issue.getProjectObject().getKey()" id="jira_project_$count">
						<input type="hidden" id="state_$count">
                        <input type="submit" value="Authorize" class="button">
                      </div>      
                </form>
                </div>
                <!-- Dialog footer -->
                <footer class="aui-dialog2-footer">
                    <!-- Actions to render on the right of the footer -->
                    <div class="aui-dialog2-footer-actions">
                        <button id="Acc_dialog_close_button_$count" class="aui-button aui-button-link closebutton">Close</button>
                    </div>
                    <!-- Hint text is rendered on the left of the footer -->
                    <div class="aui-dialog2-footer-hint"></div>
                </footer>
            </section>
        #end

        <br/>
        <p><a href="${review.additionalProperties.get("link")}">Go to review ${review.reviewId.reviewId}</a>
        </p>
        <br/>
        <button class="accordion">
            <h4>Comments</h4>
        </button>
        <div class="panel">
        #if( $review.additionalProperties.get("commentContainers").getFeedItems() )        
            #foreach( $item in $review.additionalProperties.get("commentContainers").getFeedItems() )
            <table class="aui">
                <tbody>
                #foreach( $comment in $item.discussion.comments )
                <tr><td><p>$comment.text</p><h5>$!{comment.additionalProperties.get("timestamp")} - $!{comment.additionalProperties.get("author").name}</h5></td></tr>
                <!-- <tr><td>$comment.date - $comment.authorId</td></tr> -->
                #end
                </tbody>
            </table>
            #end
        #else
        <p>No comments on this review.</p>
        #end
        </div>
        <br/>
        </div>
    #end
    #end
</div>

IssueTabPanelScript.js & GeneralScript.js


(function ($) {
    console.log(">>> UPSOURCE CONNECTIVITY PLUGIN ISSUE TAB PANEL JS LOADED <<<");
    $(document).ready(function () { 
        actionButtons();
    });
    // Set triggers for accordion folds on IssueTabPanel; Needs separate ready command
    JIRA.ViewIssueTabs.onTabReady(function () {
        accordion();
    });

}
)(AJS.$ || jQuery);

function accordion() {
    var acc = document.getElementsByClassName("accordion");
    var i;

    for (i = 0; i < acc.length; i++) {
        acc[i].onclick = function () {
            this.classList.toggle("active");
            var panel = this.nextElementSibling;
            if (panel.style.display === "block") {
                panel.style.display = "none";
            } else {
                panel.style.display = "block";
            }
        }
    }
}

function actionButtons() {
    AJS.$(".accshowbutton").click(function () {
        var count = detectCount(this);
        AJS.$("#state_"+count).val(3);
        AJS.$("#actiontype_"+count).text("Accept");
        AJS.dialog2("#Acc_dialog_" + count).show();
    });
    
    AJS.$(".decshowbutton").click(function () {
        var count = detectCount(this);
        AJS.$("#state_"+count).val(4);
        AJS.$("#actiontype_"+count).text("Raise Concern");
        AJS.dialog2("#Acc_dialog_" + count).show();
    });
    
    AJS.$(".acc").submit(function(e){
        var count = detectCount(this);
        e.preventDefault();
        authorizeAction(count)
    });

    AJS.$(".closebutton").click(function (e) {
        var count = detectCount(this);
        e.preventDefault();
        AJS.dialog2("#Acc_dialog_" + count).hide();
    });
}

function authorizeAction(i){
    var data = getData(i);
    if(typeof data === 'string'){
        AJS.$.ajax({
            url:AJS.contextPath() + "/rest/upsourceAction/1.0/reviewStateChange/state",
            type:"POST",
            contentType: "application/json",
            data:data,
            processData:false
        })
                .done(function(){
                    console.log("succes");
                    JIRA.Messages.showSuccessMsg('action succesful');
                    AJS.dialog2("#Acc_dialog_" + i).hide();
                })
                .fail(function (xhr, ajaxOptions, thrownError) {
                    console.log("failure");
                    if(xhr.status == 401){
                        JIRA.Messages.showErrorMsg("Authorization failed.");
                    }else{
                        JIRA.Messages.showErrorMsg('action failed, please try again. If this problem persists, contact an administrator');
                    }
                    console.log(xhr.responseText);
                });
    }else{
        data.forEach(function(element){
            JIRA.Messages.showErrorMsg(element);
        });
    }
}

function getData(i){
    var err = [];
     var user = AJS.$("#Acc_user_" + i).val();
    if(!user){
        err.push("Please fill in user");
    }
    var pass = AJS.$("#Acc_pass_" + i).val();
    if(!pass){
        err.push("Please fill in password")
    }
    var state = AJS.$("#state_" + i).val();
    if(err.length != 0){
        return err;
    }
    var payload =
            '{"jiraProjectId":"' + AJS.$("#jira_project_"+i).val() 
            + '","upsourceProjectId":"'+ AJS.$("#upsource_project_"+i).val()
            + '","reviewId":"' + AJS.$("#review_"+i).val()
            + '","user":"' + user
            + '","password":"' + pass
            + '","state":' + state + '}';
    return payload;
}


console.log(">>> UPSOURCE CONNECTIVITY PLUGIN GENERAL JS LOADED <<<")

function detectCount(object) {
    var id = object.id;
    var regex = new RegExp('_\\d+$');
    var match = id.search(regex);
    var count;
    if (match > 0) {
        count = id.substring(match + 1);
    }
    return count;
}

Did you validate that the files are actually not loaded? Maybe the files are loaded and it’s your javascript function that’s not triggered. Also, my script are loaded in the search view but I use the context:

<context>atl.general</context>

Did you try with this context to see if it loads?

1 Like

I’m pretty sure that the file isn’t loaded.

  1. It works as intended and without reporting error on other pages (the issue detail page).
  2. I’ve included a console.log with a document onReady (in the atlassian recommended way). It does not display the message, nor reports errors in the log.

Including the files in the atl.general context does cause them to load, but this is not a clean solution.

  1. There are conflicts in some of the code depending on which page is loaded, breaking the script
  2. Scripts shouldnt just be added to the general context, seeing as they’d be loaded (and possibly executed) in the whole application, rather than just the locations they’re needed.
    This could provide a workaround, but I’d much rather find a way to do this properly.

After some more research I found this piece of documentation [<>]. In short, it explains how certain pages do not load js properly through requireResource, and how this way of loading will be depricated in the future. Indeed, adding resources to atl.general is the advised solution.

This however, still leaves me with conflicting JS. I will open another question regarding this specifically.

@theredwarmaster I try with adding context atl.general but still, it is not working.
Any suggestion will be helpful for me.