Declarative Actions Contributions and Selection

Reference Implementation Demo Video:    selection_actions_declarative2.swf

OSGS Thread Discussion:   http://groups.google.com/group/opensocial-and-gadgets-spec/browse_thread/thread/a4b4fcf9d3b02bc8/1d2fd87929fda943?lnk=gst&q=Declarative+actions#1d2fd87929fda943

Issue Tracker: http://code.google.com/p/opensocial-resources/issues/detail?id=1161&sort=id&colspec=ID%20Type%20Container%20Stars%20Summary%20Status

User Stories

A VOIP company wants to build a gadget to provide their SampleVOIP service to all available open social containers, ranging from traditional social networks MySpace to Gmail, and wants to enable certain actions every time a person's name is seen (ie. a phone icon/action), that would invoke their service.  This phone icon may be present even before the SampleVOIP gadget is rendered on the page.

A social network, or any site, has content they want to allow third parties to syndicate/share, and wants to provide a dynamic mechanism for partner sites to provide their own share actions, without integrating and maintaining each third party sites specific javascript.
In both these user stories, actions are contributed to a container/host page and the container provides the context/selection of what the action was invoked upon, as well as how the actions are rendered.  Furthermore, the gadgets as points of integration do not need to be rendered immediately, and can be rendered only once the required action is invoked.

Selection Service( gadgets.selection )

The selection service gives gadgets the ability to participate in the container’s selection eventing. The concept of selection is common among many containers. Examples include “selected” or “highlighted” documents in an email inbox view or the selected meetings in a calendar view. A user can select a collection of documents and move them to a folder or highlight a meeting to view a preview of the details of the meeting. Selection is a user driven singleton belonging to the container. Typically a user gesture triggers a gadget to post selection to the container’s selection service. Each time something is posted to the selection service, gadgets that are registered to listen for selection events are notified. The current selection for the container represents what was posted last and can be queried at any time. The selected object can represent a single object or a collection of objects.

Note gadgets must opt in to publish data via selection and should be aware it is a broadcast event.

Selection's distinct value over pub-sub methods of container-gadget and gadget-gadget communication exists in the context of executing gadget provided actions.  When an action is executed the selection service is used to provide the specific container selection to the action.  In a pub-sub model multiple events can be firing simultaneously and since it is not a singleton, the container cannot guarantee the object provided is the same one the action was invoked upon.  Furthermore, when using declarative actions, the gadget receiving the action may not even be loaded/rendered when the action occurs, so it is not possible for it to listen to pub-sub type events.

Compatibility with the Inter-gadget Event Common Namespace Proposal is intended so that the current selection is surfaced as org.opensocial.container.selection.  This compatibility will be implemented by a pub-sub-container.selection feature so that core selection service does not depend on all of pub-sub2 and associated OAHub.  gadgets.selection will only depend on gadgets.rpc for core communication

Action Service ( container.actions )

The action service allows gadgets to contribute buttons and menu items to the container’s UI. Some containers have global action contribution areas such as the header and footers of the container. For example the Google Documents application has a collection of menus and toolbars. If Google Documents were a gadget container it would be beneficial for gadgets to be able to contribute menus and toolbar items to these global areas.

Additionally a gadget may need to contribute actions to specific Open Social Data Objects, whether rendered in the container or inside other gadgets.  In traditional desktop applications these actions for example are often rendered as menu items on context menus when you click on a person or file attachments. This implies that the container maintains some ownership over the management of actions for well defined data types.  The container and gadgets may choose independently how to best render these actions, but the gadget providing the action may attempt to provide a level of UI consistency by contributing actions in a standard manner.  Gadgets register actions to show in context menus by specifying a label, icon and a callback function. In some cases these actions are context sensitive, being displayed only when the context menu is activated for a particular context. Gadget developers do not need to worry about other UI details.

Example Use Case: A gadget for a document management service, wishes to extend a host container's top level actions to include an action for "new Document"

Example Use Case: A gadget providing VOIP wishes to extend a container's contact list with a call action on people's names.

