<html><head><style type='text/css'>p { margin: 0; }</style></head><body><div style='font-family: times new roman,new york,times,serif; font-size: 12pt; color: #000000'>Hi Chris,<br><br>well, I planned to implement simple "show dialog with content loaded from given URL" plugin API function some time ago, but then I thought you might be experimenting with it already :) please feel free to work on that, we can include it into PoC revision 7.<br><br>Regarding planned items for revision 7, here's my current list (feel free to add/modify things here if necessary):<br><ul><li>"add custom sub tab" plugin API function, with initial sub tab implementation showing content of the given URL (we can add more tab types later on, e.g. form-based or table-based tab)</li><li>"add custom task to task pane" plugin API function, requires some UiCommon integration coding (I will handle this)<br></li><li>"show dialog with content loaded from given URL" plugin API function, initial implementation could simply open new browser popup window using window.open (but any other approach is fine as well)<br></li><li>integration with REST API, e.g. "obtain REST API authentication token" plugin API function, maybe also some API for performing actual HTTP REST calls?<br></li></ul><p><br></p><p>As for the Engine REST API authentication token, need to learn more about it, as WebAdmin GUI currently uses GWT RPC instead of REST API when communicating with the backend.</p><p></p><p><br></p><p>You also mentioned that "we can make calls from our server into the REST API" - I assume this will be used together with "custom content for tab/dialog" plugin API functionality? (Otherwise I'd say the UI Plugin itself could make REST API calls on its own..)<br></p><p><br></p><p>Regards,</p><p>Vojtech<br></p><br><br><hr id="zwchr"><div style="color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><b>From: </b>"Christopher Morrissey" &lt;Christopher.Morrissey@netapp.com&gt;<br><b>To: </b>"Vojtech Szocs" &lt;vszocs@redhat.com&gt;, "engine-devel" &lt;engine-devel@ovirt.org&gt;<br><b>Sent: </b>Monday, October 22, 2012 5:25:53 PM<br><b>Subject: </b>RE: [Engine-devel] UI Plugins: PoC patch revision 6 now available<br><br>



<style><!--

@font-face
        {font-family:Wingdings;
        panose-1:5 0 0 0 0 0 0 0 0 0;}
@font-face
        {font-family:Wingdings;
        panose-1:5 0 0 0 0 0 0 0 0 0;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
        {font-family:Tahoma;
        panose-1:2 11 6 4 3 5 4 4 2 4;}

p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0in;
        margin-bottom:.0001pt;
        font-size:12.0pt;
        font-family:"Times New Roman","serif";}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
        {mso-style-priority:99;
        color:purple;
        text-decoration:underline;}
p
        {mso-style-priority:99;
        margin:0in;
        margin-bottom:.0001pt;
        font-size:12.0pt;
        font-family:"Times New Roman","serif";}
