# Adding a link item in the pull request overview panel

This tutorial will show how you can add some custom UI elements into the pull request overview. Given an example
\"personal TODO\" plugin, we'll be adding a count of TODOs for a pull request near the JIRA issue and build counts.

The [full source](https://bitbucket.org/atlassian/bitbucket-example-pull-request-ui-plugin) for this tutorial is available 
on Bitbucket as [atlassian/bitbucket-example-pull-request-ui-plugin](https://bitbucket.org/atlassian/bitbucket-example-pull-request-ui-plugin).

## Discovering web fragment locations

The first step is to figure out where the available spots are to place your web fragment. This can be done by appending
`?web.items&web.panels&web.sections` to any URL to display the locations. Visit the
[Web Fragments documentation](../reference/web-fragments.html) for more information.

<a class="fancybox" href="../images/pull-request-overview-panels.png">
    <img alt="Pull Request Overview panel locations" src="../images/pull-request-overview-panels.png" width="400" />
</a>

The one we want for this tutorial is `bitbucket.pull-request.related-entities`. You'll notice that it accepts Client Web
Panels. That means two things:

* As a _panel_ location, it allows you to add arbitrary HTML to the page in that location.
* As a _client_ panel location, it will render your HTML in the user's browser, rather than on the server.

You'll also see that a `pull-request` object is provided to you to help you do your rendering.

## Plugin descriptor - atlassian-plugin.xml

Great, so we know what we're doing! Let's get started then creating a Client Web Panel.

Here is the `atlassian-plugin.xml` for our panel:

```xml
<atlassian-plugin key="\${project.groupId}.\${project.artifactId}" name="\${project.name}" plugins-version="2">
    <plugin-info>
        <description>\${project.description}</description>
        <version>\${project.version}</version>
        <vendor name="\${project.organization.name}" url="\${project.organization.url}" />
    </plugin-info>
    
    <client-resource key="pr-overview-resources" name="Pull Request Overview Resources">
        <directory location="/css/" />
        <directory location="/js/" />
        <directory location="/soy/" />
        
        <dependency>com.atlassian.auiplugin:ajs</dependency>
        <dependency>com.atlassian.bitbucket.server.bitbucket-web-api:pull-request-web-panels</dependency>
    </client-resource>

    <client-web-panel name="PR TODOs link" key="pr-overview-todo-panel" location="bitbucket.pull-request.related-entities" weight="2000">
        <client-context-provider>MyCompany.TODO.getTODOStats</client-context-provider>
        <resource name="view" type="soy" location="com.atlassian.bitbucket.server.bitbucket-example-pull-request-plugin:pr-overview-resources/com.mycompany.todo.prOverviewPanel" />
        <dependency>com.atlassian.bitbucket.server.bitbucket-example-pull-request-plugin:pr-overview-resources</dependency>
    </client-web-panel>

</atlassian-plugin>
```

You might notice some bits like `\${project.artifactId}` in this file. These are variables that are being populated
automatically from values in our `pom.xml` file so that we can be certain the pom and plugin are in sync. The
only variables you need to be concerned with for this tutorial are:

- `\${project.groupId}` == `"com.atlassian.bitbucket.server"`
- `\${project.artifactId}` == `"bitbucket-example-pull-request-plugin"`

These two are joined to generate the plugin's key - "com.atlassian.bitbucket.server.bitbucket-example-pull-request-plugin"

The plugin elements:

* The `<plugin-info />` describes the plugin as a whole.
* The `<client-resource />` describes the JS, LESS/CSS, and client-side Soy templates we'll need for our Client Web Panel.
    This particular resource encapsulates a JS file and a Soy file. You can learn more by checking out the
    [Web Resource Plugin Module](https://developer.atlassian.com/display/DOCS/Web+Resource+Plugin+Module).
* The `<client-web-panel />` is the most interesting part for us. Let's dissect it.

### client-web-panel

```xml
<client-web-panel name="PR TODOs link" key="pr-overview-todo-panel" location="bitbucket.pull-request.related-entities" weight="2000">
```

This line gives a human `name` of \"PR TODOs link\" to our panel, and is known for programmatic purposes as
`pr-overview-todo-panel`. The `location` attribute specifies where the panel will be placed, and we've set it to the
location \"bitbucket.pull-request.related-entities\" that we found earlier.

```xml
<dependency>com.atlassian.bitbucket.server.bitbucket-example-pull-request-plugin:pr-overview-resources</dependency>
```

This line says that whenever the `pr-overview-todo-panel` is shown, also include the resources in our
    "pr-overview-resources" `<client-resource />`

```xml
<client-context-provider>MyCompany.TODO.getTODOStats</client-context-provider>
```

This line references some JavaScript functions that will be used when rendering your web panel. The `<client-context-provider/>`
will transform incoming data into the shape your template needs. This function is defined in a JavaScript file in the
`<client-resource/>` dependency, and we'll look at it later.

```xml
<resource name="view" type="soy" location="com.atlassian.bitbucket.server.bitbucket-example-pull-request-plugin:pr-overview-resources/com.mycompany.todo.prOverviewPanel" />
```

Every Client Web Panel requires a view template that produces the HTML. Here we specify a `<resource />` with the name \"view\".
We specify that our resource is a Soy (Closure Templates) template with `type="soy"`.

Then we provide the location of the Soy template. You can see we're referencing our `<client-resource />` element via its
fully qualified name (plugin key; colon; module key) in the location property:
`com.atlassian.bitbucket.server.bitbucket-example-pull-request-plugin:pr-overview-resources`. The second half of the 
location parameter is the name of the Soy template to use within that resource. Let's write that template now.

## The Soy template (Closure Template) - pull-request-overview.soy

```soy
{namespace com.mycompany.todo}

/**
 * @param count The count of TODOs in this PR.
 */
{template .prOverviewPanel}
    {call bitbucket.feature.pullRequest.relatedEntitiesWebPanel}
        {param linkText: $count == 1 ? 'TODO' : 'TODOs' /}
        {param linkHref: '#' /}
        {param iconClass: 'todo-icon' /}
        {param count: $count /}
        {param extraClasses: 'mycompany-todos-link'  /}
    {/call}
{/template}
```

This Soy file defines the `prOverviewPanel` template in the `com.mycompany.todo` namespace. This template takes in a 
single parameter `count`, and is very simple - it calls a Bitbucket Server template that will create the standard markup 
for the location we want. Most locations won't have a standard template like this and you will simply generate your own 
HTML. Read more about Soy at [Closure Templates](https://developers.google.com/closure/templates/docs/overview). 
Your Bitbucket Server version may not always be on the latest version of Closure Templates, so you'll find the most 
accurate documentation in the [Internet Archive](http://web.archive.org/web/20121018175803/https://developers.google.com/closure/templates/docs/overview).

## The JavaScript - pull-request-overview.js

```javascript
(function($) {
    // Set up our namespace
    window.MyCompany = window.MyCompany || {};
    MyCompany.TODO = MyCompany.TODO || {};

    var storage = {
        getTODOs : function(pullRequestJson) {
            // ...
            return [];
        },
        putTODOs : function(pullRequestJson, todos) {
            // put(storageKey(pullRequestJson), JSON.stringify(todos));
        }
    };

    /**
     * The client-condition function takes in the context
     * before it is transformed by the client-context-provider.
     * If it returns a truthy value, the panel will be displayed.
     */
    function hasAnyTODOs(context) {
        var todos = storage.getTODOs(context['pullRequest']);
        return todos.length;
    }

    /**
     * The client-context-provider function takes in context and transforms
     * it to match the shape our template requires.
     */
    function getTODOStats(context) {
        var todos = storage.getTODOs(context['pullRequest']);
        return {
            count : todos.length
        };
    }

    /* Expose the client-context-provider function */
    MyCompany.TODO.getTODOStats = getTODOStats;

    /* use a live event to handle the link being clicked. */
    $(document).on('click', '.mycompany-todos-link', function(e) {
        e.preventDefault();

        // open a dialog to show the TODO details.
    });
}(AJS.$));
```

This file defines the implementation for our `<client-context-provider/>` by creating the globally accessible JavaScript
function that we reference in our XML: `MyCompany.TODO.getTODOStats`.

It also adds a live event listener to the document for any clicks on our \".mycompany-todos-link\".

It uses localStorage to hold the user's TODOs.