Action contributions may be made programmatically calling addAction(actionObject) at anytime by a gadget.  Gadget's may use this to dyanmically add/remove actions based on the functionality of their gadgets.

Declarative Actions

A gadget may declaratively contribute an action to the container, to provide points of integration when the gadget may not even be rendered yet on the page.  When the container.preloadsGadgets() on the page, container.actions, processes the corresponding action-contributions made by the gadgets, and notifies the appropriate container specific renders and updates the action registries appropriately.

When a user clicks on an action provided by declarative actions, the container renders the gadget if it is not already visible, navigates to the optionally specified view. Note containers have full control over how the gadget is rendered.  As the gadget is rendered, it contributes the JS callback for the action by calling actions.updateAction() with the action:id attribute, providing the javascript callback handler for the action.  When the container receives the actions.updateAction() call it is then able to invoke the provided action via gadget.rpc.  As part of the gadget rendering, the corresponding selection is always available via gadgets.selection.getSelection(); 

Declarative Actions
<Module>
<ModulePrefs title="Sample VOIP">
<Optional feature="actions">
<Param name="action-contributions">
   <action id="org.samplevoip.callbyperson" dataObject="opensocial.Person" 
      label="Call using VOIP Phone" view="DialByPerson" icon="http://ww.samplervoip.org/phone.gif"/>
   <action id="org.samplervoip.navLink" path="container/navigationLinks" label="Phone" />
</Param>
Example
var myaction = {
    id: "org.samplevoip.callbyperson",
    callback: mycallback
}

container.actions.updateAction(myaction);

Today individual containers and platforms each provide different non-standard mechanisms to extend global navigation, and action contribution.  Some notable examples are in Google Apps navigation link contribution via external Application Manifests.  This mechanism is not standardized within open social and it only provides links to url based points of integration and does not integrate actions that may be provided by gadgets already on the page.  Similarly the Contextual Gadgets extension of gmail, attempts to introduce gadget bindings to specific recognized patterns in plain text within Gmail, but does not provide action based hooks to known data types exposed by the container.

Declarative actions open up an entirely new world of points of integration and extensibility within containers, while leveraging the existing gadget specification and meta-data/gadget pre-loading mechanism.

Use Cases in Detail

Actions:

Action contribution by path  
A container shall render actions as link, buttons, menus, however the container chooses to render the ui.

  1. Gadget contributes a top level action to the container, aka such as a link at the top
  2. Gadget contributes a action to the Gadget's drop down menu(drawn by the container)
  3. Gadget contributes an action to a container specific defined path location, such as a container's toolbar

