You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by an...@apache.org on 2013/08/29 07:23:11 UTC
[10/12] git commit: updated refs/heads/4.2 to abd3461
CLOUDSTACK-883. DOC. How to integrate your 3rd-party plugin with the UI. Contains brief third-party plugin code tutorial.
(cherry picked from commit 5036e4f9f9b3b06a1268657c6c0fcc1e1ec5e83a)
Signed-off-by: animesh <an...@apache.org>
Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/00d427d4
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/00d427d4
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/00d427d4
Branch: refs/heads/4.2
Commit: 00d427d4625d2f246a8b9b40c3ad325e8c18c14b
Parents: 301c82f
Author: Jessica <je...@citrix.com>
Authored: Wed Aug 28 17:01:30 2013 -0700
Committer: animesh <an...@apache.org>
Committed: Wed Aug 28 22:15:16 2013 -0700
----------------------------------------------------------------------
docs/en-US/Developers_Guide.xml | 1 +
docs/en-US/images/plugin1.jpg | Bin 0 -> 32999 bytes
docs/en-US/images/plugin2.jpg | Bin 0 -> 35149 bytes
docs/en-US/images/plugin3.jpg | Bin 0 -> 41983 bytes
docs/en-US/images/plugin4.jpg | Bin 0 -> 32125 bytes
docs/en-US/images/plugin_intro.jpg | Bin 0 -> 22247 bytes
docs/en-US/third-party-ui-plugin.xml | 347 ++++++++++++++++++++++++++++++
7 files changed, 348 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/00d427d4/docs/en-US/Developers_Guide.xml
----------------------------------------------------------------------
diff --git a/docs/en-US/Developers_Guide.xml b/docs/en-US/Developers_Guide.xml
index e6fb5ce..7452e29 100644
--- a/docs/en-US/Developers_Guide.xml
+++ b/docs/en-US/Developers_Guide.xml
@@ -51,6 +51,7 @@
<xi:include href="api-calls.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="working-with-usage-data.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="storage-plugins.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="third-party-ui-plugin.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="working-with-documentation.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="tools.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="event-types.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/00d427d4/docs/en-US/images/plugin1.jpg
----------------------------------------------------------------------
diff --git a/docs/en-US/images/plugin1.jpg b/docs/en-US/images/plugin1.jpg
new file mode 100644
index 0000000..970233d
Binary files /dev/null and b/docs/en-US/images/plugin1.jpg differ
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/00d427d4/docs/en-US/images/plugin2.jpg
----------------------------------------------------------------------
diff --git a/docs/en-US/images/plugin2.jpg b/docs/en-US/images/plugin2.jpg
new file mode 100644
index 0000000..9c8a610
Binary files /dev/null and b/docs/en-US/images/plugin2.jpg differ
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/00d427d4/docs/en-US/images/plugin3.jpg
----------------------------------------------------------------------
diff --git a/docs/en-US/images/plugin3.jpg b/docs/en-US/images/plugin3.jpg
new file mode 100644
index 0000000..07fae79
Binary files /dev/null and b/docs/en-US/images/plugin3.jpg differ
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/00d427d4/docs/en-US/images/plugin4.jpg
----------------------------------------------------------------------
diff --git a/docs/en-US/images/plugin4.jpg b/docs/en-US/images/plugin4.jpg
new file mode 100644
index 0000000..2bcec9f
Binary files /dev/null and b/docs/en-US/images/plugin4.jpg differ
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/00d427d4/docs/en-US/images/plugin_intro.jpg
----------------------------------------------------------------------
diff --git a/docs/en-US/images/plugin_intro.jpg b/docs/en-US/images/plugin_intro.jpg
new file mode 100644
index 0000000..113ffb3
Binary files /dev/null and b/docs/en-US/images/plugin_intro.jpg differ
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/00d427d4/docs/en-US/third-party-ui-plugin.xml
----------------------------------------------------------------------
diff --git a/docs/en-US/third-party-ui-plugin.xml b/docs/en-US/third-party-ui-plugin.xml
new file mode 100644
index 0000000..62ee485
--- /dev/null
+++ b/docs/en-US/third-party-ui-plugin.xml
@@ -0,0 +1,347 @@
+<?xml version='1.0' encoding='utf-8' ?>
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "file:///C:/Program%20Files%20(x86)/Publican/DocBook_DTD/docbookx.dtd" [
+<!ENTITY % BOOK_ENTITIES SYSTEM "cloudstack.ent">
+%BOOK_ENTITIES;
+]>
+ <chapter id="third-party-ui-plugin">
+ <!-- CS-17459 -->
+ <title>Third-Party UI Plugin Framework</title>
+ <para>Using the new third-party plugin framework, you can write and install extensions to
+ &PRODUCT;. The installed and enabled plugins will appear in the UI alongside the
+ other features.
+ The code for the plugin is simply placed in a special directory
+ within &PRODUCT;’s installed code at any time after &PRODUCT; installation. The new plugin
+ appears only when it is enabled by the cloud administrator.</para>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="./images/plugin_intro.jpg"/>
+ </imageobject>
+ <textobject>
+ <phrase>plugin_intro.jpg: New plugin button in product navbar</phrase>
+ </textobject>
+ </mediaobject>
+ <para>The left navigation bar of the &PRODUCT; UI has a new Plugins button to help you work with UI plugins.</para>
+ <section id="plugin-howto-overview">
+ <title>How to Write a Plugin: Overview</title>
+ <para>The basic procedure for writing a plugin is:</para>
+ <orderedlist>
+ <listitem>
+ <para>Write the code and create the other files needed. You will need the plugin code
+ itself (in Javascript), a thumbnail image, the plugin listing, and a CSS file.</para>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="./images/plugin1.jpg"/>
+ </imageobject>
+ <textobject>
+ <phrase>plugin1.jpg: Write the plugin code</phrase>
+ </textobject>
+ </mediaobject>
+ <para>All UI plugins have the following set of files:</para>
+ <programlisting>+-- cloudstack/
+ +-- ui/
+ +-- plugins/
+ +-- csMyFirstPlugin/
+ +-- config.js --> Plugin metadata (title, author, vendor URL, etc.)
+ +-- icon.png --> Icon, shown on side nav bar and plugin listing
+ (should be square, and ~50x50px)
+ +-- csMyFirstPlugin.css --> CSS file, loaded automatically when plugin loads
+ +-- csMyFirstPlugin.js --> Main JS file, containing plugin code
+ </programlisting>
+ <para>The same files must also be present at /tomcat/webapps/client/plugins.</para>
+ </listitem>
+ <listitem>
+ <para>The &PRODUCT; administrator adds the folder containing your plugin code under the
+ &PRODUCT; PLUGINS folder.</para>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="./images/plugin2.jpg"/>
+ </imageobject>
+ <textobject>
+ <phrase>plugin2.jpg: The plugin code is placed in the PLUGINS folder</phrase>
+ </textobject>
+ </mediaobject>
+ </listitem>
+ <listitem>
+ <para>The administrator also adds the name of your plugin to the plugin.js file in the
+ PLUGINS folder.</para>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="./images/plugin3.jpg"/>
+ </imageobject>
+ <textobject>
+ <phrase>plugin3.jpg: The plugin name is added to plugin.js in the PLUGINS
+ folder</phrase>
+ </textobject>
+ </mediaobject>
+ </listitem>
+ <listitem>
+ <para>The next time the user refreshes the UI in the browser, your plugin will appear in
+ the left navigation bar.</para>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="./images/plugin4.jpg"/>
+ </imageobject>
+ <textobject>
+ <phrase>plugin4.jpg: The plugin appears in the UI</phrase>
+ </textobject>
+ </mediaobject>
+ </listitem>
+ </orderedlist>
+ </section>
+ <section id="plugin-howto-details">
+ <title>How to Write a Plugin: Implementation Details</title>
+ <para>This section requires an understanding of JavaScript and the &PRODUCT; API. You don't
+ need knowledge of specific frameworks for this tutorial (jQuery, etc.), since the
+ &PRODUCT; UI handles the front-end rendering for you.</para>
+ <para>There is much more to the &PRODUCT; UI framework than can be described here. The UI is
+ very flexible to handle many use cases, so there are countless options and variations. The
+ best reference right now is to read the existing code for the main UI, which is in the /ui
+ folder. Plugins are written in a very similar way to the main UI.</para>
+ <orderedlist>
+ <listitem>
+ <para><emphasis role="bold">Create the directory to hold your plugin.</emphasis></para>
+ <para>All plugins are composed of set of required files in the directory
+ /ui/plugins/pluginID, where pluginID is a short name for your plugin. It's recommended
+ that you prefix your folder name (for example, bfMyPlugin) to avoid naming conflicts
+ with other people's plugins.</para>
+ <para>In this example, the plugin is named csMyFirstPlugin.</para>
+ <programlisting>$ cd cloudstack/ui/plugins
+$ mkdir csMyFirstPlugin
+$ ls -l
+
+total 8
+drwxr-xr-x 2 bgregory staff 68 Feb 11 14:44 csMyFirstPlugin
+-rw-r--r-- 1 bgregory staff 101 Feb 11 14:26 plugins.js
+ </programlisting>
+ </listitem>
+ <listitem>
+ <para><emphasis role="bold">Change to your new plugin directory.</emphasis></para>
+ <programlisting>$ cd csMyFirstPlugin
+ </programlisting>
+ </listitem>
+ <listitem>
+ <para><emphasis role="bold">Set up the listing.</emphasis></para>
+ <para>Add the file config.js, using your favorite editor.</para>
+ <programlisting>$ vi config.js</programlisting>
+ <para>Add the following content to config.js. This information will be displayed on the
+ plugin listing page in the UI:</para>
+ <programlisting>(function (cloudStack) {
+ cloudStack.plugins.csMyFirstPlugin.config = {
+ title: 'My first plugin',
+ desc: 'Tutorial plugin',
+ externalLink: 'http://www.cloudstack.org/',
+ authorName: 'Test Plugin Developer',
+ authorEmail: 'plugin.developer@example.com'
+ };
+}(cloudStack));
+ </programlisting>
+ </listitem>
+ <listitem>
+ <para><emphasis role="bold">Add a new main section.</emphasis></para>
+ <para>Add the file csMyFirstPlugin.js, using your favorite editor.</para>
+ <programlisting>$ vi csMyFirstPlugin.js</programlisting>
+ <para>Add the following content to csMyFirstPlugin.js:</para>
+ <programlisting>(function (cloudStack) {
+ cloudStack.plugins.csMyFirstPlugin = function(plugin) {
+ plugin.ui.addSection({
+ id: 'csMyFirstPlugin',
+ title: 'My Plugin',
+ preFilter: function(args) {
+ return isAdmin();
+ },
+ show: function() {
+ return $('<div>').html('Content will go here');
+ }
+ });
+ };
+}(cloudStack));
+ </programlisting>
+ </listitem>
+ <listitem>
+ <para><emphasis role="bold">Register the plugin.</emphasis></para>
+ <para>You now have the minimal content needed to run the plugin, so you can activate the
+ plugin in the UI by adding it to plugins.js. First, edit the file:</para>
+ <programlisting>$ cd cloudstack/ui/plugins
+$ vi plugins.js
+ </programlisting>
+ <para>Now add the following to plugins.js:</para>
+ <programlisting>(function($, cloudStack) {
+ cloudStack.plugins = [
+ 'csMyFirstPlugin'
+ ];
+}(jQuery, cloudStack));
+ </programlisting>
+ </listitem>
+ <listitem>
+ <para><emphasis role="bold">Check the plugin in the UI.</emphasis></para>
+ <para>First, copy all the plugin code that you have created so far to
+ /tomcat/webapps/client/plugins. Then refresh the browser and click Plugins in the side
+ navigation bar. You should see your new plugin.</para>
+ </listitem>
+ <listitem>
+ <para><emphasis role="bold">Make the plugin do something.</emphasis></para>
+ <para>Right now, you just have placeholder content in the new plugin. It's time to add
+ real code. In this example, you will write a basic list view, which renders data from
+ an API call. You will list all virtual machines owned by the logged-in user. To do
+ this, replace the 'show' function in the plugin code with a 'listView' block,
+ containing the required syntax for a list view. To get the data, use the
+ listVirtualMachines API call. Without any parameters, it will return VMs only for your
+ active user. Use the provided 'apiCall' helper method to handle the server call. Of
+ course, you are free to use any other method for making the AJAX call (for example,
+ jQuery's $.ajax method).</para>
+ <para>First, open your plugin's JavaScript source file in your favorite editor:</para>
+ <programlisting>$ cd csMyFirstPlugin
+$ vi csMyFirstPlugin.js
+ </programlisting>
+ <para>Add the following code in csMyFirstPlugin.js:</para>
+ <programlisting>(function (cloudStack) {
+ cloudStack.plugins.csMyFirstPlugin = function(plugin) {
+ plugin.ui.addSection({
+ id: 'csMyFirstPlugin',
+ title: 'My Plugin',
+ preFilter: function(args) {
+ return isAdmin();
+ },
+
+ // Render page as a list view
+ listView: {
+ id: 'testPluginInstances',
+ fields: {
+ name: { label: 'label.name' },
+ instancename: { label: 'label.internal.name' },
+ displayname: { label: 'label.display.name' },
+ zonename: { label: 'label.zone.name' }
+ },
+ dataProvider: function(args) {
+ // API calls go here, to retrive the data asynchronously
+ //
+ // On successful retrieval, call
+ // args.response.success({ data: [data array] });
+ plugin.ui.apiCall('listVirtualMachines', {
+ success: function(json) {
+ var vms = json.listvirtualmachinesresponse.virtualmachine;
+
+ args.response.success({ data: vms });
+ },
+ error: function(errorMessage) {
+ args.response.error(errorMessage)
+ }
+ });
+ }
+ }
+ });
+ };
+}(cloudStack));
+ </programlisting>
+ </listitem>
+ <listitem>
+ <para><emphasis role="bold">Test the plugin.</emphasis></para>
+ <para>First, copy all the plugin code that you have created so far to
+ /tomcat/webapps/client/plugins. Then refresh the browser. You can see that your
+ placeholder content was replaced with a list table, containing 4 columns of virtual
+ machine data.</para>
+ </listitem>
+ <listitem>
+ <para><emphasis role="bold">Add an action button.</emphasis></para>
+ <para>Let's add an action button to the list view, which will reboot the VM. To do this,
+ add an actions block under listView. After specifying the correct format, the actions
+ will appear automatically to the right of each row of data.</para>
+ <programlisting>$ vi csMyFirstPlugin.js
+ </programlisting>
+ <para>Now add the following new code in csMyFirstPlugin.js. (The dots ... show where we
+ have omitted some existing code for the sake of space. Don't actually cut and paste
+ that part):</para>
+ <programlisting>...
+ listView: {
+ id: 'testPluginInstances',
+ ...
+
+ actions: {
+ // The key/ID you specify here will determine what icon is
+ // shown in the UI for this action,
+ // and will be added as a CSS class to the action's element
+ // (i.e., '.action.restart')
+ //
+ // -- here, 'restart' is a predefined name in &PRODUCT; that will
+ // automatically show a 'reboot' arrow as an icon;
+ // this can be changed in csMyFirstPlugin.css
+ restart: {
+ label: 'Restart VM',
+ messages: {
+ confirm: function() { return 'Are you sure you want to restart this VM?' },
+ notification: function() { return 'Rebooted VM' }
+ },
+ action: function(args) {
+ // Get the instance object of the selected row from context
+ //
+ // -- all currently loaded state is stored in 'context' as objects,
+ // such as the selected list view row,
+ // the selected section, and active user
+ //
+ // -- for list view actions, the object's key will be the same as
+ // listView.id, specified above;
+ // always make sure you specify an 'id' for the listView,
+ // or else it will be 'undefined!'
+ var instance = args.context.testPluginInstances[0];
+
+ plugin.ui.apiCall('rebootVirtualMachine', {
+ // These will be appended to the API request
+ //
+ // i.e., rebootVirtualMachine&id=...
+ data: {
+ id: instance.id
+ },
+ success: function(json) {
+ args.response.success({
+ // This is an async job, so success here only indicates
+ // that the job was initiated.
+ //
+ // To pass the job ID to the notification UI
+ // (for checking to see when action is completed),
+ // '_custom: { jobID: ... }' needs to always be passed on success,
+ // in the same format as below
+ _custom: { jobId: json.rebootvirtualmachineresponse.jobid }
+ });
+ },
+
+
+ error: function(errorMessage) {
+ args.response.error(errorMessage); // Cancel action, show error message returned
+ }
+ });
+ },
+
+ // Because rebootVirtualMachine is an async job, we need to add
+ // a poll function, which will perodically check
+ // the management server to see if the job is ready
+ // (via pollAsyncJobResult API call)
+ //
+ // The plugin API provides a helper function, 'plugin.ui.pollAsyncJob',
+ / which will work for most jobs
+ // in &PRODUCT;
+ notification: {
+ poll: plugin.ui.pollAsyncJob
+ }
+ }
+ },
+
+ dataProvider: function(args) {
+ ...
+...
+ </programlisting>
+ </listitem>
+ <listitem>
+ <para><emphasis role="bold">Add the thumbnail icon.</emphasis></para>
+ <para>Create an icon file; it should be square, about 50x50 pixels, and named icon.png.
+ Copy it into the same directory with your plugin code:
+ cloudstack/ui/plugins/csMyFirstPlugin/icon.png.</para>
+ </listitem>
+ <listitem>
+ <para><emphasis role="bold">Add the stylesheet.</emphasis></para>
+ <para>Create a CSS file, with the same name as your .js file. Copy it into the same
+ directory with your plugin code:
+ cloudstack/ui/plugins/csMyFirstPlugin/csMyFirstPlugin.css.</para>
+ </listitem>
+ </orderedlist>
+ </section>
+ </chapter>