<?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=Matthias.biggeleben</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=Matthias.biggeleben"/>
	<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=Special:Contributions/Matthias.biggeleben"/>
	<updated>2026-08-17T22:34:38Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:OX_Drive&amp;diff=22035</id>
		<title>AppSuite:OX Drive</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:OX_Drive&amp;diff=22035"/>
		<updated>2016-06-20T18:18:32Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* 7.8.1 and later */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= OX Drive =&lt;br /&gt;
&lt;br /&gt;
In OX App Suite, Open-Xchange provides a cloud storage called OX Drive. It provides file- and folder synchronization across multiple devices in the most simplest way for the end user, fully optimized for each device type. This article explains how to set up the server-side components for OX Drive, as well as details about the app setup.&lt;br /&gt;
&lt;br /&gt;
== Key features ==&lt;br /&gt;
&lt;br /&gt;
* Native Apps for Windows, Mac OS, iOS and Android&lt;br /&gt;
* Easy and attractive upsell properties (e.g. Upsell based on Quota limitations)&lt;br /&gt;
* Apps are specially designed for the devices they run on taking into account limitations such as battery life, screen limitations, bandwidth etc.&lt;br /&gt;
* Controlled synchronization of files across devices&lt;br /&gt;
* Storage management (e.g. quota control, upload limits)&lt;br /&gt;
* Connection type recognition and adaptation (e.g. Wi-Fi, cell network)&lt;br /&gt;
&lt;br /&gt;
== Availability ==&lt;br /&gt;
&lt;br /&gt;
OX Drive is a combination of two components:&lt;br /&gt;
* OX Drive in OX App Suite&lt;br /&gt;
* OX Drive Apps (optional native app components for synchronization)&lt;br /&gt;
&lt;br /&gt;
OX Drive is available for the following native apps:&lt;br /&gt;
* OX Drive for Windows&lt;br /&gt;
* OX Drive for Mac OS&lt;br /&gt;
* OX Drive for iOS&lt;br /&gt;
* OX Drive for Android&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
&lt;br /&gt;
You will find the requirements under [[AppSuite:OX_System_Requirements#OX_Drive_for_Clients|OX Drive App and Platform Requirements]]&lt;br /&gt;
&lt;br /&gt;
= Server-side Installation and Configuration =&lt;br /&gt;
&lt;br /&gt;
This chapter describes how the backend components of OX Drive are installed and configured on the server.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
* Open-Xchange Server v7.4.2 and above (&#039;&#039;open-xchange-core&#039;&#039;)&lt;br /&gt;
* Grizzly HTTP connector (&#039;&#039;open-xchange-grizzly&#039;&#039;), see [[AppSuite:Grizzly]] for details, with &#039;&#039;Comet&#039;&#039; support enabled in &#039;&#039;grizzly.properties&#039;&#039;&lt;br /&gt;
* Valid Push-Certificates / API keys for cloud-based notifications, see configuration below&lt;br /&gt;
* Enabled Hazelcast for inter-OX-communication, see [[AppSuite:Running_a_cluster]] for details&lt;br /&gt;
&lt;br /&gt;
== Available packages ==&lt;br /&gt;
&lt;br /&gt;
Open-Xchange Drive is available with the following backend packages:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;open-xchange-drive&#039;&#039; - The main server components for OX Drive&lt;br /&gt;
* &#039;&#039;open-xchange-drive-comet&#039;&#039; - Provides the Push interface via long-polling for the desktop apps&lt;br /&gt;
* &#039;&#039;open-xchange-drive-help-*&#039;&#039; - Online help in various languages for the OX Drive applications (these were called &#039;&#039;open-xchange-appsuite-help-drive-*&#039;&#039; in versions earlier than Open-Xchange Server v7.6.2)&lt;br /&gt;
* &#039;&#039;open-xchange-drive-restricted&#039;&#039; - Restricted components, including prerequisites for cloud-based push notifications&lt;br /&gt;
&lt;br /&gt;
Installation on the server varies depending on the underlying distribution, details are available in the following chapters.&lt;br /&gt;
&lt;br /&gt;
=== OX Drive for Windows ===&lt;br /&gt;
&lt;br /&gt;
Additionally OX Drive for Windows can be provided to users by installing additional packages. However there are significant differences between 7.8.0 and earlier releases and 7.8.1 and later ones:&lt;br /&gt;
&lt;br /&gt;
==== 7.8.0 and earlier ====&lt;br /&gt;
&lt;br /&gt;
OX Drive for Windows can be provided via the [[AppSuite:Open-Xchange_Updater|Open-Xchange Updater]] by installing the package &#039;&#039;open-xchange-updater-drive&#039;&#039;. Initial installation and updates are performed by the Updater then. You don&#039;t have to add the repositories below.&lt;br /&gt;
&lt;br /&gt;
==== 7.8.1 and later ====&lt;br /&gt;
&lt;br /&gt;
With OX App Suite 7.8.1 and OX Drive for Windows 2.0.0, the app is able to update itself and can be downloaded from the Web UI directly via the [[AppSuite:Client_Onboarding|App Onboarding Wizard]]. Therefore the wizard must be installed and configured for OX Drive users. Besides the client onboarding feature you need the following packages:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;open-xchange-drive-client-windows&#039;&#039; - The main server component to provide the app&lt;br /&gt;
* &#039;&#039;open-xchange-drive-client-windows-generic&#039;&#039; - The package providing the final binary files to be installed on users machines.&lt;br /&gt;
&lt;br /&gt;
To make the app download available via the app onboarding wizard, you need to enable the according scenario. Edit &#039;&#039;/opt/open-xchange/etc/client-onboarding-scenarios.yml&#039;&#039; and get to the &#039;&#039;drivewindowsclientinstall&#039;&#039; section, where you have to set the &#039;&#039;enabled&#039;&#039; property to &#039;&#039;true&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Please note that there still exists a compatibility layer between the old Updater-based approach and the new direct install and self-update one. If you start providing OX Drive for Windows with 7.8.1 or later you MAY decide to enable that compatibility by installing &#039;&#039;open-xchange-updater-drive&#039;&#039;, however we advice not to do so. If you already provided OX Drive for Windows with 7.8.0 or earlier, you MUST install this package and further provide the Updater. Otherwise the apps out there will not be able to be updated to the 2.0.0 version, which is then self-update capable.&lt;br /&gt;
&lt;br /&gt;
===== Custom branded apps =====&lt;br /&gt;
&lt;br /&gt;
OX Drive for Windows can be purchased with a custom theming. In such cases, the according binary files need to be installed on the middleware servers. Instead of the &#039;&#039;open-xchange-drive-client-windows-generic&#039;&#039; package, a package containing the branded binaries will be provided as &#039;&#039;open-xchange-drive-client-windows-&amp;lt;brand-name&amp;gt;&#039;&#039; and needs to be installed. You MUST then set the property &#039;&#039;com.openexchange.drive.update.branding&#039;&#039; in &#039;&#039;/opt/open-xchange/etc/drive-client-windows.properties&#039;&#039; to &#039;&#039;&amp;lt;brand-name&amp;gt;&#039;&#039; accordingly.&lt;br /&gt;
&lt;br /&gt;
It is also possible to provide multiple brands of the app via the same OX App Suite deployment, multiple &#039;&#039;open-xchange-drive-client-windows-&amp;lt;brand-name&amp;gt;&#039;&#039; packages can be installed in parallel. Which brand is available for a certain user is determined by the &#039;&#039;com.openexchange.drive.update.branding&#039;&#039; property, which can be overwritten via [[ConfigCascade|Config Cascade]] therefore.&lt;br /&gt;
&lt;br /&gt;
App brands can be managed via command line tools. The tool &#039;&#039;/opt/open-xchange/sbin/listdriveclients&#039;&#039; lists all installed brands. After updating a certain brand by installing a newer package version or installing an additional one, no server restart is necessary. Instead &#039;&#039;/opt/open-xchange/sbin/reloaddriveclients&#039;&#039; can be executed to refresh the internal server state.&lt;br /&gt;
&lt;br /&gt;
=== Redhat Enterprise Linux 6 or CentOS 6 ===&lt;br /&gt;
&lt;br /&gt;
If not already done, add the following repositories to your Open-Xchange yum configuration:&lt;br /&gt;
&lt;br /&gt;
 {{for loop||call=YUMRepo|pv=reponame|pc1n=path|pc1v=products/appsuite/stable|pc2n=rhelname|pc2v=RHEL6|backend}}&lt;br /&gt;
 {{for loop||call=YUMRepo|pv=reponame|pc1n=path|pc1v=products/drive/stable|pc2n=rhelname|pc2v=RHEL6|drive-help}}&lt;br /&gt;
 {{for loop||call=YUMRepo|pv=reponame|pc1n=path|pc1v=products/appsuite/stable|pc2n=rhelname|pc2v=RHEL6|pc3n=ldbaccount|pc3v=LDBUSER:LDBPASSWORD|backend/updates|drive|updater}}&lt;br /&gt;
 {{for loop||call=YUMRepo|pv=reponame|pc1n=path|pc1v=products/drive/stable|pc2n=rhelname|pc2v=RHEL6|pc3n=ldbaccount|pc3v=LDBUSER:LDBPASSWORD|drive-help/updates}}&lt;br /&gt;
 {{for loop||call=YUMRepo|pv=reponame|pc1n=path|pc1v=products/drive/stable|pc2n=rhelname|pc2v=RHEL6|pc3n=ldbaccount|pc3v=LDBUSER:LDBPASSWORD|drive-client-windows}}&lt;br /&gt;
&lt;br /&gt;
and run&lt;br /&gt;
&lt;br /&gt;
 $ yum update&lt;br /&gt;
 $ yum install open-xchange-drive open-xchange-drive-comet open-xchange-drive-restricted open-xchange-drive-client-windows&lt;br /&gt;
&lt;br /&gt;
=== Redhat Enterprise Linux 7 or CentOS 7 ===&lt;br /&gt;
&lt;br /&gt;
If not already done, add the following repositories to your Open-Xchange yum configuration:&lt;br /&gt;
&lt;br /&gt;
 {{for loop||call=YUMRepo|pv=reponame|pc1n=path|pc1v=products/appsuite/stable|pc2n=rhelname|pc2v=RHEL7|backend}}&lt;br /&gt;
 {{for loop||call=YUMRepo|pv=reponame|pc1n=path|pc1v=products/drive/stable|pc2n=rhelname|pc2v=RHEL7|drive-help}}&lt;br /&gt;
 {{for loop||call=YUMRepo|pv=reponame|pc1n=path|pc1v=products/appsuite/stable|pc2n=rhelname|pc2v=RHEL7|pc3n=ldbaccount|pc3v=LDBUSER:LDBPASSWORD|backend/updates|drive|updater}}&lt;br /&gt;
 {{for loop||call=YUMRepo|pv=reponame|pc1n=path|pc1v=products/drive/stable|pc2n=rhelname|pc2v=RHEL7|pc3n=ldbaccount|pc3v=LDBUSER:LDBPASSWORD|drive-help/updates}}&lt;br /&gt;
 {{for loop||call=YUMRepo|pv=reponame|pc1n=path|pc1v=products/drive/stable|pc2n=rhelname|pc2v=RHEL7|pc3n=ldbaccount|pc3v=LDBUSER:LDBPASSWORD|drive-client-windows}}&lt;br /&gt;
&lt;br /&gt;
and run&lt;br /&gt;
&lt;br /&gt;
 $ yum update&lt;br /&gt;
 $ yum install open-xchange-drive open-xchange-drive-comet open-xchange-drive-restricted open-xchange-drive-client-windows&lt;br /&gt;
&lt;br /&gt;
=== Debian GNU/Linux 7.0 ===&lt;br /&gt;
&lt;br /&gt;
If not already done, add the following repositories to your Open-Xchange apt configuration:&lt;br /&gt;
&lt;br /&gt;
 {{for loop||call=APTRepo|pv=reponame|pc1n=path|pc1v=products/appsuite/stable|pc2n=debianname|pc2v=DebianWheezy|backend}}&lt;br /&gt;
 {{for loop||call=APTRepo|pv=reponame|pc1n=path|pc1v=products/drive/stable|pc2n=debianname|pc2v=DebianWheezy|drive-help}}&lt;br /&gt;
 {{for loop||call=APTRepo|pv=reponame|pc1n=path|pc1v=products/appsuite/stable|pc2n=debianname|pc2v=DebianWheezy|pc3n=ldbaccount|pc3v=LDBUSER:LDBPASSWORD|backend/updates|drive|updater}}&lt;br /&gt;
 {{for loop||call=APTRepo|pv=reponame|pc1n=path|pc1v=products/drive/stable|pc2n=debianname|pc2v=DebianWheezy|pc3n=ldbaccount|pc3v=LDBUSER:LDBPASSWORD|drive-help/updates}}&lt;br /&gt;
 {{for loop||call=APTRepo|pv=reponame|pc1n=path|pc1v=products/drive/stable|pc2n=debianname|pc2v=DebianWheezy|pc3n=ldbaccount|pc3v=LDBUSER:LDBPASSWORD|drive-client-windows}}&lt;br /&gt;
&lt;br /&gt;
and run&lt;br /&gt;
&lt;br /&gt;
 $ apt-get update&lt;br /&gt;
 $ apt-get install open-xchange-drive open-xchange-drive-comet open-xchange-drive-restricted open-xchange-drive-client-windows&lt;br /&gt;
&lt;br /&gt;
=== Debian GNU/Linux 8.0 ===&lt;br /&gt;
&lt;br /&gt;
If not already done, add the following repositories to your Open-Xchange apt configuration:&lt;br /&gt;
&lt;br /&gt;
 {{for loop||call=APTRepo|pv=reponame|pc1n=path|pc1v=products/appsuite/stable|pc2n=debianname|pc2v=DebianJessie|backend}}&lt;br /&gt;
 {{for loop||call=APTRepo|pv=reponame|pc1n=path|pc1v=products/drive/stable|pc2n=debianname|pc2v=DebianJessie|drive-help}}&lt;br /&gt;
 {{for loop||call=APTRepo|pv=reponame|pc1n=path|pc1v=products/appsuite/stable|pc2n=debianname|pc2v=DebianJessie|pc3n=ldbaccount|pc3v=LDBUSER:LDBPASSWORD|backend/updates|drive|updater}}&lt;br /&gt;
 {{for loop||call=APTRepo|pv=reponame|pc1n=path|pc1v=products/drive/stable|pc2n=debianname|pc2v=DebianJessie|pc3n=ldbaccount|pc3v=LDBUSER:LDBPASSWORD|drive-help/updates}}&lt;br /&gt;
 {{for loop||call=APTRepo|pv=reponame|pc1n=path|pc1v=products/drive/stable|pc2n=debianname|pc2v=DebianJessie|pc3n=ldbaccount|pc3v=LDBUSER:LDBPASSWORD|drive-client-windows}}&lt;br /&gt;
and run&lt;br /&gt;
&lt;br /&gt;
 $ apt-get update&lt;br /&gt;
 $ apt-get install open-xchange-drive open-xchange-drive-comet open-xchange-drive-restricted open-xchange-drive-client-windows&lt;br /&gt;
&lt;br /&gt;
=== SUSE Linux Enterprise Server 11 ===&lt;br /&gt;
&lt;br /&gt;
Add the package repository using zypper if not already present:&lt;br /&gt;
&lt;br /&gt;
 {{for loop||call=SUSERepo|pv=reponame|pc1n=path|pc1v=products/appsuite/stable|pc2n=susename|pc2v=SLES11|backend}}&lt;br /&gt;
 {{for loop||call=SUSERepo|pv=reponame|pc1n=path|pc1v=products/drive/stable|pc2n=susename|pc2v=SLES11|drive-help}}&lt;br /&gt;
 {{for loop||call=SUSERepo|pv=reponame|pc1n=path|pc1v=products/appsuite/stable|pc2n=susename|pc2v=SLES11|pc3n=ldbaccount|pc3v=LDBUSER:LDBPASSWORD|backend/updates|drive|updater}}&lt;br /&gt;
 {{for loop||call=SUSERepo|pv=reponame|pc1n=path|pc1v=products/drive/stable|pc2n=susename|pc2v=SLES11|pc3n=ldbaccount|pc3v=LDBUSER:LDBPASSWORD|drive-help/updates}}&lt;br /&gt;
 {{for loop||call=SUSERepo|pv=reponame|pc1n=path|pc1v=products/drive/stable|pc2n=susename|pc2v=SLES11|pc3n=ldbaccount|pc3v=LDBUSER:LDBPASSWORD|drive-client-windows}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
and run&lt;br /&gt;
&lt;br /&gt;
 $ zypper ref&lt;br /&gt;
 $ zypper in open-xchange-drive open-xchange-drive-comet open-xchange-drive-restricted open-xchange-drive-client-windows&lt;br /&gt;
&lt;br /&gt;
=== SUSE Linux Enterprise Server 12 ===&lt;br /&gt;
&lt;br /&gt;
Add the package repository using zypper if not already present:&lt;br /&gt;
&lt;br /&gt;
 {{for loop||call=SUSERepo|pv=reponame|pc1n=path|pc1v=products/appsuite/stable|pc2n=susename|pc2v=SLE_12|backend}}&lt;br /&gt;
 {{for loop||call=SUSERepo|pv=reponame|pc1n=path|pc1v=products/drive/stable|pc2n=susename|pc2v=SLE_12|drive-help}}&lt;br /&gt;
 {{for loop||call=SUSERepo|pv=reponame|pc1n=path|pc1v=products/appsuite/stable|pc2n=susename|pc2v=SLE_12|pc3n=ldbaccount|pc3v=LDBUSER:LDBPASSWORD|backend/updates|drive|updater}}&lt;br /&gt;
 {{for loop||call=SUSERepo|pv=reponame|pc1n=path|pc1v=products/drive/stable|pc2n=susename|pc2v=SLE_12|pc3n=ldbaccount|pc3v=LDBUSER:LDBPASSWORD|drive-help/updates}}&lt;br /&gt;
 {{for loop||call=SUSERepo|pv=reponame|pc1n=path|pc1v=products/drive/stable|pc2n=susename|pc2v=SLE_12|pc3n=ldbaccount|pc3v=LDBUSER:LDBPASSWORD|drive-client-windows}}&lt;br /&gt;
&lt;br /&gt;
and run&lt;br /&gt;
&lt;br /&gt;
 $ zypper ref&lt;br /&gt;
 $ zypper in open-xchange-drive open-xchange-drive-comet open-xchange-drive-restricted open-xchange-drive-client-windows&lt;br /&gt;
&lt;br /&gt;
=== OX Server Edition / App Suite for UCS ===&lt;br /&gt;
&lt;br /&gt;
If you have purchased the OX Server Edition / App Suite for UCS, the OX Drive is part of the offering and after the installation/update available. The necessary package for push, is available with a valid license and can be installed via the Univention App Center.&lt;br /&gt;
&lt;br /&gt;
* The new license is already registered at the LDB after purchase.&lt;br /&gt;
* Log on at the Univention Management Console (UMC)&lt;br /&gt;
* Make sure, that the correct LDB account has been selected in the UMC module &amp;quot;OX License Management&amp;quot;&lt;br /&gt;
* Click on &amp;quot;App Center&amp;quot; at the UMC und switch to the tab &amp;quot;Repository Settings&amp;quot;&lt;br /&gt;
* Within the component list, select &amp;quot;Open-Xchange Drive&amp;quot; and press the &amp;quot;Install&amp;quot; button&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
The following gives an overview about the most important settings to enable file synchronization via OX Drive, especially when it comes to real-time Push notifications for the apps.&lt;br /&gt;
&lt;br /&gt;
All settings regarding the OX Drive backend component are located in the configuration file &#039;&#039;drive.properties&#039;&#039;. The default configuration should be sufficient for a basic &amp;quot;up-and-running&amp;quot; setup (with the exception of defining the Push certificates and API keys for cloud-based app notifications, see next chapters). Please refer to the inline documentation of the configuration file for more advanced options. &lt;br /&gt;
&lt;br /&gt;
=== Push via Google Cloud Messaging (GCM) ===&lt;br /&gt;
&lt;br /&gt;
The OX Drive application for Android devices is able to receive Push notifications from the Open-Xchange Server via [http://developer.android.com/google/gcm/index.html Google Cloud Messaging (GCM)]. To issue those Push messages, the backend needs to be provided with a suitable API key for the corresponding Android app application. The API key is included in the restricted components installation package &#039;&#039;open-xchange-drive-restricted&#039;&#039; for the &amp;quot;vanilla&amp;quot; Android app. Alternatively, the key can be specified directly in the &#039;&#039;drive.properties&#039;&#039; configuration file:&lt;br /&gt;
&lt;br /&gt;
 # Specifies the API key of the server application. Required if &lt;br /&gt;
 # &amp;quot;com.openexchange.drive.events.gcm.enabled&amp;quot; is &amp;quot;true&amp;quot; and the package &lt;br /&gt;
 # containing the restricted drive components is not installed.&lt;br /&gt;
 com.openexchange.drive.events.gcm.key=&lt;br /&gt;
&lt;br /&gt;
Push via GCM can be enabled via:&lt;br /&gt;
&lt;br /&gt;
 # Enables or disables push event notifications to apps using the Google&lt;br /&gt;
 # Cloud Messaging (GCM) service. This requires a valid configuration for the &lt;br /&gt;
 # GCM API key, see options below. Defaults to &amp;quot;false&amp;quot;. &lt;br /&gt;
 com.openexchange.drive.events.gcm.enabled=true&lt;br /&gt;
&lt;br /&gt;
Please note that push via GCM needs to be enabled explicitly - also if &#039;&#039;open-xchange-drive-restricted&#039;&#039; is installed.&lt;br /&gt;
&lt;br /&gt;
=== Push via Apple Push Notification service (APNs) ===&lt;br /&gt;
&lt;br /&gt;
The OX Drive application for iOS and Mac OS devices is able to receive Push notifications from the Open-Xchange Server via [http://developer.apple.com/library/IOS/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html Apple Push Notification service (APNs)]. To issue those Push messages, the backend needs to be provided with a suitable keystore container file (PKCS #12) containing the APNs certificate and keys. Note that the Mac OS desktop app and the iOS mobile app are served separately with different certificates, so that both needs to be configured independantly. The required certificates are already included in the restricted components installation package &#039;&#039;open-xchange-drive-restricted&#039;&#039; for the &amp;quot;vanilla&amp;quot; iOS and Mac OS app applications. Therefore no additional configuration is needed. Alternatively, a external certificate can be specified directly in the &#039;&#039;drive.properties&#039;&#039; configuration file (the following only shows the setup for iOS). First, the path to the PKCS #12 container file needs to be specified at:&lt;br /&gt;
&lt;br /&gt;
 # Specifies the path to the local keystore file (PKCS #12) containing the APNS &lt;br /&gt;
 # certificate and keys for the iOS application, e.g. &lt;br /&gt;
 # &amp;quot;/opt/open-xchange/etc/drive-apns.p12&amp;quot;. Required if &lt;br /&gt;
 # &amp;quot;com.openexchange.drive.events.apn.enabled&amp;quot; is &amp;quot;true&amp;quot; and the package &lt;br /&gt;
 # containing the restricted drive components is not installed.&lt;br /&gt;
 com.openexchange.drive.events.apn.ios.keystore=&lt;br /&gt;
&lt;br /&gt;
This file is opened by the backend using the password as supplied via: &lt;br /&gt;
 &lt;br /&gt;
 # Specifies the password used when creating the referenced keystore containing&lt;br /&gt;
 # the certificate of the iOS application. Note that blank or null passwords &lt;br /&gt;
 # are in violation of the PKCS #12 specifications. Required if &lt;br /&gt;
 # &amp;quot;com.openexchange.drive.events.apn.enabled&amp;quot; is &amp;quot;true&amp;quot; and the package &lt;br /&gt;
 # containing the restricted drive components is not installed.&lt;br /&gt;
 com.openexchange.drive.events.apn.ios.password=&lt;br /&gt;
&lt;br /&gt;
Configuration also allows to switch between development and production environments, however, this setting should be &#039;&#039;true&#039;&#039; normally:&lt;br /&gt;
&lt;br /&gt;
 # Indicates which APNS service is used when sending push notifications to iOS&lt;br /&gt;
 # devices. A value of &amp;quot;true&amp;quot; will use the production service, a value of &lt;br /&gt;
 # &amp;quot;false&amp;quot; the sandbox service. Defaults to &amp;quot;true&amp;quot;.&lt;br /&gt;
 com.openexchange.drive.events.apn.ios.production=true&lt;br /&gt;
&lt;br /&gt;
The OX backend contacts the APNs servers from time to time to get informed about apps no longer reachable apps where the applications was uninstalled. The interval can be defined with the following setting: &lt;br /&gt;
&lt;br /&gt;
 # Configures the interval between queries to the APN feedback service for the&lt;br /&gt;
 # subscribed iOS devices. The value can be defined using units of measurement: &lt;br /&gt;
 # &amp;quot;D&amp;quot; (=days), &amp;quot;W&amp;quot; (=weeks) and &amp;quot;H&amp;quot; (=hours). Defaults to &amp;quot;1D&amp;quot; (one day). &lt;br /&gt;
 # Leaving this parameter empty disables the feedback queries on this node. &lt;br /&gt;
 # Since each received feedback is processed cluster-wide, only one node in the &lt;br /&gt;
 # cluster should be enabled here. &lt;br /&gt;
 com.openexchange.drive.events.apn.ios.feedbackQueryInterval=1D&lt;br /&gt;
&lt;br /&gt;
Please note that if you have multiple backend nodes in the cluster, it&#039;s recommended that only one node is configured to contact the feedback query service. Finally, Push notifications via APN for iOS can be enabled via:&lt;br /&gt;
&lt;br /&gt;
 # Enables or disables push event notifications to apps using the Apple Push&lt;br /&gt;
 # Notification service (APNS) for Mac OS devices. This requires a valid &lt;br /&gt;
 # configuration for the APNS certificate and keys, see either options below, &lt;br /&gt;
 # or install the restricted components packages for drive. Defaults to &lt;br /&gt;
 # &amp;quot;false&amp;quot;. &lt;br /&gt;
 com.openexchange.drive.events.apn.ios.enabled=false&lt;br /&gt;
&lt;br /&gt;
As stated above, configuration for Push notifications via APN for the Mac OS desktop application is configured similarly, the relevant options are prefixed with &#039;&#039;com.openexchange.drive.events.apn.macos&#039;&#039;. Please also note that push via APNS needs to be enabled explicitly - also if &#039;&#039;open-xchange-drive-restricted&#039;&#039; is installed.&lt;br /&gt;
&lt;br /&gt;
=== Further Configuration ===&lt;br /&gt;
&lt;br /&gt;
* The backend component of OX Drive supplies the apps with various hyperlinks, e.g. deep-links to files and folders in the groupware webinterface or an URL to the online help. In order to point to the suitable web interface, please ensure that the correct UI web path is configured via &#039;&#039;com.openexchange.UIWebPath&#039;&#039; located in &#039;&#039;server.properties&#039;&#039;.&lt;br /&gt;
* As already mentioned above, the backend relies on the [https://grizzly.java.net/comet.html Comet] component of the Grizzly http connector for sending push notifications to the desktop apps. Therefore, &#039;&#039;com.openexchange.http.grizzly.hasCometEnabled&#039;&#039; needs to be set to &#039;&#039;true&#039;&#039; in &#039;&#039;grizzly.properties&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Enabling OX Drive for Users =&lt;br /&gt;
&lt;br /&gt;
OX Drive is enabled for all users that have the capability &#039;&#039;com.openexchange.capability.drive&#039;&#039;. Please note that users need to have the &#039;&#039;infostore&#039;&#039; permission set to use drive. So the users that have &#039;&#039;drive&#039;&#039; enabled must be a subset of those users with &#039;&#039;infostore&#039;&#039; permission. Since 7.6.0 we enforce this via the default configuration. You can also enable this cabaility globally with the following setting in the &#039;&#039;drive.properties&#039;&#039; configuration file:&lt;br /&gt;
&lt;br /&gt;
 # Enables or disables the &amp;quot;drive&amp;quot; module capability globally. The capability&lt;br /&gt;
 # can also be set more fine-grained via config cascade. Per default it is only&lt;br /&gt;
 # enabled for users that have the &amp;quot;infostore&amp;quot; permission set. This is configured&lt;br /&gt;
 # in /opt/open-xchange/etc/contextSets/drive.yml.&lt;br /&gt;
 com.openexchange.capability.drive=false&lt;br /&gt;
&lt;br /&gt;
More details about capabilities can be found at [[AppSuite:Capabilities]]. Furthermore, this capability can be defined in a more granular way using the Config Cascade as described at [[ConfigCascade]].&lt;br /&gt;
&lt;br /&gt;
= Installation of the Apps =&lt;br /&gt;
&lt;br /&gt;
== Installation of Mac OS X Desktop App ==&lt;br /&gt;
&lt;br /&gt;
The OX Drive for Mac OS X will be provided via the Apple App Store:&lt;br /&gt;
&lt;br /&gt;
* https://itunes.apple.com/app/ox-drive/id818195014?mt=12&lt;br /&gt;
&lt;br /&gt;
=== Enabling the Finder Extension on Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
In order to be able to use the Finder to open documents or share objects on a Mac OS X workstation, you need to enable the Drive Finder Extension.&lt;br /&gt;
&lt;br /&gt;
How to enable the Drive Finder Extension on a Mac OS X workstation:&lt;br /&gt;
# Open the System Preferences. In the System Preferences window, select Extensions.&lt;br /&gt;
# In the Extensions window, select Finder. Enable Drive Finder Extension.&lt;br /&gt;
&lt;br /&gt;
== Installation of Windows Desktop App ==&lt;br /&gt;
&lt;br /&gt;
See [[AppSuite:OX_Drive#OX_Drive_for_Windows|OX Drive for Windows]].&lt;br /&gt;
&lt;br /&gt;
== Installation on Mobile Apps ==&lt;br /&gt;
&lt;br /&gt;
The OX Drive App is available via the different App Stores:&lt;br /&gt;
&lt;br /&gt;
* iOS: https://itunes.apple.com/app/ox-drive/id798570177?mt=8&lt;br /&gt;
&lt;br /&gt;
* Android: https://play.google.com/store/apps/details?id=com.openexchange.drive.vanilla&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== App Configuration and Deployment ==&lt;br /&gt;
&lt;br /&gt;
The user needs to enter the server URL and provide his username and password. Afterwards, app-specific settings may be configured. This includes the synchronization mode (All files / Favorites only) and Photostream settings on mobile devices, or the local root synchronization folder for the desktop applications. More information is available in the online documentation. &lt;br /&gt;
&lt;br /&gt;
After the initial synchronization is completed, all further changes are synchronized instantly across all devices.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
You will find it under [http://knowledgebase.open-xchange.com/sales-marketing/additional-software/frequently-asked-questions/ox-drive-for-clients.html OX Drive App and Platform FAQ].&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=Portal:AppSuite_UI/Topics&amp;diff=22034</id>
		<title>Portal:AppSuite UI/Topics</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=Portal:AppSuite_UI/Topics&amp;diff=22034"/>
		<updated>2016-06-20T18:17:14Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: Fixed typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Getting started ==&lt;br /&gt;
* [[AppSuite:GettingStarted | Getting started developing an external app]]&lt;br /&gt;
* [[AppSuite:Upgrade_app_using_yo | Upgrade your external app to latest App Suite version]]&lt;br /&gt;
* [[AppSuite:UI developer primer| Skills needed to develop the UI]]&lt;br /&gt;
* [[AppSuite:Getting started developing the UI | Getting started developing the UI]]&lt;br /&gt;
* [[AppSuite:UI_Development_Style_Guide | UI Development Style Guide]]&lt;br /&gt;
* [[AppSuite:Appserver | Appserver]]&lt;br /&gt;
* [[AppSuite:Apache Configuration | Apache Configuration]]&lt;br /&gt;
* [[AppSuite:Definition of done|Definition of done]]&lt;br /&gt;
* [[Appsuite:UI_FAQ | Frequently asked questions]]&lt;br /&gt;
&lt;br /&gt;
== Upgrading apps, plugins, and themes to 7.6 ==&lt;br /&gt;
* [[AppSuite:Upgrading_to_7_6|Upgrading to 7.6]]&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
* [[AppSuite:Test basics | Lessons learned while testing]]&lt;br /&gt;
* [[AppSuite:RunTests | Running ui tests]]&lt;br /&gt;
* [[AppSuite:Testing_3rd-party_code | In-depth article on testing]] aimed at external developers&lt;br /&gt;
&lt;br /&gt;
== How-to articles ==&lt;br /&gt;
* [[AppSuite:Action links|Understanding action links]]&lt;br /&gt;
** [[AppSuite:Files App Actions|Adding actions to the files app]]&lt;br /&gt;
* [[AppSuite:Date_and_time|Date and time]]&lt;br /&gt;
* [[AppSuite:i18n | Internationalization (i18n)]]&lt;br /&gt;
* [[AppSuite:a11y | Accessibility (a11y)]]&lt;br /&gt;
* [[AppSuite:Mediaplayer | Mediaplayer]]&lt;br /&gt;
* [[AppSuite:Theming | Theming]]&lt;br /&gt;
** [[AppSuite:Dynamic Theme|The Dynamic Theme plugin]]&lt;br /&gt;
* [[AppSuite:UI manifests explained | UI manifests explained]]&lt;br /&gt;
* [[AppSuite:Upsell | Upsell]]&lt;br /&gt;
* [[AppSuite:VGrid | VGrid]]&lt;br /&gt;
* [[AppSuite:Mobile | Mobile development]]&lt;br /&gt;
* [[AppSuite:Upsell tools| Upsell tools]]&lt;br /&gt;
* [[AppSuite:Guided tours| Guided tours]]&lt;br /&gt;
* [[AppSuite:Browserdetection | Browser detection with form-login]]&lt;br /&gt;
* [[AppSuite:BackboneMiniViews | Backbone mini views]]&lt;br /&gt;
&lt;br /&gt;
=== Server communication ===&lt;br /&gt;
* [[AppSuite:APIs | APIs]]&lt;br /&gt;
* [[AppSuite:API_Factory | API Factory]]&lt;br /&gt;
* [[AppSuite:http.js | http.js]]&lt;br /&gt;
&lt;br /&gt;
=== Extension points ===&lt;br /&gt;
* [[AppSuite:Extending_the_UI_(Hands-on_introduction)| Hands-on introduction]]&lt;br /&gt;
* [[AppSuite:Extending_the_UI | General information on extension points]]&lt;br /&gt;
* [[AppSuite:Modifying forms by using extension points | Modifying forms]]&lt;br /&gt;
* [[AppSuite:Create custom folderview entries in settings app | Edit folderview entries in settings]]&lt;br /&gt;
* [[AppSuite:Extension points for calendar |extension points: Calendar]]&lt;br /&gt;
* [[AppSuite:Extension points for contact |extension points: Contact]]&lt;br /&gt;
* [[AppSuite:Extension points for core | extension points: Core]]&lt;br /&gt;
* [[AppSuite:Extension points for email | extension points: E-Mail]]&lt;br /&gt;
* [[AppSuite:Extension points for files |extension points: Files]]&lt;br /&gt;
* [[AppSuite:Extension points for tasks |extension points: Tasks]]&lt;br /&gt;
* [[AppSuite:Extension points for miscellaneous |extension points: Miscellaneous]]&lt;br /&gt;
&lt;br /&gt;
=== Writing components ===&lt;br /&gt;
* [[AppSuite:Writing a portal plugin | Writing a portal plugin]]&lt;br /&gt;
** [[AppSuite:Configuring portal plugins|Configuring a portal plugin]]&lt;br /&gt;
** [[AppSuite:Using the Upsell widget|Using the Upsell widget]]&lt;br /&gt;
* [[AppSuite:Writing a simple application | Writing a simple application]]&lt;br /&gt;
* [[AppSuite:Writing a simple application with embedded iframe  | Writing a simple application with embedded iframe and launcher link ]]&lt;br /&gt;
* [[AppSuite:Writing a notification area plugin | Writing a plugin for the notification area]]&lt;br /&gt;
* [[AppSuite:Writing a wizard | Writing a wizard]]&lt;br /&gt;
* [[AppSuite:Capabilities | Capabilities explained]] - if you want to enable or disable your plugin&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous articles ==&lt;br /&gt;
* [[AppSuite:External libraries for the UI | External libraries used by the UI]]&lt;br /&gt;
* [[AppSuite:Configuration| Configuration]]&lt;br /&gt;
* [[AppSuite:Custom configurations| Custom configurations]]&lt;br /&gt;
* [[AppSuite:UI build system| The UI build system]]&lt;br /&gt;
* [[AppSuite:Embedding your settings into AppSuite settings|How to embed your own settings into the Appsuite settings page]]&lt;br /&gt;
* [[AppSuite:Debugging the UI|Debugging the UI]]&lt;br /&gt;
* [[Appsuite:Debugging production servers|Debugging production servers]]&lt;br /&gt;
* [[AppSuite:Memory leaks (UI)|Finding memory leaks in the UI]]&lt;br /&gt;
* [[AppSuite:Device reference|_.device() reference]]&lt;br /&gt;
* [[AppSuite:Paste inline images|Paste inline images]]&lt;br /&gt;
* [[AppSuite:Client Onboarding|Client Onboarding]]&lt;br /&gt;
* All articles regarding the UI are filed in the  category [[:Category:UI]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Client_Onoarding&amp;diff=22033</id>
		<title>AppSuite:Client Onoarding</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Client_Onoarding&amp;diff=22033"/>
		<updated>2016-06-20T18:13:32Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: Redirected page to &amp;quot;AppSuite:Client Onboarding&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[&amp;quot;AppSuite:Client_Onboarding&amp;quot;]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Client_Onoarding&amp;diff=22032</id>
		<title>AppSuite:Client Onoarding</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Client_Onoarding&amp;diff=22032"/>
		<updated>2016-06-20T18:10:48Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: Delete&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Embedding_your_settings_into_AppSuite_settings&amp;diff=21625</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=21625"/>
		<updated>2016-03-08T08:57:05Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Declare the page you want to embed */&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;
&#039;&#039;Synopsis:&#039;&#039; This article explains how you can embed your own configuration page via iFrame into the AppSuite&#039;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;
Before you start you need to know the group where you want to add the settings (since 7.8.0):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
gerneral:&lt;br /&gt;
Basic Settings&lt;br /&gt;
Accounts&lt;br /&gt;
&lt;br /&gt;
main:&lt;br /&gt;
Portal&lt;br /&gt;
Mail&lt;br /&gt;
Guard&lt;br /&gt;
Address Book&lt;br /&gt;
Calendar&lt;br /&gt;
Tasks&lt;br /&gt;
Drive&lt;br /&gt;
&lt;br /&gt;
tools:&lt;br /&gt;
Pubications and Subscriptions&lt;br /&gt;
Error Log&lt;br /&gt;
Download&lt;br /&gt;
&lt;br /&gt;
external:&lt;br /&gt;
Groups&lt;br /&gt;
Resources&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the layout of the settings page might differ depending on which packages you have installed and/or which capabilities the user has.&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;
    group: general&lt;br /&gt;
    title: &amp;quot;Change Plan&amp;quot;&lt;br /&gt;
    before: &amp;quot;io.ox/settings/accounts&amp;quot;&lt;br /&gt;
    advancedMode: false # not needed anymore for 7.8.0&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;io.ox/settings/configjump&#039;&#039; 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;
* &#039;&#039;&#039;url&#039;&#039;&#039;: 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;
* &#039;&#039;&#039;title&#039;&#039;&#039;: The title as to be seen on the settings page.&lt;br /&gt;
* &#039;&#039;&#039;group&#039;&#039;&#039;: Settings section where the page is supposed to appear. Valid groups are: &amp;quot;general&amp;quot;, &amp;quot;main&amp;quot;, &amp;quot;tools&amp;quot;, and &amp;quot;external&amp;quot;. Default is &amp;quot;tools&amp;quot;. For settings inside folders you have to use the group and the name of the config object, for example: group: main/io.ox/mail&lt;br /&gt;
* &#039;&#039;&#039;after&#039;&#039;&#039;, &#039;&#039;&#039;before&#039;&#039;&#039; or &#039;&#039;&#039;index&#039;&#039;&#039;: To define the exact location of the page. &#039;&#039;Hint:&#039;&#039; 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 (for example http://my.ox/appsuite/#!!&amp;amp;app=io.ox/settings&amp;amp;folder=virtual/settings/io.ox/tasks will be io.ox/tasks)&lt;br /&gt;
* &#039;&#039;&#039;requires&#039;&#039;&#039;: A string of capabilities. Supports boolean operators like &amp;amp;&amp;amp; (AND), || (OR), ! (NOT), as well as parentheses. Example: !guest.&lt;br /&gt;
&lt;br /&gt;
It&#039;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;
* &#039;&#039;&#039;token&#039;&#039;&#039;: The token you want to trade.&lt;br /&gt;
* &#039;&#039;&#039;secret&#039;&#039;&#039;: 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>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Embedding_your_settings_into_AppSuite_settings&amp;diff=21624</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=21624"/>
		<updated>2016-03-08T08:56:34Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Declare the page you want to embed */&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;
&#039;&#039;Synopsis:&#039;&#039; This article explains how you can embed your own configuration page via iFrame into the AppSuite&#039;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;
Before you start you need to know the group where you want to add the settings (since 7.8.0):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
gerneral:&lt;br /&gt;
Basic Settings&lt;br /&gt;
Accounts&lt;br /&gt;
&lt;br /&gt;
main:&lt;br /&gt;
Portal&lt;br /&gt;
Mail&lt;br /&gt;
Guard&lt;br /&gt;
Address Book&lt;br /&gt;
Calendar&lt;br /&gt;
Tasks&lt;br /&gt;
Drive&lt;br /&gt;
&lt;br /&gt;
tools:&lt;br /&gt;
Pubications and Subscriptions&lt;br /&gt;
Error Log&lt;br /&gt;
Download&lt;br /&gt;
&lt;br /&gt;
external:&lt;br /&gt;
Groups&lt;br /&gt;
Resources&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the layout of the settings page might differ depending on which packages you have installed and/or which capabilities the user has.&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;
    group: general&lt;br /&gt;
    title: &amp;quot;Change Plan&amp;quot;&lt;br /&gt;
    before: &amp;quot;io.ox/settings/accounts&amp;quot;&lt;br /&gt;
    advancedMode: false # not needed anymore for 7.8.0&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;io.ox/settings/configjump&#039;&#039; 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;
* &#039;&#039;&#039;url&#039;&#039;&#039;: 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;
* &#039;&#039;&#039;title&#039;&#039;&#039;: The title as to be seen on the settings page.&lt;br /&gt;
* &#039;&#039;&#039;group&#039;&#039;&#039;: Settings section where the page is supposed to appear. Valid groups are: &amp;quot;general&amp;quot;, &amp;quot;main&amp;quot;, &amp;quot;tools&amp;quot;, and &amp;quot;external&amp;quot;. Default is &amp;quot;tools&amp;quot;. For settings inside folders you have to use the group and the name of the config object, for example: group: main/io.ox/mail&lt;br /&gt;
* &#039;&#039;&#039;after&#039;&#039;&#039;, &#039;&#039;&#039;before&#039;&#039;&#039; or &#039;&#039;&#039;index&#039;&#039;&#039;: To define the exact location of the page. &#039;&#039;Hint:&#039;&#039; 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 (for example http://my.ox/appsuite/#!!&amp;amp;app=io.ox/settings&amp;amp;folder=virtual/settings/io.ox/tasks will be io.ox/tasks)&lt;br /&gt;
* &#039;&#039;&#039;requires&#039;&#039;&#039; A string of capabilities. Supports boolean operators like &amp;amp;&amp;amp; (AND), || (OR), ! (NOT), as well as parentheses. Example: !guest.&lt;br /&gt;
&lt;br /&gt;
It&#039;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;
* &#039;&#039;&#039;token&#039;&#039;&#039;: The token you want to trade.&lt;br /&gt;
* &#039;&#039;&#039;secret&#039;&#039;&#039;: 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>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Embedding_your_settings_into_AppSuite_settings&amp;diff=21474</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=21474"/>
		<updated>2016-02-12T12:10:17Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: Added groups&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;
&#039;&#039;Synopsis:&#039;&#039; This article explains how you can embed your own configuration page via iFrame into the AppSuite&#039;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;
&#039;&#039;io.ox/settings/configjump&#039;&#039; 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;
* &#039;&#039;&#039;url&#039;&#039;&#039;: 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;
* &#039;&#039;&#039;title&#039;&#039;&#039;: The title as to be seen on the settings page.&lt;br /&gt;
* &#039;&#039;&#039;group&#039;&#039;&#039;: Settings section where the page is supposed to appear. Valid groups are: &amp;quot;general&amp;quot;, &amp;quot;main&amp;quot;, &amp;quot;tools&amp;quot;, and &amp;quot;external&amp;quot;. Default is &amp;quot;tools&amp;quot;.&lt;br /&gt;
* &#039;&#039;&#039;after&#039;&#039;&#039;, &#039;&#039;&#039;before&#039;&#039;&#039; or &#039;&#039;&#039;index&#039;&#039;&#039;: To define the exact location of the page. &#039;&#039;Hint:&#039;&#039; 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;
&lt;br /&gt;
It&#039;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;
* &#039;&#039;&#039;token&#039;&#039;&#039;: The token you want to trade.&lt;br /&gt;
* &#039;&#039;&#039;secret&#039;&#039;&#039;: 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>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Upsell&amp;diff=20902</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=20902"/>
		<updated>2015-11-18T12:20:33Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Upsell Wizard */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Upsell&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Abstract.&#039;&#039;&#039; 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 &#039;&#039;capabilities&#039;&#039;. UI, however, offers functionality beyond that limited set for promotion purposes. Actions, e.g. inline links, that require missing capabilities trigger an &#039;&#039;&#039;in-app upsell&#039;&#039;&#039;. 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 &#039;&#039;loosely coupled&#039;&#039; 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 &#039;&#039;&#039;upsell-appsuite.properties&#039;&#039;&#039; or append to existing &#039;&#039;&#039;appsuite.properties&#039;&#039;&#039; (mind the &#039;&#039;&#039;double-slash&#039;&#039;&#039;; this in not a typo!). If you configure upsell in the &#039;&#039;&#039;upsell-appsuite.properties&#039;&#039;&#039; the properties are loaded when you trigger the &#039;&#039;&#039;live reload&#039;&#039;&#039; 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 &#039;&#039;&#039;&amp;quot;&amp;amp;demo=upsell&amp;quot;&#039;&#039;&#039; 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 &#039;&#039;&#039;active_sync&#039;&#039;&#039; OR &#039;&#039;&#039;caldav&#039;&#039;&#039; OR &#039;&#039;&#039;carddav&#039;&#039;&#039;. That means, if one of those capabilities is not set for a user and the upsell is activated for &#039;&#039;&#039;active_sync&#039;&#039;&#039; AND &#039;&#039;&#039;caldav&#039;&#039;&#039; AND &#039;&#039;&#039;carddav&#039;&#039;&#039; 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 &#039;&#039;&#039;.properties&#039;&#039;&#039; 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 &#039;custom&#039; and with id &#039;secondary-toolbar&#039; 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 &#039;&#039;&#039;fa-star&#039;&#039;&#039; 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 &#039;&#039;&#039;.properties&#039;&#039;&#039; 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 &#039;&#039;&#039;requires&#039;&#039;&#039; 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 &#039;&#039;&#039;upgrade to premium&#039;&#039;&#039; 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/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/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;
| 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 &#039;&#039;&#039;imageURL&#039;&#039;&#039; and can make this widget removable by setting &#039;&#039;&#039;removable&#039;&#039;&#039; 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 &#039;&#039;&#039;upsellLimit&#039;&#039;&#039; (in Bytes). If the maximum mail quota is larger than &#039;&#039;&#039;upsellLimit&#039;&#039;&#039;, 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 &#039;&#039;plugins/upsell/simple-wizard&#039;&#039; by [[AppSuite:Capabilities#Set_a_capability|setting the capability]] &#039;&#039;&#039;simple-wizard&#039;&#039;&#039; server-side (or by adding it to the URL &#039;&#039;&#039;...&amp;amp;cap=simple-wizard&#039;&#039;&#039; for testing/development purposes).&lt;br /&gt;
&lt;br /&gt;
This plugin registers for the event &#039;&#039;&amp;quot;upsell:requires-upsell&amp;quot;&#039;&#039;, 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 &#039;&#039;&#039;upsell.properties&#039;&#039;&#039; or append to existing &#039;&#039;&#039;appsuite.properties&#039;&#039;&#039; (mind the &#039;&#039;&#039;double-slash&#039;&#039;&#039;; 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. &#039;&#039;$missing&#039;&#039; 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&#039;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&#039;s imap login&lt;br /&gt;
|-&lt;br /&gt;
| $language&lt;br /&gt;
| The current user&#039;s language, e.g. de_DE or en_US&lt;br /&gt;
|-&lt;br /&gt;
| $mail&lt;br /&gt;
| The current user&#039;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&#039;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&#039;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&#039;s numeric id&lt;br /&gt;
|-&lt;br /&gt;
| $user_login&lt;br /&gt;
| The current user&#039;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(&#039;plugins/upsell/simple-wizard/register&#039;);&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&#039;t use this in production plugins, it&#039;s just a hack for console: &lt;br /&gt;
   // if you don&#039;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([&#039;plugins/upsell/simple-wizard/register&#039;], 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: &#039;app&#039;, id: &#039;io.ox/files&#039;, missing: &#039;files&#039; });&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: &#039;app&#039;, id: &#039;io.ox/files&#039;, missing: &#039;files&#039; });&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(&#039;upsell:simple-wizard:show:before&#039;, _.inspect);&lt;br /&gt;
ox.on(&#039;upsell:simple-wizard:show&#039;, _.inspect);&lt;br /&gt;
ox.on(&#039;upsell:simple-wizard:close&#039;, _.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(&#039;upsell:simple-wizard:init&#039;, _.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(&#039;plugins/upsell/simple-wizard/register&#039;); &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: &#039;5px solid #08c&#039;, boxSizing: &#039;border-box&#039; });&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(&#039;upsell:simple-wizard:show:before&#039;, function (e, popup) {&lt;br /&gt;
  popup.getContentNode().find(&#039;iframe&#039;)&lt;br /&gt;
    .css({ border: &#039;5px solid #08c&#039;, boxSizing: &#039;border-box&#039; });&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(&#039;plugins/upsell/simple-wizard/register&#039;);&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(&#039;apps/plugins/upsell/simple-wizard/close.html&#039;);&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 &#039;&#039;close.html&#039;&#039;. Since this page is part of the UI and therefore located on the same domain, it is allowed to call the wizard&#039;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(&#039;io.ox/calendar/detail/actions/sendmail&#039;, {&lt;br /&gt;
    // this action requires the capability &amp;quot;webmail&amp;quot;&lt;br /&gt;
    capabilities: &#039;webmail&#039;,&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(&#039;upsell:requires-upgrade&#039;);&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&#039;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(&#039;id&#039;).sort();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An example: Free-mail users might just have &#039;&#039;&#039;webmail&#039;&#039;&#039; and &#039;&#039;&#039;contacts&#039;&#039;&#039;. If &#039;&#039;&#039;infostore&#039;&#039;&#039; 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 &#039;&#039;&#039;infostore&#039;&#039;&#039; 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&#039;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 &#039;&#039;&amp;quot;upsell:requires-upgrade&amp;quot;&#039;&#039; 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([&#039;io.ox/core/tk/dialogs&#039;], function (dialogs) {&lt;br /&gt;
        new dialogs.ModalDialog({ easyOut: true })&lt;br /&gt;
            .build(function () {&lt;br /&gt;
                this.getHeader().append(&lt;br /&gt;
                    $(&#039;&amp;lt;h4&amp;gt;&#039;).text(&#039;Upgrade required&#039;)&lt;br /&gt;
                );&lt;br /&gt;
                this.getContentNode().append(&lt;br /&gt;
                    $.txt(&#039;This feature is not available.&#039;),&lt;br /&gt;
                    $.txt(&#039;You need to upgrade your account now.&#039;),&lt;br /&gt;
                    $.txt(&#039; &#039;),&lt;br /&gt;
                    $.txt(&#039;The first 90 days are free.&#039;)&lt;br /&gt;
                );&lt;br /&gt;
                this.addPrimaryButton(&#039;upgrade&#039;, &#039;Get free upgrade&#039;);&lt;br /&gt;
                this.addButton(&#039;cancel&#039;, &#039;Cancel&#039;);&lt;br /&gt;
            })&lt;br /&gt;
            .setUnderlayStyle({&lt;br /&gt;
                opacity: 0.70,&lt;br /&gt;
                backgroundColor: &#039;#08C&#039;&lt;br /&gt;
            })&lt;br /&gt;
            .on(&#039;upgrade&#039;, function () {&lt;br /&gt;
                ox.trigger(&#039;upsell:upgrade&#039;, options);&lt;br /&gt;
            })&lt;br /&gt;
            .on(&#039;show&#039;, function () {&lt;br /&gt;
                ox.off(&#039;upsell:requires-upgrade&#039;, showUpgradeDialog);&lt;br /&gt;
            })&lt;br /&gt;
            .on(&#039;close&#039;, function () {&lt;br /&gt;
                ox.on(&#039;upsell:requires-upgrade&#039;, 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(&#039;upgrade&#039;, options);&lt;br /&gt;
    alert(&#039;User decided to upgrade! (global event: upsell:upgrade)&#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ox.on(&#039;upsell:requires-upgrade&#039;, showUpgradeDialog);&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * convention: &#039;upsell:upgrade&#039; 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(&#039;upsell:upgrade&#039;, upgrade);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The second event &#039;&#039;&#039;&amp;quot;upsell:upgrade&amp;quot;&#039;&#039;&#039; 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&#039;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(&#039;plugins/portal/upsell/register&#039;,&lt;br /&gt;
    [&#039;io.ox/core/extensions&#039;,&lt;br /&gt;
     &#039;io.ox/files/api&#039;,&lt;br /&gt;
     &#039;gettext!plugins/portal&#039;], function (ext, api, gt) {&lt;br /&gt;
&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var title = gt(&#039;Upgrade to premium&#039;);&lt;br /&gt;
&lt;br /&gt;
    ext.point(&#039;io.ox/portal/widget/upsell&#039;).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(&#039;hide-title&#039;).append(&lt;br /&gt;
                $(&#039;&amp;amp;lt;div class=&amp;quot;content centered&amp;quot; style=&amp;quot;cursor: pointer; padding-top: 3em;&amp;quot;&amp;gt;&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;amp;lt;h2&amp;gt;&#039;).append(&lt;br /&gt;
                        $.txt(title + &#039; &#039;),&lt;br /&gt;
                        $(&#039;&amp;amp;lt;i class=&amp;quot;icon-star&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
                    ),&lt;br /&gt;
                    $(&#039;&amp;amp;lt;div&amp;gt;&#039;).text(gt(&#039;Click here for free trial.&#039;))&lt;br /&gt;
                )&lt;br /&gt;
                .on(&#039;click&#039;, function () {&lt;br /&gt;
                    ox.trigger(&#039;upsell:upgrade&#039;, {&lt;br /&gt;
                        type: &#039;widget&#039;,&lt;br /&gt;
                        id: &#039;io.ox/portal/widget/upsell&#039;,&lt;br /&gt;
                        missing: &#039;&#039;&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 &#039;&#039;&amp;quot;io.ox/core&amp;quot;&#039;&#039;, the path is &#039;&#039;&amp;quot;upsell/enabled&amp;quot;&#039;&#039;. 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(&#039;settings!io.ox/core&#039;).get(&#039;upsell/enabled&#039;);&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 &#039;&#039;&#039;not&#039;&#039;&#039; 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 &#039;&#039;&amp;quot;upsell:requires-upgrade&amp;quot;&#039;&#039; 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(&#039;io.ox/core/upsell&#039;);&lt;br /&gt;
&lt;br /&gt;
// check capabilities (space-separated) &lt;br /&gt;
upsell.has(&#039;portal webmail&#039;);&lt;br /&gt;
&lt;br /&gt;
// get missing capabilities (would return &amp;quot;calendar&amp;quot; in demo mode) &lt;br /&gt;
upsell.missing([&#039;portal webmail&#039;, &#039;contacts&#039;, &#039;calendar&#039;]);&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([&#039;portal webmail&#039;, &#039;webmail calendar&#039;]);&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([&#039;portal webmail&#039;, &#039;contacts&#039;, &#039;calendar&#039;]);&lt;br /&gt;
&lt;br /&gt;
// likewise if neither capability set nor enabled for upsell, we get a false &lt;br /&gt;
upsell.visible([&#039;foo&#039;]);&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(&#039;id&#039;).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>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Upsell&amp;diff=20901</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=20901"/>
		<updated>2015-11-18T12:15:15Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Upsell Wizard */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Upsell&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Abstract.&#039;&#039;&#039; 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 &#039;&#039;capabilities&#039;&#039;. UI, however, offers functionality beyond that limited set for promotion purposes. Actions, e.g. inline links, that require missing capabilities trigger an &#039;&#039;&#039;in-app upsell&#039;&#039;&#039;. 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 &#039;&#039;loosely coupled&#039;&#039; 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 &#039;&#039;&#039;upsell-appsuite.properties&#039;&#039;&#039; or append to existing &#039;&#039;&#039;appsuite.properties&#039;&#039;&#039; (mind the &#039;&#039;&#039;double-slash&#039;&#039;&#039;; this in not a typo!). If you configure upsell in the &#039;&#039;&#039;upsell-appsuite.properties&#039;&#039;&#039; the properties are loaded when you trigger the &#039;&#039;&#039;live reload&#039;&#039;&#039; 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 &#039;&#039;&#039;&amp;quot;&amp;amp;demo=upsell&amp;quot;&#039;&#039;&#039; 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 &#039;&#039;&#039;active_sync&#039;&#039;&#039; OR &#039;&#039;&#039;caldav&#039;&#039;&#039; OR &#039;&#039;&#039;carddav&#039;&#039;&#039;. That means, if one of those capabilities is not set for a user and the upsell is activated for &#039;&#039;&#039;active_sync&#039;&#039;&#039; AND &#039;&#039;&#039;caldav&#039;&#039;&#039; AND &#039;&#039;&#039;carddav&#039;&#039;&#039; 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 &#039;&#039;&#039;.properties&#039;&#039;&#039; 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 &#039;custom&#039; and with id &#039;secondary-toolbar&#039; 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 &#039;&#039;&#039;fa-star&#039;&#039;&#039; 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 &#039;&#039;&#039;.properties&#039;&#039;&#039; 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 &#039;&#039;&#039;requires&#039;&#039;&#039; 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 &#039;&#039;&#039;upgrade to premium&#039;&#039;&#039; 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/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/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;
| 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 &#039;&#039;&#039;imageURL&#039;&#039;&#039; and can make this widget removable by setting &#039;&#039;&#039;removable&#039;&#039;&#039; 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 &#039;&#039;&#039;upsellLimit&#039;&#039;&#039; (in Bytes). If the maximum mail quota is larger than &#039;&#039;&#039;upsellLimit&#039;&#039;&#039;, 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 &#039;&#039;plugins/upsell/simple-wizard&#039;&#039; by [http://oxpedia.org/wiki/index.php?title=AppSuite:Capabilities#Set_a_capability|setting the capability] &#039;&#039;&#039;simple-wizard&#039;&#039;&#039; server-side (or by adding it to the URL &#039;&#039;&#039;...&amp;amp;cap=simple-wizard&#039;&#039;&#039; for testing/development purposes).&lt;br /&gt;
&lt;br /&gt;
This plugin registers for the event &#039;&#039;&amp;quot;upsell:requires-upsell&amp;quot;&#039;&#039;, 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 &#039;&#039;&#039;upsell.properties&#039;&#039;&#039; or append to existing &#039;&#039;&#039;appsuite.properties&#039;&#039;&#039; (mind the &#039;&#039;&#039;double-slash&#039;&#039;&#039;; 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. &#039;&#039;$missing&#039;&#039; 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&#039;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&#039;s imap login&lt;br /&gt;
|-&lt;br /&gt;
| $language&lt;br /&gt;
| The current user&#039;s language, e.g. de_DE or en_US&lt;br /&gt;
|-&lt;br /&gt;
| $mail&lt;br /&gt;
| The current user&#039;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&#039;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&#039;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&#039;s numeric id&lt;br /&gt;
|-&lt;br /&gt;
| $user_login&lt;br /&gt;
| The current user&#039;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(&#039;plugins/upsell/simple-wizard/register&#039;);&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&#039;t use this in production plugins, it&#039;s just a hack for console: &lt;br /&gt;
   // if you don&#039;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([&#039;plugins/upsell/simple-wizard/register&#039;], 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: &#039;app&#039;, id: &#039;io.ox/files&#039;, missing: &#039;files&#039; });&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: &#039;app&#039;, id: &#039;io.ox/files&#039;, missing: &#039;files&#039; });&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(&#039;upsell:simple-wizard:show:before&#039;, _.inspect);&lt;br /&gt;
ox.on(&#039;upsell:simple-wizard:show&#039;, _.inspect);&lt;br /&gt;
ox.on(&#039;upsell:simple-wizard:close&#039;, _.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(&#039;upsell:simple-wizard:init&#039;, _.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(&#039;plugins/upsell/simple-wizard/register&#039;); &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: &#039;5px solid #08c&#039;, boxSizing: &#039;border-box&#039; });&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(&#039;upsell:simple-wizard:show:before&#039;, function (e, popup) {&lt;br /&gt;
  popup.getContentNode().find(&#039;iframe&#039;)&lt;br /&gt;
    .css({ border: &#039;5px solid #08c&#039;, boxSizing: &#039;border-box&#039; });&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(&#039;plugins/upsell/simple-wizard/register&#039;);&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(&#039;apps/plugins/upsell/simple-wizard/close.html&#039;);&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 &#039;&#039;close.html&#039;&#039;. Since this page is part of the UI and therefore located on the same domain, it is allowed to call the wizard&#039;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(&#039;io.ox/calendar/detail/actions/sendmail&#039;, {&lt;br /&gt;
    // this action requires the capability &amp;quot;webmail&amp;quot;&lt;br /&gt;
    capabilities: &#039;webmail&#039;,&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(&#039;upsell:requires-upgrade&#039;);&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&#039;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(&#039;id&#039;).sort();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An example: Free-mail users might just have &#039;&#039;&#039;webmail&#039;&#039;&#039; and &#039;&#039;&#039;contacts&#039;&#039;&#039;. If &#039;&#039;&#039;infostore&#039;&#039;&#039; 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 &#039;&#039;&#039;infostore&#039;&#039;&#039; 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&#039;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 &#039;&#039;&amp;quot;upsell:requires-upgrade&amp;quot;&#039;&#039; 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([&#039;io.ox/core/tk/dialogs&#039;], function (dialogs) {&lt;br /&gt;
        new dialogs.ModalDialog({ easyOut: true })&lt;br /&gt;
            .build(function () {&lt;br /&gt;
                this.getHeader().append(&lt;br /&gt;
                    $(&#039;&amp;lt;h4&amp;gt;&#039;).text(&#039;Upgrade required&#039;)&lt;br /&gt;
                );&lt;br /&gt;
                this.getContentNode().append(&lt;br /&gt;
                    $.txt(&#039;This feature is not available.&#039;),&lt;br /&gt;
                    $.txt(&#039;You need to upgrade your account now.&#039;),&lt;br /&gt;
                    $.txt(&#039; &#039;),&lt;br /&gt;
                    $.txt(&#039;The first 90 days are free.&#039;)&lt;br /&gt;
                );&lt;br /&gt;
                this.addPrimaryButton(&#039;upgrade&#039;, &#039;Get free upgrade&#039;);&lt;br /&gt;
                this.addButton(&#039;cancel&#039;, &#039;Cancel&#039;);&lt;br /&gt;
            })&lt;br /&gt;
            .setUnderlayStyle({&lt;br /&gt;
                opacity: 0.70,&lt;br /&gt;
                backgroundColor: &#039;#08C&#039;&lt;br /&gt;
            })&lt;br /&gt;
            .on(&#039;upgrade&#039;, function () {&lt;br /&gt;
                ox.trigger(&#039;upsell:upgrade&#039;, options);&lt;br /&gt;
            })&lt;br /&gt;
            .on(&#039;show&#039;, function () {&lt;br /&gt;
                ox.off(&#039;upsell:requires-upgrade&#039;, showUpgradeDialog);&lt;br /&gt;
            })&lt;br /&gt;
            .on(&#039;close&#039;, function () {&lt;br /&gt;
                ox.on(&#039;upsell:requires-upgrade&#039;, 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(&#039;upgrade&#039;, options);&lt;br /&gt;
    alert(&#039;User decided to upgrade! (global event: upsell:upgrade)&#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ox.on(&#039;upsell:requires-upgrade&#039;, showUpgradeDialog);&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * convention: &#039;upsell:upgrade&#039; 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(&#039;upsell:upgrade&#039;, upgrade);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The second event &#039;&#039;&#039;&amp;quot;upsell:upgrade&amp;quot;&#039;&#039;&#039; 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&#039;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(&#039;plugins/portal/upsell/register&#039;,&lt;br /&gt;
    [&#039;io.ox/core/extensions&#039;,&lt;br /&gt;
     &#039;io.ox/files/api&#039;,&lt;br /&gt;
     &#039;gettext!plugins/portal&#039;], function (ext, api, gt) {&lt;br /&gt;
&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var title = gt(&#039;Upgrade to premium&#039;);&lt;br /&gt;
&lt;br /&gt;
    ext.point(&#039;io.ox/portal/widget/upsell&#039;).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(&#039;hide-title&#039;).append(&lt;br /&gt;
                $(&#039;&amp;amp;lt;div class=&amp;quot;content centered&amp;quot; style=&amp;quot;cursor: pointer; padding-top: 3em;&amp;quot;&amp;gt;&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;amp;lt;h2&amp;gt;&#039;).append(&lt;br /&gt;
                        $.txt(title + &#039; &#039;),&lt;br /&gt;
                        $(&#039;&amp;amp;lt;i class=&amp;quot;icon-star&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
                    ),&lt;br /&gt;
                    $(&#039;&amp;amp;lt;div&amp;gt;&#039;).text(gt(&#039;Click here for free trial.&#039;))&lt;br /&gt;
                )&lt;br /&gt;
                .on(&#039;click&#039;, function () {&lt;br /&gt;
                    ox.trigger(&#039;upsell:upgrade&#039;, {&lt;br /&gt;
                        type: &#039;widget&#039;,&lt;br /&gt;
                        id: &#039;io.ox/portal/widget/upsell&#039;,&lt;br /&gt;
                        missing: &#039;&#039;&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 &#039;&#039;&amp;quot;io.ox/core&amp;quot;&#039;&#039;, the path is &#039;&#039;&amp;quot;upsell/enabled&amp;quot;&#039;&#039;. 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(&#039;settings!io.ox/core&#039;).get(&#039;upsell/enabled&#039;);&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 &#039;&#039;&#039;not&#039;&#039;&#039; 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 &#039;&#039;&amp;quot;upsell:requires-upgrade&amp;quot;&#039;&#039; 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(&#039;io.ox/core/upsell&#039;);&lt;br /&gt;
&lt;br /&gt;
// check capabilities (space-separated) &lt;br /&gt;
upsell.has(&#039;portal webmail&#039;);&lt;br /&gt;
&lt;br /&gt;
// get missing capabilities (would return &amp;quot;calendar&amp;quot; in demo mode) &lt;br /&gt;
upsell.missing([&#039;portal webmail&#039;, &#039;contacts&#039;, &#039;calendar&#039;]);&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([&#039;portal webmail&#039;, &#039;webmail calendar&#039;]);&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([&#039;portal webmail&#039;, &#039;contacts&#039;, &#039;calendar&#039;]);&lt;br /&gt;
&lt;br /&gt;
// likewise if neither capability set nor enabled for upsell, we get a false &lt;br /&gt;
upsell.visible([&#039;foo&#039;]);&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(&#039;id&#039;).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>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Upsell&amp;diff=20900</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=20900"/>
		<updated>2015-11-18T12:14:30Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Upsell Wizard */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Upsell&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Abstract.&#039;&#039;&#039; 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 &#039;&#039;capabilities&#039;&#039;. UI, however, offers functionality beyond that limited set for promotion purposes. Actions, e.g. inline links, that require missing capabilities trigger an &#039;&#039;&#039;in-app upsell&#039;&#039;&#039;. 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 &#039;&#039;loosely coupled&#039;&#039; 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 &#039;&#039;&#039;upsell-appsuite.properties&#039;&#039;&#039; or append to existing &#039;&#039;&#039;appsuite.properties&#039;&#039;&#039; (mind the &#039;&#039;&#039;double-slash&#039;&#039;&#039;; this in not a typo!). If you configure upsell in the &#039;&#039;&#039;upsell-appsuite.properties&#039;&#039;&#039; the properties are loaded when you trigger the &#039;&#039;&#039;live reload&#039;&#039;&#039; 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 &#039;&#039;&#039;&amp;quot;&amp;amp;demo=upsell&amp;quot;&#039;&#039;&#039; 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 &#039;&#039;&#039;active_sync&#039;&#039;&#039; OR &#039;&#039;&#039;caldav&#039;&#039;&#039; OR &#039;&#039;&#039;carddav&#039;&#039;&#039;. That means, if one of those capabilities is not set for a user and the upsell is activated for &#039;&#039;&#039;active_sync&#039;&#039;&#039; AND &#039;&#039;&#039;caldav&#039;&#039;&#039; AND &#039;&#039;&#039;carddav&#039;&#039;&#039; 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 &#039;&#039;&#039;.properties&#039;&#039;&#039; 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 &#039;custom&#039; and with id &#039;secondary-toolbar&#039; 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 &#039;&#039;&#039;fa-star&#039;&#039;&#039; 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 &#039;&#039;&#039;.properties&#039;&#039;&#039; 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 &#039;&#039;&#039;requires&#039;&#039;&#039; 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 &#039;&#039;&#039;upgrade to premium&#039;&#039;&#039; 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/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/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;
| 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 &#039;&#039;&#039;imageURL&#039;&#039;&#039; and can make this widget removable by setting &#039;&#039;&#039;removable&#039;&#039;&#039; 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 &#039;&#039;&#039;upsellLimit&#039;&#039;&#039; (in Bytes). If the maximum mail quota is larger than &#039;&#039;&#039;upsellLimit&#039;&#039;&#039;, 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 &#039;&#039;plugins/upsell/simple-wizard&#039;&#039; by [[http://oxpedia.org/wiki/index.php?title=AppSuite:Capabilities#Set_a_capability|setting the capability]] &#039;&#039;&#039;simple-wizard&#039;&#039;&#039; server-side (or by adding it to the URL &#039;&#039;&#039;...&amp;amp;cap=simple-wizard&#039;&#039;&#039; for testing/development purposes).&lt;br /&gt;
&lt;br /&gt;
This plugin registers for the event &#039;&#039;&amp;quot;upsell:requires-upsell&amp;quot;&#039;&#039;, 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 &#039;&#039;&#039;upsell.properties&#039;&#039;&#039; or append to existing &#039;&#039;&#039;appsuite.properties&#039;&#039;&#039; (mind the &#039;&#039;&#039;double-slash&#039;&#039;&#039;; 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. &#039;&#039;$missing&#039;&#039; 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&#039;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&#039;s imap login&lt;br /&gt;
|-&lt;br /&gt;
| $language&lt;br /&gt;
| The current user&#039;s language, e.g. de_DE or en_US&lt;br /&gt;
|-&lt;br /&gt;
| $mail&lt;br /&gt;
| The current user&#039;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&#039;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&#039;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&#039;s numeric id&lt;br /&gt;
|-&lt;br /&gt;
| $user_login&lt;br /&gt;
| The current user&#039;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(&#039;plugins/upsell/simple-wizard/register&#039;);&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&#039;t use this in production plugins, it&#039;s just a hack for console: &lt;br /&gt;
   // if you don&#039;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([&#039;plugins/upsell/simple-wizard/register&#039;], 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: &#039;app&#039;, id: &#039;io.ox/files&#039;, missing: &#039;files&#039; });&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: &#039;app&#039;, id: &#039;io.ox/files&#039;, missing: &#039;files&#039; });&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(&#039;upsell:simple-wizard:show:before&#039;, _.inspect);&lt;br /&gt;
ox.on(&#039;upsell:simple-wizard:show&#039;, _.inspect);&lt;br /&gt;
ox.on(&#039;upsell:simple-wizard:close&#039;, _.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(&#039;upsell:simple-wizard:init&#039;, _.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(&#039;plugins/upsell/simple-wizard/register&#039;); &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: &#039;5px solid #08c&#039;, boxSizing: &#039;border-box&#039; });&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(&#039;upsell:simple-wizard:show:before&#039;, function (e, popup) {&lt;br /&gt;
  popup.getContentNode().find(&#039;iframe&#039;)&lt;br /&gt;
    .css({ border: &#039;5px solid #08c&#039;, boxSizing: &#039;border-box&#039; });&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(&#039;plugins/upsell/simple-wizard/register&#039;);&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(&#039;apps/plugins/upsell/simple-wizard/close.html&#039;);&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 &#039;&#039;close.html&#039;&#039;. Since this page is part of the UI and therefore located on the same domain, it is allowed to call the wizard&#039;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(&#039;io.ox/calendar/detail/actions/sendmail&#039;, {&lt;br /&gt;
    // this action requires the capability &amp;quot;webmail&amp;quot;&lt;br /&gt;
    capabilities: &#039;webmail&#039;,&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(&#039;upsell:requires-upgrade&#039;);&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&#039;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(&#039;id&#039;).sort();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An example: Free-mail users might just have &#039;&#039;&#039;webmail&#039;&#039;&#039; and &#039;&#039;&#039;contacts&#039;&#039;&#039;. If &#039;&#039;&#039;infostore&#039;&#039;&#039; 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 &#039;&#039;&#039;infostore&#039;&#039;&#039; 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&#039;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 &#039;&#039;&amp;quot;upsell:requires-upgrade&amp;quot;&#039;&#039; 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([&#039;io.ox/core/tk/dialogs&#039;], function (dialogs) {&lt;br /&gt;
        new dialogs.ModalDialog({ easyOut: true })&lt;br /&gt;
            .build(function () {&lt;br /&gt;
                this.getHeader().append(&lt;br /&gt;
                    $(&#039;&amp;lt;h4&amp;gt;&#039;).text(&#039;Upgrade required&#039;)&lt;br /&gt;
                );&lt;br /&gt;
                this.getContentNode().append(&lt;br /&gt;
                    $.txt(&#039;This feature is not available.&#039;),&lt;br /&gt;
                    $.txt(&#039;You need to upgrade your account now.&#039;),&lt;br /&gt;
                    $.txt(&#039; &#039;),&lt;br /&gt;
                    $.txt(&#039;The first 90 days are free.&#039;)&lt;br /&gt;
                );&lt;br /&gt;
                this.addPrimaryButton(&#039;upgrade&#039;, &#039;Get free upgrade&#039;);&lt;br /&gt;
                this.addButton(&#039;cancel&#039;, &#039;Cancel&#039;);&lt;br /&gt;
            })&lt;br /&gt;
            .setUnderlayStyle({&lt;br /&gt;
                opacity: 0.70,&lt;br /&gt;
                backgroundColor: &#039;#08C&#039;&lt;br /&gt;
            })&lt;br /&gt;
            .on(&#039;upgrade&#039;, function () {&lt;br /&gt;
                ox.trigger(&#039;upsell:upgrade&#039;, options);&lt;br /&gt;
            })&lt;br /&gt;
            .on(&#039;show&#039;, function () {&lt;br /&gt;
                ox.off(&#039;upsell:requires-upgrade&#039;, showUpgradeDialog);&lt;br /&gt;
            })&lt;br /&gt;
            .on(&#039;close&#039;, function () {&lt;br /&gt;
                ox.on(&#039;upsell:requires-upgrade&#039;, 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(&#039;upgrade&#039;, options);&lt;br /&gt;
    alert(&#039;User decided to upgrade! (global event: upsell:upgrade)&#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ox.on(&#039;upsell:requires-upgrade&#039;, showUpgradeDialog);&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * convention: &#039;upsell:upgrade&#039; 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(&#039;upsell:upgrade&#039;, upgrade);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The second event &#039;&#039;&#039;&amp;quot;upsell:upgrade&amp;quot;&#039;&#039;&#039; 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&#039;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(&#039;plugins/portal/upsell/register&#039;,&lt;br /&gt;
    [&#039;io.ox/core/extensions&#039;,&lt;br /&gt;
     &#039;io.ox/files/api&#039;,&lt;br /&gt;
     &#039;gettext!plugins/portal&#039;], function (ext, api, gt) {&lt;br /&gt;
&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var title = gt(&#039;Upgrade to premium&#039;);&lt;br /&gt;
&lt;br /&gt;
    ext.point(&#039;io.ox/portal/widget/upsell&#039;).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(&#039;hide-title&#039;).append(&lt;br /&gt;
                $(&#039;&amp;amp;lt;div class=&amp;quot;content centered&amp;quot; style=&amp;quot;cursor: pointer; padding-top: 3em;&amp;quot;&amp;gt;&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;amp;lt;h2&amp;gt;&#039;).append(&lt;br /&gt;
                        $.txt(title + &#039; &#039;),&lt;br /&gt;
                        $(&#039;&amp;amp;lt;i class=&amp;quot;icon-star&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
                    ),&lt;br /&gt;
                    $(&#039;&amp;amp;lt;div&amp;gt;&#039;).text(gt(&#039;Click here for free trial.&#039;))&lt;br /&gt;
                )&lt;br /&gt;
                .on(&#039;click&#039;, function () {&lt;br /&gt;
                    ox.trigger(&#039;upsell:upgrade&#039;, {&lt;br /&gt;
                        type: &#039;widget&#039;,&lt;br /&gt;
                        id: &#039;io.ox/portal/widget/upsell&#039;,&lt;br /&gt;
                        missing: &#039;&#039;&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 &#039;&#039;&amp;quot;io.ox/core&amp;quot;&#039;&#039;, the path is &#039;&#039;&amp;quot;upsell/enabled&amp;quot;&#039;&#039;. 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(&#039;settings!io.ox/core&#039;).get(&#039;upsell/enabled&#039;);&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 &#039;&#039;&#039;not&#039;&#039;&#039; 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 &#039;&#039;&amp;quot;upsell:requires-upgrade&amp;quot;&#039;&#039; 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(&#039;io.ox/core/upsell&#039;);&lt;br /&gt;
&lt;br /&gt;
// check capabilities (space-separated) &lt;br /&gt;
upsell.has(&#039;portal webmail&#039;);&lt;br /&gt;
&lt;br /&gt;
// get missing capabilities (would return &amp;quot;calendar&amp;quot; in demo mode) &lt;br /&gt;
upsell.missing([&#039;portal webmail&#039;, &#039;contacts&#039;, &#039;calendar&#039;]);&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([&#039;portal webmail&#039;, &#039;webmail calendar&#039;]);&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([&#039;portal webmail&#039;, &#039;contacts&#039;, &#039;calendar&#039;]);&lt;br /&gt;
&lt;br /&gt;
// likewise if neither capability set nor enabled for upsell, we get a false &lt;br /&gt;
upsell.visible([&#039;foo&#039;]);&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(&#039;id&#039;).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>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Upsell&amp;diff=20899</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=20899"/>
		<updated>2015-11-18T12:08:10Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Upsell Wizard */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Upsell&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Abstract.&#039;&#039;&#039; 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 &#039;&#039;capabilities&#039;&#039;. UI, however, offers functionality beyond that limited set for promotion purposes. Actions, e.g. inline links, that require missing capabilities trigger an &#039;&#039;&#039;in-app upsell&#039;&#039;&#039;. 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 &#039;&#039;loosely coupled&#039;&#039; 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 &#039;&#039;&#039;upsell-appsuite.properties&#039;&#039;&#039; or append to existing &#039;&#039;&#039;appsuite.properties&#039;&#039;&#039; (mind the &#039;&#039;&#039;double-slash&#039;&#039;&#039;; this in not a typo!). If you configure upsell in the &#039;&#039;&#039;upsell-appsuite.properties&#039;&#039;&#039; the properties are loaded when you trigger the &#039;&#039;&#039;live reload&#039;&#039;&#039; 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 &#039;&#039;&#039;&amp;quot;&amp;amp;demo=upsell&amp;quot;&#039;&#039;&#039; 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 &#039;&#039;&#039;active_sync&#039;&#039;&#039; OR &#039;&#039;&#039;caldav&#039;&#039;&#039; OR &#039;&#039;&#039;carddav&#039;&#039;&#039;. That means, if one of those capabilities is not set for a user and the upsell is activated for &#039;&#039;&#039;active_sync&#039;&#039;&#039; AND &#039;&#039;&#039;caldav&#039;&#039;&#039; AND &#039;&#039;&#039;carddav&#039;&#039;&#039; 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 &#039;&#039;&#039;.properties&#039;&#039;&#039; 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 &#039;custom&#039; and with id &#039;secondary-toolbar&#039; 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 &#039;&#039;&#039;fa-star&#039;&#039;&#039; 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 &#039;&#039;&#039;.properties&#039;&#039;&#039; 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 &#039;&#039;&#039;requires&#039;&#039;&#039; 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 &#039;&#039;&#039;upgrade to premium&#039;&#039;&#039; 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/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/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;
| 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 &#039;&#039;&#039;imageURL&#039;&#039;&#039; and can make this widget removable by setting &#039;&#039;&#039;removable&#039;&#039;&#039; 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 &#039;&#039;&#039;upsellLimit&#039;&#039;&#039; (in Bytes). If the maximum mail quota is larger than &#039;&#039;&#039;upsellLimit&#039;&#039;&#039;, 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;
Hosters 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 [[AppSuite:UI_manifests_explained#Loading_custom_manifest_during_development|enable]] &#039;&#039;plugins/upsell/simple-wizard&#039;&#039;, i.e. add the capability &#039;&#039;&#039;simple-wizard&#039;&#039;&#039;. This plugin registers for the event &#039;&#039;&amp;quot;upsell:requires-upsell&amp;quot;&#039;&#039;, 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 &#039;&#039;&#039;upsell.properties&#039;&#039;&#039; or append to existing &#039;&#039;&#039;appsuite.properties&#039;&#039;&#039; (mind the &#039;&#039;&#039;double-slash&#039;&#039;&#039;; 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. &#039;&#039;$missing&#039;&#039; 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&#039;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&#039;s imap login&lt;br /&gt;
|-&lt;br /&gt;
| $language&lt;br /&gt;
| The current user&#039;s language, e.g. de_DE or en_US&lt;br /&gt;
|-&lt;br /&gt;
| $mail&lt;br /&gt;
| The current user&#039;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&#039;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&#039;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&#039;s numeric id&lt;br /&gt;
|-&lt;br /&gt;
| $user_login&lt;br /&gt;
| The current user&#039;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(&#039;plugins/upsell/simple-wizard/register&#039;);&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&#039;t use this in production plugins, it&#039;s just a hack for console: &lt;br /&gt;
   // if you don&#039;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([&#039;plugins/upsell/simple-wizard/register&#039;], 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: &#039;app&#039;, id: &#039;io.ox/files&#039;, missing: &#039;files&#039; });&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: &#039;app&#039;, id: &#039;io.ox/files&#039;, missing: &#039;files&#039; });&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(&#039;upsell:simple-wizard:show:before&#039;, _.inspect);&lt;br /&gt;
ox.on(&#039;upsell:simple-wizard:show&#039;, _.inspect);&lt;br /&gt;
ox.on(&#039;upsell:simple-wizard:close&#039;, _.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(&#039;upsell:simple-wizard:init&#039;, _.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(&#039;plugins/upsell/simple-wizard/register&#039;); &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: &#039;5px solid #08c&#039;, boxSizing: &#039;border-box&#039; });&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(&#039;upsell:simple-wizard:show:before&#039;, function (e, popup) {&lt;br /&gt;
  popup.getContentNode().find(&#039;iframe&#039;)&lt;br /&gt;
    .css({ border: &#039;5px solid #08c&#039;, boxSizing: &#039;border-box&#039; });&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(&#039;plugins/upsell/simple-wizard/register&#039;);&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(&#039;apps/plugins/upsell/simple-wizard/close.html&#039;);&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 &#039;&#039;close.html&#039;&#039;. Since this page is part of the UI and therefore located on the same domain, it is allowed to call the wizard&#039;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(&#039;io.ox/calendar/detail/actions/sendmail&#039;, {&lt;br /&gt;
    // this action requires the capability &amp;quot;webmail&amp;quot;&lt;br /&gt;
    capabilities: &#039;webmail&#039;,&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(&#039;upsell:requires-upgrade&#039;);&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&#039;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(&#039;id&#039;).sort();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An example: Free-mail users might just have &#039;&#039;&#039;webmail&#039;&#039;&#039; and &#039;&#039;&#039;contacts&#039;&#039;&#039;. If &#039;&#039;&#039;infostore&#039;&#039;&#039; 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 &#039;&#039;&#039;infostore&#039;&#039;&#039; 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&#039;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 &#039;&#039;&amp;quot;upsell:requires-upgrade&amp;quot;&#039;&#039; 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([&#039;io.ox/core/tk/dialogs&#039;], function (dialogs) {&lt;br /&gt;
        new dialogs.ModalDialog({ easyOut: true })&lt;br /&gt;
            .build(function () {&lt;br /&gt;
                this.getHeader().append(&lt;br /&gt;
                    $(&#039;&amp;lt;h4&amp;gt;&#039;).text(&#039;Upgrade required&#039;)&lt;br /&gt;
                );&lt;br /&gt;
                this.getContentNode().append(&lt;br /&gt;
                    $.txt(&#039;This feature is not available.&#039;),&lt;br /&gt;
                    $.txt(&#039;You need to upgrade your account now.&#039;),&lt;br /&gt;
                    $.txt(&#039; &#039;),&lt;br /&gt;
                    $.txt(&#039;The first 90 days are free.&#039;)&lt;br /&gt;
                );&lt;br /&gt;
                this.addPrimaryButton(&#039;upgrade&#039;, &#039;Get free upgrade&#039;);&lt;br /&gt;
                this.addButton(&#039;cancel&#039;, &#039;Cancel&#039;);&lt;br /&gt;
            })&lt;br /&gt;
            .setUnderlayStyle({&lt;br /&gt;
                opacity: 0.70,&lt;br /&gt;
                backgroundColor: &#039;#08C&#039;&lt;br /&gt;
            })&lt;br /&gt;
            .on(&#039;upgrade&#039;, function () {&lt;br /&gt;
                ox.trigger(&#039;upsell:upgrade&#039;, options);&lt;br /&gt;
            })&lt;br /&gt;
            .on(&#039;show&#039;, function () {&lt;br /&gt;
                ox.off(&#039;upsell:requires-upgrade&#039;, showUpgradeDialog);&lt;br /&gt;
            })&lt;br /&gt;
            .on(&#039;close&#039;, function () {&lt;br /&gt;
                ox.on(&#039;upsell:requires-upgrade&#039;, 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(&#039;upgrade&#039;, options);&lt;br /&gt;
    alert(&#039;User decided to upgrade! (global event: upsell:upgrade)&#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ox.on(&#039;upsell:requires-upgrade&#039;, showUpgradeDialog);&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * convention: &#039;upsell:upgrade&#039; 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(&#039;upsell:upgrade&#039;, upgrade);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The second event &#039;&#039;&#039;&amp;quot;upsell:upgrade&amp;quot;&#039;&#039;&#039; 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&#039;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(&#039;plugins/portal/upsell/register&#039;,&lt;br /&gt;
    [&#039;io.ox/core/extensions&#039;,&lt;br /&gt;
     &#039;io.ox/files/api&#039;,&lt;br /&gt;
     &#039;gettext!plugins/portal&#039;], function (ext, api, gt) {&lt;br /&gt;
&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var title = gt(&#039;Upgrade to premium&#039;);&lt;br /&gt;
&lt;br /&gt;
    ext.point(&#039;io.ox/portal/widget/upsell&#039;).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(&#039;hide-title&#039;).append(&lt;br /&gt;
                $(&#039;&amp;amp;lt;div class=&amp;quot;content centered&amp;quot; style=&amp;quot;cursor: pointer; padding-top: 3em;&amp;quot;&amp;gt;&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;amp;lt;h2&amp;gt;&#039;).append(&lt;br /&gt;
                        $.txt(title + &#039; &#039;),&lt;br /&gt;
                        $(&#039;&amp;amp;lt;i class=&amp;quot;icon-star&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
                    ),&lt;br /&gt;
                    $(&#039;&amp;amp;lt;div&amp;gt;&#039;).text(gt(&#039;Click here for free trial.&#039;))&lt;br /&gt;
                )&lt;br /&gt;
                .on(&#039;click&#039;, function () {&lt;br /&gt;
                    ox.trigger(&#039;upsell:upgrade&#039;, {&lt;br /&gt;
                        type: &#039;widget&#039;,&lt;br /&gt;
                        id: &#039;io.ox/portal/widget/upsell&#039;,&lt;br /&gt;
                        missing: &#039;&#039;&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 &#039;&#039;&amp;quot;io.ox/core&amp;quot;&#039;&#039;, the path is &#039;&#039;&amp;quot;upsell/enabled&amp;quot;&#039;&#039;. 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(&#039;settings!io.ox/core&#039;).get(&#039;upsell/enabled&#039;);&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 &#039;&#039;&#039;not&#039;&#039;&#039; 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 &#039;&#039;&amp;quot;upsell:requires-upgrade&amp;quot;&#039;&#039; 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(&#039;io.ox/core/upsell&#039;);&lt;br /&gt;
&lt;br /&gt;
// check capabilities (space-separated) &lt;br /&gt;
upsell.has(&#039;portal webmail&#039;);&lt;br /&gt;
&lt;br /&gt;
// get missing capabilities (would return &amp;quot;calendar&amp;quot; in demo mode) &lt;br /&gt;
upsell.missing([&#039;portal webmail&#039;, &#039;contacts&#039;, &#039;calendar&#039;]);&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([&#039;portal webmail&#039;, &#039;webmail calendar&#039;]);&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([&#039;portal webmail&#039;, &#039;contacts&#039;, &#039;calendar&#039;]);&lt;br /&gt;
&lt;br /&gt;
// likewise if neither capability set nor enabled for upsell, we get a false &lt;br /&gt;
upsell.visible([&#039;foo&#039;]);&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(&#039;id&#039;).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>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Writing_a_notification_area_plugin&amp;diff=20370</id>
		<title>AppSuite:Writing a notification area plugin</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Writing_a_notification_area_plugin&amp;diff=20370"/>
		<updated>2015-09-10T08:22:48Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Writing a plugin for the notification area&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Abstract:&#039;&#039;&#039; This article is a step by step tutorial to build your own notification plugin.&lt;br /&gt;
These plugins can be used for various purposes, for example reminding the user of something or showing him new invitations.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
Lorem ipsum ...&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Writing_a_notification_area_plugin&amp;diff=20369</id>
		<title>AppSuite:Writing a notification area plugin</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Writing_a_notification_area_plugin&amp;diff=20369"/>
		<updated>2015-09-10T08:22:11Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hallo Welt&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Writing_a_notification_area_plugin_(7.6.x)&amp;diff=20368</id>
		<title>AppSuite:Writing a notification area plugin (7.6.x)</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Writing_a_notification_area_plugin_(7.6.x)&amp;diff=20368"/>
		<updated>2015-09-10T08:21:28Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Writing a plugin for the notification area (7.6.x)&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Abstract:&#039;&#039;&#039; This article is a step by step tutorial to build your own notification plugin.&lt;br /&gt;
These plugins can be used for various purposes, for example reminding the user of something or showing him new invitations.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
==Preparations==&lt;br /&gt;
&lt;br /&gt;
To start a new plugin for the notification area you have to add a new folder at &#039;&#039;apps.plugins/notifications/&#039;&#039; .&lt;br /&gt;
For this tutorial we will create &#039;&#039;plugins/notifications/tutorial/&#039;&#039; .&lt;br /&gt;
&lt;br /&gt;
Now add a new file to your folder and name it &#039;&#039;register.js&#039;&#039; .&lt;br /&gt;
Then add the basic markup such as copyright, define for &#039;&#039;require.js&#039;&#039;, use strict and so on.&lt;br /&gt;
In our tutorial the result looks like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * your copyright here&lt;br /&gt;
 * @author Mister Test &amp;lt;mister.test@test.test&amp;gt;&lt;br /&gt;
 */&lt;br /&gt;
 &lt;br /&gt;
define(&#039;plugins/notifications/tutorial/register&#039;,&lt;br /&gt;
    [&#039;io.ox/core/extensions&#039;], function (ext) {&lt;br /&gt;
&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    &lt;br /&gt;
    //just to give something back&lt;br /&gt;
    return true;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: We need to use extensions so we need to require the needed resources with&lt;br /&gt;
  [&#039;io.ox/core/extensions&#039;], function (ext)&lt;br /&gt;
as seen above.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===Manifests===&lt;br /&gt;
&lt;br /&gt;
Your file is not loaded yet. To do this we need to create a manifest file.&lt;br /&gt;
Create a new file in your folder with the name &#039;&#039;manifest.json&#039;&#039; with the following code in it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
	namespace: &amp;quot;io.ox/core/notifications&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For developing add the following code to &#039;&#039;src/manifests.js&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    namespace: [&#039;io.ox/core/notifications&#039;],&lt;br /&gt;
    path: &#039;plugins/notifications/tutorial/register&#039;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For further information about manifests look [[AppSuite:UI manifests explained|here]].&lt;br /&gt;
&lt;br /&gt;
==Coding the base==&lt;br /&gt;
===Registering your plugin===&lt;br /&gt;
&lt;br /&gt;
Now you need to register your plugin by extending the right extension point, which is &#039;&#039;io.ox/core/notifications/register&#039;&#039; .&lt;br /&gt;
Give your plugin a unique id, indexnumber and a register function.&lt;br /&gt;
Inside this function we register our notification plugin at the controller and also give it an id and our view, we create later on.&lt;br /&gt;
Do so by adding:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//register our notification plugin&lt;br /&gt;
ext.point(&#039;io.ox/core/notifications/register&#039;).extend({&lt;br /&gt;
    id: &#039;tutorial&#039;,//unique id&lt;br /&gt;
    index: 500, //unused index&lt;br /&gt;
    register: function (controller) {&lt;br /&gt;
        //give our plugin a name and send it to the controller together with our view&lt;br /&gt;
        var notifications = controller.get(&#039;io.ox/tutorial&#039;, NotificationsView);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;io.ox/tutorial&#039;&#039; is the id the controller should use to refer to our plugin and &#039;&#039;NotificationsView&#039;&#039; is the view we will create now to display it.&lt;br /&gt;
&lt;br /&gt;
===Creating the View===&lt;br /&gt;
&lt;br /&gt;
Since we use will use backbone to create our plugin it obviously needs a view.&lt;br /&gt;
Call the variable like the one you gave to the controller to make it work.&lt;br /&gt;
In our example the code to create the view looks like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//the view of our plugin&lt;br /&gt;
var NotificationsView = Backbone.View.extend({&lt;br /&gt;
&lt;br /&gt;
    className: &#039;notifications&#039;,&lt;br /&gt;
    id: &#039;io-ox-notifications-tutorial&#039;,&lt;br /&gt;
        &lt;br /&gt;
    //events from our items&lt;br /&gt;
    events: {&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    //draws the plugin&lt;br /&gt;
    render: function () {&lt;br /&gt;
        return this;&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: Events and render function are empty at the moment, but we will fix that soon.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===Adding the headline===&lt;br /&gt;
&lt;br /&gt;
Now we want to draw something. We could just put it in the render method, but since we have the extension point architecture we will make use of it, to keep our actual render method cleaned up.&lt;br /&gt;
&lt;br /&gt;
We start by creating an extension point we will use to draw our headline and create a container for our notifications to put in later.&lt;br /&gt;
Add this code to your views render method to create the point and invoke the draw method of its extensions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//build baton to wrap things up&lt;br /&gt;
var baton = ext.Baton({ view: this });&lt;br /&gt;
//draw header and container by creating an extension point and invoke drawing on its extensions&lt;br /&gt;
ext.point(&#039;io.ox/core/notifications/tutorial/header&#039;).invoke(&#039;draw&#039;, this.$el.empty(), baton);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: Here we use our special baton objects to pass the data to the extension points. By using this.$el.empty() as a dom node to draw we ensure that we clean up properly before drawing.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Now extend the point with a simple draw method for our header and container.&lt;br /&gt;
&#039;&#039;this&#039;&#039; refers to our views dom node we draw in in the rendering method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//the header and container for the notification plugin&lt;br /&gt;
ext.point(&#039;io.ox/core/notifications/tutorial/header&#039;).extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
            $(&#039;&amp;lt;legend class=&amp;quot;section-title&amp;quot;&amp;gt;&#039;).text(&#039;Hello World&#039;),//header&lt;br /&gt;
            $(&#039;&amp;lt;div class=&amp;quot;notifications&amp;quot;&amp;gt;&#039;)//the container for our notifications&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Congratulations the first steps are done. Time for some testing to see if we did it right.&lt;br /&gt;
&lt;br /&gt;
==First testing==&lt;br /&gt;
&lt;br /&gt;
Now we want to see how it looks in the program.&lt;br /&gt;
To do this add this line of code to your register method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
notifications.collection.reset(new Backbone.Model());&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates an empty notification model and adds it to our plugins collection.&lt;br /&gt;
We get to know how this collection works later on, for now we just need something in it for the controller to think that there is a notification to display.&lt;br /&gt;
&lt;br /&gt;
When finished start your appsuite and login, add &#039;&#039;&amp;amp;customManifests=true&#039;&#039; to the url to load your plugin and reload the page.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: The notification area is loaded with a delay, so you have to wait a bit.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
After it&#039;s loaded you should see something like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: header.png]]&lt;br /&gt;
&lt;br /&gt;
Well done now we need to add some real notifications.&lt;br /&gt;
&lt;br /&gt;
==Adding Notifications==&lt;br /&gt;
&lt;br /&gt;
===Triggering the events===&lt;br /&gt;
Normally a notification area listens for events of the app it is related to. For example the mail notifications, listen for events on from the mail api.&lt;br /&gt;
&lt;br /&gt;
For this tutorial we will just create a small dummy to create some Notifications for us and then trigger the proper event.&lt;br /&gt;
&lt;br /&gt;
Our dummy looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
/* simple helper to trigger some events&lt;br /&gt;
   normally this is done by mailapi, taskapi, etc. */&lt;br /&gt;
var myEventTriggerer = {&lt;br /&gt;
        lookForItems: function () {&lt;br /&gt;
            //build some items and put them in an array&lt;br /&gt;
            var items = [{title: &#039;I am a notification&#039;, description: &#039;Hello world!&#039;},&lt;br /&gt;
                         {title: &#039;I am a notification too&#039;, description: &#039;Hooray!&#039;}];&lt;br /&gt;
            //trigger the event to add them&lt;br /&gt;
            $(myEventTriggerer).trigger(&#039;set-tutorial-notification&#039;, [items]);&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This dummy creates an array with two objects containing our notifications data.&lt;br /&gt;
Then it triggers an event on itself and passes the array as an argument.&lt;br /&gt;
&lt;br /&gt;
===Helper functions===&lt;br /&gt;
Notifications are stored as backbone models in a collection of our view.&lt;br /&gt;
Or models have the attributes title and description. A cid is added automatically that we use to identify them later on. You can also give ids as normal attributes and use them if you want more control over it.&lt;br /&gt;
This collection is available in the register method under &#039;&#039;notifications.collection&#039;&#039;.&lt;br /&gt;
The controller looks for changes in this collection and triggers a redraw.&lt;br /&gt;
&lt;br /&gt;
To do this we create a simple functions for resetting notification models in our collection.&lt;br /&gt;
Remove our testing line &#039;&#039;notifications.collection.reset(new Backbone.Model());&#039;&#039; from the register method and add our new function:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
/* fill our collection of notification models with new ones&lt;br /&gt;
   items here is an array of Objects containing our attributes */&lt;br /&gt;
function reset(e, items) {&lt;br /&gt;
    var models = [];&lt;br /&gt;
    items = [].concat(items);//make sure we have an array&lt;br /&gt;
    _(items).each(function (item) {&lt;br /&gt;
        models.push(new Backbone.Model(item));&lt;br /&gt;
        });&lt;br /&gt;
    notifications.collection.reset(models);//fill the collection&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function simply loops over the array of items they are given, creates models from them and fills the collection with it. Reset means that the old models are gone now and only the new ones are in our collection.&lt;br /&gt;
Functions to add and remove models are done the same way but are not always needed, as in this example. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: notifications.collection.add() does not trigger the add event. You need to do this manually, this seems to be a backbone issue.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===Listen for events===&lt;br /&gt;
&lt;br /&gt;
So now we need just listen to the event to launch our reset function and call our little dummy to fill our initial collection.&lt;br /&gt;
We do this by adding this code to the register method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//now add the event listeners&lt;br /&gt;
$(myEventTriggerer).on(&#039;set-tutorial-notification&#039;, reset);&lt;br /&gt;
&lt;br /&gt;
//just to make sure it gets filled with values on load we trigger our search method&lt;br /&gt;
myEventTriggerer.lookForItems();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Drawing the Notifications===&lt;br /&gt;
Now that we have proper models, we need to draw them.&lt;br /&gt;
To do this we do the same as with the header, create an extension point and invoke drawing.&lt;br /&gt;
In our views render method we need to add:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//loop over collection and draw&lt;br /&gt;
this.collection.each(function (model) {&lt;br /&gt;
    baton = ext.Baton({ model: model, view: this });&lt;br /&gt;
    ext.point(&#039;io.ox/core/notifications/tutorial/item&#039;)&lt;br /&gt;
        .invoke(&#039;draw&#039;, this.$(&#039;.notifications&#039;), baton);//draw the item&lt;br /&gt;
}, this);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This time we draw in the container div we created by our header drawing method.&lt;br /&gt;
Next we extend our extension point to draw the actual notification items.&lt;br /&gt;
&lt;br /&gt;
In our example it looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//draw a single notification item&lt;br /&gt;
ext.point(&#039;io.ox/core/notifications/tutorial/item&#039;).extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $(&#039;&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
        .attr(&#039;data-cid&#039;, baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $(&#039;&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;&#039;).text(baton.model.get(&#039;title&#039;)),//some text to fill it&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;
As you can see we add div with the classes tutorial and item and give it the attribute &#039;&#039;data-cid&#039;&#039; which we fill with our models cid  or your custom id from the models attributes for later identification.&lt;br /&gt;
After that we add a node and fill it with the title text of our model.&lt;br /&gt;
&lt;br /&gt;
Open up your Appsuite and check if everything works. Be sure to load the custom manifests if needed.&lt;br /&gt;
It should look like this:&lt;br /&gt;
&lt;br /&gt;
[[Image: notifications.png]]&lt;br /&gt;
&lt;br /&gt;
==Adding functionality==&lt;br /&gt;
===Adding a Sidepopup===&lt;br /&gt;
&lt;br /&gt;
A notification without functions is a bit boring, so lets add some action to them by opening a sidepopup and draw the description in it.&lt;br /&gt;
&lt;br /&gt;
Change your views events so it looks like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//events from our items&lt;br /&gt;
events: {&lt;br /&gt;
    &#039;click .item&#039;: &#039;openPopup&#039;,&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This calls the &#039;&#039;openPopup&#039;&#039; function of our view if you click on a div with the class item, which, in this case, are our notifications.&lt;br /&gt;
We will create the &#039;&#039;openPopup&#039;&#039; function now, add this function to your view:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//the action for our sidepopup&lt;br /&gt;
openPopup: function (e) {&lt;br /&gt;
            &lt;br /&gt;
    var overlay = $(&#039;#io-ox-notifications-overlay&#039;),//the overlay we draw our sidepopup in&lt;br /&gt;
        cid = $(e.currentTarget).attr(&#039;data-cid&#039;),//getting the right model&lt;br /&gt;
        model = this.collection.getByCid(cid);&lt;br /&gt;
            &lt;br /&gt;
    require([&#039;io.ox/core/tk/dialogs&#039;], function (dialogs) {//require dialogs&lt;br /&gt;
        //create the popup&lt;br /&gt;
        new dialogs.SidePopup({ arrow: false, side: &#039;right&#039; })&lt;br /&gt;
            .setTarget(overlay)&lt;br /&gt;
            .show(e, function (popup) {&lt;br /&gt;
                //fill it with our data&lt;br /&gt;
                popup.append($(&#039;&amp;lt;div&amp;gt;&#039;).text(model.get(&#039;title&#039;)),&lt;br /&gt;
                             $(&#039;&amp;lt;br&amp;gt;&#039;),&lt;br /&gt;
                             $(&#039;&amp;lt;div&amp;gt;&#039;).text(model.get(&#039;description&#039;)));&lt;br /&gt;
            });&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
First we  define some variables. &#039;&#039;overlay&#039;&#039; is the div we append our popup to, &#039;&#039;cid&#039;&#039; is the cid we added as an attribute to our notification div earlier and &#039;&#039;model&#039;&#039; is the model we get from our collection by this cid.&lt;br /&gt;
After this we require our &#039;&#039;dialogs&#039;&#039; plugin and create a new sidepopup. The target we draw it on is the overlay we grabbed earlier.&lt;br /&gt;
In the show method we draw the contents of our popup. In this case its the models title variable and description variable.&lt;br /&gt;
&lt;br /&gt;
Time to check if it works. In the appsuite your notifications should now open a sidepopup if you click on them.&lt;br /&gt;
&lt;br /&gt;
===Add a remove option===&lt;br /&gt;
We don&#039;t want our notifications to stay there forever, so we need a way to remove them.&lt;br /&gt;
Let&#039;s add a button to do this.&lt;br /&gt;
&lt;br /&gt;
Add the button in your item draw function:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
ext.point(&#039;io.ox/core/notifications/tutorial/item&#039;).extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $(&#039;&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
        .attr(&#039;data-cid&#039;, baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $(&#039;&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;&#039;).text(baton.model.get(&#039;title&#039;)),//some text to fill it&lt;br /&gt;
                $(&#039;&amp;lt;button class=&amp;quot;mybutton btn btn-primary&amp;quot; data-action=&amp;quot;close&amp;quot;&amp;gt;&#039;).text(&#039;close&#039;)//close button&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;
Now add an event to your view to be triggered on clicking it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
events: {&lt;br /&gt;
    &#039;click .item&#039;: &#039;openPopup&#039;,&lt;br /&gt;
    &#039;click [data-action=&amp;quot;close&amp;quot;]&#039;: &#039;closeNotification&#039;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will call the closeNotification if the user clicks on a dom node where the attribute data-action has the value close, like our button.&lt;br /&gt;
&lt;br /&gt;
Finally add the close funktion to your view:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//the action for the close button&lt;br /&gt;
closeNotification: function (e) {&lt;br /&gt;
    e.stopPropagation();//to prevent sidepopup from opening&lt;br /&gt;
            &lt;br /&gt;
    var cid = $(e.currentTarget).closest(&#039;.item&#039;).attr(&#039;data-cid&#039;),//getting the right model&lt;br /&gt;
        model = this.collection.getByCid(cid);&lt;br /&gt;
            &lt;br /&gt;
    this.collection.remove(model);//remove it from the collection&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Again get the cid to identify the right model, then remove it from the collection.&lt;br /&gt;
&#039;&#039;e.stopPropagation()&#039;&#039; is important here because otherwise the event would bubble up and trigger the click event for opening our sidepopup too.&lt;br /&gt;
&lt;br /&gt;
Congratulations your notifications should be removed if you click the button.&lt;br /&gt;
&lt;br /&gt;
The final version should look like this.&lt;br /&gt;
&lt;br /&gt;
[[Image: finished.png]]&lt;br /&gt;
&lt;br /&gt;
==Download==&lt;br /&gt;
You can download the examplecode here.&lt;br /&gt;
&lt;br /&gt;
[[File: Notification_tutorial.zip]].&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&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>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Writing_a_notification_area_plugin&amp;diff=20367</id>
		<title>AppSuite:Writing a notification area plugin</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Writing_a_notification_area_plugin&amp;diff=20367"/>
		<updated>2015-09-10T08:20:48Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: Matthias.biggeleben moved page AppSuite:Writing a notification area plugin to AppSuite:Writing a notification area plugin (7.6.x): Deprecated&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[AppSuite:Writing a notification area plugin (7.6.x)]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Writing_a_notification_area_plugin_(7.6.x)&amp;diff=20366</id>
		<title>AppSuite:Writing a notification area plugin (7.6.x)</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Writing_a_notification_area_plugin_(7.6.x)&amp;diff=20366"/>
		<updated>2015-09-10T08:20:47Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: Matthias.biggeleben moved page AppSuite:Writing a notification area plugin to AppSuite:Writing a notification area plugin (7.6.x): Deprecated&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Stability-experimental}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Writing a plugin for the notification area&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Abstract:&#039;&#039;&#039; This article is a step by step tutorial to build your own notification plugin.&lt;br /&gt;
These plugins can be used for various purposes, for example reminding the user of something or showing him new invitations.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
==Preparations==&lt;br /&gt;
&lt;br /&gt;
To start a new plugin for the notification area you have to add a new folder at &#039;&#039;apps.plugins/notifications/&#039;&#039; .&lt;br /&gt;
For this tutorial we will create &#039;&#039;plugins/notifications/tutorial/&#039;&#039; .&lt;br /&gt;
&lt;br /&gt;
Now add a new file to your folder and name it &#039;&#039;register.js&#039;&#039; .&lt;br /&gt;
Then add the basic markup such as copyright, define for &#039;&#039;require.js&#039;&#039;, use strict and so on.&lt;br /&gt;
In our tutorial the result looks like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * your copyright here&lt;br /&gt;
 * @author Mister Test &amp;lt;mister.test@test.test&amp;gt;&lt;br /&gt;
 */&lt;br /&gt;
 &lt;br /&gt;
define(&#039;plugins/notifications/tutorial/register&#039;,&lt;br /&gt;
    [&#039;io.ox/core/extensions&#039;], function (ext) {&lt;br /&gt;
&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
    &lt;br /&gt;
    //just to give something back&lt;br /&gt;
    return true;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: We need to use extensions so we need to require the needed resources with&lt;br /&gt;
  [&#039;io.ox/core/extensions&#039;], function (ext)&lt;br /&gt;
as seen above.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===Manifests===&lt;br /&gt;
&lt;br /&gt;
Your file is not loaded yet. To do this we need to create a manifest file.&lt;br /&gt;
Create a new file in your folder with the name &#039;&#039;manifest.json&#039;&#039; with the following code in it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
	namespace: &amp;quot;io.ox/core/notifications&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For developing add the following code to &#039;&#039;src/manifests.js&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    namespace: [&#039;io.ox/core/notifications&#039;],&lt;br /&gt;
    path: &#039;plugins/notifications/tutorial/register&#039;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For further information about manifests look [[AppSuite:UI manifests explained|here]].&lt;br /&gt;
&lt;br /&gt;
==Coding the base==&lt;br /&gt;
===Registering your plugin===&lt;br /&gt;
&lt;br /&gt;
Now you need to register your plugin by extending the right extension point, which is &#039;&#039;io.ox/core/notifications/register&#039;&#039; .&lt;br /&gt;
Give your plugin a unique id, indexnumber and a register function.&lt;br /&gt;
Inside this function we register our notification plugin at the controller and also give it an id and our view, we create later on.&lt;br /&gt;
Do so by adding:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//register our notification plugin&lt;br /&gt;
ext.point(&#039;io.ox/core/notifications/register&#039;).extend({&lt;br /&gt;
    id: &#039;tutorial&#039;,//unique id&lt;br /&gt;
    index: 500, //unused index&lt;br /&gt;
    register: function (controller) {&lt;br /&gt;
        //give our plugin a name and send it to the controller together with our view&lt;br /&gt;
        var notifications = controller.get(&#039;io.ox/tutorial&#039;, NotificationsView);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;io.ox/tutorial&#039;&#039; is the id the controller should use to refer to our plugin and &#039;&#039;NotificationsView&#039;&#039; is the view we will create now to display it.&lt;br /&gt;
&lt;br /&gt;
===Creating the View===&lt;br /&gt;
&lt;br /&gt;
Since we use will use backbone to create our plugin it obviously needs a view.&lt;br /&gt;
Call the variable like the one you gave to the controller to make it work.&lt;br /&gt;
In our example the code to create the view looks like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//the view of our plugin&lt;br /&gt;
var NotificationsView = Backbone.View.extend({&lt;br /&gt;
&lt;br /&gt;
    className: &#039;notifications&#039;,&lt;br /&gt;
    id: &#039;io-ox-notifications-tutorial&#039;,&lt;br /&gt;
        &lt;br /&gt;
    //events from our items&lt;br /&gt;
    events: {&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    //draws the plugin&lt;br /&gt;
    render: function () {&lt;br /&gt;
        return this;&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: Events and render function are empty at the moment, but we will fix that soon.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===Adding the headline===&lt;br /&gt;
&lt;br /&gt;
Now we want to draw something. We could just put it in the render method, but since we have the extension point architecture we will make use of it, to keep our actual render method cleaned up.&lt;br /&gt;
&lt;br /&gt;
We start by creating an extension point we will use to draw our headline and create a container for our notifications to put in later.&lt;br /&gt;
Add this code to your views render method to create the point and invoke the draw method of its extensions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//build baton to wrap things up&lt;br /&gt;
var baton = ext.Baton({ view: this });&lt;br /&gt;
//draw header and container by creating an extension point and invoke drawing on its extensions&lt;br /&gt;
ext.point(&#039;io.ox/core/notifications/tutorial/header&#039;).invoke(&#039;draw&#039;, this.$el.empty(), baton);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: Here we use our special baton objects to pass the data to the extension points. By using this.$el.empty() as a dom node to draw we ensure that we clean up properly before drawing.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Now extend the point with a simple draw method for our header and container.&lt;br /&gt;
&#039;&#039;this&#039;&#039; refers to our views dom node we draw in in the rendering method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//the header and container for the notification plugin&lt;br /&gt;
ext.point(&#039;io.ox/core/notifications/tutorial/header&#039;).extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
            $(&#039;&amp;lt;legend class=&amp;quot;section-title&amp;quot;&amp;gt;&#039;).text(&#039;Hello World&#039;),//header&lt;br /&gt;
            $(&#039;&amp;lt;div class=&amp;quot;notifications&amp;quot;&amp;gt;&#039;)//the container for our notifications&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Congratulations the first steps are done. Time for some testing to see if we did it right.&lt;br /&gt;
&lt;br /&gt;
==First testing==&lt;br /&gt;
&lt;br /&gt;
Now we want to see how it looks in the program.&lt;br /&gt;
To do this add this line of code to your register method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
notifications.collection.reset(new Backbone.Model());&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates an empty notification model and adds it to our plugins collection.&lt;br /&gt;
We get to know how this collection works later on, for now we just need something in it for the controller to think that there is a notification to display.&lt;br /&gt;
&lt;br /&gt;
When finished start your appsuite and login, add &#039;&#039;&amp;amp;customManifests=true&#039;&#039; to the url to load your plugin and reload the page.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: The notification area is loaded with a delay, so you have to wait a bit.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
After it&#039;s loaded you should see something like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: header.png]]&lt;br /&gt;
&lt;br /&gt;
Well done now we need to add some real notifications.&lt;br /&gt;
&lt;br /&gt;
==Adding Notifications==&lt;br /&gt;
&lt;br /&gt;
===Triggering the events===&lt;br /&gt;
Normally a notification area listens for events of the app it is related to. For example the mail notifications, listen for events on from the mail api.&lt;br /&gt;
&lt;br /&gt;
For this tutorial we will just create a small dummy to create some Notifications for us and then trigger the proper event.&lt;br /&gt;
&lt;br /&gt;
Our dummy looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
/* simple helper to trigger some events&lt;br /&gt;
   normally this is done by mailapi, taskapi, etc. */&lt;br /&gt;
var myEventTriggerer = {&lt;br /&gt;
        lookForItems: function () {&lt;br /&gt;
            //build some items and put them in an array&lt;br /&gt;
            var items = [{title: &#039;I am a notification&#039;, description: &#039;Hello world!&#039;},&lt;br /&gt;
                         {title: &#039;I am a notification too&#039;, description: &#039;Hooray!&#039;}];&lt;br /&gt;
            //trigger the event to add them&lt;br /&gt;
            $(myEventTriggerer).trigger(&#039;set-tutorial-notification&#039;, [items]);&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This dummy creates an array with two objects containing our notifications data.&lt;br /&gt;
Then it triggers an event on itself and passes the array as an argument.&lt;br /&gt;
&lt;br /&gt;
===Helper functions===&lt;br /&gt;
Notifications are stored as backbone models in a collection of our view.&lt;br /&gt;
Or models have the attributes title and description. A cid is added automatically that we use to identify them later on. You can also give ids as normal attributes and use them if you want more control over it.&lt;br /&gt;
This collection is available in the register method under &#039;&#039;notifications.collection&#039;&#039;.&lt;br /&gt;
The controller looks for changes in this collection and triggers a redraw.&lt;br /&gt;
&lt;br /&gt;
To do this we create a simple functions for resetting notification models in our collection.&lt;br /&gt;
Remove our testing line &#039;&#039;notifications.collection.reset(new Backbone.Model());&#039;&#039; from the register method and add our new function:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
/* fill our collection of notification models with new ones&lt;br /&gt;
   items here is an array of Objects containing our attributes */&lt;br /&gt;
function reset(e, items) {&lt;br /&gt;
    var models = [];&lt;br /&gt;
    items = [].concat(items);//make sure we have an array&lt;br /&gt;
    _(items).each(function (item) {&lt;br /&gt;
        models.push(new Backbone.Model(item));&lt;br /&gt;
        });&lt;br /&gt;
    notifications.collection.reset(models);//fill the collection&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function simply loops over the array of items they are given, creates models from them and fills the collection with it. Reset means that the old models are gone now and only the new ones are in our collection.&lt;br /&gt;
Functions to add and remove models are done the same way but are not always needed, as in this example. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: notifications.collection.add() does not trigger the add event. You need to do this manually, this seems to be a backbone issue.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===Listen for events===&lt;br /&gt;
&lt;br /&gt;
So now we need just listen to the event to launch our reset function and call our little dummy to fill our initial collection.&lt;br /&gt;
We do this by adding this code to the register method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//now add the event listeners&lt;br /&gt;
$(myEventTriggerer).on(&#039;set-tutorial-notification&#039;, reset);&lt;br /&gt;
&lt;br /&gt;
//just to make sure it gets filled with values on load we trigger our search method&lt;br /&gt;
myEventTriggerer.lookForItems();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Drawing the Notifications===&lt;br /&gt;
Now that we have proper models, we need to draw them.&lt;br /&gt;
To do this we do the same as with the header, create an extension point and invoke drawing.&lt;br /&gt;
In our views render method we need to add:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//loop over collection and draw&lt;br /&gt;
this.collection.each(function (model) {&lt;br /&gt;
    baton = ext.Baton({ model: model, view: this });&lt;br /&gt;
    ext.point(&#039;io.ox/core/notifications/tutorial/item&#039;)&lt;br /&gt;
        .invoke(&#039;draw&#039;, this.$(&#039;.notifications&#039;), baton);//draw the item&lt;br /&gt;
}, this);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This time we draw in the container div we created by our header drawing method.&lt;br /&gt;
Next we extend our extension point to draw the actual notification items.&lt;br /&gt;
&lt;br /&gt;
In our example it looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//draw a single notification item&lt;br /&gt;
ext.point(&#039;io.ox/core/notifications/tutorial/item&#039;).extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $(&#039;&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
        .attr(&#039;data-cid&#039;, baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $(&#039;&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;&#039;).text(baton.model.get(&#039;title&#039;)),//some text to fill it&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;
As you can see we add div with the classes tutorial and item and give it the attribute &#039;&#039;data-cid&#039;&#039; which we fill with our models cid  or your custom id from the models attributes for later identification.&lt;br /&gt;
After that we add a node and fill it with the title text of our model.&lt;br /&gt;
&lt;br /&gt;
Open up your Appsuite and check if everything works. Be sure to load the custom manifests if needed.&lt;br /&gt;
It should look like this:&lt;br /&gt;
&lt;br /&gt;
[[Image: notifications.png]]&lt;br /&gt;
&lt;br /&gt;
==Adding functionality==&lt;br /&gt;
===Adding a Sidepopup===&lt;br /&gt;
&lt;br /&gt;
A notification without functions is a bit boring, so lets add some action to them by opening a sidepopup and draw the description in it.&lt;br /&gt;
&lt;br /&gt;
Change your views events so it looks like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//events from our items&lt;br /&gt;
events: {&lt;br /&gt;
    &#039;click .item&#039;: &#039;openPopup&#039;,&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This calls the &#039;&#039;openPopup&#039;&#039; function of our view if you click on a div with the class item, which, in this case, are our notifications.&lt;br /&gt;
We will create the &#039;&#039;openPopup&#039;&#039; function now, add this function to your view:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//the action for our sidepopup&lt;br /&gt;
openPopup: function (e) {&lt;br /&gt;
            &lt;br /&gt;
    var overlay = $(&#039;#io-ox-notifications-overlay&#039;),//the overlay we draw our sidepopup in&lt;br /&gt;
        cid = $(e.currentTarget).attr(&#039;data-cid&#039;),//getting the right model&lt;br /&gt;
        model = this.collection.getByCid(cid);&lt;br /&gt;
            &lt;br /&gt;
    require([&#039;io.ox/core/tk/dialogs&#039;], function (dialogs) {//require dialogs&lt;br /&gt;
        //create the popup&lt;br /&gt;
        new dialogs.SidePopup({ arrow: false, side: &#039;right&#039; })&lt;br /&gt;
            .setTarget(overlay)&lt;br /&gt;
            .show(e, function (popup) {&lt;br /&gt;
                //fill it with our data&lt;br /&gt;
                popup.append($(&#039;&amp;lt;div&amp;gt;&#039;).text(model.get(&#039;title&#039;)),&lt;br /&gt;
                             $(&#039;&amp;lt;br&amp;gt;&#039;),&lt;br /&gt;
                             $(&#039;&amp;lt;div&amp;gt;&#039;).text(model.get(&#039;description&#039;)));&lt;br /&gt;
            });&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
First we  define some variables. &#039;&#039;overlay&#039;&#039; is the div we append our popup to, &#039;&#039;cid&#039;&#039; is the cid we added as an attribute to our notification div earlier and &#039;&#039;model&#039;&#039; is the model we get from our collection by this cid.&lt;br /&gt;
After this we require our &#039;&#039;dialogs&#039;&#039; plugin and create a new sidepopup. The target we draw it on is the overlay we grabbed earlier.&lt;br /&gt;
In the show method we draw the contents of our popup. In this case its the models title variable and description variable.&lt;br /&gt;
&lt;br /&gt;
Time to check if it works. In the appsuite your notifications should now open a sidepopup if you click on them.&lt;br /&gt;
&lt;br /&gt;
===Add a remove option===&lt;br /&gt;
We don&#039;t want our notifications to stay there forever, so we need a way to remove them.&lt;br /&gt;
Let&#039;s add a button to do this.&lt;br /&gt;
&lt;br /&gt;
Add the button in your item draw function:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
ext.point(&#039;io.ox/core/notifications/tutorial/item&#039;).extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $(&#039;&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;&#039;)&lt;br /&gt;
        .attr(&#039;data-cid&#039;, baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $(&#039;&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;&#039;).text(baton.model.get(&#039;title&#039;)),//some text to fill it&lt;br /&gt;
                $(&#039;&amp;lt;button class=&amp;quot;mybutton btn btn-primary&amp;quot; data-action=&amp;quot;close&amp;quot;&amp;gt;&#039;).text(&#039;close&#039;)//close button&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;
Now add an event to your view to be triggered on clicking it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt;&lt;br /&gt;
events: {&lt;br /&gt;
    &#039;click .item&#039;: &#039;openPopup&#039;,&lt;br /&gt;
    &#039;click [data-action=&amp;quot;close&amp;quot;]&#039;: &#039;closeNotification&#039;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will call the closeNotification if the user clicks on a dom node where the attribute data-action has the value close, like our button.&lt;br /&gt;
&lt;br /&gt;
Finally add the close funktion to your view:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
//the action for the close button&lt;br /&gt;
closeNotification: function (e) {&lt;br /&gt;
    e.stopPropagation();//to prevent sidepopup from opening&lt;br /&gt;
            &lt;br /&gt;
    var cid = $(e.currentTarget).closest(&#039;.item&#039;).attr(&#039;data-cid&#039;),//getting the right model&lt;br /&gt;
        model = this.collection.getByCid(cid);&lt;br /&gt;
            &lt;br /&gt;
    this.collection.remove(model);//remove it from the collection&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Again get the cid to identify the right model, then remove it from the collection.&lt;br /&gt;
&#039;&#039;e.stopPropagation()&#039;&#039; is important here because otherwise the event would bubble up and trigger the click event for opening our sidepopup too.&lt;br /&gt;
&lt;br /&gt;
Congratulations your notifications should be removed if you click the button.&lt;br /&gt;
&lt;br /&gt;
The final version should look like this.&lt;br /&gt;
&lt;br /&gt;
[[Image: finished.png]]&lt;br /&gt;
&lt;br /&gt;
==Download==&lt;br /&gt;
You can download the examplecode here.&lt;br /&gt;
&lt;br /&gt;
[[File: Notification_tutorial.zip]].&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&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>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20190</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20190"/>
		<updated>2015-08-10T09:04:08Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* API */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
Basic popup&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|500px|frameless]]&lt;br /&gt;
&lt;br /&gt;
Step with hotspot&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard-hotspot.png|799px|frameless]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| referTo(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| hotspot(selector, options)&lt;br /&gt;
or hotspot(array)&lt;br /&gt;
| Places a hotspot on top left corner of given element; options can define &amp;quot;top&amp;quot; and &amp;quot;left&amp;quot; to adjust the position (pixel values). The API also support multiple hotspots: hotspot(array) with array containing selectors (string) or array of [selector, options].&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector, [timeout])&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup. Optional: Use a custom timeout. Default is 5 seconds.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
| step:*&lt;br /&gt;
| All step-based events are forwarded, e.g. step:back, step:next, step:before:show&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|-&lt;br /&gt;
| before:navigate&lt;br /&gt;
| Triggered before the step navigates to another app&lt;br /&gt;
|- &lt;br /&gt;
| navigate&lt;br /&gt;
| Triggered after the step has navigated to another app&lt;br /&gt;
|-&lt;br /&gt;
| wait&lt;br /&gt;
| Triggered when the step wait for a certain element (waitFor)&lt;br /&gt;
|-&lt;br /&gt;
| ready&lt;br /&gt;
| Triggered when &amp;quot;waitFor&amp;quot; is resolved (this event is always triggered, when a step is ready to be displayed)&lt;br /&gt;
|-&lt;br /&gt;
| align&lt;br /&gt;
| Triggered right after the automatic alignment but shortly before the step is shown&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Smartphone support ===&lt;br /&gt;
The framework has embedded smartphone support. However, it is recommended to create dedicated tours for smartphones because their overall appearance is different, i.e. tours run in fullscreen. Therefore, referTo(), spotlight(), navigateTo(), waitFor() have no effect because you cannot see the underlying UI; which probably has an impact on the text as well. Generally, a tour should be quite simple. Less is more.&lt;br /&gt;
&lt;br /&gt;
[[File:Tour_on_smartphone.png|frameless|319px]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20189</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20189"/>
		<updated>2015-08-10T09:01:28Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Events */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
Basic popup&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|500px|frameless]]&lt;br /&gt;
&lt;br /&gt;
Step with hotspot&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard-hotspot.png|799px|frameless]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| referTo(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| hotspot(selector, options)&lt;br /&gt;
or hotspot(array)&lt;br /&gt;
| Places a hotspot on top left corner of given element; options can define &amp;quot;top&amp;quot; and &amp;quot;left&amp;quot; to adjust the position (pixel values). The API also support multiple hotspots: hotspot(array) with array containing selectors (string) or array of [selector, options].&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
| step:*&lt;br /&gt;
| All step-based events are forwarded, e.g. step:back, step:next, step:before:show&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|-&lt;br /&gt;
| before:navigate&lt;br /&gt;
| Triggered before the step navigates to another app&lt;br /&gt;
|- &lt;br /&gt;
| navigate&lt;br /&gt;
| Triggered after the step has navigated to another app&lt;br /&gt;
|-&lt;br /&gt;
| wait&lt;br /&gt;
| Triggered when the step wait for a certain element (waitFor)&lt;br /&gt;
|-&lt;br /&gt;
| ready&lt;br /&gt;
| Triggered when &amp;quot;waitFor&amp;quot; is resolved (this event is always triggered, when a step is ready to be displayed)&lt;br /&gt;
|-&lt;br /&gt;
| align&lt;br /&gt;
| Triggered right after the automatic alignment but shortly before the step is shown&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Smartphone support ===&lt;br /&gt;
The framework has embedded smartphone support. However, it is recommended to create dedicated tours for smartphones because their overall appearance is different, i.e. tours run in fullscreen. Therefore, referTo(), spotlight(), navigateTo(), waitFor() have no effect because you cannot see the underlying UI; which probably has an impact on the text as well. Generally, a tour should be quite simple. Less is more.&lt;br /&gt;
&lt;br /&gt;
[[File:Tour_on_smartphone.png|frameless|319px]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20188</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20188"/>
		<updated>2015-08-10T09:00:12Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Events */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
Basic popup&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|500px|frameless]]&lt;br /&gt;
&lt;br /&gt;
Step with hotspot&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard-hotspot.png|799px|frameless]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| referTo(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| hotspot(selector, options)&lt;br /&gt;
or hotspot(array)&lt;br /&gt;
| Places a hotspot on top left corner of given element; options can define &amp;quot;top&amp;quot; and &amp;quot;left&amp;quot; to adjust the position (pixel values). The API also support multiple hotspots: hotspot(array) with array containing selectors (string) or array of [selector, options].&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
| step:*&lt;br /&gt;
| All step-based events are forwarded, e.g. step:back, step:next, step:before:show&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|-&lt;br /&gt;
| wait&lt;br /&gt;
| Triggered when the step wait for a certain element (waitFor)&lt;br /&gt;
|-&lt;br /&gt;
| ready&lt;br /&gt;
| Triggered when &amp;quot;waitFor&amp;quot; is resolved (this event is always triggered, when a step is ready to be displayed)&lt;br /&gt;
|-&lt;br /&gt;
| align&lt;br /&gt;
| Triggered right after the automatic alignment but shortly before the step is shown&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Smartphone support ===&lt;br /&gt;
The framework has embedded smartphone support. However, it is recommended to create dedicated tours for smartphones because their overall appearance is different, i.e. tours run in fullscreen. Therefore, referTo(), spotlight(), navigateTo(), waitFor() have no effect because you cannot see the underlying UI; which probably has an impact on the text as well. Generally, a tour should be quite simple. Less is more.&lt;br /&gt;
&lt;br /&gt;
[[File:Tour_on_smartphone.png|frameless|319px]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20050</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20050"/>
		<updated>2015-07-23T09:58:50Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* API */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
Basic popup&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|500px|frameless]]&lt;br /&gt;
&lt;br /&gt;
Step with hotspot&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard-hotspot.png|799px|frameless]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| referTo(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| hotspot(selector, options)&lt;br /&gt;
or hotspot(array)&lt;br /&gt;
| Places a hotspot on top left corner of given element; options can define &amp;quot;top&amp;quot; and &amp;quot;left&amp;quot; to adjust the position (pixel values). The API also support multiple hotspots: hotspot(array) with array containing selectors (string) or array of [selector, options].&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Smartphone support ===&lt;br /&gt;
The framework has embedded smartphone support. However, it is recommended to create dedicated tours for smartphones because their overall appearance is different, i.e. tours run in fullscreen. Therefore, referTo(), spotlight(), navigateTo(), waitFor() have no effect because you cannot see the underlying UI; which probably has an impact on the text as well. Generally, a tour should be quite simple. Less is more.&lt;br /&gt;
&lt;br /&gt;
[[File:Tour_on_smartphone.png|frameless|319px]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20049</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20049"/>
		<updated>2015-07-23T09:58:10Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* API */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
Basic popup&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|500px|frameless]]&lt;br /&gt;
&lt;br /&gt;
Step with hotspot&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard-hotspot.png|799px|frameless]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| referTo(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| hotspot(selector, options)&lt;br /&gt;
| Places a hotspot on top left corner of given element; options can define &amp;quot;top&amp;quot; and &amp;quot;left&amp;quot; to adjust the position (pixel values). The API also support multiple hotspots: hotspot(array) with array containing selectors (string) or array of [selector, options].&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Smartphone support ===&lt;br /&gt;
The framework has embedded smartphone support. However, it is recommended to create dedicated tours for smartphones because their overall appearance is different, i.e. tours run in fullscreen. Therefore, referTo(), spotlight(), navigateTo(), waitFor() have no effect because you cannot see the underlying UI; which probably has an impact on the text as well. Generally, a tour should be quite simple. Less is more.&lt;br /&gt;
&lt;br /&gt;
[[File:Tour_on_smartphone.png|frameless|319px]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20042</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20042"/>
		<updated>2015-07-22T13:33:41Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Screenshot */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
Basic popup&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|500px|frameless]]&lt;br /&gt;
&lt;br /&gt;
Step with hotspot&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard-hotspot.png|799px|frameless]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| referTo(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| hotspot(selector, options)&lt;br /&gt;
| Places a hotspot on top left corner of given element; options can define &amp;quot;top&amp;quot; and &amp;quot;left&amp;quot; to adjust the position (pixel values).&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Smartphone support ===&lt;br /&gt;
The framework has embedded smartphone support. However, it is recommended to create dedicated tours for smartphones because their overall appearance is different, i.e. tours run in fullscreen. Therefore, referTo(), spotlight(), navigateTo(), waitFor() have no effect because you cannot see the underlying UI; which probably has an impact on the text as well. Generally, a tour should be quite simple. Less is more.&lt;br /&gt;
&lt;br /&gt;
[[File:Tour_on_smartphone.png|frameless|319px]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20041</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20041"/>
		<updated>2015-07-22T13:33:25Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Screenshot */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
Basic popup&lt;br /&gt;
[[File:Wizard_example_popup.png|500px|frameless]]&lt;br /&gt;
&lt;br /&gt;
Step with hotspot&lt;br /&gt;
[[File:Wizard-hotspot.png|799px|frameless]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| referTo(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| hotspot(selector, options)&lt;br /&gt;
| Places a hotspot on top left corner of given element; options can define &amp;quot;top&amp;quot; and &amp;quot;left&amp;quot; to adjust the position (pixel values).&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Smartphone support ===&lt;br /&gt;
The framework has embedded smartphone support. However, it is recommended to create dedicated tours for smartphones because their overall appearance is different, i.e. tours run in fullscreen. Therefore, referTo(), spotlight(), navigateTo(), waitFor() have no effect because you cannot see the underlying UI; which probably has an impact on the text as well. Generally, a tour should be quite simple. Less is more.&lt;br /&gt;
&lt;br /&gt;
[[File:Tour_on_smartphone.png|frameless|319px]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20040</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20040"/>
		<updated>2015-07-22T13:32:58Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Screenshot */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|500px|frameless|Basic popup]]&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard-hotspot.png|799px|frameless|Step with hotspot]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| referTo(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| hotspot(selector, options)&lt;br /&gt;
| Places a hotspot on top left corner of given element; options can define &amp;quot;top&amp;quot; and &amp;quot;left&amp;quot; to adjust the position (pixel values).&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Smartphone support ===&lt;br /&gt;
The framework has embedded smartphone support. However, it is recommended to create dedicated tours for smartphones because their overall appearance is different, i.e. tours run in fullscreen. Therefore, referTo(), spotlight(), navigateTo(), waitFor() have no effect because you cannot see the underlying UI; which probably has an impact on the text as well. Generally, a tour should be quite simple. Less is more.&lt;br /&gt;
&lt;br /&gt;
[[File:Tour_on_smartphone.png|frameless|319px]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20039</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20039"/>
		<updated>2015-07-22T13:31:36Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Screenshot */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|500px|frame|Basic popup]]&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard-hotspot.png|799px|frame|Step with hotspot]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| referTo(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| hotspot(selector, options)&lt;br /&gt;
| Places a hotspot on top left corner of given element; options can define &amp;quot;top&amp;quot; and &amp;quot;left&amp;quot; to adjust the position (pixel values).&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Smartphone support ===&lt;br /&gt;
The framework has embedded smartphone support. However, it is recommended to create dedicated tours for smartphones because their overall appearance is different, i.e. tours run in fullscreen. Therefore, referTo(), spotlight(), navigateTo(), waitFor() have no effect because you cannot see the underlying UI; which probably has an impact on the text as well. Generally, a tour should be quite simple. Less is more.&lt;br /&gt;
&lt;br /&gt;
[[File:Tour_on_smartphone.png|frameless|319px]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=File:Wizard-hotspot.png&amp;diff=20038</id>
		<title>File:Wizard-hotspot.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=File:Wizard-hotspot.png&amp;diff=20038"/>
		<updated>2015-07-22T13:29:10Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20029</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20029"/>
		<updated>2015-07-22T11:34:11Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* API */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|frameless|500px]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| referTo(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| hotspot(selector, options)&lt;br /&gt;
| Places a hotspot on top left corner of given element; options can define &amp;quot;top&amp;quot; and &amp;quot;left&amp;quot; to adjust the position (pixel values).&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Smartphone support ===&lt;br /&gt;
The framework has embedded smartphone support. However, it is recommended to create dedicated tours for smartphones because their overall appearance is different, i.e. tours run in fullscreen. Therefore, referTo(), spotlight(), navigateTo(), waitFor() have no effect because you cannot see the underlying UI; which probably has an impact on the text as well. Generally, a tour should be quite simple. Less is more.&lt;br /&gt;
&lt;br /&gt;
[[File:Tour_on_smartphone.png|frameless|319px]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20028</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20028"/>
		<updated>2015-07-22T11:31:34Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: Renamed pointAt to referTo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|frameless|500px]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| referTo(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .referTo(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Smartphone support ===&lt;br /&gt;
The framework has embedded smartphone support. However, it is recommended to create dedicated tours for smartphones because their overall appearance is different, i.e. tours run in fullscreen. Therefore, referTo(), spotlight(), navigateTo(), waitFor() have no effect because you cannot see the underlying UI; which probably has an impact on the text as well. Generally, a tour should be quite simple. Less is more.&lt;br /&gt;
&lt;br /&gt;
[[File:Tour_on_smartphone.png|frameless|319px]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20025</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20025"/>
		<updated>2015-07-21T17:58:04Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* API */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|frameless|500px]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Smartphone support ===&lt;br /&gt;
The framework has embedded smartphone support. However, it is recommended to create dedicated tours for smartphones because their overall appearance is different, i.e. tours run in fullscreen. Therefore, pointAt(), spotlight(), navigateTo(), waitFor() have no effect because you cannot see the underlying UI; which probably has an impact on the text as well. Generally, a tour should be quite simple. Less is more.&lt;br /&gt;
&lt;br /&gt;
[[File:Tour_on_smartphone.png|frameless|319px]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20008</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20008"/>
		<updated>2015-07-20T18:42:50Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Smartphone support */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|frameless|500px]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Smartphone support ===&lt;br /&gt;
The framework has embedded smartphone support. However, it is recommended to create dedicated tours for smartphones because their overall appearance is different, i.e. tours run in fullscreen. Therefore, pointAt(), spotlight(), navigateTo(), waitFor() have no effect because you cannot see the underlying UI; which probably has an impact on the text as well. Generally, a tour should be quite simple. Less is more.&lt;br /&gt;
&lt;br /&gt;
[[File:Tour_on_smartphone.png|frameless|319px]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20007</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20007"/>
		<updated>2015-07-20T14:07:29Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Smartphone support */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|frameless|500px]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Smartphone support ===&lt;br /&gt;
The framework has embedded smartphone support. However, it is recommended to create dedicated tours for smartphones because their appearance is different, i.e. a tour runs in fullscreen. Therefore, alignment, spotlight, navigateTo, waitFor have no effect because you cannot see the underlying UI. Generally, a tour should be quite simple. Less is more.&lt;br /&gt;
&lt;br /&gt;
[[File:Tour_on_smartphone.png|frameless|319px]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20006</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20006"/>
		<updated>2015-07-20T14:06:29Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Smartphone support */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|frameless|500px]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Smartphone support ===&lt;br /&gt;
The framework has embedded smartphone support. However, it is recommended to create dedicated tours for smartphones because their appearance is different, i.e. a tour runs in fullscreen. Therefore, alignment and spotlight have no effect because you cannot see the underlying UI.&lt;br /&gt;
&lt;br /&gt;
[[File:Tour_on_smartphone.png|frameless|319px]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20005</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20005"/>
		<updated>2015-07-20T14:06:20Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Smartphone support */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|frameless|500px]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Smartphone support ===&lt;br /&gt;
The framework has embedded smartphone support. However, it is recommended to create dedicated tours for smartphones because their appearance is different, i.e. a tour runs in fullscreen. Therefore, alignment and spotlight have no effect because you cannot see the underlying UI.&lt;br /&gt;
&lt;br /&gt;
[[File:Tour_on_smartphone.png|frameless]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20004</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20004"/>
		<updated>2015-07-20T14:06:02Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Smartphone support */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|frameless|500px]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Smartphone support ===&lt;br /&gt;
The framework has embedded smartphone support. However, it is recommended to create dedicated tours for smartphones because their appearance is different, i.e. a tour runs in fullscreen. Therefore, alignment and spotlight have no effect because you cannot see the underlying UI.&lt;br /&gt;
&lt;br /&gt;
[[File:Tour_on_smartphone.png|frameless|300px]]&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=File:Tour_on_smartphone.png&amp;diff=20003</id>
		<title>File:Tour on smartphone.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=File:Tour_on_smartphone.png&amp;diff=20003"/>
		<updated>2015-07-20T14:05:29Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20002</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=20002"/>
		<updated>2015-07-20T14:03:20Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|frameless|500px]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Smartphone support ===&lt;br /&gt;
The framework has embedded smartphone support. However, it is recommended to create dedicated tours for smartphones because their appearance is different, i.e. a tour runs in fullscreen. Therefore, alignment and spotlight have no effect because you cannot see the underlying UI.&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19990</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19990"/>
		<updated>2015-07-17T09:14:47Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Screenshot */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|frameless|500px]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=File:Wizard_example_popup.png&amp;diff=19989</id>
		<title>File:Wizard example popup.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=File:Wizard_example_popup.png&amp;diff=19989"/>
		<updated>2015-07-17T09:14:08Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19988</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19988"/>
		<updated>2015-07-17T09:12:41Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Simple example */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Screenshot ===&lt;br /&gt;
&lt;br /&gt;
[[File:Wizard_example_popup.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19987</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19987"/>
		<updated>2015-07-17T09:06:06Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* API */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button. All buttons are enabled by default. The first step doesn&#039;t have a back button, of course. And the last step offers a &amp;quot;Done&amp;quot; button instead of the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19980</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19980"/>
		<updated>2015-07-15T18:54:48Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* API */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button.&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one.&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one.&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialog location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element.&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown.&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19973</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19973"/>
		<updated>2015-07-15T13:50:40Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Using the registry */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialogs location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Complex example ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
void require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top-bar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
            .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Search&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.io-ox-find&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Something at the bottom&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.generic-toolbar.bottom&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Top toolbar&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.&#039;)&lt;br /&gt;
            .spotlight(&#039;.toolbar.generic-toolbar.top&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 2&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/mail/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .mandatory()&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.&#039;)&lt;br /&gt;
            .spotlight(&#039;.thread-view-list&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Step 3&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .navigateTo(&#039;io.ox/contacts/main&#039;)&lt;br /&gt;
            .waitFor(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .spotlight(&#039;.folder-tree&#039;)&lt;br /&gt;
            .scrollIntoView(&#039;.folder[data-id=&amp;quot;6&amp;quot;]&#039;)&lt;br /&gt;
            .beforeShow(function () {&lt;br /&gt;
&lt;br /&gt;
                this.$(&#039;.content&#039;).append(&lt;br /&gt;
                    $(&#039;&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;enable-next&amp;quot;&amp;gt;Enable&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&#039;)&lt;br /&gt;
                );&lt;br /&gt;
&lt;br /&gt;
                this.toggleNext(false);&lt;br /&gt;
&lt;br /&gt;
                this.$el.on(&#039;click&#039;, &#039;.enable-next&#039;, function (e) {&lt;br /&gt;
                    e.preventDefault();&lt;br /&gt;
                    this.toggleNext(true);&lt;br /&gt;
                }.bind(this));&lt;br /&gt;
            })&lt;br /&gt;
            .end()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Last step&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet.&#039;)&lt;br /&gt;
            .modal(false)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19972</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19972"/>
		<updated>2015-07-15T13:48:53Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Using the registry */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialogs location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    // you can store any data you want; only &#039;id&#039; is mandatory&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19971</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19971"/>
		<updated>2015-07-15T13:48:02Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Using the registry */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialogs location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    // get all items&lt;br /&gt;
    console.log(Tour.registry.list());&lt;br /&gt;
    // just get type=tour&lt;br /&gt;
    console.log(Tour.registry.list(&#039;tour&#039;));&lt;br /&gt;
    // which is a shortcut for&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19970</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19970"/>
		<updated>2015-07-15T13:34:59Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Using the registry */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialogs location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
A tour only works once, i.e. its steps get disposed once the tour stops. This is intended behavior in order to avoid side-effects and memory leaks. If you want to run a tour twice you have to redefine it. The registry simplifies that:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
&lt;br /&gt;
    Tour.registry.add({ id: &#039;test&#039;, title: &#039;Test&#039;, type: &#039;tour&#039; }, function () {&lt;br /&gt;
&lt;br /&gt;
        new Tour()&lt;br /&gt;
        .step()&lt;br /&gt;
            .title(&#039;Welcome&#039;)&lt;br /&gt;
            .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.&#039;)&lt;br /&gt;
            .spotlight(&#039;.classic-toolbar&#039;)&lt;br /&gt;
            .end()&lt;br /&gt;
        .start();&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // run a tour&lt;br /&gt;
    Tour.registry.run(&#039;test&#039;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry also helps at listing existing tours:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
    console.log(Tour.registry.collection.where({ type: &#039;tour&#039; }));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19969</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19969"/>
		<updated>2015-07-15T13:25:44Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Simple example */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .beforeShow(function () {&lt;br /&gt;
           // do anything you want to customize the step&lt;br /&gt;
           console.log(&#039;before show&#039;, this);&lt;br /&gt;
       })&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialogs location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
Bla bla.&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19968</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19968"/>
		<updated>2015-07-15T13:24:07Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* Simple example */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains. A more complex example that puts a spotlight on an element:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Step 2&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .spotlight(&#039;#io-ox-topbar&#039;)&lt;br /&gt;
       .pointAt(&#039;.launchers-secondary&#039;)&lt;br /&gt;
       .mandatory()&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialogs location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
Bla bla.&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19967</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19967"/>
		<updated>2015-07-15T13:22:19Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* API */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains.&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step()&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialogs location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
Bla bla.&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19966</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19966"/>
		<updated>2015-07-15T13:21:52Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* API */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains.&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step(options)&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialogs location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
Bla bla.&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19965</id>
		<title>AppSuite:Wizard framework</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Wizard_framework&amp;diff=19965"/>
		<updated>2015-07-15T13:20:54Z</updated>

		<summary type="html">&lt;p&gt;Matthias.biggeleben: /* API */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Wizard/Tour framework&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
App Suite UI provides a simple but flexible framework to implement wizards and guided tours. The essence of  both a wizard and a tour is a set of steps the end-user walks through. Usually a step is a smaller modal popup.&lt;br /&gt;
&lt;br /&gt;
=== Simple example ===&lt;br /&gt;
The starting point is the &amp;quot;Wizard&amp;quot; (or &amp;quot;Tour&amp;quot;) class defined in io.ox/core/tk/wizard.js. A simple example:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-javascript&amp;quot;&amp;gt; &lt;br /&gt;
require([&#039;io.ox/core/tk/wizard&#039;], function (Tour) {&lt;br /&gt;
   new Tour()&lt;br /&gt;
   .step()&lt;br /&gt;
       .title(&#039;Welcome&#039;)&lt;br /&gt;
       .content(&#039;Lorem ipsum dolor sit amet, consetetur sadipscing elitr&#039;)&lt;br /&gt;
       .end()&lt;br /&gt;
   .start();&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The function &#039;&#039;&#039;step()&#039;&#039;&#039; adds a new step. Each step is separate &#039;&#039;&#039;Backbone view&#039;&#039;&#039; instance (DisposableView to be more precise). The following function calls &#039;&#039;&#039;title()&#039;&#039;&#039; and &#039;&#039;&#039;content()&#039;&#039;&#039; both work on that view; &#039;&#039;&#039;end()&#039;&#039;&#039; just returns to the tour (same idea as in jQuery&#039;s end()). This allows long definition chains.&lt;br /&gt;
&lt;br /&gt;
=== API ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
| step(options)&lt;br /&gt;
| Add a new wizard/tour step.&lt;br /&gt;
|-&lt;br /&gt;
| start()&lt;br /&gt;
| Start the wizard/tour&lt;br /&gt;
|-&lt;br /&gt;
| title()&lt;br /&gt;
| Append content to the popup title. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| content()&lt;br /&gt;
| Append content the popup body. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| footer()&lt;br /&gt;
| Append content to the popup footer. Handed over to jQuery&#039;s append; can be String, DOM element, jQuery set, a function.&lt;br /&gt;
|-&lt;br /&gt;
| mandatory()&lt;br /&gt;
| Makes a step mandatory. The &amp;quot;close&amp;quot; icon gets removed; escape key no longer works.&lt;br /&gt;
|-&lt;br /&gt;
| toggleNext(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Next&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
| toggleBack(state)&lt;br /&gt;
| Enables (true) or disables (false) the &amp;quot;Back&amp;quot; button&lt;br /&gt;
|-&lt;br /&gt;
| isFirst()&lt;br /&gt;
| Returns true if the current step is the first one&lt;br /&gt;
|-&lt;br /&gt;
| isLast()&lt;br /&gt;
| Returns true if the current step is the last one&lt;br /&gt;
|-&lt;br /&gt;
| pointAt(selector)&lt;br /&gt;
| Affects the dialogs location (alignment happens automatically).&lt;br /&gt;
|-&lt;br /&gt;
| spotlight(selector)&lt;br /&gt;
| Sets a spotlight on a given element&lt;br /&gt;
|-&lt;br /&gt;
| modal([state])&lt;br /&gt;
| Shows a darker backdrop. Default is true.&lt;br /&gt;
|-&lt;br /&gt;
| waitFor(selector)&lt;br /&gt;
| The step waits for a certain element to exist before showing the popup.&lt;br /&gt;
|-&lt;br /&gt;
| navigateTo(id, [options])&lt;br /&gt;
| The step launches given app (id) before showing the popup. &amp;quot;options&amp;quot; are optional; handed over to ox.launch().&lt;br /&gt;
|-&lt;br /&gt;
| scrollIntoView(selector)&lt;br /&gt;
| This element will be scrolled into view before the popup is shown&lt;br /&gt;
|-&lt;br /&gt;
| beforeShow(callback)&lt;br /&gt;
| Registers for the &amp;quot;before:show&amp;quot; event using once(). The callback&#039;s context is the step, i.e. &amp;quot;this&amp;quot; is a backbone view.&lt;br /&gt;
|-&lt;br /&gt;
| end()&lt;br /&gt;
| Go back to parent element, i.e. the Wizard or the Tour.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Events ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
! Event name !! Description&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Wizard/Tour&lt;br /&gt;
|-&lt;br /&gt;
| step:next&lt;br /&gt;
| Triggered when moving ahead.&lt;br /&gt;
|-&lt;br /&gt;
| step:back&lt;br /&gt;
| Triggered when moving back.&lt;br /&gt;
|-&lt;br /&gt;
| step:close&lt;br /&gt;
| Triggered when closing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| step:done&lt;br /&gt;
| Triggered when finishing the wizard/tour.&lt;br /&gt;
|-&lt;br /&gt;
| before:start&lt;br /&gt;
| Triggered before starting the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| start&lt;br /&gt;
| Triggered when the wizard or the tour has been started&lt;br /&gt;
|-&lt;br /&gt;
| before:stop&lt;br /&gt;
| Triggered before closing the wizard or the tour.&lt;br /&gt;
|-&lt;br /&gt;
| stop&lt;br /&gt;
| Triggered when the wizard or the tour has been closed&lt;br /&gt;
|-&lt;br /&gt;
!colspan=&amp;quot;2&amp;quot;|Step&lt;br /&gt;
|-&lt;br /&gt;
| next / back&lt;br /&gt;
| Same as step:next or step:back (see above)&lt;br /&gt;
|-&lt;br /&gt;
| close / done&lt;br /&gt;
| Same as step:close or step:done (see above)&lt;br /&gt;
|-&lt;br /&gt;
| before:show&lt;br /&gt;
| Triggered before showing the step&lt;br /&gt;
|-&lt;br /&gt;
| show&lt;br /&gt;
| Triggered when the step is visible&lt;br /&gt;
|-&lt;br /&gt;
| before:hide&lt;br /&gt;
| Triggered before hiding the step&lt;br /&gt;
|-&lt;br /&gt;
| hide&lt;br /&gt;
| Triggered when the step is hidden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Using the registry ===&lt;br /&gt;
Bla bla.&lt;/div&gt;</summary>
		<author><name>Matthias.biggeleben</name></author>
	</entry>
</feed>