AppSuite:Extending the UI: Difference between revisions

From Open-Xchange
mNo edit summary
Line 1: Line 1:
<div class="title">Extending the UI </div>
<div class="title">Extending the UI </div>
__TOC__
__TOC__
=Introduction=
==Introduction==
Abstractly speaking extension points are an architecture for letting plugins contribute functionality to other parts of the program. They form the core of the OX App Suite plugin architecture. A less detailed hands-on introduction can be found [[ AppSuite:Extending_the_UI_(Hands-on_introduction) | here]]. Some basics about the extention point concept and advantages compared to inheritance.
Abstractly speaking extension points are an architecture for letting plugins contribute functionality to other parts of the program. They form the core of the OX App Suite plugin architecture. A less detailed hands-on introduction can be found [[ AppSuite:Extending_the_UI_(Hands-on_introduction) | here]]. Some basics about the extention point concept and advantages compared to inheritance.


== inheritance vs. extension points ==
=== inheritance vs. extension points ===


OX App Suite uses the extension point concept to create extension points that allow a simple and flexible way to extending functionality. When system reaches a part that can be extended it asks a central registry if extensions are registered. In that case these extensions will be executes independent of the providing component (some plugin or OX App Suite itself).
OX App Suite uses the extension point concept to create extension points that allow a simple and flexible way to extending functionality. When system reaches a part that can be extended it asks a central registry if extensions are registered. In that case these extensions will be executes independent of the providing component (some plugin or OX App Suite itself).
Line 18: Line 18:




== Some characteristics ==
=== Some characteristics ===


* good fences: extension points unregister corrupt extenders
* good fences: extension points unregister corrupt extenders
Line 26: Line 26:




== Components ==
=== Components ===


The extension point system lives in the 'io.ox/core/extensions' module and consists of these elements:
The extension point system lives in the 'io.ox/core/extensions' module and consists of these elements:
Line 35: Line 35:
* baton: object used to store context, passed back through callbacks
* baton: object used to store context, passed back through callbacks


= Extension Point System =
 
== Extension Point System ==


<pre class="language-javascript"> //load extension points module
<pre class="language-javascript"> //load extension points module
Line 42: Line 43:
  });</pre>
  });</pre>


= Registry =
 
== Registry ==


* manages extension points, extensions and their state
* manages extension points, extensions and their state




== list points ==
=== list points ===


<pre class="language-javascript"> // returns array of point ids
<pre class="language-javascript"> // returns array of point ids
Line 53: Line 55:




== get/create point ==
=== get/create point ===


<pre class="language-javascript"> //also registers point if not created yet
<pre class="language-javascript"> //also registers point if not created yet
  var mypoint = ext.point('io.ox/calendar/detail');</pre>
  var mypoint = ext.point('io.ox/calendar/detail');</pre>


= Extension Point =
 
== Extension Point ==


* part of the systems that can be extended, referenced by a unique id
* part of the systems that can be extended, referenced by a unique id
Line 64: Line 67:




== attributes ==
=== attributes ===


* id
* id
Line 78: Line 81:




== add extension ==
=== add extension ===


* important: existing extensions with same id will not be overwritten - use replace insted
* important: existing extensions with same id will not be overwritten - use replace insted
Line 96: Line 99:




== replace extension ==
=== replace extension ===


* important: only extension properties will be replaced (jquery extend)
* important: only extension properties will be replaced (jquery extend)
Line 114: Line 117:




== use extensions==
=== use extensions===


* invoking point extensions by defining functionname, context and baton
* invoking point extensions by defining functionname, context and baton
Line 131: Line 134:




== access extensions ==
=== access extensions ===


<pre class="language-javascript"> //returns array containing all extension ids
<pre class="language-javascript"> //returns array containing all extension ids
Line 153: Line 156:




== enabling/disabling ==
=== enabling/disabling ===


<pre class="language-javascript"> var enabled = mypoint.isEnabled();</pre>
<pre class="language-javascript"> var enabled = mypoint.isEnabled();</pre>
Line 165: Line 168:
  ext.point('io.ox/mail/detail/header').disable('receiveddate');</pre>
  ext.point('io.ox/mail/detail/header').disable('receiveddate');</pre>


== underscore equivalents ==
=== underscore equivalents ===


* only considers enabled extensions
* only considers enabled extensions
Line 185: Line 188:




== Event Hub ==
=== Event Hub ===


* Event Hub based on jQuery's on, off, one, and trigger
* Event Hub based on jQuery's on, off, one, and trigger
Line 204: Line 207:




= Extension =
== Extension ==


* adding/replacing functionality during runtime
* adding/replacing functionality during runtime
Line 210: Line 213:




