Difference between revisions of "AppSuite:Extending the UI"

m
 
(36 intermediate revisions by 5 users not shown)
Line 1: Line 1:
<div class="title">Extending the UI </div>
+
The content on this page has moved to https://documentation.open-xchange.com/latest/ui/extension-points.html
__TOC__
 
=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.
 
  
== inheritance vs. extension points ==
+
Note: Open-Xchange is in the process of migrating all its technical documentation to a new and improved documentation system (documentation.open-xchange.com). Please note as the migration takes place more information will be available on the new system and less on this system. Thank you for your understanding during this period of transition.
 
 
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
 
 
 
[[File:Ui_ext_01.gif|frame|border|left]]
 
 
 
 
 
== 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 =
 
 
 
<pre class="language-javascript">//load extension points module
 
require(['io.ox/core/extensions'], function (ext) {
 
    //insert code here
 
});</pre>
 
 
 
= Registry =
 
 
 
* manages extension points, extensions and their state
 
 
 
 
 
== list points ==
 
 
 
<pre class="language-javascript">// returns array of point ids
 
var keys = ext.keys();</pre>
 
 
 
 
 
== get/create point ==
 
 
 
<pre class="language-javascript">//also registers point if not created yet
 
var mypoint = ext.point('io.ox/calendar/detail');</pre>
 
 
 
= 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'''''
 
 
 
<pre class="language-javascript">//get a point and get it's description
 
var point = ext.point('io.ox/mail/links/toolbar'),
 
    descr = point.description = '';
 
</pre>
 
 
 
 
 
== add extension ==
 
 
 
* important: existing extensions with same id will not be overwritten - use replace insted
 
* example: add extension with id 'date'
 
 
 
 
 
'''''example'''''
 
 
 
<pre class="language-javascript">// 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
 
    }
 
});</pre>
 
 
 
 
 
== 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'''''
 
 
 
<pre class="language-javascript">// chainable (returns mypoint)
 
mypoint.replace({
 
    id: 'example1',
 
    index: 100,
 
    draw: function (baton) {
 
        //draw something completely different
 
    }
 
});</pre>
 
 
 
 
 
== 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
 
 
 
<pre class="language-javascript">mypoint.invoke(name, context, baton);</pre>
 
 
 
 
 
'''''example'''''
 
 
 
<pre class="language-javascript">//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);</pre>
 
 
 
 
 
== access extensions ==
 
 
 
<pre class="language-javascript">//returns array containing all extension ids
 
mypoint.keys();</pre>
 
<pre class="language-javascript">//returns array containing all extensions
 
mypoint.all();</pre>
 
<pre class="language-javascript">//executes callback for a specific extension
 
//chainable (returns point)
 
mypoint.get(id, callback);</pre>
 
<pre class="language-javascript">//disabled extension will return true also;
 
var exists = mypoint.has(id);</pre>
 
 
 
 
 
'''''enabled only'''''
 
 
 
<pre class="language-javascript">//returns array containing all enabled extensions
 
mypoint.list()</pre>
 
<pre class="language-javascript">//returns number containing enabled extensions
 
mypoint.count()
 
</pre>
 
 
 
 
 
== disabling and enabling ==
 
 
 
<pre class="language-javascript">var enabled = mypoint.isEnabled();</pre>
 
<pre class="language-javascript">//chainable (returns mypoint)
 
mypoint.enable(id);</pre>
 
<pre class="language-javascript">//chainable (returns mypoint)
 
mypoint.disable(id);</pre>
 
'''example'''
 
 
 
<pre class="language-javascript">//disable
 
ext.point('io.ox/mail/detail/header').disable('receiveddate');</pre>
 
 
 
 
 
== underscore equivalents ==
 
 
 
* only considers enabled extensions
 
* functions returns underscore-chain object of enabled extensions
 
* take a look at http://underscorejs.org/ for more details
 
 
 
<pre class="language-javascript">mypoint.chain()
 
mypoint.each(callback)
 