Action contribution to an Open Social Data type  
Containers may choose to render actions bound to data type as either context menus, buttons, etc, where the implementation of the UI is left up entirely to the container. When the action is invoked, the gadgets.selection.getSelection() will return the corresponding data type as defined in [ http://opensocial-resources.googlecode.com/svn/spec/1.1/Social-Data.xml|http://opensocial-resources.googlecode.com/svn/spec/1.1/Social-Data.xml].

  1. Gadget contributes an action to container's Person object. Example, a gadget providing SMS capability can bind to a container's list of active contacts/buddy list.
  2. Gadget contributes an action to a container's Message. Example, a new button or action is added to the forward, reply, delete list when displaying a message.
  3. Same holds true for all Open Social defined data types Group, Activity… etc
  4. Gadgets may bind to any container specific types that are defined.  Containers shall make all efforts to use types in shared namespace and to contribute back new types to the spec to maintain cross-container abilities of gadgets.

Paths

Container may choose to support these common action locations.  Containers should attempt to use common namespace's for agreed upon elements. 

Path

Description

container/navigationLinks

The top level navigation links, providing links to related services.

container/menus

The top level menu.  For example to add a new action to File->New->Presentation path="container/menu/File/New"

container/toolbars

Top level toolbars owned by the container. For example to add a new action to Toolbar in a docs editor.

gadget/menu

The optional menu drawn by the container, around each gadget

  TODO: get more feedback on common shared action paths multiple containers already want to expose from existing specs

Types

Containers should use the following type attributes when biding actions to OpenSocial Data types: [http://opensocial-resources.googlecode.com/svn/spec/1.1/Social-Data.xml|http://opensocial-resources.googlecode.com/svn/spec/1.1/Social-Data.xml] 

Question: Is this the correct namespace ids as the spec seems to imply but is not explicit

 opensocial.Person
 opensocial.Message
 opensocial.Activity
 opensocial.Group

Proposed API

gadgets.selection

Provides access to the container’s selection service.

setSelection

<static> gadgets.selection.setSelection (selection) 
Description: Sets the specified object(s) as the selected object for the container.  May be called from either the gadget or container.  The selection is a singleton as only one gadget or UI element in the container may provide the selection at a time, but the selection may be an array of multiple objects.

Parameters:

Name

Type

Description

selection

Object

The object to be set as selected

Example
//an array of 2 people who are currently selected
var selection = [ 
{type: "opensocial.person", dataObject: bob}, {type: "opensocial.person", dataObject: joe} ];
gadgets.selection.setSelection(selection);

getSelection

<static> Type: Object[] gadgets.selection.getSelection()

Description: Gets the current selection for the container. May be called by either gadgets or the container. 
Returns:

Type

Description

Object[]

The array of currently selected objects, with a type and dataObject properties

Example
var selection = gadgets.selection.getSelection();
window.alert("selection is a " + selection[0].type + " of value= " + selection[0].dataObject);

addListener

<static> gadgets.selection.addListener(event, callback) 
Description: Registers the callback function to be called for the specified selection event. 

Example
function selectionChanged(selection)
{
   ...
}

gadgets.selection.addEventListener('selection', selectionChanged);

Name

Type

Description

event

String

The type of selection event [selection | preselection | postselection]

callback

Function

The callback function that is called for the specified selection event

removeListener

<static> gadgets.selection.removeListener(event, callback) 
Description: Unregisters the callback function to be called whenever for the specified selection event. 
Parameters:

Name

Type

Description

event

String

The type of selection event [selection | preselection | postselection]

callback

Function

The callback function to be unregistered

container.selection

These APIs are accessible only to the container.  

init

<static> container.selection.init ()

Description: Initializes the selection service for the container.

gadgets.actions

Allows gadgets to contribute actions to types and container defined places, such as toolbar buttons and menus in the container’s UI.

addAction

<static> gadgets.actions.addAction (action)

Description: Adds the specified action to the container. May be called from the container or gadget.

Example
var myaction = {

    id: "com.acme.action",
    label: "My Action",    callback: mycallback,
    path: "container/navigationLinks"    
    tooltip: "My Action Tooltip",
    icon: "images/myicon.png",
    iconOffset: {x: "13", y: "15"}, 
    size: {w: "13", h: "15"},
    style: "ICON_AND_LABEL"
}

gadgets.actions.addAction(myaction);

attribute

Description

Required/Optional

 

id

ID

required

 

label

Text string to display for this action

required

 

path

The container defined location for this action.  Example: path=container/menus/Edit

Optional*

 

dataType

The Open Social Data type to contribute this action to. Ex opensocial.Person

Optional*

 

tooltip

Tooltip to display on hovering over an icon

optional

 

icon

Path to image icon.  Absolute URL or URL relative to gadget URL

optional

 

size

w and height of the image

optional

 

iconOffset

Used with image sprites

optional

 

style

Style to be displayed, ICON_AND_LABEL.  Container may choose to ignore/obey depending on its own UI design

optional

 

view

The view ID the gadget shoudl navigateTo(viewId) when the action is invoked.

optional

 

* Either path or dataType MUST be specified.

Example contributing an action to a container's Person object
var mycallback = function(){
	//get the person object from the selection
	var sel = container.selection.getSelection();
	if(selection.type == "opensocial.Person"){
        	//call them by email address using my fiction VOIP service
 		var person = selection.dataObject;
		myVOIP.call(person.displayName,person.emails[0]);
	}
};


var myaction = {

    id: "com.acme.mycallaction",
    tooltip: "My Action Tooltip",
    label: "Call Person",
    icon: "images/myicon.png",
    iconOffset: {x: "13", y: "15"},
    style: "ICON_AND_LABEL",
    callback: mycallback,
    dataType: "opensocial.Person"
}

gadgets.actions.addAction(myaction);

Parameters:

Name

Type

Description

action

Object

The action to be added to the container

removeAction

<static> container.actions.removeAction (action)

Description: Removes the specified action to the container. May be called from the container or gadget.

Parameters:

Name

Type

Description

action

Object

The action to be removed from the container

updateAction

<static> gadgets.actions.updateAction (action)

Description: Updates the specified action in the container with the specified callback function. 

Example
var myaction = {
    id: "com.acme.action",
    callback: myCallbackFunction
}

gadgets.actions.updateAction(myaction);

container.actions 

These APIs can be used by the container to render the actions consistently in the global contribution areas (ie. navigation links at the top) or as part of each gadget's dropdown menu (ie. in the gadget's titlebar), providing a consistent user experience.  These APIs could also be accessible from gadgets to let them leverage actions contributed by other gadgets, specifically getActionsByType(), but from a UI perspective this could be confusing since each gadget might render contributed actions differently than the parent container.  This limitation can easily be removed if use cases exist.

getActions()

<static> container.actions.getActions ()

Description: Returns an array of action objects currently active in the container.  May only be called from the container

getActionsByPath()

<static> container.actions.getActionsByPath (path)

Description: Returns an array of action objects bound to a specific path.  May only be called from the container.  

For gadget specific contributions this API returns the global view of the action registry. For example a gadget contributes gadget/menu/myaction1 this function would return  <gadgetID>/gadget/menu/myaction1

getActionsByDataType()

<static> container.actions.getActionsByObject(dataType)

Description: Returns an array of action objects bound to a specific data type.  May only be called from the container.

Container only API, not accessible from gadgets

runAction()

<static> container.actions.runAction(actionId)

Description: Makes RPC call to ask the gadget to run the callback function associated with the actionId in the context of the gadget.  This is used as the callback function referenced in registerShowActionHandler()

registerShowActionHandler()

<static> container.actions.registerShowActionHandler(handler)

Description: Allows the container to implement its specific UI to draw actions for any dataType or path as specified in the actionObject.  The container registers the handler, and the actions feature calls the handler upon preloading of a gadget when its metadata containing declarative actions is read.

Example
var handler = function(actionObject){
       // draw the UI, toolbars menus, etc using your containers JS library
       // to do the invocation of the action, call the following API:
       // container.actions.runAction(actionObject.id);  
}
container.actions.registerShowActionHandler(handler);

registerHideActionHandler()

<static> container.actions.registerHideActionHandler(handler)

Description: Allows the container to implement its specific code for removing actions from the UI.  The container registers the handler, and the actions feature calls the handler upon unloading of a gadget.

Example
var handler = function(actionObject){
       // hide the UI, toolbars menus, etc using your containers JS library
}
container.actions.registerHideActionHandler(handler);

registerNavigateGadgetHandler()

<static> container.actions.registerActionNavigateGadgetHandler(handler)

Description: Allows the container to implement its specific UI to show a requested gadget as requested by a declarative action.  The container registers the handler, and the actions feature calls the handler upon execution of an action, if the gadget is not yet rendered. The container may re-use the gadget if it is already visible on the page, or may choose to render it in a new or existing gadget view site.  For example some containers could choose to render the gadget for these actions in a floating div area while others do so on a sidebar.

Example
var handler = function(gadgetUrl, opt_params){
    // invoke common container APIs to show the appropriate gadget
    // ie. wrapper around call to CommonContainer.navigateGadget(site, gadgetUrl, viewParams, renderParams, opt_callback);
    // or return the gadgetID of an existing visble gadget navigatingTo the optional viewId.
    return gadgetId;
}
container.actions.registerNavigateGadgetHandler(handler);