== Attributes ==
=== Attributes ===


* id
* id
Line 230: Line 233:




== extensions patterns ==
=== extensions patterns ===




Line 274: Line 277:
*Engine
*Engine


= Baton =
 
== Baton ==


Part of extension points system is a structure called baton which serves as an context object. The baton ipassed back through callbacks within programmatic flow so data exchange between extension points is simple.
Part of extension points system is a structure called baton which serves as an context object. The baton ipassed back through callbacks within programmatic flow so data exchange between extension points is simple.




== attributes ==
=== attributes ===


* data: usually contains current entity object, also used for data exchange
* data: usually contains current entity object, also used for data exchange
Line 288: Line 292:




== disable extensions ==
=== disable extensions ===


<pre class="language-javascript"> //disable
<pre class="language-javascript"> //disable
Line 312: Line 316:




== data exchange ==
=== data exchange ===




Line 356: Line 360:




== ensure ==
=== ensure ===
* ensure that submitted object is instanceof baton
* ensure that submitted object is instanceof baton
* return obj if it's an instanceof baton
* return obj if it's an instanceof baton
Line 373: Line 377:


<!--
<!--
=== wrapp ===
==== wrapp ====


* wrap a baton 'around' submitted object
* wrap a baton 'around' submitted object

Revision as of 13:52, 11 April 2013

Extending the UI

Introduction

Abstractly speaking extension points are an architecture for letting plugins contribute functionality to other parts of the program. They form the core of the OX App Suite plugin architecture. A less detailed hands-on introduction can be found here. Some basics about the extention point concept and advantages compared to inheritance.

inheritance vs. extension points

OX App Suite uses the extension point concept to create extension points that allow a simple and flexible way to extending functionality. When system reaches a part that can be extended it asks a central registry if extensions are registered. In that case these extensions will be executes independent of the providing component (some plugin or OX App Suite itself).

The illustrated example compares inheritance and extension points. The main benefit of using extension points ist that the programm is still the active component and is in controll. This leads to the following advantages:

  • Reduced coupling
  • Increased cohesion
  • Modularity- Re-usability
  • Dynamic
Ui ext 01.gif


Some characteristics

  • good fences: extension points unregister corrupt extenders
  • lazy loading: extenders are loaded if they are used
  • fair play: all extenders have equal rights
  • diversity: extension points support different extension


Components

The extension point system lives in the 'io.ox/core/extensions' module and consists of these elements:

  • extension point system: accessing the outer parts
  • registry: manages extension points, extensions and their state
  • extension point: part of the systems that can be extended, referenced by a unique id
  • extension: adding/replacing functionality during runtime, referenced by a unique id
  • baton: object used to store context, passed back through callbacks


Extension Point System

 //load extension points module
 require(['io.ox/core/extensions'], function (ext) {
     //insert code here
 });


Registry

  • manages extension points, extensions and their state


list points

 // returns array of point ids
 var keys = ext.keys();


get/create point

 //also registers point if not created yet
 var mypoint = ext.point('io.ox/calendar/detail');


Extension Point

  • part of the systems that can be extended, referenced by a unique id
  • defienes as some kind of contract about what it expects its extensions to provide


attributes

  • id
  • description (optional)


example

 //get a point and get it's description
 var point = ext.point('io.ox/mail/links/toolbar'),
     descr = point.description = '';


add extension

  • important: existing extensions with same id will not be overwritten - use replace insted
  • example: add extension with id 'date'


example

 // chainable (returns mypoint)
 point.extend({
     id: 'example1', // Every extension is supposed to have an id
     index: 100,     // Extensions are ordered based on their indexes
     draw: function () {
         //draw something
     }
 });


replace extension

  • important: only extension properties will be replaced (jquery extend)
  • hint: replace can also be executed before extension is intially created with extend function


example

 // chainable (returns mypoint)
 mypoint.replace({
     id: 'example1',
     index: 100,
     draw: function (baton) {
         //draw something completely different
     }
 });


use extensions

  • invoking point extensions by defining functionname, context and baton
  • node used as context (function is called via apply(node, args))
  • baton forwarded within programmatic flow and used for storing and exchanging data between extensions
 mypoint.invoke(name, context, baton);


example

 //call 'draw' of all registered extensions (order defined by index attribute)
 //node used as context ('draw' function is called via apply(node, args))
 //baton's data property contains relevant information about current entity (for example a mail)
 mypoint.invoke('draw', node, baton);


access extensions

 //returns array containing all extension ids
 mypoint.keys();
 //returns array containing all extensions
 mypoint.all();
 //executes callback for a specific extension
 //chainable (returns point)
 mypoint.get(id, callback);
 //disabled extension will return true also;
 var exists = mypoint.has(id);


