Return-Path: X-Original-To: apmail-cloudstack-commits-archive@www.apache.org Delivered-To: apmail-cloudstack-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 235BC1032A for ; Fri, 30 Aug 2013 23:28:01 +0000 (UTC) Received: (qmail 3411 invoked by uid 500); 30 Aug 2013 23:27:55 -0000 Delivered-To: apmail-cloudstack-commits-archive@cloudstack.apache.org Received: (qmail 3351 invoked by uid 500); 30 Aug 2013 23:27:55 -0000 Mailing-List: contact commits-help@cloudstack.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cloudstack.apache.org Delivered-To: mailing list commits@cloudstack.apache.org Received: (qmail 2976 invoked by uid 99); 30 Aug 2013 23:27:55 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 30 Aug 2013 23:27:55 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 32EA68BA12E; Fri, 30 Aug 2013 23:27:55 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: duffy@apache.org To: commits@cloudstack.apache.org Date: Fri, 30 Aug 2013 23:28:08 -0000 Message-Id: <311fd87c4e6049cb9786067ef903cc5d@git.apache.org> In-Reply-To: <700e697e7a0749e0837c89b14205d7c9@git.apache.org> References: <700e697e7a0749e0837c89b14205d7c9@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [15/50] [abbrv] git commit: updated refs/heads/ldapplugin to bdba0dd CLOUDSTACK-883. DOC. How to integrate your 3rd-party plugin with the UI. Contains brief third-party plugin code tutorial. Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/75041b9c Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/75041b9c Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/75041b9c Branch: refs/heads/ldapplugin Commit: 75041b9c55e536eb0a0020f983ec750df4af8832 Parents: f002b8e Author: Jessica Authored: Wed Aug 28 17:01:30 2013 -0700 Committer: Jessica Committed: Wed Aug 28 17:01:30 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/75041b9c/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 @@ + http://git-wip-us.apache.org/repos/asf/cloudstack/blob/75041b9c/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/75041b9c/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/75041b9c/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/75041b9c/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/75041b9c/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/75041b9c/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 @@ + + +%BOOK_ENTITIES; +]> + + + Third-Party UI Plugin Framework + 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. + + + + + + plugin_intro.jpg: New plugin button in product navbar + + + The left navigation bar of the &PRODUCT; UI has a new Plugins button to help you work with UI plugins. +
+ How to Write a Plugin: Overview + The basic procedure for writing a plugin is: + + + 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. + + + + + + plugin1.jpg: Write the plugin code + + + All UI plugins have the following set of files: + +-- 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 + + The same files must also be present at /tomcat/webapps/client/plugins. + + + The &PRODUCT; administrator adds the folder containing your plugin code under the + &PRODUCT; PLUGINS folder. + + + + + + plugin2.jpg: The plugin code is placed in the PLUGINS folder + + + + + The administrator also adds the name of your plugin to the plugin.js file in the + PLUGINS folder. + + + + + + plugin3.jpg: The plugin name is added to plugin.js in the PLUGINS + folder + + + + + The next time the user refreshes the UI in the browser, your plugin will appear in + the left navigation bar. + + + + + + plugin4.jpg: The plugin appears in the UI + + + + +
+
+ How to Write a Plugin: Implementation Details + 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. + 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. + + + Create the directory to hold your plugin. + 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. + In this example, the plugin is named csMyFirstPlugin. + $ 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 + + + + Change to your new plugin directory. + $ cd csMyFirstPlugin + + + + Set up the listing. + Add the file config.js, using your favorite editor. + $ vi config.js + Add the following content to config.js. This information will be displayed on the + plugin listing page in the UI: + (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)); + + + + Add a new main section. + Add the file csMyFirstPlugin.js, using your favorite editor. + $ vi csMyFirstPlugin.js + Add the following content to csMyFirstPlugin.js: + (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)); + + + + Register the plugin. + 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: + $ cd cloudstack/ui/plugins +$ vi plugins.js + + Now add the following to plugins.js: + (function($, cloudStack) { + cloudStack.plugins = [ + 'csMyFirstPlugin' + ]; +}(jQuery, cloudStack)); + + + + Check the plugin in the UI. + 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. + + + Make the plugin do something. + 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). + First, open your plugin's JavaScript source file in your favorite editor: + $ cd csMyFirstPlugin +$ vi csMyFirstPlugin.js + + Add the following code in csMyFirstPlugin.js: + (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)); + + + + Test the plugin. + 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. + + + Add an action button. + 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. + $ vi csMyFirstPlugin.js + + 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): + ... + 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) { + ... +... + + + + Add the thumbnail icon. + 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. + + + Add the stylesheet. + 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. + + +
+