<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.open-xchange.com/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Frank.paczynski</id>
	<title>Open-Xchange - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.open-xchange.com/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Frank.paczynski"/>
	<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=Special:Contributions/Frank.paczynski"/>
	<updated>2026-06-30T23:21:56Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Configuring_portal_plugins&amp;diff=23131</id>
		<title>AppSuite:Configuring portal plugins</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Configuring_portal_plugins&amp;diff=23131"/>
		<updated>2017-03-16T10:30:18Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-stable}}&lt;br /&gt;
{{VersionFrom|7.4.0}}&lt;br /&gt;
&lt;br /&gt;
= Configuring portal plugins =&lt;br /&gt;
&lt;br /&gt;
''Synopsis:'' This article covers how to configure which plugins (&amp;quot;tiles&amp;quot;) are shown, whether they are mandatory or just suggested to the user.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Configuring the portal ==&lt;br /&gt;
&lt;br /&gt;
When running OX AppSuite you may want to specify a starting configuration for which tiles the portal shows and whether certain tiles are mandatory or not. This is especially useful when you are introducing your own tile implementations. To make this possible, the portal consists of three types of tiles: User tiles, eager tiles and protected tiles. User tiles are tiles that the user added herself to the portal page, eager tiles are those suggested by the installation which can be removed and protected tiles are set by the backend. &lt;br /&gt;
&lt;br /&gt;
In order to specify a tile, you will have to know about the configuration data to enter. You can use an appsuite installation to do that. If you want to configure a tile as eager or protected, navigate to the settings area of the portal page, add the tile you want and, in the JS console, enter:&lt;br /&gt;
&lt;br /&gt;
 require(&amp;quot;settings!io.ox/portal&amp;quot;).get(&amp;quot;widgets/user&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
and in the list find the tile you want to suggest to or force on your users. As an example, we'll use the birthday widget:&lt;br /&gt;
&lt;br /&gt;
 birthdays_0: Object&lt;br /&gt;
    color: &amp;quot;lightgreen&amp;quot;&lt;br /&gt;
    enabled: true&lt;br /&gt;
    id: &amp;quot;birthdays_0&amp;quot;&lt;br /&gt;
    index: 6&lt;br /&gt;
    inverse: false&lt;br /&gt;
    plugin: &amp;quot;plugins/portal/birthdays/register&amp;quot;&lt;br /&gt;
    props: Object&lt;br /&gt;
    type: &amp;quot;birthdays&amp;quot;&lt;br /&gt;
&lt;br /&gt;
In order to configure widgets on the backend we have to turn the above into a valid YAML structure:&lt;br /&gt;
&lt;br /&gt;
 birthdays_0:&lt;br /&gt;
    color: &amp;quot;lightgreen&amp;quot; # one of black, red, orange, lightgreen, &lt;br /&gt;
                        # green, lightblue, blue, purple, pink, gray&lt;br /&gt;
    enabled: true       # Has to be true &lt;br /&gt;
    index: &amp;quot;first&amp;quot;      # Where the widget is supposed to show up. &lt;br /&gt;
                        # Possible values are numbers, &amp;quot;first&amp;quot; or &lt;br /&gt;
                        # &amp;quot;last&amp;quot; &lt;br /&gt;
    inverse: false      # If true, the color is applied to the body &lt;br /&gt;
                        # of the tile and not the title. Can highlight &lt;br /&gt;
                        # particular tiles&lt;br /&gt;
    plugin: &amp;quot;plugins/portal/birthdays/register&amp;quot; # The source file that contains the tile code&lt;br /&gt;
    type: &amp;quot;birthdays&amp;quot;   # The id of the widget type. Not the id above &lt;br /&gt;
                        # has to have the form [type]_[someNumber]&lt;br /&gt;
&lt;br /&gt;
Now we can use this snippet to configure the birthday widget in a variety of ways&lt;br /&gt;
&lt;br /&gt;
== I want to suggest a widget, but the user can remove it, if they don't like it ==&lt;br /&gt;
&lt;br /&gt;
For this, you can use the &amp;quot;eager&amp;quot; configuration method. Not that if you specify an eager or protected widget, all default widgets from Open-Xchange will not be configured as defaults anymore, so you might want to configure them as eager tiles as well. But let's stick to the birthday widget for now. &lt;br /&gt;
&lt;br /&gt;
* Add a file /opt/open-xchange/etc/settings/portal.yml&lt;br /&gt;
&lt;br /&gt;
 io.ox/portal//widgets/eager/gen_0:&lt;br /&gt;
    birthdays_0:&lt;br /&gt;
        color: &amp;quot;lightgreen&amp;quot;&lt;br /&gt;
        enabled: true&lt;br /&gt;
        index: &amp;quot;first&amp;quot;&lt;br /&gt;
        inverse: false&lt;br /&gt;
        plugin: &amp;quot;plugins/portal/birthdays/register&amp;quot;&lt;br /&gt;
        type: &amp;quot;birthdays&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This means the widget will be enabled for users, but users can still disable it. If you want to, at a later time and since you've made significant improvements to the tile, want to show it to the user again, you have to increase the &amp;quot;generation&amp;quot; of the widget configuration and offer it again:&lt;br /&gt;
&lt;br /&gt;
 io.ox/portal/:&lt;br /&gt;
    generation: 1&lt;br /&gt;
&lt;br /&gt;
 io.ox/portal//widgets/eager/gen_0:&lt;br /&gt;
    birthdays_0:&lt;br /&gt;
        color: &amp;quot;lightgreen&amp;quot;&lt;br /&gt;
        enabled: true&lt;br /&gt;
        index: &amp;quot;first&amp;quot;&lt;br /&gt;
        inverse: false&lt;br /&gt;
        plugin: &amp;quot;plugins/portal/birthdays/register&amp;quot;&lt;br /&gt;
        type: &amp;quot;birthdays&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 io.ox/portal//widgets/eager/gen_1:&lt;br /&gt;
    birthdays_0:&lt;br /&gt;
        color: &amp;quot;lightgreen&amp;quot;&lt;br /&gt;
        enabled: true&lt;br /&gt;
        index: &amp;quot;first&amp;quot;&lt;br /&gt;
        inverse: false&lt;br /&gt;
        plugin: &amp;quot;plugins/portal/birthdays/register&amp;quot;&lt;br /&gt;
        type: &amp;quot;birthdays&amp;quot;&lt;br /&gt;
&lt;br /&gt;
All the &amp;quot;gen_[number]&amp;quot; entries up the tie io.ox/portal//generation will be used for this configuration. If a tile is deleted it is deleted only in that generation so can be reintroduced in a later portal configuration generation. If you want to keep the default tiles as used as a fallback for App Suite, you need this configuration to start out with:&lt;br /&gt;
&lt;br /&gt;
 io.ox/portal//widgets/eager/gen_0:&lt;br /&gt;
    mail_0: &lt;br /&gt;
        plugin: 'plugins/portal/mail/register'&lt;br /&gt;
        color: 'blue'&lt;br /&gt;
        enabled: true&lt;br /&gt;
        index: 1&lt;br /&gt;
    calendar_0: &lt;br /&gt;
        plugin: 'plugins/portal/calendar/register'&lt;br /&gt;
        color: 'red'&lt;br /&gt;
        enabled: true&lt;br /&gt;
        index: 2&lt;br /&gt;
    tasks_0: &lt;br /&gt;
        plugin: 'plugins/portal/tasks/register'&lt;br /&gt;
        color: 'green'&lt;br /&gt;
        enabled: true&lt;br /&gt;
        index: 3&lt;br /&gt;
    birthdays_0:&lt;br /&gt;
        plugin: 'plugins/portal/birthdays/register'&lt;br /&gt;
        color: 'lightgreen'&lt;br /&gt;
        enabled: true&lt;br /&gt;
        index: 4&lt;br /&gt;
    twitter_0:&lt;br /&gt;
        plugin: 'plugins/portal/twitter/register'&lt;br /&gt;
        color: 'pink'&lt;br /&gt;
        enabled: true&lt;br /&gt;
        index: 5&lt;br /&gt;
    linkedin_0:&lt;br /&gt;
        plugin: 'plugins/portal/linkedin/register'&lt;br /&gt;
        color: 'lightblue'&lt;br /&gt;
        enabled: true&lt;br /&gt;
        index: 6&lt;br /&gt;
    &lt;br /&gt;
And then modify this as wanted for your deployment&lt;br /&gt;
&lt;br /&gt;
== Forcing a tile == &lt;br /&gt;
&lt;br /&gt;
Similarly to the eager tiles, tiles can be &amp;quot;protected&amp;quot;, i.e. they can not be moved, removed and disabled. The configuration for this looks like that:&lt;br /&gt;
&lt;br /&gt;
 io.ox/portal//widgets/protected:&lt;br /&gt;
    birthdays_0:&lt;br /&gt;
        color: &amp;quot;lightgreen&amp;quot;&lt;br /&gt;
        enabled: true&lt;br /&gt;
        index: &amp;quot;first&amp;quot;&lt;br /&gt;
        inverse: false&lt;br /&gt;
        plugin: &amp;quot;plugins/portal/birthdays/register&amp;quot;&lt;br /&gt;
        type: &amp;quot;birthdays&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to allow movement of the widget, you can enable this in the following way:&lt;br /&gt;
&lt;br /&gt;
 io.ox/portal//widgets/protected:&lt;br /&gt;
    birthdays_0:&lt;br /&gt;
        color: &amp;quot;lightgreen&amp;quot;&lt;br /&gt;
        enabled: true&lt;br /&gt;
        index: &amp;quot;first&amp;quot;&lt;br /&gt;
        inverse: false&lt;br /&gt;
        plugin: &amp;quot;plugins/portal/birthdays/register&amp;quot;&lt;br /&gt;
        type: &amp;quot;birthdays&amp;quot;&lt;br /&gt;
        changeable:&lt;br /&gt;
            index: true&lt;br /&gt;
&lt;br /&gt;
In which case, users will be allowed to move the tile. &lt;br /&gt;
&lt;br /&gt;
One caveat in all this: After changing any of the above configuration, you will have to reload the UI *twice*, since App Suite uses read-through caching for the settings data.&lt;br /&gt;
&lt;br /&gt;
== Disabling a tile completely ==&lt;br /&gt;
The combination of making a tile both protected and disabling it makes it impossible for the user to enable it. From 7.6.0 on, this means it is not shown in the settings either. Before that, it was greyed out but still present.&lt;br /&gt;
&lt;br /&gt;
  io.ox/portal/:&lt;br /&gt;
    widgets:&lt;br /&gt;
      protected:&lt;br /&gt;
        quota_0:&lt;br /&gt;
          color: &amp;quot;red&amp;quot;&lt;br /&gt;
          id: &amp;quot;quota_0&amp;quot;&lt;br /&gt;
          enabled: false&lt;br /&gt;
          index: 1&lt;br /&gt;
          inverse: false&lt;br /&gt;
          plugin: &amp;quot;plugins/portal/quota/register&amp;quot;&lt;br /&gt;
          type: &amp;quot;quota&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend]][[Category:AppSuite]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Portal]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Administrator]][[Category:Developer]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Guided_tours&amp;diff=21885</id>
		<title>AppSuite:Guided tours</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Guided_tours&amp;diff=21885"/>
		<updated>2016-05-03T09:46:54Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Synopsis:''' Guided tours are series of little steps meant to explain the various functions of OX to an end user. They can be configured by system administrators.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Basic framework ==&lt;br /&gt;
Guided tours are built on [https://github.com/linkedin/hopscotch LinkedIn's hopscotch.js] framework. It forms a series of small information &amp;quot;bubbles&amp;quot; that point to UI elements and display a text as well as small navigation elements.&lt;br /&gt;
&lt;br /&gt;
== State before 7.4.1 ==&lt;br /&gt;
In 7.4.0, Guided Tours could not be configured.&lt;br /&gt;
&lt;br /&gt;
== State after 7.4.1 ==&lt;br /&gt;
=== Package ===&lt;br /&gt;
Guided tours are contained in a separate package, named ''open-xchange-guidedtours''. This will install UI as well as backend components (in the form of a config file).&lt;br /&gt;
&lt;br /&gt;
=== Configuration ===&lt;br /&gt;
Several configuration parameters guide the running of Guided Tours:&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|io.ox/tours//server/disableTours&lt;br /&gt;
|Disabled the tours completely&lt;br /&gt;
|-&lt;br /&gt;
|io.ox/tours//server/disable/$moduleName&lt;br /&gt;
| Disables the tour for a specific module, e.g. &amp;quot;io.ox/tasks&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| io.ox/tours//server/startOnFirstLogin&lt;br /&gt;
| start the tour the first time a user logs in&lt;br /&gt;
|-&lt;br /&gt;
|io.ox/tours//server/version&lt;br /&gt;
|arbitrary integer denoting the version of the tour. If startOnFirstLogin is true, the user might have seen a previous version but not the recent one. Increasing the number makes sure the users sees the updated version again.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Customizing ===&lt;br /&gt;
Tours use [[AppSuite:Extension points|extension points]] as mechanism to extend them. The relevant point is ''io.ox/tours/extensions''.&lt;br /&gt;
&lt;br /&gt;
==== Example tour ====&lt;br /&gt;
A tours looks like this:&lt;br /&gt;
  ext.point('io.ox/tours/extensions').extend({&lt;br /&gt;
    id: 'default/io.ox/intro',&lt;br /&gt;
    app: 'io.ox/intro',&lt;br /&gt;
    priority: 1,&lt;br /&gt;
    tour: {&lt;br /&gt;
      id: 'Switching from OX6',&lt;br /&gt;
      steps: [{&lt;br /&gt;
        title: gt('Launching an app'),&lt;br /&gt;
        placement: 'bottom',&lt;br /&gt;
        target: function () { return $('.launcher[data-app-name=&amp;quot;io.ox/mail&amp;quot;]')[0]; },&lt;br /&gt;
        content: gt('To launch an app, click on an entry on the top-left side of the menu bar.')&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
        onShow: function () { notifications.hideList(); },&lt;br /&gt;
        title: gt('Displaying the help or the settings'),&lt;br /&gt;
        placement: 'left',&lt;br /&gt;
        target: function () { return $('.launcher .icon-cog:visible')[0]; },&lt;br /&gt;
        content: gt('To display the help or the settings, use the icons on the right side of the menu bar.'),&lt;br /&gt;
        arrowOffset: 1,&lt;br /&gt;
        yOffset: -5&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
        onShow: function () { notifications.showList(); },&lt;br /&gt;
        title: gt('New objects icon'),&lt;br /&gt;
        placement: 'left',&lt;br /&gt;
        target: function () { return $('#io-ox-notifications-icon:visible')[0]; },&lt;br /&gt;
        content: gt('The New objects icon shows the number of unread E-Mails or other notifications. If clicking the icon, the info area opens.'),&lt;br /&gt;
        arrowOffset: -1&lt;br /&gt;
      }, [...]&lt;br /&gt;
&lt;br /&gt;
==== Components of a tour ====&lt;br /&gt;
A tours is a set of steps that are can be navigated by going forward and backwards. It needs...&lt;br /&gt;
{|&lt;br /&gt;
| id&lt;br /&gt;
| This needs to be unique, like for all extension points. Even if you want to overwrite another tour, you need a different id!&lt;br /&gt;
|-&lt;br /&gt;
| app&lt;br /&gt;
| The application this tour is related to. &lt;br /&gt;
|-&lt;br /&gt;
| Priority&lt;br /&gt;
| The tour with the largest number for the priority for a specific app is the one shown. This is how you overwrite an existing tour with your custom one.&lt;br /&gt;
|-&lt;br /&gt;
| tour&lt;br /&gt;
| The steps of a tour&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Tour steps ====&lt;br /&gt;
A tour step is a text bubble that is shown next to a UI element. It needs...&lt;br /&gt;
{|&lt;br /&gt;
| title&lt;br /&gt;
| This needs to be unique, like for all extension points. Even if you want to overwrite another tour, you need a different id!&lt;br /&gt;
|-&lt;br /&gt;
| target&lt;br /&gt;
| The UI element this text bubble is displayed next to. Hopscotch only allows identifiers as passed to JQuery, but App Suite extends this to functions. Most functions used simply return JQuery identifiers, the difference is when it is resolved: Functions are resolved later, which is helpful if your application is built by doing DOM manipulations as late as possible - App Suite is.&lt;br /&gt;
|-&lt;br /&gt;
| content&lt;br /&gt;
| The content of a text bubble. Can be html, as it uses the innerHtml method of JQuery to attach itself.&lt;br /&gt;
|-&lt;br /&gt;
| placement&lt;br /&gt;
| Where the text bubble is placed in relation to the UI element. Possible values are &amp;quot;top&amp;quot;, &amp;quot;bottom&amp;quot;, &amp;quot;left&amp;quot; and &amp;quot;right&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| xOffset&lt;br /&gt;
| Passed directly to hopscotch. Moves the box horizontally relative to its standard position.&lt;br /&gt;
|-&lt;br /&gt;
| yOffset&lt;br /&gt;
| Passed directly to hopscotch. Moves the box vertically relative to its standard position.&lt;br /&gt;
|-&lt;br /&gt;
| arrowOffset&lt;br /&gt;
| Passed directly to hopscotch. Moves the arrow relative to its standard position, horizontally if the bubble is placed &amp;quot;top&amp;quot; or &amp;quot;bottom&amp;quot;, vertically if &amp;quot;left&amp;quot; or &amp;quot;right&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| onShow&lt;br /&gt;
| Passed directly to hopscotch. A function that is executed when the bubble is shown.&lt;br /&gt;
|-&lt;br /&gt;
| onShowDeferred&lt;br /&gt;
| A workaround for onShow: This waits for the deferred to be resolved before showing the bubble.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
And that is all there is to creating a tour.&lt;br /&gt;
&lt;br /&gt;
== State after 7.8.0 ==&lt;br /&gt;
&lt;br /&gt;
This framework was replaced by the [[AppSuite:Wizard_framework|Wizard/Tour framework]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Administrator]]&lt;br /&gt;
[[Category:Developer]]&lt;br /&gt;
[[Category:Custom development]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Client_Onboarding&amp;diff=21880</id>
		<title>AppSuite:Client Onboarding</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Client_Onboarding&amp;diff=21880"/>
		<updated>2016-04-28T12:39:06Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: add 'disable old parts'&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Client Onboarding&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{VersionFrom|7.8.1}}&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= Configuration guide for Open-Xchange Client Onboarding =&lt;br /&gt;
&lt;br /&gt;
With Open-Xchange Server v7.8.1 a new module is added allowing users to integrate several different clients and devices with Open-Xchange; such as providing a link to a commercial App Store to install certain apps on mobile devices.&lt;br /&gt;
&lt;br /&gt;
By default, Open-Xchange ships with several built-in providers; thereof&lt;br /&gt;
&lt;br /&gt;
* CalDAV&lt;br /&gt;
* CardDAV&lt;br /&gt;
* OX Mail App&lt;br /&gt;
* OX Drive App&lt;br /&gt;
* Connector for Microsoft Outlook®&lt;br /&gt;
* Sync App for Android&lt;br /&gt;
* Microsoft ActiveSync&lt;br /&gt;
* eM Client&lt;br /&gt;
* Mail (IMAP/SMTP)&lt;br /&gt;
* Generic Mobile App provider&lt;br /&gt;
&lt;br /&gt;
Those providers allow accessing and/or synchronizing with certain data held by Open-Xchange on a supported device.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
To install the Client Onboarding module, the package open-xchange-client-onboarding needs to be installed.&lt;br /&gt;
&lt;br /&gt;
Moreover the property &amp;lt;code&amp;gt;&amp;quot;com.openexchange.client.onboarding.enabled&amp;quot;&amp;lt;/code&amp;gt;; in installed file &amp;lt;code&amp;gt;/opt/open-xchange/etc/client-onboarding.properties&amp;lt;/code&amp;gt; needs to be set to &amp;lt;code&amp;gt;&amp;quot;true&amp;quot;&amp;lt;/code&amp;gt; (default). So getting rid off the entire client on-boarding module simply requires setting the mentioned property to &amp;lt;code&amp;gt;&amp;quot;false&amp;quot;&amp;lt;/code&amp;gt; (and executing &amp;lt;code&amp;gt;/opt/open-xchange/sbin/reloadconfiguration&amp;lt;/code&amp;gt; command-line tool).&lt;br /&gt;
&lt;br /&gt;
== Onboarding scenarios ==&lt;br /&gt;
&lt;br /&gt;
The way to specify what and how a certain device is able to get “on-boarded” is mainly determined by so called scenarios. A scenario describes what gets deployed using which providers that contribute their onboarding possibilities and how it is made accessible to the client’s device.&lt;br /&gt;
&lt;br /&gt;
A scenario consists of the following configuration attributes/options&lt;br /&gt;
&lt;br /&gt;
* A unique scenario identifier&lt;br /&gt;
* A flag determining if scenario is enabled or not. If set to 'false' the scenario will not be available, useful for testing/enabling the scenario later on&lt;br /&gt;
* A scenario type, which is one of &amp;amp;quot;plist&amp;amp;quot;, &amp;amp;quot;manual&amp;amp;quot;, or &amp;amp;quot;link&amp;amp;quot;&lt;br /&gt;
** &amp;amp;quot;plist&amp;amp;quot; for generating a PLIST configuration file for iOS and OSX devices,&lt;br /&gt;
** &amp;amp;quot;manual&amp;amp;quot; for a description for the user for a manual set-up/configuration&lt;br /&gt;
** &amp;amp;quot;link&amp;amp;quot; for a link/URL to either Apple App Store / Google Play Store or to downloadable executable&lt;br /&gt;
* The &amp;amp;quot;link&amp;amp;quot; attribute, which is only considered it type is set to &amp;amp;quot;link&amp;amp;quot;. That attribute consists of the sub-attributes &amp;amp;quot;url&amp;amp;quot; and &amp;amp;quot;type&amp;amp;quot;.&lt;br /&gt;
** &amp;amp;quot;url&amp;amp;quot; provides the actual link/URL to Apple App Store, Apple Mac Store Google Play Store or to downloadable executable. For specifying a property that provides the actual link, please use special &amp;amp;quot;property&amp;amp;quot; scheme; e.g. &amp;amp;quot;property://com.openexchange.client.onboarding.app.mylink&amp;amp;quot;&lt;br /&gt;
** &amp;amp;quot;type&amp;amp;quot; indicates if &amp;amp;quot;url&amp;amp;quot; holds a link for either Apple App Store, Apple Mac Store or Google Play Store. Must only be specified in case &amp;amp;quot;url&amp;amp;quot; points either of those commercial stores. Supported values are &amp;amp;quot;appstore&amp;amp;quot;, &amp;amp;quot;macstore&amp;amp;quot; and &amp;amp;quot;playstore&amp;amp;quot;&lt;br /&gt;
* The identifiers for the providers, which contribute to the scenario;&amp;lt;br /&amp;gt;&lt;br /&gt;
please check command-line tool &amp;lt;code&amp;gt;/opt/open-xchange/sbin/listonboardingproviders&amp;lt;/code&amp;gt; to check, which ones are available&lt;br /&gt;
* The identifiers for alternative scenarios. This is typically used to provide alternative manual setup possibility.&lt;br /&gt;
* The comma-separated names of Font Awesome icons that are supposed to be displayed for the scenario&amp;lt;br /&amp;gt;&lt;br /&gt;
(only the ones from v4.4.0 are currently supported)&lt;br /&gt;
* The translatable display name; check &amp;lt;code&amp;gt;/opt/open-xchange/sbin/parsei18nyaml&amp;lt;/code&amp;gt; command-line tool to generate a .pot file from translatable texts&lt;br /&gt;
* The translatable description; check &amp;lt;code&amp;gt;/opt/open-xchange/sbin/parsei18nyaml&amp;lt;/code&amp;gt; command-line tool to generate a .pot file from translatable texts&lt;br /&gt;
&lt;br /&gt;
== Onboarding providers ==&lt;br /&gt;
&lt;br /&gt;
As described in the previous section, a scenario specifies what providers contribute to it. A provider represents a certain App or (synchronization) protocol that a device can install, download and/or communicate with.&lt;br /&gt;
&lt;br /&gt;
Moreover a provider specifies what Onboarding types are supported (&amp;amp;quot;plist&amp;amp;quot;, &amp;amp;quot;manual&amp;amp;quot;, and/or &amp;amp;quot;link&amp;amp;quot;). Hence, once a provider is chosen to contribute to a certain scenario, the supported on-boarding type needs to match the one of the scenario itself. E.g. a scenario, which indicates to be of type &amp;amp;quot;link&amp;amp;quot;, will always fail if the associated provider signals to support types &amp;amp;quot;plist&amp;amp;quot; and &amp;amp;quot;manual&amp;amp;quot;.&lt;br /&gt;
&lt;br /&gt;
To check what providers are available on your system, please execute the &amp;lt;code&amp;gt;/opt/open-xchange/sbin/listonboardingproviders&amp;lt;/code&amp;gt; command-line tool, which outputs something like:&lt;br /&gt;
&lt;br /&gt;
[[File:onboarding_1.png]]&lt;br /&gt;
&lt;br /&gt;
Each provider might require one or more capabilities/permissions to be available for the requesting user. If capabilities/permissions are not satisfied, the associated scenario cannot be applied, but will be displayed to the user for upsell opportunities.&lt;br /&gt;
&lt;br /&gt;
=== Generic Onboarding provider for Apps ===&lt;br /&gt;
&lt;br /&gt;
Identifier: &amp;lt;code&amp;gt;app&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The generic onboarding provider for apps allows specifying custom scenarios of type &amp;amp;quot;link&amp;amp;quot; for arbitrary links/URLs pointing to Apple App Store, Apple Mac Store Google Play Store or to downloadable executable. The link can be set directly or via a property.&lt;br /&gt;
&lt;br /&gt;
This provider requires no capability/permission.&lt;br /&gt;
&lt;br /&gt;
An exemplary section in the &amp;lt;code&amp;gt;/opt/open-xchange/etc/client-onboarding-scenarios.yml&amp;lt;/code&amp;gt; YAML file might look like (check &amp;amp;quot;Configuring Onboarding scenarios&amp;amp;quot; chapter for more details):&lt;br /&gt;
&lt;br /&gt;
[[File:onboarding_2.png]]&lt;br /&gt;
&lt;br /&gt;
=== CalDAV/CardDAV ===&lt;br /&gt;
&lt;br /&gt;
Identifiers: &amp;lt;code&amp;gt;caldav&amp;lt;/code&amp;gt; &amp;amp;amp; &amp;lt;code&amp;gt;carddav&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In order for a scenario (having CalDAV/CardDAV in its provider listing) to be executable by a user, the &amp;amp;quot;caldav&amp;amp;quot; capability and &amp;amp;quot;carddav&amp;amp;quot; capability respectively are required.&lt;br /&gt;
&lt;br /&gt;
Moreover, the appropriate *DAV end-points are supposed to be configured through property &amp;lt;code&amp;gt;com.openexchange.client.onboarding.caldav.url&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;client-onboarding-caldav.properties&amp;lt;/code&amp;gt; file and property &amp;lt;code&amp;gt;com.openexchange.client.onboarding.carddav.url&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;client-onboarding-carddav.properties&amp;lt;/code&amp;gt; file.&lt;br /&gt;
&lt;br /&gt;
=== Drive/Mail Apps ===&lt;br /&gt;
&lt;br /&gt;
Identifiers: &amp;lt;code&amp;gt;driveapp&amp;lt;/code&amp;gt; &amp;amp;amp; &amp;lt;code&amp;gt;mailapp&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Open-Xchange ships with built-in providers for Drive App and Mail App. The Drive-associated providers do require the &amp;amp;quot;drive&amp;amp;quot; capability and the Mail App requires the &amp;amp;quot;mobile_mail_app&amp;amp;quot; one.&lt;br /&gt;
&lt;br /&gt;
The appropriate links to the apps in the corresponding stores are configured in associated .properties files:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;client-onboarding-driveapp.properties&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;client-onboarding-mailapp.properties&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
By default the links to the official apps are set, but may be changed to ones for branded versions.&lt;br /&gt;
&lt;br /&gt;
=== Drive Windows Client ===&lt;br /&gt;
&lt;br /&gt;
Identifier: &amp;lt;code&amp;gt;drivewindowsclient&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The provider for the windows Drive Client. Requires the &amp;amp;quot;drive&amp;amp;quot; capability, but has no configuration options. However, this provider requires the &amp;amp;quot;open-xchange-drive-client-windows&amp;amp;quot; and the orderly configured binaries (e.g. through installing appropriate package according to the brand).&lt;br /&gt;
&lt;br /&gt;
Please check [https://oxpedia.org/wiki/index.php?title=AppSuite:OX_Drive#OX_Drive_for_Windows ''this''] documentation for more details.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === eM Client ===&lt;br /&gt;
&lt;br /&gt;
Identifier: &amp;lt;code&amp;gt;emclient&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The provider for the eM Client needs the &amp;amp;quot;emclient&amp;amp;quot; capability.&lt;br /&gt;
&lt;br /&gt;
Allows to configure the URL pointing to the executable file through &amp;lt;code&amp;gt;com.openexchange.client.onboarding.emclient.url&amp;lt;/code&amp;gt; in file 'client-onboarding-emclient.properties' file.&lt;br /&gt;
&lt;br /&gt;
=== Sync App ===&lt;br /&gt;
&lt;br /&gt;
Identifier: &amp;lt;code&amp;gt;syncapp&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The provider for the Sync App for Android. Requires &amp;amp;quot;caldav&amp;amp;quot; and &amp;amp;quot;carddav&amp;amp;quot; capabilities.&lt;br /&gt;
&lt;br /&gt;
The link to Google Play Store is specified via property &amp;lt;code&amp;gt;com.openexchange.client.onboarding.syncapp.store.google.playstore&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;client-onboarding-syncapp.properties&amp;lt;/code&amp;gt; file. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mail (IMAP/SMTP) ===&lt;br /&gt;
&lt;br /&gt;
Identifier: &amp;lt;code&amp;gt;mail&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The provider for deploying the IMAP/STMP account on target device using native/stock mail app. All IMAP/SMTP related settings are settable in file 'client-onboarding-mail.properties'.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft ActiveSync (EAS) ===&lt;br /&gt;
&lt;br /&gt;
Identifier: &amp;lt;code&amp;gt;eas&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Configures the ActiveSync account on target device. Allows specifying the EAS end-point through &amp;lt;code&amp;gt;com.openexchange.client.onboarding.eas.url&amp;lt;/code&amp;gt; property in 'client-onboarding-eas.properties'.&lt;br /&gt;
&lt;br /&gt;
=== OX Updater ===&lt;br /&gt;
&lt;br /&gt;
Identifier: &amp;lt;code&amp;gt;oxupdater&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The provider offering the download for the OX Client Updater ([http://oxpedia.org/wiki/index.php?title=AppSuite:Open-Xchange_Updater ''http:''][http://oxpedia.org/wiki/index.php?title=AppSuite:Open-Xchange_Updater ''//oxpedia''][http://oxpedia.org/wiki/index.php?title=AppSuite:Open-Xchange_Updater ''.''][http://oxpedia.org/wiki/index.php?title=AppSuite:Open-Xchange_Updater ''org/wiki/index''][http://oxpedia.org/wiki/index.php?title=AppSuite:Open-Xchange_Updater ''.''][http://oxpedia.org/wiki/index.php?title=AppSuite:Open-Xchange_Updater ''php''][http://oxpedia.org/wiki/index.php?title=AppSuite:Open-Xchange_Updater ''?''][http://oxpedia.org/wiki/index.php?title=AppSuite:Open-Xchange_Updater ''title=AppSuite''][http://oxpedia.org/wiki/index.php?title=AppSuite:Open-Xchange_Updater '':''][http://oxpedia.org/wiki/index.php?title=AppSuite:Open-Xchange_Updater ''Open-Xchange_Updater'']). The provider is available as soon as the according package is installed and at least one product can be installed/updated via it by the user. A pre-configured scenario 'oxupdaterinstall' is already contained in &amp;lt;code&amp;gt;/opt/open-xchange/etc/client-onboarding-scenarios.yml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Configuring Onboarding scenarios ==&lt;br /&gt;
&lt;br /&gt;
Onboarding scenarios are configured through the &amp;lt;code&amp;gt;/opt/open-xchange/etc/client-onboarding-scenarios.yml&amp;lt;/code&amp;gt; YAML file. Each scenario starts with its own &amp;amp;quot;section&amp;amp;quot; in that YAML file by typing its unique identifier. All further attributes as outlined in chapter &amp;amp;quot;Onboarding scenarios&amp;amp;quot; are nested below that identifier.&lt;br /&gt;
&lt;br /&gt;
=== Easily enabling/disabling scenarios ===&lt;br /&gt;
&lt;br /&gt;
By default, Open-Xchange ships with a set of pre-defined scenarios that might apply to the most common installations. Each scenario can easily be enabled/disabled through its &amp;amp;quot;enabled&amp;amp;quot; Boolean attribute in the &amp;lt;code&amp;gt;/opt/open-xchange/etc/client-onboarding-scenarios.yml&amp;lt;/code&amp;gt; YAML file. Executing &amp;lt;code&amp;gt;/opt/open-xchange/sbin/reloadconfiguration&amp;lt;/code&amp;gt; command-line tool applies the changes without the need for restart.&lt;br /&gt;
&lt;br /&gt;
=== Translatable strings ===&lt;br /&gt;
&lt;br /&gt;
Those attributes ending with &amp;amp;quot;_t10e&amp;amp;quot; refer to localizable strings and are placed into &amp;amp;quot;client-onboarding-scenarios.pot&amp;amp;quot; file. Once such attributes ending with &amp;amp;quot;_t10e&amp;amp;quot; are changed/customized and/or added, the appropriate &amp;amp;quot;client-onboarding-scenarios.pot&amp;amp;quot; file needs to be re-created in order to get translated. For generating that &amp;amp;quot;client-onboarding-scenarios.pot&amp;amp;quot; file, please execute the /opt/open-xchange/sbin/parsei18nyaml command-line tool. That .pot file needs then be turned to the .po files for the individual languages.&lt;br /&gt;
&lt;br /&gt;
Thus changing any of the attributes ending with &amp;amp;quot;_t10e&amp;amp;quot; requires (provided that appropriate .po files are available in &amp;lt;code&amp;gt;/opt/open-xchange/i18n&amp;lt;/code&amp;gt; directory) either a restart or executing &amp;lt;code&amp;gt;/opt/open-xchange/sbin/reloadconfiguration&amp;lt;/code&amp;gt; command-line tool together with stop/start of the &amp;amp;quot;com.openexchange.i18n&amp;amp;quot; bundle.&lt;br /&gt;
&lt;br /&gt;
=== Scenario scope ===&lt;br /&gt;
&lt;br /&gt;
While the previously mentioned &amp;amp;quot;enabled&amp;amp;quot; attribute offers some kind of generic on/off switch, the properties outlined in this section allow defining the scope for a scenario. Scope in terms of&lt;br /&gt;
&lt;br /&gt;
* For what devices (from the set of those specified by providers) is that scenario available and&lt;br /&gt;
* For which users is it available&lt;br /&gt;
&lt;br /&gt;
As explained above, each scenario specifies one or more type-compatible providers associated with it. In turn, each provider determines to which devices the scenario applies. In order to further control, which device and which users are allowed to access a certain scenario, there are appropriate options available in file &amp;lt;code&amp;gt;/opt/open-xchange/etc/client-onboarding.properties&amp;lt;/code&amp;gt;. Every option is fully [http://oxpedia.org/wiki/index.php?title=ConfigCascade ''config-cascade''] aware and therefore can be controlled on a global, per context set, per context and per user basis.&lt;br /&gt;
&lt;br /&gt;
Thus, to make a scenario available for certain devices (as dictated by scenario’s providers) and for users as well, the scenario identifier needs to be added to the appropriate properties ending with &amp;amp;quot;.scenarios&amp;amp;quot; (for devices) and added to the &amp;amp;quot;com.openexchange.client.onboarding.enabledScenarios&amp;amp;quot; property (for user) as well:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;com.openexchange.client.onboarding.apple.mac.scenarios&amp;lt;br /&amp;gt;&lt;br /&gt;
com.openexchange.client.onboarding.apple.ipad.scenarios&amp;lt;br /&amp;gt;&lt;br /&gt;
com.openexchange.client.onboarding.apple.iphone.scenarios&amp;lt;br /&amp;gt;&lt;br /&gt;
com.openexchange.client.onboarding.android.tablet.scenarios&amp;lt;br /&amp;gt;&lt;br /&gt;
com.openexchange.client.onboarding.android.phone.scenarios&amp;lt;br /&amp;gt;&lt;br /&gt;
com.openexchange.client.onboarding.windows.desktop.scenarios&lt;br /&gt;
&lt;br /&gt;
com.openexchange.client.onboarding.enabledScenarios&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Once a scenario is made accessible through configuration it is visible to users trying to perform a client onboarding using one of associated devices. However, whether a user is effectively allowed to execute that scenario is determined by the required capabilities of the denoted providers; if not allowed it gets displayed as an upsell opportunity.&lt;br /&gt;
&lt;br /&gt;
=== Configuring actions ===&lt;br /&gt;
&lt;br /&gt;
The type for a scenario determines what actions are associated with it to &amp;amp;quot;''transport''&amp;amp;quot; the onboarding information to the client. The types &amp;amp;quot;manual&amp;amp;quot; and &amp;amp;quot;link&amp;amp;quot; only show static information like displaying a link. In contrast the type &amp;amp;quot;plist&amp;amp;quot; allows several actions to transport the configuration profile onto the client. Thereof&lt;br /&gt;
&lt;br /&gt;
* E-Mail&lt;br /&gt;
* SMS&lt;br /&gt;
&lt;br /&gt;
In order to utilize the &amp;amp;quot;E-Mail&amp;amp;quot; action, a special transport must be configured for system-composed E-Mails [http://oxpedia.org/wiki/index.php?title=AppSuite:Sharing_and_Guest_Mode#Share_Notifications ''as it is for using the sharing functionality'']. This transport is configured in noreply.properties. All properties therein are config-cascade capable, so their values can be sensitive to the current user or context.&lt;br /&gt;
&lt;br /&gt;
Using the SMS action requires a SIP Gate being available. Please check [http://oxpedia.org/wiki/index.php?title=AppSuite:SMS_Sipgate ''this documentation''] how to setup a SIP Gate for sending SMS to capable clients.&lt;br /&gt;
&lt;br /&gt;
Moreover, scenarios of type &amp;amp;quot;plist&amp;amp;quot; require having PLIST-signing enabled. Otherwise the device will show a warning when importing a received PLIST configuration profile. Please follow the instructions as outlined in [http://oxpedia.org/wiki/index.php?title=AppSuite:PList_signing ''this article''] how to enable and configure signing for PLIST files.&lt;br /&gt;
&lt;br /&gt;
== Adding custom scenarios ==&lt;br /&gt;
&lt;br /&gt;
The first thing to do, is to add an appropriate scenario configuration to the 'client-onboarding-scenarios.yml' file; e.g.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;mygoogleapp:&amp;lt;br /&amp;gt;&lt;br /&gt;
 enabled: true&amp;lt;br /&amp;gt;&lt;br /&gt;
 type: link&amp;lt;br /&amp;gt;&lt;br /&gt;
 link:&amp;lt;br /&amp;gt;&lt;br /&gt;
 url: http://play.google.com/store/apps?id=mygoogleapp.invalid&amp;lt;br /&amp;gt;&lt;br /&gt;
 type: playstore&amp;lt;br /&amp;gt;&lt;br /&gt;
 providers: [app]&amp;lt;br /&amp;gt;&lt;br /&gt;
 alternatives: []&amp;lt;br /&amp;gt;&lt;br /&gt;
 icon: fa-cloud&amp;lt;br /&amp;gt;&lt;br /&gt;
 displayName_t10e: &amp;amp;quot;My App for synchronizing files&amp;amp;quot;&amp;lt;br /&amp;gt;&lt;br /&gt;
 description_t10e: &amp;amp;quot;Synchronize your files with our Drive application.&amp;amp;quot;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
The next step is to make that scenario accessible through adapting 'client-onboarding.properties' file. Hence, the scenario identifier needs to be added to the devices, to which it applies (Android devices in this case):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;com.openexchange.client.onboarding.android.tablet.scenarios=..., mygoogleapp&amp;lt;br /&amp;gt;&lt;br /&gt;
com.openexchange.client.onboarding.android.phone.scenarios=..., mygoogleapp&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
Furthermore, that scenario needs to be made accessible to users as well:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;com.openexchange.client.onboarding.enabledScenarios=..., mygoogleapp&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
At last, a .pot file is supposed to be generated from the translatable strings using /opt/open-xchange/sbin/parsei18nyaml command-line tool to yield the .po files for the individual languages.&lt;br /&gt;
&lt;br /&gt;
Provided that target users do hold appropriate capabilities, they are allowed to execute that scenario. In the example above the &amp;amp;quot;app&amp;amp;quot; provider is specified, which does no require any capabilities. Hence, that scenario is available.&lt;br /&gt;
&lt;br /&gt;
== HowTos ==&lt;br /&gt;
&lt;br /&gt;
=== How can I quickly setup a default/test configuration ? ===&lt;br /&gt;
&lt;br /&gt;
# Install open-xchange-client-onboarding package&lt;br /&gt;
# Edit &amp;lt;code&amp;gt;/opt/open-xchange/etc/client-onboarding.properties&amp;lt;/code&amp;gt; and set &amp;lt;code&amp;gt;com.openexchange.client.onboarding.enabled&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; &lt;br /&gt;
# Edit &amp;lt;code&amp;gt;noreply.properties&amp;lt;/code&amp;gt; to enable autoconfig sending via email. PS: Have a fully working smtp login available like “noreply@yourdomain.tld”&lt;br /&gt;
# Edit client-onboarding-scenarios.yml and set “enable” for : “driveappinstall” , “davsync” “davmanual” “mailsync” “mailmanual” to &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; &lt;br /&gt;
# Now set the correct caldav/carddav server FQDNs in the corresponding files for your setup&lt;br /&gt;
#*&amp;lt;code&amp;gt;client-onboarding-caldav.properties&amp;lt;/code&amp;gt; &lt;br /&gt;
#*&amp;lt;code&amp;gt;client-onboarding-carddav.properties&amp;lt;/code&amp;gt;&lt;br /&gt;
# Restart OX process&lt;br /&gt;
# Login to AppSuite. Click on Settings - Connect your device&lt;br /&gt;
# Verify that you have following onboarding options available: &lt;br /&gt;
#* windows -&amp;gt; imap/smtp details&lt;br /&gt;
#* android/fon+tablet -&amp;gt; imap/smtp details&lt;br /&gt;
#* android/fon+tablet -&amp;gt; drive app &lt;br /&gt;
#* apple -&amp;gt; iphone -&amp;gt; imap/smtp config button&lt;br /&gt;
#* apple -&amp;gt; iphone -&amp;gt; imap/smtp details for experts&lt;br /&gt;
#* apple -&amp;gt; iphone -&amp;gt; calendar/addressbok -&amp;gt; config button&lt;br /&gt;
#* apple -&amp;gt; iphone -&amp;gt; calendar/addressbok -&amp;gt; details for dav setup&lt;br /&gt;
#* apple -&amp;gt; iphone -&amp;gt; drive app&lt;br /&gt;
#* apple -&amp;gt; ipad -&amp;gt; imap/smtp config send email&lt;br /&gt;
#* apple -&amp;gt; ipad -&amp;gt; imap/smtp config button&lt;br /&gt;
#* apple -&amp;gt; ipad -&amp;gt; imap/smtp details for experts&lt;br /&gt;
#* apple -&amp;gt; ipad -&amp;gt; calendar/addressbook config send email&lt;br /&gt;
#* apple -&amp;gt; ipad -&amp;gt; calendar/addressbook config button&lt;br /&gt;
#* apple -&amp;gt; ipad -&amp;gt; calendar/addressbook details for experts&lt;br /&gt;
#* apple -&amp;gt; ipad -&amp;gt; drive app&lt;br /&gt;
&lt;br /&gt;
=== How can I quickly disable a certain scenario ===&lt;br /&gt;
&lt;br /&gt;
Simply enter the 'client-onboarding-scenarios.yml' file and go to the section specifying the target scenario. Switch the 'enabled' flag to &amp;amp;quot;false&amp;amp;quot; and execute /opt/open-xchange/sbin/reloadconfiguration command-line tool.&lt;br /&gt;
&lt;br /&gt;
E.g. to disable the OX Drive App:&lt;br /&gt;
&lt;br /&gt;
[[File:onboarding_3.png]]&lt;br /&gt;
&lt;br /&gt;
=== How can I disable the outdated ui parts like 'Downloads'? ===&lt;br /&gt;
&lt;br /&gt;
The wizard replaces and extends the functionality of the old Download Section in settings and the corresponding widgets. We recommend to disable these outdated parts.&lt;br /&gt;
# widget: get ox drive&lt;br /&gt;
# widget: update&lt;br /&gt;
# settings entry: downloads&lt;br /&gt;
&lt;br /&gt;
'''remove settings section 'Downloads' '''&lt;br /&gt;
&lt;br /&gt;
Edit the property file and change the value to true:&lt;br /&gt;
* ''io.ox/core//settings/downloadsDisabled''&lt;br /&gt;
&lt;br /&gt;
'''remove widgets'''&lt;br /&gt;
&lt;br /&gt;
To remove the both related portal widget/tiles please refer to the&lt;br /&gt;
[[AppSuite:Configuring_portal_plugins#Disabling_a_tile_completely | portal plugins wiki page ]]&lt;br /&gt;
&lt;br /&gt;
* ''io.ox/portal/widget/oxdriveclients''&lt;br /&gt;
* ''io.ox/portal/widget/updater''&lt;br /&gt;
&lt;br /&gt;
=== How can I add my own App? ===&lt;br /&gt;
&lt;br /&gt;
Provided that the App is accessible by a link (pointing to a commercial App Store or to a downloadable executable/installer), the generic &amp;amp;quot;app&amp;amp;quot; is the suitable provider to choose.&lt;br /&gt;
&lt;br /&gt;
Hence, an appropriate section is supposed to be added to the 'client-onboarding-scenarios.yml' file having a unique name and &amp;amp;quot;provider&amp;amp;quot; attribute set to &amp;amp;quot;[app]&amp;amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;mygoogleapp:&amp;lt;br /&amp;gt;&lt;br /&gt;
 enabled: true&amp;lt;br /&amp;gt;&lt;br /&gt;
 type: link&amp;lt;br /&amp;gt;&lt;br /&gt;
 link:&amp;lt;br /&amp;gt;&lt;br /&gt;
 url: http://play.google.com/store/apps?id=mygoogleapp.invalid&amp;lt;br /&amp;gt;&lt;br /&gt;
 type: playstore&amp;lt;br /&amp;gt;&lt;br /&gt;
 providers: [app]&amp;lt;br /&amp;gt;&lt;br /&gt;
 alternatives: []&amp;lt;br /&amp;gt;&lt;br /&gt;
 icon: fa-cloud&amp;lt;br /&amp;gt;&lt;br /&gt;
 displayName_t10e: &amp;amp;quot;My App for synchronizing files&amp;amp;quot;&amp;lt;br /&amp;gt;&lt;br /&gt;
 description_t10e: &amp;amp;quot;Synchronize your files with our Drive application.&amp;amp;quot;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
The attribute &amp;amp;quot;type&amp;amp;quot; needs to be set to &amp;amp;quot;link&amp;amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The attribute &amp;amp;quot;link&amp;amp;quot; should be configured with:&lt;br /&gt;
&lt;br /&gt;
* &amp;amp;quot;url&amp;amp;quot; sub-attribute containing the actual link pointing to the URL location&lt;br /&gt;
* &amp;amp;quot;type&amp;amp;quot; sub-attribute specifying of what type that link is: either &amp;amp;quot;appstore&amp;amp;quot;, &amp;amp;quot;macappstore&amp;amp;quot;, &amp;amp;quot;playstore&amp;amp;quot; or &amp;amp;quot;common&amp;amp;quot;.&amp;lt;br /&amp;gt;&lt;br /&gt;
The type &amp;amp;quot;common&amp;amp;quot; is supposed to be used for links that do not point to a commercial App Store, but to a downloadable executable/installer file.&lt;br /&gt;
&lt;br /&gt;
The &amp;amp;quot;icon&amp;amp;quot; attribute is supposed to contain a comma-separated list of Font Awesome icons (only the ones from v4.4.0 are currently supported) that represent the App; e.g.&lt;br /&gt;
&lt;br /&gt;
* &amp;amp;quot;fa-cloud&amp;amp;quot; for Drive/file-related nature&lt;br /&gt;
* &amp;amp;quot;fa-calendar, fa-users&amp;amp;quot; for Calendar and Address Book synchronization&lt;br /&gt;
* &amp;amp;quot;fa-envelope-o&amp;amp;quot; for Mail nature&lt;br /&gt;
&lt;br /&gt;
Next, the translatable display name and description should be set through setting the attributes &amp;amp;quot;displayName_t10e&amp;amp;quot; and &amp;amp;quot;description_t10e&amp;amp;quot;. As described previously, executing the /opt/open-xchange/sbin/parsei18nyaml command-line tool yields an appropriate .pot file, which can then be used for generating the individual .po file for the different translations.&lt;br /&gt;
&lt;br /&gt;
Finally, the 'client-onboarding.properties' file needs to be modified to specify the &amp;amp;quot;scope&amp;amp;quot; for that new scenario. Add the scenario’s unique name to the appropriate device properties and add it to &amp;amp;quot;com.openexchange.client.onboarding.enabledScenarios&amp;amp;quot; property as well.&lt;br /&gt;
&lt;br /&gt;
After executing /opt/open-xchange/sbin/reloadconfiguration command-line tool the new client onboarding scenario is available for the selected users/devices.&lt;br /&gt;
&lt;br /&gt;
=== How can I upsell my own app? ===&lt;br /&gt;
&lt;br /&gt;
Having the same prerequisites/steps as for &amp;amp;quot;How can I add my own App?&amp;amp;quot; the scenario description in the 'client-onboarding-scenarios.yml' file simply needs to be extended by the desired capabilities, which are supposed to be used for managing the upsell.&lt;br /&gt;
&lt;br /&gt;
Example&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;mygoogleapp:&amp;lt;br /&amp;gt;&lt;br /&gt;
 enabled: true&amp;lt;br /&amp;gt;&lt;br /&gt;
 type: link&amp;lt;br /&amp;gt;&lt;br /&gt;
 link:&amp;lt;br /&amp;gt;&lt;br /&gt;
 url: http://play.google.com/store/apps?id=mygoogleapp.invalid&amp;lt;br /&amp;gt;&lt;br /&gt;
 type: playstore&amp;lt;br /&amp;gt;&lt;br /&gt;
 '''capabilities: &amp;amp;quot;mygoogleapp_capability&amp;amp;quot;'''&amp;lt;br /&amp;gt;&lt;br /&gt;
 providers: [app]&amp;lt;br /&amp;gt;&lt;br /&gt;
 alternatives: []&amp;lt;br /&amp;gt;&lt;br /&gt;
 icon: fa-cloud&amp;lt;br /&amp;gt;&lt;br /&gt;
 displayName_t10e: &amp;amp;quot;My App for synchronizing files&amp;amp;quot;&amp;lt;br /&amp;gt;&lt;br /&gt;
 description_t10e: &amp;amp;quot;Synchronize your files with our Drive application.&amp;amp;quot;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
The special &amp;amp;quot;capabilities&amp;amp;quot; attribute is only supported for scenarios of type &amp;amp;quot;link&amp;amp;quot; with the special &amp;amp;quot;app&amp;amp;quot; provider.&lt;br /&gt;
&lt;br /&gt;
With such a &amp;amp;quot;capabilities&amp;amp;quot; attribute only users, which have the &amp;amp;quot;mygoogleapp_capability&amp;amp;quot; capability are allowed to get the link. For those who don’t, the upsell opportunity will be displayed; e.g.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;[[File:onboarding_4.png]]&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Theming_the_login_page&amp;diff=21465</id>
		<title>AppSuite:Theming the login page</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Theming_the_login_page&amp;diff=21465"/>
		<updated>2016-02-11T10:12:32Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!--PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/doc umentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Theming the login page&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Abstract.''' In this article, you can learn how to create a customized theme for the default login page for your appsuite installation and also how to configure different ones for different hostnames. It only explains where and how to configure and apply the modifications but not the [[AppSuite:Theming|basics of App Suite theming]].&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== style.less ==&lt;br /&gt;
&lt;br /&gt;
To apply a theme to the login page you just add the relevant snippets to the style.less file like for a normal theme and include the logos and artifacts in the theme directory.&lt;br /&gt;
&lt;br /&gt;
Here are some examples of CSS selectors which can be addressed on the login page:&lt;br /&gt;
&lt;br /&gt;
 #io-ox-login-username&lt;br /&gt;
 #io-ox-login-screen .btn-primary&lt;br /&gt;
 #io-ox-login-screen .btn-primary:hover&lt;br /&gt;
 #io-ox-login-header-prefix&lt;br /&gt;
 #io-ox-login-header-label&lt;br /&gt;
 #io-ox-login-container&lt;br /&gt;
 .wallpaper&lt;br /&gt;
 body.down #io-ox-login-container .alert.alert-info&lt;br /&gt;
 .language-delimiter&lt;br /&gt;
 #io-ox-copyright&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== as-config.yml ==&lt;br /&gt;
&lt;br /&gt;
To actually apply the above definition the theme needs to be specified in the file /opt/open-xchange/etc/as-config.yml:&lt;br /&gt;
&lt;br /&gt;
 signinTheme: MYTHEME&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As as-config.yml can have different configuration based on different hostnames a multi branded configuration can be applied as well.&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Dynamic_Theme&amp;diff=21464</id>
		<title>AppSuite:Dynamic Theme</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Dynamic_Theme&amp;diff=21464"/>
		<updated>2016-02-11T10:11:26Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-unstable}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Dynamic Theming&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The dynamic theme plugin allows to have custom colors and logo without creating a real theme for each possible color combination. The customization information is stored in the [[ConfigCascade|Configuration Cascade]] and applied at runtime, immediately after login.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
The theme generator consists of the single package &amp;lt;tt&amp;gt;open-xchange-dynamic-theme&amp;lt;/tt&amp;gt;, which is installed on the OX middleware.&lt;br /&gt;
&lt;br /&gt;
Before OX App Suite v7.6.2, the package included the command line tool &amp;lt;tt&amp;gt;/opt/open-xchange/sbin/update-dynamic-theme&amp;lt;/tt&amp;gt;, which must be called after every update of other plugins. It is called automatically after the installation and upgrades of &amp;lt;tt&amp;gt;open-xchange-dynamic-theme&amp;lt;/tt&amp;gt;. The tool examines all installed &amp;lt;tt&amp;gt;.less&amp;lt;/tt&amp;gt; files and computes which CSS rules need to be changed to apply the custom colors everywhere.&lt;br /&gt;
&lt;br /&gt;
Since v7.6.2, the tool is called automatically whenever necessary, so it is not necessary to call it manually, and therefore it is not installed in the &amp;lt;tt&amp;gt;/opt/open-xchange/sbin/&amp;lt;/tt&amp;gt; directory anymore.&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
=== Enabling the Plugin ===&lt;br /&gt;
&lt;br /&gt;
The plugin is enabled or disabled by the [[AppSuite:Capabilities|capability]] &amp;lt;tt&amp;gt;dynamic-theme&amp;lt;/tt&amp;gt;, e.g. in a file &amp;lt;tt&amp;gt;/opt/open-xchange/etc/dynamic-theme.properties&amp;lt;/tt&amp;gt; set&lt;br /&gt;
&lt;br /&gt;
 com.openexchange.capability.dynamic-theme=true&lt;br /&gt;
&lt;br /&gt;
When using the plugin, the actual theme should be the built-in &amp;lt;tt&amp;gt;default&amp;lt;/tt&amp;gt; theme. To prevent all users from changing it and hide the theme selector in the preferences, set the property &amp;lt;tt&amp;gt;io.ox/core//theme&amp;lt;/tt&amp;gt; to read-only. To do this, change the file &amp;lt;tt&amp;gt;/opt/open-xchange/etc/meta/appsuite.yaml&amp;lt;/tt&amp;gt; as follows: &lt;br /&gt;
&lt;br /&gt;
 io.ox/core//theme:&lt;br /&gt;
     protected: true&lt;br /&gt;
&lt;br /&gt;
If some users won't use dynamic themes, this approach won't work. Instead, the theme selector can be disabled an enabled by controlling the list of available themes. The theme selector is hidden if the list is empty. To be able to do that with the Configuration Cascade, the entire list of themes needs to be specified as a single JSON value, e.g. by changing the file &amp;lt;tt&amp;gt;/opt/open-xchange/etc/settings/appsuite.properties&amp;lt;/tt&amp;gt; as follows:&lt;br /&gt;
&lt;br /&gt;
 io.ox/core/settingOptions//themes={&amp;quot;default&amp;quot;:&amp;quot;Default Theme&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
Then, to hide the theme selector for users with a dynamic theme, use the Configuration Cascade to change the value to &amp;lt;tt&amp;gt;{}&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Specifying a Theme ===&lt;br /&gt;
&lt;br /&gt;
The package installs the file &amp;lt;tt&amp;gt;/opt/open-xchange/etc/settings/open-xchange-dynamic-theme.properties&amp;lt;/tt&amp;gt;, which contains the global defaults for the custom theme settings. Any of the settings contained in that file can be changed per-context or at any other granularity supported by the [[ConfigCascade#UI_Properties|Configuration Cascade]]. The individual settings are as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:auto&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Prefix&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;th&amp;gt; Key                     &amp;lt;/th&amp;gt;&amp;lt;th&amp;gt; Default value    &amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
  &amp;lt;tr&amp;gt;&amp;lt;td rowspan=&amp;quot;5&amp;quot; style=&amp;quot;vertical-align:middle&amp;quot;&amp;gt;&amp;lt;tt&amp;gt;io.ox/dynamic-theme//&amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt;&amp;lt;tt&amp;gt; frameColor     &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;tt&amp;gt; #333    &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
  &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;tt&amp;gt; iconColor      &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;tt&amp;gt; #bbb    &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
  &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;tt&amp;gt; selectionColor &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;tt&amp;gt; #39a9e1 &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
  &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;tt&amp;gt; logoWidth      &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;tt&amp;gt; 60      &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
  &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;tt&amp;gt; logoURL        &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&lt;br /&gt;
                 &amp;lt;tt&amp;gt; apps/themes/default/logo-small.png &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The colors can be any CSS color; &amp;lt;tt&amp;gt;logoWidth&amp;lt;/tt&amp;gt; is specified in pixels; &amp;lt;tt&amp;gt;logoURL&amp;lt;/tt&amp;gt; can be relative (to &amp;lt;tt&amp;gt;/appsuite/&amp;lt;/tt&amp;gt;), or absolute. When using absolute URLs to point to different hosts, use the form &amp;lt;tt&amp;gt;//&amp;lt;var&amp;gt;hostname&amp;lt;/var&amp;gt;/&amp;lt;var&amp;gt;path&amp;lt;/var&amp;gt;&amp;lt;/tt&amp;gt; to keep the protocol (HTTP or HTTPS) and avoid any unnecessary security warnings.&lt;br /&gt;
&lt;br /&gt;
== Maintenance ==&lt;br /&gt;
&lt;br /&gt;
The source file &amp;lt;tt&amp;gt;apps/io.ox/dynamic-theme/definitions.less.in&amp;lt;/tt&amp;gt; contains the Less rules for the theme. All keys in the namespace &amp;lt;tt&amp;gt;io.ox/dynamic-theme//&amp;lt;/tt&amp;gt; are converted to Less variables by prefixing them with &amp;lt;tt&amp;gt;@io-ox-dynamic-theme-&amp;lt;/tt&amp;gt;. The set of keys in the following files must be kept in sync:&lt;br /&gt;
* &amp;lt;tt&amp;gt;apps/io.ox/dynamic-themes/definitions.less.in&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;apps/io.ox/dynamic-theme/settings/defaults.js&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;lib/update-dynamic-theme.js&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;conf/settings/open-xchange-dynamic-theme.properties&amp;lt;/tt&amp;gt;&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Dynamic_Theme&amp;diff=21463</id>
		<title>AppSuite:Dynamic Theme</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Dynamic_Theme&amp;diff=21463"/>
		<updated>2016-02-11T10:10:34Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! -&lt;br /&gt;
&lt;br /&gt;
{{Stability-unstable}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Dynamic Theming&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The dynamic theme plugin allows to have custom colors and logo without creating a real theme for each possible color combination. The customization information is stored in the [[ConfigCascade|Configuration Cascade]] and applied at runtime, immediately after login.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
The theme generator consists of the single package &amp;lt;tt&amp;gt;open-xchange-dynamic-theme&amp;lt;/tt&amp;gt;, which is installed on the OX middleware.&lt;br /&gt;
&lt;br /&gt;
Before OX App Suite v7.6.2, the package included the command line tool &amp;lt;tt&amp;gt;/opt/open-xchange/sbin/update-dynamic-theme&amp;lt;/tt&amp;gt;, which must be called after every update of other plugins. It is called automatically after the installation and upgrades of &amp;lt;tt&amp;gt;open-xchange-dynamic-theme&amp;lt;/tt&amp;gt;. The tool examines all installed &amp;lt;tt&amp;gt;.less&amp;lt;/tt&amp;gt; files and computes which CSS rules need to be changed to apply the custom colors everywhere.&lt;br /&gt;
&lt;br /&gt;
Since v7.6.2, the tool is called automatically whenever necessary, so it is not necessary to call it manually, and therefore it is not installed in the &amp;lt;tt&amp;gt;/opt/open-xchange/sbin/&amp;lt;/tt&amp;gt; directory anymore.&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
=== Enabling the Plugin ===&lt;br /&gt;
&lt;br /&gt;
The plugin is enabled or disabled by the [[AppSuite:Capabilities|capability]] &amp;lt;tt&amp;gt;dynamic-theme&amp;lt;/tt&amp;gt;, e.g. in a file &amp;lt;tt&amp;gt;/opt/open-xchange/etc/dynamic-theme.properties&amp;lt;/tt&amp;gt; set&lt;br /&gt;
&lt;br /&gt;
 com.openexchange.capability.dynamic-theme=true&lt;br /&gt;
&lt;br /&gt;
When using the plugin, the actual theme should be the built-in &amp;lt;tt&amp;gt;default&amp;lt;/tt&amp;gt; theme. To prevent all users from changing it and hide the theme selector in the preferences, set the property &amp;lt;tt&amp;gt;io.ox/core//theme&amp;lt;/tt&amp;gt; to read-only. To do this, change the file &amp;lt;tt&amp;gt;/opt/open-xchange/etc/meta/appsuite.yaml&amp;lt;/tt&amp;gt; as follows: &lt;br /&gt;
&lt;br /&gt;
 io.ox/core//theme:&lt;br /&gt;
     protected: true&lt;br /&gt;
&lt;br /&gt;
If some users won't use dynamic themes, this approach won't work. Instead, the theme selector can be disabled an enabled by controlling the list of available themes. The theme selector is hidden if the list is empty. To be able to do that with the Configuration Cascade, the entire list of themes needs to be specified as a single JSON value, e.g. by changing the file &amp;lt;tt&amp;gt;/opt/open-xchange/etc/settings/appsuite.properties&amp;lt;/tt&amp;gt; as follows:&lt;br /&gt;
&lt;br /&gt;
 io.ox/core/settingOptions//themes={&amp;quot;default&amp;quot;:&amp;quot;Default Theme&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
Then, to hide the theme selector for users with a dynamic theme, use the Configuration Cascade to change the value to &amp;lt;tt&amp;gt;{}&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Specifying a Theme ===&lt;br /&gt;
&lt;br /&gt;
The package installs the file &amp;lt;tt&amp;gt;/opt/open-xchange/etc/settings/open-xchange-dynamic-theme.properties&amp;lt;/tt&amp;gt;, which contains the global defaults for the custom theme settings. Any of the settings contained in that file can be changed per-context or at any other granularity supported by the [[ConfigCascade#UI_Properties|Configuration Cascade]]. The individual settings are as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:auto&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Prefix&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;th&amp;gt; Key                     &amp;lt;/th&amp;gt;&amp;lt;th&amp;gt; Default value    &amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
  &amp;lt;tr&amp;gt;&amp;lt;td rowspan=&amp;quot;5&amp;quot; style=&amp;quot;vertical-align:middle&amp;quot;&amp;gt;&amp;lt;tt&amp;gt;io.ox/dynamic-theme//&amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt;&amp;lt;tt&amp;gt; frameColor     &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;tt&amp;gt; #333    &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
  &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;tt&amp;gt; iconColor      &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;tt&amp;gt; #bbb    &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
  &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;tt&amp;gt; selectionColor &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;tt&amp;gt; #39a9e1 &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
  &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;tt&amp;gt; logoWidth      &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;tt&amp;gt; 60      &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
  &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;tt&amp;gt; logoURL        &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&lt;br /&gt;
                 &amp;lt;tt&amp;gt; apps/themes/default/logo-small.png &amp;lt;/tt&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The colors can be any CSS color; &amp;lt;tt&amp;gt;logoWidth&amp;lt;/tt&amp;gt; is specified in pixels; &amp;lt;tt&amp;gt;logoURL&amp;lt;/tt&amp;gt; can be relative (to &amp;lt;tt&amp;gt;/appsuite/&amp;lt;/tt&amp;gt;), or absolute. When using absolute URLs to point to different hosts, use the form &amp;lt;tt&amp;gt;//&amp;lt;var&amp;gt;hostname&amp;lt;/var&amp;gt;/&amp;lt;var&amp;gt;path&amp;lt;/var&amp;gt;&amp;lt;/tt&amp;gt; to keep the protocol (HTTP or HTTPS) and avoid any unnecessary security warnings.&lt;br /&gt;
&lt;br /&gt;
== Maintenance ==&lt;br /&gt;
&lt;br /&gt;
The source file &amp;lt;tt&amp;gt;apps/io.ox/dynamic-theme/definitions.less.in&amp;lt;/tt&amp;gt; contains the Less rules for the theme. All keys in the namespace &amp;lt;tt&amp;gt;io.ox/dynamic-theme//&amp;lt;/tt&amp;gt; are converted to Less variables by prefixing them with &amp;lt;tt&amp;gt;@io-ox-dynamic-theme-&amp;lt;/tt&amp;gt;. The set of keys in the following files must be kept in sync:&lt;br /&gt;
* &amp;lt;tt&amp;gt;apps/io.ox/dynamic-themes/definitions.less.in&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;apps/io.ox/dynamic-theme/settings/defaults.js&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;lib/update-dynamic-theme.js&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;conf/settings/open-xchange-dynamic-theme.properties&amp;lt;/tt&amp;gt;&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Test_basics&amp;diff=21462</id>
		<title>AppSuite:Test basics</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Test_basics&amp;diff=21462"/>
		<updated>2016-02-11T08:56:56Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lessons learned painfully while doing testing:&lt;br /&gt;
* PhantomJS thinks it is a '''touch''' device (Modernizr.touch === true)&lt;br /&gt;
* Almost no CSS is loaded. Don't rely on classes like &amp;quot;hidden&amp;quot; (bootstrap class)&lt;br /&gt;
* Turn off CSS transitions - any DOM-based test might fail (caused by random DOM reflow delay)&lt;br /&gt;
* Don't clear &amp;lt;body&amp;gt; (e.g. $(document.body).empty()). UI is not robust against that.&lt;br /&gt;
** instead attach a new element to the body (or anywhere else) and remove it afterwards&lt;br /&gt;
* The browser window is elastic. It has no fixed size. Usually affects scrolling tests.&lt;br /&gt;
* If weird things happen, try to check if your app/window/node is really still in the DOM.&lt;br /&gt;
* HTML fixtures cannot be loaded (don't know why yet); just rename your files to *.txt&lt;br /&gt;
* Please mind that your fake server only works inside &amp;quot;description&amp;quot; (not across specs)&lt;br /&gt;
* PhantomJS fails at: Date.parse(&amp;quot;2012-01-01&amp;quot;); (see https://code.google.com/p/phantomjs/issues/detail?id=267#c2)&lt;br /&gt;
* The fake server is global, if someone has registered your desired api/call already, your response will never get send&lt;br /&gt;
** it is possible to use this.server.responses.filter to remove another problematic response implementation&lt;br /&gt;
* Any checks for z-index will probably fail due to the lack of loaded CSS. If an element is not positioned (relative or absolute) the computed style is &amp;quot;auto&amp;quot;. Just reposition affected elements in your spec.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing on windows ==&lt;br /&gt;
&lt;br /&gt;
A small and incomplete collection of things, that might work different on a windows machine:&lt;br /&gt;
&lt;br /&gt;
* the UI relies on zonefiles being installed on the system, this is the case for mac and linux environments, but not on windows&lt;br /&gt;
** it should be possible to install the zonefiles manually and make karma serve those&lt;br /&gt;
&lt;br /&gt;
== Fixtures ==&lt;br /&gt;
&lt;br /&gt;
* using 0 as identifier value (id, user_id, etc.) could cause unexpected system behavior&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:Developer]]&lt;br /&gt;
[[Category:Testing]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Testing_3rd-party_code&amp;diff=21461</id>
		<title>AppSuite:Testing 3rd-party code</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Testing_3rd-party_code&amp;diff=21461"/>
		<updated>2016-02-11T08:46:22Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Testing 3rd-party code&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
This article explains one way to add automatic testing to UI plugins. &lt;br /&gt;
&lt;br /&gt;
== Libraries ==&lt;br /&gt;
&lt;br /&gt;
* [http://visionmedia.github.io/mocha/ Mocha] - the fun, simple, flexible JS testing framework&lt;br /&gt;
* [http://sinonjs.org/  Sinon.JS] - Standalone test spies, stubs and mocks for JavaScript.&lt;br /&gt;
* [http://karma-runner.github.io/  Karma Runner] - test runner&lt;br /&gt;
* [http://chaijs.com/ Chai.js] - Assertion framework&lt;br /&gt;
&lt;br /&gt;
== using karma-ox-ui ==&lt;br /&gt;
&lt;br /&gt;
The [[AppSuite:GettingStarted_7.6.0|shared grunt configuration]] ships with most of the parts pre-configured. Still, a little setup is needed to enable automatic testing for an external app. Since we use many standard libraries, this approach can be extended as you wish.&lt;br /&gt;
&lt;br /&gt;
=== Setup ===&lt;br /&gt;
&lt;br /&gt;
Make sure, karma executable is installed:&lt;br /&gt;
&lt;br /&gt;
  npm install -g karma-cli&lt;br /&gt;
&lt;br /&gt;
Install a few more testing libraries locally:&lt;br /&gt;
&lt;br /&gt;
  npm install --save-dev karma-mocha karma-chai karma-sinon karma-ox-ui karma-phantomjs-launcher&lt;br /&gt;
&lt;br /&gt;
After that, in your plugin directory generate a new karma.conf.js:&lt;br /&gt;
&lt;br /&gt;
  jb@wiggum ~/code/appsuite/ox_pgp_mail (git)-[ding] % karma init&lt;br /&gt;
  &lt;br /&gt;
  Which testing framework do you want to use ?&lt;br /&gt;
  Press tab to list possible options. Enter to move to the next question.&lt;br /&gt;
  &amp;gt; mocha                                                                                                                                                                                                                                     &lt;br /&gt;
  &lt;br /&gt;
  Do you want to use Require.js ?&lt;br /&gt;
  This will add Require.js plugin.&lt;br /&gt;
  Press tab to list possible options. Enter to move to the next question.&lt;br /&gt;
  &amp;gt; no                                                                                                                                                                                                                                        &lt;br /&gt;
  &lt;br /&gt;
  Do you want to capture any browsers automatically ?&lt;br /&gt;
  Press tab to list possible options. Enter empty string to move to the next question.&lt;br /&gt;
  &amp;gt; PhantomJS                                                                                                                                                                                                                                 &lt;br /&gt;
  &amp;gt;                                                                                                                                                                                                                                           &lt;br /&gt;
  &lt;br /&gt;
  What is the location of your source and test files ?&lt;br /&gt;
  You can use glob patterns, eg. &amp;quot;js/*.js&amp;quot; or &amp;quot;test/**/*Spec.js&amp;quot;.&lt;br /&gt;
  Enter empty string to move to the next question.&lt;br /&gt;
  &amp;gt;                                                                                                                                                                                                                                           &lt;br /&gt;
  &lt;br /&gt;
  Should any of the files included by the previous patterns be excluded ?&lt;br /&gt;
  You can use glob patterns, eg. &amp;quot;**/*.swp&amp;quot;.&lt;br /&gt;
  Enter empty string to move to the next question.&lt;br /&gt;
  &amp;gt;                                                                                                                                                                                                                                           &lt;br /&gt;
  &lt;br /&gt;
  Do you want Karma to watch all the files and run the tests on change ?&lt;br /&gt;
  Press tab to list possible options.&lt;br /&gt;
  &amp;gt; no                                                                                                                                                                                                                                        &lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  Config file generated at &amp;quot;/home/jb/code/appsuite/ox_pgp_mail/karma.conf.js&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Edit the generated file and adjust the following configuration variables:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
basePath: 'build/',&lt;br /&gt;
frameworks: ['ox-ui', 'sinon', 'mocha', 'chai'],&lt;br /&gt;
files: [&lt;br /&gt;
    'spec/test-main.js',&lt;br /&gt;
    {pattern: 'apps/**/*.js', included: false},&lt;br /&gt;
    {pattern: 'spec/**/*_spec.js', included: false}&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Generate a main loader script to start the test after App Suite Core UI&lt;br /&gt;
has been booted. The file should be put in `spec/test-main.js`:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
var allTestFiles = [];&lt;br /&gt;
var TEST_REGEXP = /_spec\.js$/i;&lt;br /&gt;
&lt;br /&gt;
var pathToModule = function(path) {&lt;br /&gt;
  return path;&lt;br /&gt;
//   return path.replace(/^\/base\//, '').replace(/\.js$/, '');&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
Object.keys(window.__karma__.files).forEach(function(file) {&lt;br /&gt;
  if (TEST_REGEXP.test(file)) {&lt;br /&gt;
    // Normalize paths to RequireJS module names.&lt;br /&gt;
    allTestFiles.push(pathToModule(file));&lt;br /&gt;
  }&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
require(['io.ox/core/extPatterns/stage'], function (Stage) {&lt;br /&gt;
&lt;br /&gt;
    'use strict';&lt;br /&gt;
&lt;br /&gt;
        ox.testUtils.stubAppsuiteBody();&lt;br /&gt;
&lt;br /&gt;
        new Stage('io.ox/core/stages', {&lt;br /&gt;
            id: 'run_tests',&lt;br /&gt;
            index: 99999,&lt;br /&gt;
            run: function (baton) {&lt;br /&gt;
                requirejs.config({&lt;br /&gt;
                    // Karma serves files from '/base/apps'&lt;br /&gt;
                    baseUrl: '/base/apps',&lt;br /&gt;
&lt;br /&gt;
                    // ask Require.js to load these files (all our tests)&lt;br /&gt;
                    deps: allTestFiles,&lt;br /&gt;
&lt;br /&gt;
                    // start test run, once Require.js is done&lt;br /&gt;
                    callback: window.__karma__.start&lt;br /&gt;
                });&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Dealing with JSHINT ===&lt;br /&gt;
JSHINT is notoriously picky. And rightly so. But we still need to teach it to ignore our test frameworks' peculiarities. Extend the &amp;lt;tt&amp;gt;global&amp;lt;/tt&amp;gt; part of your &amp;lt;tt&amp;gt;.jshintrc&amp;lt;/tt&amp;gt; by these switches:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
   &amp;quot;globals&amp;quot;: {&lt;br /&gt;
        &amp;quot;assert&amp;quot;: false,&lt;br /&gt;
        &amp;quot;describe&amp;quot;: false,&lt;br /&gt;
        &amp;quot;it&amp;quot;: false,&lt;br /&gt;
        &amp;quot;beforeEach&amp;quot;: false,&lt;br /&gt;
        &amp;quot;afterEach&amp;quot;: false,&lt;br /&gt;
        &amp;quot;expect&amp;quot;: false,&lt;br /&gt;
        &amp;quot;waitsFor&amp;quot;: false,&lt;br /&gt;
        &amp;quot;runs&amp;quot;: false,&lt;br /&gt;
        &amp;quot;chai&amp;quot;: false,&lt;br /&gt;
        &amp;quot;sinon&amp;quot;: false,&lt;br /&gt;
        &amp;quot;spyOn&amp;quot;: false,&lt;br /&gt;
        &amp;quot;xit&amp;quot;: false,&lt;br /&gt;
        &amp;quot;xdescribe&amp;quot;: false,&lt;br /&gt;
        &amp;quot;jasmine&amp;quot;: false&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Running the tests ===&lt;br /&gt;
&lt;br /&gt;
There are multiple targets provided in&lt;br /&gt;
[https://github.com/Open-Xchange-Frontend/shared-grunt-config shared grunt configuration].&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; needs to be configured and point to an existing build of the core UI (coreDir setting). When testing on a machine with the core UI installed from distribution packages, also the German translations need to be installed to run the tests. After that, coreDir can be set to &amp;lt;tt&amp;gt;/opt/open-xchange/appsuite/&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The recommended way to start testing is to run:&lt;br /&gt;
&lt;br /&gt;
  grunt dev&lt;br /&gt;
&lt;br /&gt;
This will start a connect server, the karma&lt;br /&gt;
test server and a watcher for changes. Optionally, it is possible to connect multiple browsers to the host running the karma server (port 9876). Tests will run in those browsers, too. You can trigger a test run manually by running:&lt;br /&gt;
&lt;br /&gt;
  grunt testrun&lt;br /&gt;
&lt;br /&gt;
in another terminal. This will be done automatically by the grunt watch&lt;br /&gt;
task, after any source file of your project has been changed.&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Development process]]&lt;br /&gt;
[[Category:Testing]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:RunTests&amp;diff=21458</id>
		<title>AppSuite:RunTests</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:RunTests&amp;diff=21458"/>
		<updated>2016-02-11T08:39:42Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Running the ui tests&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
This article explains the test system of the frontend. It is aimed at developers that want to work with the frontend, be it creating new plugins or applications or modifying existing code using BDD. Bringing a BDD testing infrastructure to the frontend is still a work in progress and subject to (breaking) changes. Please contribute to the stability by reporting any issues or ideas to [[User:J.ohny.b|me]].&lt;br /&gt;
&lt;br /&gt;
== Libraries ==&lt;br /&gt;
&lt;br /&gt;
* [http://pivotal.github.io/jasmine/ Jasmine] - JS BDD framework&lt;br /&gt;
* [http://sinonjs.org/  Sinon.JS] - Standalone test spies, stubs and mocks for JavaScript.&lt;br /&gt;
* [http://karma-runner.github.io/  Karma Runner] - test runner&lt;br /&gt;
* [http://chaijs.com/ Chai.js] - Assertion framework&lt;br /&gt;
&lt;br /&gt;
== Setting up your system ==&lt;br /&gt;
&lt;br /&gt;
=== Before starting: Mac only ===&lt;br /&gt;
   brew install phantomjs&lt;br /&gt;
...or link it in path and set executable bit on phantomjs binary yourself. After this follow the rest of the guide.&lt;br /&gt;
&lt;br /&gt;
=== All ===&lt;br /&gt;
You need at least ''node'' version 0.8 to use the latest version of ''karma'', which we need. Karma will be installed with all other development dependencies. So just make sure you ran&lt;br /&gt;
&lt;br /&gt;
    npm install&lt;br /&gt;
&lt;br /&gt;
within your ui directory in the appsuite repository.&lt;br /&gt;
&lt;br /&gt;
== Running the tests ==&lt;br /&gt;
&lt;br /&gt;
The recommended way to start testing is to run:&lt;br /&gt;
&lt;br /&gt;
  grunt dev&lt;br /&gt;
&lt;br /&gt;
This will start a connect server, the karma&lt;br /&gt;
test server and a watcher for changes. Optionally, it is possible to connect multiple browsers to the host running the karma server (port 9876). Tests will run in those browsers, too. You can trigger a test run manually by running:&lt;br /&gt;
&lt;br /&gt;
  grunt testrun&lt;br /&gt;
&lt;br /&gt;
in another terminal. This will be done automatically by the grunt watch&lt;br /&gt;
task, after any source file of your project has been changed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Development process]]&lt;br /&gt;
[[Category:Testing]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Http.js&amp;diff=21457</id>
		<title>AppSuite:Http.js</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Http.js&amp;diff=21457"/>
		<updated>2016-02-11T08:30:13Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Requesting server api with http.js&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Abstract'''&lt;br /&gt;
Http is intended as centralized server communication layer currently heavily used in our [[AppSuite:APIs | front end APIs]]. For more details than covered in this article dive into it's source code found in ''io.ox/core/http.js''.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= HTTP facades =&lt;br /&gt;
&lt;br /&gt;
''general example''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;http.GET({ &lt;br /&gt;
        module: 'mail',&lt;br /&gt;
        params: { &lt;br /&gt;
            action: 'all', &lt;br /&gt;
            folder: 'default0/INBOX' &lt;br /&gt;
        }&lt;br /&gt;
    });&amp;lt;/pre&amp;gt;&lt;br /&gt;
== GET(options) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/**&lt;br /&gt;
 * Send a GET request&lt;br /&gt;
 * @param {Object}  options Request options &lt;br /&gt;
 * @returns {Object} jQuery's Deferred&lt;br /&gt;
 */&amp;lt;/pre&amp;gt;&lt;br /&gt;
== POST(options) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/**&lt;br /&gt;
 * Send a POST request&lt;br /&gt;
 * @param {Object} options Request options&lt;br /&gt;
 * @returns {Object} jQuery's Deferred&lt;br /&gt;
 */&amp;lt;/pre&amp;gt;&lt;br /&gt;
== FORM(options) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/**&lt;br /&gt;
 * Send a POST request using a FormData object&lt;br /&gt;
 * @param {Object} options Request options&lt;br /&gt;
 * @param {string} options.module Module, e.g. folder, mail, calendar etc.&lt;br /&gt;
 * @param {Object} options.params URL parameters&lt;br /&gt;
 * @returns {Object} jQuery's Deferred&lt;br /&gt;
 */&amp;lt;/pre&amp;gt;&lt;br /&gt;
== PUT(options) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/**&lt;br /&gt;
 * Send a PUT request&lt;br /&gt;
 * @param {Object} options Request options&lt;br /&gt;
 * @returns {Object} jQuery's Deferred&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== DELETE(options) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/**&lt;br /&gt;
 * Send a DELETE request&lt;br /&gt;
 * @param {Object} options Request options&lt;br /&gt;
 * @returns {Object} jQuery's Deferred&lt;br /&gt;
 */&amp;lt;/pre&amp;gt;&lt;br /&gt;
== UPLOAD(options) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/**&lt;br /&gt;
 * Send a UPLOAD request&lt;br /&gt;
 * @param {Object} options Request options&lt;br /&gt;
 * @returns {Object} jQuery's Deferred&lt;br /&gt;
 */&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Column Mappings =&lt;br /&gt;
&lt;br /&gt;
* server requests&lt;br /&gt;
** still require use of columns ids&lt;br /&gt;
* server response&lt;br /&gt;
** column_id keys will be replaced with column names to ease handling&lt;br /&gt;
&lt;br /&gt;
== getAllColumns(module, join) ==&lt;br /&gt;
&amp;lt;pre&amp;gt;/**&lt;br /&gt;
 * get all columns of a module &lt;br /&gt;
 * @param {string} module (name) &lt;br /&gt;
 * @param {boolean} join (join array with comma separator ) &lt;br /&gt;
 * @return {arrray|string} ids */ ```&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== getColumnMapping(module) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/**&lt;br /&gt;
 * returns the column mapping of a module&lt;br /&gt;
 * @param {string} module The module name.&lt;br /&gt;
 * @returns {object} A map from numeric column IDs to the corresponding field names.&lt;br /&gt;
 */&amp;lt;/pre&amp;gt;&lt;br /&gt;
== makeObject(data, module, columns) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/**&lt;br /&gt;
 * transform objects with array-based columns into key-value-based columns&lt;br /&gt;
 * @param {Array} data Data&lt;br /&gt;
 * @param {string} module Module name&lt;br /&gt;
 * @param {Array} columns Columns&lt;br /&gt;
 * @returns {Object} Transformed object&lt;br /&gt;
 */&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Request Stacking =&lt;br /&gt;
&lt;br /&gt;
* stack ability for calls to minimize overhead of server communication&lt;br /&gt;
&lt;br /&gt;
== pause() ''and'' resume() ==&lt;br /&gt;
&lt;br /&gt;
'''example'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// pause http layer&lt;br /&gt;
http.pause();&lt;br /&gt;
&lt;br /&gt;
// process all updates&lt;br /&gt;
_(list).map(function (item) {&lt;br /&gt;
    return http.PUT({&lt;br /&gt;
        module: 'calendar',&lt;br /&gt;
        params: {&lt;br /&gt;
            action: 'update',&lt;br /&gt;
            id: item.id,&lt;br /&gt;
            folder: item.folder_id,&lt;br /&gt;
            timestamp: item.timestamp&lt;br /&gt;
        },&lt;br /&gt;
        data: {  ...  },&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
// resume &amp;amp;amp; trigger refresh&lt;br /&gt;
http.resume()&amp;lt;/pre&amp;gt;&lt;br /&gt;
== retry (request) ==&lt;br /&gt;
&lt;br /&gt;
* retry request&lt;br /&gt;
&lt;br /&gt;
= Utils =&lt;br /&gt;
&lt;br /&gt;
== simplify(list) ==&lt;br /&gt;
&lt;br /&gt;
* simplify objects in array for list requests&lt;br /&gt;
* returns array of items&lt;br /&gt;
* possible returned item types&lt;br /&gt;
** { id: '8978989' }&lt;br /&gt;
** { folder: 'inbox' }&amp;lt;br /&amp;gt;&lt;br /&gt;
** { recurrence_position: 'inbox' }&lt;br /&gt;
** 8978989&lt;br /&gt;
** 'inbox'&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/**&lt;br /&gt;
 * Simplify objects in array for list requests&lt;br /&gt;
 * @param  {array} list&lt;br /&gt;
 * @returns {array} list    &lt;br /&gt;
 */&amp;lt;/pre&amp;gt;&lt;br /&gt;
== fixList(ids, deferred) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/**&lt;br /&gt;
 * Fixes order of list requests (temp. fixes backend bug)&lt;br /&gt;
 * @param  {array} ids&lt;br /&gt;
 * @param  {deferred} deferred&lt;br /&gt;
 * @return {deferred} resolve returns array&lt;br /&gt;
 */&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Logging =&lt;br /&gt;
&lt;br /&gt;
== log() ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/**&lt;br /&gt;
 * returns failed calls&lt;br /&gt;
 * @return {backbone.collection}&lt;br /&gt;
 */&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:UI-Server-Interaction]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Paste_inline_images&amp;diff=21454</id>
		<title>AppSuite:Paste inline images</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Paste_inline_images&amp;diff=21454"/>
		<updated>2016-02-11T06:59:49Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
'''Abstract: ''' Pasting images is basically supported by any major browser. But there are several difficulties to provide a cross-browser solution. This article covers, how to paste images into the different browsers. &lt;br /&gt;
&lt;br /&gt;
== Paste images ==&lt;br /&gt;
&lt;br /&gt;
If speaking about pasting images, it is important to consider the source where the image was copied. Basically, copying images from an application is possible (for details see Section Applications). It seems like these applications are simply storing the image data in the clipboard which can be accessed by the paste events of the browser. &lt;br /&gt;
&lt;br /&gt;
Pasting images from a folder of the operating system does not work on any tested environment. Whereas OS X does provide the filename and a dummy picture, the clipboard of all browsers on Windows Systems remains empty. &lt;br /&gt;
&lt;br /&gt;
== Support ==&lt;br /&gt;
&lt;br /&gt;
The following table lists all major browsers and their ability to insert pasted images from applications.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
! browser&lt;br /&gt;
! paste support (tested version)&lt;br /&gt;
! specifics&lt;br /&gt;
|-&lt;br /&gt;
| Chrome&lt;br /&gt;
| 43.0.2357.81&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| Safari&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color: red;&amp;quot;&amp;gt;no support in 8.0.6&amp;lt;/span&amp;gt;&lt;br /&gt;
| Does not provide images in clipboardData. After pasting, the browser inserts an img-tag with webkit-fake-url. There is no pure javascript solution to retrieve the image data for upload. &lt;br /&gt;
|-&lt;br /&gt;
| Firefox&lt;br /&gt;
| 38.0.1&lt;br /&gt;
| Does not provide images in clipboardData. After pasting, the browser inserts an img-tag with base64 encoded src. &lt;br /&gt;
|-&lt;br /&gt;
| Internet Explorer&lt;br /&gt;
| 11.0.9600.16428&lt;br /&gt;
| IE 10 does not support image pasting.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Applications ==&lt;br /&gt;
&lt;br /&gt;
This table lists some applications which were tested as copy sources for images. Mainly, OS related applications does are not supported as copy source whereas image viewing/editing related applications are supported. &lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
! application&lt;br /&gt;
! paste support&lt;br /&gt;
! operating system&lt;br /&gt;
|-&lt;br /&gt;
| Finder&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color:red;&amp;quot;&amp;gt;no&amp;lt;/span&amp;gt;&lt;br /&gt;
| OS X&lt;br /&gt;
|-&lt;br /&gt;
| Preview&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color: green;&amp;quot;&amp;gt;yes&amp;lt;/span&amp;gt;&lt;br /&gt;
| OS X&lt;br /&gt;
|-&lt;br /&gt;
| Fotos&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color: green;&amp;quot;&amp;gt;yes&amp;lt;/span&amp;gt;&lt;br /&gt;
| OS X&lt;br /&gt;
|-&lt;br /&gt;
| Chrome&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color: green;&amp;quot;&amp;gt;yes&amp;lt;/span&amp;gt;&lt;br /&gt;
| OS X and Windows&lt;br /&gt;
|-&lt;br /&gt;
| Internet Explorer&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color: green;&amp;quot;&amp;gt;yes&amp;lt;/span&amp;gt;&lt;br /&gt;
| Windows&lt;br /&gt;
|-&lt;br /&gt;
| Windows Explorer&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color: red;&amp;quot;&amp;gt;no&amp;lt;/span&amp;gt;&lt;br /&gt;
| Windows&lt;br /&gt;
|-&lt;br /&gt;
| Windows Photo Viewer&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color: red;&amp;quot;&amp;gt;no&amp;lt;/span&amp;gt;&lt;br /&gt;
| Windows&lt;br /&gt;
|-&lt;br /&gt;
| Paint&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color: green;&amp;quot;&amp;gt;yes&amp;lt;/span&amp;gt;&lt;br /&gt;
| Windows&lt;br /&gt;
|-&lt;br /&gt;
| GimP&lt;br /&gt;
| &amp;lt;span style=&amp;quot;color: green;&amp;quot;&amp;gt;yes&amp;lt;/span&amp;gt;&lt;br /&gt;
| OS X and Windows&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Memory_leaks_(UI)&amp;diff=21453</id>
		<title>AppSuite:Memory leaks (UI)</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Memory_leaks_(UI)&amp;diff=21453"/>
		<updated>2016-02-11T06:21:05Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Finding memory leaks in the UI&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Intro''': Searching for memory leaks in the UI is not a pleasant task, but sadly necessary.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Avoiding memory leaks ==&lt;br /&gt;
&lt;br /&gt;
'''resources:'''&lt;br /&gt;
*[https://www.ibm.com/developerworks/library/wa-memleak/ Memory leak patterns]&lt;br /&gt;
&lt;br /&gt;
== Issues ==&lt;br /&gt;
Different browsers offer different ways of tracing memory leaks. As of writing, the core team's favourite browser is Google Chrome.&lt;br /&gt;
&lt;br /&gt;
== Using Chrome ==&lt;br /&gt;
Addy Osmani has written extensively on finding memory leaks with Chrome. His often-quoted article &amp;quot;[http://addyosmani.com/blog/taming-the-unicorn-easing-javascript-memory-profiling-in-devtools/ Taming the Unicorn]&amp;quot; explains how to use the Chrome DevTools for this purpose.&lt;br /&gt;
&lt;br /&gt;
'''additional resources:'''&lt;br /&gt;
* [https://developer.chrome.com/devtools/docs/heap-profiling-dom-leaks Uncovering DOM Leaks]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category: UI]]&lt;br /&gt;
[[Category: Developer]]&lt;br /&gt;
[[Category: Custom development]]&lt;br /&gt;
[[Category: AppSuite]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Supported_file_types&amp;diff=21452</id>
		<title>AppSuite:Supported file types</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Supported_file_types&amp;diff=21452"/>
		<updated>2016-02-11T06:16:41Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= OX App Suite File Support =&lt;br /&gt;
&lt;br /&gt;
OX App Suite web frontend allows you to preview, view and edit files at the different modules. &lt;br /&gt;
&lt;br /&gt;
One of the modules is OX Drive, an integral part the OX App Suite user frontend. OX Drive provides view, preview, run and even edit a variety of media file types (e.g. photos, music documents etc.). Note: to edit documents you do need to have the relevant document editor activated within OX App Suite.&lt;br /&gt;
&lt;br /&gt;
Other components of OX App Suite include OX Document Viewer, which allows users to preview and view various file formats, and OX Documents which further enhances collaboration and creativity by allowing real time sharing, collaboration and editing of documents online. OX Documents contains OX Text and OX Spreadsheet.&lt;br /&gt;
&lt;br /&gt;
A common question is what files are supported of the different modules. The following list is not definitive&lt;br /&gt;
&lt;br /&gt;
== OX Drive integrated Mediaplayer ==&lt;br /&gt;
&lt;br /&gt;
The OX Drive &amp;quot;media player&amp;quot; uses the native html 5 video playback of each browser. The actual file support depends on the user's browser, so a definitive list can not be given:&lt;br /&gt;
&lt;br /&gt;
=== Supported Video-Files inside OX Drive Mediaplayer ===&lt;br /&gt;
&lt;br /&gt;
* Chrome: m4v, ogv, webm&lt;br /&gt;
* Safari: m4v&lt;br /&gt;
* IE: m4v&lt;br /&gt;
* Firefox: ogv, webm&lt;br /&gt;
&lt;br /&gt;
=== Supported Audio-Files inside OX Drive Mediaplayer ===&lt;br /&gt;
&lt;br /&gt;
For audio files a flash/silverlight fallback is used if a browser is not capable of native mp3 playback like Firefox.&lt;br /&gt;
&lt;br /&gt;
* Chrome: mp3, wav, m4a, m4b, ogg&lt;br /&gt;
* Safari: mp3, wav, m4a, m4b, aac&lt;br /&gt;
* IE: mp3, m4a, m4b&lt;br /&gt;
* Firefox: mp3, wav, ogg&lt;br /&gt;
&lt;br /&gt;
== OX Document Viewer ==&lt;br /&gt;
&lt;br /&gt;
The OX Document Viewer delivers plugin-free document viewing capabilities for Microsoft Office (.docx, .doc, .rtf, .pptx, .ppt, .xlsx, xls) and OpenDocument (.odt, .ods, .odp, .odg) file types as well as for the Portable Document Format (.pdf). It extends OX App Suite with content thumbnails and preview capabilities.&lt;br /&gt;
&lt;br /&gt;
=== Images ===&lt;br /&gt;
&lt;br /&gt;
* png&lt;br /&gt;
* jpg, jpeg&lt;br /&gt;
&lt;br /&gt;
=== Text Documents ===&lt;br /&gt;
&lt;br /&gt;
* docx (Microsoft Office Word text document)&lt;br /&gt;
* docm (Microsoft Office Word macro-enabled text document)&lt;br /&gt;
* dotx (Microsoft Office Word text document template)&lt;br /&gt;
* dotm (Microsoft Office Word macro-enabled text document template)&lt;br /&gt;
* odt (OpenDocument text document)&lt;br /&gt;
* ott (OpenDocument text document template)&lt;br /&gt;
* doc (Microsoft Word 97-2003 text document)&lt;br /&gt;
* dot (Microsoft Word 97-2003 text document template)&lt;br /&gt;
* rtf (Rich Text Format)&lt;br /&gt;
&lt;br /&gt;
=== Spreadsheet Documents ===&lt;br /&gt;
&lt;br /&gt;
* xlsx (Microsoft Office Excel workbook)&lt;br /&gt;
* xlsm (Microsoft Office Excel macro-enabled workbook)&lt;br /&gt;
* xltx (Microsoft Office Excel workbook template)&lt;br /&gt;
* xltm (Microsoft Office Excel macro-enabled workbook template)&lt;br /&gt;
* xlsb (Microsoft Office Excel macro-enabled workbook, binary format)&lt;br /&gt;
* xlam (Microsoft Office Excel add-in)&lt;br /&gt;
* ods (OpenDocument spreadsheet document)&lt;br /&gt;
* ots (OpenDocument spreadsheet document template)&lt;br /&gt;
* xls (Microsoft Excel 97-2003 workbook)&lt;br /&gt;
* xlt (Microsoft Excel 97-2003 workbook template)&lt;br /&gt;
* xla (Microsoft Excel 97-2003 add-in)&lt;br /&gt;
&lt;br /&gt;
=== Presentation Documents ===&lt;br /&gt;
&lt;br /&gt;
* pptx (Microsoft Office PowerPoint presentation)&lt;br /&gt;
* pptm (Microsoft Office PowerPoint macro-enabled presentation)&lt;br /&gt;
* potx (Microsoft Office PowerPoint presentation template)&lt;br /&gt;
* potm (Microsoft Office PowerPoint macro-enabled presentation template)&lt;br /&gt;
* ppsx (Microsoft Office PowerPoint slide show)&lt;br /&gt;
* ppsm (Microsoft Office PowerPoint macro-enabled slide show)&lt;br /&gt;
* ppam (Microsoft Office PowerPoint add-in)&lt;br /&gt;
* odp (OpenDocument presentation document)&lt;br /&gt;
* otp (OpenDocument presentation document template)&lt;br /&gt;
* ppt (Microsoft PowerPoint 97-2003 presentation)&lt;br /&gt;
* pot (Microsoft PowerPoint 97-2003 presentation template)&lt;br /&gt;
* pps (Microsoft PowerPoint 97-2003 slide show)&lt;br /&gt;
* ppa (Microsoft PowerPoint 97-2003 add-in)&lt;br /&gt;
&lt;br /&gt;
=== Miscellaneous ===&lt;br /&gt;
&lt;br /&gt;
* pdf (Portable Document Format)&lt;br /&gt;
* odg (OpenDocument drawing document)&lt;br /&gt;
* otg (OpenDocument drawing document template)&lt;br /&gt;
* odi (OpenDocument image document)&lt;br /&gt;
* oti (OpenDocument image document template)&lt;br /&gt;
* odc (OpenDocument chart document)&lt;br /&gt;
* otc (OpenDocument chart document template)&lt;br /&gt;
* odf (OpenDocument formula document)&lt;br /&gt;
* otf (OpenDocument formula document template)&lt;br /&gt;
* odm (OpenDocument global text document)&lt;br /&gt;
&lt;br /&gt;
== OX Documents ==&lt;br /&gt;
&lt;br /&gt;
OX Documents contains OX Text and OX Spreadsheet.&lt;br /&gt;
&lt;br /&gt;
=== OX Text Documents ===&lt;br /&gt;
&lt;br /&gt;
OX Text, is a web-based word processor. Its main focus is on reducing the complexities of text editing while promoting collaborative document creation.&lt;br /&gt;
&lt;br /&gt;
* docx (Microsoft Office Word text document)&lt;br /&gt;
* docm (Microsoft Office Word macro-enabled text document)&lt;br /&gt;
* dotx (Microsoft Office Word text document template)&lt;br /&gt;
* dotm (Microsoft Office Word macro-enabled text document template)&lt;br /&gt;
* odt (OpenDocument text document)&lt;br /&gt;
* ott (OpenDocument text document template)&lt;br /&gt;
* doc (Microsoft Word 97-2003 text document, converted to &amp;quot;docx&amp;quot; when edited)&lt;br /&gt;
* dot (Microsoft Word 97-2003 text document template, converted to &amp;quot;dotx&amp;quot; when edited)&lt;br /&gt;
* rtf (Rich Text Format, converted to &amp;quot;docx&amp;quot; when edited)&lt;br /&gt;
&lt;br /&gt;
=== OX Spreadsheet Documents ===&lt;br /&gt;
&lt;br /&gt;
OX Spreadsheet is a cloud based spreadsheet product that can work with Microsoft Excel documents. You can also edit shared spreadsheets on various devices. OX Spreadsheet is the first browser-based spreadsheet application that reads and writes native Microsoft Excel files without loss of format or detail (&amp;quot;Round-trip editing&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* xlsx (Microsoft Office Excel workbook)&lt;br /&gt;
* xlsm (Microsoft Office Excel macro-enabled workbook)&lt;br /&gt;
* xltx (Microsoft Office Excel workbook template)&lt;br /&gt;
* xltm (Microsoft Office Excel macro-enabled workbook template)&lt;br /&gt;
* xlsb (Microsoft Office Excel macro-enabled workbook, binary format)&lt;br /&gt;
* xlam (Microsoft Office Excel add-in)&lt;br /&gt;
* ods (OpenDocument spreadsheet document)&lt;br /&gt;
* ots (OpenDocument spreadsheet document template)&lt;br /&gt;
* xls (Microsoft Excel 97-2003 workbook)&lt;br /&gt;
* xlt (Microsoft Excel 97-2003 workbook template)&lt;br /&gt;
* xla (Microsoft Excel 97-2003 add-in)&lt;br /&gt;
&lt;br /&gt;
=== OX Presenter ===&lt;br /&gt;
&lt;br /&gt;
OX Presenter allows you to view presentations in a specialized user interface, where you also can give local presentations or presentations where remote participants can join.&lt;br /&gt;
&lt;br /&gt;
For a list of supported file types, see &amp;quot;Presentation Documents&amp;quot; in the OX Document Viewer section above.&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Server]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:UI_FAQ&amp;diff=21451</id>
		<title>AppSuite:UI FAQ</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:UI_FAQ&amp;diff=21451"/>
		<updated>2016-02-11T06:16:04Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Synopsis:''' Frequently asked questions about running the App Suite UI. If you need a tool set for finding things out about the UI, see [[AppSuite:Debugging the UI]].&lt;br /&gt;
&lt;br /&gt;
= Getting the UI started =&lt;br /&gt;
Frequently asked by new developers. And forgetful veterans. Who therefore write documentation pieces like this.&lt;br /&gt;
&lt;br /&gt;
== Does show the login screen after logging in ==&lt;br /&gt;
This means you managed to find a bug even before our error message module could be loaded. There is something rather basic broken, maybe the DB connection or some of the standard .js&lt;br /&gt;
&lt;br /&gt;
The only hint we can give from a UI perspective is to use the developer console on your browser and, if applicable, turn on &amp;quot;halt on all errors&amp;quot; or &amp;quot;preserve logging data&amp;quot;. You might see an error message that otherwise gets eaten by a redirect.&lt;br /&gt;
&lt;br /&gt;
If you are running the backend yourself, you are in luck, go, look at the server error log.&lt;br /&gt;
&lt;br /&gt;
== Does not load, fails with error: Could not read... ==&lt;br /&gt;
 &amp;quot;Could not read 'io.ox/core/http.js'&amp;quot; &lt;br /&gt;
 &amp;quot;Could not read 'io.ox/core/events.js'&amp;quot; &lt;br /&gt;
That's not all, the system is probably hiding even more errors. It is more probable that the whole UI cannot be found. Check the com.openexchange.apps.path, usually hidden in manifests.properties. Does it point to where your webserver serves the files from? The default is /var/www/appsuite, on OSX is is more likely to be /Library/WebServer/Documents/appsuite&lt;br /&gt;
&lt;br /&gt;
== App Suite loads, but no apps show up ==&lt;br /&gt;
So bother backend and frontend are devoid of errors, the UI loads nicely, you see the top bar with settings, notifications and refresh button but no apps at all, right? This is a manifest problem. Check the capabilities (see hint in the beginning of this page). If even those are correct, check the backend's /tmp folder and the manifest.properties there: Are the paths of &amp;lt;tt&amp;gt;com.openexchange.apps.path&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;com.openexchange.apps.manifestPath&amp;lt;/tt&amp;gt; pointing to your build directory?&lt;br /&gt;
&lt;br /&gt;
== Could not read 'io.ox/office/preview/app/... when loading anything but the portal ==&lt;br /&gt;
You lack preview capabilities. Since you can choose what to use, here are your options&lt;br /&gt;
* use the URL parameter &amp;lt;tt&amp;gt;disableFeature=document_preview%2Ctext&amp;lt;/tt&amp;gt;&lt;br /&gt;
* figure out how to install office&lt;br /&gt;
* use the existing preview bundles&lt;br /&gt;
&lt;br /&gt;
== After logging in I'm dumped back to the login page with a #autologin=false added to the URL ==&lt;br /&gt;
&lt;br /&gt;
You might not see an error message in the console then. Configure your JS console to preserve messages on navigation (so after a redirect), and you might get a hint as to what's wrong.&lt;br /&gt;
&lt;br /&gt;
= UI build environment =&lt;br /&gt;
&lt;br /&gt;
There is an explicit [[AppSuite:GruntFAQ|FAQ page]] for grunt related questions when working with external modules.&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Developer]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Emoji&amp;diff=21450</id>
		<title>AppSuite:Emoji</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Emoji&amp;diff=21450"/>
		<updated>2016-02-11T05:58:36Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Emoji Support&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Abstract.''' In this article, the support for emoji is described in detail. Learn about how different icon sets can be included and configured.&lt;br /&gt;
__TOC__&lt;br /&gt;
== Enabling emoji support ==&lt;br /&gt;
Emoji support is disabled by default. In order to enable the feature, you must define the capability '''emoji'''. This is done by just adding the word &amp;quot;emoji&amp;quot; to the property &amp;quot;permissions&amp;quot; in '''/opt/openexchange/etc/permission.properties''':&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
permissions=...,emoji&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Settings ==&lt;br /&gt;
Configuration will be served via [[AppSuite:jslob|jslob]] service at the following path.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
io.ox/mail/emoji&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following settings are relevant for emoji support:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
io.ox/mail/emoji//defaultCollection=unified&lt;br /&gt;
io.ox/mail/emoji//availableCollections=unified,two,three&lt;br /&gt;
io.ox/mail/emoji//forceEmojiIcons=false&lt;br /&gt;
io.ox/mail/emoji//collectionControl=none # possible values 'none', 'tabs', 'dropdown'&lt;br /&gt;
io.ox/mail/emoji//autoClose=false&lt;br /&gt;
io.ox/mail/emoji//userCollection=emoji/defaultCollection&lt;br /&gt;
io.ox/mail/emoji//overrideUserCollection=false&lt;br /&gt;
io.ox/mail/emoji//sendEncoding=unified # possible values 'unified', 'pua'&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In order to configure this server-side, just append to existing appsuite.properties or create a new file emoji.properties (in same directory; please mind the double-slash, this in not a typo! plus: changing such settings requires a backend restart).&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Description&lt;br /&gt;
|-&lt;br /&gt;
| defaultCollection || Default collection.&lt;br /&gt;
|-&lt;br /&gt;
| availableCollections || Available collections. Comma separated. The order is important, since this will be used as an order for the fallback mechanism. If an icon is not found in the userCollection, the icon is looked up in each available collection and the first 'hit' will be used.&lt;br /&gt;
|-&lt;br /&gt;
| forceEmojiIcons || always convert emoji unicode characters to img-tags&lt;br /&gt;
|-&lt;br /&gt;
| collectionControl || none, dropdown, tabs.&lt;br /&gt;
|-&lt;br /&gt;
| autoClose || Emoji pallet closes when user click or presses any key inside editor&lt;br /&gt;
|-&lt;br /&gt;
| userCollection || Current user collection&lt;br /&gt;
|-&lt;br /&gt;
| overrideUserCollection&lt;br /&gt;
||Set this setting to true and the current user collection will not be prefered when replacing unicode characters with an image. (See comment for availableCollections describing the fallback mechanism.) Since Mail compose works with a current collection object, this setting has no effect when inserting icons from the editor plugin into the text. But when rendering any text with unicode characters, this setting will have an effect on the icon that is shown.&lt;br /&gt;
|-&lt;br /&gt;
| sendEncoding || Override the default send encoding to use something else than unified (unicode6)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== How to add a new icon set ==&lt;br /&gt;
&lt;br /&gt;
It is possible to add new icon sets by [[Appsuite:Writing_a_simple_application|writing a core plugin]]. It is recommended, to install the files to&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
apps/3rd.party/emoji/&amp;lt;iconSetName&amp;gt;/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Put all your CSS code and images into this directory and create a register.js adding the CSS/LESS files as dependencies.&lt;br /&gt;
&lt;br /&gt;
Once this is done, you need to add the CSS also to the tinyMCE editor, because an iframe is used to edit the text and in order to have full emoji support, you need to load the CSS code there as well. Doing so is really easy. There is an extension point you can use to add the paths to your CSS files.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
define('3rd.party/emoji/greatestIconSet/register',&lt;br /&gt;
   ['io.ox/core/extensions',&lt;br /&gt;
    'css!3rd.party/emoji/greatestIconSet/emoji.css',&lt;br /&gt;
    'css!3rd.party/emoji/greatestIconSet/emoji_categories.css'&lt;br /&gt;
   ], function (ext) {&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;use strict&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    ext.point('3rd.party/emoji/editor_css').extend({&lt;br /&gt;
        id: 'greatestIconSet/categories',&lt;br /&gt;
        css: '3rd.party/emoji/greatestIconSet/emoji_categories.css'&lt;br /&gt;
    });&lt;br /&gt;
    ext.point('3rd.party/emoji/editor_css').extend({&lt;br /&gt;
        id: 'greatestIconSet/icons',&lt;br /&gt;
        css: '3rd.party/emoji/greatestIconSet/emoji.css'&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Naming conventions in CSS code ===&lt;br /&gt;
&lt;br /&gt;
The CSS must follow some easy structure. There must be a base class for all icons that defines the background image. It must match the name of your icon set prefixed with “.emoji-”.&lt;br /&gt;
&lt;br /&gt;
Each icon will then be identified by a string that can be defined in the icon set metadata.&lt;br /&gt;
&lt;br /&gt;
Find here a short example copied from the unified icon set shipped with Appsuite.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.emoji-unified { &lt;br /&gt;
    background: url(&amp;quot;emoji.png&amp;quot;) top left no-repeat;&lt;br /&gt;
    width: 20px;&lt;br /&gt;
    height: 20px;&lt;br /&gt;
    display: -moz-inline-stack;&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    vertical-align: top;&lt;br /&gt;
    zoom: 1;&lt;br /&gt;
    *display: inline;&lt;br /&gt;
}&lt;br /&gt;
.emoji2600 { &lt;br /&gt;
    background-position: -500px -120px;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Server]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:i18n]]&lt;br /&gt;
[[Category:Config]]&lt;br /&gt;
[[Category:AppSuite]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Embedding_your_settings_into_AppSuite_settings&amp;diff=21449</id>
		<title>AppSuite:Embedding your settings into AppSuite settings</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Embedding_your_settings_into_AppSuite_settings&amp;diff=21449"/>
		<updated>2016-02-11T05:57:59Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Embedding your settings into AppSuite settings&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Synopsis:'' This article explains how you can embed your own configuration page via iFrame into the AppSuite's settings and pass our session onto your implementation. This is a replacement for &amp;quot;Config Jump&amp;quot; of OX6. Not to be confused with [[AppSuite:Creating_a_settings_section_in_AppSuite_settings | simply adding new settings]] into AppSuite&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Declare the page you want to embed ==&lt;br /&gt;
&lt;br /&gt;
You can declare pages to embed via [[ConfigCascade|Config Cascade]] settings. There are several ways to do so, this example uses the most comfortable one, a YAML declaration:&lt;br /&gt;
&lt;br /&gt;
 ➜ /opt/open-xchange/etc/settings/configjump.yml&lt;br /&gt;
 io.ox/settings/configjump//changePlans:&lt;br /&gt;
    url: &amp;quot;http://localhost/~fla/changePlans.php?token=[token]&amp;quot;&lt;br /&gt;
    title: &amp;quot;Change Plan&amp;quot;&lt;br /&gt;
    after: &amp;quot;io.ox/mail&amp;quot;&lt;br /&gt;
    advancedMode: false&lt;br /&gt;
&lt;br /&gt;
''io.ox/settings/configjump'' contains one object per embedded page (e.g. &amp;quot;changePlans&amp;quot;). If you want to add more pages, follow this pattern.&lt;br /&gt;
&lt;br /&gt;
An object of this type has the following properties:&lt;br /&gt;
&lt;br /&gt;
* '''url''': The URL to be branched to. The place holder [token] will be replaced by the token you get from the token login system&lt;br /&gt;
* '''title''': The title as to be seen on the settings page.&lt;br /&gt;
* '''after''', '''before''' or '''index''': Where the page is supposed to be positioned. ''Hint:'' If you want to name a page as reference (as opposed to using the index), you need to figure out the name. One way to do so is go to that page in the settings and check for the id parameter in the URL.&lt;br /&gt;
* '''advancedMode''': true or false to define if shown in advanced settings mode or not. Default is false&lt;br /&gt;
&lt;br /&gt;
It's also possible to provide custom translations for the title. Just add &amp;quot;title_&amp;quot; plus the locale:&lt;br /&gt;
 &lt;br /&gt;
 io.ox/settings/configjump//changePlans:&lt;br /&gt;
    url: &amp;quot;http://localhost/~fla/changePlans.php?token=[token]&amp;quot;&lt;br /&gt;
    title: &amp;quot;Change Plan&amp;quot;&lt;br /&gt;
    title_en_US: &amp;quot;Change plan&amp;quot;&lt;br /&gt;
    title_de_DE: &amp;quot;Plan ändern&amp;quot;&lt;br /&gt;
    title_fr_FR: &amp;quot;...&amp;quot;&lt;br /&gt;
    ...&lt;br /&gt;
    after: &amp;quot;io.ox/mail&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Create a secret ==&lt;br /&gt;
&lt;br /&gt;
Now you just need to declare the app your are about to embed in the backend and you are good to go:&lt;br /&gt;
&lt;br /&gt;
 ➜ cat /opt/open-xchange/etc/tokenlogin-secrets&lt;br /&gt;
 #&lt;br /&gt;
 # Listing of known Web Application secrets followed by an optional semicolon-separated parameter list&lt;br /&gt;
 #&lt;br /&gt;
 # e.g. 1254654698621354; accessPasword=true&lt;br /&gt;
 #&lt;br /&gt;
 &lt;br /&gt;
 # Dummy entry&lt;br /&gt;
 # 1234-56789-98765-4321; accessPassword=true&lt;br /&gt;
 12345-phpapp-54321&lt;br /&gt;
&lt;br /&gt;
This secret, combined with the token, can be traded for a login.&lt;br /&gt;
&lt;br /&gt;
== Redeem a token ==&lt;br /&gt;
&lt;br /&gt;
 GET /login?action=redeemToken&lt;br /&gt;
&lt;br /&gt;
* '''token''': The token you want to trade.&lt;br /&gt;
* '''secret''': A valid secret for your app.&lt;br /&gt;
&lt;br /&gt;
This request can be sent by the embedded app to the AppSuite backend to get authorisation info.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
&lt;br /&gt;
[[Category:UI]][[Category:Backend]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Administrator]][[Category:Developer]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Stuck somewhere? ==&lt;br /&gt;
You got stuck with a problem while developing? OXpedia might help you out with the article about [[AppSuite:Debugging_the_UI | debugging the UI]].&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Device_reference&amp;diff=21448</id>
		<title>AppSuite:Device reference</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Device_reference&amp;diff=21448"/>
		<updated>2016-02-11T05:39:11Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-unstable}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;_.device() reference&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;tt&amp;gt;_.device()&amp;lt;/tt&amp;gt; is used to test various device-specific aspects of the runtime environment. It is also used to evaluate the &amp;lt;tt&amp;gt;device&amp;lt;/tt&amp;gt; clause of manifest entries.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= Syntax =&lt;br /&gt;
&lt;br /&gt;
The single parameter of the function is a string describing the test to perform. The string is evaluated as a JavaScript expression which uses one or more pre-defined variables. The result is then always converted to a boolean value. Empty strings and other 'falsy' values evaluate to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;. While the implementation currently simply uses the native JavaScript interpreter, only parentheses, boolean operators and numerical comparisons are guaranteed to actually work.&lt;br /&gt;
&lt;br /&gt;
= Variables =&lt;br /&gt;
&lt;br /&gt;
This section lists the supported variable names which can be passed to &amp;lt;tt&amp;gt;_.device()&amp;lt;/tt&amp;gt;. The current list of variables and their values can be displayed by calling &amp;lt;tt&amp;gt;_.device()&amp;lt;/tt&amp;gt; without arguments in the browser's JavaScript console. All variables are case-insensitive, and by convention are lower-case.&lt;br /&gt;
&lt;br /&gt;
While all variables can be used as boolean flags, most variables representing browsers and operating systems are actually numbers and can be used to check for specific browser or OS versions. The remaining browser and OS variables can become numbers in the future, too. The numbers are usually integers, i.e. they contain only the major version number.&lt;br /&gt;
&lt;br /&gt;
== Browsers ==&lt;br /&gt;
&lt;br /&gt;
* Numeric versions&lt;br /&gt;
** &amp;lt;tt&amp;gt;ie&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;opera&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;safari&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;phantomjs&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;chrome&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;firefox&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Boolean flags&lt;br /&gt;
** &amp;lt;tt&amp;gt;webkit&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;chromeios&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;uiwebview&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Operating systems ==&lt;br /&gt;
&lt;br /&gt;
* Numeric versions&lt;br /&gt;
** &amp;lt;tt&amp;gt;blackberry&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;ios&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;android&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Boolean flags&lt;br /&gt;
** &amp;lt;tt&amp;gt;windowsphone&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;macos&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;windows&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Display metrics ==&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tt&amp;gt;small&amp;lt;/tt&amp;gt; (up to 480px)&lt;br /&gt;
* &amp;lt;tt&amp;gt;medium&amp;lt;/tt&amp;gt; (481px to 1024px)&lt;br /&gt;
* &amp;lt;tt&amp;gt;large&amp;lt;/tt&amp;gt; (1025px and more)&lt;br /&gt;
* &amp;lt;tt&amp;gt;landscape&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;portrait&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;retina&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;smartphone&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;tablet&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;desktop&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Language ==&lt;br /&gt;
&lt;br /&gt;
The current language code is defined as a variable. To match any variant of the current language, the second part can be replaced by an asterisk. E.g. if the current language is en_US, then the following variables would be defined:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tt&amp;gt;en_us&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;en_*&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous ==&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tt&amp;gt;touch&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;standalone&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;emoji&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Examples =&lt;br /&gt;
&lt;br /&gt;
The simplest example is a single variable, e.g.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;_.device('smartphone')&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;_.device('firefox')&amp;lt;/pre&amp;gt;&lt;br /&gt;
or&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;_.device('de_*')&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Workarounds for specific older browsers can be enabled by testing for a specific version, e.g.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;_.device('ie &amp;amp;&amp;amp; (ie &amp;lt; 11)')&amp;lt;/pre&amp;gt;&lt;br /&gt;
The additional check for the correct browser is necessary because in other browsers, the variable &amp;lt;tt&amp;gt;ie&amp;lt;/tt&amp;gt; evaluates to &amp;lt;tt&amp;gt;undefined&amp;lt;/tt&amp;gt;, and the comparisons with numbers always return &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;. This may or may not be what is desired, so an explicit check for the browser is less error-prone and easier to understand.&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Debugging_production_servers&amp;diff=21447</id>
		<title>AppSuite:Debugging production servers</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Debugging_production_servers&amp;diff=21447"/>
		<updated>2016-02-11T05:36:52Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Debugging production servers&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A how-to for debugging your local UI plugin in combination with production and staging servers, which use redirects, HTTPS, and other things which break with the auto-generated Grunt configuration.&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Not everybody has access to a dedicated test server. Sometimes, a plugin requires a backend system which is hard to access outside of existing infrastructure. And sometimes, a bug appears only on a production server. There are many reasons why a server which is used by &amp;lt;tt&amp;gt;grunt dev&amp;lt;/tt&amp;gt; can not be reconfigured and has to be used as-is.&lt;br /&gt;
&lt;br /&gt;
The most frequent reason, why debugging with a remote server fails, is a redirect to an external login page. After the login, the browser is usually redirected to the original domain, and all the cookies used for authentication are also set for that domain, not http://localhost:8337, where Grunt listens.&lt;br /&gt;
&lt;br /&gt;
The solution is to use grunt as an HTTP proxy server, which intercepts all requests to the server's domain.&lt;br /&gt;
&lt;br /&gt;
In this article, &amp;lt;tt&amp;gt;''example.com''&amp;lt;/tt&amp;gt; is user for the server domain, which you should replace by the real domain of the OX server.&lt;br /&gt;
&lt;br /&gt;
== Configuring Grunt ==&lt;br /&gt;
&lt;br /&gt;
Following is an example &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; file:&lt;br /&gt;
&lt;br /&gt;
 {&lt;br /&gt;
     &amp;quot;proxy&amp;quot;: true,&lt;br /&gt;
     &amp;quot;appserver&amp;quot;: {&lt;br /&gt;
         &amp;quot;protocol&amp;quot;: &amp;quot;https&amp;quot;,&lt;br /&gt;
         &amp;quot;livereload&amp;quot;: false,&lt;br /&gt;
         &amp;quot;server&amp;quot;: &amp;quot;https://''example.com''/''custom-path''/&amp;quot;,&lt;br /&gt;
         &amp;quot;path&amp;quot;: &amp;quot;/''custom-path''/&amp;quot;,&lt;br /&gt;
         &amp;quot;rejectUnauthorized&amp;quot;: false,&lt;br /&gt;
         &amp;quot;verbose&amp;quot;: [],&lt;br /&gt;
         &amp;quot;prefixes&amp;quot;: [&lt;br /&gt;
             &amp;quot;build/&amp;quot;,&lt;br /&gt;
             &amp;quot;../../web/ui/build&amp;quot;&lt;br /&gt;
         ],&lt;br /&gt;
         &amp;quot;manifests&amp;quot;: [&lt;br /&gt;
             &amp;quot;build/manifests/&amp;quot;,&lt;br /&gt;
             &amp;quot;../../web/ui/build/manifests&amp;quot;&lt;br /&gt;
         ]&lt;br /&gt;
     },&lt;br /&gt;
     &amp;quot;debug&amp;quot;: true,&lt;br /&gt;
     &amp;quot;coreDir&amp;quot;: &amp;quot;../../web/ui/build&amp;quot;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The relevant settings are:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;proxy&amp;quot;: true&amp;lt;/tt&amp;gt;&lt;br /&gt;
: instructs Grunt to start an HTTP proxy on port 8080. Note that the value is outside the &amp;lt;tt&amp;gt;&amp;quot;appserver&amp;quot;&amp;lt;/tt&amp;gt; section.&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;protocol&amp;quot;: &amp;quot;https&amp;quot;&amp;lt;/tt&amp;gt;&lt;br /&gt;
: to use HTTPS on port 8337.&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;livereload&amp;quot;: false&amp;lt;/tt&amp;gt;&lt;br /&gt;
: because LiveReload can't be proxied.&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;path&amp;quot;: &amp;quot;/''custom-path''/&amp;quot;&amp;lt;/tt&amp;gt;&lt;br /&gt;
: must correspond to the path in the &amp;lt;tt&amp;gt;&amp;quot;server&amp;quot;&amp;lt;/tt&amp;gt; entry and will in most cases be &amp;lt;tt&amp;gt;&amp;quot;/appsuite/&amp;quot;&amp;lt;/tt&amp;gt;.&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;rejectUnauthorized&amp;quot;: false&amp;lt;/tt&amp;gt;&lt;br /&gt;
: is helpful with certificate problems, especially for integration tests of the login mechanism on a POC system with self-signed certificates.&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;index&amp;quot;: &amp;quot;/&amp;quot;&amp;lt;/tt&amp;gt;&lt;br /&gt;
: If the main path uses an HTTP redirect for a custom login page, then the initial request should not be intercepted by Grunt. Adding this entry in the &amp;lt;tt&amp;gt;&amp;quot;appserver&amp;quot;&amp;lt;/tt&amp;gt; section disables the interception. This has two side-effects:&lt;br /&gt;
:# The main HTML file is always served by the original server, and therefore&lt;br /&gt;
:# The timestamps used for cache-busting are not updated by Grunt.&lt;br /&gt;
:Unfortunately, this means clearing the cache will be required more often. If that becomes too much overhead, you can log in, then remove the option and restart Grunt.&lt;br /&gt;
&lt;br /&gt;
== Generating certificates ==&lt;br /&gt;
&lt;br /&gt;
To avoid unnecessary warnings in the browser, Grunt can use a set of certificates which are trusted by the browser. For local use, a self-created CA should be enough. By default, Grunt looks for the certificates in the subdirectory &amp;lt;tt&amp;gt;ssl&amp;lt;/tt&amp;gt;, so you can create them there with the following commands:&lt;br /&gt;
&lt;br /&gt;
 mkdir ssl&lt;br /&gt;
 cd ssl&lt;br /&gt;
 openssl req -x509 -newkey rsa:4096 -keyout rootCA.key -days 3650 -out rootCA.crt&lt;br /&gt;
 openssl req -newkey rsa:4096 -nodes -keyout host.key -out host.csr&lt;br /&gt;
&lt;br /&gt;
While most data entered for the certificates doesn't matter, the Common Name for the certificate signing request in the second &amp;lt;tt&amp;gt;openssl&amp;lt;/tt&amp;gt; command must match the domain of the &amp;lt;tt&amp;gt;&amp;quot;server&amp;quot;&amp;lt;/tt&amp;gt; setting in &amp;lt;tt&amp;gt;local.conf.json&amp;lt;/tt&amp;gt;. Alternatively, it can be a higher domain with a wildcard, e.g. &amp;lt;tt&amp;gt;*.example.com&amp;lt;/tt&amp;gt; for &amp;lt;tt&amp;gt;&amp;quot;server&amp;quot;: &amp;quot;https://my.example.com/appsuite/&amp;quot;&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 openssl x509 -req -in host.csr -CA rootCA.crt -CAkey rootCA.key \&lt;br /&gt;
         -CAcreateserial -out host.crt -days 365&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;host.csr&amp;lt;/tt&amp;gt; is only temporary and can be deleted now. Only the files &amp;lt;tt&amp;gt;rootCA.crt&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;host.key&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;host.crt&amp;lt;/tt&amp;gt; are used by Grunt. The &amp;lt;tt&amp;gt;rootCA.*&amp;lt;/tt&amp;gt; files can be reused to create multiple host certificates. The first &amp;lt;tt&amp;gt;openssl&amp;lt;/tt&amp;gt; command then only needs to be executed once. The &amp;lt;tt&amp;gt;host.*&amp;lt;/tt&amp;gt; files can also be reused as long as the domain(-wildcard) matches.&lt;br /&gt;
&lt;br /&gt;
The file &amp;lt;tt&amp;gt;rootCA.crt&amp;lt;/tt&amp;gt; needs to be imported into your browser as a trusted certificate authority (only once, if you are going to reuse the &amp;lt;tt&amp;gt;rootCA.*&amp;lt;/tt&amp;gt; files). For Firefox, look in Preferences &amp;amp;rarr; Advanced &amp;amp;rarr; Certificates &amp;amp;rarr; View Certificates &amp;amp;rarr; Authorities &amp;amp;rarr; Import...&lt;br /&gt;
For Chrome, look in Settings &amp;amp;rarr; Show advanced settings... &amp;amp;rarr; HTTPS/SSL &amp;amp;rarr; Manage certificates... &amp;amp;rarr; Authorities &amp;amp;rarr; Import...&lt;br /&gt;
Since the authority can sign certificates for any domain, and its key is not protected much, it's better to not use the browser(-profile) which trusts this authority for normal Internet browsing.&lt;br /&gt;
&lt;br /&gt;
== Launching the browser ==&lt;br /&gt;
&lt;br /&gt;
After starting the proxy with &amp;lt;tt&amp;gt;grunt dev&amp;lt;/tt&amp;gt;, you just need to use it when debugging your code in the browser. Most browsers have their own proxy settings, which override system-wide settings. They also respect environment variables like &amp;lt;tt&amp;gt;http_proxy&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 http_proxy=http://localhost:8080 firefox&lt;br /&gt;
&lt;br /&gt;
Chrome does not have its own settings (it starts the global settings dialog) and it ignores &amp;lt;tt&amp;gt;http_proxy&amp;lt;/tt&amp;gt;. Instead, you have to use a command line parameter:&lt;br /&gt;
&lt;br /&gt;
 chromium --proxy-server=http://localhost:8080&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Configuration&amp;diff=21446</id>
		<title>AppSuite:Configuration</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Configuration&amp;diff=21446"/>
		<updated>2016-02-11T05:24:59Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Configuration&amp;lt;/div&amp;gt;&lt;br /&gt;
Overview of server-side settings mainly hiding/disabling front end elements without affecting backend. Please mind that this listing isn't complete yet.&lt;br /&gt;
&lt;br /&gt;
==Folders: hiding folders in panel==&lt;br /&gt;
In order to configure this server-side, just create a new property file in '''/opt/open-xchange/etc/settings''' or append to the existing file '''/opt/open-xchange/etc/settings/appsuite.properties''' (mind the '''double-slash'''; this in not a typo!):&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-config&amp;quot;&amp;gt; &lt;br /&gt;
io.ox/core//folder/blacklist/&amp;lt;id&amp;gt;=true|false&lt;br /&gt;
&lt;br /&gt;
# example: hide global address book&lt;br /&gt;
io.ox/core//folder/blacklist/6=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Avoid white-space between the folder id and equal sign!'''&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
&lt;br /&gt;
==Mediaplayer: enabling/disabling ==&lt;br /&gt;
Please take a look at the [[ AppSuite:Mediaplayer#How_to_enable.2Fdisable_the_media_player | media player article ]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Custom_configurations&amp;diff=21445</id>
		<title>AppSuite:Custom configurations</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Custom_configurations&amp;diff=21445"/>
		<updated>2016-02-11T05:16:52Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Custom configurations&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Abstract:''' This article is a summary of special settings to customize the ui. This article is still in developement. It is not complete and may change.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
==Mail==&lt;br /&gt;
&lt;br /&gt;
These are special settings for the mail app.&lt;br /&gt;
&lt;br /&gt;
io.ox/mail/features/alwaysDeleteDraft: true/false&lt;br /&gt;
&lt;br /&gt;
This setting is used when drafts should always be deleted when sending them. This is useful if you don't want to manually delete your not needed drafts. Keep in mind that this will affect every draft, with this setting drafts that were used as templates will also be deleted when sending.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:External_libraries_for_the_UI&amp;diff=21444</id>
		<title>AppSuite:External libraries for the UI</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:External_libraries_for_the_UI&amp;diff=21444"/>
		<updated>2016-02-11T05:14:15Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-stable}}&lt;br /&gt;
&lt;br /&gt;
'''Abstract: ''' An overview over libraries that are used by the UI, either when running or when building it. The field &amp;quot;modified&amp;quot; is a hint that simply updating the library to a newer version might break some feature that we depend on. Feel free to explain the details of the change, too.&lt;br /&gt;
&lt;br /&gt;
== UI ==&lt;br /&gt;
Libraries that are used by the frontend, as delivered to the customer, are listed here: [http://www.open-xchange.com/licenses-ox-app-suite List of 3rd party libraries used in the UI]&lt;br /&gt;
&lt;br /&gt;
== Build system ==&lt;br /&gt;
{|&lt;br /&gt;
! Name &amp;amp; Link &lt;br /&gt;
! License &lt;br /&gt;
! Modified?&lt;br /&gt;
|-&lt;br /&gt;
| [http://nodejs.org Node.js] &amp;lt;em&amp;gt;not included&amp;lt;/em&amp;gt;&lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| [https://github.com/mde/jake Jake] &lt;br /&gt;
| Apache&lt;br /&gt;
| Yes&lt;br /&gt;
|-&lt;br /&gt;
| [https://github.com/mishoo/UglifyJS#readme UglifiyJS]&lt;br /&gt;
| BSD&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| [http://jshint.com/ JSHint]&lt;br /&gt;
| modified MIT license&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| [http://vowsjs.org/ Vows]&lt;br /&gt;
| BSD&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| [https://github.com/cloudhead/eyes.js eyes.js]&lt;br /&gt;
| BSD&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| [https://github.com/isaacs/rimraf rimraf]&lt;br /&gt;
| BSD&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| [http://lesscss.org/ LESS]&lt;br /&gt;
| Apache&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| [https://github.com/isaacs/sax-js sax-js]&lt;br /&gt;
| MIT&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| [https://github.com/Leonidas-from-XIV/node-xml2js node-xml2js]&lt;br /&gt;
| MIT&lt;br /&gt;
| No&lt;br /&gt;
|-&lt;br /&gt;
| [https://github.com/zzdhidden/node-jquery-deferred node-jquery-deferred]&lt;br /&gt;
| MIT&lt;br /&gt;
| No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]][[Category:UI]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Upsell&amp;diff=21443</id>
		<title>AppSuite:Upsell</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Upsell&amp;diff=21443"/>
		<updated>2016-02-10T13:58:23Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Upsell&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Abstract.''' This article is mainly for UI developers and introduces the concept of upsell from a technical point of view. In short: End-user has a set of so-called ''capabilities''. UI, however, offers functionality beyond that limited set for promotion purposes. Actions, e.g. inline links, that require missing capabilities trigger an '''in-app upsell'''. This process leads to a trial period or a new subscription. Technical challenge for the UI developer is to check what the end-user has, what can be shown beyond that, and how to handle upsell. It is also possible for hosting companies to easily integrate their own online shop into OX Upsell, since the internal mechanisms are ''loosely coupled'' via events.&lt;br /&gt;
__TOC__&lt;br /&gt;
==Enable upsell==&lt;br /&gt;
In order to configure upsell server-side, just create a new file '''upsell-appsuite.properties''' or append to existing '''appsuite.properties''' (mind the '''double-slash'''; this in not a typo!). If you configure upsell in the '''upsell-appsuite.properties''' the properties are loaded when you trigger the '''live reload''' function. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-settings&amp;quot;&amp;gt; &lt;br /&gt;
io.ox/core//upsell/enabled/infostore=true&lt;br /&gt;
io.ox/core//upsell/enabled/portal=true&lt;br /&gt;
io.ox/core//upsell/enabled/tasks=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each line enables a specific [[AppSuite:Upsell#Capabilities|capability]] for upsell. That means whenever a feature misses one these capabilities a special upsell-related event is triggered.&lt;br /&gt;
&lt;br /&gt;
Hint: For simple demo purposes, you can enable an internal upsell configuration by appending '''&amp;quot;&amp;amp;demo=upsell&amp;quot;''' to the URL. Needs to reload page, of course.&lt;br /&gt;
&lt;br /&gt;
==Custom upsell links==&lt;br /&gt;
Since other upsell triggers than the usual links would require custom development of the UI, the appsuite provides several upsell triggers which can be configured via settings. Those triggers will appear, when the expression of required capabilities is not satisfied and the required set of upsell triggers is satisfied. If you configure the upsell settings, the custom upsell triggers will be enabled by default but you can disable them if you want to. &lt;br /&gt;
&lt;br /&gt;
===Example===&lt;br /&gt;
&lt;br /&gt;
To clearify, when triggers are shown or not, we proceed with an example: A hoster can provide a custom upsell trigger in the secondary toolbar (next to the reload icon). This upsell trigger is inteded to sell a premium account to a user and has the default requirement of '''active_sync''' OR '''caldav''' OR '''carddav'''. That means, if one of those capabilities is not set for a user and the upsell is activated for '''active_sync''' AND '''caldav''' AND '''carddav''' the upsell trigger will be shown. You can enable upsell for those capabilities with&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-settings&amp;quot;&amp;gt;&lt;br /&gt;
io.ox/core//upsell/enabled/active_sync=true&lt;br /&gt;
io.ox/core//upsell/enabled/caldav=true&lt;br /&gt;
io.ox/core//upsell/enabled/carddav=true&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
inside an existing or new '''.properties''' file. Note that you have to restart the server so that the changes take place.&lt;br /&gt;
&lt;br /&gt;
If a user clicks on the upsell trigger, a upsell event of type 'custom' and with id 'secondary-toolbar' is triggered so that the page or dialog which will be opened can react depending on the clicked link. &lt;br /&gt;
&lt;br /&gt;
===Change appearance===&lt;br /&gt;
&lt;br /&gt;
Any custom upsell triggers which have icons will use a '''fa-star''' as default icon. You can change the default icon to any font-awesome icon (or a set of space separated icons) via&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-settings&amp;quot;&amp;gt;&lt;br /&gt;
io.ox/core//upsell/defaultIcon=&amp;quot;fa-star&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also change the icon of individual custom triggers with&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-settings&amp;quot;&amp;gt;&lt;br /&gt;
io.ox/core//features/upsell/$id/icon=&amp;quot;fa-star&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
where $id is the id of the upsell trigger.&lt;br /&gt;
&lt;br /&gt;
The color of custom upsell links can be changed with &lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-settings&amp;quot;&amp;gt;&lt;br /&gt;
io.ox/core//features/upsell/$id/color=&amp;quot;#f00&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
where $id is the id of the upsell trigger and the color string can be any css color string.&lt;br /&gt;
&lt;br /&gt;
If you want to disable a custom upsell trigger, then you can add &lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-settings&amp;quot;&amp;gt;&lt;br /&gt;
io.ox/core//features/upsell/$id/enabled=false&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
to a '''.properties''' file. &lt;br /&gt;
&lt;br /&gt;
===Customize strings===&lt;br /&gt;
&lt;br /&gt;
Some of the custom upsell triggers use have a title (or other strings) which a hoster could customize. It is important, that several translations are provided. You can provide your own texts via &lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-settings&amp;quot;&amp;gt;&lt;br /&gt;
io.ox/core//features/upsell/$id/i18n/$lang/title=&amp;quot;A custom title&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
where $lang is the current language identifier (e.g. &amp;quot;en_US&amp;quot;). You can see the current language identifier when you open the webconsole and type &lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-settings&amp;quot;&amp;gt;&lt;br /&gt;
ox.language&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Customize required capabilities===&lt;br /&gt;
If you want certain upsell triggers to appear on different capabilities, you can configure this inside a .properties file. Therefore, you have to configure the '''requires''' field of the appropriate trigger. This field expects a logical expression of capabilities. The following example requires eas and caldav or not carddav. If the actual capabilities does not satisfy the expression and the upsell capabilites satisfy this expression, the upsell trigger will be drawn. &lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-settings&amp;quot;&amp;gt;&lt;br /&gt;
io.ox/core//features/upsell/$id/requires=&amp;quot;active_sync &amp;amp;&amp;amp; (caldav || !carddav)&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===List of custom triggers===&lt;br /&gt;
&lt;br /&gt;
This sections lists the custom triggers and how they can be configured.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! ID !! Config !! Texts !! Default capabilities !! Description&lt;br /&gt;
|-&lt;br /&gt;
| secondary-launcher&lt;br /&gt;
| icon, color&lt;br /&gt;
| title&lt;br /&gt;
| active_sync or caldav or carddav&lt;br /&gt;
| This trigger is located in the secondary toolbar left of the notifications icon. It can contain an icon and text and can be colored.  It is intended as '''upgrade to premium''' trigger. This trigger is not shown on mobile devices due to space limitations. &lt;br /&gt;
|-&lt;br /&gt;
| folderview/mail&lt;br /&gt;
| icon, color&lt;br /&gt;
| title&lt;br /&gt;
| active_sync&lt;br /&gt;
| This trigger is located below the folderview of mails. &lt;br /&gt;
|-&lt;br /&gt;
| folderview/mail/bottom&lt;br /&gt;
| color&lt;br /&gt;
| title&lt;br /&gt;
| active_sync&lt;br /&gt;
| This trigger is located at the bottom of the folderview of the mail app in the premium area. This trigger is styled as a button with the default text 'Try now' and has no icon by default. &lt;br /&gt;
|-&lt;br /&gt;
| folderview/contacts&lt;br /&gt;
| icon, color&lt;br /&gt;
| title&lt;br /&gt;
| carddav&lt;br /&gt;
| This trigger is located below the folderview of the address book. &lt;br /&gt;
|-&lt;br /&gt;
| folderview/contacts/bottom&lt;br /&gt;
| color&lt;br /&gt;
| title&lt;br /&gt;
| carddav&lt;br /&gt;
| This trigger is located at the bottom of the folderview of the contacts app in the premium area. This trigger is styled as a button with the default text 'Try now' and has no icon by default. &lt;br /&gt;
|-&lt;br /&gt;
| folderview/calendar&lt;br /&gt;
| icon, color&lt;br /&gt;
| title&lt;br /&gt;
| caldav&lt;br /&gt;
| This trigger is located below the folderview of the calendar. &lt;br /&gt;
|-&lt;br /&gt;
| folderview/calendar/bottom&lt;br /&gt;
| color&lt;br /&gt;
| title&lt;br /&gt;
| caldav&lt;br /&gt;
| This trigger is located at the bottom of the folderview of the calendar app in the premium area. This trigger is styled as a button with the default text 'Try now' and has no icon by default. &lt;br /&gt;
|-&lt;br /&gt;
| folderview/infostore/bottom&lt;br /&gt;
| color&lt;br /&gt;
| title&lt;br /&gt;
| boxcom or google or msliveconnect&lt;br /&gt;
| This trigger is located at the bottom of the folderview of the drive app in the premium area. This trigger is styled as a button with the default text 'Try now' and has no icon by default. &lt;br /&gt;
|-&lt;br /&gt;
| topbar-dropdown&lt;br /&gt;
| icon, color&lt;br /&gt;
| title&lt;br /&gt;
| active_sync or caldav or carddav&lt;br /&gt;
| This trigger is located on the first position of the dropdown in the secondary toolbar. It contains a text and an icon. &lt;br /&gt;
|-&lt;br /&gt;
| portal-widget&lt;br /&gt;
| imageURL, removable (boolean), icon&lt;br /&gt;
| title&lt;br /&gt;
| active_sync or caldav or carddav&lt;br /&gt;
| This trigger adds a draggable portal widget to the appsuite portal. This widget is not removable by default and displays a default text. A customer can add a backgroundimage with '''imageURL''' and can make this widget removable by setting '''removable''' to true. If no image is used, the widget displays the title in the center with a customizable space separated list of font-awesome icons. &lt;br /&gt;
|-&lt;br /&gt;
| mail-folderview-quota&lt;br /&gt;
| upsellLimit, icon, color&lt;br /&gt;
| title&lt;br /&gt;
| active_sync or caldav or carddav&lt;br /&gt;
| This trigger is appended below the mail quota in the folderview. You can set the '''upsellLimit''' (in Bytes). If the maximum mail quota is larger than '''upsellLimit''', the upsell button will not be shown. This upsell trigger has no icon by default. &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Upsell Wizard==&lt;br /&gt;
&amp;lt;div style=&amp;quot;color: #3A87AD; text-align: right; margin: -1em 0 1em 0; padding: 0.5em; background-color: #D9EDF7;&amp;quot;&amp;gt;Shipped with 7.2.1&amp;lt;/div&amp;gt;&lt;br /&gt;
Customers usually want to offer context-sensitive content in an IFRAME if the upsell is triggered. Therefore, App Suite comes with an integrated but optional plugin that takes care of this. Just enable ''plugins/upsell/simple-wizard'' by [[AppSuite:Capabilities#Set_a_capability|setting the capability]] '''simple-wizard''' server-side (or by adding it to the URL '''...&amp;amp;cap=simple-wizard''' for testing/development purposes).&lt;br /&gt;
&lt;br /&gt;
This plugin registers for the event ''&amp;quot;upsell:requires-upsell&amp;quot;'', opens a modal popup, and loads a custom URL in an embedded IFRAME.&lt;br /&gt;
&lt;br /&gt;
[[File:Simple_upsell_wizard.png|800 px|Custom content in an IFRAME]]&lt;br /&gt;
&lt;br /&gt;
===Wizard settings===&lt;br /&gt;
In order to configure this server-side, just create a new file '''upsell.properties''' or append to existing '''appsuite.properties''' (mind the '''double-slash'''; this in not a typo! plus: changing such settings requires a backend restart):&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-settings&amp;quot;&amp;gt;&lt;br /&gt;
plugins/upsell/simple-wizard//url=blank.html?user=$user,user_id=$user_id,context_id=$context_id&lt;br /&gt;
plugins/upsell/simple-wizard//overlayOpacity=0.5&lt;br /&gt;
plugins/upsell/simple-wizard//overlayColor=black&lt;br /&gt;
plugins/upsell/simple-wizard//zeroPadding=true&lt;br /&gt;
plugins/upsell/simple-wizard//width=750&lt;br /&gt;
plugins/upsell/simple-wizard//height=390&lt;br /&gt;
plugins/upsell/simple-wizard//closeButton=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Settings !! Description&lt;br /&gt;
|-&lt;br /&gt;
| url&lt;br /&gt;
| Custom URL that is loaded in IFRAME; can contain special [[AppSuite:Upsell#Custom_URL_variables|variables]].&lt;br /&gt;
|-&lt;br /&gt;
| overlayOpacity &lt;br /&gt;
| CSS opacity value for overlay; default is 0.5&lt;br /&gt;
|-&lt;br /&gt;
| overlayColor&lt;br /&gt;
| CSS background color for overlay; default is black&lt;br /&gt;
|-&lt;br /&gt;
| zeroPadding&lt;br /&gt;
| If true (default) there is no inner padding inside modal dialog, i.e. the IFRAME covers the popup&lt;br /&gt;
|-&lt;br /&gt;
| width&lt;br /&gt;
| Width of outer popup (not IFRAME) in pixel&lt;br /&gt;
|-&lt;br /&gt;
| height&lt;br /&gt;
| Height of IFRAME in pixel&lt;br /&gt;
|-&lt;br /&gt;
| closeButton&lt;br /&gt;
| If true (default) the wizard shows its own close button&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Custom URL variables===&lt;br /&gt;
The plugin offers a set of variables that help providing context-sensitive content. ''$missing'' is probably the most prominent one. Other variables help identifying the user. An example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-setting&amp;quot;&amp;gt;&lt;br /&gt;
upsell.php?user_id=$user_id&amp;amp;context_id=$context_id&amp;amp;language=$language&amp;amp;missing=$missing&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Variable !! Description&lt;br /&gt;
|-&lt;br /&gt;
| $context_id&lt;br /&gt;
| context_id of current user&lt;br /&gt;
|-&lt;br /&gt;
| $hostname&lt;br /&gt;
| hostname of current session, e.g. www.one-of-countless-virtual-hosts.com&lt;br /&gt;
|-&lt;br /&gt;
| $id&lt;br /&gt;
| The trigger's identifier, e.g. &amp;quot;io.ox/files&amp;quot;. Can refer to an app, an inline action, or a portal plugin. See $type&lt;br /&gt;
|-&lt;br /&gt;
| $imap_login&lt;br /&gt;
| The current user's imap login&lt;br /&gt;
|-&lt;br /&gt;
| $language&lt;br /&gt;
| The current user's language, e.g. de_DE or en_US&lt;br /&gt;
|-&lt;br /&gt;
| $mail&lt;br /&gt;
| The current user's primary email address&lt;br /&gt;
|-&lt;br /&gt;
| $missing&lt;br /&gt;
| The set of missing capabilities, comma separated, e.g. &amp;quot;files&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| $session&lt;br /&gt;
| The current user's session id&lt;br /&gt;
|-&lt;br /&gt;
| $type&lt;br /&gt;
| Either app, inline-action, or portal-widget. Describes what triggered the upsell. See $id&lt;br /&gt;
|-&lt;br /&gt;
| $user&lt;br /&gt;
| The current user's login name (can include context name, i.e somebody@foo)&lt;br /&gt;
|-&lt;br /&gt;
| $user_id&lt;br /&gt;
| The current user's numeric id&lt;br /&gt;
|-&lt;br /&gt;
| $user_login&lt;br /&gt;
| The current user's login (usually without context name)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Develop and debug===&lt;br /&gt;
While experimenting or developing, you can use the following helpful functions:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
// get plugin (this must be properly loaded, otherwise you get a runtime error)&lt;br /&gt;
var wizard = require('plugins/upsell/simple-wizard/register');&lt;br /&gt;
&lt;br /&gt;
// if you have no chance to enabled this plugin server-side, use the following approach &lt;br /&gt;
   // but don't use this in production plugins, it's just a hack for console: &lt;br /&gt;
   // if you don't know the difference please take a look at &lt;br /&gt;
   // http://requirejs.org/docs/errors.html#notloaded &lt;br /&gt;
var wizard; require(['plugins/upsell/simple-wizard/register'], function (w) { wizard = w; });&lt;br /&gt;
&lt;br /&gt;
// get variables (optional: options from upsell:require-upgrade event)&lt;br /&gt;
wizard.getVariables({ type: 'app', id: 'io.ox/files', missing: 'files' });&lt;br /&gt;
&lt;br /&gt;
// get URL (optional: options from upsell:require-upgrade event)&lt;br /&gt;
   // replaces placeholders ($foo) by variable values&lt;br /&gt;
wizard.getURL({ type: 'app', id: 'io.ox/files', missing: 'files' });&lt;br /&gt;
&lt;br /&gt;
// get all settings (can be changed on the fly)&lt;br /&gt;
console.log(wizard.settings);&lt;br /&gt;
&lt;br /&gt;
// global upsell events; parameters: (e, popup)&lt;br /&gt;
ox.on('upsell:simple-wizard:show:before', _.inspect);&lt;br /&gt;
ox.on('upsell:simple-wizard:show', _.inspect);&lt;br /&gt;
ox.on('upsell:simple-wizard:close', _.inspect);&lt;br /&gt;
&lt;br /&gt;
// special event to customize settings (e, variables, settings)&lt;br /&gt;
   // triggered before creating dialog instance;&lt;br /&gt;
   // 2nd parameter has variables like type, missing, user_id etc.&lt;br /&gt;
   // 3rd parameter refers to a local copy of wizard settings&lt;br /&gt;
ox.on('upsell:simple-wizard:init', _.inspect);&lt;br /&gt;
&lt;br /&gt;
// open wizard manually&lt;br /&gt;
wizard.open();&lt;br /&gt;
&lt;br /&gt;
// close wizard manually&lt;br /&gt;
wizard.close();&lt;br /&gt;
&lt;br /&gt;
// disable wizard (unregisters upsell event)&lt;br /&gt;
wizard.disable();&lt;br /&gt;
&lt;br /&gt;
// and of course: enable wizard (registers for upsell event)&lt;br /&gt;
wizard.enable();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Some examples for customizations in UI plugins or in console:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
// get plugin (this muse be properly loaded, otherwise you get a runtime error)&lt;br /&gt;
var wizard = require('plugins/upsell/simple-wizard/register'); &lt;br /&gt;
&lt;br /&gt;
// extend IFRAME constructor (see http://underscorejs.org/#compose)&lt;br /&gt;
var custom = function (iframe) {&lt;br /&gt;
  return iframe.css({ border: '5px solid #08c', boxSizing: 'border-box' });&lt;br /&gt;
};&lt;br /&gt;
wizard.getIFrame = _.compose(custom, wizard.getIFrame);&lt;br /&gt;
&lt;br /&gt;
// use an event to customize the IFRAME&lt;br /&gt;
ox.on('upsell:simple-wizard:show:before', function (e, popup) {&lt;br /&gt;
  popup.getContentNode().find('iframe')&lt;br /&gt;
    .css({ border: '5px solid #08c', boxSizing: 'border-box' });&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Close wizard===&lt;br /&gt;
The upsell wizard can easily be closed via javascript or by redirecting the IFRAME to a prepared HTML page. In order to see this in action, run the following code (step by step):&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
// get plugin (this must be properly loaded, otherwise you get a runtime error)&lt;br /&gt;
var wizard = require('plugins/upsell/simple-wizard/register');&lt;br /&gt;
&lt;br /&gt;
// open wizard&lt;br /&gt;
wizard.open();&lt;br /&gt;
&lt;br /&gt;
// redirect now&lt;br /&gt;
wizard.setSrc('apps/plugins/upsell/simple-wizard/close.html');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Custom backend systems that run on a different domain cannot use javascript to close the wizard [http://en.wikipedia.org/wiki/Cross-site_scripting]. However, such systems can redirect to ''close.html''. Since this page is part of the UI and therefore located on the same domain, it is allowed to call the wizard's close function.&lt;br /&gt;
&lt;br /&gt;
==Custom development==&lt;br /&gt;
This section documents some of the inner workings of the upsell layer. It should provide some useful insights and hopefully helps at implementing custom upsell solutions.&lt;br /&gt;
===Events===&lt;br /&gt;
Whenever the user starts an app or clicks on an inline-action, a capability-check is performed. For example, all inline actions have native support for such checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
new Action('io.ox/calendar/detail/actions/sendmail', {&lt;br /&gt;
    // this action requires the capability &amp;quot;webmail&amp;quot;&lt;br /&gt;
    capabilities: 'webmail',&lt;br /&gt;
    action: function (baton) {&lt;br /&gt;
        // send mail&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the end-user does not have &amp;quot;webmail&amp;quot; (e.g. in a files-only setup) but calls this action, a proper event is fired:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
// if any action misses a capability&lt;br /&gt;
ox.trigger('upsell:requires-upgrade');&lt;br /&gt;
// which provides the following data for apps:&lt;br /&gt;
{&lt;br /&gt;
  type: &amp;quot;app&amp;quot;, // type of the upsell trigger&lt;br /&gt;
  id: &amp;quot;io.ox/mail/main&amp;quot;, // upsell trigger ID&lt;br /&gt;
  missing: &amp;quot;webmail&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
// and for inline-actions:&lt;br /&gt;
{&lt;br /&gt;
  type: &amp;quot;inline-action&amp;quot;,&lt;br /&gt;
  id: &amp;quot;io.ox/calendar/detail/actions/sendmail&amp;quot;,&lt;br /&gt;
  missing: &amp;quot;webmail&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Capabilities and Upsell triggers===&lt;br /&gt;
There are lots of different capabilities. They are defined on the server-side and basically they are just strings. Let's keep it simple and understand them as either services (e.g. mobility), specific functionalities (e.g. multiple_mail_accounts) or applications (e.g. calendar). Some obvious examples:&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Capability !! Description !! Upsell trigger (if capability is missing)&lt;br /&gt;
|-&lt;br /&gt;
| calendar&lt;br /&gt;
| User has &amp;quot;Calendar&amp;quot; app&lt;br /&gt;
|&lt;br /&gt;
* Mail/All recipients: Invite to appointment&lt;br /&gt;
* Add portal widget&lt;br /&gt;
* Top bar&lt;br /&gt;
|-&lt;br /&gt;
| contacts &lt;br /&gt;
| User has &amp;quot;Address Book&amp;quot; app&lt;br /&gt;
|&lt;br /&gt;
* Mail/App recipients: Save as distribution list&lt;br /&gt;
* Calendar: Save participants as distribution list&lt;br /&gt;
* Top bar&lt;br /&gt;
|-&lt;br /&gt;
| infostore&lt;br /&gt;
| User has &amp;quot;Files&amp;quot; app&lt;br /&gt;
|&lt;br /&gt;
* Mail: Save in infostore&lt;br /&gt;
* Add portal widget (My latest files, Recently changed files)&lt;br /&gt;
* Top bar&lt;br /&gt;
|-&lt;br /&gt;
| portal&lt;br /&gt;
| User has &amp;quot;Portal&amp;quot; app&lt;br /&gt;
|&lt;br /&gt;
* Mail: Add to portal&lt;br /&gt;
* Contacts: Add to portal&lt;br /&gt;
* Files: Add to portal&lt;br /&gt;
* Top bar&lt;br /&gt;
|-&lt;br /&gt;
| tasks&lt;br /&gt;
| User has &amp;quot;Tasks&amp;quot; app&lt;br /&gt;
|&lt;br /&gt;
* Mail: Remind me&lt;br /&gt;
* Add portal widget&lt;br /&gt;
* Top bar&lt;br /&gt;
|-&lt;br /&gt;
| webmail&lt;br /&gt;
| User has &amp;quot;Mail&amp;quot; app&lt;br /&gt;
|&lt;br /&gt;
* Calendar: Send mail to all participants&lt;br /&gt;
* Contacts: Send mail&lt;br /&gt;
* Contacts: Send vCard&lt;br /&gt;
* Files: Send as link&lt;br /&gt;
* Files: Send by mail&lt;br /&gt;
* Add portal widget&lt;br /&gt;
* Top bar&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
// list all available capabilities&lt;br /&gt;
_(ox.serverConfig.capabilities).pluck('id').sort();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An example: Free-mail users might just have '''webmail''' and '''contacts'''. If '''infostore''' is enabled for upsell, end-users will see the link to store mail attachments. But since this capability is missing, the event &amp;quot;upsell:requires-upgrade&amp;quot; is triggered which starts the upsell process. Upon successful completion this process should unlock the capability '''infostore''' for the end-user.&lt;br /&gt;
&lt;br /&gt;
The advantage of using rather atomic capabilities as the foundation for upsell is that developers don't have to consider and implement sales programs or marketing matrices in UI code.&lt;br /&gt;
&lt;br /&gt;
===Example dialog===&lt;br /&gt;
Whenever the event ''&amp;quot;upsell:requires-upgrade&amp;quot;'' is triggered there should be some response for the end-user. Usually an upsell dialog should open. This can be implemented as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
function showUpgradeDialog(e, options) {&lt;br /&gt;
    require(['io.ox/core/tk/dialogs'], function (dialogs) {&lt;br /&gt;
        new dialogs.ModalDialog({ easyOut: true })&lt;br /&gt;
            .build(function () {&lt;br /&gt;
                this.getHeader().append(&lt;br /&gt;
                    $('&amp;lt;h4&amp;gt;').text('Upgrade required')&lt;br /&gt;
                );&lt;br /&gt;
                this.getContentNode().append(&lt;br /&gt;
                    $.txt('This feature is not available.'),&lt;br /&gt;
                    $.txt('You need to upgrade your account now.'),&lt;br /&gt;
                    $.txt(' '),&lt;br /&gt;
                    $.txt('The first 90 days are free.')&lt;br /&gt;
                );&lt;br /&gt;
                this.addPrimaryButton('upgrade', 'Get free upgrade');&lt;br /&gt;
                this.addButton('cancel', 'Cancel');&lt;br /&gt;
            })&lt;br /&gt;
            .setUnderlayStyle({&lt;br /&gt;
                opacity: 0.70,&lt;br /&gt;
                backgroundColor: '#08C'&lt;br /&gt;
            })&lt;br /&gt;
            .on('upgrade', function () {&lt;br /&gt;
                ox.trigger('upsell:upgrade', options);&lt;br /&gt;
            })&lt;br /&gt;
            .on('show', function () {&lt;br /&gt;
                ox.off('upsell:requires-upgrade', showUpgradeDialog);&lt;br /&gt;
            })&lt;br /&gt;
            .on('close', function () {&lt;br /&gt;
                ox.on('upsell:requires-upgrade', showUpgradeDialog);&lt;br /&gt;
            })&lt;br /&gt;
            .show();&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function upgrade(e, options) {&lt;br /&gt;
    console.debug('upgrade', options);&lt;br /&gt;
    alert('User decided to upgrade! (global event: upsell:upgrade)');&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ox.on('upsell:requires-upgrade', showUpgradeDialog);&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * convention: 'upsell:upgrade' is used to trigger final upsell&lt;br /&gt;
 * the current user and user_id can be found in global variables ox.user and ox.user_id&lt;br /&gt;
 */&lt;br /&gt;
ox.on('upsell:upgrade', upgrade);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The second event '''&amp;quot;upsell:upgrade&amp;quot;''' can be understood as the final imperative to request the upsell server-side.&lt;br /&gt;
&lt;br /&gt;
===Example portal widget===&lt;br /&gt;
Besides waiting for the user to click on such links, it's always a good idea to offer explicit controls to trigger an upsell. One option is creating a portal widget that advertises a premium subscription:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
/**&lt;br /&gt;
 * This work is provided under the terms of the CREATIVE COMMONS PUBLIC&lt;br /&gt;
 * LICENSE. This work is protected by copyright and/or other applicable&lt;br /&gt;
 * law. Any use of the work other than as authorized under this license&lt;br /&gt;
 * or copyright law is prohibited.&lt;br /&gt;
 *&lt;br /&gt;
 * http://creativecommons.org/licenses/by-nc-sa/2.5/&lt;br /&gt;
 * © 2013 Open-Xchange Inc., Tarrytown, NY, USA. info@open-xchange.com&lt;br /&gt;
 *&lt;br /&gt;
 * @author Matthias Biggeleben &amp;lt;matthias.biggeleben@open-xchange.com&amp;gt;&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
define('plugins/portal/upsell/register',&lt;br /&gt;
    ['io.ox/core/extensions',&lt;br /&gt;
     'io.ox/files/api',&lt;br /&gt;
     'gettext!plugins/portal'], function (ext, api, gt) {&lt;br /&gt;
&lt;br /&gt;
    'use strict';&lt;br /&gt;
&lt;br /&gt;
    var title = gt('Upgrade to premium');&lt;br /&gt;
&lt;br /&gt;
    ext.point('io.ox/portal/widget/upsell').extend({&lt;br /&gt;
&lt;br /&gt;
        title: title,&lt;br /&gt;
&lt;br /&gt;
        preview: function (baton) {&lt;br /&gt;
&lt;br /&gt;
            this.addClass('hide-title').append(&lt;br /&gt;
                $('&amp;amp;lt;div class=&amp;quot;content centered&amp;quot; style=&amp;quot;cursor: pointer; padding-top: 3em;&amp;quot;&amp;gt;').append(&lt;br /&gt;
                    $('&amp;amp;lt;h2&amp;gt;').append(&lt;br /&gt;
                        $.txt(title + ' '),&lt;br /&gt;
                        $('&amp;amp;lt;i class=&amp;quot;icon-star&amp;quot;&amp;gt;')&lt;br /&gt;
                    ),&lt;br /&gt;
                    $('&amp;amp;lt;div&amp;gt;').text(gt('Click here for free trial.'))&lt;br /&gt;
                )&lt;br /&gt;
                .on('click', function () {&lt;br /&gt;
                    ox.trigger('upsell:upgrade', {&lt;br /&gt;
                        type: 'widget',&lt;br /&gt;
                        id: 'io.ox/portal/widget/upsell',&lt;br /&gt;
                        missing: ''&lt;br /&gt;
                    });&lt;br /&gt;
                })&lt;br /&gt;
            );&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Accessing upsell settings===&lt;br /&gt;
The upsell configuration is located in the namespace ''&amp;quot;io.ox/core&amp;quot;'', the path is ''&amp;quot;upsell/enabled&amp;quot;''. Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
// get all capabilities that can trigger upsell&lt;br /&gt;
require('settings!io.ox/core').get('upsell/enabled');&lt;br /&gt;
&lt;br /&gt;
// contains data like this&lt;br /&gt;
{&lt;br /&gt;
  infostore: true,&lt;br /&gt;
  portal: true,&lt;br /&gt;
  tasks: true&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If upsell is '''not''' enabled and the end-user lacks specific capabilities, the app or the inline-action is not shown. If upsell is enabled by the upper configuration, inline-actions are shown and trigger the upsell event ''&amp;quot;upsell:requires-upgrade&amp;quot;'' if clicked (but do not execute the action itself).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
/* &lt;br /&gt;
 * if you want to create your own controls, you can use the following helpers &lt;br /&gt;
 */&lt;br /&gt;
var upsell = require('io.ox/core/upsell');&lt;br /&gt;
&lt;br /&gt;
// check capabilities (space-separated) &lt;br /&gt;
upsell.has('portal webmail');&lt;br /&gt;
&lt;br /&gt;
// get missing capabilities (would return &amp;quot;calendar&amp;quot; in demo mode) &lt;br /&gt;
upsell.missing(['portal webmail', 'contacts', 'calendar']);&lt;br /&gt;
&lt;br /&gt;
/* checks if upsell is enabled for a set of capabilities &lt;br /&gt;
 * true if at least one set matches &lt;br /&gt;
 */&lt;br /&gt;
upsell.enabled(['portal webmail', 'webmail calendar']);&lt;br /&gt;
&lt;br /&gt;
/* convenience function: &amp;quot;visible&amp;quot; &lt;br /&gt;
 * checks if something should be visible depending on required capabilities &lt;br /&gt;
 * true if any item matches requires capabilities &lt;br /&gt;
 * true if any item does not match its requirements but is enabled for upsell &lt;br /&gt;
 * this function is used for any inline link, for example, to decide whether or not showing it &lt;br /&gt;
 */&lt;br /&gt;
upsell.visible(['portal webmail', 'contacts', 'calendar']);&lt;br /&gt;
&lt;br /&gt;
// likewise if neither capability set nor enabled for upsell, we get a false &lt;br /&gt;
upsell.visible(['foo']);&lt;br /&gt;
&lt;br /&gt;
// in case something weird happens (usually bad configuration) debug() helps&lt;br /&gt;
upsell.debug();&lt;br /&gt;
&lt;br /&gt;
// and this one&lt;br /&gt;
_(ox.serverConfig.capabilities).pluck('id').sort();&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Upsell in OX6==&lt;br /&gt;
Please also consider [[Upsell|this article]] as it also covers backend aspects.&lt;br /&gt;
&lt;br /&gt;
[[Category:Upsell]]&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Upsell_tools&amp;diff=21442</id>
		<title>AppSuite:Upsell tools</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Upsell_tools&amp;diff=21442"/>
		<updated>2016-02-10T13:51:27Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Synopsis:''' This article introduces several ways of promoting applications and other upgrades for App Suite. Sine Upsell is usually very customer specific, we do only provide the framework.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== The Upsell Widget ==&lt;br /&gt;
The Upsell widget is a widget displayed on the portal. It can show images, text or combinations thereof. A widget can contain several &amp;quot;slides&amp;quot;. A click on the widget starts the Upsell Wizard. There can be more than one Upsell Widget (just remember not to annoy your customer with too many!). Upsell widgets can be moved, but not removed from the portal.&lt;br /&gt;
&lt;br /&gt;
The Upsell Widget needs two different configurations:&lt;br /&gt;
  io.ox/portal//widgets/protected:&lt;br /&gt;
    upsellads_0:&lt;br /&gt;
      plugin: &amp;quot;plugins/portal/upsellads/register&amp;quot;&lt;br /&gt;
      type: &amp;quot;upsellads&amp;quot;&lt;br /&gt;
      index: 0&lt;br /&gt;
      changeable:&lt;br /&gt;
        index: true&lt;br /&gt;
      props:&lt;br /&gt;
        ad: &amp;quot;openexchangeAdvertisement&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This part defines a widget as protected. This is not upsell-specific, it is [[AppSuite:Configuring_portal_plugins|an option for every kind of portal widget]]. The upsell-specific part is the value of &amp;quot;props/ad&amp;quot;, which identifies the content via the name &amp;quot;openexchangeAdvertisement&amp;quot;. That name points to another part of the YAML file that looks like this:&lt;br /&gt;
&lt;br /&gt;
  plugins/upsell//ads:&lt;br /&gt;
    delayInMilliseconds: 10000&lt;br /&gt;
    openexchangeAdvertisement:&lt;br /&gt;
      upsellWizard: &amp;quot;shop&amp;quot;&lt;br /&gt;
      slides:&lt;br /&gt;
        en_US:&lt;br /&gt;
          slide1:&lt;br /&gt;
            type: text-bottom&lt;br /&gt;
            image: 'https://image1'&lt;br /&gt;
            text: 'Awesome stuff'&lt;br /&gt;
          slide2:&lt;br /&gt;
            type: text-top&lt;br /&gt;
            image: 'https:image2'&lt;br /&gt;
            text: 'More awesome stuff'&lt;br /&gt;
&lt;br /&gt;
What you can see here is that an advertisement consists of several slides. Due to some peculiarities of the App Suite YAML parser, you have to name them &amp;quot;slide?&amp;quot; with a number and cannot use an array. The slides will be sorted alphabetically, so if you plan to use more than 10 slides, remember to pad the number with enough zeros, the first slide being 00, the second being 01.&lt;br /&gt;
&lt;br /&gt;
Slides can be provided in different languages. This example provides slides only for en_US. The slides need to match the user language exactly (sorry, no smart guessing so that British users with en_UK get the en_US version). The system defaults to en_US when no appropriate language can be found.&lt;br /&gt;
&lt;br /&gt;
The delayInMilliseconds represents the transition time from one slide to the next.&lt;br /&gt;
&lt;br /&gt;
The text can contain HTML and is inserted via the innerHtml method of JQuery in case you feel the need for markup.&lt;br /&gt;
&lt;br /&gt;
The value of &amp;quot;image&amp;quot; is put into the src attribute of an &amp;amp;lt;img/;gt; element, so you can use a local path as well as a URL.&lt;br /&gt;
&lt;br /&gt;
The type can be one of text-top, text-bottom, text-only and image-only. Text usually takes up a third of an ad that also contains an image. Text and image are cut off in case they exceed the space.&lt;br /&gt;
&lt;br /&gt;
== The Upsell Bubbles ==&lt;br /&gt;
Upsell bubbles are little popups based on hopscotch, similar to the [[AppSuite:Guided tours|guided tours]]. They show up after a given amount of time and point to a defined UI element to display some text. Clicking on them starts the Upsell Wizard (unless you click &amp;quot;cancel&amp;quot;, of course).&lt;br /&gt;
&lt;br /&gt;
  plugins/upsell//bubbles:&lt;br /&gt;
    skipFirstLogin: true&lt;br /&gt;
    repeatInMilliseconds: 900000&lt;br /&gt;
    repeatPerLogins: 1&lt;br /&gt;
    bubbles:&lt;br /&gt;
      en_US:&lt;br /&gt;
        bubble1:&lt;br /&gt;
          app: 'io.ox/portal'&lt;br /&gt;
          content: &amp;quot;Did you know...?&amp;quot;&lt;br /&gt;
          startDate: '2013-07-01'&lt;br /&gt;
          endDate: '2019-06-31'&lt;br /&gt;
&lt;br /&gt;
Upsell bubbles appear after the amount of time in ''repeatInMilliseconds'' has passed. They can be set up not to bother the first time user (''skipFirstLogin'') and only to show up ''repeatPerLogins''-times every login.&lt;br /&gt;
&lt;br /&gt;
As with Upsell Widgets, they can be internationalized, this example only contains a version for American English (en_US). The name must match the user locality exactly. You can have several bubbles.&lt;br /&gt;
&lt;br /&gt;
Each bubble can be set up to be valid only during a certain time span (or from a date, or to a date), but this can be omitted. Each bubble needs to point to one application.&lt;br /&gt;
&lt;br /&gt;
== The Upsell Wizard ==&lt;br /&gt;
The Upsell Wizard is a small shopping cart application that displays things that can be sold. They process is the usual three-step process of putting items in your cart, reviewing them and ordering them. Upon completion the Wizard calls an URL with the ordered items as well as the shoppers's ID and context number. It is left to the provider to implement some handler for that.&lt;br /&gt;
&lt;br /&gt;
  plugins/upsell//shop:&lt;br /&gt;
    priceFormat: '$%sUSD'&lt;br /&gt;
    target: 'http://localhost/order-confirmation?cartContents=OXUPSELLCART&amp;amp;context=OXUPSELLCONTEXT&amp;amp;user=OXUPSELLUSER'&lt;br /&gt;
    disclaimer:&lt;br /&gt;
      en_US: 'You're going to sell your soul to us'&lt;br /&gt;
  products:&lt;br /&gt;
    en_US:&lt;br /&gt;
      p0:&lt;br /&gt;
        image: 'https://product-image1'&lt;br /&gt;
        title: 'All - special offer'&lt;br /&gt;
        price: 99&lt;br /&gt;
        description: 'This Special Offer is only for a limited time. '&lt;br /&gt;
&lt;br /&gt;
The price format is given as format string to provide maximum flexibility. ''%s'' represents the amount.&lt;br /&gt;
&lt;br /&gt;
The target is the URL that is called after shopping is completed. The values OXUPSELLCART, OXUPSELLCONTEXT and OXUPSELLUSER are replaced by the Wizard with a comma-separated list of the ids of items bought, the buyer's context ID and their user ID.&lt;br /&gt;
&lt;br /&gt;
The disclaimer can be internationalized like texts for upsell widgets and bubbles. The same possibilities and restrictions apply.&lt;br /&gt;
&lt;br /&gt;
Products shown can also be country-specific. If only one locality (here: en_US) is given, that is always picked.&lt;br /&gt;
&lt;br /&gt;
A product consists of an image (displayed on the left), a title (on the right), a description and a price value. The description can, as usual, be marked up as HTML.&lt;br /&gt;
&lt;br /&gt;
== Making all of it work ==&lt;br /&gt;
The configuration for all three elements is done in YAML instead of JavaScript, assuming that most users of this feature are not developers but from marketing or sales departments. They will probably need starting help from a sysadmin, though, as by default, the whole Upsell process is disabled two-fold: &lt;br /&gt;
* The configuration file is not deployed. To do so, one needs to create one in the settings subfolder of the server configuration. This is usually /opt/openexchange/etc/settings. And example can be found in the UI folder as upsell-examples.yml&lt;br /&gt;
* The capability is not enabled. It is called &amp;quot;upsell&amp;quot; and, well, needs to be enabled.&lt;br /&gt;
&lt;br /&gt;
After that, a server restart (necessary for every config change, sorry!) and some hard refreshing to get rid of eventual caching artefacts, you are good to go!&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:Sales]]&lt;br /&gt;
[[Category:Administrator]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Upgrade_app_using_yo&amp;diff=21441</id>
		<title>AppSuite:Upgrade app using yo</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Upgrade_app_using_yo&amp;diff=21441"/>
		<updated>2016-02-10T13:50:33Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Upgrade an app using yo&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
With the release of App Suite version 7.6.0, we moved towards more easy dependency management for UI modules. We switched to grunt as a task-runner and are using npm and bower to handle dependencies. This article should provide a short guide on what to do in order to upgrade your app to the latest and greatest version of our shared grunt configuration and other development tools.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Development Tools ==&lt;br /&gt;
&lt;br /&gt;
First, you might want to check on the versions of the tools which are necessary for UI development.&lt;br /&gt;
&lt;br /&gt;
 $ npm outdated -g grunt-cli bower yo generator-ox-ui-module&lt;br /&gt;
&lt;br /&gt;
It's good to have those up-to-date, especially generator-ox-ui-module. You can update them by running &amp;lt;tt&amp;gt;npm install -g &amp;lt;pkg&amp;gt;&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Upgrading a Workspace using yo ==&lt;br /&gt;
&lt;br /&gt;
You can generate a new version of your workspace using:&lt;br /&gt;
&lt;br /&gt;
 $ yo ox-ui-module&lt;br /&gt;
&lt;br /&gt;
It will prompt for every file that will be overwritten with a different version and ask whether you want to overwrite or not. It is possible to view a diff between both versions. If you have not changed any of the defaults, it is save to say &amp;lt;tt&amp;gt;yes&amp;lt;/tt&amp;gt; in all of these cases. If you have changed any of these files, you might still want to overwrite the file and later only commit parts of the new things or just reset to the old version. This can be done easily with your favorite SCM tool, like git or cvs (okay, in this case may be not as easy as promised).&lt;br /&gt;
&lt;br /&gt;
Please pay special attention to &amp;lt;tt&amp;gt;package.json&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;bower.json&amp;lt;/tt&amp;gt; files, since those are the files you might have changed before. Those contain meta-information unique to your app, so they will be different from the initially generated ones.&lt;br /&gt;
&lt;br /&gt;
== Manually upgrading single node modules ==&lt;br /&gt;
&lt;br /&gt;
If the approach above is a little to radical for you, because you have customized everything, you might only want to update to the latest shared grunt configuration. Go to the [https://github.com/Open-Xchange-Frontend/shared-grunt-config/releases releases page of shared-grunt-config] and add the name of the latest tag to your apps &amp;lt;tt&amp;gt;package.json&amp;lt;/tt&amp;gt;. It should look something like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;quot;shared-grunt-config&amp;quot;: &amp;quot;Open-Xchange-Frontend/shared-grunt-config#v0.5.0&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the string after the # is the tag-name that will be checked out. Save the file and run:&lt;br /&gt;
&lt;br /&gt;
 $ npm install --no-shrinkwrap&lt;br /&gt;
&lt;br /&gt;
to make sure all your node_modules are up-to-date. If something fails, remove the node_modules directory and try again.&lt;br /&gt;
&lt;br /&gt;
== Testing the most important tasks ==&lt;br /&gt;
&lt;br /&gt;
A quick way to check if everything still runs fine is to run the following grunt tasks&lt;br /&gt;
&lt;br /&gt;
 grunt dist&lt;br /&gt;
 grunt install:dist --prefix backend --htdoc www&lt;br /&gt;
&lt;br /&gt;
After that, check if the directories &amp;lt;tt&amp;gt;backend&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;www&amp;lt;/tt&amp;gt; contain files that look a little familiar. If everything seems to look fine, you can remove those directories, again.&lt;br /&gt;
&lt;br /&gt;
During this test, keep an eye on the output and check any warning or error message. If everything looks fine, use &amp;lt;tt&amp;gt;grunt dev&amp;lt;/tt&amp;gt; task to test your app in a browser.&lt;br /&gt;
&lt;br /&gt;
== Packaging ==&lt;br /&gt;
&lt;br /&gt;
For packaging it works similar as for the app mentioned above. You can re-run the yo generators&lt;br /&gt;
&lt;br /&gt;
  $ yo ox-ui-module:deb-pkg&lt;br /&gt;
  $ yo ox-ui-module:rpm-pkg&lt;br /&gt;
&lt;br /&gt;
to generate new versions of the files. However, in case of packaging the chance is even higher, that you have changed the generated files. So the best advice would be to to check the differences and cherry pick the changes into your packaging information, i.e. manually add the changes you need to your files.&lt;br /&gt;
&lt;br /&gt;
== Further Reading ==&lt;br /&gt;
&lt;br /&gt;
* In case you want to create a new app for OX App Suite, read [[Appsuite:GettingStarted_7.6.0 | the getting started guide]].&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Upgrade_app_using_yo&amp;diff=21440</id>
		<title>AppSuite:Upgrade app using yo</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Upgrade_app_using_yo&amp;diff=21440"/>
		<updated>2016-02-10T13:48:27Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Upgrade an app using yo&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
With the release of App Suite version 7.6.0, we moved towards more easy dependency management for UI modules. We switched to grunt as a task-runner and are using npm and bower to handle dependencies. This article should provide a short guide on what to do in order to upgrade your app to the latest and greatest version of our shared grunt configuration and other development tools.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Development Tools ==&lt;br /&gt;
&lt;br /&gt;
First, you might want to check on the versions of the tools which are necessary for UI development.&lt;br /&gt;
&lt;br /&gt;
 $ npm outdated -g grunt-cli bower yo generator-ox-ui-module&lt;br /&gt;
&lt;br /&gt;
It's good to have those up-to-date, especially generator-ox-ui-module. You can update them by running &amp;lt;tt&amp;gt;npm install -g &amp;lt;pkg&amp;gt;&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Upgrading a Workspace using yo ==&lt;br /&gt;
&lt;br /&gt;
You can generate a new version of your workspace using:&lt;br /&gt;
&lt;br /&gt;
 $ yo ox-ui-module&lt;br /&gt;
&lt;br /&gt;
It will prompt for every file that will be overwritten with a different version and ask whether you want to overwrite or not. It is possible to view a diff between both versions. If you have not changed any of the defaults, it is save to say &amp;lt;tt&amp;gt;yes&amp;lt;/tt&amp;gt; in all of these cases. If you have changed any of these files, you might still want to overwrite the file and later only commit parts of the new things or just reset to the old version. This can be done easily with your favorite SCM tool, like git or cvs (okay, in this case may be not as easy as promised).&lt;br /&gt;
&lt;br /&gt;
Please pay special attention to &amp;lt;tt&amp;gt;package.json&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;bower.json&amp;lt;/tt&amp;gt; files, since those are the files you might have changed before. Those contain meta-information unique to your app, so they will be different from the initially generated ones.&lt;br /&gt;
&lt;br /&gt;
== Manually upgrading single node modules ==&lt;br /&gt;
&lt;br /&gt;
If the approach above is a little to radical for you, because you have customized everything, you might only want to update to the latest shared grunt configuration. Go to the [https://github.com/Open-Xchange-Frontend/shared-grunt-config/releases releases page of shared-grunt-config] and add the name of the latest tag to your apps &amp;lt;tt&amp;gt;package.json&amp;lt;/tt&amp;gt;. It should look something like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    &amp;quot;shared-grunt-config&amp;quot;: &amp;quot;Open-Xchange-Frontend/shared-grunt-config#v0.5.0&amp;quot;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the string after the # is the tag-name that will be checked out. Save the file and run:&lt;br /&gt;
&lt;br /&gt;
 $ npm install --no-shrinkwrap&lt;br /&gt;
&lt;br /&gt;
to make sure all your node_modules are up-to-date. If something fails, remove the node_modules directory and try again.&lt;br /&gt;
&lt;br /&gt;
== Testing the most important tasks ==&lt;br /&gt;
&lt;br /&gt;
A quick way to check if everything still runs fine is to run the following grunt tasks&lt;br /&gt;
&lt;br /&gt;
 grunt dist&lt;br /&gt;
 grunt install:dist --prefix backend --htdoc www&lt;br /&gt;
&lt;br /&gt;
After that, check if the directories &amp;lt;tt&amp;gt;backend&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;www&amp;lt;/tt&amp;gt; contain files that look a little familiar. If everything seems to look fine, you can remove those directories, again.&lt;br /&gt;
&lt;br /&gt;
During this test, keep an eye on the output and check any warning or error message. If everything looks fine, use &amp;lt;tt&amp;gt;grunt dev&amp;lt;/tt&amp;gt; task to test your app in a browser.&lt;br /&gt;
&lt;br /&gt;
== Packaging ==&lt;br /&gt;
&lt;br /&gt;
For packaging it works similar as for the app mentioned above. You can re-run the yo generators&lt;br /&gt;
&lt;br /&gt;
  $ yo ox-ui-module:deb-pkg&lt;br /&gt;
  $ yo ox-ui-module:rpm-pkg&lt;br /&gt;
&lt;br /&gt;
to generate new versions of the files. However, in case of packaging the chance is even higher, that you have changed the generated files. So the best advice would be to to check the differences and cherry pick the changes into your packaging information, i.e. manually add the changes you need to your files.&lt;br /&gt;
&lt;br /&gt;
== Further Reading ==&lt;br /&gt;
&lt;br /&gt;
* In case you want to create a new app for OX App Suite, read [[Appsuite:GettingStarted_7.6.0 | the getting started guide]].&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:ISV_Mockups_Wireframes&amp;diff=21439</id>
		<title>AppSuite:ISV Mockups Wireframes</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:ISV_Mockups_Wireframes&amp;diff=21439"/>
		<updated>2016-02-10T13:40:38Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Create Open-Xchange App Suite Mockups / Wireframes&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
[[File:Ox-contacts.png|300px|thumb|center]]&lt;br /&gt;
&lt;br /&gt;
[[File:Ox-calendar.png|300px|thumb|center]]&lt;br /&gt;
&lt;br /&gt;
[[File:Ox-core.png|300px|thumb|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Why build mockups? ===&lt;br /&gt;
&lt;br /&gt;
Here are some reasons, why YOU should think about using the power of mockups:&lt;br /&gt;
&lt;br /&gt;
*  It reproduces the experience of sketching on a whiteboard, but using a computer.&lt;br /&gt;
*  It dramatically helps to visually any kind of ideas, features of a web application. Mockups are very useful, for example in contracts, proposals, user stories or software change request forms.&lt;br /&gt;
*  Enhance the visibility of your upcoming features inside a product / customization.&lt;br /&gt;
*  Iterate easily over ideas + get feedback from potential users and customers until they are ready to get implemented via the development department.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
This page contains needed informations, to easily build wireframes/mockups for the Open-Xchange App Suite application. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
IMPORTANT NOTE: To build/use the mockups, which get described in this article, you need to install a 3rd party software (Balsamiq) on your local PC/Laptop. Balsamiq can be used in a trial period. Once you decide to constantly use it, you must purchase a license from their [http://balsamiq.com/products/mockups/] website.&lt;br /&gt;
&lt;br /&gt;
=== What´s needed to build beautiful App Suite mockups ? ===&lt;br /&gt;
&lt;br /&gt;
Following Steps are required, to get started to build nice mockups for App Suite. &lt;br /&gt;
&lt;br /&gt;
*  You need to install the desktop application called &amp;quot;Balsamiq Desktop App&amp;quot;. You can download it from: http://balsamiq.com&lt;br /&gt;
&lt;br /&gt;
*  Download the Open-Xchange Balsamiq Assets/Library/Samples from [[File:Ox-assets-balsamiq-7.6.2.zip]]&lt;br /&gt;
&lt;br /&gt;
*  Import the downloaded App Suite assets/library into the Balsamiq application and use the included examples:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On MAC OS X / Linux / Windows&lt;br /&gt;
&lt;br /&gt;
# Checkout from GIT or download ZIP file.&lt;br /&gt;
# Unzip &amp;amp; navigate to the created directory &amp;quot;assets&amp;quot; . Remember/Copy complete local path to this directory. Example: &amp;quot;/Users/&amp;lt;myuser&amp;gt;/Downloads/ox-assets-balsamiq/assets&amp;quot;&lt;br /&gt;
# Open Balsamiq Application and &amp;gt; open menu &amp;quot;Balsamiq Mockups&amp;quot; &amp;gt; Click &amp;quot;About Balsamiq Application&amp;quot;&lt;br /&gt;
# &amp;quot;About Dialog&amp;quot; opens with a link to &amp;quot;Open Local Store Folder&amp;quot;. Click on that link, which opens your explorer/finder application. &lt;br /&gt;
# Now create the required file called &amp;quot;BalsamiqMockups.cfg&amp;quot; inside that directory (&amp;quot;Local Store&amp;quot; folder) with following content. '''IMPORTANT''' You must change the '''assets''' path attribute to '''YOUR path''' structure on YOUR local system.&lt;br /&gt;
  &amp;lt;config&amp;gt;&lt;br /&gt;
    &amp;lt;fontFace&amp;gt;Helvetica Neue&amp;lt;/fontFace&amp;gt;&lt;br /&gt;
    &amp;lt;rememberWindowSize&amp;gt;true&amp;lt;/rememberWindowSize&amp;gt;&lt;br /&gt;
    &amp;lt;useCookies&amp;gt;true&amp;lt;/useCookies&amp;gt;&lt;br /&gt;
    &amp;lt;'''assetsPath'''&amp;gt;/Users/&amp;lt;myuser&amp;gt;/Downloads/ox-assets-balsamiq/assets&amp;lt;/'''assetsPath'''&amp;gt;&lt;br /&gt;
  &amp;lt;/config&amp;gt;&lt;br /&gt;
# Now use your explorer/finder/shell and copy all &amp;quot;.bmml&amp;quot; files from within the &amp;quot;visuals/assets&amp;quot; directory to your work/dev directory. &lt;br /&gt;
# Close the Balsamiq application and reopen it. You now should see a new tab called &amp;quot;Account Assets&amp;quot; in the UI components library. All OX related components are prefixed with &amp;quot;ox-&amp;quot; for easy find as you type usage. &lt;br /&gt;
# Go to your work/dev directory which contains the copied &amp;quot;.bmml&amp;quot; files. Click on one of the .bmml files. You are DONE!&lt;br /&gt;
&lt;br /&gt;
*  Let the fun begin! Mockup as your like and save the newly created file to your work/dev directory!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{#ev:youtube|RstDlq9X-jA}}&lt;br /&gt;
&lt;br /&gt;
==== General basics of Balsamiq ====&lt;br /&gt;
&lt;br /&gt;
*  Working with Symbols - http://support.balsamiq.com/customer/portal/articles/110439&lt;br /&gt;
&lt;br /&gt;
*  How to Use an Existing File as a Symbol in Mockups - http://support.balsamiq.com/customer/portal/articles/110439&lt;br /&gt;
&lt;br /&gt;
*  Working with Text - http://support.balsamiq.com/customer/portal/articles/110121&lt;br /&gt;
&lt;br /&gt;
*  Tutorials - http://support.balsamiq.com/customer/portal/topics/49503-tutorials/articles&lt;br /&gt;
&lt;br /&gt;
==== Common pitfalls when using Balsamiq ====&lt;br /&gt;
* Any files stored in an subdirectory of your assets folder don't show up in the 'account assets' tab within Balsamic cause currently subdirectories are ignored. &lt;br /&gt;
* In case your assets folder only contains images the 'account assets' will not show up&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Mobile&amp;diff=21438</id>
		<title>AppSuite:Mobile</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Mobile&amp;diff=21438"/>
		<updated>2016-02-10T13:34:20Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Developing for mobile devices&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Introduction==&lt;br /&gt;
&lt;br /&gt;
App Suite is designed to work on all device types and sizes. The UI uses responsive design principles to scale nicely on each device size. We do define three display sizes to macht the majority of devices. These are simply named &amp;quot;small&amp;quot;, &amp;quot;medium&amp;quot; and &amp;quot;large&amp;quot;. Theses classes are used to match smartphones, tablets and desktop PCs. If you are developing a app for App Suite make sure it runs nicely and looks great on all of these three device categories. (If you are not familiar with latest CSS techniques and the principles of responsive design you should have a look at this [[AppSuite:UI_developer_primer | article]]).&lt;br /&gt;
&lt;br /&gt;
Often the simple use of media queries is not enough to customize your app for small and medium screens, you may need to customize your application code as well. We have integrated a function to detect everything you might want to know during runtime in your javascript code.&lt;br /&gt;
&lt;br /&gt;
==Sizes==&lt;br /&gt;
&lt;br /&gt;
The minimum device target size is 320 x 480 pixels. Your App should work on devices with this resolution.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
|-&lt;br /&gt;
|small || up to 480px&lt;br /&gt;
|-&lt;br /&gt;
|medium || 481px up to 1024px&lt;br /&gt;
|-&lt;br /&gt;
|large || 1025px and higher&lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
==The _.device function==&lt;br /&gt;
&lt;br /&gt;
We extended underscore with a new function called &amp;lt;tt&amp;gt;_.device&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;_.device()&amp;lt;/tt&amp;gt; function can be used to retrieve informations about the device. The function takes a string as argument which contains a boolean expression. This expression will be evaluated and the result is returned as a boolean.&lt;br /&gt;
&lt;br /&gt;
The device function uses &amp;lt;tt&amp;gt;_.browser&amp;lt;/tt&amp;gt; object for informations in combination with &amp;lt;tt&amp;gt;_.screenInfo&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The device class &amp;lt;tt&amp;gt;'smartphone'&amp;lt;/tt&amp;gt; is used to determine a mobile device and is detected by several criteria. For more details, see this [[AppSuite:UI_smartphone_device_classification | article about smartphone classification. ]]&lt;br /&gt;
&lt;br /&gt;
==Examples for _.device==&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
// handle different mobile operating systems&lt;br /&gt;
if (_.device('ios')) {&lt;br /&gt;
   // true for all devices running iOS, no matter what version&lt;br /&gt;
   console.log('you are running iOS');&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// combined statements&lt;br /&gt;
if (_.device('ios &amp;amp;&amp;amp; android')) {&lt;br /&gt;
   // true for all android and iOS devices&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// negation&lt;br /&gt;
if (_.device('!android')) {&lt;br /&gt;
    // true for all devices except android &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// screen information&lt;br /&gt;
if (_.device('small &amp;amp;&amp;amp; iOS ')) {&lt;br /&gt;
   // true for iPhone, not for iPad&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// shorthands&lt;br /&gt;
_.device('smartphone')&lt;br /&gt;
// true for small devices running a mobile OS (iOS, Android, BB or Windowsphone)&lt;br /&gt;
&lt;br /&gt;
_.device('tablet')&lt;br /&gt;
// true for medium sized devices running a mobile OS&lt;br /&gt;
&lt;br /&gt;
_.device('desktop')&lt;br /&gt;
// true for all devices not running a mobile OS&lt;br /&gt;
&lt;br /&gt;
// getting version informations&lt;br /&gt;
_.device('ios &amp;gt; 5 || android &amp;gt; 4') &lt;br /&gt;
// true for ios &amp;gt; 5, i.e. 5.1 and 6. Same for Android, 4.0 will fail 4.1 or 4.2 will be true. &lt;br /&gt;
&lt;br /&gt;
// enhanced screen information &lt;br /&gt;
_.device('iOS &amp;amp;&amp;amp; retina &amp;amp;&amp;amp; small')&lt;br /&gt;
// true for iPhone 4, 4s and 5 (retina display)&lt;br /&gt;
&lt;br /&gt;
_.device('landscape &amp;amp;&amp;amp; android &amp;amp;&amp;amp; medium') &lt;br /&gt;
// true for android tablet held in landscape mode&lt;br /&gt;
&lt;br /&gt;
// other information, simple browser detection&lt;br /&gt;
_.device('safari || firefox')&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Please note that information about device orientation may change during usage.&lt;br /&gt;
&lt;br /&gt;
==Mobile considerations==&lt;br /&gt;
&lt;br /&gt;
As of today mobile usage has become much more important than some years ago. Always consider the the fact a user may want to use your App on a smartphone. So, optimizing for mobile should not be last step in your development process, it should be one of the first. This will safe you a lot of painful debugging and layout fixes.&lt;br /&gt;
&lt;br /&gt;
You should ask you a simple question: Does function X in my App do have a mobile use case? Or more simple: Will anybody use this on a smartphone? &lt;br /&gt;
If not, disable or remove this function on a mobile device. Nobody will perform a complex 35-click action in your App on a smartphone.&lt;br /&gt;
&lt;br /&gt;
Developing for mobile should follow some simple rules:&lt;br /&gt;
* Mobile phones do have small screens. Safe space in your layout, reduce margins and paddings.&lt;br /&gt;
* Touch is not click, keep buttons and links big enough to be touchable. 40px should be a minium.&lt;br /&gt;
* Mobile networks are slow and have a high latency. Safe network requests and handle failing requests properly&lt;br /&gt;
* Mobile devices are not as fast as desktop PCs. Not everybody has a high end smartphone so keep your code clean and fast&lt;br /&gt;
* Always test your App on a real device&lt;br /&gt;
&lt;br /&gt;
==Remote Debugging==&lt;br /&gt;
&lt;br /&gt;
To setup remote debugging on windows, mac and linux you can follow the instructions from the chrome developer tools website. &lt;br /&gt;
&lt;br /&gt;
[https://developer.chrome.com/devtools/docs/remote-debugging Remote Debugging on Android with Chrome]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If this is not working or not applicable for you, use the following description:&lt;br /&gt;
&lt;br /&gt;
[[AppSuite:UI_remote_debugging_android_mac | How to setup remote debugging for Chrome on android devices with a mac.]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Media_player&amp;diff=21437</id>
		<title>AppSuite:Media player</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Media_player&amp;diff=21437"/>
		<updated>2016-02-10T13:31:42Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Mediaplayer&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
==Browser and device support==&lt;br /&gt;
App Suite uses ''mediaelement.js'' [http://mediaelementjs.com] as media player. The player uses native HTML5 audio and video elements plus it falls back to flash or silverlight if they are not available.&lt;br /&gt;
&lt;br /&gt;
Every browser supports a different set of supported codecs and container formats. Follow this link to see which ones: http://mediaelementjs.com/#devices&lt;br /&gt;
&lt;br /&gt;
==Figure out version of mediaelement.js==&lt;br /&gt;
We always try to integrate the latest version of mediaelement.js in order to get latest fixes. To figure out which version is integrated in your release, please run the following code:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
// logs &amp;quot;2.11.0&amp;quot; in current development branch (April 2013)&lt;br /&gt;
require(['apps/mediaelement/mediaelement-and-player.js'], function () {&lt;br /&gt;
  console.log(mejs.version);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==How to enable/disable the media player==&lt;br /&gt;
In order to configure this server-side, just create a new property file or append to existing '''appsuite.properties''' (mind the '''double-slash'''; this in not a typo!):&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-config&amp;quot;&amp;gt; &lt;br /&gt;
io.ox/files//audioEnabled = true|false&lt;br /&gt;
io.ox/files//videoEnabled = true|false&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Date_and_time&amp;diff=21436</id>
		<title>AppSuite:Date and time</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Date_and_time&amp;diff=21436"/>
		<updated>2016-02-10T12:51:52Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Date and Time&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The handling of date and time is a complicated mess of historical conventions, which are still changed from time to time by governments around the world. To keep this away from day-to-day activities of developers, the OX App Suite platform provides the module date, which performs conversion between different time zones, formatting and parsing of date and time values.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
The examples in this article use some global variables. For the examples to work the way they are intended, you will first need to load the [[AppSuite:I18n | gettext module]] and the date module and store them in variables &amp;lt;tt&amp;gt;gettext&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;date&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
var gettext;&lt;br /&gt;
var date;&lt;br /&gt;
require(['gettext!example', 'io.ox/core/date']).done(function (gt, d) {&lt;br /&gt;
	gettext = gt;&lt;br /&gt;
	date = d;&lt;br /&gt;
});	&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In real code, you should not use global variables. Instead, you would use the &amp;lt;tt&amp;gt;gt&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;d&amp;lt;/tt&amp;gt; parameters directly, without storing them in another variable.&lt;br /&gt;
&lt;br /&gt;
== Time Zones ==&lt;br /&gt;
&lt;br /&gt;
The built-in JavaScript class Date supports calculations using UTC and the operating system's local time zone. Unfortunately, this is not enough. Examples include using a time zone different from the time zone of the client system, or displaying times in multiple time zones at once.&lt;br /&gt;
&lt;br /&gt;
The class Local is almost a drop-in replacement for Date, with the main difference being that it operates in the default time zone of the OX App Suite user, even if it is different from the time zone of the browser's operating system. The following code displays the current date and time. It will show a different time when called after changing the user's time zone.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
alert(new date.Local());&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To work with other time zones, the function &amp;lt;tt&amp;gt;getTimeZone()&amp;lt;/tt&amp;gt; can be used to create replacement classes similar to Local, which are all descendants of the private class LocalDate (for which all methods are documented below). Since the time zone definitions are loaded on-demand, the function returns a jQuery promise, which is resolved to the class constructor once the time zone definition is loaded. This class can then be used to e.g. convert between time zones.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
date.getTimeZone('America/New_York').done(function (NewYork) {&lt;br /&gt;
  alert(gettext('New York celebrated New Year\'s at %1$s',&lt;br /&gt;
          new date.Local(new NewYork(2012, 0, 1))));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The function getTimeZone() is memoized for performance reasons, i.e. multiple calls with the same argument will return the same promise and will therefore resolve to the same class object.&lt;br /&gt;
&lt;br /&gt;
== Formatting ==&lt;br /&gt;
&lt;br /&gt;
The default &amp;lt;tt&amp;gt;LocalDate.prototype.toString()&amp;lt;/tt&amp;gt; method displays the full date and time, including the day of week and the time zone. To allow better control of the resulting string, the method &amp;lt;tt&amp;gt;LocalDate.prototype.format()&amp;lt;/tt&amp;gt; accepts a set of format flags. The flags determine, which fields should be included in the output. Currently, there are four flags:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
var d = new date.Local();&lt;br /&gt;
alert(d.format(date.DATE));&lt;br /&gt;
alert(d.format(date.TIME));&lt;br /&gt;
alert(d.format(date.DAYOFWEEK));&lt;br /&gt;
alert(d.format(date.TIMEZONE));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Multiple flags can be combined by adding them, or by using one of the predefined combination constants. Not all possible combinations produce unique results. When DAYOFWEEK is specified together with any other fields, DATE is automatically included in the output. Similarly, TIMEZONE implies TIME when used with other fields.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
var d = new date.Local();&lt;br /&gt;
assert(d.format(date.DAYOFWEEK + date.TIMEZONE) === d.format(date.FULL_DATE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The format flags select one of several predefined format strings, which is stored in the current locale settings. This frees both the developers and the translators from having to remember arcane letter combinations.&lt;br /&gt;
&lt;br /&gt;
For the case that even finer control of the generated output is required, LocalDate.prototype.format() accepts a format string directly. The syntax of the format strings is a subset of CLDR date format patterns. Format specifiers which are not used in the Gregorian calendar will be implemented on-demand.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
alert(new date.Local().format('EEEE'));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In general, if format strings are required, this indicates that the current date API should be extended. Feedback is always welcome.&lt;br /&gt;
&lt;br /&gt;
One situation where direct access to format strings is useful, is the manipulation of the localized format strings, e.g. to decorate individual fields in a displayed date with HTML markup. To achieve this, the localized format string is retrieved with getFormat(), and parts of it passed individually to LocalDate.prototype.format().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
// Get the original format string.&lt;br /&gt;
var fmt = date.getFormat(date.FULL_DATE);&lt;br /&gt;
&lt;br /&gt;
// Regular expression to parse the format string.&lt;br /&gt;
//  .  .  ( time zone )|quote|'quoted  text'|rest&lt;br /&gt;
var re = /(v+|V+|z+|Z+)|(?:''|'(?:[^']|'')*'|[^vVzZ'])+/g;&lt;br /&gt;
&lt;br /&gt;
// Appends a formatted date to a jQuery node and returns&lt;br /&gt;
// the time zone as a separate node.&lt;br /&gt;
function decorateTimeZone(d, parent) {&lt;br /&gt;
  var span;&lt;br /&gt;
  fmt.replace(re, function (match, tz) {&lt;br /&gt;
    if (tz) { // found the time zone field&lt;br /&gt;
      // Wrap the formatted time zone in a &amp;lt;span&amp;gt; element.&lt;br /&gt;
      span = $('&amp;lt;span&amp;gt;').text(d.format(match));&lt;br /&gt;
      parent.append(result);&lt;br /&gt;
    } else {&lt;br /&gt;
      // Append all other formatted text as plain text nodes.&lt;br /&gt;
      parent.append($.txt(d.format(match)));&lt;br /&gt;
    }&lt;br /&gt;
  });&lt;br /&gt;
  // Return the wrapped field for further customization.&lt;br /&gt;
  return span;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Does anybody want this use case as an API function?''&lt;br /&gt;
&lt;br /&gt;
== Intervals ==&lt;br /&gt;
&lt;br /&gt;
In addition to formatting individual dates, LocalDate instances can format intervals. The difference to simply inserting two dates into a translated string is that short intervals may have a compacter representation, e.g. 'Jan 1–10, 2012' instead of 'Jan 1, 2012 – Jan 10, 2012'.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
var start = new date.Local(), end = new date.Local(start);&lt;br /&gt;
end.add(date.DAY);&lt;br /&gt;
alert(start.formatInterval(end, date.DATE));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The methods LocalDate.prototype.formatInterval() and LocalDate.prototype.getIntervalFormat() are used like LocalDate.prototype.format() and getFormat(), with two differences. First, the interval functions accept the end of the interval as first parameter before the format flags. And second, since the format string depends on the actual interval, LocalDate.prototype.getIntervalFormat() is a member function and requires the same end value as used later for LocalDate.prototype.formatInterval().&lt;br /&gt;
&lt;br /&gt;
== Parsing ==&lt;br /&gt;
&lt;br /&gt;
Parsing is the reverse of formatting, except that ideally, users should be able to enter almost anything, as long as it is not ambiguous. The practice looks a bit more restricted. The LocalDate.parse() function takes a format parameter like the formatting functions, and expects the parsed string to match it very closely.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
var input = prompt(gettext('Please enter a date'), '');&lt;br /&gt;
var d = date.Local.parse(input, date.DATE);&lt;br /&gt;
alert(gettext('I understood %1$s', d);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If there is demand, we can implement some heuristics. There are a lot of abstract algorithm descriptions in the standards, which describe how to parse almost anything while using the format string only as disambiguation help.&lt;br /&gt;
&lt;br /&gt;
== Manipulation of LocalDate objects ==&lt;br /&gt;
&lt;br /&gt;
Since the LocalDate class is designed as a drop-in for the Date class, its instances support getter and setter methods for all fields of a date. They can be used to modify an existing LocalDate object, e. g. to round a date to the nearest day or hour.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
var start = new date.Local(), end = new date.Local(start);&lt;br /&gt;
start.setHours(0, 0, 0, 0);&lt;br /&gt;
end.setHours(24, 0, 0, 0);&lt;br /&gt;
alert(gettext('Today is %1$s', start.formatInterval(end)));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
One manipulation, which is often needed in calendars, is iteration over time. This operation seems simple, at least for iteration steps with a constant duration like hours, days or weeks. One just needs to add the step duration to the timestamp. While there is a method to do exactly this, LocalDate.prototype.addUTC(), you will encounter problems as soon as the iteration transcends a daylight savings switch. First, days and weeks are not actually constant. And second, sometimes the iteration might need to follow the displayed 'wall clock' time instead of the physical time. For these cases, the method LocalDate.prototype.add() increments the local time instead of the UTC time. Both methods accept an increment value in milliseconds, which can also be negative. There are predefined constants for the most common (almost-)fixed-duration periods.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
var d = new date.Local(2012, 2);&lt;br /&gt;
for (; d.getMonth() &amp;lt; 3; d.add(date.DAY)) {&lt;br /&gt;
  // Display a day in a month view.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Iteration with larger intervals like months and years has the additional difficulty, that a single numeric parameter like 30 * date.DAY cannot be interpreted as a month, because it might be just really 30 days. To solve this, there are separate methods for months and years: LocalDate.prototype.addMonths() and LocalDate.prototype.addYears(). They accept the number of months, respective years as parameter.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
var d = new date.Local(2012, 0);&lt;br /&gt;
for (; g.getYear() &amp;lt; 2013; d.addMonths(1)) {&lt;br /&gt;
  // Display a month in a year view.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally, most calendars default to displaying the current date, and therefore need to figure out the iteration range based on an arbitrary point inside that range. In most cases, the start can be computed by simply calling the appropriate setters with all zeros as parameters to find the start of the range, and adding the range duration to find the end. One exception is the week. There is no setter for the day of the week. Instead, the method LocalDate.prototype.setStartOfWeek() can be used to find the start of a week.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
// Find the start and the end of the current week&lt;br /&gt;
var d = new date.Local().setStartOfWeek();&lt;br /&gt;
var end = new date.Local(start).add(date.WEEK);&lt;br /&gt;
// Iterate over the week&lt;br /&gt;
for (; d &amp;lt; end; d.add(date.DAY)) {&lt;br /&gt;
  // Display a day in a week view.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In addition to explicit method calls, the JavaScript standard method LocalDate.prototype.valueOf() allows native comparison, addition and subtraction operators to work on LocalDate objects. The result of LocalDate.prototype.valueOf(), and therefore of addition and subtraction, is a timestamp, which can be passed to the LocalDate constructor to get a LocalDate object again.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
var start = date.Local.parse(prompt(gettext('Start date'), ''));&lt;br /&gt;
var end = date.Local.parse(prompt(gettext('End date'), ''));&lt;br /&gt;
if (end &amp;lt; start) alert(gettext('Invalid date range!'));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Differences between LocalDate and Date ==&lt;br /&gt;
&lt;br /&gt;
The LocalDate classes duplicate most of the Date API with a few exceptions:&lt;br /&gt;
&lt;br /&gt;
* The constructor can not be called as a function. The Date constructor does in this case nothing useful anyway.&lt;br /&gt;
* The constructor always uses full years, it does not add 1900 for years 0 to 99.&lt;br /&gt;
* Date.UTC() should not be necessary, but since it returns a numeric timestamp and has nothing to do with time zones, it can still be used directly.&lt;br /&gt;
* Except for LocalDate.prototype.toString(), all to*String() methods are replaced by LocalDate.prototype.format().&lt;br /&gt;
* There are no UTC variants of getters and setters. They should not be necessary. But just in case, you can still use a LocalDate class for the time zone 'UTC' instead.&lt;br /&gt;
* The getter and setter for the year are called getYear and setYear instead of getFullYear and setFullYear since we have no legacy code with Y2K issues.&lt;br /&gt;
* Setters return the modified object instead of the timestamp. This is useful for chaining of method calls.&lt;br /&gt;
* Date.prototype.getTimeZoneOffset() should not be necessary, since hiding these details is the whole point of the date module. If still necessary, LocalDate.getTTInfo() can be used instead.&lt;br /&gt;
&lt;br /&gt;
== API Reference ==&lt;br /&gt;
&lt;br /&gt;
This section provides a short reference of the date API.&lt;br /&gt;
&lt;br /&gt;
=== Locale ===&lt;br /&gt;
&lt;br /&gt;
A locale describes the localization settings which usually depend not only on the language, but also on the region and optionally even on the personal preferences of the user. The locale object is loaded at startup and provides various settings and translations.&lt;br /&gt;
&lt;br /&gt;
Various arrays containing translations can be used directly without any other date function.&lt;br /&gt;
&lt;br /&gt;
   locale.dayPeriods&lt;br /&gt;
A map from an identifier of a day period to the corresponding translation. The members am and pm are used in the 12h time format, but other members may be useful for greetings. Is anyone interested in a function which returns the correct greeting period for a given time? If not, maybe remove everything except AM and PM?&lt;br /&gt;
   locale.days&lt;br /&gt;
An array with translated full names of days of the week, starting with Sunday. CLDR context: &amp;quot;format&amp;quot;, CLDR width: &amp;quot;wide&amp;quot;.&lt;br /&gt;
   locale.daysShort&lt;br /&gt;
An array with translated abbreviated names of days of the week, starting with Sunday. CLDR context: &amp;quot;format&amp;quot;, CLDR width: &amp;quot;abbreviated&amp;quot;.&lt;br /&gt;
   locale.daysStandalone&lt;br /&gt;
An array with translated standalone names of days of the week, starting with Sunday. CLDR context: &amp;quot;standalone&amp;quot;, CLDR width: &amp;quot;abbreviated&amp;quot;.&lt;br /&gt;
   locale.eras&lt;br /&gt;
An array with translated abbreviations for the two eras of the Gregorian calendar: BC and AD (in that order).&lt;br /&gt;
   locale.months&lt;br /&gt;
An array with translated full names of months. CLDR context: &amp;quot;format&amp;quot;, CLDR width: &amp;quot;wide&amp;quot;.&lt;br /&gt;
   locale.monthsShort&lt;br /&gt;
An array with translated abbreviated names of months. CLDR context: &amp;quot;format&amp;quot;, CLDR width: &amp;quot;abbreviated&amp;quot;.&lt;br /&gt;
Diverse week-based calculations need to know on which day the week starts and how the first week of the year is defined.&lt;br /&gt;
&lt;br /&gt;
   locale.daysInFirstWeek&lt;br /&gt;
The lowest number of days of a week which must be in the new year for that week to be considered week number 1. Common values are&lt;br /&gt;
* 1 if the week of January 1st is week number 1,&lt;br /&gt;
* 4 if the first week which has most of its days in the new year is week number 1.&lt;br /&gt;
* 7 if the first week which starts in the new year is week number 1.&lt;br /&gt;
   locale.weekStart&lt;br /&gt;
First day of the week. Common values are&lt;br /&gt;
* 0 for Sunday,&lt;br /&gt;
* 1 for Monday.&lt;br /&gt;
Deprecated and internal fields should not be used since they can change or disappear entirely without notice. They are still documented here for completeness.&lt;br /&gt;
&lt;br /&gt;
   locale.date&lt;br /&gt;
Deprecated, use DATE instead.&lt;br /&gt;
   locale.dateTime&lt;br /&gt;
Deprecated, use DATE_TIME instead.&lt;br /&gt;
   locale.dateTimeFormat&lt;br /&gt;
The default format used to combine a time (%1$s) and a date (%2$s). Not used yet, will probably disappear.&lt;br /&gt;
   locale.formats&lt;br /&gt;
A map from various canonical sets of format fields to the corresponding localized format strings. Used mainly by getFormat(). Maybe remove it from the public interface after loading, since it's internal?&lt;br /&gt;
   locale.h12&lt;br /&gt;
A boolean indicating whether the 12h format is used. WANTED: a better name.&lt;br /&gt;
   locale.intervals&lt;br /&gt;
A map from various canonical sets of format fields to the corresponding formatting rules. A formatting rule is a map from the largest field, which is different between the start and end of the interval, to the corresponding localized formatting string. The field is specified as a single lower case letter. The formatting string is split at the first repeating field. The first part is formatted using the start of the interval, the second part is formatted using the end of the interval. Used mainly by LocalDate.prototype.getIntervalFormat(). Maybe remove it from the public interface after loading, since it's internal?&lt;br /&gt;
   locale.intervals.fallback&lt;br /&gt;
The default format string to combine the start (%1$s) and end (%2$s) of an interval when none of the other members of locale.intervals apply. Maybe remove it from the public interface after loading, since it's internal?&lt;br /&gt;
   locale.time&lt;br /&gt;
Deprecated, use TIME instead.&lt;br /&gt;
   getTimeZone&lt;br /&gt;
&lt;br /&gt;
The main entry points of the date API are the class Local which replaces Date for use with the user's default time zone and a function to generate similar classes for arbitrary time zones.&lt;br /&gt;
&lt;br /&gt;
   getTimeZone(name)&lt;br /&gt;
Creates a LocalDate class which operates in the specified time zone. Multiple calls with the same name will return the same object.&lt;br /&gt;
name String - The name of the requested time zone. It must be one of the values returned by api/config/availableTimeZones.&lt;br /&gt;
Returns Promise - A promise which resolves to a LocalDate class which uses the requested time zone.&lt;br /&gt;
Local&lt;br /&gt;
A convenience LocalDate class which uses the user's current time zone.&lt;br /&gt;
&lt;br /&gt;
=== LocalDate ===&lt;br /&gt;
&lt;br /&gt;
The core of the date API is the abstract class LocalDate which is the superclass of time zone specific classes. The class itself is not publically available, only its subclasses can be created by calling getTimeZone(). The subclasses and their instances are referred to as LocalDate classes and LocalDate objects.&lt;br /&gt;
&lt;br /&gt;
The constructor mimics the behavior of the Date class, but it can't be called as a function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
new LocalDate()&lt;br /&gt;
new LocalDate(timestamp)&lt;br /&gt;
new LocalDate(year, month, date, hours, minutes, seconds, ms)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The constructor accepts the same parameters as the Date constructor.&lt;br /&gt;
The entire functionality of the class is based on a few low level functions. They should not be necessary outside the date module itself, assuming the API is complete. If you find you need these functions, please let's extend the high level APIs instead.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.getTTInfo(t)&lt;br /&gt;
Returns the GMT offset, daylight savings and the abbreviation of the time zone which are in effect at the specified time.&lt;br /&gt;
   t Timestamp - The timestamp.&lt;br /&gt;
Returns { gmtoff, isdst, abbr } - An object with the GMT offset in milliseconds, whether daylight savings are in effect (0 or 1, not a real boolean) and the abbreviation like 'CET' (!isdst) or 'CEST' (!!isdst).&lt;br /&gt;
   LocalDate.getTTInfoLocal(t)&lt;br /&gt;
* Returns the GMT offset, daylight savings and the abbreviation of the time zone which are in effect at the specified local time. If the local time is ambiguous or invalid, the same fallbacks as for LocalDate.utc() are used. (It is actually implemented as a wrapper for this function.)&lt;br /&gt;
* t Number - The local time.&lt;br /&gt;
* Returns { gmtoff, isdst, abbr } - An object with the GMT offset in milliseconds, whether daylight savings are in effect (0 or 1, not a real boolean) and the abbreviation like 'CET' (!isdst) or 'CEST' (!!isdst).&lt;br /&gt;
&lt;br /&gt;
   LocalDate.localTime(t)&lt;br /&gt;
* Converts a UTC timestamp to local time.&lt;br /&gt;
* t Timestamp - The UTC timestamp to convert.&lt;br /&gt;
* Returns Number - A local time which is used in computations of date and time components.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.utc(t)&lt;br /&gt;
* Converts local time to a UTC timestamp. If the local time is ambiguous because of a DST switch, the local time is interpreted as before the switch. E.g. 02:30 at the end of DST is interpreted as DST. If the local time is invalid because of a DST switch, the local time is interpreted as if the switch already occurred. E.g. 02:30 at the start of DST returns the same timestamp as 01:30 before the switch.&lt;br /&gt;
* t Number - The local time to convert.&lt;br /&gt;
* Returns Timestamp - The corresponsing UTC timestamp.&lt;br /&gt;
Metadata about a time zone is stored directly on the LocalDate object.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.id&lt;br /&gt;
* Original name used to retrieve this time zone.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.displayName&lt;br /&gt;
* A human-readable name of this time zone as provided by the config module under &amp;quot;availableTimeZones&amp;quot;.&lt;br /&gt;
* Parsing of date and time strings as entered by a user is done using format flags from Constants. The current implementation expects the string to match the corresponding localized format string pretty closely. Any difficulties with parsing common entered dates and times should be taken as an opportunity to extend the parsing heuristics.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.parse(string, format)&lt;br /&gt;
* Parses a string using either the specified format string or localized version of one of predefined format strings selected by format flags.&lt;br /&gt;
* string String - The string to parse.&lt;br /&gt;
* format String or Number - Either a format string with the syntax of CLDR date format patterns, or one of the format flag constants. In the second case, the actual format string is localized for the current user's locale according to locale.formats.&lt;br /&gt;
* Returns LocalDate or null - A new LocalDate object which represents the parsed date and time or null if the string could not be parsed.&lt;br /&gt;
* Methods of LocalDate instances can be grouped into several categories. The first are the setters and getters for individual fields from Date. Since each LocalDate class has its own time zone, There are no UTC variants of each getter and setter. They all work with local time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
LocalDate.prototype.getYear()&lt;br /&gt;
LocalDate.prototype.getMonth()&lt;br /&gt;
LocalDate.prototype.getDate()&lt;br /&gt;
LocalDate.prototype.getHours()&lt;br /&gt;
LocalDate.prototype.getMinutes()&lt;br /&gt;
LocalDate.prototype.getSeconds()&lt;br /&gt;
LocalDate.prototype.getMilliseconds()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Return the corresponding field of the local date or time.&lt;br /&gt;
* Returns Number - The requested field, as a number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
LocalDate.prototype.setYear(year, month, date)&lt;br /&gt;
LocalDate.prototype.setMonth(month, date)&lt;br /&gt;
LocalDate.prototype.setDate(date)&lt;br /&gt;
LocalDate.prototype.setHours(hour, min, sec, ms)&lt;br /&gt;
LocalDate.prototype.setMinutes(min, sec, ms)&lt;br /&gt;
LocalDate.prototype.setSeconds(sec, ms)&lt;br /&gt;
LocalDate.prototype.setMilliseconds(ms)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Set the specified date or time fields. Any unspecified fields retain their current value. Values outside of the specified ranges will result in overflow to the neighboring periods and can therefore be used for date arithmetic.&lt;br /&gt;
** year Number - The year.&lt;br /&gt;
** month Number - The month. Values range from 0 for January to 11 for December.&lt;br /&gt;
** date Number - The date. Values range from 1 to 31.&lt;br /&gt;
** hour Number - The hours. Values range from 0 to 23.&lt;br /&gt;
** min Number - The minutes. Values range from 0 to 59.&lt;br /&gt;
** sec Number - The seconds. Values range from 0 to 59.&lt;br /&gt;
** ms Number - The milliseconds. Values range from 0 to 999.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.prototype.getDay()&lt;br /&gt;
* Returns Number - The day of the week. Values range from 0 for Sunday to 6 for Saturday.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.prototype.getTimeZone()&lt;br /&gt;
* This method is not present in Date. It returns the abbreviation of the specific time zone. The abbreviation indicate the GMT offset and is therefore different between daylight savings time and standard time.&lt;br /&gt;
* Returns String - The abbreviation of the specific time zone.&lt;br /&gt;
* Other getters and setters work with the entire timestamp and not just individual fields.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.prototype.getDays()&lt;br /&gt;
* Returns the day number of this object. This may be useful to find the start of the same day (by multiplying the result with DAY).&lt;br /&gt;
* Returns Number - The number of days since 1970-01-01 in this object's time zone.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.prototype.getTime()&lt;br /&gt;
* Returns Timestamp - The UTC timestamp of this object.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.prototype.setTime(time)&lt;br /&gt;
* Sets the UTC timestamp to a new value.&lt;br /&gt;
* time Timestamp - The new UTC timestamp of this object.&lt;br /&gt;
* While setters can be used to perform date arithmetic, LocalDate provides convenience functions for the most frequent cases of adding and subtracting a time period and finding the start of a week.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.prototype.add(time)&lt;br /&gt;
* Adds or subtracts a time period in local time. The results may be invalid if the end result ends up in the middle of a daylight savings switch.&lt;br /&gt;
* time Number - The time period to add, in milliseconds. Use negative values to subtract. See also Constants.&lt;br /&gt;
* Returns this - This object for chaining.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.prototype.addUTC(time)&lt;br /&gt;
* Adds or subtracts a physical time period, i.e. simply increments the timestamp.&lt;br /&gt;
time Number - The time period to add, in milliseconds. Use negative values to subtract. See also Constants.&lt;br /&gt;
* Returns this - This object for chaining.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.prototype.addMonths(months)&lt;br /&gt;
* Adds or subtracts a number of months in local time. The results may be invalid if the end result ends up in the middle of a daylight savings switch.&lt;br /&gt;
months Number - The number of months to add. Use negative values to subtract.&lt;br /&gt;
Returns this - This object for chaining.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.prototype.addYears(years)&lt;br /&gt;
* Adds or subtracts a number of years in local time. The results may be invalid if the end result ends up in the middle of a daylight savings switch.&lt;br /&gt;
years Number - The number of years to add. Use negative values to subtract.&lt;br /&gt;
* Returns this - This object for chaining.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.prototype.setStartOfWeek()&lt;br /&gt;
* Sets the date to the start of the same week as determined by locale.weekStart. The time is reset to midnight.&lt;br /&gt;
* Returns this - This object for chaining.&lt;br /&gt;
* Formatting functions implement the conversion of dates and intervals into localized strings which are suitable for direct presentation to the user.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.prototype.format(format)&lt;br /&gt;
* Formats the date according to the specified format flags or format string.&lt;br /&gt;
* format String or Number - Either a format string with the syntax of CLDR date format patterns, or one of the format flag constants. In the second case, the actual format string is localized for the current user's locale according to locale.formats. The default value is DATE_TIME.&lt;br /&gt;
* Returns String - This object formatted according to the specified format.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.prototype.getIntervalFormat(end, format)&lt;br /&gt;
* Returns a format string for a time interval with this object as the start and another LocalDate object as the end.&lt;br /&gt;
* end LocalDate - The end of the interval.&lt;br /&gt;
* format String or Number - Either a format string with the syntax of CLDR date format patterns, or one of the format flag constants. In the second case, the actual format string is localized for the current user's locale according to locale.intervals and locale.formats. The default value is DATE_TIME.&lt;br /&gt;
* Returns String - The format string for the interval accorging to the specified format.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.prototype.formatInterval(end, format)&lt;br /&gt;
* Formats an interval with this object as the start and another LocalDate object as the end.&lt;br /&gt;
end LocalDate - The end of the interval.&lt;br /&gt;
format String or Number - Either a format string with the syntax of CLDR date format patterns, or one of the format flag constants. In the second case, the actual format string is localized for the current user's locale according to locale.intervals and locale.formats. The default value is DATE_TIME.&lt;br /&gt;
* Returns String - The interval formatted accorging to the specified format.&lt;br /&gt;
* Finally, the JavaScript standard conversion functions allow easy debugging and arithmetic on timestamps.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.prototype.toString()&lt;br /&gt;
* Converts this object to a string using this.format(FULL_DATE).&lt;br /&gt;
* Returns String - The string representation of this object.&lt;br /&gt;
&lt;br /&gt;
   LocalDate.prototype.valueOf()&lt;br /&gt;
* Converts this object to a primitive value by returning the UTC timestamp. This can be used for arithmetic directly on LocalDate objects and as the single parameter to new LocalDate().&lt;br /&gt;
* Returns Timestamp - The UTC timestamp of this object.&lt;br /&gt;
&lt;br /&gt;
=== Constants ===&lt;br /&gt;
&lt;br /&gt;
All date classes operate on timestamps expressed as milliseconds since the UNIX epoch, 1970-01-01 00:00 UTC. The date module defines constants for common time intervals with a constant duration.&lt;br /&gt;
&lt;br /&gt;
   SECOND&lt;br /&gt;
Number of milliseconds in a second.&lt;br /&gt;
   MINUTE&lt;br /&gt;
Number of milliseconds in a minute.&lt;br /&gt;
   HOUR&lt;br /&gt;
Number of milliseconds in an hour.&lt;br /&gt;
   DAY&lt;br /&gt;
Number of milliseconds in a day.&lt;br /&gt;
   WEEK&lt;br /&gt;
Number of milliseconds in a week.&lt;br /&gt;
&lt;br /&gt;
Format flags for parsing and formatting functions are defined as constants. Multiple flags can be combined via addition or bitwise ORing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
DAYOFWEEK&lt;br /&gt;
Day of the week&lt;br /&gt;
DATE&lt;br /&gt;
Date&lt;br /&gt;
TIME&lt;br /&gt;
Time&lt;br /&gt;
TIMEZONE&lt;br /&gt;
Timezone&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Valid format flag combinations have dedicated constants. In a combination, DAYOFWEEK implies DATE and TIMEZONE implies TIME.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
DAYOFWEEK_DATE&lt;br /&gt;
DAYOFWEEK + DATE&lt;br /&gt;
DATE_TIME&lt;br /&gt;
DATE + TIME&lt;br /&gt;
DAYOFWEEK_DATE_TIME&lt;br /&gt;
DAYOFWEEK + DATE + TIME&lt;br /&gt;
TIME_TIMEZONE&lt;br /&gt;
TIME + TIMEZONE&lt;br /&gt;
DATE_TIME_TIMEZONE&lt;br /&gt;
DATE + TIME + TIMEZONE&lt;br /&gt;
FULL_DATE&lt;br /&gt;
DAYOFWEEK + DATE + TIME + TIMEZONE&lt;br /&gt;
Miscellaneous&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
See [[#Formatting|Formatting]] for usage of this function.&lt;br /&gt;
&lt;br /&gt;
   getFormat(format)&lt;br /&gt;
* Returns the localized format string for the specified format flags.&lt;br /&gt;
format String or Number - Either a format string with the syntax of CLDR date format patterns, or one of the format flag constants. In the second case, the actual format string is localized for the current user's locale according to locale.formats. The default value is DATE_TIME.&lt;br /&gt;
* Returns String - The localized format string which corresponds to the specified format flags. If format is a string, that string is returned unmodified.&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]][[Category:UI]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Browserdetection&amp;diff=21435</id>
		<title>AppSuite:Browserdetection</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Browserdetection&amp;diff=21435"/>
		<updated>2016-02-10T12:48:55Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Browser detection outside AppSuite ==&lt;br /&gt;
[[File:loginpage.png|thumb|250px|This warning is not shown to a user if you use the form-login]]&lt;br /&gt;
&lt;br /&gt;
AppSuite detects the client browser and collects some information about the current device the visitor is using. This information is used to serve the appropriate UI and enable/disable certain features for the visitor's device. &lt;br /&gt;
&lt;br /&gt;
The browser detection is done by a standalone, dependency free lib that can be included via script-tag to other sites. The original AppSuite login page performs this browser detection. But if you use a form-login and jump directly into AppSuite, the user will miss the warning where AppSuite statesthat an unsupported browser is used.&lt;br /&gt;
&lt;br /&gt;
To show the same warning to a user without using the original AppSuite login page, you should include the browser detection lib in your own login page. That way you can show a warning to the user if they do not use a supported browser.&lt;br /&gt;
&lt;br /&gt;
=== Including the browser.js lib ===&lt;br /&gt;
&lt;br /&gt;
You can easily include the browser detection via script-tag in your own login page. It's a small, dependency free piece of Javascript code which adds a function to the global scope called &amp;lt;pre&amp;gt;isBrowserSupported()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The lib is located in the AppSuite UI under &amp;lt;tt&amp;gt;http://somedomain.com/appsuite/src/browser.js&amp;lt;/tt&amp;gt;. Add this script tag to your page head &amp;lt;pre&amp;gt;&amp;lt;script src=&amp;quot;http://somedomain.com/appsuite/src/browser.js&amp;quot; type=&amp;quot;text/javascript&amp;quot; charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In your page &amp;lt;tt&amp;gt;onLoad&amp;lt;/tt&amp;gt; you can then call the global function &amp;lt;tt&amp;gt;isBrowserSupported&amp;lt;/tt&amp;gt; which returns a boolean. If &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; is returned by the function you should show a warning to the user that his browser is not supported.&lt;br /&gt;
&lt;br /&gt;
[[Category:Custom development]]&lt;br /&gt;
[[Category:Administrator]]&lt;br /&gt;
[[Category:Developer]]&lt;br /&gt;
&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:A11y&amp;diff=21434</id>
		<title>AppSuite:A11y</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:A11y&amp;diff=21434"/>
		<updated>2016-02-10T12:48:06Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Accessibility is specific to the application you are developing. Here are resources that the Open-Xchange developers use for this topic:&lt;br /&gt;
&lt;br /&gt;
* &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Developer]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Modifying_forms_by_using_extension_points&amp;diff=21433</id>
		<title>AppSuite:Modifying forms by using extension points</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Modifying_forms_by_using_extension_points&amp;diff=21433"/>
		<updated>2016-02-10T12:46:47Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Extension points:&amp;lt;br&amp;gt;Modifying forms&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Abstract'''&lt;br /&gt;
&lt;br /&gt;
''This articles covers how to apply different changes to the contact form via modifying its extensionpoints and extensions.''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
==Show available extension points==&lt;br /&gt;
&lt;br /&gt;
The edit form of the contacts app is constructed by a set of extension points. Each extension point controls a single aspect of the form. To apply modifications, the id of the point and the extension is needed. For a quick overview of the available points and extensions you can use the browser console:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
// show all available extension points (across all apps)&lt;br /&gt;
require('io.ox/core/extensions').keys();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
// you can filter down the list by using regular expression &lt;br /&gt;
_(require('io.ox/core/extensions').keys()).filter(function (point) {&lt;br /&gt;
    if (/io.ox\/contacts\/edit/.test(point)) {&lt;br /&gt;
        return point;&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
// show all available extensions of a known extension point&lt;br /&gt;
require('io.ox/core/extensions').point('io.ox/contacts/edit/personal').all();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Modify extension points==&lt;br /&gt;
&lt;br /&gt;
As described in [[ AppSuite:Extending_the_UI_(Hands-on_introduction) | Hands-on_introduction]] extension points can be modified in multiple aspects:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
// disable the display_name field&lt;br /&gt;
require('io.ox/core/extensions').point('io.ox/contacts/edit/personal').disable('display_name');&lt;br /&gt;
&lt;br /&gt;
// reenable the display_name field&lt;br /&gt;
require('io.ox/core/extensions').point('io.ox/contacts/edit/personal').enable('display_name');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
// replace the display_name field by &amp;quot;Hallo World!&amp;quot;&lt;br /&gt;
require('io.ox/core/extensions').point('io.ox/contacts/edit/personal')&lt;br /&gt;
.replace({&lt;br /&gt;
    id: &amp;quot;display_name&amp;quot;,&lt;br /&gt;
    draw: function () {&lt;br /&gt;
        this.append(&lt;br /&gt;
            $(&amp;quot;&amp;lt;div&amp;gt;&amp;quot;).addClass(&amp;quot;title&amp;quot;).text(&amp;quot;Hello World!&amp;quot;)&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
// modify the index of the display_name field to bring it on top&lt;br /&gt;
require('io.ox/core/extensions').point('io.ox/contacts/edit/personal')&lt;br /&gt;
.replace({&lt;br /&gt;
    id:&amp;quot;display_name&amp;quot;,&lt;br /&gt;
    index: 50&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
// modify the hidden status to hide the display_name field via get() as alternative way&lt;br /&gt;
require('io.ox/core/extensions').point('io.ox/contacts/edit/personal')&lt;br /&gt;
.get('display_name', function (extension) {&lt;br /&gt;
    extension.hidden = true;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Extending the form validation via extension points==&lt;br /&gt;
&lt;br /&gt;
In addition to the default validation, another validation step can be implemented by extending the proper extension point:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
// extend the validation of the display_name field&lt;br /&gt;
require('io.ox/core/extensions')&lt;br /&gt;
.point('io.ox/contacts/model/validation/display_name')&lt;br /&gt;
.extend({&lt;br /&gt;
    id: 'check_for_klaus',&lt;br /&gt;
    validate: function (value) {&lt;br /&gt;
        if (value !== 'Klaus') {&lt;br /&gt;
            return 'The display name has to be Klaus';&lt;br /&gt;
       	}&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Extension points]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:I18n&amp;diff=21432</id>
		<title>AppSuite:I18n</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:I18n&amp;diff=21432"/>
		<updated>2016-02-10T12:39:22Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Internationalization (i18n)&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
Providing software for users in the whole world means providing it in multiple languages. This consists of two steps:&lt;br /&gt;
&lt;br /&gt;
* ''Internationalization (i18n)'' - Making the software international, i. e. preparing it to be adapted to different languages and locations.&lt;br /&gt;
* ''Localization (L10n)'' - Adapting the software to a particular language and/or location.&lt;br /&gt;
The Open-Xchange platform offers several facilities to simplify both parts. The L10n part is mostly a concern for translators. Open-Xchange facilities for that consist mainly of using a well-established format for translations: [http://www.gnu.org/s/gettext/ GNU Gettext] Portable Object (PO) files. This allows translators to use existing dedicated translation tools or a simple UTF-8-capable text editor.&lt;br /&gt;
&lt;br /&gt;
The i18n part is what software developers will be mostly dealing with and is what the rest of this document describes.&lt;br /&gt;
&lt;br /&gt;
==Translation==&lt;br /&gt;
&lt;br /&gt;
The main part of i18n is the translation of various text strings which are presented to the user. For this purpose, the Open-Xchange platform provides the RequireJS plugin &amp;lt;tt&amp;gt;'gettext'&amp;lt;/tt&amp;gt;. Individual translation files are specified as a module dependency of the form &amp;lt;tt&amp;gt;'gettext!module_name'&amp;lt;/tt&amp;gt;. The resulting module API is a function which can be called to translate a string using the specified translation files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
define('com.example/example', ['gettext!com.example/example'],&lt;br /&gt;
  function (gt) {&lt;br /&gt;
    'use strict';&lt;br /&gt;
    alert(gt('Hello, world!'));&lt;br /&gt;
  });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A file named ox.pot is created by the &amp;lt;tt&amp;gt;ox.pot&amp;lt;/tt&amp;gt; (&amp;lt; 7.6.0) or &amp;lt;tt&amp;gt;create_pot&amp;lt;/tt&amp;gt; (&amp;gt;= 7.6.0) tasks of the build system. So for versions &amp;lt; 7.6.0 you will need to run:&lt;br /&gt;
&lt;br /&gt;
 $ build-appsuite ox.pot&lt;br /&gt;
&lt;br /&gt;
to create the file in the apps root directory.&lt;br /&gt;
&lt;br /&gt;
Working with a version starting from 7.6.0, the command:&lt;br /&gt;
&lt;br /&gt;
 $ grunt create_pot&lt;br /&gt;
&lt;br /&gt;
will generate this file within the &amp;lt;tt&amp;gt;i18n/&amp;lt;/tt&amp;gt; directory. It will contain an entry for every call to one of &amp;lt;tt&amp;gt;gettext&amp;lt;/tt&amp;gt; functions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-gettext&amp;quot;&amp;gt;&lt;br /&gt;
#: apps/com.example/example.js:4&lt;br /&gt;
msgid &amp;quot;Hello, world!&amp;quot;&lt;br /&gt;
msgstr &amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This file can be sent to the translators and during the translation process, one PO file for each language will be created. The PO files in the directory &amp;lt;tt&amp;gt;i18n&amp;lt;/tt&amp;gt; shoud contain the translated entry:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-gettext&amp;quot;&amp;gt;&lt;br /&gt;
#: apps/com.example/example.js:4&lt;br /&gt;
msgid &amp;quot;Hello, world!&amp;quot;&lt;br /&gt;
msgstr &amp;quot;Hallo, Welt!&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
During the next build, the entries are copied from the central PO files into individual translation files. In our example, this would be &amp;lt;tt&amp;gt;apps/com.example/example.de_DE.js&amp;lt;/tt&amp;gt;. Because of the added language ID, translation files can usually have the same name as the corresponding main module. Multiple related modules should use the same translation file to avoid the overhead of too many small translation files.&lt;br /&gt;
&lt;br /&gt;
Most modules will require more complex translations than can be provided by a simple string lookup. To handle some of these cases, the &amp;lt;tt&amp;gt;gettext&amp;lt;/tt&amp;gt; module provides traditional methods in addition to being a callable function. Other cases are handled by the build system.&lt;br /&gt;
&lt;br /&gt;
==Composite Phrases==&lt;br /&gt;
&lt;br /&gt;
In most cases, the translated texts will not be static, but contain dynamic values as parts of a sentence. The straight-forward approach of translating parts of the sentence individually and then using string concatenation to compose the final text is a BAD idea. Different languages have different sentence structures, and if there are multiple dynamic values, their order might need to be reversed in some languages, and not reversed in others.&lt;br /&gt;
&lt;br /&gt;
The solution is to translate entire sentences and then to use the &amp;lt;tt&amp;gt;gettext&amp;lt;/tt&amp;gt; function to insert dynamic values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
alert(&lt;br /&gt;
  //#. %1$s is the given name&lt;br /&gt;
  //#. %2$s is the family name&lt;br /&gt;
  //#, c-format&lt;br /&gt;
  gt('Welcome, %1$s %2$s!', firstName, lastName));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Results in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-gettext&amp;quot;&amp;gt;&lt;br /&gt;
#. %1$s is the given name&lt;br /&gt;
#. %2$s is the family name&lt;br /&gt;
#, c-format&lt;br /&gt;
msgid &amp;quot;Welcome, %1$s, %2$s&amp;quot;&lt;br /&gt;
msgstr &amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As shown in the example, it is possible to add comments for translators by starting a comment with &amp;lt;tt&amp;gt;&amp;quot;#.&amp;quot;&amp;lt;/tt&amp;gt;. Such comments must be placed immediately before the name of the variable which refers to the &amp;lt;tt&amp;gt;gettext&amp;lt;/tt&amp;gt; module (in this case &amp;lt;tt&amp;gt;gt&amp;lt;/tt&amp;gt;). They can be separated by arbitrary whitespace and newlines, but not other tokens. All such &amp;lt;tt&amp;gt;gettext&amp;lt;/tt&amp;gt; calls should have comments explaining every format specifier.&lt;br /&gt;
&lt;br /&gt;
Comments starting with &amp;lt;tt&amp;gt;&amp;quot;#,&amp;quot;&amp;lt;/tt&amp;gt; are meant for &amp;lt;tt&amp;gt;gettext&amp;lt;/tt&amp;gt; tools, which in the case of &amp;lt;tt&amp;gt;&amp;quot;#, c-format&amp;quot;&amp;lt;/tt&amp;gt;, can ensure that the translator did not leave out or mistype any of the format specifiers.&lt;br /&gt;
&lt;br /&gt;
For the cases when the format string must be translated by one of the functions described below, there is a dedicated format function &amp;lt;tt&amp;gt;gettext.format&amp;lt;/tt&amp;gt; which, except for debugging, is an alias for &amp;lt;tt&amp;gt;_.printf&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Debugging==&lt;br /&gt;
&lt;br /&gt;
One of the most common i18n errors is forgetting to use a &amp;lt;tt&amp;gt;gettext&amp;lt;/tt&amp;gt; function. To catch this kind of error, the UI can be started with the hash parameter &amp;lt;tt&amp;gt;&amp;quot;#debug-i18n=1&amp;quot;&amp;lt;/tt&amp;gt;. (Reloading of the browser tab is usually required for the setting to take effect.)&lt;br /&gt;
&lt;br /&gt;
In this mode, every translated string is marked with invisible Unicode characters, and any DOM text without those characters gets reported on the console. The &amp;lt;tt&amp;gt;gettext.format&amp;lt;/tt&amp;gt; function then also checks that every parameter is translated. This is the reason why &amp;lt;tt&amp;gt;_.printf&amp;lt;/tt&amp;gt; should not be used for user-visible strings directly.&lt;br /&gt;
&lt;br /&gt;
Unfortunately, this method will also report any string which does not actually require translation. Examples of such strings include user data, numbers, strings already translated by the server, etc. To avoid filling the console with such false positives, every such string must be marked by passing it through the function &amp;lt;tt&amp;gt;gettext.noI18n&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
alert(&lt;br /&gt;
  //#. %1$s is the given name&lt;br /&gt;
  //#. %2$s is the family name&lt;br /&gt;
  //#, c-format&lt;br /&gt;
  gt('Welcome, %1$s %2$s!', gt.noI18n(firstName), gt.noI18n(lastName)));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This results in the strings being marked as 'translated' without actually changing their visible contents. When not debugging, &amp;lt;tt&amp;gt;gettext.noI18n&amp;lt;/tt&amp;gt; simply returns its first parameter.&lt;br /&gt;
&lt;br /&gt;
==Advanced gettext Functions==&lt;br /&gt;
&lt;br /&gt;
Besides &amp;lt;tt&amp;gt;gettext.format&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;gettext.noI18n&amp;lt;/tt&amp;gt; there are several other functions which are required to cover all typical translation scenarios.&lt;br /&gt;
&lt;br /&gt;
===Contexts===&lt;br /&gt;
&lt;br /&gt;
Sometimes, the same English word or phrase has multiple meanings and must be translated differently depending on context. To be able to tell the individual translations apart, the method &amp;lt;tt&amp;gt;gettext.pgettext&amp;lt;/tt&amp;gt; ('p' stands for 'particular') should be used instead of calling &amp;lt;tt&amp;gt;gettext&amp;lt;/tt&amp;gt; directly. It takes the context as the first parameter and the text to translate as the second parameter:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
alert(gt.pgettext('description', 'Title'));&lt;br /&gt;
alert(gt.pgettext('salutation', 'Title'));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Results in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-gettext&amp;quot;&amp;gt;&lt;br /&gt;
msctxt &amp;quot;description&amp;quot;&lt;br /&gt;
msgid &amp;quot;Title&amp;quot;&lt;br /&gt;
msgstr &amp;quot;Beschreibung&amp;quot;&lt;br /&gt;
&lt;br /&gt;
msctxt &amp;quot;salutation&amp;quot;&lt;br /&gt;
msgid &amp;quot;Title&amp;quot;&lt;br /&gt;
msgstr &amp;quot;Anrede&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Plural forms===&lt;br /&gt;
&lt;br /&gt;
In the case of numbers, the rules to select the proper plural form can be very complex. With the exception of languages with no separate plural forms, English is the second simplest language in this respect, having only two plural forms: singular and plural. Other languages can have up to four forms, and theoretically even more. The functions &amp;lt;tt&amp;gt;gettext.ngettext&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;gettext.npgettext&amp;lt;/tt&amp;gt; (for a combination of plural forms with contexts) can select the proper plural form by using a piece of executable code embedded in the header of a PO file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
alert(gt.format(&lt;br /&gt;
  //#. %1$d is the number of mails&lt;br /&gt;
  //#, c-format&lt;br /&gt;
  gt.ngettext('You have %1$d mail', 'You have %1$d mails', n),&lt;br /&gt;
  gt.noI18n(n)));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The function &amp;lt;tt&amp;gt;gettext.ngettext&amp;lt;/tt&amp;gt; accepts three parameters: the English singular and plural forms and the number which determines the chosen plural form. The function &amp;lt;tt&amp;gt;gettext.npgettext&amp;lt;/tt&amp;gt; adds a context parameter before the others, similar to &amp;lt;tt&amp;gt;gettext.pgettext&amp;lt;/tt&amp;gt;. They are usually used in combination with &amp;lt;tt&amp;gt;gettext.format&amp;lt;/tt&amp;gt; to insert the actual number into the translated string.&lt;br /&gt;
&lt;br /&gt;
The above example results in the following entry:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-gettext&amp;quot;&amp;gt;&lt;br /&gt;
#. %1$d is the number of mails&lt;br /&gt;
#, c-format&lt;br /&gt;
msgid &amp;quot;You have %1$d mail&amp;quot;&lt;br /&gt;
msgid_plural &amp;quot;You have %1$d mails&amp;quot;&lt;br /&gt;
msgstr[0] &amp;quot;&amp;quot;&lt;br /&gt;
msgstr[1] &amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The number of &amp;lt;tt&amp;gt;msgstr[N]&amp;lt;/tt&amp;gt; lines is determined by the number of plural forms in each language. This number is specified in the header of each PO file, together with the code to compute the index of the correct plural form the supplied number.&lt;br /&gt;
&lt;br /&gt;
==Custom Translations==&lt;br /&gt;
App Suite has support for custom translations. ''gettext'' provides a function ''addTranslation(dictionary, key, value)'' that allows adding custom translation to a global dictionary (&amp;quot;*&amp;quot;) as well as replacing translations in existing dictionaries. In order to use this, you have to create a plugin for the &amp;quot;core&amp;quot; namespace. With release 7.8.0, there is a special namespace for exactly this use-case. It is now possible to define plugins to be loaded for the &amp;quot;i18n&amp;quot; namespace, which is loaded right before gettext is being enabled and after that the core plugins will be loaded.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require(['io.ox/core/gettext'], function (gt) {&lt;br /&gt;
    gt.addTranslation('*', 'Existing translation', 'My custom translation');&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To keep it simple you can use the internal escaping to build counterparts for ngettext, pgettext, as well as npgettext. A context is escaped by \x00. Singular and plural are separated by \x01:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
// load gettext&lt;br /&gt;
var gt = require('io.ox/core/gettext');&lt;br /&gt;
&lt;br /&gt;
// replace translation in dictionary 'io.ox/core'. Apps use context &amp;quot;app&amp;quot;.&lt;br /&gt;
gt.addTranslation('io.ox/core', 'app\x00Address Book', 'My Contacts');&lt;br /&gt;
&lt;br /&gt;
// replace translation - counterpart to ngettext&lt;br /&gt;
gt.addTranslation('io.ox/mail', 'Copy\x01Copies', { 0: 'Kopie', 1: 'Kopien' });&lt;br /&gt;
&lt;br /&gt;
// replace translation - counterpart to npgettext&lt;br /&gt;
gt.addTranslation('io.ox/mail', 'test\x00One\x01Two', { 0: 'Eins', 1: 'Zwei' });&lt;br /&gt;
&lt;br /&gt;
// some checks&lt;br /&gt;
require(['gettext!io.ox/mail'], function (gt) {&lt;br /&gt;
&lt;br /&gt;
    console.log('Sigular:', gt.ngettext('Copy', 'Copies', 1));&lt;br /&gt;
    console.log('Plural:', gt.ngettext('Copy', 'Copies', 2));&lt;br /&gt;
&lt;br /&gt;
    console.log('Without context', gt.gettext('Files')); // should be 'My File Box'&lt;br /&gt;
    console.log('With context', gt.pgettext('test', 'Files')); // should be 'Files'&lt;br /&gt;
&lt;br /&gt;
    console.log('Plural with context', gt.npgettext('test', 'One', 'Two', 2)); // should be 'Two'&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''addTranslation()'' always works for current language. If you want to benefit from automatic POT file generation, use the following approach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
define('plugins/example/register', ['io.ox/core/gettext', 'gettext!custom'], function (gettext, gt) {&lt;br /&gt;
&lt;br /&gt;
    'use strict';&lt;br /&gt;
&lt;br /&gt;
    /* Just list all custom translations in your plugin by standalone gt() calls.&lt;br /&gt;
       The build system recognizes these strings and collects them in a POT file, &lt;br /&gt;
       so that they can be subject to translation processes. At runtime, gt('...')&lt;br /&gt;
       returns translations for current language.&lt;br /&gt;
    */&lt;br /&gt;
    if (false) {&lt;br /&gt;
        gt('Tree');&lt;br /&gt;
        gt('House');&lt;br /&gt;
        gt('Dog');&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // get internal dictionary (for current language of course)&lt;br /&gt;
    var dictionary = gt.getDictionary();&lt;br /&gt;
&lt;br /&gt;
    // add all translations to global dictionary. Done!&lt;br /&gt;
    gettext.addTranslation('*', dictionary);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Developer]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extending_the_UI_(Hands-on_introduction)&amp;diff=21428</id>
		<title>AppSuite:Extending the UI (Hands-on introduction)</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extending_the_UI_(Hands-on_introduction)&amp;diff=21428"/>
		<updated>2016-02-10T11:54:43Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Extension points:&amp;lt;br&amp;gt;Hands-on introduction&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Abstract'''&lt;br /&gt;
&lt;br /&gt;
''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. This is a hands-on introduction based on simple code examples. For more details take a look at  [[ AppSuite:Extending_the_UI| Extending the UI]].''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
==Extending the UI==&lt;br /&gt;
In case you want try code by your own please follow these steps:&lt;br /&gt;
* get a appropriate browser to execute custom javascript during runtime  (for example chrome with it's [https://developers.google.com/chrome-developer-tools/docs/console developer tools])&lt;br /&gt;
* login on OX App Suite&lt;br /&gt;
* copy code from beyond to you console and execute&lt;br /&gt;
* perhaps it could be necessary to switch app or reload the page&lt;br /&gt;
&lt;br /&gt;
=== Your first extension ===&lt;br /&gt;
Let's start with some example code.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; // The extension point system lives in the&lt;br /&gt;
 // 'io.ox/core/extensions' module&lt;br /&gt;
 require([&amp;quot;io.ox/core/extensions&amp;quot;], function (ext) {&lt;br /&gt;
  &lt;br /&gt;
     // An extension point represents the system that can be extended&lt;br /&gt;
     // It has an id (in this case 'io.ox/mail/detail')&lt;br /&gt;
     var point = ext.point(&amp;quot;io.ox/mail/detail&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
     // Now, let's extend that extension point. Every extension point&lt;br /&gt;
     // has some kind of contract about what it expects its extensions to provide.&lt;br /&gt;
     // In this case, the extension is supposed to provide a draw method&lt;br /&gt;
     // and is passed the node to draw into as the 'this' variable&lt;br /&gt;
     point.extend({&lt;br /&gt;
         id: 'example1', // Every extension is supposed to have an id&lt;br /&gt;
         index: 1, // Extensions are ordered based on their indexes&lt;br /&gt;
         draw: function () {&lt;br /&gt;
             // This function is called by the part of the code that&lt;br /&gt;
             // offers this extension point&lt;br /&gt;
             this.append($(&amp;quot;&amp;lt;h3&amp;gt;&amp;quot;).text(&amp;quot;Hello, Traveller!&amp;quot;));&lt;br /&gt;
         }&lt;br /&gt;
     });&lt;br /&gt;
 });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Please have a look at the detail view of a mail now. Perhaps you have to select another mail to see the difference.&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Ui_ext_handson_1_1.png|step 1: default mail detail &lt;br /&gt;
File:Ui_ext_handson_1_2.png|step 2: execute code in chrome dev tools console&lt;br /&gt;
File:Ui_ext_handson_1_3.png|step 3: extended mail detail&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
=== Mail app get's a new action ===&lt;br /&gt;
Like this point in mail, many parts of the UI offer extension points. Let's extend the OX App Suite UI in a few other places.  First, let us add a button to the mail app toolbar.&lt;br /&gt;
&lt;br /&gt;
'''Attention''' This example will only work if you haven't visited the mail app yet. If you have, just reload this page, then run the example and then visit the mail app.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
require(['io.ox/core/extensions', 'io.ox/core/extPatterns/links'], function (ext, links) {&lt;br /&gt;
&lt;br /&gt;
    // Firstly we'll add a new button to the toolbar&lt;br /&gt;
&lt;br /&gt;
    // This addas a new button to the toolbar and offers itself an extension point 'io.ox/mail/actions/exampleAction', which&lt;br /&gt;
    // can be extended to contain the functionality for this button&lt;br /&gt;
    // This way, more than one button or link can reference the same&lt;br /&gt;
    // action&lt;br /&gt;
    ext.point('io.ox/mail/links/toolbar').extend(new links.Link({&lt;br /&gt;
        index: 200,&lt;br /&gt;
        id: 'example',&lt;br /&gt;
        label: 'Do something',&lt;br /&gt;
        ref: 'io.ox/mail/actions/exampleAction'&lt;br /&gt;
    }));&lt;br /&gt;
&lt;br /&gt;
    // This defines the extension for the above newly created extension point&lt;br /&gt;
    // and contains the code to run, once the button is clicked&lt;br /&gt;
    new links.Action('io.ox/mail/actions/exampleAction', {&lt;br /&gt;
        id: 'exampleAction',&lt;br /&gt;
        action: function () {&lt;br /&gt;
            alert(&amp;quot;Hello, traveller!&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
=== A qualified mail footer  ===&lt;br /&gt;
Let's try and add a section to the mails detail view and use some data of the currently viewed mail. For information about the baton object please take a look at the [[ AppSuite:Extending_the_UI| more detailed]] article how to extend the UI.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
require(['io.ox/core/extensions'], function (ext) {&lt;br /&gt;
&lt;br /&gt;
    ext.point(&amp;quot;io.ox/mail/detail&amp;quot;).extend({&lt;br /&gt;
        id: &amp;quot;lessonExample&amp;quot;,&lt;br /&gt;
        index: 300,&lt;br /&gt;
        draw: function (baton) {&lt;br /&gt;
            var data = baton.data;&lt;br /&gt;
            this.append(&amp;quot;&amp;lt;br&amp;gt;&amp;quot;);&lt;br /&gt;
            this.append($(&amp;quot;&amp;lt;em&amp;gt;&amp;quot;).append(&lt;br /&gt;
                $.txt(&amp;quot;The eMail '&amp;quot;),&lt;br /&gt;
                $(&amp;quot;&amp;lt;b&amp;gt;&amp;quot;).text(data.subject),&lt;br /&gt;
                $.txt(&amp;quot;' was brought to you by: &amp;quot;),&lt;br /&gt;
                $(&amp;quot;&amp;lt;b&amp;gt;&amp;quot;).text(&amp;quot;Your Name Inc.&amp;quot;)&lt;br /&gt;
            ));&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Ui_ext_handson_3_1.png|step 1: default mail detail &lt;br /&gt;
File:Ui_ext_handson_3_2.png|step 2: execute code in chrome dev tools console&lt;br /&gt;
File:Ui_ext_handson_3_3.png|step 3: extended mail detail&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After switching to the mail app again, you might have to select another mail to once again run through the rendering process that calls on the extensions. As you can see in the example above, the code calling the extension passes along the eMail in the ''data'' attribute of the [[AppSuite:Extending_the_UI#Baton | baton]] parameter. The ''index'' of the extension means that it is rendered after the mail body, who's extensions index is '''300'''. Currently (until we have more comprehensive documentation) you can only find the indexes (and the way an extension is supposed to behave) by reading our code. Reload the page (to clear out the registered extensions) and try switching the index to '''190''' and see where the added sentence shows up now.&lt;br /&gt;
&lt;br /&gt;
==Customizing the UI==&lt;br /&gt;
Since extensions are a property of the runtime system, you can also modify them. The extension system offers a couple of things you can do with existing extensions like '''changing their order, disabling them or replacing them.''' Let's look at how to accomplish all of these, again by modifying the mail detail view. &lt;br /&gt;
&lt;br /&gt;
===switch off inline links===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
require([&amp;quot;io.ox/core/extensions&amp;quot;], function (ext) {&lt;br /&gt;
    // Here the id of the extension comes into play again.&lt;br /&gt;
    // If you look at the code of the mail detail view (in io.ox/mail/view-detail)&lt;br /&gt;
    // You can see the extension registers itself with the id 'inline-links'&lt;br /&gt;
    // So that is the argument we pass to disable&lt;br /&gt;
    ext.point(&amp;quot;io.ox/mail/detail&amp;quot;).disable('inline-links');&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Ui_ext_handson_4_1.png|step 1: default mail detail &lt;br /&gt;
File:Ui_ext_handson_4_2.png|step 2: execute code in chrome dev tools console&lt;br /&gt;
File:Ui_ext_handson_4_3.png|step 3: mail detail with disabled inline links&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Again: When navigating back to the email view you might have to select another mail to make this change visible.''&lt;br /&gt;
&lt;br /&gt;
===replace the way time is rendered===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
require([&amp;quot;io.ox/core/extensions&amp;quot;, &amp;quot;io.ox/mail/util&amp;quot;, &amp;quot;io.ox/core/date&amp;quot;], function (ext, util, date) {&lt;br /&gt;
    ext.point(&amp;quot;io.ox/mail/detail/header&amp;quot;).replace({&lt;br /&gt;
        //current extension will extended not fully replaced&lt;br /&gt;
        // so we do not have to specify the index to keep time on it's place &lt;br /&gt;
        id: 'receiveddate', // The extension we want to replace has this id as well&lt;br /&gt;
        draw: function (baton) {&lt;br /&gt;
            var data = baton.data;&lt;br /&gt;
            //show unix timestamp (plus trailing '000' for milliseconds )&lt;br /&gt;
            var timeToRender = (data.received_date || data.sent_date || 0);&lt;br /&gt;
            this.append(&lt;br /&gt;
                $('&amp;lt;div&amp;gt;').addClass('date list').text(timeToRender)&lt;br /&gt;
            );&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Ui_ext_handson_5_1.png|step 1: default mail detail &lt;br /&gt;
File:Ui_ext_handson_5_2.png|step 2: execute code in chrome dev tools console&lt;br /&gt;
File:Ui_ext_handson_5_3.png|step 3: extended mail detail with unix timestamp&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== change order ===&lt;br /&gt;
And now let's switch the order around:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
require([&amp;quot;io.ox/core/extensions&amp;quot;], function (ext) {&lt;br /&gt;
    // From the extension point 'io.ox/mail/detail' get the extension with&lt;br /&gt;
    // the id 'subject' (which is passed to the callback)&lt;br /&gt;
    ext.point(&amp;quot;io.ox/mail/detail/header&amp;quot;).get(&amp;quot;subject&amp;quot;, function (extension) {&lt;br /&gt;
        // Put it last&lt;br /&gt;
        extension.index = 300;&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Ui_ext_handson_6_1.png|step 1: default mail detail &lt;br /&gt;
File:Ui_ext_handson_6_2.png|step 2: execute code in chrome dev tools console&lt;br /&gt;
File:Ui_ext_handson_6_3.png|step 3: pushed subject to header bottom &lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Extension points]]&lt;br /&gt;
&lt;br /&gt;
== Trouble finding the right extension point? ==&lt;br /&gt;
&lt;br /&gt;
You don't know where to append your plugin's content / which extension point you should use?&lt;br /&gt;
A good way to find the suitable extension point, is to search in the OX Source Code for an HTML-element displayed in the user interface. For example an easy to identify text. Once found the HTML-element in the source, you can see which extension point was used appending this element to the UI and append your content there aswell.&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extending_the_UI_(best_practices)&amp;diff=21427</id>
		<title>AppSuite:Extending the UI (best practices)</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extending_the_UI_(best_practices)&amp;diff=21427"/>
		<updated>2016-02-10T11:54:21Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Extension points:&amp;lt;br&amp;gt; Best practices&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Abstract'''&lt;br /&gt;
&lt;br /&gt;
''The extension points system allows different strategies to realize the desired behavior. This is a list of solutions for common scenarios pointing out also some disadvantages of different solutions.''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
=== scenario: writing extension in general ===&lt;br /&gt;
&lt;br /&gt;
''separate extension declaration and logic for reusability''&lt;br /&gt;
&lt;br /&gt;
* example: io.ox/mail/common-extensions.js and io.ox/mail/detail/view.js&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    var fnProcess = function (baton) { ... };&lt;br /&gt;
    // use function within some extension&lt;br /&gt;
    ext.point('io.ox/mail/detail/content').extend({&lt;br /&gt;
        id: 'links',&lt;br /&gt;
        index: 100,&lt;br /&gt;
        process: fnProcess&lt;br /&gt;
    });&lt;br /&gt;
    // use function within another extension&lt;br /&gt;
    ext.point('io.ox/mail/detail/content').extend({&lt;br /&gt;
        id: 'images',&lt;br /&gt;
        index: 100,&lt;br /&gt;
        process: fnProcess&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== scenario: hide/show action ===&lt;br /&gt;
&lt;br /&gt;
''simply register a fresh new extension with an index less than the original action extension.''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    // original extension&lt;br /&gt;
    new Action('io.ox/mail/actions/compose', {&lt;br /&gt;
        id: 'compose',&lt;br /&gt;
        index: 100,&lt;br /&gt;
        requires: function () {&lt;br /&gt;
            return true;&lt;br /&gt;
        },&lt;br /&gt;
        ...&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // your extension to hide it&lt;br /&gt;
    ext.point('io.ox/mail/actions/compose').extend({&lt;br /&gt;
        // must be a different id than used in original extension&lt;br /&gt;
        id: 'compose_preprocess',&lt;br /&gt;
        // important: must be smaller than the one used in original extension that is usually 'default'&lt;br /&gt;
        index: 50,&lt;br /&gt;
        requires: function (e) {&lt;br /&gt;
            // is your condition meet?&lt;br /&gt;
            if (myCondition === true) {&lt;br /&gt;
                // stop propagation to suppress execution of&lt;br /&gt;
                // requires-handlers of extensions with a higher index&lt;br /&gt;
                e.stopPropagation();&lt;br /&gt;
                // force hide (or force show by using 'return true')&lt;br /&gt;
                return false;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''unfavorable variants'''&lt;br /&gt;
&lt;br /&gt;
''using ext.point(...).replace''&lt;br /&gt;
&lt;br /&gt;
* original requires function is replaced and can not be accessed anymore&lt;br /&gt;
* also copy and paste it contents does not help cause future changes are not carried over automatically&lt;br /&gt;
* see [[AppSuite:Extending_the_UI#replace_extension | documentation for .replace ]]&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    // please do not use replace to overwrite 'requires'&lt;br /&gt;
    ext.point('io.ox/mail/actions/compose').replace({ &lt;br /&gt;
        requires: function () {&lt;br /&gt;
            // my custom logic&lt;br /&gt;
        } &lt;br /&gt;
    })&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== scenario: stop propagation of action ===&lt;br /&gt;
&lt;br /&gt;
''simply register a fresh new extension with an index less than the original action extension.''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    // original extension&lt;br /&gt;
    new Action('io.ox/mail/actions/compose', {&lt;br /&gt;
        index: 100,&lt;br /&gt;
        requires: function () {&lt;br /&gt;
            return true;&lt;br /&gt;
        },&lt;br /&gt;
        ...&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // your extension to hide it&lt;br /&gt;
    ext.point('io.ox/mail/actions/compose').extend({&lt;br /&gt;
        // must be a different id than used in original extension that is usually 'default'&lt;br /&gt;
        id: 'compose_preprocess',&lt;br /&gt;
        // important: must be smaller than the one used in original extension&lt;br /&gt;
        index: 50,&lt;br /&gt;
        action: function (baton) {&lt;br /&gt;
            // be robust when clicked again and conditions may have changed&lt;br /&gt;
            baton.enable('default');&lt;br /&gt;
            if (myCondition === true) {&lt;br /&gt;
                baton.disable('default');&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
'''hint'''&lt;br /&gt;
In case the condition for a single action target (f.e. a mail item) do not change you can use 'baton.preventDefault()' alternatively when you condition is met.&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Extension points]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extending_the_UI&amp;diff=21424</id>
		<title>AppSuite:Extending the UI</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extending_the_UI&amp;diff=21424"/>
		<updated>2016-02-10T10:16:59Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Extension points:&amp;lt;br&amp;gt;General information &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Abstract.''' 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.&lt;br /&gt;
__TOC__&lt;br /&gt;
== Introduction ==&lt;br /&gt;
=== Inheritance vs. Extension points ===&lt;br /&gt;
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).&lt;br /&gt;
&lt;br /&gt;
The illustrated example compares inheritance and extension points. The main benefit of using extension points is that the programm is still the active component and it's in controll. This leads to the following advantages:&lt;br /&gt;
* Reduced coupling&lt;br /&gt;
* Increased cohesion&lt;br /&gt;
* Modularity- Re-usability&lt;br /&gt;
* Dynamic&lt;br /&gt;
&lt;br /&gt;
[[File:Ui_ext_01.gif|frame|border|left]]&lt;br /&gt;
&lt;br /&gt;
=== Some characteristics ===&lt;br /&gt;
* good fences: extension points unregister corrupt extenders&lt;br /&gt;
* lazy loading: extenders are loaded if they are used&lt;br /&gt;
* fair play: all extenders have equal rights&lt;br /&gt;
* diversity: extension points support different extension&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Components ===&lt;br /&gt;
The extension point system lives in the 'io.ox/core/extensions' module and consists of these elements:&lt;br /&gt;
* extension point system: accessing the outer parts&lt;br /&gt;
* registry: manages extension points, extensions and their state&lt;br /&gt;
* extension point: part of the systems that can be extended, referenced by a unique id&lt;br /&gt;
* extension: adding/replacing functionality during runtime, referenced by a unique id&lt;br /&gt;
* baton: object used to store context, passed back through callbacks&lt;br /&gt;
&lt;br /&gt;
==Extension Point System==&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; //load extension points module&lt;br /&gt;
 require(['io.ox/core/extensions'], function (ext) {&lt;br /&gt;
     //insert code here&lt;br /&gt;
 });&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Registry ==&lt;br /&gt;
* manages extension points, extensions and their state&lt;br /&gt;
&lt;br /&gt;
=== list points ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; // returns array of point ids&lt;br /&gt;
 var keys = ext.keys();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== get/create point ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; //also registers a point if not happened yet&lt;br /&gt;
 var mypoint = ext.point('io.ox/calendar/detail');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Extension Point ==&lt;br /&gt;
* part of the systems that can be extended&lt;br /&gt;
* referenced by a unique id&lt;br /&gt;
* defines some kind of contract it's extension that to comply &lt;br /&gt;
&lt;br /&gt;
=== attributes ===&lt;br /&gt;
* id&lt;br /&gt;
* description (optional)&lt;br /&gt;
&lt;br /&gt;
'''''example'''''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; //get a point and it's description&lt;br /&gt;
 var point = ext.point('io.ox/mail/links/toolbar'),&lt;br /&gt;
     descr = point.description = '';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== add extension ===&lt;br /&gt;
* important: existing extensions with same id will not be overwritten - use replace instead&lt;br /&gt;
* If the id is not specified, the value 'default' is used.&lt;br /&gt;
* example: add extension with id 'date'&lt;br /&gt;
&lt;br /&gt;
'''''example'''''&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; // chainable (returns mypoint)&lt;br /&gt;
 point.extend({&lt;br /&gt;
     id: 'example1', // Every extension is supposed to have an id&lt;br /&gt;
     index: 100,     // Extensions are ordered based on their indexes&lt;br /&gt;
     draw: function () {&lt;br /&gt;
         //draw something&lt;br /&gt;
     }&lt;br /&gt;
 });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== replace extension ===&lt;br /&gt;
* important: only extension properties will be replaced (jQuery extend is used internally)&lt;br /&gt;
* when calling replace on a not yet existing extension a new extension is created&lt;br /&gt;
* hint: therefore ''replace'' can also be executed before extension is initially created with ''extend''&lt;br /&gt;
&lt;br /&gt;
'''''example'''''&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; // chainable (returns mypoint)&lt;br /&gt;
 mypoint.replace({&lt;br /&gt;
     id: 'example1',&lt;br /&gt;
     index: 100,&lt;br /&gt;
     draw: function (baton) {&lt;br /&gt;
         //draw something completely different&lt;br /&gt;
     }&lt;br /&gt;
 });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== use extensions===&lt;br /&gt;
* ''invoking'' extension point extensions by defining functionname, context and baton&lt;br /&gt;
* node used as context (function is called via apply(node, args))&lt;br /&gt;
* baton forwarded within programmatic flow and used for storing and exchanging data between extensions&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
mypoint.invoke(name, context, baton);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''''example'''''&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; //call 'draw' of all registered extensions (order defined by index attribute)&lt;br /&gt;
 //node used as context ('draw' function is called via apply(node, args))&lt;br /&gt;
 //baton's data property contains relevant information about current entity (for example a mail object)&lt;br /&gt;
 mypoint.invoke('draw', node, baton);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== access extensions ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
// returns array containing all extension ids&lt;br /&gt;
mypoint.keys();&lt;br /&gt;
&lt;br /&gt;
// returns array containing all extensions&lt;br /&gt;
mypoint.all();&lt;br /&gt;
&lt;br /&gt;
// executes callback for a specific extension; chainable (returns point)&lt;br /&gt;
mypoint.get(id, callback);&lt;br /&gt;
&lt;br /&gt;
// disabled extension will return true also;&lt;br /&gt;
var exists = mypoint.has(id);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''''enabled only'''''&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
// returns array containing all enabled extensions&lt;br /&gt;
mypoint.list();&lt;br /&gt;
&lt;br /&gt;
// returns number containing enabled extensions&lt;br /&gt;
mypoint.count();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== enabling/disabling ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
var enabled = mypoint.isEnabled(id);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; // chainable (returns mypoint)&lt;br /&gt;
 mypoint.enable(id);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; // chainable (returns mypoint)&lt;br /&gt;
 mypoint.disable(id);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''example'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; //disable&lt;br /&gt;
 ext.point('io.ox/mail/detail/header').disable('receiveddate');&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== underscore equivalents ===&lt;br /&gt;
* only considers enabled extensions&lt;br /&gt;
* functions returns underscore-chain object of enabled extensions&lt;br /&gt;
* take a look at [ http://underscorejs.org ] for more details&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; mypoint.chain();&lt;br /&gt;
 mypoint.each(callback);&lt;br /&gt;
 mypoint.map(callback);&lt;br /&gt;
 mypoint.filter(callback); //select alias&lt;br /&gt;
 mypoint.reduce(callback, memo); //inject alias&lt;br /&gt;
 mypoint.pluck(propertyName);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''example'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; // Shuffle extension order&lt;br /&gt;
 ext.point('io.ox/calendar/detail').each(function (e) {&lt;br /&gt;
     e.index = Math.random() * 1000 &amp;amp;gt;&amp;amp;gt; 0;&lt;br /&gt;
 }).sort();&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Event Hub ===&lt;br /&gt;
&lt;br /&gt;
* Event Hub based on jQuery's ''on'', ''off'', ''one'' and ''trigger''&lt;br /&gt;
* differences documentated for each function&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; // attach listener&lt;br /&gt;
 mypoint.on(type, data, function);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; // detach listener&lt;br /&gt;
 mypoint.off(type, function);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; // attach listener for a single execution&lt;br /&gt;
 mypoint.one(type, data, function);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; // trigger event&lt;br /&gt;
 // difference: allows multiple types separated by spaces.&lt;br /&gt;
 // difference: actually calls triggerHandler since we are not in the DOM.&lt;br /&gt;
 mypoint.trigger(types);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; // explicit destroy to clean up.&lt;br /&gt;
 mypoint.destroy();&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Extension ==&lt;br /&gt;
* adding/replacing functionality during runtime&lt;br /&gt;
* referenced by a unique id&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Attributes ===&lt;br /&gt;
* id&lt;br /&gt;
* index (optional): numeric value used for specify order of execution (also valid are 'first' and 'last')&lt;br /&gt;
* functions: as required by the extension point contract&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''''example'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//defining a extension for some extension point that requires a draw function&lt;br /&gt;
{&lt;br /&gt;
    id: 'example1',&lt;br /&gt;
    index: 100,&lt;br /&gt;
    draw: function () {&lt;br /&gt;
        //draw something&lt;br /&gt;
    }&lt;br /&gt;
};&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== execution order ===&lt;br /&gt;
&lt;br /&gt;
To ensure your extension is called first or last use the index 'first' or 'last'. Keep in mind that a defined call order does not guarantees all extensions with a lower index finished already when your extension is called (for example some asynchronous code). Nevertheless for the most common use case (draw extensions that create/modify nodes with already present data) this execution order should be quite reliable to do some DOM manipulation within an custom extension indexed as 'last'. If more than one extension of a point has the index 'first' or 'last' these will be executed first/last in the order they were added.&lt;br /&gt;
&lt;br /&gt;
=== extensions patterns ===&lt;br /&gt;
OX App Suite uses extensions patterns. Please keep in mind that this list not necessarily covers all pattern currently used.  &lt;br /&gt;
&lt;br /&gt;
'''io.ox/backbone/forms.js'''&lt;br /&gt;
*CheckBoxField&lt;br /&gt;
*ControlGroup&lt;br /&gt;
*DateControlGroup&lt;br /&gt;
*DatePicker&lt;br /&gt;
*ErrorAlert&lt;br /&gt;
*Header&lt;br /&gt;
*InputField&lt;br /&gt;
*Section&lt;br /&gt;
*SectionLegend&lt;br /&gt;
*SelectBoxField&lt;br /&gt;
*SelectControlGroup&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''io.ox/backbone/views.js'''&lt;br /&gt;
*AttributeView&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''io.ox/calendar/edit/recurrence-view '''&lt;br /&gt;
* RecurrenceView&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''io.ox/core/extPatterns/links'''&lt;br /&gt;
*Button&lt;br /&gt;
*DropdownLinks&lt;br /&gt;
*InlineLinks&lt;br /&gt;
*link&lt;br /&gt;
*ToolbarLinks&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''io.ox/core/tk/attachments'''&lt;br /&gt;
*AttachmentList&lt;br /&gt;
*EditableAttachmentList&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''io.ox/contacts/widgets/pictureUpload.js'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''io.ox/preview/main.js'''&lt;br /&gt;
*Engine&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Baton ==&lt;br /&gt;
Part of extension points system is a structure called baton which serves as an context object. The baton passed back through callbacks within programmatic flow allowing data exchange between extension points.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== attributes ===&lt;br /&gt;
* data: usually contains current entity object, also used for data exchange&lt;br /&gt;
* options: contains data such as the current active application if a baton is used comprehensively &lt;br /&gt;
* flow:&lt;br /&gt;
** disabled: stores disabled extensions (managed via baton.disable(pointId, extensionId))&amp;lt;br /&amp;gt;&lt;br /&gt;
* $: used to reference a jQuery node object&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== disable extensions ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; //disable&lt;br /&gt;
 baton.disable(pointid, extensionid);&lt;br /&gt;
&lt;br /&gt;
 //is disabled&lt;br /&gt;
 var isDisabled = baton.isDisabled(pointid, extensionid);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''example'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; var pointid = 'io.ox/mail/detail',&lt;br /&gt;
     extensionid = 'example3',&lt;br /&gt;
     node = $('div'),&lt;br /&gt;
     baton = ext.Baton();&lt;br /&gt;
&lt;br /&gt;
 //disable extension&lt;br /&gt;
 //returns undefined&lt;br /&gt;
 baton.disable(pointid,extensionid);&lt;br /&gt;
&lt;br /&gt;
 //invoke extension with baton instance &lt;br /&gt;
 ext.point(pointid).invoke('draw', node, baton)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== data exchange ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''example'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; //extension using baton to store data&lt;br /&gt;
 {&lt;br /&gt;
     id: 'example1',&lt;br /&gt;
     index: 100,&lt;br /&gt;
     draw: function (baton) {&lt;br /&gt;
         //get the currenty process mail object&lt;br /&gt;
         var mail = baton.data;&lt;br /&gt;
         //append subject to current node referenced as this&lt;br /&gt;
         this.append(&lt;br /&gt;
             $('div').text(mail.subject);&lt;br /&gt;
         )&lt;br /&gt;
         //extend mail object to store some flag&lt;br /&gt;
         mail.drawn = mail.drawn || {};&lt;br /&gt;
         mail.drawn.subject = true;&lt;br /&gt;
         //disable extension3&lt;br /&gt;
         baton.disable()&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
 {&lt;br /&gt;
     id: 'example2',&lt;br /&gt;
     index: 200,&lt;br /&gt;
     draw: function (baton) {&lt;br /&gt;
         //get the currenty process mail object&lt;br /&gt;
         var mail = baton.data;&lt;br /&gt;
         //use value set by 'example1'&lt;br /&gt;
         if(mail &amp;amp;amp;&amp;amp;amp; mail.drawn &amp;amp;amp;&amp;amp;amp; mail.drawn.subject) {&lt;br /&gt;
             //do something&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
 {&lt;br /&gt;
     id: 'example3',&lt;br /&gt;
     index: 300,&lt;br /&gt;
     draw: function (baton) {&lt;br /&gt;
         //wil not be executed if baton from 'disable example' is used&lt;br /&gt;
     }&lt;br /&gt;
 };&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ensure ===&lt;br /&gt;
* ensure that submitted object is instanceof baton&lt;br /&gt;
* return obj if it's an instanceof baton&lt;br /&gt;
* return new baton instance '''where baton.data''' is extended by obj ''or'' obj.data (if exists)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; var baton = ext.Baton.ensure(obj) &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''example'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; //new baton.data extended by object&lt;br /&gt;
 var baton = ext.Baton.ensure({ id: 2 })&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Ui_ext_02.png|Ui_ext_02.png]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
&lt;br /&gt;
As you can see, unlike adding functionality, customizing and modifying existing extensions is always more of a grey box operation and might incur some risks when updating the software. For example when replacing a certain functionality parts of the original functionality will have to be reimplemented, and all that extra code will have to be maintained in the future. &lt;br /&gt;
&lt;br /&gt;
In essence extension points are better suited to integrating new functionality into the product rather than customizing existing functionality, but, when in a pinch or really wanting to change a certain part of the software, this is certainly a way to consider. At its most extreme use you could even disable all extensions for the mail detail view to register a set of your own extensions to completely change the way mails are displayed, at the cost of having to maintain your own detail view.&lt;br /&gt;
&lt;br /&gt;
This wraps up our little tour of the OX App Suite extension point system. It is used to integrate new functionality into the OX App Suite and provides a system for 3rd party applications to become extensible themselves. It can be used to customize the existing UI at the cost of havint to know a bit more about the internals of our application. For now until more comprehensive documentation becomes available, look at the existing OX App Suite code to see concrete extensions and extension points in action.&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Extension points]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Using_the_Upsell_widget&amp;diff=21423</id>
		<title>AppSuite:Using the Upsell widget</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Using_the_Upsell_widget&amp;diff=21423"/>
		<updated>2016-02-10T10:15:18Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Summary''': The Upsell widget is going to be released with 7.4.1. It uses an existing portal widget, which you just need to configure on the backend. It makes use of the [[AppSuite:Configuring portal plugins|config options]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Using the Upsell widget&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Nomenclature and inner workings ==&lt;br /&gt;
The ''widget'' is one portal tile. A ''tile'' covers one topic, like &amp;quot;Buy more storage capacity!&amp;quot;. One widget contains several slides, like &amp;quot;this is why you need more storage space&amp;quot;, &amp;quot;your neighbours have more storage space than you&amp;quot;, &amp;quot;Your colleagues at work have more storage space than you&amp;quot; and &amp;quot;Your significant other dreams of more storage space&amp;quot;. ''Slides'' contain a picture or a text (or both) that advertise one topic. A click on the tile (independent of which slide is shown) can start an app, usually an Upsell wizard.&lt;br /&gt;
&lt;br /&gt;
== YaML file ==&lt;br /&gt;
=== Example layout ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
io.ox/portal//widgets/protected:&lt;br /&gt;
  upsellads_0:&lt;br /&gt;
    plugin: &amp;quot;plugins/portal/upsellads/register&amp;quot;&lt;br /&gt;
    type: &amp;quot;upsellads&amp;quot;&lt;br /&gt;
    index: 0&lt;br /&gt;
    changeable:&lt;br /&gt;
      index: true&lt;br /&gt;
    props:&lt;br /&gt;
      ad: &amp;quot;loremgibsum&amp;quot;&lt;br /&gt;
&lt;br /&gt;
io.ox/upsell//ads:&lt;br /&gt;
  loremgibsum:&lt;br /&gt;
    capabilities: loremgibsum-active&lt;br /&gt;
    startDate: 2013-01-01&lt;br /&gt;
    endDate: 2013-12-31&lt;br /&gt;
    slides:&lt;br /&gt;
      en_US:&lt;br /&gt;
        slide1:&lt;br /&gt;
          type: text-only&lt;br /&gt;
          text: nodal point -ware pen drone pre- shrine otaku. lights vehicle decay uplink concrete engine vehicle. nano- smart- face forwards office shoes beef noodles courier. computer advert wristwatch A.I. &lt;br /&gt;
        slide2:&lt;br /&gt;
          type: text-top&lt;br /&gt;
          image: http://placekitten.com/1024/768&lt;br /&gt;
          text: plastic drugs paranoid -ware fluidity artisanal grenade. meta- RAF franchise sentient shrine nodality savant. smart- woman otaku hotdog woman rebar construct. car hacker garage crypto- dolphin industrial grade sprawl. soul-delay pen boat city computer artisanal beef noodles. nodal point table engine A.I. numinous crypto- chrome. denim market render-farm shrine spook meta- footage. claymore mine saturation point dolphin singularity meta- advert decay nano- receding into warehouse motion geodesic RAF faded apophenia. &lt;br /&gt;
        slide3:&lt;br /&gt;
          type: text-bottom&lt;br /&gt;
          image: http://placekitten.com/960/720&lt;br /&gt;
          text: urban Kowloon assassin skyscraper systema table render-farm. cyber- free-market grenade pistol network engine futurity. youtube paranoid sprawl shoes fetishism nodal point dead. otaku katana decay narrative man tanto dead. range-rover bomb digital BASE jump silent soul-delay crypto-. post- construct apophenia silent soul-delay nodal point drugs. computer tanto receding sensory bridge neon refrigerator. claymore mine saturation point dolphin singularity meta- advert decay nano- receding into warehouse motion geodesic RAF faded apophenia. realism gang warehouse A.I. bridge film post- tank-traps sign youtube &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Explanations ===&lt;br /&gt;
The .yml file contains two sections. The first defines the widget itself, how it is place, that it is not removable, but can be moved around. This seems to be a good setup for advertisements, but variations can be found in [[AppSuite:Configuring portal plugins|Configuring portal plugins]].&lt;br /&gt;
&lt;br /&gt;
The second section defines the content of the widget. The main part are the slides, but there is also the option to make sure this ad only runs for a specific time frame.&lt;br /&gt;
&lt;br /&gt;
The two parts are connected by an ID (in this case &amp;quot;loremgibsum&amp;quot;), which is in ...props.ad for the first part and is the key in the second part.&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
* if the user's language cannot be matched, the first language given is used. In most cases, it is prudent to make that some English variant.&lt;br /&gt;
* Slides are named &amp;quot;slide1&amp;quot;, &amp;quot;slide2&amp;quot; and so on. This is due to the OX parser not being able to deal with YaML lists, not working with integers as keys and due to JavaScript doing some weird sorting of keys in an object.&lt;br /&gt;
* Start and end date are interpreted in the user's time zone.&lt;br /&gt;
* remember that ''you'' will have to provide the translations. You won't be able to get the translators of the main product to do it for you.&lt;br /&gt;
&lt;br /&gt;
== Stuck somewhere? ==&lt;br /&gt;
You got stuck with a problem while developing? OXpedia might help you out with the article about [[AppSuite:Debugging_the_UI | debugging the UI]].&lt;br /&gt;
&lt;br /&gt;
[[Category: AppSuite]]&lt;br /&gt;
&lt;br /&gt;
[[Category: UI]]&lt;br /&gt;
[[Category: Backend]]&lt;br /&gt;
&lt;br /&gt;
[[Category: Admin]]&lt;br /&gt;
[[Category: Sales]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extension_points_for_core&amp;diff=21422</id>
		<title>AppSuite:Extension points for core</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extension_points_for_core&amp;diff=21422"/>
		<updated>2016-02-10T09:20:17Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Extension points for appsuite core&amp;lt;/div&amp;gt;&lt;br /&gt;
'''Abstract:''' This describes extension points for the appsuite core, allowing you to add functions.&lt;br /&gt;
&lt;br /&gt;
==io.ox/core/apps/category==&lt;br /&gt;
==io.ox/core/apps/installed==&lt;br /&gt;
==io.ox/core/apps/manage==&lt;br /&gt;
==io.ox/core/apps/store==&lt;br /&gt;
==io.ox/core/desktop==&lt;br /&gt;
==io.ox/core/notifications/due-tasks/header==&lt;br /&gt;
==io.ox/core/notifications/due-tasks/item==&lt;br /&gt;
==io.ox/core/notifications/invites/header==&lt;br /&gt;
==io.ox/core/notifications/invites/item==&lt;br /&gt;
==io.ox/core/notifications/mail/header==&lt;br /&gt;
==io.ox/core/notifications/mail/item==&lt;br /&gt;
==io.ox/core/notifications/register==&lt;br /&gt;
==io.ox/core/notifications/task-reminder/header==&lt;br /&gt;
==io.ox/core/notifications/task-reminder/item==&lt;br /&gt;
==io.ox/core/person:action==&lt;br /&gt;
==io.ox/core/relogin==&lt;br /&gt;
==io.ox/core/tk/attachments/links==&lt;br /&gt;
==io.ox/core/topbar==&lt;br /&gt;
==io.ox/core/topbar/favorites==&lt;br /&gt;
==io.ox/core/topbar/launchpad==&lt;br /&gt;
==io.ox/core/topbar/right==&lt;br /&gt;
==io.ox/core/topbar/right/dropdown==&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Extension points]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extension_points_for_miscellaneous&amp;diff=21421</id>
		<title>AppSuite:Extension points for miscellaneous</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extension_points_for_miscellaneous&amp;diff=21421"/>
		<updated>2016-02-10T09:19:50Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Miscellaneous Extension points&amp;lt;/div&amp;gt;&lt;br /&gt;
'''Abstract:''' This describes miscellaneous extension points, allowing you to add functions.&lt;br /&gt;
&lt;br /&gt;
=application-foldertree=&lt;br /&gt;
==io.ox/application-foldertree/links==&lt;br /&gt;
&lt;br /&gt;
=backbone=&lt;br /&gt;
==io.ox/backbone/validation/formats==&lt;br /&gt;
&lt;br /&gt;
=conversations=&lt;br /&gt;
==io.ox/conversations/actions/create==&lt;br /&gt;
==io.ox/conversations/links/toolbar==&lt;br /&gt;
&lt;br /&gt;
=folder=&lt;br /&gt;
==io.ox/foldertree/folder==&lt;br /&gt;
&lt;br /&gt;
=halo=&lt;br /&gt;
==io.ox/halo/contact:renderer==&lt;br /&gt;
==io.ox/halo/contact:requestEnhancement==&lt;br /&gt;
&lt;br /&gt;
=help=&lt;br /&gt;
==io.ox/help/helper==&lt;br /&gt;
&lt;br /&gt;
=keychain=&lt;br /&gt;
==io.ox/keychain/api==&lt;br /&gt;
==io.ox/keychain/model==&lt;br /&gt;
&lt;br /&gt;
=portal=&lt;br /&gt;
==io.ox/portal/settings/detail==&lt;br /&gt;
==io.ox/portal/widget==&lt;br /&gt;
==io.ox/portal/widget/mail==&lt;br /&gt;
==io.ox/portal/widget/mail/actions/compose==&lt;br /&gt;
==io.ox/portal/widget/mail/links/inline==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=preview=&lt;br /&gt;
==io.ox/preview/engine==&lt;br /&gt;
&lt;br /&gt;
=settings=&lt;br /&gt;
==io.ox/settings/accounts/mail/settings/detail==&lt;br /&gt;
&lt;br /&gt;
=linkedIn=&lt;br /&gt;
==io.ox/linkedIn/details/actions==&lt;br /&gt;
==io.ox/linkedIn/details/renderer==&lt;br /&gt;
&lt;br /&gt;
=plugins=&lt;br /&gt;
==io.ox/plugins/portal/linkedIn/updates/renderer==&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Extension points]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extension_points_for_tasks&amp;diff=21418</id>
		<title>AppSuite:Extension points for tasks</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extension_points_for_tasks&amp;diff=21418"/>
		<updated>2016-02-10T08:58:24Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Extension points for tasks&amp;lt;/div&amp;gt;&lt;br /&gt;
'''Abstract:''' This describes extension points for the tasks module, allowing you to add functions.&lt;br /&gt;
&lt;br /&gt;
==io.ox/tasks/attachment/links==&lt;br /&gt;
==io.ox/tasks/detail-attach==&lt;br /&gt;
==io.ox/tasks/detail-inline==&lt;br /&gt;
==io.ox/tasks/links/inline==&lt;br /&gt;
==io.ox/tasks/model/validation==&lt;br /&gt;
==io.ox/tasks/settings/detail==&lt;br /&gt;
==io.ox/tasks/vgrid/toolbar==&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Extension points]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Create_custom_folderview_entries_in_settings_app&amp;diff=21417</id>
		<title>AppSuite:Create custom folderview entries in settings app</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Create_custom_folderview_entries_in_settings_app&amp;diff=21417"/>
		<updated>2016-02-10T08:53:34Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Extension points:&amp;lt;br&amp;gt;Create custom folderview entries in settings&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Abstract'''&lt;br /&gt;
&lt;br /&gt;
''This articles covers which extension points are provided by the settings app and how to extend them to add custom folderview entries. ''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
==Add a new settings link==&lt;br /&gt;
&lt;br /&gt;
By default, the folderview in settings contains four sections. If you want to add a link to a section, you have to find out the ID of the section. The extension point you are looking for has the name &amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;'io.ox/settings/pane/' + sectionID&amp;lt;/pre&amp;gt;For example, if you want to have a setting in the section external (this is where you should usually put your settings) you can use the following code: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
ext.point('io.ox/settings/pane/external').extend({&lt;br /&gt;
    title: gt('Title'),&lt;br /&gt;
    index: 350,&lt;br /&gt;
    id: 'myUniqueID',&lt;br /&gt;
    ref: 'reference/to/settings/page'&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Create a subsetting link==&lt;br /&gt;
&lt;br /&gt;
Let's say you have a setting and you want to add a subsetting beyond that. To do this, you have to know the name and ID of the parent extension point. Then you simply have to extend the extension point with the name: &amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;parentName + '/' + parentID&amp;lt;/pre&amp;gt; The following code example creates a subsetting for the setting created above:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
ext.point('io.ox/settings/pane/external/myUniqueID').extend({&lt;br /&gt;
    title: gt('Title of Subsetting'),&lt;br /&gt;
    index: 100,&lt;br /&gt;
    id: 'myOtherUniqueID',&lt;br /&gt;
    ref: 'reference/to/other/settings/page'&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Create a new settingsgroup==&lt;br /&gt;
&lt;br /&gt;
If you have several settings that should be provided in a seperate section, you can extend the following extension point: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
ext.point('io.ox/settings/pane').extend({&lt;br /&gt;
    id: 'mySectionID',&lt;br /&gt;
    index: 500,&lt;br /&gt;
    subgroup: 'io.ox/settings/pane/mySectionID'&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You have to provide a unique sectionID and a unique subgroupID to create a section. If you want to add links to this section, you just have to extend the extension point &amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;'io.ox/settings/pane/mySectionId'&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extension_points_for_email&amp;diff=21416</id>
		<title>AppSuite:Extension points for email</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extension_points_for_email&amp;diff=21416"/>
		<updated>2016-02-10T08:52:52Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Extension points for e-mail&amp;lt;/div&amp;gt;&lt;br /&gt;
'''Abstract:''' This describes extension points for the e-mail module, allowing you to add functions.&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/all/actions ==&lt;br /&gt;
The point for the &amp;quot;all&amp;quot; actions dropdown in the detail view of a selected mail.&lt;br /&gt;
This place should be used for actions in context with all involved contacts.&lt;br /&gt;
The baton is forwarded to the the single action functions.&lt;br /&gt;
&lt;br /&gt;
baton contains:&lt;br /&gt;
data - holds the mail object of the current selected contact&lt;br /&gt;
&lt;br /&gt;
data.threadKey - &lt;br /&gt;
data.threadPosition - &lt;br /&gt;
data.threadSize - &lt;br /&gt;
tracker&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/attachment/links ==&lt;br /&gt;
The point for actions related to the attachments of a mail.&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/detail == &lt;br /&gt;
The point for the detail view of a selected mail. &lt;br /&gt;
The baton is forwarded.&lt;br /&gt;
It is followed by points for each mail detail:&lt;br /&gt;
&lt;br /&gt;
* io.ox/mail/detail/contact-picture&lt;br /&gt;
* io.ox/mail/detail/receiveddate&lt;br /&gt;
* io.ox/mail/detail/fromlist&lt;br /&gt;
* io.ox/mail/detail/thread-position&lt;br /&gt;
* io.ox/mail/detail/flag&lt;br /&gt;
* io.ox/mail/detail/subject&lt;br /&gt;
* io.ox/mail/detail/tocopy&lt;br /&gt;
* io.ox/mail/detail/attachments&lt;br /&gt;
* io.ox/mail/detail/inline-links&lt;br /&gt;
* io.ox/mail/detail/phishing-warning&lt;br /&gt;
* io.ox/mail/detail/externalresources-warning&lt;br /&gt;
* io.ox/mail/detail/content&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/detail/notification ==&lt;br /&gt;
The point for the notification handling.&lt;br /&gt;
A extended mail object is forwarded.&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/detail/notification/update-notification ==&lt;br /&gt;
The point to remove read mails in notification Area.&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/dnd/actions ==&lt;br /&gt;
The point for mail import via drag &amp;amp; drop.&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/links/inline ==&lt;br /&gt;
The Point for inserting actions like reply &amp;amp; delete.&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/settings/detail ==&lt;br /&gt;
The point for the mail settings detailpage.&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/settings/detail/section ==&lt;br /&gt;
This point is not longer supported.&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/thread ==&lt;br /&gt;
The Point for inserting actions related to the whole thread like move &amp;amp; delete.&lt;br /&gt;
The baton is forwarded.&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/vgrid/options ==&lt;br /&gt;
The point for applying options to the vgrid related to the mail app.&lt;br /&gt;
&lt;br /&gt;
=== sort ===&lt;br /&gt;
* specifies default sort field&lt;br /&gt;
* in case threadview is enabled/used and default sort field is '''not''' set to 'thread' user has to select 'sort by -&amp;gt; chat' manually after each login/page refresh&lt;br /&gt;
* alternatively you can use sever setting ''''io.ox/mail//vgrid/sort=''''&lt;br /&gt;
&lt;br /&gt;
''option: by date''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sort: 610&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''option: by from''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sort: 603&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''option: by subject''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sort: 607&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''option: by label''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sort: 102&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''option: show threads (if threadview is enabled), sort by date (if threadview are disabled)''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sort: 'thread'&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/vgrid/toolbar ==&lt;br /&gt;
The point for extending the vgrid toolbar in the mail app.&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/write/toolbar ==&lt;br /&gt;
The point for inserting inline buttons on top of the mail write view.&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/write/actions/send ==&lt;br /&gt;
The point for the action related to the send button.&lt;br /&gt;
The baton is available.&lt;br /&gt;
== io.ox/mail/write/autoCompleteItem (since 7.4.2) ==&lt;br /&gt;
The point for extending the autocomplete items in the mail app.&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/write/contactItem (since 7.4.2) ==&lt;br /&gt;
The point for extending the contact list items in the mail app.&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/write/contactPicture (since 7.4.2) ==&lt;br /&gt;
The point for extending contact picture in autocomplete and contact list.&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/write/displayName (since 7.4.2) ==&lt;br /&gt;
The point for extending display name in autocomplete and contact list.&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/write/emailAddress (since 7.4.2) ==&lt;br /&gt;
The point for extending email address in autocomplete and contact list.&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/write/actions/draft ==&lt;br /&gt;
The point for the action related to the save button.&lt;br /&gt;
The baton is available.&lt;br /&gt;
&lt;br /&gt;
== io.ox/mail/write/actions/discard ==&lt;br /&gt;
The point for the action related to the discard button.&lt;br /&gt;
The baton is available.&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Extension points]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extension_points_for_files&amp;diff=21414</id>
		<title>AppSuite:Extension points for files</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extension_points_for_files&amp;diff=21414"/>
		<updated>2016-02-10T08:39:36Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Extension points for files&amp;lt;/div&amp;gt;&lt;br /&gt;
'''Abstract:''' This describes extension points for the files module, allowing you to add functions.&lt;br /&gt;
&lt;br /&gt;
==io.ox/files/actions/edit/cancel==&lt;br /&gt;
==io.ox/files/actions/edit/save==&lt;br /&gt;
==io.ox/files/create/action==&lt;br /&gt;
==io.ox/files/create/form==&lt;br /&gt;
==io.ox/files/details/sections==&lt;br /&gt;
==io.ox/files/details/sections/content==&lt;br /&gt;
==io.ox/files/details/sections/header==&lt;br /&gt;
==io.ox/files/details/sections/header/basicInfo==&lt;br /&gt;
==io.ox/files/details/sections/upload==&lt;br /&gt;
==io.ox/files/details/sections/versions==&lt;br /&gt;
==io.ox/files/details/versions/details==&lt;br /&gt;
==io.ox/files/dnd/actions==&lt;br /&gt;
==io.ox/files/icons==&lt;br /&gt;
==io.ox/files/icons/actions==&lt;br /&gt;
==io.ox/files/icons/file==&lt;br /&gt;
==io.ox/files/icons/inline==&lt;br /&gt;
==io.ox/files/links/edit/inline==&lt;br /&gt;
==io.ox/files/links/inline==&lt;br /&gt;
==io.ox/files/settings/detail==&lt;br /&gt;
==io.ox/files/shortcuts==&lt;br /&gt;
==io.ox/files/versions/links/inline==&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Extension points]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extension_points_for_calendar&amp;diff=21413</id>
		<title>AppSuite:Extension points for calendar</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extension_points_for_calendar&amp;diff=21413"/>
		<updated>2016-02-10T08:38:54Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Extension points for calendar&amp;lt;/div&amp;gt;&lt;br /&gt;
'''Abstract:''' This describes extension points for the calendar module, allowing you to add functions.&lt;br /&gt;
&lt;br /&gt;
==io.ox/calendar/detail==&lt;br /&gt;
==io.ox/calendar/detail/actions==&lt;br /&gt;
==io.ox/calendar/detail/actions-participantrelated==&lt;br /&gt;
==io.ox/calendar/detail/actions/create==&lt;br /&gt;
==io.ox/calendar/detail/actions/edit==&lt;br /&gt;
==io.ox/calendar/detail/attachments==&lt;br /&gt;
==io.ox/calendar/detail/date==&lt;br /&gt;
==io.ox/calendar/detail/details==&lt;br /&gt;
==io.ox/calendar/edit/dnd/actions==&lt;br /&gt;
==io.ox/calendar/edit/section/buttons==&lt;br /&gt;
==io.ox/calendar/edit/section/title==&lt;br /&gt;
==io.ox/calendar/links/inline==&lt;br /&gt;
==io.ox/calendar/links/inline-participants==&lt;br /&gt;
==io.ox/calendar/model/validation==&lt;br /&gt;
==io.ox/calendar/month/view/appointment==&lt;br /&gt;
==io.ox/calendar/settings/detail==&lt;br /&gt;
==io.ox/calendar/vgrid/toolbar==&lt;br /&gt;
==io.ox/calendar/week/view/appointment==&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Extension points]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extension_points_for_contact&amp;diff=21412</id>
		<title>AppSuite:Extension points for contact</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Extension_points_for_contact&amp;diff=21412"/>
		<updated>2016-02-10T08:35:16Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Extension points for contact&amp;lt;/div&amp;gt;&lt;br /&gt;
'''Abstract:''' This describes extension points for the contact module, allowing you to add functions.&lt;br /&gt;
&lt;br /&gt;
==io.ox/contacts/api/search ==&lt;br /&gt;
An opportunity to extend search requests. Extensions are called as methods on the search request object and can modify it before it is sent to the server.&lt;br /&gt;
The search request as described in the [http://oxpedia.org/wiki/index.php?title=HTTP_API#SearchContactsAlternative HTTP API].&lt;br /&gt;
&amp;lt;pre class=&amp;quot;text&amp;quot;&amp;gt;* this - take a look at the linked HTTP API documentation.&lt;br /&gt;
* query ''string'' - The search query as entered by the user.&lt;br /&gt;
* options ''Object'' - The options object passed to the contacts search API.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==io.ox/contacts/detail==&lt;br /&gt;
The mainpoint for the detail view of a selected single contact. The baton is forwarded as argument.&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt; baton contains:&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;text&amp;quot;&amp;gt;app&lt;br /&gt;
&lt;br /&gt;
e.g.&lt;br /&gt;
&lt;br /&gt;
app.attributes - functions and infos related to the contact app&lt;br /&gt;
app.attributes.window - functions and infos related to the app window (e.g. addButton etc.)&lt;br /&gt;
app.currentContact - provides access to the folder_id and the id of the current selected contact&lt;br /&gt;
app.settings- provides access to the app settings&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;text&amp;quot;&amp;gt;data - holds the contact data object of the current selected contact&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==io.ox/contacts/detail/head==&lt;br /&gt;
The Point in the contact detail view for the headregion of a single selected contact. The baton is forwarded as argument.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==io.ox/contacts/detail/actions==&lt;br /&gt;
The mainpoint for inserting actions related to the selected contact. The contact data object (baton.data) is forwarded as argument.&amp;lt;br /&amp;gt; &lt;br /&gt;
&lt;br /&gt;
==io.ox/contacts/links/inline ==&lt;br /&gt;
The Point for inserting actions like edit &amp;amp;amp; delete.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==io.ox/contacts/edit/view/personal==&lt;br /&gt;
The point for the personal section in the contact edit form. It is followed by points for each attribute:&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* io.ox/contacts/edit/view/personal/title &lt;br /&gt;
* io.ox/contacts/edit/view/personal/first_name &lt;br /&gt;
* io.ox/contacts/edit/view/personal/last_name &lt;br /&gt;
* io.ox/contacts/edit/view/personal/display_name &lt;br /&gt;
* io.ox/contacts/edit/view/personal/second_name &lt;br /&gt;
* io.ox/contacts/edit/view/personal/suffix &lt;br /&gt;
* io.ox/contacts/edit/view/personal/nickname &lt;br /&gt;
* io.ox/contacts/edit/view/personal/birthday &lt;br /&gt;
* io.ox/contacts/edit/view/personal/marital_status &lt;br /&gt;
* io.ox/contacts/edit/view/personal/number_of_children &lt;br /&gt;
* io.ox/contacts/edit/view/personal/spouse_name &lt;br /&gt;
* io.ox/contacts/edit/view/personal/anniversary &lt;br /&gt;
* io.ox/contacts/edit/view/personal/url&lt;br /&gt;
&lt;br /&gt;
==io.ox/contacts/edit/view/messaging==&lt;br /&gt;
The point for the messaging section in the contact edit form. It is followed by points for each attribute:&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* io.ox/contacts/edit/view/messaging/email1 &lt;br /&gt;
* io.ox/contacts/edit/view/messaging/email2 &lt;br /&gt;
* io.ox/contacts/edit/view/messaging/email3 &lt;br /&gt;
* io.ox/contacts/edit/view/messaging/instant_messenger1 &lt;br /&gt;
* io.ox/contacts/edit/view/messaging/instant_messenger2&lt;br /&gt;
&lt;br /&gt;
==io.ox/contacts/edit/view/phone==&lt;br /&gt;
The point for the phone section in the contact edit form. It is followed by points for each attribute:&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* io.ox/contacts/edit/view/phone/cellular_telephone1 &lt;br /&gt;
* io.ox/contacts/edit/view/phone/cellular_telephone2 &lt;br /&gt;
* io.ox/contacts/edit/view/phone/telephone_business1 &lt;br /&gt;
* io.ox/contacts/edit/view/phone/telephone_business2 &lt;br /&gt;
* io.ox/contacts/edit/view/phone/telephone_home1 &lt;br /&gt;
* io.ox/contacts/edit/view/phone/telephone_home2 &lt;br /&gt;
* io.ox/contacts/edit/view/phone/telephone_company &lt;br /&gt;
* io.ox/contacts/edit/view/phone/telephone_other &lt;br /&gt;
* io.ox/contacts/edit/view/phone/fax_business &lt;br /&gt;
* io.ox/contacts/edit/view/phone/fax_home &lt;br /&gt;
* io.ox/contacts/edit/view/phone/fax_other &lt;br /&gt;
* io.ox/contacts/edit/view/phone/telephone_car &lt;br /&gt;
* io.ox/contacts/edit/view/phone/telephone_isdn &lt;br /&gt;
* io.ox/contacts/edit/view/phone/telephone_pager &lt;br /&gt;
* io.ox/contacts/edit/view/phone/telephone_primary &lt;br /&gt;
* io.ox/contacts/edit/view/phone/telephone_radio &lt;br /&gt;
* io.ox/contacts/edit/view/phone/telephone_telex &lt;br /&gt;
* io.ox/contacts/edit/view/phone/telephone_ttytdd &lt;br /&gt;
* io.ox/contacts/edit/view/phone/telephone_ip &lt;br /&gt;
* io.ox/contacts/edit/view/phone/telephone_assistant &lt;br /&gt;
* io.ox/contacts/edit/view/phone/telephone_callback&lt;br /&gt;
&lt;br /&gt;
== io.ox/contacts/edit/view/home_address== &lt;br /&gt;
The point for the home address section in the contact edit form. It is followed by points for each attribute:&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* io.ox/contacts/edit/view/home_address/street_home &lt;br /&gt;
* io.ox/contacts/edit/view/home_address/city_home &lt;br /&gt;
* io.ox/contacts/edit/view/home_address/state_home &lt;br /&gt;
* io.ox/contacts/edit/view/home_address/country_home&lt;br /&gt;
&lt;br /&gt;
==io.ox/contacts/edit/view/business_address==&lt;br /&gt;
The point for the business address section in the contact edit form. It is followed by points for each attribute:&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* io.ox/contacts/edit/view/business_address/street_business &lt;br /&gt;
* io.ox/contacts/edit/view/business_address/city_business &lt;br /&gt;
* io.ox/contacts/edit/view/business_address/state_business &lt;br /&gt;
* io.ox/contacts/edit/view/business_address/country_business&lt;br /&gt;
&lt;br /&gt;
==io.ox/contacts/edit/view/other_address==&lt;br /&gt;
The point for the other address section in the contact edit form. It is followed by points for each attribute:&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* io.ox/contacts/edit/view/other_address/street_other &lt;br /&gt;
* io.ox/contacts/edit/view/other_address/city_other &lt;br /&gt;
* io.ox/contacts/edit/view/other_address/state_other &lt;br /&gt;
* io.ox/contacts/edit/view/other_address/country_other&lt;br /&gt;
&lt;br /&gt;
==io.ox/contacts/edit/view/job== &lt;br /&gt;
The point for the job section in the contact edit form. It is followed by points for each attribute:&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* io.ox/contacts/edit/view/job/profession &lt;br /&gt;
* io.ox/contacts/edit/view/job/position &lt;br /&gt;
* io.ox/contacts/edit/view/job/department &lt;br /&gt;
* io.ox/contacts/edit/view/job/company &lt;br /&gt;
* io.ox/contacts/edit/view/job/room_number &lt;br /&gt;
* io.ox/contacts/edit/view/job/employee_type &lt;br /&gt;
* io.ox/contacts/edit/view/job/number_of_employees &lt;br /&gt;
* io.ox/contacts/edit/view/job/sales_volume &lt;br /&gt;
* io.ox/contacts/edit/view/job/tax_id &lt;br /&gt;
* io.ox/contacts/edit/view/job/commercial_register &lt;br /&gt;
* io.ox/contacts/edit/view/job/branches &lt;br /&gt;
* io.ox/contacts/edit/view/job/business_category &lt;br /&gt;
* io.ox/contacts/edit/view/job/info &lt;br /&gt;
* io.ox/contacts/edit/view/job/manager_name &lt;br /&gt;
* io.ox/contacts/edit/view/job/assistant_name&lt;br /&gt;
&lt;br /&gt;
==io.ox/contacts/edit/view/userfields==&lt;br /&gt;
The point for the userfields section in the contact edit form. It is followed by points for each attribute:&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield01 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield02 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield03 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield04 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield05 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield06 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield07 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield08 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield09 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield10 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield11 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield12 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield13 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield14 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield15 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield16 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield17 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield18 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield19 &lt;br /&gt;
* io.ox/contacts/edit/view/userfields/userfield20&lt;br /&gt;
&lt;br /&gt;
==io.ox/contacts/edit/view/comment==&lt;br /&gt;
The point for the comment section in the contact edit form. It is followed by a point for the attribute:&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* io.ox/contacts/edit/view/comment/note&lt;br /&gt;
&lt;br /&gt;
==io.ox/contacts/edit/view/misc==&lt;br /&gt;
The point for the misc section in the contact edit form. It is followed by a point for the attribute:&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* io.ox/contacts/edit/view/misc/private_flag&lt;br /&gt;
&lt;br /&gt;
==io.ox/contacts/edit/main/model==&lt;br /&gt;
The point for adding additional functions to the model of a contact.&lt;br /&gt;
&lt;br /&gt;
==io.ox/contacts/model/validation/distribution_list ==&lt;br /&gt;
The point for adding additional validation to the distribution_list array.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Extension points]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Files_App_Actions&amp;diff=21410</id>
		<title>AppSuite:Files App Actions</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Files_App_Actions&amp;diff=21410"/>
		<updated>2016-02-10T07:47:31Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Adding actions to the files detail area&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To add an action to the files app detail area the extensionpoint io.ox/files/links/inline is used.&lt;br /&gt;
Use the Link pattern in io.ox/core/extPatterns/links.js to extend this point.&lt;br /&gt;
Try via console:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require(['io.ox/core/extensions', 'io.ox/core/extPatterns/links'], function (ext, links) {&lt;br /&gt;
&lt;br /&gt;
  new links.Action('io.ox/files/actions/testlink', {&lt;br /&gt;
    requires: function (e) {&lt;br /&gt;
      e.collection.has('some') &amp;amp;&amp;amp; capabilities.has('webmail');&lt;br /&gt;
    },&lt;br /&gt;
    multiple: function (baton) {&lt;br /&gt;
      console.log(baton);&lt;br /&gt;
    }&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  ext.point('io.ox/files/links/inline').extend(new links.Link({&lt;br /&gt;
    id: 'testlink',&lt;br /&gt;
    index: 400,&lt;br /&gt;
    label: 'labelname',&lt;br /&gt;
    ref: 'io.ox/files/actions/testlink'&lt;br /&gt;
  }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Action ==&lt;br /&gt;
The first argument is the unique id of the action. &lt;br /&gt;
&lt;br /&gt;
requires - checks for the needed components and the collection status. (''some'' is used for multiple elements, ''one'' for an single one)&lt;br /&gt;
&lt;br /&gt;
* If the action is used only on a single element use action.&lt;br /&gt;
* If the action should be applied to multiple elements use multiple.&lt;br /&gt;
* In both cases the baton is available to the action.&lt;br /&gt;
&lt;br /&gt;
== Link==&lt;br /&gt;
* id - must be unique&lt;br /&gt;
* index - the position/order of the link&lt;br /&gt;
* label - the label for the link&lt;br /&gt;
* ref - the reference to the action id&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Writing_a_wizard&amp;diff=21409</id>
		<title>AppSuite:Writing a wizard</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Writing_a_wizard&amp;diff=21409"/>
		<updated>2016-02-10T07:01:28Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Writing a wizard&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Summary:''' In this example we will build a simple welcome wizard that tries to complete the users information.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== A simple welcome wizzard ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * All content on this website (including text, images, source&lt;br /&gt;
 * code and any other original works), unless otherwise noted,&lt;br /&gt;
 * is licensed under a Creative Commons License.&lt;br /&gt;
 *&lt;br /&gt;
 * http://creativecommons.org/licenses/by-nc-sa/2.5/&lt;br /&gt;
 *&lt;br /&gt;
 * Copyright (C) Open-Xchange Inc., 2006-2011&lt;br /&gt;
 * Mail: info@open-xchange.com&lt;br /&gt;
 *&lt;br /&gt;
 * @author Francisco Laguna &amp;lt;...&amp;gt;&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
* In this example we will build a simple welcome wizard, that tries to complete the users information.&lt;br /&gt;
*/&lt;br /&gt;
define('io.ox/dev/wizard/welcomeWizard', ['io.ox/core/extensions', 'io.ox/core/wizard/registry'], function (ext, wizards) {&lt;br /&gt;
    'use strict';&lt;br /&gt;
    // Grab the extension point for the wizard&lt;br /&gt;
    // Every page in the wizard will be an extension to this extension point&lt;br /&gt;
    var point = ext.point(&amp;quot;io.ox/dev/wizard/welcomeWizard&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    // We will build a few pages here to showcase how you can use the framework.&lt;br /&gt;
    // Firstly, the simplest case, just a static page&lt;br /&gt;
    // It's a nice trick to start off with a static page, so the subsequent page &lt;br /&gt;
    // can already start loading data&lt;br /&gt;
    // and initialize itself without the user having to wait around for that. &lt;br /&gt;
    // Distract them with a nice welcome page!&lt;br /&gt;
    point.extend({&lt;br /&gt;
        id: 'welcomeMessage',&lt;br /&gt;
        index: 100,&lt;br /&gt;
        title: &amp;quot;Welcome to App Suite&amp;quot;, // be sure to internationalize this&lt;br /&gt;
        draw: function (baton) {&lt;br /&gt;
            // A regular #draw method, that you may know and love from other extension points&lt;br /&gt;
            // Just append to 'this' to draw what you need. One caveat though: Make sure to &lt;br /&gt;
            // unlock the 'next' button&lt;br /&gt;
            // so this step can be finished&lt;br /&gt;
&lt;br /&gt;
            // Some text. Note that you want to take some more care here, to make this look&lt;br /&gt;
            // good and make sense. We'll firmly stay in example land here and not make a &lt;br /&gt;
            // fuss about looks&lt;br /&gt;
            // Make sure you do better than this, also, this needs to be internationalized &lt;br /&gt;
            // with a gt() call!&lt;br /&gt;
            this.append($(&amp;quot;&amp;lt;p&amp;gt;&amp;quot;).text(&amp;quot;Hi there, stranger! Welcome to OX App Suite, glad you made it. To make sure your experience with us is a pleasant one, let's set up some basics together!&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
            // Enable the next (or 'done', if this is the last page) button.&lt;br /&gt;
            // You will have to call this once for every page, once every needed entry has been made.&lt;br /&gt;
            baton.buttons.enableNext();&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Now let's actually ask for some user input. Let's start with the users gender.&lt;br /&gt;
    point.extend({&lt;br /&gt;
        id: 'gender',&lt;br /&gt;
        index: 200,&lt;br /&gt;
        title: &amp;quot;Gender&amp;quot;,&lt;br /&gt;
        draw: function (baton) {&lt;br /&gt;
            // Every method of a page is always called with a baton that is unique to &lt;br /&gt;
            // every page instance, so&lt;br /&gt;
            // we can set state information in it to our hearts content without &lt;br /&gt;
            // bothering everyone else.&lt;br /&gt;
            // The baton holds some interesting objects, though. 'wizard' is the instance &lt;br /&gt;
            // of the wizard object, 'buttons', like above&lt;br /&gt;
            // can be used to enable or disable the next button. The wizard also has a &lt;br /&gt;
            // pageData member object that we can use to store&lt;br /&gt;
            // data that is available to every subsequent page. Note though, that that &lt;br /&gt;
            // tightly couples pages together, so use this with care!&lt;br /&gt;
            // We will use this for some fun, though.&lt;br /&gt;
            baton.form = {};&lt;br /&gt;
&lt;br /&gt;
            this.append(&lt;br /&gt;
                $('&amp;lt;p/&amp;gt;').text(&amp;quot;Please pick one:&amp;quot;),&lt;br /&gt;
                $('&amp;lt;form&amp;gt;').append(&lt;br /&gt;
                    $(&amp;quot;&amp;lt;fieldset&amp;gt;&amp;quot;).append(&lt;br /&gt;
                        $('&amp;lt;label class=&amp;quot;radio&amp;quot;&amp;gt;').append(&lt;br /&gt;
                            baton.form.male = $('&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;genderRadio&amp;quot; value=&amp;quot;male&amp;quot;&amp;gt;'),&lt;br /&gt;
                            $.txt(&amp;quot;Gentleman&amp;quot;)&lt;br /&gt;
                        ),&lt;br /&gt;
                        $('&amp;lt;label class=&amp;quot;radio&amp;quot;&amp;gt;').append(&lt;br /&gt;
                            baton.form.female = $('&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;genderRadio&amp;quot; value=&amp;quot;male&amp;quot;&amp;gt;'),&lt;br /&gt;
                            $.txt(&amp;quot;Lady&amp;quot;)&lt;br /&gt;
                        )&lt;br /&gt;
                    )&lt;br /&gt;
                )&lt;br /&gt;
            );&lt;br /&gt;
&lt;br /&gt;
            // We want to enable the navigation once the user picked his or her gender.&lt;br /&gt;
            // And we'll capture that logic in one method that we call everytime we have&lt;br /&gt;
            // reason to believe the state changed&lt;br /&gt;
            baton.helpers = {&lt;br /&gt;
                updateState: function () {&lt;br /&gt;
                    if (baton.form.male.attr(&amp;quot;checked&amp;quot;) === 'checked' || baton.form.female.attr(&amp;quot;checked&amp;quot;) === 'checked') {&lt;br /&gt;
                        // One of the two was picked, so enable the next button&lt;br /&gt;
                        baton.buttons.enableNext();&lt;br /&gt;
                    } else {&lt;br /&gt;
                        // No choice was made, so disable the button&lt;br /&gt;
                        baton.buttons.disableNext();&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            };&lt;br /&gt;
            baton.form.male.on('click', baton.helpers.updateState);&lt;br /&gt;
            baton.form.female.on('click', baton.helpers.updateState);&lt;br /&gt;
                &lt;br /&gt;
        },&lt;br /&gt;
&lt;br /&gt;
        activate: function (baton) {&lt;br /&gt;
            // Whenever the page is entered, the activate method is called.&lt;br /&gt;
            // we just have to make sure the button state is correct&lt;br /&gt;
            baton.helpers.updateState();&lt;br /&gt;
        },&lt;br /&gt;
&lt;br /&gt;
        finish: function (baton) {&lt;br /&gt;
            // When the page is left, the 'finish' method is called and we can do something&lt;br /&gt;
            // with the entered value, in this case we'll remember it in the wizards data section for inter-page stuff&lt;br /&gt;
&lt;br /&gt;
            var gender = null;&lt;br /&gt;
            if (baton.form.male.attr(&amp;quot;checked&amp;quot;) === 'checked') {&lt;br /&gt;
                gender = 'male';&lt;br /&gt;
            } else if (baton.form.female.attr(&amp;quot;checked&amp;quot;) === 'checked') {&lt;br /&gt;
                gender = 'female';&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            baton.wizard.pageData.gender = gender;&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Anything above a trivial form may benefit from using backbone model and view classes&lt;br /&gt;
    point.extend({&lt;br /&gt;
        id: 'completeUserInfo',&lt;br /&gt;
        index: 300,&lt;br /&gt;
        title: &amp;quot;Personal Information&amp;quot;,&lt;br /&gt;
        load: function (baton) {&lt;br /&gt;
            // The load method is an optional method. It is called to load data that you &lt;br /&gt;
            // need to set up the page&lt;br /&gt;
            // And it is called as soon as the page is the 'next' or 'previous' page of the &lt;br /&gt;
            // active page, so you can start loading&lt;br /&gt;
            // even before the page shows up. Return a deferred to let the wizard framework &lt;br /&gt;
            // know when you're done.&lt;br /&gt;
            &lt;br /&gt;
&lt;br /&gt;
            // We will fetch the user data for our example.&lt;br /&gt;
            var def = $.Deferred();&lt;br /&gt;
&lt;br /&gt;
            require([&amp;quot;io.ox/core/api/user&amp;quot;, &amp;quot;io.ox/backbone/basicModel&amp;quot;, &amp;quot;io.ox/backbone/mini-views&amp;quot;], function (userAPI, Model, mini) {&lt;br /&gt;
                // Alright, let's stick the APIs into our baton, we'll need these later&lt;br /&gt;
                // This is also a nice little trick for loading APIs in the wizard framework.&lt;br /&gt;
                baton.libraries = {&lt;br /&gt;
                    userAPI: userAPI,&lt;br /&gt;
                    mini: mini&lt;br /&gt;
                };&lt;br /&gt;
&lt;br /&gt;
                // And let's load the current user&lt;br /&gt;
&lt;br /&gt;
                userAPI.getCurrentUser().done(function (user) {&lt;br /&gt;
                    // Note, that this is a backbone model&lt;br /&gt;
                    // We could turn this into a model by instantiating a BasicModel, otherwise.&lt;br /&gt;
                    baton.user = user;&lt;br /&gt;
&lt;br /&gt;
                    // We want to enable the next button on this page based on whether a first &lt;br /&gt;
                    // an last name is set, so, let's listen for the change events on the user object&lt;br /&gt;
                    function updateButtonState() {&lt;br /&gt;
                        if (!_.isEmpty(user.get(&amp;quot;first_name&amp;quot;)) &amp;amp;&amp;amp; !_.isEmpty(user.get(&amp;quot;last_name&amp;quot;))) {&lt;br /&gt;
                            baton.buttons.enableNext();&lt;br /&gt;
                        } else {&lt;br /&gt;
                            baton.buttons.disableNext();&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                    baton.user.on('change', updateButtonState);&lt;br /&gt;
&lt;br /&gt;
                    updateButtonState();&lt;br /&gt;
&lt;br /&gt;
                    // And we're done&lt;br /&gt;
                    def.resolve();&lt;br /&gt;
                }).fail(def.reject);&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            return def;&lt;br /&gt;
        },&lt;br /&gt;
&lt;br /&gt;
        draw: function (baton) {&lt;br /&gt;
            // Now, for fun, let's try and build a backbone backed form&lt;br /&gt;
            // Depending on the complexity of the form, this is a good route to take&lt;br /&gt;
            // I would, however, also suggest to scour the appsuite source code for&lt;br /&gt;
            // reusable parts, as they will usually be internationalized, localized,&lt;br /&gt;
            // responsive to different devices and accessible. Depending on the use of this&lt;br /&gt;
            // wizard, you'll have to take care of these aspects yourself.&lt;br /&gt;
&lt;br /&gt;
            // Firstly some fun, though. Why not have this wizard be flirtatious, since it's just&lt;br /&gt;
            // getting to know the user. Personally, I think software would do well to be more &lt;br /&gt;
            // flirtatious, but I'm just a lonely developer, so YMMV&lt;br /&gt;
            if (baton.wizard.pageData.gender &amp;amp;&amp;amp; baton.wizard.pageData.gender === 'male') {&lt;br /&gt;
                this.append($(&amp;quot;&amp;lt;p&amp;gt;&amp;quot;).text(&amp;quot;So, who are you, handsome?&amp;quot;));&lt;br /&gt;
            } else if (baton.wizard.pageData.gender &amp;amp;&amp;amp; baton.wizard.pageData.gender === 'female') {&lt;br /&gt;
                this.append($(&amp;quot;&amp;lt;p&amp;gt;&amp;quot;).text(&amp;quot;So, who are you, beautiful?&amp;quot;));&lt;br /&gt;
            } else {&lt;br /&gt;
                this.append($(&amp;quot;&amp;lt;p&amp;gt;&amp;quot;).text(&amp;quot;So, who are you, stranger?&amp;quot;));&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Now, on to the serious business&lt;br /&gt;
            var mini = baton.libraries.mini;&lt;br /&gt;
&lt;br /&gt;
            this.append(&lt;br /&gt;
                $('&amp;lt;form class=&amp;quot;form-horizontal&amp;quot; /&amp;gt;').append(&lt;br /&gt;
                    $('&amp;lt;div class=&amp;quot;control-group&amp;quot; /&amp;gt;').append(&lt;br /&gt;
                        $('&amp;lt;label class=&amp;quot;control-label&amp;quot; for=&amp;quot;first_name&amp;quot; /&amp;gt;').text(&amp;quot;First Name&amp;quot;), // Don't forget i18n in your own wizard!&lt;br /&gt;
                        $('&amp;lt;div class=&amp;quot;controls&amp;quot; /&amp;gt;').append(&lt;br /&gt;
                            new mini.InputView({name: 'first_name', model: baton.user}).render().$el&lt;br /&gt;
                        )&lt;br /&gt;
                    ),&lt;br /&gt;
                    $('&amp;lt;div class=&amp;quot;control-group&amp;quot; /&amp;gt;').append(&lt;br /&gt;
                        $('&amp;lt;label class=&amp;quot;control-label&amp;quot; for=&amp;quot;last_name&amp;quot; /&amp;gt;').text(&amp;quot;Last Name&amp;quot;), // Don't forget i18n in your own wizard!&lt;br /&gt;
                        $('&amp;lt;div class=&amp;quot;controls&amp;quot; /&amp;gt;').append(&lt;br /&gt;
                            new mini.InputView({name: 'last_name', model: baton.user}).render().$el&lt;br /&gt;
                        )&lt;br /&gt;
                    )&lt;br /&gt;
                )&lt;br /&gt;
            );&lt;br /&gt;
&lt;br /&gt;
        },&lt;br /&gt;
&lt;br /&gt;
        finish: function (baton) {&lt;br /&gt;
            // Depending on the capabilities of the model, this could be more complicated&lt;br /&gt;
            // you might have to interrogate the model for the #changedAttributes&lt;br /&gt;
            // and call an API method. In any case, finish may return a deferred object&lt;br /&gt;
            // to denote the state of the save operation&lt;br /&gt;
            return baton.user.save();&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // If you want to provide your own navigation controls in the page&lt;br /&gt;
    // (useful for a simple choice), you can get rid of the default buttons&lt;br /&gt;
    // of the dialog, but have to then call baton.wizard.next or baton.wizard.prev &lt;br /&gt;
    // or baton.wizard.goToPage(pageNumberOrID) manually.&lt;br /&gt;
&lt;br /&gt;
    point.extend({&lt;br /&gt;
        id: 'spamMe',&lt;br /&gt;
        index: 400,&lt;br /&gt;
        title: &amp;quot;Special Offers&amp;quot;,&lt;br /&gt;
        hideButtons: 'true',&lt;br /&gt;
        draw: function (baton) {&lt;br /&gt;
            this.append(&lt;br /&gt;
                $('&amp;lt;div /&amp;gt;').text(&amp;quot;Would you like to be informed of special offers from time to time?&amp;quot;),&lt;br /&gt;
                &amp;quot;&amp;lt;br /&amp;gt;&amp;quot;,&lt;br /&gt;
                $('&amp;lt;div /&amp;gt;').append(&lt;br /&gt;
                    $('&amp;lt;button class=&amp;quot;btn btn-primary&amp;quot; /&amp;gt;').text(&amp;quot;Yes! Send me information about special offers&amp;quot;).on(&amp;quot;click&amp;quot;, function () {&lt;br /&gt;
                        baton.specialOffers = true;&lt;br /&gt;
                        baton.buttons.enableNext();&lt;br /&gt;
                        baton.wizard.next();&lt;br /&gt;
                    })&lt;br /&gt;
                ),&lt;br /&gt;
                &amp;quot;&amp;lt;br /&amp;gt;&amp;quot;,&lt;br /&gt;
                $('&amp;lt;div /&amp;gt;').append(&lt;br /&gt;
                    $('&amp;lt;button class=&amp;quot;btn&amp;quot; /&amp;gt;').text(&amp;quot;No, thanks&amp;quot;).on(&amp;quot;click&amp;quot;, function () {&lt;br /&gt;
                        baton.specialOffers = false;&lt;br /&gt;
                        baton.buttons.enableNext();&lt;br /&gt;
                        baton.wizard.next();&lt;br /&gt;
                    })&lt;br /&gt;
                )&lt;br /&gt;
            );&lt;br /&gt;
        },&lt;br /&gt;
&lt;br /&gt;
        finish: function (baton) {&lt;br /&gt;
            // Save baton.specialOffers preference&lt;br /&gt;
            console.log(baton.specialOffers);&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    point.extend({&lt;br /&gt;
        id: 'byebye',&lt;br /&gt;
        index: 500,&lt;br /&gt;
        title: &amp;quot;Thank you!&amp;quot;,&lt;br /&gt;
        draw: function (baton) {&lt;br /&gt;
            this.append($(&amp;quot;&amp;lt;p&amp;gt;&amp;quot;).text(&amp;quot;Thank you for completing our welcome wizard! Be sure to tell us what you like and what we could improve in App Suite!&amp;quot;));&lt;br /&gt;
            baton.buttons.enableNext();&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // To enable the wizard to run upon startup, you have to use the extension system to add a new &lt;br /&gt;
    // stage to the boot process.&lt;br /&gt;
    // Use a manifest.json to extend the core/main file:&lt;br /&gt;
    // {&lt;br /&gt;
    //      namespace: 'io.ox/core/main'&lt;br /&gt;
    // }&lt;br /&gt;
&lt;br /&gt;
    // Then, in the plugins file, define a new stage that runs the wizard after the curtain has &lt;br /&gt;
    // been drawn back:&lt;br /&gt;
&lt;br /&gt;
    /*&lt;br /&gt;
    define('...', ['io.ox/core/extPatterns/stage'], function (Stage) {&lt;br /&gt;
    'use strict';&lt;br /&gt;
&lt;br /&gt;
        new Stage('io.ox/core/stages', {&lt;br /&gt;
            id: 'welcome-wizard',&lt;br /&gt;
            after: 'curtain',&lt;br /&gt;
            run: function (baton) {&lt;br /&gt;
                var def = $.Deferred();&lt;br /&gt;
                //TODO: Check a JSLob if the wizard needs to be run, or has been cleared successfully&lt;br /&gt;
                // If it has to be run, require the wizards source file and trigger the wizard&lt;br /&gt;
                require([&amp;quot;io.ox/dev/wizard/welcomeWizard&amp;quot;], function (w) {&lt;br /&gt;
                    w.getInstance().start().done(function () {&lt;br /&gt;
                        //TODO: Mark this wizard as passed, so as not to start it again&lt;br /&gt;
                        // Resolve the deferred, so the next stage can start&lt;br /&gt;
                        def.resolve();&lt;br /&gt;
                    }).fail(def.reject);&lt;br /&gt;
                });&lt;br /&gt;
&lt;br /&gt;
                return def;&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
         &lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    */&lt;br /&gt;
    return {&lt;br /&gt;
        getInstance: function () {&lt;br /&gt;
            // Create a new instance of the wizard. Note that the id of the wizard determines &lt;br /&gt;
            // the extension point&lt;br /&gt;
            // that pages have to extend&lt;br /&gt;
            return wizards.getWizard({id: 'io.ox/dev/wizard/welcomeWizard', closeable: true});&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Developer]]&lt;br /&gt;
&lt;br /&gt;
== Stuck somewhere? ==&lt;br /&gt;
You got stuck with a problem while developing? OXpedia might help you out with the article about [[AppSuite:Debugging_the_UI | debugging the UI]].&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:BackboneMiniViews&amp;diff=21408</id>
		<title>AppSuite:BackboneMiniViews</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:BackboneMiniViews&amp;diff=21408"/>
		<updated>2016-02-10T06:51:17Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Backbone mini views&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Abstract'''&lt;br /&gt;
&lt;br /&gt;
''Mini views are small backbone views which helps you to create single form elements like input fields, checkbox and so on. They should be used to build form pages in a DRY way. &lt;br /&gt;
To use the mini-views simply require io.ox/backbone/mini-views in your view pane.''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Benefits of using mini-views==&lt;br /&gt;
* automatic cleanup (model &amp;amp; events) if the form element is removed from the DOM &lt;br /&gt;
* the wiring between form element and model data is handled for each mini-view via attribute name or id of the element&lt;br /&gt;
* DRY&lt;br /&gt;
&lt;br /&gt;
==Abstract view==&lt;br /&gt;
The abstract view is used to manage the dispose feature for all mini-views.&lt;br /&gt;
&lt;br /&gt;
==Common mini-views==&lt;br /&gt;
All mini-views provides functions for handling the common interactions between model data and form element like initial setup, update and rendering.&lt;br /&gt;
To provide some basic a11y the tabindex is set by default to 1 and can be changed via this.options.tabindex to every needed value.&lt;br /&gt;
&lt;br /&gt;
The 'id' option should be used to connect the view with the model data and render an id attribute. This can be used to connect with a label which is not surrounding the form field.&lt;br /&gt;
&lt;br /&gt;
==List of form-elements included==&lt;br /&gt;
&lt;br /&gt;
===InputView===&lt;br /&gt;
It draws a simple input form field.&lt;br /&gt;
&lt;br /&gt;
Usage&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
new InputView({ id: [attribute], model: [model] }).render().$el&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It renders the following markup:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;text&amp;quot; class=&amp;quot;form-control&amp;quot; name=&amp;quot;text&amp;quot; tabindex=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===PasswordView===&lt;br /&gt;
It draws a simple password form field.&lt;br /&gt;
&lt;br /&gt;
Usage&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
new PasswordView({ name: [attribute], model: [model] }).render().$el&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It renders the following markup:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;input type=&amp;quot;password&amp;quot; id=&amp;quot;password&amp;quot; class=&amp;quot;form-control&amp;quot; autocomplete=&amp;quot;off&amp;quot; autocorrect=&amp;quot;off&amp;quot; name=&amp;quot;password&amp;quot; tabindex=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If no password value is provided via model the field value is changed to „'********“. This especially is needed since already provided passwords are not delivered via the api request. The attributes „autocomplete&amp;quot; and „autocorrect“ are set to „off“ by default.&lt;br /&gt;
&lt;br /&gt;
===TextView===&lt;br /&gt;
It draws a simple textarea form field.&lt;br /&gt;
&lt;br /&gt;
Usage&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
new TextView({ name: [attribute], model: [model], rows: [rowcount] }).render().$el&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It renders the following markup:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;textarea id=&amp;quot;textarea&amp;quot; class=&amp;quot;form-control&amp;quot; name=&amp;quot;textarea&amp;quot; tabindex=&amp;quot;1&amp;quot; rows=&amp;quot;4&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use this.rows to set the row attribute of the area to the needed value.&lt;br /&gt;
&lt;br /&gt;
===CheckboxView===&lt;br /&gt;
It draws a simple checkbox form field.&lt;br /&gt;
&lt;br /&gt;
Usage&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
new CheckboxView({ name: [attribute], model: [model] }).render().$el&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It renders the following markup:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;input type=&amp;quot;checkbox&amp;quot; id=&amp;quot;checkbox&amp;quot; name=&amp;quot;checkbox&amp;quot; tabindex=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===RadioView===&lt;br /&gt;
It draws a simple radio form field.&lt;br /&gt;
&lt;br /&gt;
Usage&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
new RadioView({ name: [attribute], model: [model], list: [radioOptions] }).render().$el&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Use this.options.list to set the list of available options and labels.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
var radioOptions = var options = [&lt;br /&gt;
    { label: '1', value: '1'},&lt;br /&gt;
    { label: '2', value: '2'},&lt;br /&gt;
    { label: '3', value: '3'}&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It renders the following markup:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;radio&amp;quot; class=&amp;quot;controls&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;radio&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;label&amp;gt;&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;radio&amp;quot; value=&amp;quot;1&amp;quot; tabindex=&amp;quot;1&amp;quot;&amp;gt;1&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;radio&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;label&amp;gt;&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;radio&amp;quot; value=&amp;quot;2&amp;quot; tabindex=&amp;quot;1&amp;quot;&amp;gt;2&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;radio&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;label&amp;gt;&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;radio&amp;quot; value=&amp;quot;3&amp;quot; tabindex=&amp;quot;1&amp;quot;&amp;gt;3&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===SelectView===&lt;br /&gt;
It draws a simple select form field.&lt;br /&gt;
&lt;br /&gt;
Usage&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
new SelectView({ name: [attribute], model: [model], list: [selectOptions] }).render().$el&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Use this.options.list to set the list of available options and labels.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
var selectOptions = var options = [&lt;br /&gt;
    { label: '1', value: '1'},&lt;br /&gt;
    { label: '2', value: '2'},&lt;br /&gt;
    { label: '3', value: '3'}&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It renders the following markup:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;select id=&amp;quot;select&amp;quot; class=&amp;quot;input-xlarge form-control&amp;quot; name=&amp;quot;select&amp;quot; tabindex=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;option value=&amp;quot;1&amp;quot;&amp;gt;1&amp;lt;/option&amp;gt;&lt;br /&gt;
    &amp;lt;option value=&amp;quot;2&amp;quot;&amp;gt;2&amp;lt;/option&amp;gt;&lt;br /&gt;
    &amp;lt;option value=&amp;quot;3&amp;quot;&amp;gt;3&amp;lt;/option&amp;gt;&lt;br /&gt;
&amp;lt;/select&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ErrorView===&lt;br /&gt;
{{VersionFrom|7.6.1}}&lt;br /&gt;
&lt;br /&gt;
It draws a span with the class &amp;quot;help-block&amp;quot; to display error messages in case of a validation fault.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;help-block&amp;quot; aria-live=&amp;quot;assertive&amp;quot; style=&amp;quot;display: none;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Usage&lt;br /&gt;
&lt;br /&gt;
It always should be placed beneath the corresponding form field.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
new ErrorView({ selector: [css selector] }).render().$el;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The 'selector' option is used to connect the ErrorView with an custom surrounding container for the ErrorView and the related form field. It returns the closest matching container. This container is used by the ErrorView to listen to the 'valid/invalid' events triggered by the validation.&lt;br /&gt;
&lt;br /&gt;
The default selector returns the closest matching '.form-group' or '[class*=&amp;quot;col-&amp;quot;]' container.&lt;br /&gt;
&lt;br /&gt;
===FormView===&lt;br /&gt;
{{VersionFrom|7.6.1}}&lt;br /&gt;
&lt;br /&gt;
It draws a simple form element.&lt;br /&gt;
As a miniview, it automatically cleans up the connected model &amp;amp; events.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;form&amp;gt;&amp;lt;/form&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
new FormView({ model: [model] }).render().$el;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===DateView===&lt;br /&gt;
&lt;br /&gt;
It draws a set of 'select' form fields to display and edit a date.&lt;br /&gt;
Day, month and year will each be represented by a single &amp;lt;select&amp;gt; item.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
new DateView({ name: [attribute], model: [model] }).render().$el&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It renders the following markup:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;date&amp;quot; class=&amp;quot;native-date-picker row&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;col-xs-3 col-sm-3 col-md-3 col-lg-3&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;select tabindex=&amp;quot;1&amp;quot; name=&amp;quot;date&amp;quot; title=&amp;quot;Tag&amp;quot; class=&amp;quot;form-control date&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;option value=&amp;quot;&amp;quot;&amp;gt;&amp;lt;/option&amp;gt;&lt;br /&gt;
            &amp;lt;option value=&amp;quot;1&amp;quot;&amp;gt;1&amp;lt;/option&amp;gt;&lt;br /&gt;
            &amp;lt;option value=&amp;quot;2&amp;quot;&amp;gt;2&amp;lt;/option&amp;gt;&lt;br /&gt;
            &amp;lt;option value=&amp;quot;3&amp;quot;&amp;gt;3&amp;lt;/option&amp;gt;&lt;br /&gt;
                           *&lt;br /&gt;
                           *&lt;br /&gt;
                           *&lt;br /&gt;
            &amp;lt;option value=&amp;quot;29&amp;quot;&amp;gt;29&amp;lt;/option&amp;gt;&lt;br /&gt;
            &amp;lt;option value=&amp;quot;30&amp;quot;&amp;gt;30&amp;lt;/option&amp;gt;&lt;br /&gt;
            &amp;lt;option value=&amp;quot;31&amp;quot; disabled=&amp;quot;&amp;quot;&amp;gt;31&amp;lt;/option&amp;gt;&lt;br /&gt;
         &amp;lt;/select&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;col-xs-5 col-sm-5 col-md-5 col-lg-5&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;select tabindex=&amp;quot;1&amp;quot; name=&amp;quot;month&amp;quot; title=&amp;quot;Monat&amp;quot; class=&amp;quot;form-control month&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;option value=&amp;quot;&amp;quot;&amp;gt;&amp;lt;/option&amp;gt;&lt;br /&gt;
            &amp;lt;option value=&amp;quot;0&amp;quot;&amp;gt;Januar&amp;lt;/option&amp;gt;&lt;br /&gt;
            &amp;lt;option value=&amp;quot;1&amp;quot;&amp;gt;Februar&amp;lt;/option&amp;gt;&lt;br /&gt;
                           *&lt;br /&gt;
                           *&lt;br /&gt;
                           *&lt;br /&gt;
            &amp;lt;option value=&amp;quot;10&amp;quot;&amp;gt;November&amp;lt;/option&amp;gt;&lt;br /&gt;
            &amp;lt;option value=&amp;quot;11&amp;quot;&amp;gt;Dezember&amp;lt;/option&amp;gt;&lt;br /&gt;
        &amp;lt;/select&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;col-xs-4 col-sm-4 col-md-4 col-lg-4&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;select tabindex=&amp;quot;1&amp;quot; name=&amp;quot;year&amp;quot; title=&amp;quot;Jahr&amp;quot; class=&amp;quot;form-control year&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;option value=&amp;quot;0001&amp;quot;&amp;gt;&amp;lt;/option&amp;gt;&lt;br /&gt;
            &amp;lt;option value=&amp;quot;2014&amp;quot;&amp;gt;2014&amp;lt;/option&amp;gt;&lt;br /&gt;
            &amp;lt;option value=&amp;quot;2013&amp;quot;&amp;gt;2013&amp;lt;/option&amp;gt;&lt;br /&gt;
            &amp;lt;option value=&amp;quot;2012&amp;quot;&amp;gt;2012&amp;lt;/option&amp;gt;&lt;br /&gt;
                           *&lt;br /&gt;
                           *&lt;br /&gt;
                           *&lt;br /&gt;
            &amp;lt;option value=&amp;quot;1866&amp;quot;&amp;gt;1866&amp;lt;/option&amp;gt;&lt;br /&gt;
            &amp;lt;option value=&amp;quot;1865&amp;quot;&amp;gt;1865&amp;lt;/option&amp;gt;&lt;br /&gt;
            &amp;lt;option value=&amp;quot;1864&amp;quot;&amp;gt;1864&amp;lt;/option&amp;gt;&lt;br /&gt;
        &amp;lt;/select&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Dropdown===&lt;br /&gt;
The Dropdown mini view provides an easy solution to create a drop down.&lt;br /&gt;
Multiple settings and / or links may be provided to the user in a single drop-down view.&lt;br /&gt;
To use the dropdown mini view simply require io.ox/backbone/mini-views/dropdown in your view pane.&lt;br /&gt;
&lt;br /&gt;
Use this.option to fill the drop down with a single selectable option.&amp;lt;br&amp;gt;&lt;br /&gt;
arguments: [attribute], [value], [text used for labeling]&lt;br /&gt;
&lt;br /&gt;
Use this.link to provide a simple link with a callback.&amp;lt;br&amp;gt;&lt;br /&gt;
arguments: [value for attribute &amp;quot;data-name&amp;quot;], [text for link], [callback]&lt;br /&gt;
&lt;br /&gt;
Use this.header to build logical groups in the drop down while setting headers between the options.&amp;lt;br&amp;gt;&lt;br /&gt;
arguments: [text for header]&lt;br /&gt;
&lt;br /&gt;
Use this.divider to provide a visual devider between groups.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
var dropdown = new Dropdown({ model: model, label: gt('Dropdown'), tagName: 'li' })&lt;br /&gt;
    .header(gt('Options'))&lt;br /&gt;
    .option('options', 'firstOption', gt('first option'))&lt;br /&gt;
    .option('options', 'secondOption', gt('second option'))&lt;br /&gt;
    .divider()&lt;br /&gt;
    .header(gt('Choices'))&lt;br /&gt;
    .option(‚'choice', 'firstChoice', gt('first choice'))&lt;br /&gt;
    .option('choice', 'secondChoice', gt('second choice'))&lt;br /&gt;
    .divider()&lt;br /&gt;
    .link('link', gt('link'), function () { console.log('clicked'); }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
dropdown.render().$el;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It renders the following markup:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li class=&amp;quot;dropdown&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;a href=&amp;quot;#&amp;quot; tabindex=&amp;quot;1&amp;quot; role=&amp;quot;menuitem&amp;quot; aria-haspopup=&amp;quot;true&amp;quot; aria-label=&amp;quot;&amp;quot; data-toggle=&amp;quot;dropdown&amp;quot; aria-expanded=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;span class=&amp;quot;dropdown-label&amp;quot;&amp;gt;Dropdown&amp;lt;/span&amp;gt;&lt;br /&gt;
    &amp;lt;/a&amp;gt;&lt;br /&gt;
    &amp;lt;ul class=&amp;quot;dropdown-menu&amp;quot; role=&amp;quot;menu&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;li class=&amp;quot;dropdown-header&amp;quot; role=&amp;quot;presentation&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;Options&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li role=&amp;quot;presentation&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;a href=&amp;quot;#&amp;quot; role=&amp;quot;menuitemcheckbox&amp;quot; aria-checked=&amp;quot;true&amp;quot; data-name=&amp;quot;dropdownOptions&amp;quot; data-value=&amp;quot;firstOption&amp;quot; data-toggle=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;i class=&amp;quot;fa fa-fw fa-check&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;&lt;br /&gt;
                &amp;lt;span&amp;gt;first option&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;/a&amp;gt;&lt;br /&gt;
        &amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li role=&amp;quot;presentation&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;a href=&amp;quot;#&amp;quot; role=&amp;quot;menuitemcheckbox&amp;quot; aria-checked=&amp;quot;false&amp;quot; data-name=&amp;quot;dropdownOptions&amp;quot; data-value=&amp;quot;secondOption&amp;quot; data-toggle=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;i class=&amp;quot;fa fa-fw fa-none&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;&lt;br /&gt;
                &amp;lt;span&amp;gt;second option&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;/a&amp;gt;&lt;br /&gt;
        &amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li class=&amp;quot;divider&amp;quot; role=&amp;quot;presentation&amp;quot;&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li class=&amp;quot;dropdown-header&amp;quot; role=&amp;quot;presentation&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;Choices&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li role=&amp;quot;presentation&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;a href=&amp;quot;#&amp;quot; role=&amp;quot;menuitemcheckbox&amp;quot; aria-checked=&amp;quot;true&amp;quot; data-name=&amp;quot;dropdownChoice&amp;quot; data-value=&amp;quot;firstChoice&amp;quot; data-toggle=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;i class=&amp;quot;fa fa-fw fa-check&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;&lt;br /&gt;
                &amp;lt;span&amp;gt;first choice&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;/a&amp;gt;&lt;br /&gt;
        &amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li role=&amp;quot;presentation&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;a href=&amp;quot;#&amp;quot; role=&amp;quot;menuitemcheckbox&amp;quot; aria-checked=&amp;quot;false&amp;quot; data-name=&amp;quot;dropdownChoice&amp;quot; data-value=&amp;quot;secondChoice&amp;quot; data-toggle=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;i class=&amp;quot;fa fa-fw fa-none&amp;quot; aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;&lt;br /&gt;
                &amp;lt;span&amp;gt;second choice&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;/a&amp;gt;&lt;br /&gt;
        &amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li class=&amp;quot;divider&amp;quot; role=&amp;quot;presentation&amp;quot;&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li role=&amp;quot;presentation&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;a href=&amp;quot;#&amp;quot; role=&amp;quot;menuitem&amp;quot; tabindex=&amp;quot;-1&amp;quot;&amp;gt;link&amp;lt;/a&amp;gt;&lt;br /&gt;
        &amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Dropdown Link===&lt;br /&gt;
It draws a link which opens a dropdown. The link text always shows the selected option.&lt;br /&gt;
&lt;br /&gt;
Usage&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
new DropdownLinkView({ name: [attribute], model: [model], values: [dropdownOptions] }).render().$el&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use this.options.values to set the list of available values and labels.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
var dropdownOptions = { &lt;br /&gt;
    'value1': 'Dropdownlink 1',&lt;br /&gt;
    'value2': 'Dropdownlink 2',&lt;br /&gt;
    'value3': 'Dropdownlink 3'&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It renders the following markup:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;dropdownlink&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;dropdown-toggle&amp;quot; data-toggle=&amp;quot;dropdown&amp;quot; role=&amp;quot;menuitem&amp;quot; aria-haspopup=&amp;quot;true&amp;quot; tabindex=&amp;quot;1&amp;quot;&amp;gt;label1&amp;lt;/a&amp;gt;&lt;br /&gt;
    &amp;lt;ul class=&amp;quot;dropdown-menu&amp;quot; role=&amp;quot;menu&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; data-action=&amp;quot;change-value&amp;quot; data-value=&amp;quot;value1&amp;quot; tabindex=&amp;quot;1&amp;quot;&amp;gt;Dropdownlink 1&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; data-action=&amp;quot;change-value&amp;quot; data-value=&amp;quot;value2&amp;quot; tabindex=&amp;quot;1&amp;quot;&amp;gt;Dropdownlink 2&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; data-action=&amp;quot;change-value&amp;quot; data-value=&amp;quot;value3&amp;quot; tabindex=&amp;quot;1&amp;quot;&amp;gt;Dropdownlink 3&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
[[Category:AppSuite]][[Category:UI]][[Category:Developer]]&lt;br /&gt;
&lt;br /&gt;
==Playground==&lt;br /&gt;
Simply copy/paste the code beneath in to your browser console while being logged in. This creates an playground for the mentioned mini views.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
require(['io.ox/backbone/mini-views', 'io.ox/backbone/mini-views/dropdown'], function(mini, Dropdown) {&lt;br /&gt;
    &lt;br /&gt;
    var model = window.model = new Backbone.Model({&lt;br /&gt;
        text: 'text',&lt;br /&gt;
        radio: '1',&lt;br /&gt;
        password: 'password',&lt;br /&gt;
        textarea: 'textarea',&lt;br /&gt;
        checkbox: true,&lt;br /&gt;
        select: '1',&lt;br /&gt;
        date: 1411023212550,&lt;br /&gt;
        dropdownOptions: 'firstOption',&lt;br /&gt;
        dropdownChoice: 'firstChoice',&lt;br /&gt;
        dropdownLink: 'value1'&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var form = new mini.FormView({ model: model });&lt;br /&gt;
&lt;br /&gt;
    // use listenTo to benefit from automatic clean-up&lt;br /&gt;
    form.listenTo(model, 'change', function () {&lt;br /&gt;
        console.log(this.model.toJSON());&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    var options = [&lt;br /&gt;
        { label: '1', value: '1'},&lt;br /&gt;
        { label: '2', value: '2'},&lt;br /&gt;
        { label: '3', value: '3'}&lt;br /&gt;
    ];&lt;br /&gt;
    &lt;br /&gt;
    var dropdownOptions = { &lt;br /&gt;
        'value1': 'Dropdownlink 1',&lt;br /&gt;
        'value2': 'Dropdownlink 2',&lt;br /&gt;
        'value3': 'Dropdownlink 3'&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    var dropdown = new Dropdown({ model: model, label: 'Dropdown' })&lt;br /&gt;
        .header('Options')&lt;br /&gt;
        .option('dropdownOptions', 'firstOption', 'first option')&lt;br /&gt;
        .option('dropdownOptions', 'secondOption', 'second option')&lt;br /&gt;
        .divider()&lt;br /&gt;
        .header('Choices')&lt;br /&gt;
        .option('dropdownChoice', 'firstChoice', 'first choice')&lt;br /&gt;
        .option('dropdownChoice', 'secondChoice', 'second choice')&lt;br /&gt;
        .divider()&lt;br /&gt;
        .link('link', 'link', function () { console.log('clicked'); }&lt;br /&gt;
    );&lt;br /&gt;
&lt;br /&gt;
    form.$el.append(&lt;br /&gt;
        $('&amp;lt;fieldset&amp;gt;').append(&lt;br /&gt;
            $('&amp;lt;div class=&amp;quot;row form-group&amp;quot;&amp;gt;').append(&lt;br /&gt;
                $('&amp;lt;label for=&amp;quot;text&amp;quot; class=&amp;quot;control-label col-sm-4&amp;quot;&amp;gt;').text('Text'),&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;col-sm-8&amp;quot;&amp;gt;').append(&lt;br /&gt;
                    new mini.InputView({ id: 'text', model: model }).render().$el&lt;br /&gt;
                )&lt;br /&gt;
            ),&lt;br /&gt;
            $('&amp;lt;div class=&amp;quot;row form-group&amp;quot;&amp;gt;').append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;col-sm-8 col-sm-offset-4&amp;quot;&amp;gt;').append(&lt;br /&gt;
                    dropdown.render().$el&lt;br /&gt;
                )&lt;br /&gt;
            ),&lt;br /&gt;
            $('&amp;lt;div class=&amp;quot;row form-group&amp;quot;&amp;gt;').append(&lt;br /&gt;
                $('&amp;lt;label class=&amp;quot;control-label col-sm-4&amp;quot;&amp;gt;').text('Radio'),&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;col-sm-8&amp;quot;&amp;gt;').append(&lt;br /&gt;
                    new mini.RadioView({&lt;br /&gt;
                        list: options,&lt;br /&gt;
                        id: 'radio',&lt;br /&gt;
                        model: model&lt;br /&gt;
                    }).render().$el&lt;br /&gt;
                )&lt;br /&gt;
           ),&lt;br /&gt;
           $('&amp;lt;div class=&amp;quot;row form-group&amp;quot;&amp;gt;').append(&lt;br /&gt;
               $('&amp;lt;label for=&amp;quot;password&amp;quot; class=&amp;quot;control-label col-sm-4&amp;quot;&amp;gt;').text('Password'),&lt;br /&gt;
               $('&amp;lt;div class=&amp;quot;col-sm-8&amp;quot;&amp;gt;').append(&lt;br /&gt;
                   new mini.PasswordView({ id: 'password', model: model }).render().$el,&lt;br /&gt;
                   new mini.ErrorView({ selector: '.row' }).render().$el&lt;br /&gt;
               )&lt;br /&gt;
           ),&lt;br /&gt;
           $('&amp;lt;div class=&amp;quot;row form-group&amp;quot;&amp;gt;').append(&lt;br /&gt;
               $('&amp;lt;label for=&amp;quot;textarea&amp;quot; class=&amp;quot;control-label col-sm-4&amp;quot;&amp;gt;').text('Textarea'),&lt;br /&gt;
               $('&amp;lt;div class=&amp;quot;col-sm-8&amp;quot;&amp;gt;').append(&lt;br /&gt;
                   new mini.TextView({ id: 'textarea', rows: '4', model: model }).render().$el&lt;br /&gt;
               )&lt;br /&gt;
           ),&lt;br /&gt;
           $('&amp;lt;div class=&amp;quot;row form-group&amp;quot;&amp;gt;').append(&lt;br /&gt;
               $('&amp;lt;div class=&amp;quot;col-sm-8 col-sm-offset-4&amp;quot;&amp;gt;').append(&lt;br /&gt;
                   $('&amp;lt;label class=&amp;quot;checkbox&amp;quot;&amp;gt;').append(&lt;br /&gt;
                       new mini.CheckboxView({ id: 'checkbox', model: model }).render().$el,&lt;br /&gt;
                       document.createTextNode('Checkbox')&lt;br /&gt;
                   )&lt;br /&gt;
               )&lt;br /&gt;
           ),&lt;br /&gt;
           $('&amp;lt;div class=&amp;quot;row form-group&amp;quot;&amp;gt;').append(&lt;br /&gt;
               $('&amp;lt;label for=&amp;quot;select&amp;quot; class=&amp;quot;control-label col-sm-4&amp;quot;&amp;gt;').text('Select'),&lt;br /&gt;
               $('&amp;lt;div class=&amp;quot;col-sm-8&amp;quot;&amp;gt;').append(&lt;br /&gt;
                   new mini.SelectView({&lt;br /&gt;
                       list: options,&lt;br /&gt;
                       id: 'select',&lt;br /&gt;
                       model: model&lt;br /&gt;
                   }).render().$el&lt;br /&gt;
               )&lt;br /&gt;
           ),&lt;br /&gt;
           $('&amp;lt;div class=&amp;quot;row form-group&amp;quot;&amp;gt;').append(&lt;br /&gt;
               $('&amp;lt;label class=&amp;quot;control-label col-sm-4&amp;quot;&amp;gt;').text('Date'),&lt;br /&gt;
               $('&amp;lt;div class=&amp;quot;col-sm-8&amp;quot;&amp;gt;').append(&lt;br /&gt;
                   new mini.DateView({ id: 'date', model: model }).render().$el,&lt;br /&gt;
                   new mini.ErrorView({ selector: '.row' }).render().$el&lt;br /&gt;
               )&lt;br /&gt;
           ),&lt;br /&gt;
           $('&amp;lt;div class=&amp;quot;row form-group&amp;quot;&amp;gt;').append(&lt;br /&gt;
               $('&amp;lt;div class=&amp;quot;col-sm-8 col-sm-offset-4&amp;quot;&amp;gt;').append(&lt;br /&gt;
                   new mini.DropdownLinkView({ id: 'dropdownLink', model: model, values: dropdownOptions }).render().$el&lt;br /&gt;
               )&lt;br /&gt;
           )&lt;br /&gt;
        )&lt;br /&gt;
    );&lt;br /&gt;
&lt;br /&gt;
    // add button to close playground&lt;br /&gt;
    form.$el.prepend(&lt;br /&gt;
        button = $('&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;close&amp;quot;&amp;gt;').append(&lt;br /&gt;
            $('&amp;lt;span aria-hidden=&amp;quot;true&amp;quot;&amp;gt;&amp;amp;times;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;sr-only&amp;quot;&amp;gt;Close&amp;lt;/span&amp;gt;')&lt;br /&gt;
        )&lt;br /&gt;
        .css({&lt;br /&gt;
            position: 'absolute',&lt;br /&gt;
            top: '4px',&lt;br /&gt;
            right: '8px',&lt;br /&gt;
            margin: '3px'&lt;br /&gt;
        })&lt;br /&gt;
        .on('click', function () {&lt;br /&gt;
            form.remove();&lt;br /&gt;
        })&lt;br /&gt;
    );&lt;br /&gt;
&lt;br /&gt;
    form.$el.css({&lt;br /&gt;
        position: 'absolute',&lt;br /&gt;
        top: 0,&lt;br /&gt;
        right: 0,&lt;br /&gt;
        bottom: 0,&lt;br /&gt;
        width: '50%',&lt;br /&gt;
        padding: '40px',&lt;br /&gt;
        backgroundColor: '#fff',&lt;br /&gt;
        boxShadow: '0 0 30px #888',&lt;br /&gt;
        zIndex: 11,&lt;br /&gt;
        overflowX: 'hidden',&lt;br /&gt;
        overflowY: 'auto'&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    $('body').append(form.render().$el);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can copy/paste the following examples in your browser console to make changes to the data in the playground.&lt;br /&gt;
&lt;br /&gt;
- to modify values via console&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
window.model.set('password', 'new password');&lt;br /&gt;
// window.model.set([attribute], [new value]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
- to trigger an validation fault&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
window.model.trigger('invalid:password', 'something is wrong here'); &lt;br /&gt;
// window.model.trigger('invalid:' + [attribute], [Error Message]); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
- to remove an validation fault&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
window.model.trigger('valid:password');&lt;br /&gt;
// window.model.trigger('valid:' + [attribute]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Action_links&amp;diff=21407</id>
		<title>AppSuite:Action links</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Action_links&amp;diff=21407"/>
		<updated>2016-02-10T06:29:37Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;status-disclaimer&amp;quot;&amp;gt;&lt;br /&gt;
'''Please note:'''&lt;br /&gt;
This page is currently under construction. It might change any minute now.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Understanding action links&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Action links can be used to apply an action to an item or even a selection of items. Use them to extend actions for existing items or create a toolbox for your custom items available in your [[Appsuite:Writing a simple application|application]].&lt;br /&gt;
&lt;br /&gt;
= The API =&lt;br /&gt;
&lt;br /&gt;
== Action ==&lt;br /&gt;
&lt;br /&gt;
Creating a new action is pretty straight forward. Just create a new Action and give it a unique name (internally we use slashes to indicate a module hierarchy, so names are inherently unique) and provide a few options.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
  var action = new Action('unique/name/of/my/action', {&lt;br /&gt;
    //some options go in here&lt;br /&gt;
  });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Available options ===&lt;br /&gt;
&lt;br /&gt;
In the options parameter you can provide the actual logic for the action. You can provide callbacks, requirements and more.&lt;br /&gt;
&lt;br /&gt;
* id&lt;br /&gt;
** a unique identifier (String)&lt;br /&gt;
* requires (optional)&lt;br /&gt;
** (String) - a String containing one or more special keywords that are required for this action to be active&lt;br /&gt;
*** toplevel&lt;br /&gt;
*** one&lt;br /&gt;
*** some&lt;br /&gt;
*** delete&lt;br /&gt;
*** …&lt;br /&gt;
** (Function) - a function that returns a Boolean value and gets a Baton object as parameter&lt;br /&gt;
* multiple (optional)&lt;br /&gt;
** (Function) - a callback function, called when the action is triggered with a list of elements&lt;br /&gt;
* action (optional)&lt;br /&gt;
** (Function) - a callback function, called when the action is triggered for one element&lt;br /&gt;
&lt;br /&gt;
== ActionLink ==&lt;br /&gt;
&lt;br /&gt;
== ActionGroup ==&lt;br /&gt;
&lt;br /&gt;
== Link ==&lt;br /&gt;
&lt;br /&gt;
== XLink ==&lt;br /&gt;
&lt;br /&gt;
== Button ==&lt;br /&gt;
&lt;br /&gt;
== ButtonGroup ==&lt;br /&gt;
&lt;br /&gt;
== ToolbarButtons ==&lt;br /&gt;
&lt;br /&gt;
== ToolbarLinks ==&lt;br /&gt;
&lt;br /&gt;
== InlineLinks ==&lt;br /&gt;
&lt;br /&gt;
== DropdownLinks ==&lt;br /&gt;
&lt;br /&gt;
== Dropdown ==&lt;br /&gt;
&lt;br /&gt;
= Examples =&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Guided_tours&amp;diff=21406</id>
		<title>AppSuite:Guided tours</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Guided_tours&amp;diff=21406"/>
		<updated>2016-02-10T06:21:52Z</updated>

		<summary type="html">&lt;p&gt;Frank.paczynski: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&amp;lt;!-- PLEASE APPLY CHANGES ONLY TO THE NEW TECHNICAL DOCUMENTATION: wd/frontend/web/documentation --&amp;gt; &lt;br /&gt;
&amp;lt;!-- !!! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Synopsis:''' Guided tours are series of little steps meant to explain the various functions of OX to an end user. They can be configured by system administrators.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Basic framework ==&lt;br /&gt;
Guided tours are built on [https://github.com/linkedin/hopscotch LinkedIn's hopscotch.js] framework. It forms a series of small information &amp;quot;bubbles&amp;quot; that point to UI elements and display a text as well as small navigation elements.&lt;br /&gt;
&lt;br /&gt;
== State before 7.4.1 ==&lt;br /&gt;
In 7.4.0, Guided Tours could not be configured.&lt;br /&gt;
&lt;br /&gt;
== State after 7.4.1 ==&lt;br /&gt;
=== Package ===&lt;br /&gt;
Guided tours are contained in a separate package, named ''open-xchange-guidedtours''. This will install UI as well as backend components (in the form of a config file).&lt;br /&gt;
&lt;br /&gt;
=== Configuration ===&lt;br /&gt;
Several configuration parameters guide the running of Guided Tours:&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|io.ox/tours//server/disableTours&lt;br /&gt;
|Disabled the tours completely&lt;br /&gt;
|-&lt;br /&gt;
|io.ox/tours//server/disable/$moduleName&lt;br /&gt;
| Disables the tour for a specific module, e.g. &amp;quot;io.ox/tasks&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| io.ox/tours//server/startOnFirstLogin&lt;br /&gt;
| start the tour the first time a user logs in&lt;br /&gt;
|-&lt;br /&gt;
|io.ox/tours//server/version&lt;br /&gt;
|arbitrary integer denoting the version of the tour. If startOnFirstLogin is true, the user might have seen a previous version but not the recent one. Increasing the number makes sure the users sees the updated version again.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Customizing ===&lt;br /&gt;
Tours use [[AppSuite:Extension points|extension points]] as mechanism to extend them. The relevant point is ''io.ox/tours/extensions''.&lt;br /&gt;
&lt;br /&gt;
==== Example tour ====&lt;br /&gt;
A tours looks like this:&lt;br /&gt;
  ext.point('io.ox/tours/extensions').extend({&lt;br /&gt;
    id: 'default/io.ox/intro',&lt;br /&gt;
    app: 'io.ox/intro',&lt;br /&gt;
    priority: 1,&lt;br /&gt;
    tour: {&lt;br /&gt;
      id: 'Switching from OX6',&lt;br /&gt;
      steps: [{&lt;br /&gt;
        title: gt('Launching an app'),&lt;br /&gt;
        placement: 'bottom',&lt;br /&gt;
        target: function () { return $('.launcher[data-app-name=&amp;quot;io.ox/mail&amp;quot;]')[0]; },&lt;br /&gt;
        content: gt('To launch an app, click on an entry on the top-left side of the menu bar.')&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
        onShow: function () { notifications.hideList(); },&lt;br /&gt;
        title: gt('Displaying the help or the settings'),&lt;br /&gt;
        placement: 'left',&lt;br /&gt;
        target: function () { return $('.launcher .icon-cog:visible')[0]; },&lt;br /&gt;
        content: gt('To display the help or the settings, use the icons on the right side of the menu bar.'),&lt;br /&gt;
        arrowOffset: 1,&lt;br /&gt;
        yOffset: -5&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
        onShow: function () { notifications.showList(); },&lt;br /&gt;
        title: gt('New objects icon'),&lt;br /&gt;
        placement: 'left',&lt;br /&gt;
        target: function () { return $('#io-ox-notifications-icon:visible')[0]; },&lt;br /&gt;
        content: gt('The New objects icon shows the number of unread E-Mails or other notifications. If clicking the icon, the info area opens.'),&lt;br /&gt;
        arrowOffset: -1&lt;br /&gt;
      }, [...]&lt;br /&gt;
&lt;br /&gt;
==== Components of a tour ====&lt;br /&gt;
A tours is a set of steps that are can be navigated by going forward and backwards. It needs...&lt;br /&gt;
{|&lt;br /&gt;
| id&lt;br /&gt;
| This needs to be unique, like for all extension points. Even if you want to overwrite another tour, you need a different id!&lt;br /&gt;
|-&lt;br /&gt;
| app&lt;br /&gt;
| The application this tour is related to. &lt;br /&gt;
|-&lt;br /&gt;
| Priority&lt;br /&gt;
| The tour with the largest number for the priority for a specific app is the one shown. This is how you overwrite an existing tour with your custom one.&lt;br /&gt;
|-&lt;br /&gt;
| tour&lt;br /&gt;
| The steps of a tour&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Tour steps ====&lt;br /&gt;
A tour step is a text bubble that is shown next to a UI element. It needs...&lt;br /&gt;
{|&lt;br /&gt;
| title&lt;br /&gt;
| This needs to be unique, like for all extension points. Even if you want to overwrite another tour, you need a different id!&lt;br /&gt;
|-&lt;br /&gt;
| target&lt;br /&gt;
| The UI element this text bubble is displayed next to. Hopscotch only allows identifiers as passed to JQuery, but App Suite extends this to functions. Most functions used simply return JQuery identifiers, the difference is when it is resolved: Functions are resolved later, which is helpful if your application is built by doing DOM manipulations as late as possible - App Suite is.&lt;br /&gt;
|-&lt;br /&gt;
| content&lt;br /&gt;
| The content of a text bubble. Can be html, as it uses the innerHtml method of JQuery to attach itself.&lt;br /&gt;
|-&lt;br /&gt;
| placement&lt;br /&gt;
| Where the text bubble is placed in relation to the UI element. Possible values are &amp;quot;top&amp;quot;, &amp;quot;bottom&amp;quot;, &amp;quot;left&amp;quot; and &amp;quot;right&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| xOffset&lt;br /&gt;
| Passed directly to hopscotch. Moves the box horizontally relative to its standard position.&lt;br /&gt;
|-&lt;br /&gt;
| yOffset&lt;br /&gt;
| Passed directly to hopscotch. Moves the box vertically relative to its standard position.&lt;br /&gt;
|-&lt;br /&gt;
| arrowOffset&lt;br /&gt;
| Passed directly to hopscotch. Moves the arrow relative to its standard position, horizontally if the bubble is placed &amp;quot;top&amp;quot; or &amp;quot;bottom&amp;quot;, vertically if &amp;quot;left&amp;quot; or &amp;quot;right&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| onShow&lt;br /&gt;
| Passed directly to hopscotch. A function that is executed when the bubble is shown.&lt;br /&gt;
|-&lt;br /&gt;
| onShowDeferred&lt;br /&gt;
| A workaround for onShow: This waits for the deferred to be resolved before showing the bubble.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
And that is all there is to creating a tour.&lt;br /&gt;
&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Administrator]]&lt;br /&gt;
[[Category:Developer]]&lt;br /&gt;
[[Category:Custom development]]&lt;/div&gt;</summary>
		<author><name>Frank.paczynski</name></author>
	</entry>
</feed>