span.EmailStyle20
        {mso-style-type:personal-reply;
        font-family:"Calibri","sans-serif";
        color:#1F497D;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-size:10.0pt;}
@page WordSection1
        {size:8.5in 11.0in;
        margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
        {page:WordSection1;}

@list l0
        {mso-list-id:430246997;
        mso-list-template-ids:-1402284674;}
@list l0:level1
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l0:level2
        {mso-level-number-format:bullet;
        mso-level-text:o;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:"Courier New";
        mso-bidi-font-family:"Times New Roman";}
@list l0:level3
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l0:level4
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l0:level5
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l0:level6
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l0:level7
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l0:level8
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l0:level9
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l1
        {mso-list-id:675302646;
        mso-list-template-ids:1662667256;}
@list l1:level1
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l1:level2
        {mso-level-number-format:bullet;
        mso-level-text:o;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:"Courier New";
        mso-bidi-font-family:"Times New Roman";}
@list l1:level3
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l1:level4
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l1:level5
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l1:level6
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l1:level7
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l1:level8
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l1:level9
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l2
        {mso-list-id:1330719658;
        mso-list-template-ids:-1858944704;}
@list l2:level1
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l2:level2
        {mso-level-number-format:bullet;
        mso-level-text:o;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:"Courier New";
        mso-bidi-font-family:"Times New Roman";}
@list l2:level3
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l2:level4
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l2:level5
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l2:level6
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l2:level7
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l2:level8
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l2:level9
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l3
        {mso-list-id:2009794949;
        mso-list-template-ids:-1270830370;}
@list l3:level1
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l3:level2
        {mso-level-number-format:bullet;
        mso-level-text:o;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:"Courier New";
        mso-bidi-font-family:"Times New Roman";}
@list l3:level3
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l3:level4
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l3:level5
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l3:level6
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l3:level7
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l3:level8
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
@list l3:level9
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Wingdings;}
ol
        {margin-bottom:0in;}
ul
        {margin-bottom:0in;}
--></style>


<div class="WordSection1">
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Hi Vojtech,</span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">&nbsp;</span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">Thanks again for the delivery of the patch. For revision 7, do you have a list of content? I had previously indicated I could work on adding the plugin API
 to launch a dialog, but hadn’t been able to get started on it until now. I wanted to see if you by chance were already working on it or if you were planning to deliver that yourself in the next revision?</span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">&nbsp;</span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">A couple of other items we are looking for are the ability to add tasks for execution and get access to the session ID or some kind of authentication token
 so that we can make calls from our server into the REST API. I’m not very familiar yet with the REST API so I’m not sure what authentication methods are available and which would be best.</span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">&nbsp;</span></p>
<div>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">-Chris</span></p>
</div>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;color:#1F497D">&nbsp;</span></p>
<div>
<div style="border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0in 0in 0in">
<p class="MsoNormal"><b><span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;">From:</span></b><span style="font-size:10.0pt;font-family:&quot;Tahoma&quot;,&quot;sans-serif&quot;"> engine-devel-bounces@ovirt.org [mailto:engine-devel-bounces@ovirt.org]
<b>On Behalf Of </b>Vojtech Szocs<br>
<b>Sent:</b> Thursday, October 18, 2012 10:49 AM<br>
<b>To:</b> engine-devel<br>
<b>Subject:</b> [Engine-devel] UI Plugins: PoC patch revision 6 now available</span></p>
</div>
</div>
<p class="MsoNormal">&nbsp;</p>
<div>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black">Hi guys,<br>
<br>
the latest revision of UI Plugins proof-of-concept patch is now available for you to experiment with. You can download the patch from oVirt Gerrit at
<a href="http://gerrit.ovirt.org/#/c/8120/2" target="_blank">http://gerrit.ovirt.org/#/c/8120/2</a> (patch set 2).<br>
<br>
Please read on to learn what's new in this revision. If you have any comments, questions or ideas, please let me know!</span></p>
<div class="MsoNormal" style="text-align:center" align="center"><span style="color:black">
<hr align="center" size="2" width="100%">
</span></div>
<p class="MsoNormal"><span style="color:black"><br>
<strong>0. UI plugin path information resolved using local Engine configuration</strong><b><br>
</b><br>
Server-side UI plugin infrastructure now uses local (machine-specific) Engine configuration instead of global (<em>vdc_options</em> database table) Engine configuration:</span></p>
<ul type="disc">
<li class="MsoNormal" style="color:black;mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;mso-list:l3 level1 lfo1">
Previously, path information was resolved through org.ovirt.engine.core.common.config.Config class - Engine configuration values were retrieved from
<i>vdc_options</i> database table.</li><li class="MsoNormal" style="color:black;mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;mso-list:l3 level1 lfo1">
Currently, path information is resolved through org.ovirt.engine.core.utils.LocalConfig class - Engine configuration values are retrieved from local file system.</li></ul>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black">In case you're not working with oVirt Engine through RPM package system, e.g. you have a local development environment set up and you build and deploy oVirt Engine through Maven, please
 follow these steps:<br>
<br>
a. Copy default Engine configuration into /usr/share/<b>ovirt-engine</b>/conf</span></p>
<div style="margin-left:30.0pt">
<p class="MsoNormal"><span style="font-family:&quot;Courier New&quot;;color:black"># mkdir -p /usr/share/ovirt-engine/conf<br>
# cp &lt;OVIRT_HOME&gt;/backend/manager/conf/engine.conf.defaults /usr/share/ovirt-engine/conf/engine.conf.defaults</span></p>
</div>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black"><br>
b. If necessary, copy UI plugin data files from /usr/share/engine/ui-plugins to /usr/share/<b>ovirt-engine</b>/ui-plugins<br>
<br>
c. If necessary, copy UI plugin config files from /etc/engine/ui-plugins to /etc/<b>ovirt-engine</b>/ui-plugins<br>
<br>
d, In case you want to override the default Engine configuration, put your custom property file into /etc/sysconfig/ovirt-engine<br>
<br>
The reason behind this change is that path information for UI plugin data and configuration is typically machine-specific, and should be customizable per machine through Engine local configuration.</span></p>
<div class="MsoNormal" style="text-align:center" align="center"><span style="color:black">
<hr align="center" size="2" width="100%">
</span></div>
<p class="MsoNormal"><span style="color:black"><br>
<b>1. New plugin API function: addMainTabActionButton<br>
</b><br>
The "addMainTabActionButton" API adds custom context-sensitive button to the given main tab's data grid, along with corresponding data grid context menu item.<br>
<br>
</span><span style="font-family:&quot;Courier New&quot;;color:black">addMainTabActionButton(entityTypeName, label, actionButtonInterface)<br>
</span><span style="color:black"><br>
<i>entityTypeName</i> indicates which main tab's data grid the button should be added to, according to the entity type associated with the main tab.<i> entityTypeName</i> values are strings reflecting org.ovirt.engine.ui.webadmin.plugin.entityEntityType enum
 members. Following <i>entityTypeName</i> values are currently supported (values are case-sensitive): "DataCenter", "Cluster", "Host", "Storage", "Disk", "VirtualMachine", "Template".<br>
<br>
Note: "Pool" value is currently not supported, because of org.ovirt.engine.core.common.businessentities.vm_pools entity not implementing the BusinessEntity interface, not sure why though. Maybe we should switch from BusinessEntity to IVdcQueryable interface
 and always cast getQueryableId method result value to Guid?<br>
<br>
<i>label</i> is the title displayed on the button<i>.<br>
</i><br>
<i>actionButtonInterface</i> represents an object that "implements the button interface" by declaring its functions:
<i>onClick</i>, <i>isEnabled</i>, <i>isAccessible</i>. All functions of <i>actionButtonInterface</i> receive currently selected item(s) as function arguments.<br>
<br>
Let's take a closer look at the concept behind <i>actionButtonInterface</i>. In traditional class-based object-oriented languages, such as Java, interface is an abstract type that contains method declarations without an implementation. A class that implements
 the given interface must implement all methods declared by that interface (unless it's an abstract class, but this isn't relevant in our case).<br>
<br>
In contrast with traditional class-based object-oriented languages, JavaScript supports OOP through prototype-based programming model (<a href="https://developer.mozilla.org/en-US/docs/JavaScript/Introduction_to_Object-Oriented_JavaScript" target="_blank">https://developer.mozilla.org/en-US/docs/JavaScript/Introduction_to_Object-Oriented_JavaScript</a>).
 At the same time, JavaScript language is dynamically-typed and therefore doesn't support traditional concept of interface in OOP, it uses "duck typing" technique instead (<a href="http://en.wikipedia.org/wiki/Duck_typing" target="_blank">http://en.wikipedia.org/wiki/Duck_typing</a>).<br>
<br>
The simplest way to provide an object that "implements the given interface" in JavaScript is to use "duck typing" technique: providing an object that contains well-known functions. In UI plugin infrastructure, I call this concept "interface object", represented
 by org.ovirt.engine.ui.webadmin.plugin.jsni.JsInterfaceObject class. Unlike the traditional concept of interface abstract type in object-oriented languages, an "interface object"
<u>does not necessarily have to declare all functions of the given interface</u> in order to "implement" such interface. In fact, an empty object can be used as a valid "interface object". Missing functions will be simply treated as empty (no-op) functions.
 Furthermore, an "interface object" can "implement" multiple interfaces by declaring functions of those interfaces (interface composition).<br>
<br>
Getting back to "addMainTabActionButton" API, here's a sample code that adds new button to "Host" main tab data grid, as part of UiInit event handler function:<br>
<br>
</span><span style="font-family:&quot;Courier New&quot;;color:black">UiInit: </span><b><span style="font-family:&quot;Courier New&quot;;color:#990000">function</span></b><span style="font-family:&quot;Courier New&quot;;color:black">() {<br>
&nbsp;&nbsp;&nbsp; api.</span><span style="font-family:&quot;Courier New&quot;;color:#CC33CC">addMainTabActionButton</span><span style="font-family:&quot;Courier New&quot;;color:black">('Host', 'Single-Host Action',<br>
<br>
</span><span style="font-family:&quot;Courier New&quot;;color:#006600">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Action button interface object<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // All functions receive currently selected item(s) as function arguments<br>
</span><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span><span style="color:black"><br>
<br>
</span><span style="font-family:&quot;Courier New&quot;;color:#006600">&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Called when the user clicks the button</span><span style="color:#006600"><br>
</span><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp; &nbsp; </span><span style="font-family:&quot;Courier New&quot;;color:#000099">onClick</span><span style="font-family:&quot;Courier New&quot;;color:black">:
</span><b><span style="font-family:&quot;Courier New&quot;;color:#990000">function</span></b><span style="font-family:&quot;Courier New&quot;;color:black">() {<br>
</span><span style="font-family:&quot;Courier New&quot;;color:#006600">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp; &nbsp; // Calling 'arguments[0]' is safe, because onClick() can be called<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp; // only when exactly one item is currently selected in the data grid<br>
</span><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; window.alert('Selected host entity ID = ' + arguments[0].entityId);<br>
&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; },</span><span style="color:black"><br>
<br>
</span><span style="font-family:&quot;Courier New&quot;;color:#006600">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Returning 'true' means the button is enabled (clickable)</span><span style="color:#006600"><br>
</span><span style="font-family:&quot;Courier New&quot;;color:#006600">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; // Returning 'false' means the button is disabled (non-clickable)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Default value = 'true'<br>
</span><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &nbsp;&nbsp; </span><span style="font-family:&quot;Courier New&quot;;color:#000099">isEnabled</span><span style="font-family:&quot;Courier New&quot;;color:black">:
</span><b><span style="font-family:&quot;Courier New&quot;;color:#990000">function</span></b><span style="font-family:&quot;Courier New&quot;;color:black">() {<br>
</span><span style="font-family:&quot;Courier New&quot;;color:#006600">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp; // Enable button only when exactly one item is selected<br>
</span><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>
<b><span style="font-family:&quot;Courier New&quot;;color:#990000">return</span></b><span style="font-family:&quot;Courier New&quot;;color:black"> arguments.length == 1;<br>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; },</span><span style="color:black"><br>
<br>
</span><span style="font-family:&quot;Courier New&quot;;color:#006600">&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp; &nbsp; // Returning 'true' means the button is visible<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Returning 'false' means the button is hidden<br>
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Default value = 'true'<br>
</span><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span style="font-family:&quot;Courier New&quot;;color:#000099">isAccessible</span><span style="font-family:&quot;Courier New&quot;;color:black">:
</span><b><span style="font-family:&quot;Courier New&quot;;color:#990000">function</span></b><span style="font-family:&quot;Courier New&quot;;color:black">() {</span><span style="color:black"><br>
</span><span style="font-family:&quot;Courier New&quot;;color:#006600">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp; &nbsp;&nbsp; // Always show the button in the corresponding data grid<br>
</span><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>
<b><span style="font-family:&quot;Courier New&quot;;color:#990000">return</span></b><span style="font-family:&quot;Courier New&quot;;color:black">
</span><b><span style="font-family:&quot;Courier New&quot;;color:#990000">true</span></b><span style="font-family:&quot;Courier New&quot;;color:black">;<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><span style="color:black"><br>
</span><span style="font-family:&quot;Courier New&quot;;color:black"><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
<br>
&nbsp;&nbsp;&nbsp; );<br>
}</span><span style="color:black"><br>
<br>
As mentioned above, all functions of an interface object are optional. For functions expecting return value, default value is defined by UI plugin infrastructure. For example:</span></p>
<ul type="disc">
<li class="MsoNormal" style="color:black;mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;mso-list:l2 level1 lfo2">
onClick - no default value (no return value expected)</li><li class="MsoNormal" style="color:black;mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;mso-list:l2 level1 lfo2">
isEnabled / isAccessible - default value "true" (boolean return value expected)</li></ul>
<p><span style="color:black">Note: UI plugin infrastructure checks the actual return value type, and uses default value in case the function returned something of wrong (unexpected) type.</span></p>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black"><br>
In the example above, "currently selected item(s)" maps to JSON-like representations of business entities currently selected in the corresponding data grid. For now, the entity representation is quite simple and same for all entity types:<br>
<br>
</span><span style="font-family:&quot;Courier New&quot;;color:black">{ entityId: "[BusinessEntityGuidAsString]" }<br>
</span><span style="color:black"><br>
In future, we will create specific JSON-like representations for specific business entities, in compliance with Engine REST API entity structure.<br>
<br>
For a more extensive example of using "addMainTabActionButton" API, please see the attached "addMainTabActionButton.html.example" file.</span></p>
<div class="MsoNormal" style="text-align:center" align="center"><span style="color:black">
<hr align="center" size="2" width="100%">
</span></div>
<p class="MsoNormal"><span style="color:black"><br>
<b>2. Improved plugin API function: addMainTab<br>
</b><br>
The "addMainTab" API was improved to address following issues:</span></p>
<ul type="disc">
<li class="MsoNormal" style="color:black;mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;mso-list:l1 level1 lfo3">
"addMainTab" can now be called at any moment during UI plugin runtime, given that the plugin is allowed invoke plugin API functions (plugin is either INITIALIZING or IN_USE).<br>
Previously, "addMainTab" worked reliably only when called from within UiInit event handler function.<br>
Currently, it's possible to call "addMainTab" at any moment, e.g. from within some other event handler function (after UiInit has completed).</li></ul>
<ul type="disc">
<li class="MsoNormal" style="color:black;mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;mso-list:l0 level1 lfo4">
"addMainTab" now retains "active" tab (highlighted tab GUI).<br>
"addMainTab" works by adding new tab component (GWTP presenter proxy) and refreshing main tab panel GUI by removing all related tabs and re-adding them again.<br>
This logic is handled by org.ovirt.engine.ui.common.presenter.DynamicTabContainerPresenter class, which makes sure that "active" tab is retained even after main tab panel was refreshed.</li></ul>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black">Furthermore, custom main tab implementation now displays the content of the given URL through HTML iframe element.</span></p>
<div class="MsoNormal" style="text-align:center" align="center"><span style="color:black">
<hr align="center" size="2" width="100%">
</span></div>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black"><br>
<b>3. Improved native JavaScript function handling</b> (GWT JSNI)<br>
<br>
This patch introduces org.ovirt.engine.ui.webadmin.plugin.jsni.JsFunction and org.ovirt.engine.ui.webadmin.plugin.jsni.JsFunctionResultHelper classes providing Java abstraction for invoking native JavaScript functions. These classes follow the general contract
 of "interface object" as mentioned above.<br>
<br>
JsFunctionResultHelper is particularly useful when dealing with functions which are expected to return value of a certain type. Too bad standard GWT JSNI classes don't provide such abstraction for working with native functions out-of-the-box...</span></p>
<div class="MsoNormal" style="text-align:center" align="center"><span style="color:black">
<hr align="center" size="2" width="100%">
</span></div>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black"><br>
<b>4. ActionPanel and ActionTable type hierarchy refactoring</b> (related to "addMainTabActionButton" API)<b><br>
</b><br>
Previously, AbstractActionPanel and AbstractActionTable classes didn't implement any reasonable interface that would allow other components (client-side UI plugin infrastructure) to depend on their functionality in a loosely-coupled manner. This would make
 code that implements "addMainTabActionButton" API "ugly": main tab view interface would have to reference AbstractActionTable class directly. In MVP design pattern, view interface should avoid referencing specific GWT Widget classes directly.<br>
<br>
This patch introduces new interfaces for ActionPanel and ActionTable components while eliminating code redundancy (duplicate or unnecessary code).</span></p>
<div class="MsoNormal" style="text-align:center" align="center"><span style="color:black">
<hr align="center" size="2" width="100%">
</span></div>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black"><br>
<b>5. ActionPanel type hierarchy refactoring</b> (related to "addMainTab" API)<br>
<br>
Since org.ovirt.engine.ui.common.presenter.DynamicTabContainerPresenter defines new DynamicTabPanel interface that extends standard GWTP TabPanel interface, some refactoring had to be done in related ActionPanel classes.<br>
<br>
This patch makes sure that both org.ovirt.engine.ui.common.widget.tab.AbstractTabPanel (widget) and org.ovirt.engine.ui.common.view.AbstractTabPanelView (view) support DynamicTabPanel interface.<br>
<br>
Note that for now, only main tab panel (org.ovirt.engine.ui.webadmin.section.main.presenter.MainTabPanelPresenter) supports dynamic tabs within its view.</span></p>
<div class="MsoNormal" style="text-align:center" align="center"><span style="color:black">
<hr align="center" size="2" width="100%">
</span></div>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black"><br>
<b>Where is addSubTab API function?</b><br>
<br>
Implementing "addSubTab" API requires some more changes, and I didn't want to delay this PoC patch just because of it...<br>
<br>
Here's a sample code that illustrates proposed "addSubTab" API usage:<br>
<br>
</span><span style="font-family:&quot;Courier New&quot;;color:black">UiInit: </span><b><span style="font-family:&quot;Courier New&quot;;color:#990000">function</span></b><span style="font-family:&quot;Courier New&quot;;color:black">() {<br>
&nbsp;&nbsp;&nbsp; api.</span><span style="font-family:&quot;Courier New&quot;;color:#CC33CC">addSubTab</span><span style="font-family:&quot;Courier New&quot;;color:black">('Host',&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;
</span><span style="font-family:&quot;Courier New&quot;;color:#006600">// entityTypeName</span><span style="font-family:&quot;Courier New&quot;;color:black"><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'Custom Host Sub Tab',&nbsp;&nbsp; </span><span style="font-family:&quot;Courier New&quot;;color:#006600">// label</span><span style="font-family:&quot;Courier New&quot;;color:black"><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'custom-host-sub-tab', &nbsp; </span><span style="font-family:&quot;Courier New&quot;;color:#006600">// historyToken</span><span style="font-family:&quot;Courier New&quot;;color:black"><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'http://www.ovirt.org/', </span><span style="font-family:&quot;Courier New&quot;;color:#006600">// contentUrl<br>
<br>
</span><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family:&quot;Courier New&quot;;color:#006600;background:white">// Sub tab interface object<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // </span><span style="font-family:&quot;Courier New&quot;;color:#006600">All functions receive currently selected item(s)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // within the main tab data grid as function arguments</span><span style="color:#006600"><br>
</span><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span><span style="color:black"><br>
<br>
</span><span style="font-family:&quot;Courier New&quot;;color:#006600">&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp; &nbsp; // Returning 'true' means the sub tab is visible<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Returning 'false' means the sub tab is hidden<br>
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Default value = 'true'<br>
</span><span style="font-family:&quot;Courier New&quot;;color:black">&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span style="font-family:&quot;Courier New&quot;;color:#000099">isAccessible</span><span style="font-family:&quot;Courier New&quot;;color:black">:
</span><b><span style="font-family:&quot;Courier New&quot;;color:#990000">function</span></b><span style="font-family:&quot;Courier New&quot;;color:black">() {<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><b><span style="font-family:&quot;Courier New&quot;;color:#990000">return</span></b><span style="font-family:&quot;Courier New&quot;;color:black"> arguments.length == 1 &amp;&amp; arguments[0].entityId == '&lt;MyHostEntityId&gt;';<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><span style="color:black"><br>
</span><span style="font-family:&quot;Courier New&quot;;color:black"><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>
<br>
&nbsp;&nbsp;&nbsp; );<br>
}</span><span style="color:black"><br>
<br>
As part of "addSubTab" API implementation, I'll refactor custom main tab components, in order to use one "tab type" for both main and sub tabs.<br>
<br>
Currently, we have one (and only one) "tab type" - a tab that shows content of the given URL through HTML iframe element.<br>
<br>
We could also create new "tab types", e.g. form-based tab that shows key/value pairs (IMHO this could be quite useful for custom sub tabs).</span></p>
<div class="MsoNormal" style="text-align:center" align="center"><span style="color:black">
<hr align="center" size="2" width="100%">
</span></div>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black"><br>
Let me know what you think!<br>
<br>
Cheers,<br>
Vojtech</span></p>
</div>
</div>


</div><br></div></body></html>