enabled only

 //returns array containing all enabled extensions
 mypoint.list()
 //returns number containing enabled extensions
 mypoint.count()


enabling/disabling

 var enabled = mypoint.isEnabled();
 //chainable (returns mypoint)
 mypoint.enable(id);
 //chainable (returns mypoint)
 mypoint.disable(id);

example

 //disable
 ext.point('io.ox/mail/detail/header').disable('receiveddate');

underscore equivalents

  • only considers enabled extensions
  • functions returns underscore-chain object of enabled extensions
  • take a look at http://underscorejs.org/ for more details
 mypoint.chain()
 mypoint.each(callback)
 mypoint.map(callback)
 mypoint.select(callback) //filter alias
 mypoint.inject(callback, memo) //reduce alias
 mypoint.pluck(propertyName)

example

 // Shuffle extension order
 ext.point('io.ox/calendar/detail').each(function (e) {
     e.index = Math.random() * 1000 >> 0;
 }).sort();


Event Hub

  • Event Hub based on jQuery's on, off, one, and trigger
  • differences documentated for each function
 // attach listener
 mypoint.on(type, data, function)
 // detach listener
 mypoint.off(type, function)
 // attach listener for a single execution
 mypoint.one(type, data, function)
 // trigger event
 // difference: allows multiple types separated by spaces.
 // difference: actually calls triggerHandler since we are not in the DOM.
 mypoint.trigger(types)
 // explicit destroy to clean up.
 mypoint.destroy()


Extension

  • adding/replacing functionality during runtime
  • referenced by a unique id


Attributes

  • id
  • index (optional): numeric value used for specify order of execution (valid values also 'first', 'last')
  • functions: as required by the extension point contract


'example

 
//defining a extension for some extension point that requires a draw function
{
    id: 'example1',
    index: 100,
    draw: function () {
        //draw something
    }
};


extensions patterns

io.ox/backbone/forms.js

  • CheckBoxField
  • ControlGroup
  • DateControlGroup
  • DatePicker
  • ErrorAlert
  • Header
  • InputField
  • Section
  • SectionLegend
  • SelectBoxField
  • SelectControlGroup


io.ox/backbone/views.js

  • AttributeView


io.ox/calendar/edit/recurrence-view

  • RecurrenceView


io.ox/core/extPatterns/links

  • Button
  • DropdownLinks
  • InlineLinks
  • link
  • ToolbarLinks


io.ox/core/tk/attachments

  • AttachmentList
  • EditableAttachmentList


io.ox/contacts/widgets/pictureUpload.js


io.ox/preview/main.js

  • Engine


Baton

Part of extension points system is a structure called baton which serves as an context object. The baton ipassed back through callbacks within programmatic flow so data exchange between extension points is simple.


attributes

  • data: usually contains current entity object, also used for data exchange
  • options:
  • flow:
    • disabled: stores disabled extensions (managed via baton.disable(pointId, extensionId))
  • $: used to reference a jquery node object


disable extensions

 //disable
 baton.disable(pointid, extensionid);

 //is disabled
 var isDisabled = baton.isDisabled(pointid, extensionid);


example

 var pointid = 'io.ox/mail/detail',
     extensionid = 'example3',
     node = $('div'),
     baton = ext.Baton();

 //disable extension
 //returns undefined
 baton.disable(pointid,extensionid);

 //invoke extension with baton instance 
 ext.point(pointid).invoke('draw', node, baton)


data exchange

example

 //extension using baton to store data
 {
     id: 'example1',
     index: 100,
     draw: function (baton) {
         //get the currenty process mail object
         var mail = baton.data;
         //append subject to current node referenced as this
         this.append(
             $('div').text(mail.subject);
         )
         //extend mail object to store some flag
         mail.drawn = mail.drawn || {};
         mail.drawn.subject = true;
         //disable extension3
         baton.disabel()
     }
 };
 {
     id: 'example2',
     index: 200,
     draw: function (baton) {
         //get the currenty process mail object
         var mail = baton.data;
         //use value set by 'example1'
         if(mail && mail.drawn && mail.drawn.subject) {
             //do something
         }
     }
 };
 {
     id: 'example3',
     index: 300,
     draw: function (baton) {
         //wil not be executed if baton from 'disable example' is used
     }
 };


ensure

  • ensure that submitted object is instanceof baton
  • return obj if it's an instanceof baton
  • return new baton instance where baton.data is extended by obj or obj.date (if exists)
 var baton = ext.Baton.ensure(obj) 


example

 //new baton.data extended by object
 var baton = ext.Baton.ensure({ id: 2 })

Ui_ext_02.png