mypoint.map(callback)
 
mypoint.select(callback) //filter alias
 
mypoint.inject(callback, memo) //reduce alias
 
mypoint.pluck(propertyName)</pre>
 
'''example'''
 
 
 
<pre class="language-javascript">// Shuffle extension order
 
ext.point('io.ox/calendar/detail').each(function (e) {
 
    e.index = Math.random() * 1000 &gt;&gt; 0;
 
}).sort();</pre>
 
 
 
 
 
== Event Hub ==
 
 
 
* Event Hub based on jQuery's on, off, one, and trigger
 
* differences documentated for each function
 
 
 
<pre class="language-javascript">// attach listener
 
mypoint.on(type, data, function)</pre>
 
<pre class="language-javascript">// detach listener
 
mypoint.off(type, function)</pre>
 
<pre class="language-javascript">// attach listener for a single execution
 
mypoint.one(type, data, function)</pre>
 
<pre class="language-javascript">// trigger event
 
// difference: allows multiple types separated by spaces.
 
// difference: actually calls triggerHandler since we are not in the DOM.
 
mypoint.trigger(types)</pre>
 
<pre class="language-javascript">// explicit destroy to clean up.
 
mypoint.destroy()</pre>
 
 
 
 
 
= 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 ''*''
 
 
 
<pre class="language-javascript">//defining a extension for some extension point that requires a draw function
 
{
 
    id: 'example1',
 
    index: 100,
 
    draw: function () {
 
        //draw something
 
    }
 
};</pre>
 
 
 
 
 
== extension subclasses!!!!!!!!!!!!!!!!!!!!!!!!!! ==
 
 
 
* default: object
 
* special: InlineLinks
 
 
 
 
 
= 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))<br />
 
* $: used to reference a jquery node object
 
 
 
 
 
== disable extensions ==
 
 
 
<pre class="language-javascript">//disable
 
baton.disable(pointid, extensionid);
 
 
 
//is disabled
 
var isDisabled = baton.isDisabled(pointid, extensionid);</pre>
 
 
 
 
 
''*'' example ''*''
 
 
 
<pre class="language-javascript">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)</pre>
 
 
 
 
 
== data exchange ==
 
 
 
 
 
''*'' example ''*''
 
 
 
<pre class="language-javascript">//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 &amp;&amp; mail.drawn &amp;&amp; mail.drawn.subject) {
 
            //do something
 
        }
 
    }
 
};
 
{
 
    id: 'example3',
 
    index: 300,
 
    draw: function (baton) {
 
        //wil not be executed if baton from 'disable example' is used
 
    }
 
};
 
</pre>
 
 
 
 
 
== utils ==
 
 
 
 
 
=== 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)
 
 
 
<pre class="language-javascript">
 
baton = ext.Baton.ensure(obj) ```
 
</pre>
 
 
 
 
 
'''example'''
 
 
 
<pre class="language-javascript">//new baton.data extended by object
 
ext.Baton.ensure({ id: 2 })</pre>
 
 
 
[[File:Ui_ext_02.png|frame|border|left]]
 
 
 
 
 
=== wrapp ===
 
 
 
* wrap a baton 'around' submitted object
 
* return if it's an instanceof baton
 
* return new baton instance '''''where baton''''' is extended by obj '''or''' obj.date (if exists)
 
 
 
 
 
'''example'''
 
 
 
<pre class="language-javascript">//new baton object extended by obj
 
ext.Baton.wrap({ id: 2 })</pre>
 
 
 
[[File:Ui_ext_03.png|frame|border|left]]
 
 
 
[[Category:AppSuite]]
 
[[Category:UI]]
 

Latest revision as of 09:47, 22 May 2017

The content on this page has moved to https://documentation.open-xchange.com/latest/ui/extension-points.html

Note: Open-Xchange is in the process of migrating all its technical documentation to a new and improved documentation system (documentation.open-xchange.com). Please note as the migration takes place more information will be available on the new system and less on this system. Thank you for your understanding during this period of transition.