Hi Chris,
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.
Regarding planned items for revision 7, here's my current list (feel free to
add/modify things here if necessary):
* "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)
* "add custom task to task pane" plugin API function, requires some
UiCommon integration coding (I will handle this)
* "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)
* integration with REST API, e.g. "obtain REST API authentication token"
plugin API function, maybe also some API for performing actual HTTP REST calls?
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.
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..)
Regards,
Vojtech
----- Original Message -----
From: "Christopher Morrissey" <[email protected]>
To: "Vojtech Szocs" <[email protected]>, "engine-devel"
<[email protected]>
Sent: Monday, October 22, 2012 5:25:53 PM
Subject: RE: [Engine-devel] UI Plugins: PoC patch revision 6 now available
Hi Vojtech,
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?
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.
-Chris
From: [email protected] [mailto:[email protected]] On
Behalf Of Vojtech Szocs
Sent: Thursday, October 18, 2012 10:49 AM
To: engine-devel
Subject: [Engine-devel] UI Plugins: PoC patch revision 6 now available
Hi guys,
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
http://gerrit.ovirt.org/#/c/8120/2 (patch set 2).
Please read on to learn what's new in this revision. If you have any comments,
questions or ideas, please let me know!
0. UI plugin path information resolved using local Engine configuration
Server-side UI plugin infrastructure now uses local (machine-specific) Engine
configuration instead of global ( vdc_options database table) Engine
configuration:
* Previously, path information was resolved through
org.ovirt.engine.core.common.config.Config class - Engine configuration values
were retrieved from vdc_options database table.
* Currently, path information is resolved through
org.ovirt.engine.core.utils.LocalConfig class - Engine configuration values are
retrieved from local file system.
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:
a. Copy default Engine configuration into /usr/share/ ovirt-engine /conf
# mkdir -p /usr/share/ovirt-engine/conf
# cp <OVIRT_HOME>/backend/manager/conf/engine.conf.defaults
/usr/share/ovirt-engine/conf/engine.conf.defaults
b. If necessary, copy UI plugin data files from /usr/share/engine/ui-plugins to
/usr/share/ ovirt-engine /ui-plugins
c. If necessary, copy UI plugin config files from /etc/engine/ui-plugins to
/etc/ ovirt-engine /ui-plugins
d, In case you want to override the default Engine configuration, put your
custom property file into /etc/sysconfig/ovirt-engine
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.
1. New plugin API function: addMainTabActionButton
The "addMainTabActionButton" API adds custom context-sensitive button to the
given main tab's data grid, along with corresponding data grid context menu
item.
addMainTabActionButton(entityTypeName, label, actionButtonInterface)
entityTypeName indicates which main tab's data grid the button should be added
to, according to the entity type associated with the main tab. entityTypeName
values are strings reflecting
org.ovirt.engine.ui.webadmin.plugin.entityEntityType enum members. Following
entityTypeName values are currently supported (values are case-sensitive):
"DataCenter", "Cluster", "Host", "Storage", "Disk", "VirtualMachine",
"Template".
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?
label is the title displayed on the button .
actionButtonInterface represents an object that "implements the button
interface" by declaring its functions: onClick , isEnabled , isAccessible . All
functions of actionButtonInterface receive currently selected item(s) as
function arguments.
Let's take a closer look at the concept behind actionButtonInterface . 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).
In contrast with traditional class-based object-oriented languages, JavaScript
supports OOP through prototype-based programming model (
https://developer.mozilla.org/en-US/docs/JavaScript/Introduction_to_Object-Oriented_JavaScript
). 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 ( http://en.wikipedia.org/wiki/Duck_typing ).
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" does not necessarily have to declare all functions of the
given interface 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).
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:
UiInit: function () {
api. addMainTabActionButton ('Host', 'Single-Host Action',
// Action button interface object
// All functions receive currently selected item(s) as function arguments
{
// Called when the user clicks the button
onClick : function () {
// Calling 'arguments[0]' is safe, because onClick() can be called
// only when exactly one item is currently selected in the data grid
window.alert('Selected host entity ID = ' + arguments[0].entityId);
},
// Returning 'true' means the button is enabled (clickable)
// Returning 'false' means the button is disabled (non-clickable)
// Default value = 'true'
isEnabled : function () {
// Enable button only when exactly one item is selected
return arguments.length == 1;
},
// Returning 'true' means the button is visible
// Returning 'false' means the button is hidden
// Default value = 'true'
isAccessible : function () {
// Always show the button in the corresponding data grid
return true ;
}
}
);
}
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:
* onClick - no default value (no return value expected)
* isEnabled / isAccessible - default value "true" (boolean return value
expected)
Note: UI plugin infrastructure checks the actual return value type, and uses
default value in case the function returned something of wrong (unexpected)
type.
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:
{ entityId: "[BusinessEntityGuidAsString]" }
In future, we will create specific JSON-like representations for specific
business entities, in compliance with Engine REST API entity structure.
For a more extensive example of using "addMainTabActionButton" API, please see
the attached "addMainTabActionButton.html.example" file.
2. Improved plugin API function: addMainTab
The "addMainTab" API was improved to address following issues:
* "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). Previously, "addMainTab" worked reliably only when
called from within UiInit event handler function. Currently, it's possible to
call "addMainTab" at any moment, e.g. from within some other event handler
function (after UiInit has completed).
* "addMainTab" now retains "active" tab (highlighted tab GUI). "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. 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.
Furthermore, custom main tab implementation now displays the content of the
given URL through HTML iframe element.
3. Improved native JavaScript function handling (GWT JSNI)
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.
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...
4. ActionPanel and ActionTable type hierarchy refactoring (related to
"addMainTabActionButton" API)
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.
This patch introduces new interfaces for ActionPanel and ActionTable components
while eliminating code redundancy (duplicate or unnecessary code).
5. ActionPanel type hierarchy refactoring (related to "addMainTab" API)
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.
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.
Note that for now, only main tab panel
(org.ovirt.engine.ui.webadmin.section.main.presenter.MainTabPanelPresenter)
supports dynamic tabs within its view.
Where is addSubTab API function?
Implementing "addSubTab" API requires some more changes, and I didn't want to
delay this PoC patch just because of it...
Here's a sample code that illustrates proposed "addSubTab" API usage:
UiInit: function () {
api. addSubTab ('Host', // entityTypeName
'Custom Host Sub Tab', // label
'custom-host-sub-tab', // historyToken
'http://www.ovirt.org/', // contentUrl
// Sub tab interface object
// All functions receive currently selected item(s)
// within the main tab data grid as function arguments
{
// Returning 'true' means the sub tab is visible
// Returning 'false' means the sub tab is hidden
// Default value = 'true'
isAccessible : function () {
return arguments.length == 1 && arguments[0].entityId == '<MyHostEntityId>';
}
}
);
}
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.
Currently, we have one (and only one) "tab type" - a tab that shows content of
the given URL through HTML iframe element.
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).
Let me know what you think!
Cheers,
Vojtech
_______________________________________________
Engine-devel mailing list
[email protected]
http://lists.ovirt.org/mailman/listinfo/engine-devel