Speakeasy lets you to write a special, simple type of Atlassian Plugin, called an extension. The extension can then be shared with others by allowing them to opt-in, or enable, the extension on a person-by-person basis. This dramatically reduces the risk to server operation and other users, while giving you an opportunity to try out our plugin idea with production data and use it on a daily basis.
Extensions are currently limited to all things client-side:
As such, you can do things like:
Even better, take a look at the Speakeasy Extension Examples for ideas.
Great, but what can they NOT do? Since no server-side code is allowed, your extension cannot:
You can work around the last two limitations if you get creative. In the first case, you can hijack an existing URL and pass additional data via query parameters or anchors, then rewriting the DOM. For the second, if the remote server is public, you can use YQL to access that server and repackage the data into JSONP.
To create your extension, you have two options: fork an existing plugin or start from scratch. For Codegeist all extensions will start from scratch.
To try out your idea, before getting into extension development and packaging, you may find it useful to use Firefox with the Firebug extension. With this, you can try out JavaScript scripts using its multi-line editor, running them on the current page. Once you work out all the kinks, you can follow these instructions to package it as a Speakeasy extension.
When starting from scratch, you can use the extension wizard to create a new conventions-based, or "zip", extension:
Install the Speakeasy plugin on an instance of Confluence or JIRA
Navigate to your Speakeasy page, by clicking "Extensions" in the username dropdown:
You may see a warning saying 'No one has access to this page. Click here to change these settings.' You'll need to click on the link to configure access for your extensions first before proceeding.
3. To start creating your Extension, click on the + Install button, and then click on the "use the wizard" link
Fill in the key, name, and description of your new extension
Click "submit" and you'll see your new extension in the list
Click on "Enable" and refresh the page to see the banner your new extension is displaying
If you click on "Edit", you'll see this plugin have the following files, assuming your extension key is 'myext':
File | Description |
---|---|
atlassian-extension.json | The manifest for the extension. The only required attributes are "key" and "version". Required |
screenshot.png | The screenshot to show in the extension list. Referred to in the descriptor. Must be a 175 x 80 pixel gif. |
js/myext/main.js | The JavaScript module to execute on the default context of "atl.general". The example puts a banner on the top of every non-admin page with an image. |
css/main.css | The CSS file to be displayed whenever the JavaScript is executed. The example hides the second banner that says "Bye". |
images/projectavatar.png | An image file that is referenced in the JavaScript |
ui/web-items.json | A list of web items. The example is one that puts a "Yahoo" link on the Speakeasy page |
If you see an extension you like and want to tweak, or just want to do something similar, you may want to fork an existing extension following these steps:
Once you get the hang of it, you can create your own extension.
To develop your extension, you have two options: the web IDE or offline.
Speakeasy has a very simple text editor built into the plugin. With it, you can edit your extension right in the browser, with each save causing the plugin to be rebuilt and upgraded into the product. However, with this editor, you will be unable to delete, add, or rename files.
If you need to make several changes and don't want to interrupt your users, you can write your extension with just a text editor and a zip tool. These instructions assume working from the terminal, though GUI tools could be used as well.
Find your extension on the Speakeasy page and click "Download"
Click the "Download" link to download the artifact
Unzip the artifact, whether it is a jar or zip, into a new directory
With your text editor, start working on your extension
When ready to test, run:
1 2zip -r ../my-extension.zip *
Make sure you preserve your file extension
Visit the Speakeasy page in your target application and upload your newly created extension zip or jar.
Be aware that any upgrades or live edits to your extension will immediately be seen by all those that enabled your extension.
If you want to use IDEA and have created a zip extension that doesn't include a "Download as SDK Project" link, you can still use IDEA to modify your extension:
New in 0.10, you can interact with your extension via git as Speakeasy acts as a git server for each extension. Operations include:
git clone
- Checkout a copy of the extension repository ready for deploymentgit pull
- Keep up-to-date with deployed versions as well as bring in changes from forking extensionsgit push
- Deploy extension changes from the command-lineDuring development on a remote server, git push
can be a useful technique for rapidly deploying locally-developed changes:
1 2git commit -a -m "Local changes" && git push
This sections covered a few advanced topics you may encounter in Speakeasy development.
Web items are defined in the file ui/web-items.json
as a list of web item objects. The following properties are allowed on web item objects:
Name | Description |
---|---|
section | Where the web item will show up. Required |
label | The text to show for the web item. Required |
url | The URL for the link to point to. Required |
cssId | The CSS ID of the link anchor (0.12.2 or later) |
cssClass | The CSS class or set of classes for the link anchor |
weight | The weight of the web item |
tooltip | The text to show for the tool tip, if applicable |
icon | The object describing the icon. Valid properties are:
|
This feature is new in version 0.9
You can reference an image in your CSS by using a special token that will be replaced at runtime: @imagesPrefix. For example, you could refer to a background image in your CSS like this:
1 2#foo { background-image: url(@imagesPrefix/myimage.png); }
Your first plugin created with the extension wizard contains a JavaScript file that is loaded as a CommonJS module. CommonJS is a set of standards for JavaScript, of which their Module spec is the one used here. A module fundamentally can import other modules and selectively expose properties or functions to other modules. Each module is loaded in its own scope, so you don't need to worry about global variable clashes.
Entry point modules will need to tell Speakeasy on which page they should be included. This is done via the "@context" annotation in the top JavaDoc-style comment on the module. For example:
1 2/** * @context dashboard */ ... module code here ...
This example is configured to have its module loaded when on the Confluence dashboard page. These contexts are also known as 'web resource contexts', as they are the same used for scoping web resources such as JavaScript and CSS for normal Atlassian plugins. For more information about contexts, see the following links:
Most extensions will interact with CommonJS through consuming other modules, usually ones provided by Speakeasy. The "CommonJS Modules" tab shows those and all other public modules available for consumption.
Modules can be consumed via the "require(moduleId)" syntax. Here is an example of consuming the 'speakeasy/jquery' module, which exports the 'jQuery' property:
1 2var $ = require('speakeasy/jquery').jQuery;
Module ids can be specified as absolute ids or relative to your current module. Therefore, if you had a module 'myext/util' that you wanted to consume from 'myext/main', you could consume it via:
1 2var util = require('./util');
The right modules will be automatically delivered to your page via the Speakeasy plugin. Therefore, you don't need to worry whether the module is in your extension or another extension, although the end user will need to have enabled the dependent extension in order to use yours.
Modules are private to your extension by default. If you want to create a 'myext/util', just create a js/myext/util.js file in your extension and consume it from another module as described above. The goal is to make it really easy to decompose your extension into modules, making it easier to maintain and eventually share with others.
If you want to provide your module to other extensions, you can do so by adding the "@public" annotation to your module's JsDoc (the JavaDoc-like comment at the top):
1 2/** * This is a public module * @public */ exports.foo = "Foo";
This module will now show up in the "CommonJS Modules" tab and be available for other extensions to import.
It is recommended that any hack you have to do to retrieve data from the application or manipulate the page be extracted into their own modules. This way, the extension is easier to maintain or tweak by forkers as the ugly bits aren't mixed in with the extension logic, and if you decide to share your hack, you already have a shareable module ready to annotate.
If your extension is a "jar" extension, so in other words, a normal OSGi plugin that has an atlassian-plugin.xml, you can use the Plugin SDK to develop your extension.
Click on the "Download" link by your extension and click "Download as SDK Project"
Unzip the SDK Project into a new directory
In a terminal window, run:
1 2mvn confluence:run
Note, replace "confluence" with the desired target product.
In another terminal window, run:
1 2mvn confluence:cli
Again, replace "confluence" with the desired target product.
Navigate to localhost:1990/confluence your browser. See the Plugin SDK docs for other product URLs.
Login with a username of "admin" and password of "admin"
Move your cursor to your username on the top banner and click "Speakeasy". You should see your extension.
In your text editor, open files src/main/resources and start working on your extension. Most changes should be visible immediately, but others will require you to switch to your terminal window running the cli and enter 'pi'
If your extension is a "zip" extension, you can still use the Plugin SDK to develop your extension, albeit in a more limited fashion:
Click on the "Download" link by your extension and click "Download"
Unzip the artifact into a new directory
Run atlas-run-standalone to start the Atlassian product. In this example, we start Confluence 3.5.3:
1 2atlas-run-standalone --product confluence --version 3.5.2 --jvmargs "-Dplugin.resource.directories=. -Xmx512m -XX:MaxPermSize=128m" --plugins com.atlassian.labs:speakeasy-plugin:0.10.2
Upload or push your extension to your local Confluence running at localhost:1990/confluence and start developing. JS and CSS changes that don't require a plugin reinstallation can be seen immediately via a reload (thanks to that 'plugin.resource.directories' system prop)
A Speakeasy extension, at a technical level, is an OSGi plugin that contains only Speakeasy-provided dynamic module descriptors. These include:
The custom module types are necessary to ensure the plugin can be enabled on a per-user basis. Any other module descriptors, or even files considered not appropriate for such a plugin, will result in extension installation rejection. Inappropriate files include JAR files, Java class files, and Spring XML descriptor files. Since Speakeasy extensions are real plugins, they can be viewed, managed, and uninstalled from the Plugin Management UI in each product.
Keep in mind the idea is the application should work 100% correctly if your extension is not enabled. This means you shouldn't do things like create psuedo Confluence macros that show useful information for your extension users but blank screens or gibberish for all others.
Rate this page: