<?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=David.bauer</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=David.bauer"/>
	<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=Special:Contributions/David.bauer"/>
	<updated>2026-06-30T21:43:50Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Theming&amp;diff=17382</id>
		<title>AppSuite:Theming</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Theming&amp;diff=17382"/>
		<updated>2014-04-11T08:09:30Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Font */&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;Theming&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Abstract.''' In this article, you can learn how to create customized themes and use them to change the look of you appsuite installation.&lt;br /&gt;
__TOC__&lt;br /&gt;
== LESS.JS ==&lt;br /&gt;
Appsuite used LESS as dynamic stylesheet language. LESS extends CSS with dynamic behavior such as variables, mixins, operations and functions.&lt;br /&gt;
&lt;br /&gt;
Please read [http://lesscss.org/#docs LESS.JS] documentation first.&lt;br /&gt;
&lt;br /&gt;
=== Using less.js ===&lt;br /&gt;
If your theme depends on less.js, you will need one more step to make it work. Why? To accelerate the login, compilation of LessCSS files was moved from the login process in the browser to the installation process on the backend. &lt;br /&gt;
&lt;br /&gt;
Backend packages for themes and any apps which ship .less files require the following changes:&lt;br /&gt;
&lt;br /&gt;
1. Add &amp;quot;skipLess=1&amp;quot; to the build command in *.spec and in debian/rules:&lt;br /&gt;
   sh /opt/open-xchange-appsuite-dev/bin/build-appsuite app skipLess=1&lt;br /&gt;
2. Add %post and %postun sections to *.spec:&lt;br /&gt;
   %post&lt;br /&gt;
   if [ &amp;quot;$1&amp;quot; = 1 ]; then&lt;br /&gt;
   UPDATE=/opt/open-xchange/appsuite/share/update-themes.sh&lt;br /&gt;
   [ -x $UPDATE ] &amp;amp;&amp;amp; $UPDATE&lt;br /&gt;
   fi&lt;br /&gt;
   %postun&lt;br /&gt;
   UPDATE=/opt/open-xchange/appsuite/share/update-themes.sh&lt;br /&gt;
   [ -x $UPDATE ] &amp;amp;&amp;amp; $UPDATE&lt;br /&gt;
&lt;br /&gt;
For multiple binary packages, the %post and %postun sections should apply only to backend packages &lt;br /&gt;
which contain .less files.&lt;br /&gt;
&lt;br /&gt;
3. Add debian/postinst and debian/postrm containing the same content:&lt;br /&gt;
   #!/bin/sh&lt;br /&gt;
   UPDATE=/opt/open-xchange/appsuite/share/update-themes.sh&lt;br /&gt;
   [ -x $UPDATE ] &amp;amp;&amp;amp; $UPDATE&lt;br /&gt;
&lt;br /&gt;
For multiple binary packages, the postinst and postrm files should apply only to backend packages which contain .less files.&lt;br /&gt;
&lt;br /&gt;
Note: Since 7.2.1, LessCSS files must have the file extension .less to be usable with the 'less' RequireJS plugin (module dependencies of the form 'less!filename.less'). Previously we were more lenient and dealt with .css, too.&lt;br /&gt;
&lt;br /&gt;
== File structure ==&lt;br /&gt;
A theme basically consists of two files located in &amp;lt;code&amp;gt;apps/themes/&amp;lt;var&amp;gt;THEME_ID&amp;lt;/var&amp;gt;/&amp;lt;/code&amp;gt;. These files are described in this and the following sections.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;lt;var&amp;gt;THEME_ID&amp;lt;/var&amp;gt;&amp;lt;/code&amp;gt; is a unique identifier for your theme, which is not visible to users. By convention, it is best derived from a domain name you control, e.g. &amp;lt;code&amp;gt;com.example.prettytheme&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== definitions.less ===&lt;br /&gt;
This file can be used to override variables described in the &amp;quot;Variables&amp;quot; section of this article.&lt;br /&gt;
&lt;br /&gt;
=== style.less ===&lt;br /&gt;
This file can be used to define any CSS you like. Before doing this, check, if there really is no variable that can be used to achieve the same thing.&lt;br /&gt;
&lt;br /&gt;
=== Referencing paths ===&lt;br /&gt;
Since 7.2.1, all URLs are relative to the source .less file in which they are contained. This means that unless a theme changes an image it does not need to include that image anymore. &lt;br /&gt;
&lt;br /&gt;
Old themes must be updated if they change an image from the default theme: All styles from the default theme which refer to a changed image must be overwritten in the custom theme. This way the URLs resolve to the new image.&lt;br /&gt;
&lt;br /&gt;
== Variables ==&lt;br /&gt;
&lt;br /&gt;
Naming of the variables should be straight forward. Variables containing the word ''Background'' will always refer to the background color. Variables containing ''Color'' will refer to the foreground color of an element, like color of a font. ''Hover'' in most cases means &amp;quot;hovered&amp;quot; elements. ''Selected'' relates to the currently selected item. Elements that are supposed to catch the users eye can use the ''Highlight'' class and the variable contains this word.&lt;br /&gt;
&lt;br /&gt;
=== Basic ===&lt;br /&gt;
&lt;br /&gt;
==== Font ====&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x) || Default (7.4.x)&lt;br /&gt;
|-&lt;br /&gt;
| @font-family-sans-serif || @sansFontFamily       || &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif || &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif&lt;br /&gt;
|-&lt;br /&gt;
| @font-family-serif      || @serifFontFamily      || Georgia, &amp;quot;Times New Roman&amp;quot;, Times, serif || Georgia, &amp;quot;Times New Roman&amp;quot;, Times, serif&lt;br /&gt;
|-&lt;br /&gt;
| @font-family-monospace  || @monoFontFamily       || Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace || Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace&lt;br /&gt;
|-&lt;br /&gt;
| @font-size-base         || @baseFontSize         || 14px || 14px&lt;br /&gt;
|-&lt;br /&gt;
|                     || @baseFontFamily       ||  || @sansFontFamily&lt;br /&gt;
|-&lt;br /&gt;
| @line-height-base       || @baseLineHeight       || 1.428571429; // 20/14 || 20px&lt;br /&gt;
|-&lt;br /&gt;
|                      || @altFontFamily        ||  || @serifFontFamily&lt;br /&gt;
|-&lt;br /&gt;
| @font-size-touch        || @touchFontSize        || 15px || 15px&lt;br /&gt;
|-&lt;br /&gt;
| @headings-font-family   || @headingsFontFamily   || inherit || inherit&lt;br /&gt;
|-&lt;br /&gt;
| @headings-font-weight   || @headingsFontWeight   || 500 || bold&lt;br /&gt;
|-&lt;br /&gt;
| @font-size-large        || @fontSizeLarge        || ceil((@font-size-base * 1.25)); // ~18px || @baseFontSize * 1.25&lt;br /&gt;
|-&lt;br /&gt;
| @font-size-small        || @fontSizeSmall        || ceil((@font-size-base * 0.85)); // ~12px || @baseFontSize * 0.85&lt;br /&gt;
|-&lt;br /&gt;
|                      || @fontSizeMini         ||  || @baseFontSize * 0.75&lt;br /&gt;
|-&lt;br /&gt;
| @vgrid-font-size        || @vgridFontSize        || 13px || 13px&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Colors ====&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
! Variable || Default || Description &lt;br /&gt;
|-&lt;br /&gt;
| @bodyBackground       || @white&lt;br /&gt;
|-&lt;br /&gt;
| @textColor            || @grayDark&lt;br /&gt;
|-&lt;br /&gt;
| @linkColor            || #08c&lt;br /&gt;
|-&lt;br /&gt;
| @linkColorHover       || darken(@linkColor, 15%)&lt;br /&gt;
|-&lt;br /&gt;
| @linkAccentColor      ||  #ffad00&lt;br /&gt;
|-&lt;br /&gt;
| @linkAccentColorHover || darken(@linkAccentColor, 15%)&lt;br /&gt;
|-&lt;br /&gt;
| @badgeColor           || @white&lt;br /&gt;
|-&lt;br /&gt;
| @badgeBackground      || #555&lt;br /&gt;
|-&lt;br /&gt;
| @headingsColor        || inherit&lt;br /&gt;
|-&lt;br /&gt;
| @black                || #000&lt;br /&gt;
|-&lt;br /&gt;
| @grayDarker           || #222&lt;br /&gt;
|-&lt;br /&gt;
| @grayDark             || #333&lt;br /&gt;
|-&lt;br /&gt;
| @gray                 || #555&lt;br /&gt;
|-&lt;br /&gt;
| @grayLight            || #aaa&lt;br /&gt;
|-&lt;br /&gt;
| @grayLighter          || #eee&lt;br /&gt;
|-&lt;br /&gt;
| @white                || #fff&lt;br /&gt;
|-&lt;br /&gt;
| @blue                 || darken(#049cdb, 5%)&lt;br /&gt;
|-&lt;br /&gt;
| @blueDark             || #0064cd&lt;br /&gt;
|-&lt;br /&gt;
| @blueLight            || lighten(#049cdb, 25%)&lt;br /&gt;
|-&lt;br /&gt;
| @green                || #1A8D1A&lt;br /&gt;
|-&lt;br /&gt;
| @greenLight           || #92D050&lt;br /&gt;
|-&lt;br /&gt;
| @red                  || #cc0000&lt;br /&gt;
|-&lt;br /&gt;
| @yellow               || #F8E400&lt;br /&gt;
|-&lt;br /&gt;
| @orange               || #f89406&lt;br /&gt;
|-&lt;br /&gt;
| @pink                 || #E01CD9&lt;br /&gt;
|-&lt;br /&gt;
| @purple               || #7E16CF&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Space ====&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
! Variable || Default || Description&lt;br /&gt;
|-&lt;br /&gt;
| @paddingLarge          || 11px 19px&lt;br /&gt;
|-&lt;br /&gt;
| @paddingSmall          || 2px 10px&lt;br /&gt;
|-&lt;br /&gt;
| @paddingMini           || 0 6px&lt;br /&gt;
|-&lt;br /&gt;
| @baseBorderRadius      || 4px&lt;br /&gt;
|-&lt;br /&gt;
| @borderRadiusLarge     || 6px&lt;br /&gt;
|-&lt;br /&gt;
| @borderRadiusSmall     || 3px&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Pagination ===&lt;br /&gt;
&lt;br /&gt;
Used where pagination is done, for example in the Calendar weekview, each week is on one &amp;quot;page&amp;quot;; one can switch the week using a pagination widget styled with these variables.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
! Variable || Default || Description&lt;br /&gt;
|-&lt;br /&gt;
| @paginationBackground                || #fff&lt;br /&gt;
|-&lt;br /&gt;
| @paginationBorder                    || #ddd&lt;br /&gt;
|-&lt;br /&gt;
| @paginationActiveBackground          || #f5f5f5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Buttons ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
! Variable || Default || Description&lt;br /&gt;
|-&lt;br /&gt;
| @btnBackground                    || @white&lt;br /&gt;
|-&lt;br /&gt;
| @btnBackgroundHighlight           || darken(@white, 10%)&lt;br /&gt;
|-&lt;br /&gt;
| @btnBorder                        || #bbb&lt;br /&gt;
|-&lt;br /&gt;
| @btnPrimaryBackground             || @linkColor&lt;br /&gt;
|-&lt;br /&gt;
| @btnPrimaryBackgroundHighlight    || spin(@btnPrimaryBackground, 20%)&lt;br /&gt;
|-&lt;br /&gt;
| @btnInfoBackground                || #5bc0de&lt;br /&gt;
|-&lt;br /&gt;
| @btnInfoBackgroundHighlight       || #2f96b4&lt;br /&gt;
|-&lt;br /&gt;
| @btnSuccessBackground             || #62c462&lt;br /&gt;
|-&lt;br /&gt;
| @btnSuccessBackgroundHighlight    || #51a351&lt;br /&gt;
|-&lt;br /&gt;
| @btnWarningBackground             || lighten(@orange, 15%)&lt;br /&gt;
|-&lt;br /&gt;
| @btnWarningBackgroundHighlight    || @orange&lt;br /&gt;
|-&lt;br /&gt;
| @btnDangerBackground              || #ee5f5b&lt;br /&gt;
|-&lt;br /&gt;
| @btnDangerBackgroundHighlight     || #bd362f&lt;br /&gt;
|-&lt;br /&gt;
| @btnInverseBackground             || #444&lt;br /&gt;
|-&lt;br /&gt;
| @btnInverseBackgroundHighlight    || @grayDarker&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Dropdowns ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
! Variable || Default || Description&lt;br /&gt;
|-&lt;br /&gt;
| @dropdownBackground           || @white&lt;br /&gt;
|-&lt;br /&gt;
| @dropdownBorder               || rgba(0,0,0,.2)&lt;br /&gt;
|-&lt;br /&gt;
| @dropdownDividerTop           || #e5e5e5&lt;br /&gt;
|-&lt;br /&gt;
| @dropdownDividerBottom        || @white&lt;br /&gt;
|-&lt;br /&gt;
| @dropdownLinkColor            || @grayDark&lt;br /&gt;
|-&lt;br /&gt;
| @dropdownLinkColorHover       || @white&lt;br /&gt;
|-&lt;br /&gt;
| @dropdownLinkColorActive      || @white&lt;br /&gt;
|-&lt;br /&gt;
| @dropdownLinkBackgroundActive || @linkColor&lt;br /&gt;
|-&lt;br /&gt;
| @dropdownLinkBackgroundHover  || @dropdownLinkBackgroundActive&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Foldertree ===&lt;br /&gt;
{|&lt;br /&gt;
! Variable || Default || Description &lt;br /&gt;
|-&lt;br /&gt;
| @foldertreeSidepanelBackground || #333 ||&lt;br /&gt;
|-&lt;br /&gt;
| @foldertreeSectionTitleColor || #888 || Color for sectiontitles in foldertree (like &amp;quot;Public&amp;quot; folders)&lt;br /&gt;
|-&lt;br /&gt;
| @foldertreeActiveLabelColor || #ccc || ''Active'' means, user can perform an action on this item&lt;br /&gt;
|-&lt;br /&gt;
| @foldertreePassiveLabelColor || #ccc || ''Passive'' means, user can '''not''' perform any action with this item &lt;br /&gt;
|-&lt;br /&gt;
| @foldertreeHoverBackground || #888 ||&lt;br /&gt;
|-&lt;br /&gt;
| @foldertreeSelectedBackground || #222 ||&lt;br /&gt;
|-&lt;br /&gt;
| @foldertreeBadgeBackground || @bagdeBackground || see [[#Colors]] for definition of @badgeBackground&lt;br /&gt;
|-&lt;br /&gt;
| @foldertreeBadgeColor ||@badgeColor || see [[#Colors]] for definition of @badgeBackground&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Calendar ===&lt;br /&gt;
==== Appointment ====&lt;br /&gt;
{|&lt;br /&gt;
! Variable || Default || Description &lt;br /&gt;
|-&lt;br /&gt;
| @appointmentReserved || #08c /* blue */ || Appointment status color&lt;br /&gt;
|-&lt;br /&gt;
| @appointmentTemporary || #ffbb00 /* yellow */ || Appointment status color&lt;br /&gt;
|-&lt;br /&gt;
| @appointmentAbsent || #913f3f /* red */ || Appointment status color&lt;br /&gt;
|-&lt;br /&gt;
| @appointmentFree || #8eb360 /* green */ || Appointment status color&lt;br /&gt;
|-&lt;br /&gt;
| @appointmentPrivate || #555 /* gray */ || Appointment status color&lt;br /&gt;
|-&lt;br /&gt;
| @appointmentDeclinedFont || #888 /* dark gray */|| Font color for declined Appointments&lt;br /&gt;
|-&lt;br /&gt;
| @appointmentUnconfirmedAlpha || 0.4 || Transparency value for unconfirmed Appointments&lt;br /&gt;
|-&lt;br /&gt;
| @appointmentDeclinedAlpha || 0.3 || Transparency value for declined Appointments&lt;br /&gt;
|-&lt;br /&gt;
| @appointmentHoverPct || 15% || Percentage increase of the dark pigment content on hover effect&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Week View ====&lt;br /&gt;
{|&lt;br /&gt;
! Variable || Default || Description &lt;br /&gt;
|-&lt;br /&gt;
| @weekviewAppointmentLasso || #aeaeff || Lasso frame color&lt;br /&gt;
|-&lt;br /&gt;
| @weekviewDayIn || #fff /* white */ || Default background color in week view&lt;br /&gt;
|-&lt;br /&gt;
| @weekviewDayOut || #e0e0e0 /* grey */ || Background color outside of the mean working time&lt;br /&gt;
|-&lt;br /&gt;
| @weekviewTimeline || #f00 /* red */ || Color of the Line indicating the current time&lt;br /&gt;
|-&lt;br /&gt;
| @weekviewTimeWith || 58px || With of the time labels on the left side&lt;br /&gt;
|-&lt;br /&gt;
| @calendarToolbarHeight || 41px || Height of the control toolbar &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Month View ====&lt;br /&gt;
{|&lt;br /&gt;
! Variable || Default || Description &lt;br /&gt;
|-&lt;br /&gt;
| @monthviewAppointmentOut || #aaa /* light gray */ || Color of appointments, which are not in focus&lt;br /&gt;
|-&lt;br /&gt;
| @monthviewToday || #daefff /* light blue */|| Background color of the current day&lt;br /&gt;
|-&lt;br /&gt;
| @calendarToolbarHeight || 41px || Height of the control toolbar&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Area names ==&lt;br /&gt;
&lt;br /&gt;
The variables sometimes refer to common areas. To identify which area is located where, see the following annotated screenshots.&lt;br /&gt;
&lt;br /&gt;
[[File:Colors_mail.png|800px||Mail application]]&lt;br /&gt;
&lt;br /&gt;
[[File:Colors_launchpad.png|800px||Launchpad application]]&lt;br /&gt;
&lt;br /&gt;
== Replacing the logo ==&lt;br /&gt;
&lt;br /&gt;
One of the most common theme changes which requires editing &amp;lt;code&amp;gt;style.css&amp;lt;/code&amp;gt; is changing the logo in the top right corner. The logo is displayed as the background image for an element with the ID &amp;lt;code&amp;gt;io-ox-top-logo-small&amp;lt;/code&amp;gt;. A theme can therefore change the size and URL of the image:&lt;br /&gt;
 #io-ox-top-logo-small {&lt;br /&gt;
     width: 60px;&lt;br /&gt;
     height: 22px;&lt;br /&gt;
     margin: 8px 13px 0 13px;&lt;br /&gt;
     background-image: url(mylogo.png);&lt;br /&gt;
 }&lt;br /&gt;
The file &amp;lt;code&amp;gt;mylogo.png&amp;lt;/code&amp;gt; is expected to be in the same directory as &amp;lt;code&amp;gt;style.css&amp;lt;/code&amp;gt;. If you want to place the image somewhere else, then use a relative path in &amp;lt;code&amp;gt;url()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember that images in OX App Suite are served by the web server and not by the application server. This means that images need to be packaged separately (for dedicated web servers) and installed in &amp;lt;code&amp;gt;/var/www/appsuite/&amp;lt;/code&amp;gt; (or similar, depending on the target platform) instead of &amp;lt;code&amp;gt;/opt/open-xchange/appsuite/&amp;lt;/code&amp;gt;. Direct support for multiple packages is coming in version 7.4.1. Until then, use the build system from the &amp;lt;code&amp;gt;develop&amp;lt;/code&amp;gt; branch to [[AppSuite:UI_build_system#init-packaging|initialize the packaging]] if your theme contains images.&lt;br /&gt;
&lt;br /&gt;
== Mixins ==&lt;br /&gt;
In LESS, it is possible to include a bunch of properties from one ruleset into another ruleset. So say we have the following class:&lt;br /&gt;
&lt;br /&gt;
=== Sample ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-css&amp;quot;&amp;gt;&lt;br /&gt;
.border-radius(@radius: 0px) {&lt;br /&gt;
    -webkit-border-radius: @radius;&lt;br /&gt;
    -moz-border-radius: @radius;&lt;br /&gt;
    -ms-border-radius: @radius;&lt;br /&gt;
    border-radius: @radius;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#menu a {&lt;br /&gt;
  color: #111;&lt;br /&gt;
  .border-radius(5px);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Read [http://www.lesscss.org/#-mixins LESS Mixins]&lt;br /&gt;
&lt;br /&gt;
=== global OX Mixins ===&lt;br /&gt;
&lt;br /&gt;
they can be found at [[#definitions.less | definitions.less]]&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
! Mixin || Sample || Description &lt;br /&gt;
|-&lt;br /&gt;
| .box-sizing(@boxmodel) || .box-sizing(border-box) ||&lt;br /&gt;
|-&lt;br /&gt;
| .user-select(@select) || .user-select(none) ||&lt;br /&gt;
|-&lt;br /&gt;
| .border-radius(@radius) || .border-radius(3px) ||&lt;br /&gt;
|-&lt;br /&gt;
| .box-shadow(@shadow) || .box-shadow(3px) ||&lt;br /&gt;
|-&lt;br /&gt;
| .vertical-gradient(@startColor, @endColor) || .vertical-gradient(#888, #555) ||&lt;br /&gt;
|-&lt;br /&gt;
| .radial-gradient(@color1, @color2, @color3) || .radial-gradient(#111, #222, #333) ||&lt;br /&gt;
|-&lt;br /&gt;
| .transition(@transition) || .transition(background-color 0.2s linear) ||&lt;br /&gt;
|-&lt;br /&gt;
| .animation(@animation) || .animation(slidein 300ms) ||&lt;br /&gt;
|-&lt;br /&gt;
| .animation-name(@name) || .animation-name(slideright) ||&lt;br /&gt;
|-&lt;br /&gt;
| .ellipsis || .ellipsis ||&lt;br /&gt;
|-&lt;br /&gt;
| .overflow(@type) || .overflow(hidden) ||&lt;br /&gt;
|-&lt;br /&gt;
| .overflow-x(@type) || .overflow-x(hidden) ||&lt;br /&gt;
|-&lt;br /&gt;
| .overflow-y(@type) || .overflow-y(hidden) ||&lt;br /&gt;
|-&lt;br /&gt;
| .backface-visibility(@type) || .backface-visibility(hidden) ||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== How to activate a theme during development ==&lt;br /&gt;
&lt;br /&gt;
When creating a new theme, you will want to test changes quickly without building packages reinstalling them. The trick is to use [[Appsuite:Appserver|appserver]].&lt;br /&gt;
&lt;br /&gt;
# First, you need to add the theme to the list of available themes on the backend. Simply create a new file in &amp;lt;code&amp;gt;/opt/open-xchange/etc/settings/&amp;lt;/code&amp;gt; with the extension &amp;lt;code&amp;gt;.properties&amp;lt;/code&amp;gt; and add a line for your theme to it: &amp;lt;pre&amp;amp;lt;noinclude&amp;amp;gt;&amp;amp;lt;/noinclude&amp;amp;gt;&amp;gt;io.ox/core/settingOptions//themes/&amp;lt;var&amp;gt;ID&amp;lt;/var&amp;gt;=&amp;lt;var&amp;gt;Theme Name&amp;lt;/var&amp;gt;&amp;lt;/pre&amp;amp;lt;noinclude&amp;amp;gt;&amp;amp;lt;/noinclude&amp;amp;gt;&amp;gt; Replace &amp;lt;var&amp;gt;ID&amp;lt;/var&amp;gt; by the identifier (directory name) of your theme, and &amp;lt;var&amp;gt;Theme Name&amp;lt;/var&amp;gt; by the human-readable name which should appear in the UI.&lt;br /&gt;
# The server needs to be restarted to read the new settings.&lt;br /&gt;
# Now, you can use &amp;lt;code&amp;gt;appserver&amp;lt;/code&amp;gt; ([[AppSuite:appserver#Use_with_Apache|with a local web server]] if your theme includes images) to get your theme in combination with the UI which is already installed on the backend.&lt;br /&gt;
# Finally, activate your theme the list in the &amp;lt;code&amp;gt;Settings -&amp;gt; Basic&amp;lt;/code&amp;gt; view behind the option &amp;lt;code&amp;gt;Theme&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In case you also want to access the same backend without &amp;lt;code&amp;gt;appserver&amp;lt;/code&amp;gt; while your theme is selected, that theme (or at least some empty &amp;lt;code&amp;gt;.less&amp;lt;/code&amp;gt; files) should be also installed on the backend to avoid errors. To just use an empty theme, run the following as root:&lt;br /&gt;
 touch /opt/open-xchange/appsuite/apps/themes/&amp;lt;var&amp;gt;ID&amp;lt;/var&amp;gt;/definitions.less&lt;br /&gt;
 touch /opt/open-xchange/appsuite/apps/themes/&amp;lt;var&amp;gt;ID&amp;lt;/var&amp;gt;/style.less&lt;br /&gt;
 /opt/open-xchange/appsuite/share/update-themes.sh&lt;br /&gt;
The value of &amp;lt;var&amp;gt;ID&amp;lt;/var&amp;gt; here must be the same as in your &amp;lt;code&amp;gt;.properties&amp;lt;/code&amp;gt; file.&lt;br /&gt;
&lt;br /&gt;
== Favicons and mobile homescreen icons ==&lt;br /&gt;
'''Note''': This chapter is not about changing AppSuite icons which are used in the application like the brand on the upper right.&lt;br /&gt;
&lt;br /&gt;
'''This documentation applies for AppSuite v.7.4.2'''&lt;br /&gt;
&lt;br /&gt;
AppSuite ships with a standard set of icons containing a&lt;br /&gt;
# favicon&lt;br /&gt;
# set of touch icons which are mainly used by mobile Safari on iOS&lt;br /&gt;
&lt;br /&gt;
These icons are used as default for all devices and browsers as long as you don't deliver your own icons with your theme. &lt;br /&gt;
&lt;br /&gt;
=== Favicon ===&lt;br /&gt;
All major browsers support the use of a favicon. The favicon is a pixel image with the size of 16x16 (32x32) and the &amp;quot;ico&amp;quot; file ending. (see [http://en.wikipedia.org/wiki/Favicon Wikipedia Favicon] for details).&lt;br /&gt;
&lt;br /&gt;
You should provide your custom favicon within your custom theme. If you do not add a custom favicon to your theme the global OX default will be used. The default icon is located under &amp;lt;tt&amp;gt;apps/themes/icons/default&amp;lt;/tt&amp;gt; on the web server.&lt;br /&gt;
&lt;br /&gt;
'''Attention:''' Safari and Internet Explorer do not support dynamic changes to the favicon for a webpage. This means, the default icon will be shown even if a custom favicon is provided within a custom theme. To enable the right favicon for a theme on Safari and IE, the overall standard &amp;lt;tt&amp;gt;favicon.ico&amp;lt;/tt&amp;gt; located under &amp;lt;tt&amp;gt;apps/themes/icons/default&amp;lt;/tt&amp;gt; on the web server must be replaced with a custom version.&lt;br /&gt;
&lt;br /&gt;
=== Apple touch icons ===&lt;br /&gt;
iOS devices (iPhone/iPad/iPod) support so called &amp;quot;Webclips&amp;quot;. Webclips are bookmarks to websites or webapps which provide a App Icon that is shown on the iOS homescreen. AppSuite offers full support for Webclips by providing all needed App icons, splashscreens and full screen support. If a user uses the &amp;quot;Add to homescreen&amp;quot; button on his iOS device, a Webclip is created, taking the right icon for his current device. Most devices need custom resolutions of the Webclip icon in the '''png''' format.&lt;br /&gt;
&lt;br /&gt;
* iPhone 3: 57 x 57 px&lt;br /&gt;
* iPhone 4 retina: 114x114 px&lt;br /&gt;
* iPhone iPhone 5 retina: 120 x 120 px&lt;br /&gt;
* iPad: 72 x 72 px&lt;br /&gt;
* iPad (iOS 7): 76 x 76px&lt;br /&gt;
* iPad retina: 144 x 144 px &lt;br /&gt;
* iPad retina (iOS 7): 152 x 152 px &lt;br /&gt;
&lt;br /&gt;
Furthermore a fullscreen Webclip App will show a splashscreen, a jpg file that is displayed on startup during app load. There are currently three different resolutions as jpg files available. '''Note''': Splashscreens must be JPG files&lt;br /&gt;
&lt;br /&gt;
* iPhone: 320 x 460 px&lt;br /&gt;
* iPhone 4: 640 x 920 px&lt;br /&gt;
* iPhone 5: 640 x 1096 px&lt;br /&gt;
&lt;br /&gt;
'''Note''': We do not provide splashscreens for iPad&lt;br /&gt;
   &lt;br /&gt;
This list may change with Apple's iOS updates. We recommend providing all of this resolutions when customizing the Webclip icons and splashscreens, even if some iOS devices use the next best resolution for an icon if a certain file is missing.&lt;br /&gt;
&lt;br /&gt;
=== Providing custom icons ===&lt;br /&gt;
To provide custom Webclip icons locate the following path in the AppSuite installation on your web server:&lt;br /&gt;
 pathToAppSuite/apps/themes/icons/default&lt;br /&gt;
&lt;br /&gt;
This folder contains all OX default icons for Webclips icons and splashscreens. Use these as samples for your own versions.&lt;br /&gt;
&lt;br /&gt;
A clean installation will have all our default icons in the &amp;quot;default&amp;quot; directory. To customize the icons we recommend using our default icons as samples and save your customized version in your theme. '''Note''': The filename has to be the same as in the default folder. Otherwise the fallback will be applied and the default icons will be used. If more advanced rewriting is needed one should edit the contents of the &amp;lt;tt&amp;gt;.htaccess&amp;lt;/tt&amp;gt; file located under &amp;lt;tt&amp;gt;apps/themes&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Theming the login page ==&lt;br /&gt;
&lt;br /&gt;
The login page is a little bit special. If you don’t use the [[Open-Xchange_servlet_for_external_login_masks|form login]] and provide your own login page, you might want to theme the login page, too. [[AppSuite:Theming the login page|Learn how to do this here]].&lt;br /&gt;
&lt;br /&gt;
=== Providing domain based login themes ===&lt;br /&gt;
&lt;br /&gt;
If you have a multibrand installation and you want to deliver not only custom themes but also custom login-themes this can be done via Apache mod_rewrite. You can do so by a domain-based rewrite rule to deliver custom themes to a user based on the URI he's using. The needed config file &amp;lt;tt&amp;gt;.htaccess&amp;lt;/tt&amp;gt; is located under &amp;lt;tt&amp;gt;apps/themes&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Sample config for domain based login theme&lt;br /&gt;
RewriteCond %{HTTP_HOST} ^www\.domain\.com$&lt;br /&gt;
RewriteCond %{REQUEST_FILENAME} -f&lt;br /&gt;
RewriteRule ^login/(.*)$ domain_com_logintheme/$1 [L]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Best practice ==&lt;br /&gt;
&lt;br /&gt;
To be really safe, it’s best to only define your own values for the variables shown above. If this really breaks anything, we consider this a bug, please report it [https://bugs.open-xchange.com/] in our bugzilla.&lt;br /&gt;
&lt;br /&gt;
Of course, using CSS in &amp;lt;code&amp;gt;style.less&amp;lt;/code&amp;gt; file to define your own styles is also possible. Make sure to test your style in such cases more carefully. It is most likely safe to change minor things using this file, but if you plan to change any positions of larger elements, this might break the complete design. So please be careful when overwriting the default CSS rules.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
[http://bradfrost.github.io/this-is-responsive/resources.html Responsive design]&lt;br /&gt;
&lt;br /&gt;
[http://lesscss.org/ LESS]&lt;br /&gt;
&lt;br /&gt;
== Caveats ==&lt;br /&gt;
&lt;br /&gt;
It is '''not''' recommended to change the size of elements or their position. If you really want to do so, please check on all devices and in all browsers and make sure you didn’t break anything. You even have to be careful when changing the font, because this might have effects on positioning, too.&lt;br /&gt;
&lt;br /&gt;
As mentioned before, changing colors should be safe.&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Theming&amp;diff=17381</id>
		<title>AppSuite:Theming</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Theming&amp;diff=17381"/>
		<updated>2014-04-11T08:08:46Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Font */&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;Theming&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Abstract.''' In this article, you can learn how to create customized themes and use them to change the look of you appsuite installation.&lt;br /&gt;
__TOC__&lt;br /&gt;
== LESS.JS ==&lt;br /&gt;
Appsuite used LESS as dynamic stylesheet language. LESS extends CSS with dynamic behavior such as variables, mixins, operations and functions.&lt;br /&gt;
&lt;br /&gt;
Please read [http://lesscss.org/#docs LESS.JS] documentation first.&lt;br /&gt;
&lt;br /&gt;
=== Using less.js ===&lt;br /&gt;
If your theme depends on less.js, you will need one more step to make it work. Why? To accelerate the login, compilation of LessCSS files was moved from the login process in the browser to the installation process on the backend. &lt;br /&gt;
&lt;br /&gt;
Backend packages for themes and any apps which ship .less files require the following changes:&lt;br /&gt;
&lt;br /&gt;
1. Add &amp;quot;skipLess=1&amp;quot; to the build command in *.spec and in debian/rules:&lt;br /&gt;
   sh /opt/open-xchange-appsuite-dev/bin/build-appsuite app skipLess=1&lt;br /&gt;
2. Add %post and %postun sections to *.spec:&lt;br /&gt;
   %post&lt;br /&gt;
   if [ &amp;quot;$1&amp;quot; = 1 ]; then&lt;br /&gt;
   UPDATE=/opt/open-xchange/appsuite/share/update-themes.sh&lt;br /&gt;
   [ -x $UPDATE ] &amp;amp;&amp;amp; $UPDATE&lt;br /&gt;
   fi&lt;br /&gt;
   %postun&lt;br /&gt;
   UPDATE=/opt/open-xchange/appsuite/share/update-themes.sh&lt;br /&gt;
   [ -x $UPDATE ] &amp;amp;&amp;amp; $UPDATE&lt;br /&gt;
&lt;br /&gt;
For multiple binary packages, the %post and %postun sections should apply only to backend packages &lt;br /&gt;
which contain .less files.&lt;br /&gt;
&lt;br /&gt;
3. Add debian/postinst and debian/postrm containing the same content:&lt;br /&gt;
   #!/bin/sh&lt;br /&gt;
   UPDATE=/opt/open-xchange/appsuite/share/update-themes.sh&lt;br /&gt;
   [ -x $UPDATE ] &amp;amp;&amp;amp; $UPDATE&lt;br /&gt;
&lt;br /&gt;
For multiple binary packages, the postinst and postrm files should apply only to backend packages which contain .less files.&lt;br /&gt;
&lt;br /&gt;
Note: Since 7.2.1, LessCSS files must have the file extension .less to be usable with the 'less' RequireJS plugin (module dependencies of the form 'less!filename.less'). Previously we were more lenient and dealt with .css, too.&lt;br /&gt;
&lt;br /&gt;
== File structure ==&lt;br /&gt;
A theme basically consists of two files located in &amp;lt;code&amp;gt;apps/themes/&amp;lt;var&amp;gt;THEME_ID&amp;lt;/var&amp;gt;/&amp;lt;/code&amp;gt;. These files are described in this and the following sections.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;lt;var&amp;gt;THEME_ID&amp;lt;/var&amp;gt;&amp;lt;/code&amp;gt; is a unique identifier for your theme, which is not visible to users. By convention, it is best derived from a domain name you control, e.g. &amp;lt;code&amp;gt;com.example.prettytheme&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== definitions.less ===&lt;br /&gt;
This file can be used to override variables described in the &amp;quot;Variables&amp;quot; section of this article.&lt;br /&gt;
&lt;br /&gt;
=== style.less ===&lt;br /&gt;
This file can be used to define any CSS you like. Before doing this, check, if there really is no variable that can be used to achieve the same thing.&lt;br /&gt;
&lt;br /&gt;
=== Referencing paths ===&lt;br /&gt;
Since 7.2.1, all URLs are relative to the source .less file in which they are contained. This means that unless a theme changes an image it does not need to include that image anymore. &lt;br /&gt;
&lt;br /&gt;
Old themes must be updated if they change an image from the default theme: All styles from the default theme which refer to a changed image must be overwritten in the custom theme. This way the URLs resolve to the new image.&lt;br /&gt;
&lt;br /&gt;
== Variables ==&lt;br /&gt;
&lt;br /&gt;
Naming of the variables should be straight forward. Variables containing the word ''Background'' will always refer to the background color. Variables containing ''Color'' will refer to the foreground color of an element, like color of a font. ''Hover'' in most cases means &amp;quot;hovered&amp;quot; elements. ''Selected'' relates to the currently selected item. Elements that are supposed to catch the users eye can use the ''Highlight'' class and the variable contains this word.&lt;br /&gt;
&lt;br /&gt;
=== Basic ===&lt;br /&gt;
&lt;br /&gt;
==== Font ====&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x) || Default (7.4.x)&lt;br /&gt;
|-&lt;br /&gt;
| @font-family-sans-serif || @sansFontFamily       || &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif || &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif&lt;br /&gt;
|-&lt;br /&gt;
| @font-family-serif      || @serifFontFamily      || Georgia, &amp;quot;Times New Roman&amp;quot;, Times, serif || Georgia, &amp;quot;Times New Roman&amp;quot;, Times, serif&lt;br /&gt;
|-&lt;br /&gt;
| @font-family-monospace  || @monoFontFamily       || Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace || Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace&lt;br /&gt;
|-&lt;br /&gt;
| @font-size-base         || @baseFontSize         || 14px || 14px&lt;br /&gt;
|-&lt;br /&gt;
| N/A                     || @baseFontFamily       || N/A || @sansFontFamily&lt;br /&gt;
|-&lt;br /&gt;
| @line-height-base       || @baseLineHeight       || 1.428571429; // 20/14 || 20px&lt;br /&gt;
|-&lt;br /&gt;
| N/A                     || @altFontFamily        || N/A || @serifFontFamily&lt;br /&gt;
|-&lt;br /&gt;
| @font-size-touch        || @touchFontSize        || 15px || 15px&lt;br /&gt;
|-&lt;br /&gt;
| @headings-font-family   || @headingsFontFamily   || inherit || inherit&lt;br /&gt;
|-&lt;br /&gt;
| @headings-font-weight   || @headingsFontWeight   || 500 || bold&lt;br /&gt;
|-&lt;br /&gt;
| @font-size-large        || @fontSizeLarge        || ceil((@font-size-base * 1.25)); // ~18px || @baseFontSize * 1.25&lt;br /&gt;
|-&lt;br /&gt;
| @font-size-small        || @fontSizeSmall        || ceil((@font-size-base * 0.85)); // ~12px || @baseFontSize * 0.85&lt;br /&gt;
|-&lt;br /&gt;
| N/A                     || @fontSizeMini         || N/A || @baseFontSize * 0.75&lt;br /&gt;
|-&lt;br /&gt;
| @vgrid-font-size        || @vgridFontSize        || 13px || 13px&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Colors ====&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
! Variable || Default || Description &lt;br /&gt;
|-&lt;br /&gt;
| @bodyBackground       || @white&lt;br /&gt;
|-&lt;br /&gt;
| @textColor            || @grayDark&lt;br /&gt;
|-&lt;br /&gt;
| @linkColor            || #08c&lt;br /&gt;
|-&lt;br /&gt;
| @linkColorHover       || darken(@linkColor, 15%)&lt;br /&gt;
|-&lt;br /&gt;
| @linkAccentColor      ||  #ffad00&lt;br /&gt;
|-&lt;br /&gt;
| @linkAccentColorHover || darken(@linkAccentColor, 15%)&lt;br /&gt;
|-&lt;br /&gt;
| @badgeColor           || @white&lt;br /&gt;
|-&lt;br /&gt;
| @badgeBackground      || #555&lt;br /&gt;
|-&lt;br /&gt;
| @headingsColor        || inherit&lt;br /&gt;
|-&lt;br /&gt;
| @black                || #000&lt;br /&gt;
|-&lt;br /&gt;
| @grayDarker           || #222&lt;br /&gt;
|-&lt;br /&gt;
| @grayDark             || #333&lt;br /&gt;
|-&lt;br /&gt;
| @gray                 || #555&lt;br /&gt;
|-&lt;br /&gt;
| @grayLight            || #aaa&lt;br /&gt;
|-&lt;br /&gt;
| @grayLighter          || #eee&lt;br /&gt;
|-&lt;br /&gt;
| @white                || #fff&lt;br /&gt;
|-&lt;br /&gt;
| @blue                 || darken(#049cdb, 5%)&lt;br /&gt;
|-&lt;br /&gt;
| @blueDark             || #0064cd&lt;br /&gt;
|-&lt;br /&gt;
| @blueLight            || lighten(#049cdb, 25%)&lt;br /&gt;
|-&lt;br /&gt;
| @green                || #1A8D1A&lt;br /&gt;
|-&lt;br /&gt;
| @greenLight           || #92D050&lt;br /&gt;
|-&lt;br /&gt;
| @red                  || #cc0000&lt;br /&gt;
|-&lt;br /&gt;
| @yellow               || #F8E400&lt;br /&gt;
|-&lt;br /&gt;
| @orange               || #f89406&lt;br /&gt;
|-&lt;br /&gt;
| @pink                 || #E01CD9&lt;br /&gt;
|-&lt;br /&gt;
| @purple               || #7E16CF&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Space ====&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
! Variable || Default || Description&lt;br /&gt;
|-&lt;br /&gt;
| @paddingLarge          || 11px 19px&lt;br /&gt;
|-&lt;br /&gt;
| @paddingSmall          || 2px 10px&lt;br /&gt;
|-&lt;br /&gt;
| @paddingMini           || 0 6px&lt;br /&gt;
|-&lt;br /&gt;
| @baseBorderRadius      || 4px&lt;br /&gt;
|-&lt;br /&gt;
| @borderRadiusLarge     || 6px&lt;br /&gt;
|-&lt;br /&gt;
| @borderRadiusSmall     || 3px&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Pagination ===&lt;br /&gt;
&lt;br /&gt;
Used where pagination is done, for example in the Calendar weekview, each week is on one &amp;quot;page&amp;quot;; one can switch the week using a pagination widget styled with these variables.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
! Variable || Default || Description&lt;br /&gt;
|-&lt;br /&gt;
| @paginationBackground                || #fff&lt;br /&gt;
|-&lt;br /&gt;
| @paginationBorder                    || #ddd&lt;br /&gt;
|-&lt;br /&gt;
| @paginationActiveBackground          || #f5f5f5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Buttons ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
! Variable || Default || Description&lt;br /&gt;
|-&lt;br /&gt;
| @btnBackground                    || @white&lt;br /&gt;
|-&lt;br /&gt;
| @btnBackgroundHighlight           || darken(@white, 10%)&lt;br /&gt;
|-&lt;br /&gt;
| @btnBorder                        || #bbb&lt;br /&gt;
|-&lt;br /&gt;
| @btnPrimaryBackground             || @linkColor&lt;br /&gt;
|-&lt;br /&gt;
| @btnPrimaryBackgroundHighlight    || spin(@btnPrimaryBackground, 20%)&lt;br /&gt;
|-&lt;br /&gt;
| @btnInfoBackground                || #5bc0de&lt;br /&gt;
|-&lt;br /&gt;
| @btnInfoBackgroundHighlight       || #2f96b4&lt;br /&gt;
|-&lt;br /&gt;
| @btnSuccessBackground             || #62c462&lt;br /&gt;
|-&lt;br /&gt;
| @btnSuccessBackgroundHighlight    || #51a351&lt;br /&gt;
|-&lt;br /&gt;
| @btnWarningBackground             || lighten(@orange, 15%)&lt;br /&gt;
|-&lt;br /&gt;
| @btnWarningBackgroundHighlight    || @orange&lt;br /&gt;
|-&lt;br /&gt;
| @btnDangerBackground              || #ee5f5b&lt;br /&gt;
|-&lt;br /&gt;
| @btnDangerBackgroundHighlight     || #bd362f&lt;br /&gt;
|-&lt;br /&gt;
| @btnInverseBackground             || #444&lt;br /&gt;
|-&lt;br /&gt;
| @btnInverseBackgroundHighlight    || @grayDarker&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Dropdowns ===&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
! Variable || Default || Description&lt;br /&gt;
|-&lt;br /&gt;
| @dropdownBackground           || @white&lt;br /&gt;
|-&lt;br /&gt;
| @dropdownBorder               || rgba(0,0,0,.2)&lt;br /&gt;
|-&lt;br /&gt;
| @dropdownDividerTop           || #e5e5e5&lt;br /&gt;
|-&lt;br /&gt;
| @dropdownDividerBottom        || @white&lt;br /&gt;
|-&lt;br /&gt;
| @dropdownLinkColor            || @grayDark&lt;br /&gt;
|-&lt;br /&gt;
| @dropdownLinkColorHover       || @white&lt;br /&gt;
|-&lt;br /&gt;
| @dropdownLinkColorActive      || @white&lt;br /&gt;
|-&lt;br /&gt;
| @dropdownLinkBackgroundActive || @linkColor&lt;br /&gt;
|-&lt;br /&gt;
| @dropdownLinkBackgroundHover  || @dropdownLinkBackgroundActive&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Foldertree ===&lt;br /&gt;
{|&lt;br /&gt;
! Variable || Default || Description &lt;br /&gt;
|-&lt;br /&gt;
| @foldertreeSidepanelBackground || #333 ||&lt;br /&gt;
|-&lt;br /&gt;
| @foldertreeSectionTitleColor || #888 || Color for sectiontitles in foldertree (like &amp;quot;Public&amp;quot; folders)&lt;br /&gt;
|-&lt;br /&gt;
| @foldertreeActiveLabelColor || #ccc || ''Active'' means, user can perform an action on this item&lt;br /&gt;
|-&lt;br /&gt;
| @foldertreePassiveLabelColor || #ccc || ''Passive'' means, user can '''not''' perform any action with this item &lt;br /&gt;
|-&lt;br /&gt;
| @foldertreeHoverBackground || #888 ||&lt;br /&gt;
|-&lt;br /&gt;
| @foldertreeSelectedBackground || #222 ||&lt;br /&gt;
|-&lt;br /&gt;
| @foldertreeBadgeBackground || @bagdeBackground || see [[#Colors]] for definition of @badgeBackground&lt;br /&gt;
|-&lt;br /&gt;
| @foldertreeBadgeColor ||@badgeColor || see [[#Colors]] for definition of @badgeBackground&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Calendar ===&lt;br /&gt;
==== Appointment ====&lt;br /&gt;
{|&lt;br /&gt;
! Variable || Default || Description &lt;br /&gt;
|-&lt;br /&gt;
| @appointmentReserved || #08c /* blue */ || Appointment status color&lt;br /&gt;
|-&lt;br /&gt;
| @appointmentTemporary || #ffbb00 /* yellow */ || Appointment status color&lt;br /&gt;
|-&lt;br /&gt;
| @appointmentAbsent || #913f3f /* red */ || Appointment status color&lt;br /&gt;
|-&lt;br /&gt;
| @appointmentFree || #8eb360 /* green */ || Appointment status color&lt;br /&gt;
|-&lt;br /&gt;
| @appointmentPrivate || #555 /* gray */ || Appointment status color&lt;br /&gt;
|-&lt;br /&gt;
| @appointmentDeclinedFont || #888 /* dark gray */|| Font color for declined Appointments&lt;br /&gt;
|-&lt;br /&gt;
| @appointmentUnconfirmedAlpha || 0.4 || Transparency value for unconfirmed Appointments&lt;br /&gt;
|-&lt;br /&gt;
| @appointmentDeclinedAlpha || 0.3 || Transparency value for declined Appointments&lt;br /&gt;
|-&lt;br /&gt;
| @appointmentHoverPct || 15% || Percentage increase of the dark pigment content on hover effect&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Week View ====&lt;br /&gt;
{|&lt;br /&gt;
! Variable || Default || Description &lt;br /&gt;
|-&lt;br /&gt;
| @weekviewAppointmentLasso || #aeaeff || Lasso frame color&lt;br /&gt;
|-&lt;br /&gt;
| @weekviewDayIn || #fff /* white */ || Default background color in week view&lt;br /&gt;
|-&lt;br /&gt;
| @weekviewDayOut || #e0e0e0 /* grey */ || Background color outside of the mean working time&lt;br /&gt;
|-&lt;br /&gt;
| @weekviewTimeline || #f00 /* red */ || Color of the Line indicating the current time&lt;br /&gt;
|-&lt;br /&gt;
| @weekviewTimeWith || 58px || With of the time labels on the left side&lt;br /&gt;
|-&lt;br /&gt;
| @calendarToolbarHeight || 41px || Height of the control toolbar &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Month View ====&lt;br /&gt;
{|&lt;br /&gt;
! Variable || Default || Description &lt;br /&gt;
|-&lt;br /&gt;
| @monthviewAppointmentOut || #aaa /* light gray */ || Color of appointments, which are not in focus&lt;br /&gt;
|-&lt;br /&gt;
| @monthviewToday || #daefff /* light blue */|| Background color of the current day&lt;br /&gt;
|-&lt;br /&gt;
| @calendarToolbarHeight || 41px || Height of the control toolbar&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Area names ==&lt;br /&gt;
&lt;br /&gt;
The variables sometimes refer to common areas. To identify which area is located where, see the following annotated screenshots.&lt;br /&gt;
&lt;br /&gt;
[[File:Colors_mail.png|800px||Mail application]]&lt;br /&gt;
&lt;br /&gt;
[[File:Colors_launchpad.png|800px||Launchpad application]]&lt;br /&gt;
&lt;br /&gt;
== Replacing the logo ==&lt;br /&gt;
&lt;br /&gt;
One of the most common theme changes which requires editing &amp;lt;code&amp;gt;style.css&amp;lt;/code&amp;gt; is changing the logo in the top right corner. The logo is displayed as the background image for an element with the ID &amp;lt;code&amp;gt;io-ox-top-logo-small&amp;lt;/code&amp;gt;. A theme can therefore change the size and URL of the image:&lt;br /&gt;
 #io-ox-top-logo-small {&lt;br /&gt;
     width: 60px;&lt;br /&gt;
     height: 22px;&lt;br /&gt;
     margin: 8px 13px 0 13px;&lt;br /&gt;
     background-image: url(mylogo.png);&lt;br /&gt;
 }&lt;br /&gt;
The file &amp;lt;code&amp;gt;mylogo.png&amp;lt;/code&amp;gt; is expected to be in the same directory as &amp;lt;code&amp;gt;style.css&amp;lt;/code&amp;gt;. If you want to place the image somewhere else, then use a relative path in &amp;lt;code&amp;gt;url()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember that images in OX App Suite are served by the web server and not by the application server. This means that images need to be packaged separately (for dedicated web servers) and installed in &amp;lt;code&amp;gt;/var/www/appsuite/&amp;lt;/code&amp;gt; (or similar, depending on the target platform) instead of &amp;lt;code&amp;gt;/opt/open-xchange/appsuite/&amp;lt;/code&amp;gt;. Direct support for multiple packages is coming in version 7.4.1. Until then, use the build system from the &amp;lt;code&amp;gt;develop&amp;lt;/code&amp;gt; branch to [[AppSuite:UI_build_system#init-packaging|initialize the packaging]] if your theme contains images.&lt;br /&gt;
&lt;br /&gt;
== Mixins ==&lt;br /&gt;
In LESS, it is possible to include a bunch of properties from one ruleset into another ruleset. So say we have the following class:&lt;br /&gt;
&lt;br /&gt;
=== Sample ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;language-css&amp;quot;&amp;gt;&lt;br /&gt;
.border-radius(@radius: 0px) {&lt;br /&gt;
    -webkit-border-radius: @radius;&lt;br /&gt;
    -moz-border-radius: @radius;&lt;br /&gt;
    -ms-border-radius: @radius;&lt;br /&gt;
    border-radius: @radius;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#menu a {&lt;br /&gt;
  color: #111;&lt;br /&gt;
  .border-radius(5px);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Read [http://www.lesscss.org/#-mixins LESS Mixins]&lt;br /&gt;
&lt;br /&gt;
=== global OX Mixins ===&lt;br /&gt;
&lt;br /&gt;
they can be found at [[#definitions.less | definitions.less]]&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
! Mixin || Sample || Description &lt;br /&gt;
|-&lt;br /&gt;
| .box-sizing(@boxmodel) || .box-sizing(border-box) ||&lt;br /&gt;
|-&lt;br /&gt;
| .user-select(@select) || .user-select(none) ||&lt;br /&gt;
|-&lt;br /&gt;
| .border-radius(@radius) || .border-radius(3px) ||&lt;br /&gt;
|-&lt;br /&gt;
| .box-shadow(@shadow) || .box-shadow(3px) ||&lt;br /&gt;
|-&lt;br /&gt;
| .vertical-gradient(@startColor, @endColor) || .vertical-gradient(#888, #555) ||&lt;br /&gt;
|-&lt;br /&gt;
| .radial-gradient(@color1, @color2, @color3) || .radial-gradient(#111, #222, #333) ||&lt;br /&gt;
|-&lt;br /&gt;
| .transition(@transition) || .transition(background-color 0.2s linear) ||&lt;br /&gt;
|-&lt;br /&gt;
| .animation(@animation) || .animation(slidein 300ms) ||&lt;br /&gt;
|-&lt;br /&gt;
| .animation-name(@name) || .animation-name(slideright) ||&lt;br /&gt;
|-&lt;br /&gt;
| .ellipsis || .ellipsis ||&lt;br /&gt;
|-&lt;br /&gt;
| .overflow(@type) || .overflow(hidden) ||&lt;br /&gt;
|-&lt;br /&gt;
| .overflow-x(@type) || .overflow-x(hidden) ||&lt;br /&gt;
|-&lt;br /&gt;
| .overflow-y(@type) || .overflow-y(hidden) ||&lt;br /&gt;
|-&lt;br /&gt;
| .backface-visibility(@type) || .backface-visibility(hidden) ||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== How to activate a theme during development ==&lt;br /&gt;
&lt;br /&gt;
When creating a new theme, you will want to test changes quickly without building packages reinstalling them. The trick is to use [[Appsuite:Appserver|appserver]].&lt;br /&gt;
&lt;br /&gt;
# First, you need to add the theme to the list of available themes on the backend. Simply create a new file in &amp;lt;code&amp;gt;/opt/open-xchange/etc/settings/&amp;lt;/code&amp;gt; with the extension &amp;lt;code&amp;gt;.properties&amp;lt;/code&amp;gt; and add a line for your theme to it: &amp;lt;pre&amp;amp;lt;noinclude&amp;amp;gt;&amp;amp;lt;/noinclude&amp;amp;gt;&amp;gt;io.ox/core/settingOptions//themes/&amp;lt;var&amp;gt;ID&amp;lt;/var&amp;gt;=&amp;lt;var&amp;gt;Theme Name&amp;lt;/var&amp;gt;&amp;lt;/pre&amp;amp;lt;noinclude&amp;amp;gt;&amp;amp;lt;/noinclude&amp;amp;gt;&amp;gt; Replace &amp;lt;var&amp;gt;ID&amp;lt;/var&amp;gt; by the identifier (directory name) of your theme, and &amp;lt;var&amp;gt;Theme Name&amp;lt;/var&amp;gt; by the human-readable name which should appear in the UI.&lt;br /&gt;
# The server needs to be restarted to read the new settings.&lt;br /&gt;
# Now, you can use &amp;lt;code&amp;gt;appserver&amp;lt;/code&amp;gt; ([[AppSuite:appserver#Use_with_Apache|with a local web server]] if your theme includes images) to get your theme in combination with the UI which is already installed on the backend.&lt;br /&gt;
# Finally, activate your theme the list in the &amp;lt;code&amp;gt;Settings -&amp;gt; Basic&amp;lt;/code&amp;gt; view behind the option &amp;lt;code&amp;gt;Theme&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In case you also want to access the same backend without &amp;lt;code&amp;gt;appserver&amp;lt;/code&amp;gt; while your theme is selected, that theme (or at least some empty &amp;lt;code&amp;gt;.less&amp;lt;/code&amp;gt; files) should be also installed on the backend to avoid errors. To just use an empty theme, run the following as root:&lt;br /&gt;
 touch /opt/open-xchange/appsuite/apps/themes/&amp;lt;var&amp;gt;ID&amp;lt;/var&amp;gt;/definitions.less&lt;br /&gt;
 touch /opt/open-xchange/appsuite/apps/themes/&amp;lt;var&amp;gt;ID&amp;lt;/var&amp;gt;/style.less&lt;br /&gt;
 /opt/open-xchange/appsuite/share/update-themes.sh&lt;br /&gt;
The value of &amp;lt;var&amp;gt;ID&amp;lt;/var&amp;gt; here must be the same as in your &amp;lt;code&amp;gt;.properties&amp;lt;/code&amp;gt; file.&lt;br /&gt;
&lt;br /&gt;
== Favicons and mobile homescreen icons ==&lt;br /&gt;
'''Note''': This chapter is not about changing AppSuite icons which are used in the application like the brand on the upper right.&lt;br /&gt;
&lt;br /&gt;
'''This documentation applies for AppSuite v.7.4.2'''&lt;br /&gt;
&lt;br /&gt;
AppSuite ships with a standard set of icons containing a&lt;br /&gt;
# favicon&lt;br /&gt;
# set of touch icons which are mainly used by mobile Safari on iOS&lt;br /&gt;
&lt;br /&gt;
These icons are used as default for all devices and browsers as long as you don't deliver your own icons with your theme. &lt;br /&gt;
&lt;br /&gt;
=== Favicon ===&lt;br /&gt;
All major browsers support the use of a favicon. The favicon is a pixel image with the size of 16x16 (32x32) and the &amp;quot;ico&amp;quot; file ending. (see [http://en.wikipedia.org/wiki/Favicon Wikipedia Favicon] for details).&lt;br /&gt;
&lt;br /&gt;
You should provide your custom favicon within your custom theme. If you do not add a custom favicon to your theme the global OX default will be used. The default icon is located under &amp;lt;tt&amp;gt;apps/themes/icons/default&amp;lt;/tt&amp;gt; on the web server.&lt;br /&gt;
&lt;br /&gt;
'''Attention:''' Safari and Internet Explorer do not support dynamic changes to the favicon for a webpage. This means, the default icon will be shown even if a custom favicon is provided within a custom theme. To enable the right favicon for a theme on Safari and IE, the overall standard &amp;lt;tt&amp;gt;favicon.ico&amp;lt;/tt&amp;gt; located under &amp;lt;tt&amp;gt;apps/themes/icons/default&amp;lt;/tt&amp;gt; on the web server must be replaced with a custom version.&lt;br /&gt;
&lt;br /&gt;
=== Apple touch icons ===&lt;br /&gt;
iOS devices (iPhone/iPad/iPod) support so called &amp;quot;Webclips&amp;quot;. Webclips are bookmarks to websites or webapps which provide a App Icon that is shown on the iOS homescreen. AppSuite offers full support for Webclips by providing all needed App icons, splashscreens and full screen support. If a user uses the &amp;quot;Add to homescreen&amp;quot; button on his iOS device, a Webclip is created, taking the right icon for his current device. Most devices need custom resolutions of the Webclip icon in the '''png''' format.&lt;br /&gt;
&lt;br /&gt;
* iPhone 3: 57 x 57 px&lt;br /&gt;
* iPhone 4 retina: 114x114 px&lt;br /&gt;
* iPhone iPhone 5 retina: 120 x 120 px&lt;br /&gt;
* iPad: 72 x 72 px&lt;br /&gt;
* iPad (iOS 7): 76 x 76px&lt;br /&gt;
* iPad retina: 144 x 144 px &lt;br /&gt;
* iPad retina (iOS 7): 152 x 152 px &lt;br /&gt;
&lt;br /&gt;
Furthermore a fullscreen Webclip App will show a splashscreen, a jpg file that is displayed on startup during app load. There are currently three different resolutions as jpg files available. '''Note''': Splashscreens must be JPG files&lt;br /&gt;
&lt;br /&gt;
* iPhone: 320 x 460 px&lt;br /&gt;
* iPhone 4: 640 x 920 px&lt;br /&gt;
* iPhone 5: 640 x 1096 px&lt;br /&gt;
&lt;br /&gt;
'''Note''': We do not provide splashscreens for iPad&lt;br /&gt;
   &lt;br /&gt;
This list may change with Apple's iOS updates. We recommend providing all of this resolutions when customizing the Webclip icons and splashscreens, even if some iOS devices use the next best resolution for an icon if a certain file is missing.&lt;br /&gt;
&lt;br /&gt;
=== Providing custom icons ===&lt;br /&gt;
To provide custom Webclip icons locate the following path in the AppSuite installation on your web server:&lt;br /&gt;
 pathToAppSuite/apps/themes/icons/default&lt;br /&gt;
&lt;br /&gt;
This folder contains all OX default icons for Webclips icons and splashscreens. Use these as samples for your own versions.&lt;br /&gt;
&lt;br /&gt;
A clean installation will have all our default icons in the &amp;quot;default&amp;quot; directory. To customize the icons we recommend using our default icons as samples and save your customized version in your theme. '''Note''': The filename has to be the same as in the default folder. Otherwise the fallback will be applied and the default icons will be used. If more advanced rewriting is needed one should edit the contents of the &amp;lt;tt&amp;gt;.htaccess&amp;lt;/tt&amp;gt; file located under &amp;lt;tt&amp;gt;apps/themes&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Theming the login page ==&lt;br /&gt;
&lt;br /&gt;
The login page is a little bit special. If you don’t use the [[Open-Xchange_servlet_for_external_login_masks|form login]] and provide your own login page, you might want to theme the login page, too. [[AppSuite:Theming the login page|Learn how to do this here]].&lt;br /&gt;
&lt;br /&gt;
=== Providing domain based login themes ===&lt;br /&gt;
&lt;br /&gt;
If you have a multibrand installation and you want to deliver not only custom themes but also custom login-themes this can be done via Apache mod_rewrite. You can do so by a domain-based rewrite rule to deliver custom themes to a user based on the URI he's using. The needed config file &amp;lt;tt&amp;gt;.htaccess&amp;lt;/tt&amp;gt; is located under &amp;lt;tt&amp;gt;apps/themes&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Sample config for domain based login theme&lt;br /&gt;
RewriteCond %{HTTP_HOST} ^www\.domain\.com$&lt;br /&gt;
RewriteCond %{REQUEST_FILENAME} -f&lt;br /&gt;
RewriteRule ^login/(.*)$ domain_com_logintheme/$1 [L]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Best practice ==&lt;br /&gt;
&lt;br /&gt;
To be really safe, it’s best to only define your own values for the variables shown above. If this really breaks anything, we consider this a bug, please report it [https://bugs.open-xchange.com/] in our bugzilla.&lt;br /&gt;
&lt;br /&gt;
Of course, using CSS in &amp;lt;code&amp;gt;style.less&amp;lt;/code&amp;gt; file to define your own styles is also possible. Make sure to test your style in such cases more carefully. It is most likely safe to change minor things using this file, but if you plan to change any positions of larger elements, this might break the complete design. So please be careful when overwriting the default CSS rules.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
[http://bradfrost.github.io/this-is-responsive/resources.html Responsive design]&lt;br /&gt;
&lt;br /&gt;
[http://lesscss.org/ LESS]&lt;br /&gt;
&lt;br /&gt;
== Caveats ==&lt;br /&gt;
&lt;br /&gt;
It is '''not''' recommended to change the size of elements or their position. If you really want to do so, please check on all devices and in all browsers and make sure you didn’t break anything. You even have to be careful when changing the font, because this might have effects on positioning, too.&lt;br /&gt;
&lt;br /&gt;
As mentioned before, changing colors should be safe.&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17256</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17256"/>
		<updated>2014-03-12T13:54:09Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Grunt &amp;amp; Bower */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Getting started with grunt&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
== Node ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
If you are having problems or your package manager does not provide at least nodejs in version 0.10.x please have at https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
Please also make sure git is installed and in your PATH.&lt;br /&gt;
&lt;br /&gt;
''This is sometimes a problem on windows, make sure to activate the option to put git into your path during msys-git installation.''&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew]. Actually, we won't support you if you don't. It's that bad.&lt;br /&gt;
&lt;br /&gt;
Make sure you have an up-to-date homebrew installation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew update&lt;br /&gt;
brew upgrade&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Check your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt; and resolve any issues and repeat this step until homebrews output says &amp;quot;Your system is ready to brew.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew doctor&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt; if is not yet installed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew install node&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure you '''never &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node or its package manager npm'''. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ulimit -n 8192&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or you can set&lt;br /&gt;
it permanently by adding &amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo &amp;quot;limit maxfiles 8192 20480&amp;quot; | sudo tee -a /etc/launchd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Note that you will have to restart your system for the setting to be active.''&lt;br /&gt;
&lt;br /&gt;
== Grunt &amp;amp; Bower ==&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
'''Mac OS X / Windows:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
npm install -g bower grunt-cli&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Linux ''only'':'''&lt;br /&gt;
You may need root privileges to install global npm modules.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo npm install -g bower grunt-cli&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Cleaning development environment ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running &amp;lt;tt&amp;gt;git clean -ndx&amp;lt;/tt&amp;gt; this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run &amp;lt;tt&amp;gt;git clean -fdx&amp;lt;/tt&amp;gt; to really do the cleanup.&lt;br /&gt;
&lt;br /&gt;
== Installing node dependencies for OX Appsuite Frontend development ==&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,&lt;br /&gt;
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
npm install&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Building the UI ==&lt;br /&gt;
&lt;br /&gt;
Just run the default grunt task &amp;lt;tt&amp;gt;grunt&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local configuration ==&lt;br /&gt;
&lt;br /&gt;
You can override some options on your local build environment by creating the file &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt;. See &amp;lt;tt&amp;gt;grunt/local.conf.default.json&amp;lt;/tt&amp;gt; as a template.&lt;br /&gt;
&lt;br /&gt;
Hint: copy the default file to &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; to get started.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp grunt/local.conf.default.json grunt/local.conf.json&lt;br /&gt;
vi grunt/local.conf.json&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;appserver&amp;quot;: {&lt;br /&gt;
        &amp;quot;server&amp;quot;: &amp;quot;http://url.to.your.ser/appsuite/&amp;quot;,&lt;br /&gt;
        &amp;quot;verbose&amp;quot;: [&amp;quot;local:error&amp;quot;]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Accessing the UI ==&lt;br /&gt;
&lt;br /&gt;
You can run the appserver with the connect grunt task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect:server:keepalive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or in combination with the watch task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect watch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can now access the ui via &amp;lt;tt&amp;gt;http://localhost:8337/appsuite/&amp;lt;/tt&amp;gt;.&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17243</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17243"/>
		<updated>2014-03-10T13:36:14Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* appserver */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Getting started with grunt&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
== Node ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
Please also make sure git is installed and in your PATH.&lt;br /&gt;
&lt;br /&gt;
''This is sometimes a problem on windows, make sure to activate the option to put git into your path during msys-git installation.''&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew]. Actually, we won't support you if you don't. It's that bad.&lt;br /&gt;
&lt;br /&gt;
Make sure you have an up-to-date homebrew installation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew update&lt;br /&gt;
brew upgrade&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Check your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt; and resolve any issues and repeat this step until homebrews output says &amp;quot;Your system is ready to brew.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew doctor&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt; if is not yet installed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew install node&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure you '''never &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node or its package manager npm'''. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ulimit -n 8192&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or you can set&lt;br /&gt;
it permanently by adding &amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo &amp;quot;limit maxfiles 8192 20480&amp;quot; | sudo tee -a /etc/launchd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Note that you will have to restart your system for the setting to be active.''&lt;br /&gt;
&lt;br /&gt;
== Grunt &amp;amp; Bower ==&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
npm install -g bower grunt-cli&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Cleaning development environment ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running &amp;lt;tt&amp;gt;git clean -ndx&amp;lt;/tt&amp;gt; this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run &amp;lt;tt&amp;gt;git clean -fdx&amp;lt;/tt&amp;gt; to really do the cleanup.&lt;br /&gt;
&lt;br /&gt;
== Installing node dependencies for OX Appsuite Frontend development ==&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,&lt;br /&gt;
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
npm install&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Building the UI ==&lt;br /&gt;
&lt;br /&gt;
Just run the default grunt task &amp;lt;tt&amp;gt;grunt&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local configuration ==&lt;br /&gt;
&lt;br /&gt;
You can override some options on your local build environment by creating the file &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt;. See &amp;lt;tt&amp;gt;grunt/local.conf.default.json&amp;lt;/tt&amp;gt; as a template.&lt;br /&gt;
&lt;br /&gt;
Hint: copy the default file to &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; to get started.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp grunt/local.conf.default.json grunt/local.conf.json&lt;br /&gt;
vi grunt/local.conf.json&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;appserver&amp;quot;: {&lt;br /&gt;
        &amp;quot;server&amp;quot;: &amp;quot;http://url.to.your.ser/appsuite/&amp;quot;,&lt;br /&gt;
        &amp;quot;verbose&amp;quot;: [&amp;quot;local:error&amp;quot;]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Accessing the UI ==&lt;br /&gt;
&lt;br /&gt;
You can run the appserver with the connect grunt task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect:server:keepalive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or in combination with the watch task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect watch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can now access the ui via &amp;lt;tt&amp;gt;http://localhost:8337/appsuite/&amp;lt;/tt&amp;gt;.&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17242</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17242"/>
		<updated>2014-03-10T13:33:59Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Getting started with grunt&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
== Node ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
Please also make sure git is installed and in your PATH.&lt;br /&gt;
&lt;br /&gt;
''This is sometimes a problem on windows, make sure to activate the option to put git into your path during msys-git installation.''&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew]. Actually, we won't support you if you don't. It's that bad.&lt;br /&gt;
&lt;br /&gt;
Make sure you have an up-to-date homebrew installation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew update&lt;br /&gt;
brew upgrade&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Check your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt; and resolve any issues and repeat this step until homebrews output says &amp;quot;Your system is ready to brew.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew doctor&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt; if is not yet installed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew install node&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure you '''never &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node or its package manager npm'''. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ulimit -n 8192&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or you can set&lt;br /&gt;
it permanently by adding &amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo &amp;quot;limit maxfiles 8192 20480&amp;quot; | sudo tee -a /etc/launchd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Note that you will have to restart your system for the setting to be active.''&lt;br /&gt;
&lt;br /&gt;
== Grunt &amp;amp; Bower ==&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
npm install -g bower grunt-cli&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Cleaning development environment ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running &amp;lt;tt&amp;gt;git clean -ndx&amp;lt;/tt&amp;gt; this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run &amp;lt;tt&amp;gt;git clean -fdx&amp;lt;/tt&amp;gt; to really do the cleanup.&lt;br /&gt;
&lt;br /&gt;
== Installing node dependencies for OX Appsuite Frontend development ==&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,&lt;br /&gt;
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
npm install&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Building the UI ==&lt;br /&gt;
&lt;br /&gt;
Just run the default grunt task &amp;lt;tt&amp;gt;grunt&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local configuration ==&lt;br /&gt;
&lt;br /&gt;
You can override some options on your local build environment by creating the file &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt;. See &amp;lt;tt&amp;gt;grunt/local.conf.default.json&amp;lt;/tt&amp;gt; as a template.&lt;br /&gt;
&lt;br /&gt;
Hint: copy the default file to &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; to get started.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp grunt/local.conf.default.json grunt/local.conf.json&lt;br /&gt;
vi grunt/local.conf.json&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== appserver ==&lt;br /&gt;
&lt;br /&gt;
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;appserver&amp;quot;: {&lt;br /&gt;
        &amp;quot;server&amp;quot;: &amp;quot;http://url.to.your.ser/appsuite/&amp;quot;,&lt;br /&gt;
        &amp;quot;verbose&amp;quot;: [&amp;quot;local:error&amp;quot;]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can run the appserver with the connect grunt task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect:server:keepalive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or in combination with the watch task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect watch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can now access the frontend via &amp;lt;tt&amp;gt;http://localhost:8337/appsuite/&amp;lt;/tt&amp;gt;.&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17241</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17241"/>
		<updated>2014-03-10T13:24:41Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Windows */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Getting started with grunt&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
== Node ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
Please also make sure git is installed and in your PATH.&lt;br /&gt;
&lt;br /&gt;
''This is sometimes a problem on windows, make sure to activate the option to put git into your path during msys-git installation.''&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew]. Actually, we won't support you if you don't. It's that bad.&lt;br /&gt;
&lt;br /&gt;
Make sure you have an up-to-date homebrew installation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew update&lt;br /&gt;
brew upgrade&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Check your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt; and resolve any issues and repeat this step until homebrews output says &amp;quot;Your system is ready to brew.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew doctor&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt; if is not yet installed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew install node&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure you '''never &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node or its package manager npm'''. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ulimit -n 8192&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or you can set&lt;br /&gt;
it permanently by adding &amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo &amp;quot;limit maxfiles 8192 20480&amp;quot; | sudo tee -a /etc/launchd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Note that you will have to restart your system for the setting to be active.''&lt;br /&gt;
&lt;br /&gt;
== Grunt &amp;amp; Bower ==&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
npm install -g bower grunt-cli&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Cleaning development environment ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running &amp;lt;tt&amp;gt;git clean -ndx&amp;lt;/tt&amp;gt; this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run &amp;lt;tt&amp;gt;git clean -fdx&amp;lt;/tt&amp;gt; to really do the cleanup.&lt;br /&gt;
&lt;br /&gt;
== Installing node dependencies for OX Appsuite Frontend development ==&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,&lt;br /&gt;
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
npm install&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Building the UI ==&lt;br /&gt;
&lt;br /&gt;
Just run the default grunt task &amp;lt;tt&amp;gt;grunt&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local configuration ==&lt;br /&gt;
&lt;br /&gt;
You can override some options on your local build environment by creating the file &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt;. See &amp;lt;tt&amp;gt;grunt/local.conf.default.json&amp;lt;/tt&amp;gt; as a template.&lt;br /&gt;
&lt;br /&gt;
Hint: copy the default file to &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; to get started.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp grunt/local.conf.default.json grunt/local.conf.json&lt;br /&gt;
vi grunt/local.conf.json&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
&lt;br /&gt;
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;appserver&amp;quot;: {&lt;br /&gt;
        &amp;quot;server&amp;quot;: &amp;quot;http://url.to.your.ser/appsuite/&amp;quot;,&lt;br /&gt;
        &amp;quot;verbose&amp;quot;: [&amp;quot;local:error&amp;quot;]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can run the appserver with the connect grunt task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect:server:keepalive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or in combination with the watch task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect watch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17240</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17240"/>
		<updated>2014-03-10T13:24:33Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Windows */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Getting started with grunt&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
== Node ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
Please also make sure git is installed and in your PATH.&lt;br /&gt;
''This is sometimes a problem on windows, make sure to activate the option to put git into your path during msys-git installation.''&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew]. Actually, we won't support you if you don't. It's that bad.&lt;br /&gt;
&lt;br /&gt;
Make sure you have an up-to-date homebrew installation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew update&lt;br /&gt;
brew upgrade&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Check your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt; and resolve any issues and repeat this step until homebrews output says &amp;quot;Your system is ready to brew.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew doctor&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt; if is not yet installed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew install node&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure you '''never &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node or its package manager npm'''. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ulimit -n 8192&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or you can set&lt;br /&gt;
it permanently by adding &amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo &amp;quot;limit maxfiles 8192 20480&amp;quot; | sudo tee -a /etc/launchd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Note that you will have to restart your system for the setting to be active.''&lt;br /&gt;
&lt;br /&gt;
== Grunt &amp;amp; Bower ==&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
npm install -g bower grunt-cli&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Cleaning development environment ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running &amp;lt;tt&amp;gt;git clean -ndx&amp;lt;/tt&amp;gt; this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run &amp;lt;tt&amp;gt;git clean -fdx&amp;lt;/tt&amp;gt; to really do the cleanup.&lt;br /&gt;
&lt;br /&gt;
== Installing node dependencies for OX Appsuite Frontend development ==&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,&lt;br /&gt;
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
npm install&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Building the UI ==&lt;br /&gt;
&lt;br /&gt;
Just run the default grunt task &amp;lt;tt&amp;gt;grunt&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local configuration ==&lt;br /&gt;
&lt;br /&gt;
You can override some options on your local build environment by creating the file &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt;. See &amp;lt;tt&amp;gt;grunt/local.conf.default.json&amp;lt;/tt&amp;gt; as a template.&lt;br /&gt;
&lt;br /&gt;
Hint: copy the default file to &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; to get started.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp grunt/local.conf.default.json grunt/local.conf.json&lt;br /&gt;
vi grunt/local.conf.json&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
&lt;br /&gt;
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;appserver&amp;quot;: {&lt;br /&gt;
        &amp;quot;server&amp;quot;: &amp;quot;http://url.to.your.ser/appsuite/&amp;quot;,&lt;br /&gt;
        &amp;quot;verbose&amp;quot;: [&amp;quot;local:error&amp;quot;]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can run the appserver with the connect grunt task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect:server:keepalive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or in combination with the watch task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect watch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17239</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17239"/>
		<updated>2014-03-10T13:23:00Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Installing node dependencies for OX Appsuite Frontend development */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Getting started with grunt&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
== Node ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew]. Actually, we won't support you if you don't. It's that bad.&lt;br /&gt;
&lt;br /&gt;
Make sure you have an up-to-date homebrew installation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew update&lt;br /&gt;
brew upgrade&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Check your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt; and resolve any issues and repeat this step until homebrews output says &amp;quot;Your system is ready to brew.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew doctor&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt; if is not yet installed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew install node&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure you '''never &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node or its package manager npm'''. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ulimit -n 8192&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or you can set&lt;br /&gt;
it permanently by adding &amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo &amp;quot;limit maxfiles 8192 20480&amp;quot; | sudo tee -a /etc/launchd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Note that you will have to restart your system for the setting to be active.''&lt;br /&gt;
&lt;br /&gt;
== Grunt &amp;amp; Bower ==&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
npm install -g bower grunt-cli&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Cleaning development environment ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running &amp;lt;tt&amp;gt;git clean -ndx&amp;lt;/tt&amp;gt; this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run &amp;lt;tt&amp;gt;git clean -fdx&amp;lt;/tt&amp;gt; to really do the cleanup.&lt;br /&gt;
&lt;br /&gt;
== Installing node dependencies for OX Appsuite Frontend development ==&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,&lt;br /&gt;
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
npm install&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Building the UI ==&lt;br /&gt;
&lt;br /&gt;
Just run the default grunt task &amp;lt;tt&amp;gt;grunt&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local configuration ==&lt;br /&gt;
&lt;br /&gt;
You can override some options on your local build environment by creating the file &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt;. See &amp;lt;tt&amp;gt;grunt/local.conf.default.json&amp;lt;/tt&amp;gt; as a template.&lt;br /&gt;
&lt;br /&gt;
Hint: copy the default file to &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; to get started.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp grunt/local.conf.default.json grunt/local.conf.json&lt;br /&gt;
vi grunt/local.conf.json&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
&lt;br /&gt;
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;appserver&amp;quot;: {&lt;br /&gt;
        &amp;quot;server&amp;quot;: &amp;quot;http://url.to.your.ser/appsuite/&amp;quot;,&lt;br /&gt;
        &amp;quot;verbose&amp;quot;: [&amp;quot;local:error&amp;quot;]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can run the appserver with the connect grunt task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect:server:keepalive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or in combination with the watch task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect watch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17238</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17238"/>
		<updated>2014-03-10T13:19:46Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Local configuration */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Getting started with grunt&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
== Node ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew]. Actually, we won't support you if you don't. It's that bad.&lt;br /&gt;
&lt;br /&gt;
Make sure you have an up-to-date homebrew installation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew update&lt;br /&gt;
brew upgrade&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Check your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt; and resolve any issues and repeat this step until homebrews output says &amp;quot;Your system is ready to brew.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew doctor&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt; if is not yet installed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew install node&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure you '''never &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node or its package manager npm'''. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ulimit -n 8192&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or you can set&lt;br /&gt;
it permanently by adding &amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo &amp;quot;limit maxfiles 8192 20480&amp;quot; | sudo tee -a /etc/launchd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Note that you will have to restart your system for the setting to be active.''&lt;br /&gt;
&lt;br /&gt;
== Grunt &amp;amp; Bower ==&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
npm install -g bower grunt-cli&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Cleaning development environment ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running &amp;lt;tt&amp;gt;git clean -ndx&amp;lt;/tt&amp;gt; this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run &amp;lt;tt&amp;gt;git clean -fdx&amp;lt;/tt&amp;gt; to really do the cleanup.&lt;br /&gt;
&lt;br /&gt;
== Installing node dependencies for OX Appsuite Frontend development ==&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,&lt;br /&gt;
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)&lt;br /&gt;
&lt;br /&gt;
== Building the UI ==&lt;br /&gt;
&lt;br /&gt;
Just run the default grunt task &amp;lt;tt&amp;gt;grunt&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local configuration ==&lt;br /&gt;
&lt;br /&gt;
You can override some options on your local build environment by creating the file &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt;. See &amp;lt;tt&amp;gt;grunt/local.conf.default.json&amp;lt;/tt&amp;gt; as a template.&lt;br /&gt;
&lt;br /&gt;
Hint: copy the default file to &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; to get started.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp grunt/local.conf.default.json grunt/local.conf.json&lt;br /&gt;
vi grunt/local.conf.json&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
&lt;br /&gt;
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;appserver&amp;quot;: {&lt;br /&gt;
        &amp;quot;server&amp;quot;: &amp;quot;http://url.to.your.ser/appsuite/&amp;quot;,&lt;br /&gt;
        &amp;quot;verbose&amp;quot;: [&amp;quot;local:error&amp;quot;]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can run the appserver with the connect grunt task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect:server:keepalive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or in combination with the watch task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect watch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17237</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17237"/>
		<updated>2014-03-10T13:18:47Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Local configuration */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Getting started with grunt&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
== Node ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew]. Actually, we won't support you if you don't. It's that bad.&lt;br /&gt;
&lt;br /&gt;
Make sure you have an up-to-date homebrew installation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew update&lt;br /&gt;
brew upgrade&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Check your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt; and resolve any issues and repeat this step until homebrews output says &amp;quot;Your system is ready to brew.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew doctor&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt; if is not yet installed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew install node&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure you '''never &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node or its package manager npm'''. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ulimit -n 8192&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or you can set&lt;br /&gt;
it permanently by adding &amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo &amp;quot;limit maxfiles 8192 20480&amp;quot; | sudo tee -a /etc/launchd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Note that you will have to restart your system for the setting to be active.''&lt;br /&gt;
&lt;br /&gt;
== Grunt &amp;amp; Bower ==&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
npm install -g bower grunt-cli&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Cleaning development environment ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running &amp;lt;tt&amp;gt;git clean -ndx&amp;lt;/tt&amp;gt; this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run &amp;lt;tt&amp;gt;git clean -fdx&amp;lt;/tt&amp;gt; to really do the cleanup.&lt;br /&gt;
&lt;br /&gt;
== Installing node dependencies for OX Appsuite Frontend development ==&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,&lt;br /&gt;
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)&lt;br /&gt;
&lt;br /&gt;
== Building the UI ==&lt;br /&gt;
&lt;br /&gt;
Just run the default grunt task &amp;lt;tt&amp;gt;grunt&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local configuration ==&lt;br /&gt;
&lt;br /&gt;
You can override some options on your local build environment by creating the file &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt;. See &amp;lt;tt&amp;gt;grunt/local.conf.default.json&amp;lt;/tt&amp;gt; as a template.&lt;br /&gt;
&lt;br /&gt;
Hint: copy the default file to &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; to get started.&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
&lt;br /&gt;
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;appserver&amp;quot;: {&lt;br /&gt;
        &amp;quot;server&amp;quot;: &amp;quot;http://url.to.your.ser/appsuite/&amp;quot;,&lt;br /&gt;
        &amp;quot;verbose&amp;quot;: [&amp;quot;local:error&amp;quot;]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can run the appserver with the connect grunt task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect:server:keepalive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or in combination with the watch task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect watch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17236</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17236"/>
		<updated>2014-03-10T13:17:08Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Building the UI */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Getting started with grunt&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
== Node ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew]. Actually, we won't support you if you don't. It's that bad.&lt;br /&gt;
&lt;br /&gt;
Make sure you have an up-to-date homebrew installation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew update&lt;br /&gt;
brew upgrade&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Check your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt; and resolve any issues and repeat this step until homebrews output says &amp;quot;Your system is ready to brew.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew doctor&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt; if is not yet installed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew install node&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure you '''never &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node or its package manager npm'''. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ulimit -n 8192&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or you can set&lt;br /&gt;
it permanently by adding &amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo &amp;quot;limit maxfiles 8192 20480&amp;quot; | sudo tee -a /etc/launchd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Note that you will have to restart your system for the setting to be active.''&lt;br /&gt;
&lt;br /&gt;
== Grunt &amp;amp; Bower ==&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
npm install -g bower grunt-cli&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Cleaning development environment ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running &amp;lt;tt&amp;gt;git clean -ndx&amp;lt;/tt&amp;gt; this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run &amp;lt;tt&amp;gt;git clean -fdx&amp;lt;/tt&amp;gt; to really do the cleanup.&lt;br /&gt;
&lt;br /&gt;
== Installing node dependencies for OX Appsuite Frontend development ==&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,&lt;br /&gt;
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)&lt;br /&gt;
&lt;br /&gt;
== Building the UI ==&lt;br /&gt;
&lt;br /&gt;
Just run the default grunt task &amp;lt;tt&amp;gt;grunt&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local configuration ==&lt;br /&gt;
&lt;br /&gt;
You can override some options on your local build environment by creating the file &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt;. See &amp;lt;tt&amp;gt;grunt/local.conf.default.json&amp;lt;/tt&amp;gt; as a template.&lt;br /&gt;
&lt;br /&gt;
Hint: copy the default file to &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; to get started.&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
&lt;br /&gt;
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;appserver&amp;quot;: {&lt;br /&gt;
        &amp;quot;server&amp;quot;: &amp;quot;http://url.to.your.ser/appsuite/&amp;quot;,&lt;br /&gt;
        &amp;quot;verbose&amp;quot;: [&amp;quot;local:error&amp;quot;]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can run the appserver with the connect grunt task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect:server:keepalive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or in combination with the watch task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect watch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17235</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17235"/>
		<updated>2014-03-10T13:16:34Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Grunt &amp;amp; Bower */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Getting started with grunt&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
== Node ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew]. Actually, we won't support you if you don't. It's that bad.&lt;br /&gt;
&lt;br /&gt;
Make sure you have an up-to-date homebrew installation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew update&lt;br /&gt;
brew upgrade&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Check your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt; and resolve any issues and repeat this step until homebrews output says &amp;quot;Your system is ready to brew.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew doctor&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt; if is not yet installed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew install node&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure you '''never &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node or its package manager npm'''. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ulimit -n 8192&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or you can set&lt;br /&gt;
it permanently by adding &amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo &amp;quot;limit maxfiles 8192 20480&amp;quot; | sudo tee -a /etc/launchd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Note that you will have to restart your system for the setting to be active.''&lt;br /&gt;
&lt;br /&gt;
== Grunt &amp;amp; Bower ==&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
npm install -g bower grunt-cli&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Cleaning development environment ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running &amp;lt;tt&amp;gt;git clean -ndx&amp;lt;/tt&amp;gt; this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run &amp;lt;tt&amp;gt;git clean -fdx&amp;lt;/tt&amp;gt; to really do the cleanup.&lt;br /&gt;
&lt;br /&gt;
== Installing node dependencies for OX Appsuite Frontend development ==&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,&lt;br /&gt;
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)&lt;br /&gt;
&lt;br /&gt;
== Building the UI ==&lt;br /&gt;
&lt;br /&gt;
Just run the default grunt task &amp;lt;tt&amp;gt;grunt&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local configuration ==&lt;br /&gt;
&lt;br /&gt;
You can override some options on your local build environment by creating the file &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt;. See &amp;lt;tt&amp;gt;grunt/local.conf.default.json&amp;lt;/tt&amp;gt; as a template.&lt;br /&gt;
&lt;br /&gt;
Hint: copy the default file to &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; to get started.&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
&lt;br /&gt;
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;appserver&amp;quot;: {&lt;br /&gt;
        &amp;quot;server&amp;quot;: &amp;quot;http://url.to.your.ser/appsuite/&amp;quot;,&lt;br /&gt;
        &amp;quot;verbose&amp;quot;: [&amp;quot;local:error&amp;quot;]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can run the appserver with the connect grunt task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect:server:keepalive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or in combination with the watch task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect watch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17234</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17234"/>
		<updated>2014-03-10T13:09:09Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Getting started with grunt&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
== Node ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew]. Actually, we won't support you if you don't. It's that bad.&lt;br /&gt;
&lt;br /&gt;
Make sure you have an up-to-date homebrew installation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew update&lt;br /&gt;
brew upgrade&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Check your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt; and resolve any issues and repeat this step until homebrews output says &amp;quot;Your system is ready to brew.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew doctor&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt; if is not yet installed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew install node&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure you '''never &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node or its package manager npm'''. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ulimit -n 8192&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or you can set&lt;br /&gt;
it permanently by adding &amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo &amp;quot;limit maxfiles 8192 20480&amp;quot; | sudo tee -a /etc/launchd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Note that you will have to restart your system for the setting to be active.''&lt;br /&gt;
&lt;br /&gt;
== Grunt &amp;amp; Bower ==&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
== Cleaning development environment ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running &amp;lt;tt&amp;gt;git clean -ndx&amp;lt;/tt&amp;gt; this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run &amp;lt;tt&amp;gt;git clean -fdx&amp;lt;/tt&amp;gt; to really do the cleanup.&lt;br /&gt;
&lt;br /&gt;
== Installing node dependencies for OX Appsuite Frontend development ==&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,&lt;br /&gt;
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)&lt;br /&gt;
&lt;br /&gt;
== Building the UI ==&lt;br /&gt;
&lt;br /&gt;
Just run the default grunt task &amp;lt;tt&amp;gt;grunt&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local configuration ==&lt;br /&gt;
&lt;br /&gt;
You can override some options on your local build environment by creating the file &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt;. See &amp;lt;tt&amp;gt;grunt/local.conf.default.json&amp;lt;/tt&amp;gt; as a template.&lt;br /&gt;
&lt;br /&gt;
Hint: copy the default file to &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; to get started.&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
&lt;br /&gt;
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;appserver&amp;quot;: {&lt;br /&gt;
        &amp;quot;server&amp;quot;: &amp;quot;http://url.to.your.ser/appsuite/&amp;quot;,&lt;br /&gt;
        &amp;quot;verbose&amp;quot;: [&amp;quot;local:error&amp;quot;]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can run the appserver with the connect grunt task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect:server:keepalive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or in combination with the watch task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect watch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17232</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17232"/>
		<updated>2014-03-10T13:03:31Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Mac OS X */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Getting started with grunt&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
== Node ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew].&lt;br /&gt;
&lt;br /&gt;
Make sure you have an up-to-date homebrew installation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew update&lt;br /&gt;
brew upgrade&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Check your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt; and resolve any issues and repeat this step until homebrews output says &amp;quot;Your system is ready to brew.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew doctor&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt; if is not yet installed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
brew install node&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure you '''never &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node or its package manager npm'''. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ulimit -n 8192&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or you can set&lt;br /&gt;
it permanently by adding &amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo &amp;quot;limit maxfiles 8192 20480&amp;quot; | sudo tee -a /etc/launchd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Note that you will have to restart your system for the setting to be active.''&lt;br /&gt;
&lt;br /&gt;
== Grunt &amp;amp; Bower ==&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
== Cleaning development environment ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running &amp;lt;tt&amp;gt;git clean -ndx&amp;lt;/tt&amp;gt; this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run &amp;lt;tt&amp;gt;git clean -fdx&amp;lt;/tt&amp;gt; to really do the cleanup.&lt;br /&gt;
&lt;br /&gt;
== Installing node dependencies for OX Appsuite Frontend development ==&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,&lt;br /&gt;
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)&lt;br /&gt;
&lt;br /&gt;
== Building the UI ==&lt;br /&gt;
&lt;br /&gt;
Just run the default grunt task &amp;lt;tt&amp;gt;grunt&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local configuration ==&lt;br /&gt;
&lt;br /&gt;
You can override some options on your local build environment by creating the file &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt;. See &amp;lt;tt&amp;gt;grunt/local.conf.default.json&amp;lt;/tt&amp;gt; as a template.&lt;br /&gt;
&lt;br /&gt;
Hint: copy the default file to &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; to get started.&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
&lt;br /&gt;
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;appserver&amp;quot;: {&lt;br /&gt;
        &amp;quot;server&amp;quot;: &amp;quot;http://url.to.your.ser/appsuite/&amp;quot;,&lt;br /&gt;
        &amp;quot;verbose&amp;quot;: [&amp;quot;local:error&amp;quot;]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can run the appserver with the connect grunt task: &amp;lt;tt&amp;gt;grunt connect:server:keepalive&amp;lt;/tt&amp;gt; or in combination with the watch task: &amp;lt;tt&amp;gt;grunt connect watch&amp;lt;/tt&amp;gt;&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17231</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17231"/>
		<updated>2014-03-10T12:58:10Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Mac OS X */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Getting started with grunt&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
== Node ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew].&lt;br /&gt;
Make sure you have checked your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt; and resolved any issues remaining.&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt; or run &amp;lt;tt&amp;gt;brew upgrade&amp;lt;/tt&amp;gt; if you already have node installed.&lt;br /&gt;
&lt;br /&gt;
Make sure you '''don't &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node or its package manager npm'''. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ulimit -n 8192&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or you can set&lt;br /&gt;
it permanently by adding &amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo &amp;quot;limit maxfiles 8192 20480&amp;quot; | sudo tee -a /etc/launchd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Note that you will have to restart your system for the setting to be active.''&lt;br /&gt;
&lt;br /&gt;
== Grunt &amp;amp; Bower ==&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
== Cleaning development environment ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running &amp;lt;tt&amp;gt;git clean -ndx&amp;lt;/tt&amp;gt; this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run &amp;lt;tt&amp;gt;git clean -fdx&amp;lt;/tt&amp;gt; to really do the cleanup.&lt;br /&gt;
&lt;br /&gt;
== Installing node dependencies for OX Appsuite Frontend development ==&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,&lt;br /&gt;
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)&lt;br /&gt;
&lt;br /&gt;
== Building the UI ==&lt;br /&gt;
&lt;br /&gt;
Just run the default grunt task &amp;lt;tt&amp;gt;grunt&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local configuration ==&lt;br /&gt;
&lt;br /&gt;
You can override some options on your local build environment by creating the file &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt;. See &amp;lt;tt&amp;gt;grunt/local.conf.default.json&amp;lt;/tt&amp;gt; as a template.&lt;br /&gt;
&lt;br /&gt;
Hint: copy the default file to &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; to get started.&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
&lt;br /&gt;
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;appserver&amp;quot;: {&lt;br /&gt;
        &amp;quot;server&amp;quot;: &amp;quot;http://url.to.your.ser/appsuite/&amp;quot;,&lt;br /&gt;
        &amp;quot;verbose&amp;quot;: [&amp;quot;local:error&amp;quot;]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can run the appserver with the connect grunt task: &amp;lt;tt&amp;gt;grunt connect:server:keepalive&amp;lt;/tt&amp;gt; or in combination with the watch task: &amp;lt;tt&amp;gt;grunt connect watch&amp;lt;/tt&amp;gt;&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17230</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17230"/>
		<updated>2014-03-10T12:57:28Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Mac OS X */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Getting started with grunt&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
== Node ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew].&lt;br /&gt;
Make sure you have checked your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt; and resolved any issues remaining.&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt; or run &amp;lt;tt&amp;gt;brew upgrade&amp;lt;/tt&amp;gt; if you already have node installed.&lt;br /&gt;
&lt;br /&gt;
Make sure you '''don't &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node'''. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ulimit -n 8192&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or you can set&lt;br /&gt;
it permanently by adding &amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo &amp;quot;limit maxfiles 8192 20480&amp;quot; | sudo tee -a /etc/launchd.conf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Note that you will have to restart your system for the setting to be active.''&lt;br /&gt;
&lt;br /&gt;
== Grunt &amp;amp; Bower ==&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
== Cleaning development environment ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running &amp;lt;tt&amp;gt;git clean -ndx&amp;lt;/tt&amp;gt; this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run &amp;lt;tt&amp;gt;git clean -fdx&amp;lt;/tt&amp;gt; to really do the cleanup.&lt;br /&gt;
&lt;br /&gt;
== Installing node dependencies for OX Appsuite Frontend development ==&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,&lt;br /&gt;
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)&lt;br /&gt;
&lt;br /&gt;
== Building the UI ==&lt;br /&gt;
&lt;br /&gt;
Just run the default grunt task &amp;lt;tt&amp;gt;grunt&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local configuration ==&lt;br /&gt;
&lt;br /&gt;
You can override some options on your local build environment by creating the file &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt;. See &amp;lt;tt&amp;gt;grunt/local.conf.default.json&amp;lt;/tt&amp;gt; as a template.&lt;br /&gt;
&lt;br /&gt;
Hint: copy the default file to &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; to get started.&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
&lt;br /&gt;
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;appserver&amp;quot;: {&lt;br /&gt;
        &amp;quot;server&amp;quot;: &amp;quot;http://url.to.your.ser/appsuite/&amp;quot;,&lt;br /&gt;
        &amp;quot;verbose&amp;quot;: [&amp;quot;local:error&amp;quot;]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can run the appserver with the connect grunt task: &amp;lt;tt&amp;gt;grunt connect:server:keepalive&amp;lt;/tt&amp;gt; or in combination with the watch task: &amp;lt;tt&amp;gt;grunt connect watch&amp;lt;/tt&amp;gt;&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17229</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17229"/>
		<updated>2014-03-10T12:50:12Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Mac OS X */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Getting started with grunt&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
== Node ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew].&lt;br /&gt;
Make sure you have checked your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt; and resolved any issues remaining.&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt; or run &amp;lt;tt&amp;gt;brew upgrade&amp;lt;/tt&amp;gt; if you already have node installed.&lt;br /&gt;
&lt;br /&gt;
Make sure you '''don't &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node'''. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value or you can set&lt;br /&gt;
it permanently by adding&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''Note that this file may need to be created if it does not exist and you will have to restart your system for the setting to be active.''&lt;br /&gt;
&lt;br /&gt;
== Grunt &amp;amp; Bower ==&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
== Cleaning development environment ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running &amp;lt;tt&amp;gt;git clean -ndx&amp;lt;/tt&amp;gt; this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run &amp;lt;tt&amp;gt;git clean -fdx&amp;lt;/tt&amp;gt; to really do the cleanup.&lt;br /&gt;
&lt;br /&gt;
== Installing node dependencies for OX Appsuite Frontend development ==&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,&lt;br /&gt;
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)&lt;br /&gt;
&lt;br /&gt;
== Building the UI ==&lt;br /&gt;
&lt;br /&gt;
Just run the default grunt task &amp;lt;tt&amp;gt;grunt&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local configuration ==&lt;br /&gt;
&lt;br /&gt;
You can override some options on your local build environment by creating the file &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt;. See &amp;lt;tt&amp;gt;grunt/local.conf.default.json&amp;lt;/tt&amp;gt; as a template.&lt;br /&gt;
&lt;br /&gt;
Hint: copy the default file to &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; to get started.&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
&lt;br /&gt;
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;appserver&amp;quot;: {&lt;br /&gt;
        &amp;quot;server&amp;quot;: &amp;quot;http://url.to.your.ser/appsuite/&amp;quot;,&lt;br /&gt;
        &amp;quot;verbose&amp;quot;: [&amp;quot;local:error&amp;quot;]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can run the appserver with the connect grunt task: &amp;lt;tt&amp;gt;grunt connect:server:keepalive&amp;lt;/tt&amp;gt; or in combination with the watch task: &amp;lt;tt&amp;gt;grunt connect watch&amp;lt;/tt&amp;gt;&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Changes&amp;diff=17228</id>
		<title>AppSuite:Changes</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Changes&amp;diff=17228"/>
		<updated>2014-03-10T12:46:58Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Changes&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.6.0 ==&lt;br /&gt;
&lt;br /&gt;
=== grunt ===&lt;br /&gt;
grunt has superseded jake as a build system. In this section all changes will be described.&lt;br /&gt;
&lt;br /&gt;
Instead of a shell wrapper around jake (the `build-appsuite` script), grunt should now be invoked&lt;br /&gt;
directly. The recommended workflow will be described in the next subsections, where all tasks relevant&lt;br /&gt;
for development will be explained in detail.&lt;br /&gt;
&lt;br /&gt;
Grunt is a well documented task-runner (see http://gruntjs.com) and there is a large community writing plugins.&lt;br /&gt;
Unless stated otherwise, we implemented standard grunt behaviour and rely on best-practice introduced by the&lt;br /&gt;
grunt community.&lt;br /&gt;
&lt;br /&gt;
==== local configuration ====&lt;br /&gt;
All local configuration is now done in `grunt/local.conf.json` using the JSON format. This replaces&lt;br /&gt;
the `local.conf` file, that was sourced into build-appsuite shell script. This new way is more platform&lt;br /&gt;
independent.&lt;br /&gt;
&lt;br /&gt;
For further information please see our [[AppSuite:GettingStartedWithGrunt | Getting Started with grunt]] guide.&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
The functionality of the appserver is now provided by the grunt-contrib-connect plugin. The `connect` task&lt;br /&gt;
should be run in conjunction with the `watch` task like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect watch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will start the appserver middleware and start watching for changed files. In case you don't want this&lt;br /&gt;
behaviour, you have to start the `connect` task with the keepalive option (as documented in grunt-contrib-connect documentation):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect:server:keepalive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bower ===&lt;br /&gt;
Most of our third party front-end libraries are now managed via bower. The location for these libraries has changed from lib/ to bower_components/. These files will be handled when building by the new grunt build system and copied or concatinated accordingly.&lt;br /&gt;
&lt;br /&gt;
'''Warning: Manual editing/patching/hacking files in &amp;quot;bower_components/&amp;quot; is deprecated.'''&lt;br /&gt;
&lt;br /&gt;
''However if you are positive that there is a bug in one of these libraries a possible course of action would be to fork the package on github and commit your patch there and create an upstream pull request, in any case this should be handled by the frontend team.''&lt;br /&gt;
&lt;br /&gt;
An up-to-date list of packages maintained by bower can be reviewed in the bower.json file in the ui root.&lt;br /&gt;
&lt;br /&gt;
* jquery&lt;br /&gt;
* underscore&lt;br /&gt;
* mediaelement&lt;br /&gt;
* bootstrap&lt;br /&gt;
* bootstrap-datepicker&lt;br /&gt;
* bootstrap-typeahead&lt;br /&gt;
* font-awesome&lt;br /&gt;
* open-sans-fontface&lt;br /&gt;
* backbone&lt;br /&gt;
* backbone-validation&lt;br /&gt;
* Chart.js&lt;br /&gt;
* bigscreen&lt;br /&gt;
* jquery-placeholder&lt;br /&gt;
* jquery-imageloader&lt;br /&gt;
* textarea-helper&lt;br /&gt;
* requirejs&lt;br /&gt;
* bootstrap-accessibility-plugin&lt;br /&gt;
&lt;br /&gt;
Read more about bower [http://bower.io here].&lt;br /&gt;
&lt;br /&gt;
=== Require.js ===&lt;br /&gt;
Require.js updated from 2.1.8 to 2.1.11&lt;br /&gt;
&lt;br /&gt;
We also changed the way style files are included in the require.js define() method.&lt;br /&gt;
less and css files are now defined in the same way as javascript files, WITHOUT its extension.&lt;br /&gt;
&lt;br /&gt;
* less!io.ox/calendar/style.less =&amp;gt; less!io.ox/calendar/style&lt;br /&gt;
* css!io.ox/calendar/style.css =&amp;gt; css!io.ox/calendar/style&lt;br /&gt;
&lt;br /&gt;
=== jQuery ===&lt;br /&gt;
jQuery was updated from 2.0.3 to 2.1.0&lt;br /&gt;
&lt;br /&gt;
If you experience any issues please check [http://blog.jquery.com/2014/01/24/jquery-1-11-and-2-1-released/ this blogpost] for changes.&lt;br /&gt;
&lt;br /&gt;
=== Underscore.js ===&lt;br /&gt;
&lt;br /&gt;
Underscore was updated from Version 1.4.4 to Version 1.6&lt;br /&gt;
&lt;br /&gt;
Please read the [http://underscorejs.org/#changelog changelog].&lt;br /&gt;
&lt;br /&gt;
'''Hint:''' Look for &amp;quot;Removed the ability to call _.bindAll with no method name arguments&amp;quot;, this was pretty much the only issue we had whilst updating.&lt;br /&gt;
&lt;br /&gt;
=== Backbone.js ===&lt;br /&gt;
&lt;br /&gt;
Backbone.js was updated from 0.9.x to Version 1.0.0&lt;br /&gt;
&lt;br /&gt;
Please read the [http://backbonejs.org/#changelog changelog].&lt;br /&gt;
&lt;br /&gt;
'''Note:''' We currently cannot update to 1.1.2, which is the current version of backbone, but will do so in the near future, when we resolve our issues.&lt;br /&gt;
&lt;br /&gt;
=== Bootstrap ===&lt;br /&gt;
&lt;br /&gt;
Bootstrap was updated from 2.2.2 to 3.1.1&lt;br /&gt;
&lt;br /&gt;
Please read the [http://getbootstrap.com/migration/ Bootstrap 2 to Bootstrap 3 migration guide], as there are many significant changes, to markup and styles.&lt;br /&gt;
&lt;br /&gt;
All bootstrap plugins and our core components have been updated accordingly.&lt;br /&gt;
&lt;br /&gt;
'''Note:''' In Bootstrap 3 the typeahead plugin was dropped in favor of Twitters typeahead.js. This change would break our current autocomplete functions, so we added the Bootstrap 3 Typeahead (https://github.com/bassjobsen/Bootstrap-3-Typeahead) for backwards-compatibility.&lt;br /&gt;
&lt;br /&gt;
See also changes for themes below.&lt;br /&gt;
&lt;br /&gt;
=== Themes ===&lt;br /&gt;
&lt;br /&gt;
We cleaned up definitions.less and removed duplicate and obsolete definitions. If you want to override specific bootstrap default values you can do so by overriding variables from &amp;quot;/bower_components/bootstrap/less/variables.less&amp;quot; in your themes definitions.less file.&lt;br /&gt;
&lt;br /&gt;
Mixins were also removed from definitions and reside now in a seperate file &amp;quot;/apps/themes/mixins.less&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Font Awesome ===&lt;br /&gt;
&lt;br /&gt;
Font Awesome was updated to it's latest version (4.0.3).&lt;br /&gt;
You can read about the changes [http://fortawesome.github.io/Font-Awesome/whats-new/ here].&lt;br /&gt;
&lt;br /&gt;
These classes have been replaced in our core components:&lt;br /&gt;
&lt;br /&gt;
* icon-align-justify      =&amp;gt; fa fa-align-justify&lt;br /&gt;
* icon-align-left         =&amp;gt; fa fa-align-left&lt;br /&gt;
* icon-angle-left         =&amp;gt; fa fa-angle-left&lt;br /&gt;
* icon-angle-right        =&amp;gt; fa fa-angle-right&lt;br /&gt;
* icon-archive            =&amp;gt; fa fa-archive&lt;br /&gt;
* icon-arrow-down         =&amp;gt; fa fa-arrow-down&lt;br /&gt;
* icon-arrow-up           =&amp;gt; fa fa-arrow-up&lt;br /&gt;
* icon-book               =&amp;gt; fa fa-book&lt;br /&gt;
* icon-bookmark           =&amp;gt; fa fa-bookmark&lt;br /&gt;
* icon-bookmark-empty     =&amp;gt; fa fa-bookmark-o&lt;br /&gt;
* icon-caret-down         =&amp;gt; fa fa-caret-down&lt;br /&gt;
* icon-caret-right        =&amp;gt; fa fa-caret-right&lt;br /&gt;
* icon-chevron-left       =&amp;gt; fa fa-chevron-left&lt;br /&gt;
* icon-chevron-right      =&amp;gt; fa fa-chevron-right&lt;br /&gt;
* icon-circle             =&amp;gt; fa fa-circle&lt;br /&gt;
* icon-circle-arrow-right =&amp;gt; fa fa-circle-arrow-right&lt;br /&gt;
* icon-cloud-download     =&amp;gt; fa fa-cloud-download&lt;br /&gt;
* icon-cog                =&amp;gt; fa fa-cog&lt;br /&gt;
* icon-collapse-alt       =&amp;gt; fa fa-minus-square-o&lt;br /&gt;
* icon-comment-alt        =&amp;gt; fa fa-comment-o&lt;br /&gt;
* icon-download-alt       =&amp;gt; fa fa-download&lt;br /&gt;
* icon-edit               =&amp;gt; fa fa-edit&lt;br /&gt;
* icon-envelope           =&amp;gt; fa fa-envelope&lt;br /&gt;
* icon-envelope-alt       =&amp;gt; fa fa-envelope-o&lt;br /&gt;
* icon-exclamation        =&amp;gt; fa fa-exclamation&lt;br /&gt;
* icon-expand-alt         =&amp;gt; fa fa-plus-square-o&lt;br /&gt;
* icon-external-link      =&amp;gt; fa fa-external-link&lt;br /&gt;
* icon-external-link-sign =&amp;gt; fa fa-external-link-square&lt;br /&gt;
* icon-eye-open           =&amp;gt; fa fa-eye&lt;br /&gt;
* icon-file               =&amp;gt; fa fa-file&lt;br /&gt;
* icon-film               =&amp;gt; fa fa-film&lt;br /&gt;
* icon-folder-close       =&amp;gt; fa fa-folder&lt;br /&gt;
* icon-folder-open        =&amp;gt; fa fa-folder-open&lt;br /&gt;
* icon-gear               =&amp;gt; fa fa-cog&lt;br /&gt;
* icon-group              =&amp;gt; fa fa-group&lt;br /&gt;
* icon-list-alt           =&amp;gt; fa fa-list-alt&lt;br /&gt;
* icon-lock               =&amp;gt; fa fa-lock&lt;br /&gt;
* icon-magic              =&amp;gt; fa fa-magic&lt;br /&gt;
* icon-mail-forward       =&amp;gt; fa fa-mail-forward&lt;br /&gt;
* icon-minus-sign         =&amp;gt; fa fa-minus-circle&lt;br /&gt;
* icon-music              =&amp;gt; fa fa-music&lt;br /&gt;
* icon-ok                 =&amp;gt; fa fa-check&lt;br /&gt;
* icon-paper-clip         =&amp;gt; fa fa-paperclip&lt;br /&gt;
* icon-pencil             =&amp;gt; fa fa-pencil&lt;br /&gt;
* icon-picture            =&amp;gt; fa fa-picture-o&lt;br /&gt;
* icon-play               =&amp;gt; fa fa-play&lt;br /&gt;
* icon-plus-sign          =&amp;gt; fa fa-plus-circle&lt;br /&gt;
* icon-print              =&amp;gt; fa fa-print&lt;br /&gt;
* icon-qrcode             =&amp;gt; fa fa-qrcode&lt;br /&gt;
* icon-question-sign      =&amp;gt; fa fa-question-circle&lt;br /&gt;
* icon-refresh            =&amp;gt; fa fa-refresh&lt;br /&gt;
* icon-remove             =&amp;gt; fa fa-times&lt;br /&gt;
* icon-remove-circle      =&amp;gt; fa fa-times-circle&lt;br /&gt;
* icon-reorder            =&amp;gt; fa fa-bars&lt;br /&gt;
* icon-reply              =&amp;gt; fa fa-reply&lt;br /&gt;
* icon-resize-full        =&amp;gt; fa fa-expand&lt;br /&gt;
* icon-retweet            =&amp;gt; fa fa-retweet&lt;br /&gt;
* icon-rss                =&amp;gt; fa fa-rss&lt;br /&gt;
* icon-search             =&amp;gt; fa fa-search&lt;br /&gt;
* icon-shopping-cart      =&amp;gt; fa fa-shopping-cart&lt;br /&gt;
* icon-signin             =&amp;gt; fa fa-sign-in&lt;br /&gt;
* icon-spin               =&amp;gt; fa fa-spin&lt;br /&gt;
* icon-star               =&amp;gt; fa fa-star&lt;br /&gt;
* icon-table              =&amp;gt; fa fa-table&lt;br /&gt;
* icon-tag                =&amp;gt; fa fa-tag&lt;br /&gt;
* icon-th                 =&amp;gt; fa fa-th&lt;br /&gt;
* icon-th-large           =&amp;gt; fa fa-th-large&lt;br /&gt;
* icon-th-list            =&amp;gt; fa fa-th-list&lt;br /&gt;
* icon-tint               =&amp;gt; fa fa-tint&lt;br /&gt;
* icon-trash              =&amp;gt; fa fa-trash-o&lt;br /&gt;
* icon-unlock             =&amp;gt; fa fa-unlock&lt;br /&gt;
* icon-user               =&amp;gt; fa fa-user&lt;br /&gt;
&lt;br /&gt;
=== TinyMCE ===&lt;br /&gt;
&lt;br /&gt;
TinyMCE was updated from 3.4.7 to 3.5.10.&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17227</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17227"/>
		<updated>2014-03-10T11:58:51Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* appserver */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Getting started with grunt&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
== Node ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew].&lt;br /&gt;
Make sure you have checked your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt; and resolved any issues remaining.&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt; or run &amp;lt;tt&amp;gt;brew upgrade&amp;lt;/tt&amp;gt; if you already have node installed.&lt;br /&gt;
&lt;br /&gt;
Make sure you '''don't &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node'''. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value or you can set&lt;br /&gt;
it permanently by adding&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Grunt &amp;amp; Bower ==&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
== Cleaning development environment ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running &amp;lt;tt&amp;gt;git clean -ndx&amp;lt;/tt&amp;gt; this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run &amp;lt;tt&amp;gt;git clean -fdx&amp;lt;/tt&amp;gt; to really do the cleanup.&lt;br /&gt;
&lt;br /&gt;
== Installing node dependencies for OX Appsuite Frontend development ==&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,&lt;br /&gt;
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)&lt;br /&gt;
&lt;br /&gt;
== Building the UI ==&lt;br /&gt;
&lt;br /&gt;
Just run the default grunt task &amp;lt;tt&amp;gt;grunt&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local configuration ==&lt;br /&gt;
&lt;br /&gt;
You can override some options on your local build environment by creating the file &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt;. See &amp;lt;tt&amp;gt;grunt/local.conf.default.json&amp;lt;/tt&amp;gt; as a template.&lt;br /&gt;
&lt;br /&gt;
Hint: copy the default file to &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; to get started.&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
&lt;br /&gt;
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;appserver&amp;quot;: {&lt;br /&gt;
        &amp;quot;server&amp;quot;: &amp;quot;http://url.to.your.ser/appsuite/&amp;quot;,&lt;br /&gt;
        &amp;quot;verbose&amp;quot;: [&amp;quot;local:error&amp;quot;]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can run the appserver with the connect grunt task: &amp;lt;tt&amp;gt;grunt connect:server:keepalive&amp;lt;/tt&amp;gt; or in combination with the watch task: &amp;lt;tt&amp;gt;grunt connect watch&amp;lt;/tt&amp;gt;&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17226</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17226"/>
		<updated>2014-03-10T11:57:44Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* appserver */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Getting started with grunt&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
== Node ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew].&lt;br /&gt;
Make sure you have checked your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt; and resolved any issues remaining.&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt; or run &amp;lt;tt&amp;gt;brew upgrade&amp;lt;/tt&amp;gt; if you already have node installed.&lt;br /&gt;
&lt;br /&gt;
Make sure you '''don't &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node'''. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value or you can set&lt;br /&gt;
it permanently by adding&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Grunt &amp;amp; Bower ==&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
== Cleaning development environment ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running &amp;lt;tt&amp;gt;git clean -ndx&amp;lt;/tt&amp;gt; this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run &amp;lt;tt&amp;gt;git clean -fdx&amp;lt;/tt&amp;gt; to really do the cleanup.&lt;br /&gt;
&lt;br /&gt;
== Installing node dependencies for OX Appsuite Frontend development ==&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,&lt;br /&gt;
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)&lt;br /&gt;
&lt;br /&gt;
== Building the UI ==&lt;br /&gt;
&lt;br /&gt;
Just run the default grunt task &amp;lt;tt&amp;gt;grunt&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local configuration ==&lt;br /&gt;
&lt;br /&gt;
You can override some options on your local build environment by creating the file &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt;. See &amp;lt;tt&amp;gt;grunt/local.conf.default.json&amp;lt;/tt&amp;gt; as a template.&lt;br /&gt;
&lt;br /&gt;
Hint: copy the default file to &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; to get started.&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
&lt;br /&gt;
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    appserver: {&lt;br /&gt;
        server: &amp;quot;http://url.to.your.ser/appsuite/&amp;quot;,&lt;br /&gt;
        verbose: [&amp;quot;local:error&amp;quot;]&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can run the appserver with the connect grunt task: &amp;lt;tt&amp;gt;grunt connect:server:keepalive&amp;lt;/tt&amp;gt; or in combination with the watch task: &amp;lt;tt&amp;gt;grunt connect watch&amp;lt;/tt&amp;gt;&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17225</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17225"/>
		<updated>2014-03-10T11:45:08Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* appserver */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Getting started with grunt&amp;lt;/div&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
== Node ==&lt;br /&gt;
&lt;br /&gt;
=== Linux ===&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew].&lt;br /&gt;
Make sure you have checked your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt; and resolved any issues remaining.&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt; or run &amp;lt;tt&amp;gt;brew upgrade&amp;lt;/tt&amp;gt; if you already have node installed.&lt;br /&gt;
&lt;br /&gt;
Make sure you '''don't &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node'''. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value or you can set&lt;br /&gt;
it permanently by adding&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Grunt &amp;amp; Bower ==&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
== Cleaning development environment ==&lt;br /&gt;
&lt;br /&gt;
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running &amp;lt;tt&amp;gt;git clean -ndx&amp;lt;/tt&amp;gt; this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run &amp;lt;tt&amp;gt;git clean -fdx&amp;lt;/tt&amp;gt; to really do the cleanup.&lt;br /&gt;
&lt;br /&gt;
== Installing node dependencies for OX Appsuite Frontend development ==&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,&lt;br /&gt;
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)&lt;br /&gt;
&lt;br /&gt;
== Building the UI ==&lt;br /&gt;
&lt;br /&gt;
Just run the default grunt task &amp;lt;tt&amp;gt;grunt&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local configuration ==&lt;br /&gt;
&lt;br /&gt;
You can override some options on your local build environment by creating the file &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt;. See &amp;lt;tt&amp;gt;grunt/local.conf.default.json&amp;lt;/tt&amp;gt; as a template.&lt;br /&gt;
&lt;br /&gt;
Hint: copy the default file to &amp;lt;tt&amp;gt;grunt/local.conf.json&amp;lt;/tt&amp;gt; to get started.&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
&lt;br /&gt;
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    appserver: {&lt;br /&gt;
        server: 'http://url.to.your.ser/appsuite/',&lt;br /&gt;
        verbose: ['local:error']&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can run the appserver with the connect grunt task: &amp;lt;tt&amp;gt;grunt connect:server:keepalive&amp;lt;/tt&amp;gt; or in combination with the watch task: &amp;lt;tt&amp;gt;grunt connect watch&amp;lt;/tt&amp;gt;&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Changes&amp;diff=17160</id>
		<title>AppSuite:Changes</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Changes&amp;diff=17160"/>
		<updated>2014-02-25T13:41:59Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Changes&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.6.0 ==&lt;br /&gt;
&lt;br /&gt;
=== grunt ===&lt;br /&gt;
grunt has superseded jake as a build system. In this section all changes will be described.&lt;br /&gt;
&lt;br /&gt;
Instead of a shell wrapper around jake (the `build-appsuite` script), grunt should now be invoked&lt;br /&gt;
directly. The recommended workflow will be described in the next subsections, where all tasks relevant&lt;br /&gt;
for development will be explained in detail.&lt;br /&gt;
&lt;br /&gt;
Grunt is a well documented task-runner (see http://gruntjs.com) and there is a large community writing plugins.&lt;br /&gt;
Unless stated otherwise, we implemented standard grunt behaviour and rely on best-practice introduced by the&lt;br /&gt;
grunt community.&lt;br /&gt;
&lt;br /&gt;
==== local configuration ====&lt;br /&gt;
All local configuration is now done in `grunt/local.conf.json` using the JSON format. This replaces&lt;br /&gt;
the `local.conf` file, that was sourced into build-appsuite shell script. This new way is more platform&lt;br /&gt;
independent.&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
The functionality of the appserver is now provided by the grunt-contrib-connect plugin. The `connect` task&lt;br /&gt;
should be run in conjunction with the `watch` task like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect watch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will start the appserver middleware and start watching for changed files. In case you don't want this&lt;br /&gt;
behaviour, you have to start the `connect` task with the keepalive option (as documented in grunt-contrib-connect documentation):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect:server:keepalive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bower ===&lt;br /&gt;
Most of our third party front-end libraries are now managed via bower. The location for these libraries has changed from lib/ to bower_components/. These files will be handled when building by the new grunt build system and copied or concatinated accordingly.&lt;br /&gt;
&lt;br /&gt;
'''Warning: Manual editing/patching/hacking files in &amp;quot;bower_components/&amp;quot; is deprecated.'''&lt;br /&gt;
&lt;br /&gt;
''However if you are positive that there is a bug in one of these libraries a possible course of action would be to fork the package on github and commit your patch there and create an upstream pull request, in any case this should be handled by the frontend team.''&lt;br /&gt;
&lt;br /&gt;
An up-to-date list of packages maintained by bower can be reviewed in the bower.json file in the ui root.&lt;br /&gt;
&lt;br /&gt;
* jquery&lt;br /&gt;
* underscore&lt;br /&gt;
* mediaelement&lt;br /&gt;
* bootstrap&lt;br /&gt;
* bootstrap-datepicker&lt;br /&gt;
* bootstrap-typeahead&lt;br /&gt;
* font-awesome&lt;br /&gt;
* open-sans-fontface&lt;br /&gt;
* backbone&lt;br /&gt;
* backbone-validation&lt;br /&gt;
* Chart.js&lt;br /&gt;
* bigscreen&lt;br /&gt;
* jquery-placeholder&lt;br /&gt;
* jquery-imageloader&lt;br /&gt;
* textarea-helper&lt;br /&gt;
* requirejs&lt;br /&gt;
* bootstrap-accessibility-plugin&lt;br /&gt;
&lt;br /&gt;
Read more about bower [http://bower.io here].&lt;br /&gt;
&lt;br /&gt;
=== Require.js ===&lt;br /&gt;
Require.js updated from 2.1.8 to 2.1.11&lt;br /&gt;
&lt;br /&gt;
We also changed the way style files are included in the require.js define() method.&lt;br /&gt;
less and css files are now defined in the same way as javascript files, WITHOUT its extension.&lt;br /&gt;
&lt;br /&gt;
* less!io.ox/calendar/style.less =&amp;gt; less!io.ox/calendar/style&lt;br /&gt;
* css!io.ox/calendar/style.css =&amp;gt; css!io.ox/calendar/style&lt;br /&gt;
&lt;br /&gt;
=== jQuery ===&lt;br /&gt;
jQuery was updated from 2.0.3 to 2.1.0&lt;br /&gt;
&lt;br /&gt;
If you experience any issues please check [http://blog.jquery.com/2014/01/24/jquery-1-11-and-2-1-released/ this blogpost] for changes.&lt;br /&gt;
&lt;br /&gt;
=== Underscore.js ===&lt;br /&gt;
&lt;br /&gt;
Underscore was updated from Version 1.4.4 to Version 1.6&lt;br /&gt;
&lt;br /&gt;
Please read the [http://underscorejs.org/#changelog changelog].&lt;br /&gt;
&lt;br /&gt;
'''Hint:''' Look for &amp;quot;Removed the ability to call _.bindAll with no method name arguments&amp;quot;, this was pretty much the only issue we had whilst updating.&lt;br /&gt;
&lt;br /&gt;
=== Backbone.js ===&lt;br /&gt;
&lt;br /&gt;
Backbone.js was updated from 0.9.x to Version 1.0.0&lt;br /&gt;
&lt;br /&gt;
Please read the [http://backbonejs.org/#changelog changelog].&lt;br /&gt;
&lt;br /&gt;
'''Note:''' We currently cannot update to 1.1.2, which is the current version of backbone, but will do so in the near future, when we resolve our issues.&lt;br /&gt;
&lt;br /&gt;
=== Bootstrap ===&lt;br /&gt;
&lt;br /&gt;
Bootstrap was updated from 2.2.2 to 3.1.1&lt;br /&gt;
&lt;br /&gt;
Please read the [http://getbootstrap.com/migration/ Bootstrap 2 to Bootstrap 3 migration guide], as there are many significant changes, to markup and styles.&lt;br /&gt;
&lt;br /&gt;
All bootstrap plugins and our core components have been updated accordingly.&lt;br /&gt;
&lt;br /&gt;
'''Note:''' In Bootstrap 3 the typeahead plugin was dropped in favor of Twitters typeahead.js. This change would break our current autocomplete functions, so we added the Bootstrap 3 Typeahead (https://github.com/bassjobsen/Bootstrap-3-Typeahead) for backwards-compatibility.&lt;br /&gt;
&lt;br /&gt;
See also changes for themes below.&lt;br /&gt;
&lt;br /&gt;
=== Themes ===&lt;br /&gt;
&lt;br /&gt;
We cleaned up definitions.less and removed duplicate and obsolete definitions. If you want to override specific bootstrap default values you can do so by overriding variables from &amp;quot;/bower_components/bootstrap/less/variables.less&amp;quot; in your themes definitions.less file.&lt;br /&gt;
&lt;br /&gt;
Mixins were also removed from definitions and reside now in a seperate file &amp;quot;/apps/themes/mixins.less&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Font Awesome ===&lt;br /&gt;
&lt;br /&gt;
Font Awesome was updated to it's latest version (4.0.3).&lt;br /&gt;
You can read about the changes [http://fortawesome.github.io/Font-Awesome/whats-new/ here].&lt;br /&gt;
&lt;br /&gt;
These classes have been replaced in our core components:&lt;br /&gt;
&lt;br /&gt;
* icon-align-justify      =&amp;gt; fa fa-align-justify&lt;br /&gt;
* icon-align-left         =&amp;gt; fa fa-align-left&lt;br /&gt;
* icon-angle-left         =&amp;gt; fa fa-angle-left&lt;br /&gt;
* icon-angle-right        =&amp;gt; fa fa-angle-right&lt;br /&gt;
* icon-archive            =&amp;gt; fa fa-archive&lt;br /&gt;
* icon-arrow-down         =&amp;gt; fa fa-arrow-down&lt;br /&gt;
* icon-arrow-up           =&amp;gt; fa fa-arrow-up&lt;br /&gt;
* icon-book               =&amp;gt; fa fa-book&lt;br /&gt;
* icon-bookmark           =&amp;gt; fa fa-bookmark&lt;br /&gt;
* icon-bookmark-empty     =&amp;gt; fa fa-bookmark-o&lt;br /&gt;
* icon-caret-down         =&amp;gt; fa fa-caret-down&lt;br /&gt;
* icon-caret-right        =&amp;gt; fa fa-caret-right&lt;br /&gt;
* icon-chevron-left       =&amp;gt; fa fa-chevron-left&lt;br /&gt;
* icon-chevron-right      =&amp;gt; fa fa-chevron-right&lt;br /&gt;
* icon-circle             =&amp;gt; fa fa-circle&lt;br /&gt;
* icon-circle-arrow-right =&amp;gt; fa fa-circle-arrow-right&lt;br /&gt;
* icon-cloud-download     =&amp;gt; fa fa-cloud-download&lt;br /&gt;
* icon-cog                =&amp;gt; fa fa-cog&lt;br /&gt;
* icon-collapse-alt       =&amp;gt; fa fa-minus-square-o&lt;br /&gt;
* icon-comment-alt        =&amp;gt; fa fa-comment-o&lt;br /&gt;
* icon-download-alt       =&amp;gt; fa fa-download&lt;br /&gt;
* icon-edit               =&amp;gt; fa fa-edit&lt;br /&gt;
* icon-envelope           =&amp;gt; fa fa-envelope&lt;br /&gt;
* icon-envelope-alt       =&amp;gt; fa fa-envelope-o&lt;br /&gt;
* icon-exclamation        =&amp;gt; fa fa-exclamation&lt;br /&gt;
* icon-expand-alt         =&amp;gt; fa fa-plus-square-o&lt;br /&gt;
* icon-external-link      =&amp;gt; fa fa-external-link&lt;br /&gt;
* icon-external-link-sign =&amp;gt; fa fa-external-link-square&lt;br /&gt;
* icon-eye-open           =&amp;gt; fa fa-eye&lt;br /&gt;
* icon-file               =&amp;gt; fa fa-file&lt;br /&gt;
* icon-film               =&amp;gt; fa fa-film&lt;br /&gt;
* icon-folder-close       =&amp;gt; fa fa-folder&lt;br /&gt;
* icon-folder-open        =&amp;gt; fa fa-folder-open&lt;br /&gt;
* icon-gear               =&amp;gt; fa fa-cog&lt;br /&gt;
* icon-group              =&amp;gt; fa fa-group&lt;br /&gt;
* icon-list-alt           =&amp;gt; fa fa-list-alt&lt;br /&gt;
* icon-lock               =&amp;gt; fa fa-lock&lt;br /&gt;
* icon-magic              =&amp;gt; fa fa-magic&lt;br /&gt;
* icon-mail-forward       =&amp;gt; fa fa-mail-forward&lt;br /&gt;
* icon-minus-sign         =&amp;gt; fa fa-minus-circle&lt;br /&gt;
* icon-music              =&amp;gt; fa fa-music&lt;br /&gt;
* icon-ok                 =&amp;gt; fa fa-check&lt;br /&gt;
* icon-paper-clip         =&amp;gt; fa fa-paperclip&lt;br /&gt;
* icon-pencil             =&amp;gt; fa fa-pencil&lt;br /&gt;
* icon-picture            =&amp;gt; fa fa-picture-o&lt;br /&gt;
* icon-play               =&amp;gt; fa fa-play&lt;br /&gt;
* icon-plus-sign          =&amp;gt; fa fa-plus-circle&lt;br /&gt;
* icon-print              =&amp;gt; fa fa-print&lt;br /&gt;
* icon-qrcode             =&amp;gt; fa fa-qrcode&lt;br /&gt;
* icon-question-sign      =&amp;gt; fa fa-question-circle&lt;br /&gt;
* icon-refresh            =&amp;gt; fa fa-refresh&lt;br /&gt;
* icon-remove             =&amp;gt; fa fa-times&lt;br /&gt;
* icon-remove-circle      =&amp;gt; fa fa-times-circle&lt;br /&gt;
* icon-reorder            =&amp;gt; fa fa-bars&lt;br /&gt;
* icon-reply              =&amp;gt; fa fa-reply&lt;br /&gt;
* icon-resize-full        =&amp;gt; fa fa-expand&lt;br /&gt;
* icon-retweet            =&amp;gt; fa fa-retweet&lt;br /&gt;
* icon-rss                =&amp;gt; fa fa-rss&lt;br /&gt;
* icon-search             =&amp;gt; fa fa-search&lt;br /&gt;
* icon-shopping-cart      =&amp;gt; fa fa-shopping-cart&lt;br /&gt;
* icon-signin             =&amp;gt; fa fa-sign-in&lt;br /&gt;
* icon-spin               =&amp;gt; fa fa-spin&lt;br /&gt;
* icon-star               =&amp;gt; fa fa-star&lt;br /&gt;
* icon-table              =&amp;gt; fa fa-table&lt;br /&gt;
* icon-tag                =&amp;gt; fa fa-tag&lt;br /&gt;
* icon-th                 =&amp;gt; fa fa-th&lt;br /&gt;
* icon-th-large           =&amp;gt; fa fa-th-large&lt;br /&gt;
* icon-th-list            =&amp;gt; fa fa-th-list&lt;br /&gt;
* icon-tint               =&amp;gt; fa fa-tint&lt;br /&gt;
* icon-trash              =&amp;gt; fa fa-trash-o&lt;br /&gt;
* icon-unlock             =&amp;gt; fa fa-unlock&lt;br /&gt;
* icon-user               =&amp;gt; fa fa-user&lt;br /&gt;
&lt;br /&gt;
=== TinyMCE ===&lt;br /&gt;
&lt;br /&gt;
TinyMCE was updated from 3.4.7 to 3.5.10.&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Changes&amp;diff=17159</id>
		<title>AppSuite:Changes</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Changes&amp;diff=17159"/>
		<updated>2014-02-25T13:33:31Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Bower */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Changes&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.6.0 ==&lt;br /&gt;
&lt;br /&gt;
=== grunt ===&lt;br /&gt;
grunt has superseded jake as a build system. In this section all changes will be described.&lt;br /&gt;
&lt;br /&gt;
Instead of a shell wrapper around jake (the `build-appsuite` script), grunt should now be invoked&lt;br /&gt;
directly. The recommended workflow will be described in the next subsections, where all tasks relevant&lt;br /&gt;
for development will be explained in detail.&lt;br /&gt;
&lt;br /&gt;
Grunt is a well documented task-runner (see http://gruntjs.com) and there is a large community writing plugins.&lt;br /&gt;
Unless stated otherwise, we implemented standard grunt behaviour and rely on best-practice introduced by the&lt;br /&gt;
grunt community.&lt;br /&gt;
&lt;br /&gt;
==== local configuration ====&lt;br /&gt;
All local configuration is now done in `grunt/local.conf.json` using the JSON format. This replaces&lt;br /&gt;
the `local.conf` file, that was sourced into build-appsuite shell script. This new way is more platform&lt;br /&gt;
independent.&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
The functionality of the appserver is now provided by the grunt-contrib-connect plugin. The `connect` task&lt;br /&gt;
should be run in conjunction with the `watch` task like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect watch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will start the appserver middleware and start watching for changed files. In case you don't want this&lt;br /&gt;
behaviour, you have to start the `connect` task with the keepalive option (as documented in grunt-contrib-connect documentation):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect:server:keepalive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bower ===&lt;br /&gt;
Most of our third party front-end libraries are now managed via bower. The location for these libraries has changed from lib/ to bower_components/. These files will be handled when building by the new grunt build system and copied or concatinated accordingly.&lt;br /&gt;
&lt;br /&gt;
'''Warning: Manual editing/patching/hacking files in &amp;quot;bower_components/&amp;quot; is deprecated.'''&lt;br /&gt;
&lt;br /&gt;
''However if you are positive that there is a bug in one of these libraries a possible course of action would be to fork the package on github and commit your patch there and create an upstream pull request, in any case this should be handled by the frontend team.''&lt;br /&gt;
&lt;br /&gt;
An up-to-date list of packages maintained by bower can be reviewed in the bower.json file in the ui root.&lt;br /&gt;
&lt;br /&gt;
* jquery&lt;br /&gt;
* underscore&lt;br /&gt;
* mediaelement&lt;br /&gt;
* bootstrap&lt;br /&gt;
* bootstrap-datepicker&lt;br /&gt;
* bootstrap-typeahead&lt;br /&gt;
* font-awesome&lt;br /&gt;
* open-sans-fontface&lt;br /&gt;
* backbone&lt;br /&gt;
* backbone-validation&lt;br /&gt;
* Chart.js&lt;br /&gt;
* bigscreen&lt;br /&gt;
* jquery-placeholder&lt;br /&gt;
* jquery-imageloader&lt;br /&gt;
* textarea-helper&lt;br /&gt;
* requirejs&lt;br /&gt;
* bootstrap-accessibility-plugin&lt;br /&gt;
&lt;br /&gt;
Read more about bower [http://bower.io here].&lt;br /&gt;
&lt;br /&gt;
=== Require.js ===&lt;br /&gt;
Require.js updated from 2.1.8 to 2.1.11&lt;br /&gt;
&lt;br /&gt;
We also changed the way style files are included in the require.js define() method.&lt;br /&gt;
less and css files are now defined in the same way as javascript files, WITHOUT its extension.&lt;br /&gt;
&lt;br /&gt;
* less!io.ox/calendar/style.less =&amp;gt; less!io.ox/calendar/style&lt;br /&gt;
* css!io.ox/calendar/style.css =&amp;gt; css!io.ox/calendar/style&lt;br /&gt;
&lt;br /&gt;
=== jQuery ===&lt;br /&gt;
jQuery was updated from 2.0.3 to 2.1.0&lt;br /&gt;
&lt;br /&gt;
If you experience any issues please check [http://blog.jquery.com/2014/01/24/jquery-1-11-and-2-1-released/ this blogpost] for changes.&lt;br /&gt;
&lt;br /&gt;
=== Underscore.js ===&lt;br /&gt;
&lt;br /&gt;
Underscore was updated from Version 1.4.4 to Version 1.6&lt;br /&gt;
&lt;br /&gt;
Please read the [http://underscorejs.org/#changelog changelog].&lt;br /&gt;
&lt;br /&gt;
'''Hint:''' Look for &amp;quot;Removed the ability to call _.bindAll with no method name arguments&amp;quot;, this was pretty much the only issue we had whilst updating.&lt;br /&gt;
&lt;br /&gt;
=== Backbone.js ===&lt;br /&gt;
&lt;br /&gt;
Backbone.js was updated from 0.9.x to Version 1.0.0&lt;br /&gt;
&lt;br /&gt;
Please read the [http://backbonejs.org/#changelog changelog].&lt;br /&gt;
&lt;br /&gt;
**Note: We currently cannot update to 1.1.2, which is the current version of backbone, but will do so in the near future, when we resolve our issues.**&lt;br /&gt;
&lt;br /&gt;
=== Bootstrap ===&lt;br /&gt;
&lt;br /&gt;
Bootstrap was updated from 2.2.2 to 3.1.1&lt;br /&gt;
&lt;br /&gt;
Please read the [http://getbootstrap.com/migration/ Bootstrap 2 to Bootstrap 3 migration guide], as there are many significant changes, to markup and styles.&lt;br /&gt;
&lt;br /&gt;
All bootstrap plugins and our core components have been updated accordingly.&lt;br /&gt;
&lt;br /&gt;
**Note: In Bootstrap 3 the typeahead plugin was dropped in favor of Twitters typeahead.js. This change would break our current autocomplete functions, so we added the Bootstrap 3 Typeahead (https://github.com/bassjobsen/Bootstrap-3-Typeahead) for backwards-compatibility.**&lt;br /&gt;
&lt;br /&gt;
See also changes for themes below.&lt;br /&gt;
&lt;br /&gt;
=== Themes ===&lt;br /&gt;
&lt;br /&gt;
We cleaned up definitions.less and removed duplicate and obsolete definitions. If you want to override specific bootstrap default values you can do so by overriding variables from &amp;quot;/bower_components/bootstrap/less/variables.less&amp;quot; in your themes definitions.less file.&lt;br /&gt;
&lt;br /&gt;
Mixins were also removed from definitions and reside now in a seperate file &amp;quot;/apps/themes/mixins.less&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Font Awesome ===&lt;br /&gt;
&lt;br /&gt;
Font Awesome was updated to it's latest version (4.0.3).&lt;br /&gt;
You can read about the changes [http://fortawesome.github.io/Font-Awesome/whats-new/ here].&lt;br /&gt;
&lt;br /&gt;
These classes have been replaced in our core components:&lt;br /&gt;
&lt;br /&gt;
* icon-align-justify      =&amp;gt; fa fa-align-justify&lt;br /&gt;
* icon-align-left         =&amp;gt; fa fa-align-left&lt;br /&gt;
* icon-angle-left         =&amp;gt; fa fa-angle-left&lt;br /&gt;
* icon-angle-right        =&amp;gt; fa fa-angle-right&lt;br /&gt;
* icon-archive            =&amp;gt; fa fa-archive&lt;br /&gt;
* icon-arrow-down         =&amp;gt; fa fa-arrow-down&lt;br /&gt;
* icon-arrow-up           =&amp;gt; fa fa-arrow-up&lt;br /&gt;
* icon-book               =&amp;gt; fa fa-book&lt;br /&gt;
* icon-bookmark           =&amp;gt; fa fa-bookmark&lt;br /&gt;
* icon-bookmark-empty     =&amp;gt; fa fa-bookmark-o&lt;br /&gt;
* icon-caret-down         =&amp;gt; fa fa-caret-down&lt;br /&gt;
* icon-caret-right        =&amp;gt; fa fa-caret-right&lt;br /&gt;
* icon-chevron-left       =&amp;gt; fa fa-chevron-left&lt;br /&gt;
* icon-chevron-right      =&amp;gt; fa fa-chevron-right&lt;br /&gt;
* icon-circle             =&amp;gt; fa fa-circle&lt;br /&gt;
* icon-circle-arrow-right =&amp;gt; fa fa-circle-arrow-right&lt;br /&gt;
* icon-cloud-download     =&amp;gt; fa fa-cloud-download&lt;br /&gt;
* icon-cog                =&amp;gt; fa fa-cog&lt;br /&gt;
* icon-collapse-alt       =&amp;gt; fa fa-minus-square-o&lt;br /&gt;
* icon-comment-alt        =&amp;gt; fa fa-comment-o&lt;br /&gt;
* icon-download-alt       =&amp;gt; fa fa-download&lt;br /&gt;
* icon-edit               =&amp;gt; fa fa-edit&lt;br /&gt;
* icon-envelope           =&amp;gt; fa fa-envelope&lt;br /&gt;
* icon-envelope-alt       =&amp;gt; fa fa-envelope-o&lt;br /&gt;
* icon-exclamation        =&amp;gt; fa fa-exclamation&lt;br /&gt;
* icon-expand-alt         =&amp;gt; fa fa-plus-square-o&lt;br /&gt;
* icon-external-link      =&amp;gt; fa fa-external-link&lt;br /&gt;
* icon-external-link-sign =&amp;gt; fa fa-external-link-square&lt;br /&gt;
* icon-eye-open           =&amp;gt; fa fa-eye&lt;br /&gt;
* icon-file               =&amp;gt; fa fa-file&lt;br /&gt;
* icon-film               =&amp;gt; fa fa-film&lt;br /&gt;
* icon-folder-close       =&amp;gt; fa fa-folder&lt;br /&gt;
* icon-folder-open        =&amp;gt; fa fa-folder-open&lt;br /&gt;
* icon-gear               =&amp;gt; fa fa-cog&lt;br /&gt;
* icon-group              =&amp;gt; fa fa-group&lt;br /&gt;
* icon-list-alt           =&amp;gt; fa fa-list-alt&lt;br /&gt;
* icon-lock               =&amp;gt; fa fa-lock&lt;br /&gt;
* icon-magic              =&amp;gt; fa fa-magic&lt;br /&gt;
* icon-mail-forward       =&amp;gt; fa fa-mail-forward&lt;br /&gt;
* icon-minus-sign         =&amp;gt; fa fa-minus-circle&lt;br /&gt;
* icon-music              =&amp;gt; fa fa-music&lt;br /&gt;
* icon-ok                 =&amp;gt; fa fa-check&lt;br /&gt;
* icon-paper-clip         =&amp;gt; fa fa-paperclip&lt;br /&gt;
* icon-pencil             =&amp;gt; fa fa-pencil&lt;br /&gt;
* icon-picture            =&amp;gt; fa fa-picture-o&lt;br /&gt;
* icon-play               =&amp;gt; fa fa-play&lt;br /&gt;
* icon-plus-sign          =&amp;gt; fa fa-plus-circle&lt;br /&gt;
* icon-print              =&amp;gt; fa fa-print&lt;br /&gt;
* icon-qrcode             =&amp;gt; fa fa-qrcode&lt;br /&gt;
* icon-question-sign      =&amp;gt; fa fa-question-circle&lt;br /&gt;
* icon-refresh            =&amp;gt; fa fa-refresh&lt;br /&gt;
* icon-remove             =&amp;gt; fa fa-times&lt;br /&gt;
* icon-remove-circle      =&amp;gt; fa fa-times-circle&lt;br /&gt;
* icon-reorder            =&amp;gt; fa fa-bars&lt;br /&gt;
* icon-reply              =&amp;gt; fa fa-reply&lt;br /&gt;
* icon-resize-full        =&amp;gt; fa fa-expand&lt;br /&gt;
* icon-retweet            =&amp;gt; fa fa-retweet&lt;br /&gt;
* icon-rss                =&amp;gt; fa fa-rss&lt;br /&gt;
* icon-search             =&amp;gt; fa fa-search&lt;br /&gt;
* icon-shopping-cart      =&amp;gt; fa fa-shopping-cart&lt;br /&gt;
* icon-signin             =&amp;gt; fa fa-sign-in&lt;br /&gt;
* icon-spin               =&amp;gt; fa fa-spin&lt;br /&gt;
* icon-star               =&amp;gt; fa fa-star&lt;br /&gt;
* icon-table              =&amp;gt; fa fa-table&lt;br /&gt;
* icon-tag                =&amp;gt; fa fa-tag&lt;br /&gt;
* icon-th                 =&amp;gt; fa fa-th&lt;br /&gt;
* icon-th-large           =&amp;gt; fa fa-th-large&lt;br /&gt;
* icon-th-list            =&amp;gt; fa fa-th-list&lt;br /&gt;
* icon-tint               =&amp;gt; fa fa-tint&lt;br /&gt;
* icon-trash              =&amp;gt; fa fa-trash-o&lt;br /&gt;
* icon-unlock             =&amp;gt; fa fa-unlock&lt;br /&gt;
* icon-user               =&amp;gt; fa fa-user&lt;br /&gt;
&lt;br /&gt;
=== TinyMCE ===&lt;br /&gt;
&lt;br /&gt;
TinyMCE was updated from 3.4.7 to 3.5.10.&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Changes&amp;diff=17158</id>
		<title>AppSuite:Changes</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Changes&amp;diff=17158"/>
		<updated>2014-02-25T13:32:52Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Changes&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.6.0 ==&lt;br /&gt;
&lt;br /&gt;
=== grunt ===&lt;br /&gt;
grunt has superseded jake as a build system. In this section all changes will be described.&lt;br /&gt;
&lt;br /&gt;
Instead of a shell wrapper around jake (the `build-appsuite` script), grunt should now be invoked&lt;br /&gt;
directly. The recommended workflow will be described in the next subsections, where all tasks relevant&lt;br /&gt;
for development will be explained in detail.&lt;br /&gt;
&lt;br /&gt;
Grunt is a well documented task-runner (see http://gruntjs.com) and there is a large community writing plugins.&lt;br /&gt;
Unless stated otherwise, we implemented standard grunt behaviour and rely on best-practice introduced by the&lt;br /&gt;
grunt community.&lt;br /&gt;
&lt;br /&gt;
==== local configuration ====&lt;br /&gt;
All local configuration is now done in `grunt/local.conf.json` using the JSON format. This replaces&lt;br /&gt;
the `local.conf` file, that was sourced into build-appsuite shell script. This new way is more platform&lt;br /&gt;
independent.&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
The functionality of the appserver is now provided by the grunt-contrib-connect plugin. The `connect` task&lt;br /&gt;
should be run in conjunction with the `watch` task like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect watch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will start the appserver middleware and start watching for changed files. In case you don't want this&lt;br /&gt;
behaviour, you have to start the `connect` task with the keepalive option (as documented in grunt-contrib-connect documentation):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect:server:keepalive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bower ===&lt;br /&gt;
Most of our third party front-end libraries are now managed via bower. The location for these libraries has changed from lib/ to bower_components/. These files will be handled when building by the new grunt build system and copied or concatinated accordingly.&lt;br /&gt;
&lt;br /&gt;
'''Warning: Manual editing/patching/hacking files in &amp;quot;bower_components/&amp;quot; is deprecated.'''&lt;br /&gt;
&lt;br /&gt;
''However if you are positive that there is a bug in one of these libraries a possible course of action would be to fork the package on github and commit your patch there and create an upstream pull request, in any case this should be handled by the frontend team.''&lt;br /&gt;
&lt;br /&gt;
An up-to-date list of packages maintained by bower can be reviewed in the bower.json file in the ui root.&lt;br /&gt;
&lt;br /&gt;
* jquery&lt;br /&gt;
* underscore&lt;br /&gt;
* mediaelement&lt;br /&gt;
* bootstrap&lt;br /&gt;
* bootstrap-datepicker&lt;br /&gt;
* bootstrap-typeahead&lt;br /&gt;
* font-awesome&lt;br /&gt;
* open-sans-fontface&lt;br /&gt;
* backbone&lt;br /&gt;
* backbone-validation&lt;br /&gt;
* Chart.js&lt;br /&gt;
* bigscreen&lt;br /&gt;
* jquery-placeholder&lt;br /&gt;
* jquery-imageloader&lt;br /&gt;
* textarea-helper&lt;br /&gt;
* requirejs&lt;br /&gt;
* bootstrap-accessibility-plugin&lt;br /&gt;
&lt;br /&gt;
Read more about bower [http://bower.io].&lt;br /&gt;
&lt;br /&gt;
=== Require.js ===&lt;br /&gt;
Require.js updated from 2.1.8 to 2.1.11&lt;br /&gt;
&lt;br /&gt;
We also changed the way style files are included in the require.js define() method.&lt;br /&gt;
less and css files are now defined in the same way as javascript files, WITHOUT its extension.&lt;br /&gt;
&lt;br /&gt;
* less!io.ox/calendar/style.less =&amp;gt; less!io.ox/calendar/style&lt;br /&gt;
* css!io.ox/calendar/style.css =&amp;gt; css!io.ox/calendar/style&lt;br /&gt;
&lt;br /&gt;
=== jQuery ===&lt;br /&gt;
jQuery was updated from 2.0.3 to 2.1.0&lt;br /&gt;
&lt;br /&gt;
If you experience any issues please check [http://blog.jquery.com/2014/01/24/jquery-1-11-and-2-1-released/ this blogpost] for changes.&lt;br /&gt;
&lt;br /&gt;
=== Underscore.js ===&lt;br /&gt;
&lt;br /&gt;
Underscore was updated from Version 1.4.4 to Version 1.6&lt;br /&gt;
&lt;br /&gt;
Please read the [http://underscorejs.org/#changelog changelog].&lt;br /&gt;
&lt;br /&gt;
'''Hint:''' Look for &amp;quot;Removed the ability to call _.bindAll with no method name arguments&amp;quot;, this was pretty much the only issue we had whilst updating.&lt;br /&gt;
&lt;br /&gt;
=== Backbone.js ===&lt;br /&gt;
&lt;br /&gt;
Backbone.js was updated from 0.9.x to Version 1.0.0&lt;br /&gt;
&lt;br /&gt;
Please read the [http://backbonejs.org/#changelog changelog].&lt;br /&gt;
&lt;br /&gt;
**Note: We currently cannot update to 1.1.2, which is the current version of backbone, but will do so in the near future, when we resolve our issues.**&lt;br /&gt;
&lt;br /&gt;
=== Bootstrap ===&lt;br /&gt;
&lt;br /&gt;
Bootstrap was updated from 2.2.2 to 3.1.1&lt;br /&gt;
&lt;br /&gt;
Please read the [http://getbootstrap.com/migration/ Bootstrap 2 to Bootstrap 3 migration guide], as there are many significant changes, to markup and styles.&lt;br /&gt;
&lt;br /&gt;
All bootstrap plugins and our core components have been updated accordingly.&lt;br /&gt;
&lt;br /&gt;
**Note: In Bootstrap 3 the typeahead plugin was dropped in favor of Twitters typeahead.js. This change would break our current autocomplete functions, so we added the Bootstrap 3 Typeahead (https://github.com/bassjobsen/Bootstrap-3-Typeahead) for backwards-compatibility.**&lt;br /&gt;
&lt;br /&gt;
See also changes for themes below.&lt;br /&gt;
&lt;br /&gt;
=== Themes ===&lt;br /&gt;
&lt;br /&gt;
We cleaned up definitions.less and removed duplicate and obsolete definitions. If you want to override specific bootstrap default values you can do so by overriding variables from &amp;quot;/bower_components/bootstrap/less/variables.less&amp;quot; in your themes definitions.less file.&lt;br /&gt;
&lt;br /&gt;
Mixins were also removed from definitions and reside now in a seperate file &amp;quot;/apps/themes/mixins.less&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Font Awesome ===&lt;br /&gt;
&lt;br /&gt;
Font Awesome was updated to it's latest version (4.0.3).&lt;br /&gt;
You can read about the changes [http://fortawesome.github.io/Font-Awesome/whats-new/ here].&lt;br /&gt;
&lt;br /&gt;
These classes have been replaced in our core components:&lt;br /&gt;
&lt;br /&gt;
* icon-align-justify      =&amp;gt; fa fa-align-justify&lt;br /&gt;
* icon-align-left         =&amp;gt; fa fa-align-left&lt;br /&gt;
* icon-angle-left         =&amp;gt; fa fa-angle-left&lt;br /&gt;
* icon-angle-right        =&amp;gt; fa fa-angle-right&lt;br /&gt;
* icon-archive            =&amp;gt; fa fa-archive&lt;br /&gt;
* icon-arrow-down         =&amp;gt; fa fa-arrow-down&lt;br /&gt;
* icon-arrow-up           =&amp;gt; fa fa-arrow-up&lt;br /&gt;
* icon-book               =&amp;gt; fa fa-book&lt;br /&gt;
* icon-bookmark           =&amp;gt; fa fa-bookmark&lt;br /&gt;
* icon-bookmark-empty     =&amp;gt; fa fa-bookmark-o&lt;br /&gt;
* icon-caret-down         =&amp;gt; fa fa-caret-down&lt;br /&gt;
* icon-caret-right        =&amp;gt; fa fa-caret-right&lt;br /&gt;
* icon-chevron-left       =&amp;gt; fa fa-chevron-left&lt;br /&gt;
* icon-chevron-right      =&amp;gt; fa fa-chevron-right&lt;br /&gt;
* icon-circle             =&amp;gt; fa fa-circle&lt;br /&gt;
* icon-circle-arrow-right =&amp;gt; fa fa-circle-arrow-right&lt;br /&gt;
* icon-cloud-download     =&amp;gt; fa fa-cloud-download&lt;br /&gt;
* icon-cog                =&amp;gt; fa fa-cog&lt;br /&gt;
* icon-collapse-alt       =&amp;gt; fa fa-minus-square-o&lt;br /&gt;
* icon-comment-alt        =&amp;gt; fa fa-comment-o&lt;br /&gt;
* icon-download-alt       =&amp;gt; fa fa-download&lt;br /&gt;
* icon-edit               =&amp;gt; fa fa-edit&lt;br /&gt;
* icon-envelope           =&amp;gt; fa fa-envelope&lt;br /&gt;
* icon-envelope-alt       =&amp;gt; fa fa-envelope-o&lt;br /&gt;
* icon-exclamation        =&amp;gt; fa fa-exclamation&lt;br /&gt;
* icon-expand-alt         =&amp;gt; fa fa-plus-square-o&lt;br /&gt;
* icon-external-link      =&amp;gt; fa fa-external-link&lt;br /&gt;
* icon-external-link-sign =&amp;gt; fa fa-external-link-square&lt;br /&gt;
* icon-eye-open           =&amp;gt; fa fa-eye&lt;br /&gt;
* icon-file               =&amp;gt; fa fa-file&lt;br /&gt;
* icon-film               =&amp;gt; fa fa-film&lt;br /&gt;
* icon-folder-close       =&amp;gt; fa fa-folder&lt;br /&gt;
* icon-folder-open        =&amp;gt; fa fa-folder-open&lt;br /&gt;
* icon-gear               =&amp;gt; fa fa-cog&lt;br /&gt;
* icon-group              =&amp;gt; fa fa-group&lt;br /&gt;
* icon-list-alt           =&amp;gt; fa fa-list-alt&lt;br /&gt;
* icon-lock               =&amp;gt; fa fa-lock&lt;br /&gt;
* icon-magic              =&amp;gt; fa fa-magic&lt;br /&gt;
* icon-mail-forward       =&amp;gt; fa fa-mail-forward&lt;br /&gt;
* icon-minus-sign         =&amp;gt; fa fa-minus-circle&lt;br /&gt;
* icon-music              =&amp;gt; fa fa-music&lt;br /&gt;
* icon-ok                 =&amp;gt; fa fa-check&lt;br /&gt;
* icon-paper-clip         =&amp;gt; fa fa-paperclip&lt;br /&gt;
* icon-pencil             =&amp;gt; fa fa-pencil&lt;br /&gt;
* icon-picture            =&amp;gt; fa fa-picture-o&lt;br /&gt;
* icon-play               =&amp;gt; fa fa-play&lt;br /&gt;
* icon-plus-sign          =&amp;gt; fa fa-plus-circle&lt;br /&gt;
* icon-print              =&amp;gt; fa fa-print&lt;br /&gt;
* icon-qrcode             =&amp;gt; fa fa-qrcode&lt;br /&gt;
* icon-question-sign      =&amp;gt; fa fa-question-circle&lt;br /&gt;
* icon-refresh            =&amp;gt; fa fa-refresh&lt;br /&gt;
* icon-remove             =&amp;gt; fa fa-times&lt;br /&gt;
* icon-remove-circle      =&amp;gt; fa fa-times-circle&lt;br /&gt;
* icon-reorder            =&amp;gt; fa fa-bars&lt;br /&gt;
* icon-reply              =&amp;gt; fa fa-reply&lt;br /&gt;
* icon-resize-full        =&amp;gt; fa fa-expand&lt;br /&gt;
* icon-retweet            =&amp;gt; fa fa-retweet&lt;br /&gt;
* icon-rss                =&amp;gt; fa fa-rss&lt;br /&gt;
* icon-search             =&amp;gt; fa fa-search&lt;br /&gt;
* icon-shopping-cart      =&amp;gt; fa fa-shopping-cart&lt;br /&gt;
* icon-signin             =&amp;gt; fa fa-sign-in&lt;br /&gt;
* icon-spin               =&amp;gt; fa fa-spin&lt;br /&gt;
* icon-star               =&amp;gt; fa fa-star&lt;br /&gt;
* icon-table              =&amp;gt; fa fa-table&lt;br /&gt;
* icon-tag                =&amp;gt; fa fa-tag&lt;br /&gt;
* icon-th                 =&amp;gt; fa fa-th&lt;br /&gt;
* icon-th-large           =&amp;gt; fa fa-th-large&lt;br /&gt;
* icon-th-list            =&amp;gt; fa fa-th-list&lt;br /&gt;
* icon-tint               =&amp;gt; fa fa-tint&lt;br /&gt;
* icon-trash              =&amp;gt; fa fa-trash-o&lt;br /&gt;
* icon-unlock             =&amp;gt; fa fa-unlock&lt;br /&gt;
* icon-user               =&amp;gt; fa fa-user&lt;br /&gt;
&lt;br /&gt;
=== TinyMCE ===&lt;br /&gt;
&lt;br /&gt;
TinyMCE was updated from 3.4.7 to 3.5.10.&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Changes&amp;diff=17157</id>
		<title>AppSuite:Changes</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Changes&amp;diff=17157"/>
		<updated>2014-02-25T13:32:15Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Underscore.js */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Changes&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.6.0 ==&lt;br /&gt;
&lt;br /&gt;
=== grunt ===&lt;br /&gt;
grunt has superseded jake as a build system. In this section all changes will be described.&lt;br /&gt;
&lt;br /&gt;
Instead of a shell wrapper around jake (the `build-appsuite` script), grunt should now be invoked&lt;br /&gt;
directly. The recommended workflow will be described in the next subsections, where all tasks relevant&lt;br /&gt;
for development will be explained in detail.&lt;br /&gt;
&lt;br /&gt;
Grunt is a well documented task-runner (see http://gruntjs.com) and there is a large community writing plugins.&lt;br /&gt;
Unless stated otherwise, we implemented standard grunt behaviour and rely on best-practice introduced by the&lt;br /&gt;
grunt community.&lt;br /&gt;
&lt;br /&gt;
==== local configuration ====&lt;br /&gt;
All local configuration is now done in `grunt/local.conf.json` using the JSON format. This replaces&lt;br /&gt;
the `local.conf` file, that was sourced into build-appsuite shell script. This new way is more platform&lt;br /&gt;
independent.&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
The functionality of the appserver is now provided by the grunt-contrib-connect plugin. The `connect` task&lt;br /&gt;
should be run in conjunction with the `watch` task like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect watch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will start the appserver middleware and start watching for changed files. In case you don't want this&lt;br /&gt;
behaviour, you have to start the `connect` task with the keepalive option (as documented in grunt-contrib-connect documentation):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect:server:keepalive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bower ===&lt;br /&gt;
Most of our third party front-end libraries are now managed via bower. The location for these libraries has changed from lib/ to bower_components/. These files will be handled when building by the new grunt build system and copied or concatinated accordingly.&lt;br /&gt;
&lt;br /&gt;
**Warning: Manual editing/patching/hacking files in &amp;quot;bower_components/&amp;quot; is deprecated.**&lt;br /&gt;
&lt;br /&gt;
_However if you are positive that there is a bug in one of these libraries a possible course of action would be to fork the package on github and commit your patch there and create an upstream pull request, in any case this should be handled by the frontend team._&lt;br /&gt;
&lt;br /&gt;
An up-to-date list of packages maintained by bower can be reviewed in the bower.json file in the ui root.&lt;br /&gt;
&lt;br /&gt;
* jquery&lt;br /&gt;
* underscore&lt;br /&gt;
* mediaelement&lt;br /&gt;
* bootstrap&lt;br /&gt;
* bootstrap-datepicker&lt;br /&gt;
* bootstrap-typeahead&lt;br /&gt;
* font-awesome&lt;br /&gt;
* open-sans-fontface&lt;br /&gt;
* backbone&lt;br /&gt;
* backbone-validation&lt;br /&gt;
* Chart.js&lt;br /&gt;
* bigscreen&lt;br /&gt;
* jquery-placeholder&lt;br /&gt;
* jquery-imageloader&lt;br /&gt;
* textarea-helper&lt;br /&gt;
* requirejs&lt;br /&gt;
* bootstrap-accessibility-plugin&lt;br /&gt;
&lt;br /&gt;
Read more about bower [http://bower.io].&lt;br /&gt;
&lt;br /&gt;
=== Require.js ===&lt;br /&gt;
Require.js updated from 2.1.8 to 2.1.11&lt;br /&gt;
&lt;br /&gt;
We also changed the way style files are included in the require.js define() method.&lt;br /&gt;
less and css files are now defined in the same way as javascript files, WITHOUT its extension.&lt;br /&gt;
&lt;br /&gt;
* less!io.ox/calendar/style.less =&amp;gt; less!io.ox/calendar/style&lt;br /&gt;
* css!io.ox/calendar/style.css =&amp;gt; css!io.ox/calendar/style&lt;br /&gt;
&lt;br /&gt;
=== jQuery ===&lt;br /&gt;
jQuery was updated from 2.0.3 to 2.1.0&lt;br /&gt;
&lt;br /&gt;
If you experience any issues please check [http://blog.jquery.com/2014/01/24/jquery-1-11-and-2-1-released/ this blogpost] for changes.&lt;br /&gt;
&lt;br /&gt;
=== Underscore.js ===&lt;br /&gt;
&lt;br /&gt;
Underscore was updated from Version 1.4.4 to Version 1.6&lt;br /&gt;
&lt;br /&gt;
Please read the [http://underscorejs.org/#changelog changelog].&lt;br /&gt;
&lt;br /&gt;
'''Hint:''' Look for &amp;quot;Removed the ability to call _.bindAll with no method name arguments&amp;quot;, this was pretty much the only issue we had whilst updating.&lt;br /&gt;
&lt;br /&gt;
=== Backbone.js ===&lt;br /&gt;
&lt;br /&gt;
Backbone.js was updated from 0.9.x to Version 1.0.0&lt;br /&gt;
&lt;br /&gt;
Please read the [http://backbonejs.org/#changelog changelog].&lt;br /&gt;
&lt;br /&gt;
**Note: We currently cannot update to 1.1.2, which is the current version of backbone, but will do so in the near future, when we resolve our issues.**&lt;br /&gt;
&lt;br /&gt;
=== Bootstrap ===&lt;br /&gt;
&lt;br /&gt;
Bootstrap was updated from 2.2.2 to 3.1.1&lt;br /&gt;
&lt;br /&gt;
Please read the [http://getbootstrap.com/migration/ Bootstrap 2 to Bootstrap 3 migration guide], as there are many significant changes, to markup and styles.&lt;br /&gt;
&lt;br /&gt;
All bootstrap plugins and our core components have been updated accordingly.&lt;br /&gt;
&lt;br /&gt;
**Note: In Bootstrap 3 the typeahead plugin was dropped in favor of Twitters typeahead.js. This change would break our current autocomplete functions, so we added the Bootstrap 3 Typeahead (https://github.com/bassjobsen/Bootstrap-3-Typeahead) for backwards-compatibility.**&lt;br /&gt;
&lt;br /&gt;
See also changes for themes below.&lt;br /&gt;
&lt;br /&gt;
=== Themes ===&lt;br /&gt;
&lt;br /&gt;
We cleaned up definitions.less and removed duplicate and obsolete definitions. If you want to override specific bootstrap default values you can do so by overriding variables from &amp;quot;/bower_components/bootstrap/less/variables.less&amp;quot; in your themes definitions.less file.&lt;br /&gt;
&lt;br /&gt;
Mixins were also removed from definitions and reside now in a seperate file &amp;quot;/apps/themes/mixins.less&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Font Awesome ===&lt;br /&gt;
&lt;br /&gt;
Font Awesome was updated to it's latest version (4.0.3).&lt;br /&gt;
You can read about the changes [http://fortawesome.github.io/Font-Awesome/whats-new/ here].&lt;br /&gt;
&lt;br /&gt;
These classes have been replaced in our core components:&lt;br /&gt;
&lt;br /&gt;
* icon-align-justify      =&amp;gt; fa fa-align-justify&lt;br /&gt;
* icon-align-left         =&amp;gt; fa fa-align-left&lt;br /&gt;
* icon-angle-left         =&amp;gt; fa fa-angle-left&lt;br /&gt;
* icon-angle-right        =&amp;gt; fa fa-angle-right&lt;br /&gt;
* icon-archive            =&amp;gt; fa fa-archive&lt;br /&gt;
* icon-arrow-down         =&amp;gt; fa fa-arrow-down&lt;br /&gt;
* icon-arrow-up           =&amp;gt; fa fa-arrow-up&lt;br /&gt;
* icon-book               =&amp;gt; fa fa-book&lt;br /&gt;
* icon-bookmark           =&amp;gt; fa fa-bookmark&lt;br /&gt;
* icon-bookmark-empty     =&amp;gt; fa fa-bookmark-o&lt;br /&gt;
* icon-caret-down         =&amp;gt; fa fa-caret-down&lt;br /&gt;
* icon-caret-right        =&amp;gt; fa fa-caret-right&lt;br /&gt;
* icon-chevron-left       =&amp;gt; fa fa-chevron-left&lt;br /&gt;
* icon-chevron-right      =&amp;gt; fa fa-chevron-right&lt;br /&gt;
* icon-circle             =&amp;gt; fa fa-circle&lt;br /&gt;
* icon-circle-arrow-right =&amp;gt; fa fa-circle-arrow-right&lt;br /&gt;
* icon-cloud-download     =&amp;gt; fa fa-cloud-download&lt;br /&gt;
* icon-cog                =&amp;gt; fa fa-cog&lt;br /&gt;
* icon-collapse-alt       =&amp;gt; fa fa-minus-square-o&lt;br /&gt;
* icon-comment-alt        =&amp;gt; fa fa-comment-o&lt;br /&gt;
* icon-download-alt       =&amp;gt; fa fa-download&lt;br /&gt;
* icon-edit               =&amp;gt; fa fa-edit&lt;br /&gt;
* icon-envelope           =&amp;gt; fa fa-envelope&lt;br /&gt;
* icon-envelope-alt       =&amp;gt; fa fa-envelope-o&lt;br /&gt;
* icon-exclamation        =&amp;gt; fa fa-exclamation&lt;br /&gt;
* icon-expand-alt         =&amp;gt; fa fa-plus-square-o&lt;br /&gt;
* icon-external-link      =&amp;gt; fa fa-external-link&lt;br /&gt;
* icon-external-link-sign =&amp;gt; fa fa-external-link-square&lt;br /&gt;
* icon-eye-open           =&amp;gt; fa fa-eye&lt;br /&gt;
* icon-file               =&amp;gt; fa fa-file&lt;br /&gt;
* icon-film               =&amp;gt; fa fa-film&lt;br /&gt;
* icon-folder-close       =&amp;gt; fa fa-folder&lt;br /&gt;
* icon-folder-open        =&amp;gt; fa fa-folder-open&lt;br /&gt;
* icon-gear               =&amp;gt; fa fa-cog&lt;br /&gt;
* icon-group              =&amp;gt; fa fa-group&lt;br /&gt;
* icon-list-alt           =&amp;gt; fa fa-list-alt&lt;br /&gt;
* icon-lock               =&amp;gt; fa fa-lock&lt;br /&gt;
* icon-magic              =&amp;gt; fa fa-magic&lt;br /&gt;
* icon-mail-forward       =&amp;gt; fa fa-mail-forward&lt;br /&gt;
* icon-minus-sign         =&amp;gt; fa fa-minus-circle&lt;br /&gt;
* icon-music              =&amp;gt; fa fa-music&lt;br /&gt;
* icon-ok                 =&amp;gt; fa fa-check&lt;br /&gt;
* icon-paper-clip         =&amp;gt; fa fa-paperclip&lt;br /&gt;
* icon-pencil             =&amp;gt; fa fa-pencil&lt;br /&gt;
* icon-picture            =&amp;gt; fa fa-picture-o&lt;br /&gt;
* icon-play               =&amp;gt; fa fa-play&lt;br /&gt;
* icon-plus-sign          =&amp;gt; fa fa-plus-circle&lt;br /&gt;
* icon-print              =&amp;gt; fa fa-print&lt;br /&gt;
* icon-qrcode             =&amp;gt; fa fa-qrcode&lt;br /&gt;
* icon-question-sign      =&amp;gt; fa fa-question-circle&lt;br /&gt;
* icon-refresh            =&amp;gt; fa fa-refresh&lt;br /&gt;
* icon-remove             =&amp;gt; fa fa-times&lt;br /&gt;
* icon-remove-circle      =&amp;gt; fa fa-times-circle&lt;br /&gt;
* icon-reorder            =&amp;gt; fa fa-bars&lt;br /&gt;
* icon-reply              =&amp;gt; fa fa-reply&lt;br /&gt;
* icon-resize-full        =&amp;gt; fa fa-expand&lt;br /&gt;
* icon-retweet            =&amp;gt; fa fa-retweet&lt;br /&gt;
* icon-rss                =&amp;gt; fa fa-rss&lt;br /&gt;
* icon-search             =&amp;gt; fa fa-search&lt;br /&gt;
* icon-shopping-cart      =&amp;gt; fa fa-shopping-cart&lt;br /&gt;
* icon-signin             =&amp;gt; fa fa-sign-in&lt;br /&gt;
* icon-spin               =&amp;gt; fa fa-spin&lt;br /&gt;
* icon-star               =&amp;gt; fa fa-star&lt;br /&gt;
* icon-table              =&amp;gt; fa fa-table&lt;br /&gt;
* icon-tag                =&amp;gt; fa fa-tag&lt;br /&gt;
* icon-th                 =&amp;gt; fa fa-th&lt;br /&gt;
* icon-th-large           =&amp;gt; fa fa-th-large&lt;br /&gt;
* icon-th-list            =&amp;gt; fa fa-th-list&lt;br /&gt;
* icon-tint               =&amp;gt; fa fa-tint&lt;br /&gt;
* icon-trash              =&amp;gt; fa fa-trash-o&lt;br /&gt;
* icon-unlock             =&amp;gt; fa fa-unlock&lt;br /&gt;
* icon-user               =&amp;gt; fa fa-user&lt;br /&gt;
&lt;br /&gt;
=== TinyMCE ===&lt;br /&gt;
&lt;br /&gt;
TinyMCE was updated from 3.4.7 to 3.5.10.&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Changes&amp;diff=17156</id>
		<title>AppSuite:Changes</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Changes&amp;diff=17156"/>
		<updated>2014-02-25T13:31:04Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Bootstrap */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Changes&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.6.0 ==&lt;br /&gt;
&lt;br /&gt;
=== grunt ===&lt;br /&gt;
grunt has superseded jake as a build system. In this section all changes will be described.&lt;br /&gt;
&lt;br /&gt;
Instead of a shell wrapper around jake (the `build-appsuite` script), grunt should now be invoked&lt;br /&gt;
directly. The recommended workflow will be described in the next subsections, where all tasks relevant&lt;br /&gt;
for development will be explained in detail.&lt;br /&gt;
&lt;br /&gt;
Grunt is a well documented task-runner (see http://gruntjs.com) and there is a large community writing plugins.&lt;br /&gt;
Unless stated otherwise, we implemented standard grunt behaviour and rely on best-practice introduced by the&lt;br /&gt;
grunt community.&lt;br /&gt;
&lt;br /&gt;
==== local configuration ====&lt;br /&gt;
All local configuration is now done in `grunt/local.conf.json` using the JSON format. This replaces&lt;br /&gt;
the `local.conf` file, that was sourced into build-appsuite shell script. This new way is more platform&lt;br /&gt;
independent.&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
The functionality of the appserver is now provided by the grunt-contrib-connect plugin. The `connect` task&lt;br /&gt;
should be run in conjunction with the `watch` task like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect watch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will start the appserver middleware and start watching for changed files. In case you don't want this&lt;br /&gt;
behaviour, you have to start the `connect` task with the keepalive option (as documented in grunt-contrib-connect documentation):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect:server:keepalive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bower ===&lt;br /&gt;
Most of our third party front-end libraries are now managed via bower. The location for these libraries has changed from lib/ to bower_components/. These files will be handled when building by the new grunt build system and copied or concatinated accordingly.&lt;br /&gt;
&lt;br /&gt;
**Warning: Manual editing/patching/hacking files in &amp;quot;bower_components/&amp;quot; is deprecated.**&lt;br /&gt;
&lt;br /&gt;
_However if you are positive that there is a bug in one of these libraries a possible course of action would be to fork the package on github and commit your patch there and create an upstream pull request, in any case this should be handled by the frontend team._&lt;br /&gt;
&lt;br /&gt;
An up-to-date list of packages maintained by bower can be reviewed in the bower.json file in the ui root.&lt;br /&gt;
&lt;br /&gt;
* jquery&lt;br /&gt;
* underscore&lt;br /&gt;
* mediaelement&lt;br /&gt;
* bootstrap&lt;br /&gt;
* bootstrap-datepicker&lt;br /&gt;
* bootstrap-typeahead&lt;br /&gt;
* font-awesome&lt;br /&gt;
* open-sans-fontface&lt;br /&gt;
* backbone&lt;br /&gt;
* backbone-validation&lt;br /&gt;
* Chart.js&lt;br /&gt;
* bigscreen&lt;br /&gt;
* jquery-placeholder&lt;br /&gt;
* jquery-imageloader&lt;br /&gt;
* textarea-helper&lt;br /&gt;
* requirejs&lt;br /&gt;
* bootstrap-accessibility-plugin&lt;br /&gt;
&lt;br /&gt;
Read more about bower [http://bower.io].&lt;br /&gt;
&lt;br /&gt;
=== Require.js ===&lt;br /&gt;
Require.js updated from 2.1.8 to 2.1.11&lt;br /&gt;
&lt;br /&gt;
We also changed the way style files are included in the require.js define() method.&lt;br /&gt;
less and css files are now defined in the same way as javascript files, WITHOUT its extension.&lt;br /&gt;
&lt;br /&gt;
* less!io.ox/calendar/style.less =&amp;gt; less!io.ox/calendar/style&lt;br /&gt;
* css!io.ox/calendar/style.css =&amp;gt; css!io.ox/calendar/style&lt;br /&gt;
&lt;br /&gt;
=== jQuery ===&lt;br /&gt;
jQuery was updated from 2.0.3 to 2.1.0&lt;br /&gt;
&lt;br /&gt;
If you experience any issues please check [http://blog.jquery.com/2014/01/24/jquery-1-11-and-2-1-released/ this blogpost] for changes.&lt;br /&gt;
&lt;br /&gt;
=== Underscore.js ===&lt;br /&gt;
&lt;br /&gt;
Underscore was updated from Version 1.4.4 to Version 1.6&lt;br /&gt;
&lt;br /&gt;
Please read the [http://underscorejs.org/#changelog changelog].&lt;br /&gt;
&lt;br /&gt;
**Hint: Look for &amp;quot;Removed the ability to call _.bindAll with no method name arguments&amp;quot;, this was pretty much the only issue we had whilst updating.**&lt;br /&gt;
&lt;br /&gt;
=== Backbone.js ===&lt;br /&gt;
&lt;br /&gt;
Backbone.js was updated from 0.9.x to Version 1.0.0&lt;br /&gt;
&lt;br /&gt;
Please read the [http://backbonejs.org/#changelog changelog].&lt;br /&gt;
&lt;br /&gt;
**Note: We currently cannot update to 1.1.2, which is the current version of backbone, but will do so in the near future, when we resolve our issues.**&lt;br /&gt;
&lt;br /&gt;
=== Bootstrap ===&lt;br /&gt;
&lt;br /&gt;
Bootstrap was updated from 2.2.2 to 3.1.1&lt;br /&gt;
&lt;br /&gt;
Please read the [http://getbootstrap.com/migration/ Bootstrap 2 to Bootstrap 3 migration guide], as there are many significant changes, to markup and styles.&lt;br /&gt;
&lt;br /&gt;
All bootstrap plugins and our core components have been updated accordingly.&lt;br /&gt;
&lt;br /&gt;
**Note: In Bootstrap 3 the typeahead plugin was dropped in favor of Twitters typeahead.js. This change would break our current autocomplete functions, so we added the Bootstrap 3 Typeahead (https://github.com/bassjobsen/Bootstrap-3-Typeahead) for backwards-compatibility.**&lt;br /&gt;
&lt;br /&gt;
See also changes for themes below.&lt;br /&gt;
&lt;br /&gt;
=== Themes ===&lt;br /&gt;
&lt;br /&gt;
We cleaned up definitions.less and removed duplicate and obsolete definitions. If you want to override specific bootstrap default values you can do so by overriding variables from &amp;quot;/bower_components/bootstrap/less/variables.less&amp;quot; in your themes definitions.less file.&lt;br /&gt;
&lt;br /&gt;
Mixins were also removed from definitions and reside now in a seperate file &amp;quot;/apps/themes/mixins.less&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Font Awesome ===&lt;br /&gt;
&lt;br /&gt;
Font Awesome was updated to it's latest version (4.0.3).&lt;br /&gt;
You can read about the changes [http://fortawesome.github.io/Font-Awesome/whats-new/ here].&lt;br /&gt;
&lt;br /&gt;
These classes have been replaced in our core components:&lt;br /&gt;
&lt;br /&gt;
* icon-align-justify      =&amp;gt; fa fa-align-justify&lt;br /&gt;
* icon-align-left         =&amp;gt; fa fa-align-left&lt;br /&gt;
* icon-angle-left         =&amp;gt; fa fa-angle-left&lt;br /&gt;
* icon-angle-right        =&amp;gt; fa fa-angle-right&lt;br /&gt;
* icon-archive            =&amp;gt; fa fa-archive&lt;br /&gt;
* icon-arrow-down         =&amp;gt; fa fa-arrow-down&lt;br /&gt;
* icon-arrow-up           =&amp;gt; fa fa-arrow-up&lt;br /&gt;
* icon-book               =&amp;gt; fa fa-book&lt;br /&gt;
* icon-bookmark           =&amp;gt; fa fa-bookmark&lt;br /&gt;
* icon-bookmark-empty     =&amp;gt; fa fa-bookmark-o&lt;br /&gt;
* icon-caret-down         =&amp;gt; fa fa-caret-down&lt;br /&gt;
* icon-caret-right        =&amp;gt; fa fa-caret-right&lt;br /&gt;
* icon-chevron-left       =&amp;gt; fa fa-chevron-left&lt;br /&gt;
* icon-chevron-right      =&amp;gt; fa fa-chevron-right&lt;br /&gt;
* icon-circle             =&amp;gt; fa fa-circle&lt;br /&gt;
* icon-circle-arrow-right =&amp;gt; fa fa-circle-arrow-right&lt;br /&gt;
* icon-cloud-download     =&amp;gt; fa fa-cloud-download&lt;br /&gt;
* icon-cog                =&amp;gt; fa fa-cog&lt;br /&gt;
* icon-collapse-alt       =&amp;gt; fa fa-minus-square-o&lt;br /&gt;
* icon-comment-alt        =&amp;gt; fa fa-comment-o&lt;br /&gt;
* icon-download-alt       =&amp;gt; fa fa-download&lt;br /&gt;
* icon-edit               =&amp;gt; fa fa-edit&lt;br /&gt;
* icon-envelope           =&amp;gt; fa fa-envelope&lt;br /&gt;
* icon-envelope-alt       =&amp;gt; fa fa-envelope-o&lt;br /&gt;
* icon-exclamation        =&amp;gt; fa fa-exclamation&lt;br /&gt;
* icon-expand-alt         =&amp;gt; fa fa-plus-square-o&lt;br /&gt;
* icon-external-link      =&amp;gt; fa fa-external-link&lt;br /&gt;
* icon-external-link-sign =&amp;gt; fa fa-external-link-square&lt;br /&gt;
* icon-eye-open           =&amp;gt; fa fa-eye&lt;br /&gt;
* icon-file               =&amp;gt; fa fa-file&lt;br /&gt;
* icon-film               =&amp;gt; fa fa-film&lt;br /&gt;
* icon-folder-close       =&amp;gt; fa fa-folder&lt;br /&gt;
* icon-folder-open        =&amp;gt; fa fa-folder-open&lt;br /&gt;
* icon-gear               =&amp;gt; fa fa-cog&lt;br /&gt;
* icon-group              =&amp;gt; fa fa-group&lt;br /&gt;
* icon-list-alt           =&amp;gt; fa fa-list-alt&lt;br /&gt;
* icon-lock               =&amp;gt; fa fa-lock&lt;br /&gt;
* icon-magic              =&amp;gt; fa fa-magic&lt;br /&gt;
* icon-mail-forward       =&amp;gt; fa fa-mail-forward&lt;br /&gt;
* icon-minus-sign         =&amp;gt; fa fa-minus-circle&lt;br /&gt;
* icon-music              =&amp;gt; fa fa-music&lt;br /&gt;
* icon-ok                 =&amp;gt; fa fa-check&lt;br /&gt;
* icon-paper-clip         =&amp;gt; fa fa-paperclip&lt;br /&gt;
* icon-pencil             =&amp;gt; fa fa-pencil&lt;br /&gt;
* icon-picture            =&amp;gt; fa fa-picture-o&lt;br /&gt;
* icon-play               =&amp;gt; fa fa-play&lt;br /&gt;
* icon-plus-sign          =&amp;gt; fa fa-plus-circle&lt;br /&gt;
* icon-print              =&amp;gt; fa fa-print&lt;br /&gt;
* icon-qrcode             =&amp;gt; fa fa-qrcode&lt;br /&gt;
* icon-question-sign      =&amp;gt; fa fa-question-circle&lt;br /&gt;
* icon-refresh            =&amp;gt; fa fa-refresh&lt;br /&gt;
* icon-remove             =&amp;gt; fa fa-times&lt;br /&gt;
* icon-remove-circle      =&amp;gt; fa fa-times-circle&lt;br /&gt;
* icon-reorder            =&amp;gt; fa fa-bars&lt;br /&gt;
* icon-reply              =&amp;gt; fa fa-reply&lt;br /&gt;
* icon-resize-full        =&amp;gt; fa fa-expand&lt;br /&gt;
* icon-retweet            =&amp;gt; fa fa-retweet&lt;br /&gt;
* icon-rss                =&amp;gt; fa fa-rss&lt;br /&gt;
* icon-search             =&amp;gt; fa fa-search&lt;br /&gt;
* icon-shopping-cart      =&amp;gt; fa fa-shopping-cart&lt;br /&gt;
* icon-signin             =&amp;gt; fa fa-sign-in&lt;br /&gt;
* icon-spin               =&amp;gt; fa fa-spin&lt;br /&gt;
* icon-star               =&amp;gt; fa fa-star&lt;br /&gt;
* icon-table              =&amp;gt; fa fa-table&lt;br /&gt;
* icon-tag                =&amp;gt; fa fa-tag&lt;br /&gt;
* icon-th                 =&amp;gt; fa fa-th&lt;br /&gt;
* icon-th-large           =&amp;gt; fa fa-th-large&lt;br /&gt;
* icon-th-list            =&amp;gt; fa fa-th-list&lt;br /&gt;
* icon-tint               =&amp;gt; fa fa-tint&lt;br /&gt;
* icon-trash              =&amp;gt; fa fa-trash-o&lt;br /&gt;
* icon-unlock             =&amp;gt; fa fa-unlock&lt;br /&gt;
* icon-user               =&amp;gt; fa fa-user&lt;br /&gt;
&lt;br /&gt;
=== TinyMCE ===&lt;br /&gt;
&lt;br /&gt;
TinyMCE was updated from 3.4.7 to 3.5.10.&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Changes&amp;diff=17155</id>
		<title>AppSuite:Changes</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Changes&amp;diff=17155"/>
		<updated>2014-02-25T13:30:35Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Changes&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 7.6.0 ==&lt;br /&gt;
&lt;br /&gt;
=== grunt ===&lt;br /&gt;
grunt has superseded jake as a build system. In this section all changes will be described.&lt;br /&gt;
&lt;br /&gt;
Instead of a shell wrapper around jake (the `build-appsuite` script), grunt should now be invoked&lt;br /&gt;
directly. The recommended workflow will be described in the next subsections, where all tasks relevant&lt;br /&gt;
for development will be explained in detail.&lt;br /&gt;
&lt;br /&gt;
Grunt is a well documented task-runner (see http://gruntjs.com) and there is a large community writing plugins.&lt;br /&gt;
Unless stated otherwise, we implemented standard grunt behaviour and rely on best-practice introduced by the&lt;br /&gt;
grunt community.&lt;br /&gt;
&lt;br /&gt;
==== local configuration ====&lt;br /&gt;
All local configuration is now done in `grunt/local.conf.json` using the JSON format. This replaces&lt;br /&gt;
the `local.conf` file, that was sourced into build-appsuite shell script. This new way is more platform&lt;br /&gt;
independent.&lt;br /&gt;
&lt;br /&gt;
=== appserver ===&lt;br /&gt;
The functionality of the appserver is now provided by the grunt-contrib-connect plugin. The `connect` task&lt;br /&gt;
should be run in conjunction with the `watch` task like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect watch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will start the appserver middleware and start watching for changed files. In case you don't want this&lt;br /&gt;
behaviour, you have to start the `connect` task with the keepalive option (as documented in grunt-contrib-connect documentation):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
grunt connect:server:keepalive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bower ===&lt;br /&gt;
Most of our third party front-end libraries are now managed via bower. The location for these libraries has changed from lib/ to bower_components/. These files will be handled when building by the new grunt build system and copied or concatinated accordingly.&lt;br /&gt;
&lt;br /&gt;
**Warning: Manual editing/patching/hacking files in &amp;quot;bower_components/&amp;quot; is deprecated.**&lt;br /&gt;
&lt;br /&gt;
_However if you are positive that there is a bug in one of these libraries a possible course of action would be to fork the package on github and commit your patch there and create an upstream pull request, in any case this should be handled by the frontend team._&lt;br /&gt;
&lt;br /&gt;
An up-to-date list of packages maintained by bower can be reviewed in the bower.json file in the ui root.&lt;br /&gt;
&lt;br /&gt;
* jquery&lt;br /&gt;
* underscore&lt;br /&gt;
* mediaelement&lt;br /&gt;
* bootstrap&lt;br /&gt;
* bootstrap-datepicker&lt;br /&gt;
* bootstrap-typeahead&lt;br /&gt;
* font-awesome&lt;br /&gt;
* open-sans-fontface&lt;br /&gt;
* backbone&lt;br /&gt;
* backbone-validation&lt;br /&gt;
* Chart.js&lt;br /&gt;
* bigscreen&lt;br /&gt;
* jquery-placeholder&lt;br /&gt;
* jquery-imageloader&lt;br /&gt;
* textarea-helper&lt;br /&gt;
* requirejs&lt;br /&gt;
* bootstrap-accessibility-plugin&lt;br /&gt;
&lt;br /&gt;
Read more about bower [http://bower.io].&lt;br /&gt;
&lt;br /&gt;
=== Require.js ===&lt;br /&gt;
Require.js updated from 2.1.8 to 2.1.11&lt;br /&gt;
&lt;br /&gt;
We also changed the way style files are included in the require.js define() method.&lt;br /&gt;
less and css files are now defined in the same way as javascript files, WITHOUT its extension.&lt;br /&gt;
&lt;br /&gt;
* less!io.ox/calendar/style.less =&amp;gt; less!io.ox/calendar/style&lt;br /&gt;
* css!io.ox/calendar/style.css =&amp;gt; css!io.ox/calendar/style&lt;br /&gt;
&lt;br /&gt;
=== jQuery ===&lt;br /&gt;
jQuery was updated from 2.0.3 to 2.1.0&lt;br /&gt;
&lt;br /&gt;
If you experience any issues please check [http://blog.jquery.com/2014/01/24/jquery-1-11-and-2-1-released/ this blogpost] for changes.&lt;br /&gt;
&lt;br /&gt;
=== Underscore.js ===&lt;br /&gt;
&lt;br /&gt;
Underscore was updated from Version 1.4.4 to Version 1.6&lt;br /&gt;
&lt;br /&gt;
Please read the [http://underscorejs.org/#changelog changelog].&lt;br /&gt;
&lt;br /&gt;
**Hint: Look for &amp;quot;Removed the ability to call _.bindAll with no method name arguments&amp;quot;, this was pretty much the only issue we had whilst updating.**&lt;br /&gt;
&lt;br /&gt;
=== Backbone.js ===&lt;br /&gt;
&lt;br /&gt;
Backbone.js was updated from 0.9.x to Version 1.0.0&lt;br /&gt;
&lt;br /&gt;
Please read the [http://backbonejs.org/#changelog changelog].&lt;br /&gt;
&lt;br /&gt;
**Note: We currently cannot update to 1.1.2, which is the current version of backbone, but will do so in the near future, when we resolve our issues.**&lt;br /&gt;
&lt;br /&gt;
=== Bootstrap ===&lt;br /&gt;
&lt;br /&gt;
Bootstrap was updated from 2.2.2 to 3.1.1&lt;br /&gt;
&lt;br /&gt;
Please read the [http://getbootstrap.com/getting-started/#migration Bootstrap 2 to Bootstrap 3 migration guide], as there are many significant changes, to markup and styles.&lt;br /&gt;
&lt;br /&gt;
All bootstrap plugins and our core components have been updated accordingly.&lt;br /&gt;
&lt;br /&gt;
**Note: In Bootstrap 3 the typeahead plugin was dropped in favor of Twitters typeahead.js. This change would break our current autocomplete functions, so we added the Bootstrap 3 Typeahead (https://github.com/bassjobsen/Bootstrap-3-Typeahead) for backwards-compatibility.**&lt;br /&gt;
&lt;br /&gt;
See also changes for themes below.&lt;br /&gt;
&lt;br /&gt;
=== Themes ===&lt;br /&gt;
&lt;br /&gt;
We cleaned up definitions.less and removed duplicate and obsolete definitions. If you want to override specific bootstrap default values you can do so by overriding variables from &amp;quot;/bower_components/bootstrap/less/variables.less&amp;quot; in your themes definitions.less file.&lt;br /&gt;
&lt;br /&gt;
Mixins were also removed from definitions and reside now in a seperate file &amp;quot;/apps/themes/mixins.less&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Font Awesome ===&lt;br /&gt;
&lt;br /&gt;
Font Awesome was updated to it's latest version (4.0.3).&lt;br /&gt;
You can read about the changes [http://fortawesome.github.io/Font-Awesome/whats-new/ here].&lt;br /&gt;
&lt;br /&gt;
These classes have been replaced in our core components:&lt;br /&gt;
&lt;br /&gt;
* icon-align-justify      =&amp;gt; fa fa-align-justify&lt;br /&gt;
* icon-align-left         =&amp;gt; fa fa-align-left&lt;br /&gt;
* icon-angle-left         =&amp;gt; fa fa-angle-left&lt;br /&gt;
* icon-angle-right        =&amp;gt; fa fa-angle-right&lt;br /&gt;
* icon-archive            =&amp;gt; fa fa-archive&lt;br /&gt;
* icon-arrow-down         =&amp;gt; fa fa-arrow-down&lt;br /&gt;
* icon-arrow-up           =&amp;gt; fa fa-arrow-up&lt;br /&gt;
* icon-book               =&amp;gt; fa fa-book&lt;br /&gt;
* icon-bookmark           =&amp;gt; fa fa-bookmark&lt;br /&gt;
* icon-bookmark-empty     =&amp;gt; fa fa-bookmark-o&lt;br /&gt;
* icon-caret-down         =&amp;gt; fa fa-caret-down&lt;br /&gt;
* icon-caret-right        =&amp;gt; fa fa-caret-right&lt;br /&gt;
* icon-chevron-left       =&amp;gt; fa fa-chevron-left&lt;br /&gt;
* icon-chevron-right      =&amp;gt; fa fa-chevron-right&lt;br /&gt;
* icon-circle             =&amp;gt; fa fa-circle&lt;br /&gt;
* icon-circle-arrow-right =&amp;gt; fa fa-circle-arrow-right&lt;br /&gt;
* icon-cloud-download     =&amp;gt; fa fa-cloud-download&lt;br /&gt;
* icon-cog                =&amp;gt; fa fa-cog&lt;br /&gt;
* icon-collapse-alt       =&amp;gt; fa fa-minus-square-o&lt;br /&gt;
* icon-comment-alt        =&amp;gt; fa fa-comment-o&lt;br /&gt;
* icon-download-alt       =&amp;gt; fa fa-download&lt;br /&gt;
* icon-edit               =&amp;gt; fa fa-edit&lt;br /&gt;
* icon-envelope           =&amp;gt; fa fa-envelope&lt;br /&gt;
* icon-envelope-alt       =&amp;gt; fa fa-envelope-o&lt;br /&gt;
* icon-exclamation        =&amp;gt; fa fa-exclamation&lt;br /&gt;
* icon-expand-alt         =&amp;gt; fa fa-plus-square-o&lt;br /&gt;
* icon-external-link      =&amp;gt; fa fa-external-link&lt;br /&gt;
* icon-external-link-sign =&amp;gt; fa fa-external-link-square&lt;br /&gt;
* icon-eye-open           =&amp;gt; fa fa-eye&lt;br /&gt;
* icon-file               =&amp;gt; fa fa-file&lt;br /&gt;
* icon-film               =&amp;gt; fa fa-film&lt;br /&gt;
* icon-folder-close       =&amp;gt; fa fa-folder&lt;br /&gt;
* icon-folder-open        =&amp;gt; fa fa-folder-open&lt;br /&gt;
* icon-gear               =&amp;gt; fa fa-cog&lt;br /&gt;
* icon-group              =&amp;gt; fa fa-group&lt;br /&gt;
* icon-list-alt           =&amp;gt; fa fa-list-alt&lt;br /&gt;
* icon-lock               =&amp;gt; fa fa-lock&lt;br /&gt;
* icon-magic              =&amp;gt; fa fa-magic&lt;br /&gt;
* icon-mail-forward       =&amp;gt; fa fa-mail-forward&lt;br /&gt;
* icon-minus-sign         =&amp;gt; fa fa-minus-circle&lt;br /&gt;
* icon-music              =&amp;gt; fa fa-music&lt;br /&gt;
* icon-ok                 =&amp;gt; fa fa-check&lt;br /&gt;
* icon-paper-clip         =&amp;gt; fa fa-paperclip&lt;br /&gt;
* icon-pencil             =&amp;gt; fa fa-pencil&lt;br /&gt;
* icon-picture            =&amp;gt; fa fa-picture-o&lt;br /&gt;
* icon-play               =&amp;gt; fa fa-play&lt;br /&gt;
* icon-plus-sign          =&amp;gt; fa fa-plus-circle&lt;br /&gt;
* icon-print              =&amp;gt; fa fa-print&lt;br /&gt;
* icon-qrcode             =&amp;gt; fa fa-qrcode&lt;br /&gt;
* icon-question-sign      =&amp;gt; fa fa-question-circle&lt;br /&gt;
* icon-refresh            =&amp;gt; fa fa-refresh&lt;br /&gt;
* icon-remove             =&amp;gt; fa fa-times&lt;br /&gt;
* icon-remove-circle      =&amp;gt; fa fa-times-circle&lt;br /&gt;
* icon-reorder            =&amp;gt; fa fa-bars&lt;br /&gt;
* icon-reply              =&amp;gt; fa fa-reply&lt;br /&gt;
* icon-resize-full        =&amp;gt; fa fa-expand&lt;br /&gt;
* icon-retweet            =&amp;gt; fa fa-retweet&lt;br /&gt;
* icon-rss                =&amp;gt; fa fa-rss&lt;br /&gt;
* icon-search             =&amp;gt; fa fa-search&lt;br /&gt;
* icon-shopping-cart      =&amp;gt; fa fa-shopping-cart&lt;br /&gt;
* icon-signin             =&amp;gt; fa fa-sign-in&lt;br /&gt;
* icon-spin               =&amp;gt; fa fa-spin&lt;br /&gt;
* icon-star               =&amp;gt; fa fa-star&lt;br /&gt;
* icon-table              =&amp;gt; fa fa-table&lt;br /&gt;
* icon-tag                =&amp;gt; fa fa-tag&lt;br /&gt;
* icon-th                 =&amp;gt; fa fa-th&lt;br /&gt;
* icon-th-large           =&amp;gt; fa fa-th-large&lt;br /&gt;
* icon-th-list            =&amp;gt; fa fa-th-list&lt;br /&gt;
* icon-tint               =&amp;gt; fa fa-tint&lt;br /&gt;
* icon-trash              =&amp;gt; fa fa-trash-o&lt;br /&gt;
* icon-unlock             =&amp;gt; fa fa-unlock&lt;br /&gt;
* icon-user               =&amp;gt; fa fa-user&lt;br /&gt;
&lt;br /&gt;
=== TinyMCE ===&lt;br /&gt;
&lt;br /&gt;
TinyMCE was updated from 3.4.7 to 3.5.10.&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17141</id>
		<title>AppSuite:GettingStartedWithGrunt</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&amp;diff=17141"/>
		<updated>2014-02-24T15:02:52Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: Created page with &amp;quot;= Getting started with grunt =  == Prerequisites ==  === Node ===  ==== Linux ====  Install the default nodejs of your distribution via your favourite package/ports manager an...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Getting started with grunt =&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Node ===&lt;br /&gt;
&lt;br /&gt;
==== Linux ====&lt;br /&gt;
&lt;br /&gt;
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.&lt;br /&gt;
&lt;br /&gt;
==== Windows ====&lt;br /&gt;
&lt;br /&gt;
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.&lt;br /&gt;
It is recommended to restart after the installation, as paths may not be up-to-date.&lt;br /&gt;
&lt;br /&gt;
==== Mac OS X ====&lt;br /&gt;
&lt;br /&gt;
We strongly encourage you to use [http://brew.sh/ homebrew].&lt;br /&gt;
Make sure you have checked your homebrew installation with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Install node via &amp;lt;tt&amp;gt;brew install node&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Make sure you don't &amp;lt;tt&amp;gt;sudo&amp;lt;/tt&amp;gt; anything related to node. If you think you have to, you are doing something wrong and are probably dealing with a broken homebrew/macports installation! If this is the case, the easiest way of resolving this is completely deleting the homebrew (and if present macports) directories and (re)installing homebrew.&lt;br /&gt;
&lt;br /&gt;
The default system's max opened file limit in mac os x is very low (256), in order to use grunt watch, it needs to be increased.&lt;br /&gt;
&lt;br /&gt;
You can either set this in your shell via &amp;lt;tt&amp;gt;ulimit -n 8192&amp;lt;/tt&amp;gt; to a sensible value or you can set&lt;br /&gt;
it permanently by adding&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;limit maxfiles 8192 20480&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
to &amp;lt;tt&amp;gt;/etc/launchd.conf&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Grunt &amp;amp; Bower ===&lt;br /&gt;
&lt;br /&gt;
With &amp;lt;tt&amp;gt;npm install -g bower grunt-cli&amp;lt;/tt&amp;gt; you can install the global npm dependencies needed for grunt and bower.&lt;br /&gt;
&lt;br /&gt;
=== Installing node dependencies for OX Appsuite Frontend development ===&lt;br /&gt;
&lt;br /&gt;
Change to the ui directory of your git workdirectory and run &amp;lt;tt&amp;gt;npm install&amp;lt;/tt&amp;gt;.&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:RunTests&amp;diff=15712</id>
		<title>AppSuite:RunTests</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:RunTests&amp;diff=15712"/>
		<updated>2013-09-13T13:28:25Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Stability-experimental}}&lt;br /&gt;
&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Running the ui tests&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
This article explains the test system of the frontend. It is aimed at developers that want to work with the frontend, be it creating new plugins or applications or modifying existing code using BDD. Bringing a BDD testing infrastructure to the frontend is still a work in progress and subject to (breaking) changes. Please contribute to the stability by reporting any issues or ideas to [[User:J.ohny.b|me]].&lt;br /&gt;
&lt;br /&gt;
== Libraries ==&lt;br /&gt;
&lt;br /&gt;
* [http://pivotal.github.io/jasmine/ Jasmine] - JS BDD framework&lt;br /&gt;
* [http://sinonjs.org/  Sinon.JS] - Standalone test spies, stubs and mocks for JavaScript.&lt;br /&gt;
* [http://karma-runner.github.io/  Karma Runner] - test runner&lt;br /&gt;
&lt;br /&gt;
== Setting up your system ==&lt;br /&gt;
&lt;br /&gt;
You need at least ''node'' version 0.8 to use the latest version of ''karma'', which we need. To install the (latest stable version of) karma runner, run&lt;br /&gt;
&lt;br /&gt;
    npm install -g karma&lt;br /&gt;
&lt;br /&gt;
in a shell. You might need admin rights to do that, use ''sudo'' if needed.&lt;br /&gt;
&lt;br /&gt;
If you have karma installed already you may have to run&lt;br /&gt;
&lt;br /&gt;
    npm -g update&lt;br /&gt;
&lt;br /&gt;
at least once, if the karma dependencies fail at least 2 times.&lt;br /&gt;
&lt;br /&gt;
After that you may have to add the path to your node modules to the ''local.conf'' file.&lt;br /&gt;
&lt;br /&gt;
    export NODE_PATH=&amp;quot;/usr/local/lib/node_modules:/usr/local/share/npm/lib/node_modules:$NODE_PATH&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Running the tests ==&lt;br /&gt;
&lt;br /&gt;
Running the tests is pretty easy. For now, in the ''ui'' directory of your sources start the karma server with this jake task&lt;br /&gt;
&lt;br /&gt;
    ./build.sh testserver&lt;br /&gt;
&lt;br /&gt;
After that, open up a browser and point it to http://localhost:9876/.&lt;br /&gt;
&lt;br /&gt;
If you don’t want to use the appserver proxy but your own backend, you need to adjust the proxies section in your ''ui/karma.conf.js''. The appserver or a running backend won’t be needed in the future.&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Development process]]&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Supported_file_types&amp;diff=14942</id>
		<title>AppSuite:Supported file types</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Supported_file_types&amp;diff=14942"/>
		<updated>2013-07-08T11:25:23Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Mediaplayer */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;AppSuite allows you to preview, view and edit files. A common question is what files are supported. The following list is not definitive&lt;br /&gt;
&lt;br /&gt;
== Mediaplayer ==&lt;br /&gt;
The AppSuite &amp;quot;media player&amp;quot; uses the native html 5 video playback of each browser. The actual file support  depends on the user's browser, so a definitive list can not be given. Here is an attempt:&lt;br /&gt;
&lt;br /&gt;
=== Video ===&lt;br /&gt;
&lt;br /&gt;
* Chrome: m4v, ogv, webm&lt;br /&gt;
* Safari: m4v&lt;br /&gt;
* IE: m4v&lt;br /&gt;
* Firefox: ogv, webm&lt;br /&gt;
&lt;br /&gt;
=== Audio ===&lt;br /&gt;
&lt;br /&gt;
For audio files a flash/silverlight fallback is used if a browser is not capable of native mp3 playback like Firefox.&lt;br /&gt;
&lt;br /&gt;
* Chrome: mp3, wav, m4a, m4b, ogg&lt;br /&gt;
* Safari: mp3, wav, m4a, m4b, aac&lt;br /&gt;
* IE: mp3, wav, m4a, m4b&lt;br /&gt;
* Firefox: mp3, wav, ogg&lt;br /&gt;
&lt;br /&gt;
== Preview ==&lt;br /&gt;
* pdf&lt;br /&gt;
* docx&lt;br /&gt;
* odt&lt;br /&gt;
* xlsx&lt;br /&gt;
&lt;br /&gt;
== Documents ==&lt;br /&gt;
* docx&lt;br /&gt;
* odt&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Server]]&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Supported_file_types&amp;diff=14941</id>
		<title>AppSuite:Supported file types</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Supported_file_types&amp;diff=14941"/>
		<updated>2013-07-08T11:20:53Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Mediaplayer */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;AppSuite allows you to preview, view and edit files. A common question is what files are supported. The following list is not definitive&lt;br /&gt;
&lt;br /&gt;
== Mediaplayer ==&lt;br /&gt;
The AppSuite &amp;quot;media player&amp;quot; technically consists of several technologies stacked onto each other that serve as fallbacks. Most of them depend on the user's system setup, so a definitive list can not be given. Here is an attempt:&lt;br /&gt;
&lt;br /&gt;
=== Video ===&lt;br /&gt;
&lt;br /&gt;
* Chrome: m4v, ogv, webm&lt;br /&gt;
* Safari: m4v&lt;br /&gt;
* IE: m4v&lt;br /&gt;
* Firefox: ogv, webm&lt;br /&gt;
&lt;br /&gt;
=== Audio ===&lt;br /&gt;
&lt;br /&gt;
* Chrome: mp3, wav, m4a, m4b, ogg&lt;br /&gt;
* Safari: mp3, wav, m4a, m4b, aac&lt;br /&gt;
* IE: mp3, wav, m4a, m4b&lt;br /&gt;
* Firefox: mp3, wav, ogg&lt;br /&gt;
&lt;br /&gt;
== Preview ==&lt;br /&gt;
* pdf&lt;br /&gt;
* docx&lt;br /&gt;
* odt&lt;br /&gt;
* xlsx&lt;br /&gt;
&lt;br /&gt;
== Documents ==&lt;br /&gt;
* docx&lt;br /&gt;
* odt&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Server]]&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Supported_file_types&amp;diff=14940</id>
		<title>AppSuite:Supported file types</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Supported_file_types&amp;diff=14940"/>
		<updated>2013-07-08T11:19:21Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Mediaplayer */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;AppSuite allows you to preview, view and edit files. A common question is what files are supported. The following list is not definitive&lt;br /&gt;
&lt;br /&gt;
== Mediaplayer ==&lt;br /&gt;
The AppSuite &amp;quot;media player&amp;quot; technically consists of several technologies stacked onto each other that serve as fallbacks. Most of them depend on the user's system setup, so a definitive list can not be given. Here is an attempt:&lt;br /&gt;
&lt;br /&gt;
* Chrome: m4v, ogv, webm&lt;br /&gt;
* Safari: m4v&lt;br /&gt;
* IE: m4v&lt;br /&gt;
* Firefox: ogv, webm&lt;br /&gt;
&lt;br /&gt;
== Preview ==&lt;br /&gt;
* pdf&lt;br /&gt;
* docx&lt;br /&gt;
* odt&lt;br /&gt;
* xlsx&lt;br /&gt;
&lt;br /&gt;
== Documents ==&lt;br /&gt;
* docx&lt;br /&gt;
* odt&lt;br /&gt;
&lt;br /&gt;
[[Category:AppSuite]]&lt;br /&gt;
[[Category:UI]]&lt;br /&gt;
[[Category:Server]]&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:UI_remote_debugging_android_mac&amp;diff=14844</id>
		<title>AppSuite:UI remote debugging android mac</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:UI_remote_debugging_android_mac&amp;diff=14844"/>
		<updated>2013-06-24T12:46:36Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: Created page with &amp;quot;{{Stability-experimental}}  &amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;How to setup remote debugging for chrome on android devices with a mac&amp;lt;/div&amp;gt;  ==Prerequisites==  You need the latest X-Code and ...&amp;quot;&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;How to setup remote debugging for chrome on android devices with a mac&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Prerequisites==&lt;br /&gt;
&lt;br /&gt;
You need the latest X-Code and a working setup of the latest Homebrew. Check with &amp;lt;tt&amp;gt;brew doctor&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Setup==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ brew install android-sdk&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Add this to your shells rc (e.g. .bashrc or .zshrc)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export ANDROID_HOME=/usr/local/opt/android-sdk&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Launch the &amp;quot;Android SDK Manager&amp;quot;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ android&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Check &amp;lt;tt&amp;gt;Android SDK Platform-tools&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;Android Support Library&amp;lt;/tt&amp;gt; uncheck everything else, except if you plan on using other components of the SDK.&lt;br /&gt;
&lt;br /&gt;
Add this to your shells rc (e.g. .bashrc or .zshrc) for convenience:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
alias chrome-android=&amp;quot;adb forward tcp:9222 localabstract:chrome_devtools_remote&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Usage==&lt;br /&gt;
&lt;br /&gt;
Connect an Android Device via USB.&lt;br /&gt;
&lt;br /&gt;
Open a terminal and enter &amp;quot;chrome-android&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Open Chrome on your device.&lt;br /&gt;
&lt;br /&gt;
Visit &amp;lt;tt&amp;gt;localhost:9222&amp;lt;/tt&amp;gt; on your desktop machine for remote debugging.&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Mobile&amp;diff=14843</id>
		<title>AppSuite:Mobile</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Mobile&amp;diff=14843"/>
		<updated>2013-06-24T12:40:29Z</updated>

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

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

		<summary type="html">&lt;p&gt;David.bauer: /* How to enable/disable the mediaplayer */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Mediaplayer&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
==Browser and OS support==&lt;br /&gt;
We use mediaelement.js[http://mediaelementjs.com/] as HTML5 media player, which has a flash and silverlight fallback.&lt;br /&gt;
&lt;br /&gt;
Every Browser supports a different set of supported codecs and container formats, follow this link to see which ones: http://mediaelementjs.com/#devices&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==How to enable/disable the mediaplayer==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;settings!io.ox/files&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;audioEnabled [true|false]&amp;lt;/tt&amp;gt;&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Media_player&amp;diff=13870</id>
		<title>AppSuite:Media player</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:Media_player&amp;diff=13870"/>
		<updated>2013-04-17T09:28:52Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: Created page with &amp;quot;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Mediaplayer&amp;lt;/div&amp;gt;  __TOC__  ==Browser and OS support== We use mediaelement.js[http://mediaelementjs.com/] as HTML5 media player, which has a flash and silve...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;Mediaplayer&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
==Browser and OS support==&lt;br /&gt;
We use mediaelement.js[http://mediaelementjs.com/] as HTML5 media player, which has a flash and silverlight fallback.&lt;br /&gt;
&lt;br /&gt;
Every Browser supports a different set of supported codecs and container formats, follow this link to see which ones: http://mediaelementjs.com/#devices&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==How to enable/disable the mediaplayer==&lt;br /&gt;
&lt;br /&gt;
settings!io.ox/files audioEnabled&lt;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
	<entry>
		<id>https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:UI&amp;diff=13869</id>
		<title>AppSuite:UI</title>
		<link rel="alternate" type="text/html" href="https://wiki.open-xchange.com/wiki/index.php?title=AppSuite:UI&amp;diff=13869"/>
		<updated>2013-04-17T09:15:33Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* How-to articles */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;AppSuite UI&amp;lt;/div&amp;gt;&lt;br /&gt;
This page contains articles about the inner workings of the UI. &lt;br /&gt;
It is aimed at software developers that want to improve on existing features, implement extensions or just gain a general understanding.&lt;br /&gt;
&lt;br /&gt;
== Getting started ==&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:Apache Configuration | Apache Configuration]]&lt;br /&gt;
&lt;br /&gt;
== How-to articles ==&lt;br /&gt;
* [[AppSuite:Date_and_time|Date and time]]&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:Files App Actions|Adding actions to the files app]]&lt;br /&gt;
* [[AppSuite:i18n | Internationalization (i18n)]]&lt;br /&gt;
* [[AppSuite:Theming | Theming]]&lt;br /&gt;
* [[AppSuite:Upsell | Upsell]]&lt;br /&gt;
* [[AppSuite:Writing a portal plugin | Writing a portal plugin]]&lt;br /&gt;
* [[AppSuite:Writing a simple application | Writing a simple application]]&lt;br /&gt;
* [[AppSuite:Writing a notification area plugin | Writing a plugin for the notification area]]&lt;br /&gt;
* [[AppSuite:UI manifests explained | UI manifests explained]] - Vik, Cisco?&lt;br /&gt;
* [[AppSuite:VGrid | VGrid]]&lt;br /&gt;
* [[AppSuite:Mediaplayer | Mediaplayer]]&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:UI build system| The UI build system]]&lt;br /&gt;
* All articles regarding the UI are filed in the  category [[:Category:UI]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Ui]][[Category:AppSuite]][[Category:Overview]]&lt;/div&gt;</summary>
		<author><name>David.bauer</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=13794</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=13794"/>
		<updated>2013-04-12T11:32:33Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Adding a Sidepopup */&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;
'''Abstract:''' 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 ''apps.plugins/notifications/'' .&lt;br /&gt;
For this tutorial we will create ''plugins/notifications/tutorial/'' .&lt;br /&gt;
&lt;br /&gt;
Now add a new file to your folder and name it ''register.js'' .&lt;br /&gt;
Then add the basic markup such as copyright, define for ''require.js'', 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('plugins/notifications/tutorial/register',&lt;br /&gt;
    ['io.ox/core/extensions'], function (ext) {&lt;br /&gt;
&lt;br /&gt;
    'use strict';&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;
''Note: We need to use extensions so we need to require the needed resources with&lt;br /&gt;
  ['io.ox/core/extensions'], function (ext)&lt;br /&gt;
as seen above.''&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 ''manifest.json'' 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 ''src/manifests.js''&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: ['io.ox/core/notifications'],&lt;br /&gt;
    path: 'plugins/notifications/tutorial/register'&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 ''io.ox/core/notifications/register'' .&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('io.ox/core/notifications/register').extend({&lt;br /&gt;
    id: 'tutorial',//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('io.ox/tutorial', NotificationsView);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''io.ox/tutorial'' is the id the controller should use to refer to our plugin and ''NotificationsView'' 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: 'notifications',&lt;br /&gt;
    id: 'io-ox-notifications-tutorial',&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;
''Note: Events and render function are empty at the moment, but we will fix that soon.''&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('io.ox/core/notifications/tutorial/header').invoke('draw', this.$el.empty(), baton);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''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.''&lt;br /&gt;
&lt;br /&gt;
Now extend the point with a simple draw method for our header and container.&lt;br /&gt;
''this'' 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('io.ox/core/notifications/tutorial/header').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
            $('&amp;lt;legend class=&amp;quot;section-title&amp;quot;&amp;gt;').text('Hello World'),//header&lt;br /&gt;
            $('&amp;lt;div class=&amp;quot;notifications&amp;quot;&amp;gt;')//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 ''&amp;amp;customManifests=true'' to the url to load your plugin and reload the page.&lt;br /&gt;
&lt;br /&gt;
''Note: The notification area is loaded with a delay, so you have to wait a bit.''&lt;br /&gt;
&lt;br /&gt;
After it'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: 'I am a notification', description: 'Hello world!'},&lt;br /&gt;
                         {title: 'I am a notification too', description: 'Hooray!'}];&lt;br /&gt;
            //trigger the event to add them&lt;br /&gt;
            $(myEventTriggerer).trigger('set-tutorial-notification', [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 ''notifications.collection''.&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 ''notifications.collection.reset(new Backbone.Model());'' 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;
''Note: notifications.collection.add() does not trigger the add event. You need to do this manually, this seems to be a backbone issue.''&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('set-tutorial-notification', 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('io.ox/core/notifications/tutorial/item')&lt;br /&gt;
        .invoke('draw', this.$('.notifications'), 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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//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 ''data-cid'' 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;
    'click .item': 'openPopup',&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This calls the ''openPopup'' 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 ''openPopup'' 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 = $('#io-ox-notifications-overlay'),//the overlay we draw our sidepopup in&lt;br /&gt;
        cid = $(e.currentTarget).attr('data-cid'),//getting the right model&lt;br /&gt;
        model = this.collection.getByCid(cid);&lt;br /&gt;
            &lt;br /&gt;
    require(['io.ox/core/tk/dialogs'], function (dialogs) {//require dialogs&lt;br /&gt;
        //create the popup&lt;br /&gt;
        new dialogs.SidePopup({ arrow: false, side: 'right' })&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($('&amp;lt;div&amp;gt;').text(model.get('title')),&lt;br /&gt;
                             $('&amp;lt;br&amp;gt;'),&lt;br /&gt;
                             $('&amp;lt;div&amp;gt;').text(model.get('description')));&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. ''overlay'' is the div we append our popup to, ''cid'' is the cid we added as an attribute to our notification div earlier and ''model'' is the model we get from our collection by this cid.&lt;br /&gt;
After this we require our ''dialogs'' 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't want our notifications to stay there forever, so we need a way to remove them.&lt;br /&gt;
Let'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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//some text to fill it&lt;br /&gt;
                $('&amp;lt;button class=&amp;quot;mybutton btn btn-primary&amp;quot; data-action=&amp;quot;close&amp;quot;&amp;gt;').text('close')//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;
    'click .item': 'openPopup',&lt;br /&gt;
    'click [data-action=&amp;quot;close&amp;quot;]': 'closeNotification'&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('.item').attr('data-cid'),//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;
''e.stopPropagation()'' 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;/div&gt;</summary>
		<author><name>David.bauer</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=13793</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=13793"/>
		<updated>2013-04-12T11:31:55Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Registering your plugin */&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;
'''Abstract:''' 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 ''apps.plugins/notifications/'' .&lt;br /&gt;
For this tutorial we will create ''plugins/notifications/tutorial/'' .&lt;br /&gt;
&lt;br /&gt;
Now add a new file to your folder and name it ''register.js'' .&lt;br /&gt;
Then add the basic markup such as copyright, define for ''require.js'', 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('plugins/notifications/tutorial/register',&lt;br /&gt;
    ['io.ox/core/extensions'], function (ext) {&lt;br /&gt;
&lt;br /&gt;
    'use strict';&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;
''Note: We need to use extensions so we need to require the needed resources with&lt;br /&gt;
  ['io.ox/core/extensions'], function (ext)&lt;br /&gt;
as seen above.''&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 ''manifest.json'' 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 ''src/manifests.js''&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: ['io.ox/core/notifications'],&lt;br /&gt;
    path: 'plugins/notifications/tutorial/register'&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 ''io.ox/core/notifications/register'' .&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('io.ox/core/notifications/register').extend({&lt;br /&gt;
    id: 'tutorial',//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('io.ox/tutorial', NotificationsView);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''io.ox/tutorial'' is the id the controller should use to refer to our plugin and ''NotificationsView'' 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: 'notifications',&lt;br /&gt;
    id: 'io-ox-notifications-tutorial',&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;
''Note: Events and render function are empty at the moment, but we will fix that soon.''&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('io.ox/core/notifications/tutorial/header').invoke('draw', this.$el.empty(), baton);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''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.''&lt;br /&gt;
&lt;br /&gt;
Now extend the point with a simple draw method for our header and container.&lt;br /&gt;
''this'' 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('io.ox/core/notifications/tutorial/header').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
            $('&amp;lt;legend class=&amp;quot;section-title&amp;quot;&amp;gt;').text('Hello World'),//header&lt;br /&gt;
            $('&amp;lt;div class=&amp;quot;notifications&amp;quot;&amp;gt;')//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 ''&amp;amp;customManifests=true'' to the url to load your plugin and reload the page.&lt;br /&gt;
&lt;br /&gt;
''Note: The notification area is loaded with a delay, so you have to wait a bit.''&lt;br /&gt;
&lt;br /&gt;
After it'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: 'I am a notification', description: 'Hello world!'},&lt;br /&gt;
                         {title: 'I am a notification too', description: 'Hooray!'}];&lt;br /&gt;
            //trigger the event to add them&lt;br /&gt;
            $(myEventTriggerer).trigger('set-tutorial-notification', [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 ''notifications.collection''.&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 ''notifications.collection.reset(new Backbone.Model());'' 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;
''Note: notifications.collection.add() does not trigger the add event. You need to do this manually, this seems to be a backbone issue.''&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('set-tutorial-notification', 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('io.ox/core/notifications/tutorial/item')&lt;br /&gt;
        .invoke('draw', this.$('.notifications'), 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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//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 ''data-cid'' 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;
    'click .item': 'openPopup',&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This calls the ''openPopup'' 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 ''openPopup'' 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 = $('#io-ox-notifications-overlay'),//the overlay we draw our sidepopup in&lt;br /&gt;
        cid = $(e.currentTarget).attr('data-cid'),//getting the right model&lt;br /&gt;
        model = this.collection.getByCid(cid);&lt;br /&gt;
            &lt;br /&gt;
    require(['io.ox/core/tk/dialogs'], function (dialogs) {//require dialogs&lt;br /&gt;
        //create the popup&lt;br /&gt;
        new dialogs.SidePopup({ arrow: false, side: 'right' })&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($('&amp;lt;div&amp;gt;').text(model.get('title')),&lt;br /&gt;
                             $('&amp;lt;br&amp;gt;'),&lt;br /&gt;
                             $('&amp;lt;div&amp;gt;').text(model.get('description')));&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. ''overlay'' is the div we append our popup to, ''cid'' is the cid we added as an attribute to our notification div earlier and ''model'' is the model we get from our collection by this cid.&lt;br /&gt;
After this we require our ''dialogs'' 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't want our notifications to stay there forever, so we need a way to remove them.&lt;br /&gt;
Let'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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//some text to fill it&lt;br /&gt;
                $('&amp;lt;button class=&amp;quot;mybutton btn btn-primary&amp;quot; data-action=&amp;quot;close&amp;quot;&amp;gt;').text('close')//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;
    'click .item': 'openPopup',&lt;br /&gt;
    'click [data-action=&amp;quot;close&amp;quot;]': 'closeNotification'&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('.item').attr('data-cid'),//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;
''e.stopPropagation()'' 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;/div&gt;</summary>
		<author><name>David.bauer</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=13792</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=13792"/>
		<updated>2013-04-12T11:31:48Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Creating the View */&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;
'''Abstract:''' 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 ''apps.plugins/notifications/'' .&lt;br /&gt;
For this tutorial we will create ''plugins/notifications/tutorial/'' .&lt;br /&gt;
&lt;br /&gt;
Now add a new file to your folder and name it ''register.js'' .&lt;br /&gt;
Then add the basic markup such as copyright, define for ''require.js'', 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('plugins/notifications/tutorial/register',&lt;br /&gt;
    ['io.ox/core/extensions'], function (ext) {&lt;br /&gt;
&lt;br /&gt;
    'use strict';&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;
''Note: We need to use extensions so we need to require the needed resources with&lt;br /&gt;
  ['io.ox/core/extensions'], function (ext)&lt;br /&gt;
as seen above.''&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 ''manifest.json'' 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 ''src/manifests.js''&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: ['io.ox/core/notifications'],&lt;br /&gt;
    path: 'plugins/notifications/tutorial/register'&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 ''io.ox/core/notifications/register'' .&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('io.ox/core/notifications/register').extend({&lt;br /&gt;
    id: 'tutorial',//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('io.ox/tutorial', NotificationsView);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''io.ox/tutorial'' is the id the controller should use to refer to our plugin and ''NotificationsView'' 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: 'notifications',&lt;br /&gt;
    id: 'io-ox-notifications-tutorial',&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;
''Note: Events and render function are empty at the moment, but we will fix that soon.''&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('io.ox/core/notifications/tutorial/header').invoke('draw', this.$el.empty(), baton);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''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.''&lt;br /&gt;
&lt;br /&gt;
Now extend the point with a simple draw method for our header and container.&lt;br /&gt;
''this'' 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('io.ox/core/notifications/tutorial/header').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
            $('&amp;lt;legend class=&amp;quot;section-title&amp;quot;&amp;gt;').text('Hello World'),//header&lt;br /&gt;
            $('&amp;lt;div class=&amp;quot;notifications&amp;quot;&amp;gt;')//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 ''&amp;amp;customManifests=true'' to the url to load your plugin and reload the page.&lt;br /&gt;
&lt;br /&gt;
''Note: The notification area is loaded with a delay, so you have to wait a bit.''&lt;br /&gt;
&lt;br /&gt;
After it'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: 'I am a notification', description: 'Hello world!'},&lt;br /&gt;
                         {title: 'I am a notification too', description: 'Hooray!'}];&lt;br /&gt;
            //trigger the event to add them&lt;br /&gt;
            $(myEventTriggerer).trigger('set-tutorial-notification', [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 ''notifications.collection''.&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 ''notifications.collection.reset(new Backbone.Model());'' 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;
''Note: notifications.collection.add() does not trigger the add event. You need to do this manually, this seems to be a backbone issue.''&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('set-tutorial-notification', 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('io.ox/core/notifications/tutorial/item')&lt;br /&gt;
        .invoke('draw', this.$('.notifications'), 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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//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 ''data-cid'' 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;
    'click .item': 'openPopup',&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This calls the ''openPopup'' 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 ''openPopup'' 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 = $('#io-ox-notifications-overlay'),//the overlay we draw our sidepopup in&lt;br /&gt;
        cid = $(e.currentTarget).attr('data-cid'),//getting the right model&lt;br /&gt;
        model = this.collection.getByCid(cid);&lt;br /&gt;
            &lt;br /&gt;
    require(['io.ox/core/tk/dialogs'], function (dialogs) {//require dialogs&lt;br /&gt;
        //create the popup&lt;br /&gt;
        new dialogs.SidePopup({ arrow: false, side: 'right' })&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($('&amp;lt;div&amp;gt;').text(model.get('title')),&lt;br /&gt;
                             $('&amp;lt;br&amp;gt;'),&lt;br /&gt;
                             $('&amp;lt;div&amp;gt;').text(model.get('description')));&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. ''overlay'' is the div we append our popup to, ''cid'' is the cid we added as an attribute to our notification div earlier and ''model'' is the model we get from our collection by this cid.&lt;br /&gt;
After this we require our ''dialogs'' 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't want our notifications to stay there forever, so we need a way to remove them.&lt;br /&gt;
Let'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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//some text to fill it&lt;br /&gt;
                $('&amp;lt;button class=&amp;quot;mybutton btn btn-primary&amp;quot; data-action=&amp;quot;close&amp;quot;&amp;gt;').text('close')//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;
    'click .item': 'openPopup',&lt;br /&gt;
    'click [data-action=&amp;quot;close&amp;quot;]': 'closeNotification'&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('.item').attr('data-cid'),//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;
''e.stopPropagation()'' 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;/div&gt;</summary>
		<author><name>David.bauer</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=13791</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=13791"/>
		<updated>2013-04-12T11:31:40Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Adding the headline */&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;
'''Abstract:''' 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 ''apps.plugins/notifications/'' .&lt;br /&gt;
For this tutorial we will create ''plugins/notifications/tutorial/'' .&lt;br /&gt;
&lt;br /&gt;
Now add a new file to your folder and name it ''register.js'' .&lt;br /&gt;
Then add the basic markup such as copyright, define for ''require.js'', 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('plugins/notifications/tutorial/register',&lt;br /&gt;
    ['io.ox/core/extensions'], function (ext) {&lt;br /&gt;
&lt;br /&gt;
    'use strict';&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;
''Note: We need to use extensions so we need to require the needed resources with&lt;br /&gt;
  ['io.ox/core/extensions'], function (ext)&lt;br /&gt;
as seen above.''&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 ''manifest.json'' 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 ''src/manifests.js''&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: ['io.ox/core/notifications'],&lt;br /&gt;
    path: 'plugins/notifications/tutorial/register'&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 ''io.ox/core/notifications/register'' .&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('io.ox/core/notifications/register').extend({&lt;br /&gt;
    id: 'tutorial',//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('io.ox/tutorial', NotificationsView);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''io.ox/tutorial'' is the id the controller should use to refer to our plugin and ''NotificationsView'' 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: 'notifications',&lt;br /&gt;
    id: 'io-ox-notifications-tutorial',&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;
''Note: Events and render function are empty at the moment, but we will fix that soon.''&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('io.ox/core/notifications/tutorial/header').invoke('draw', this.$el.empty(), baton);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''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.''&lt;br /&gt;
&lt;br /&gt;
Now extend the point with a simple draw method for our header and container.&lt;br /&gt;
''this'' 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('io.ox/core/notifications/tutorial/header').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
            $('&amp;lt;legend class=&amp;quot;section-title&amp;quot;&amp;gt;').text('Hello World'),//header&lt;br /&gt;
            $('&amp;lt;div class=&amp;quot;notifications&amp;quot;&amp;gt;')//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 ''&amp;amp;customManifests=true'' to the url to load your plugin and reload the page.&lt;br /&gt;
&lt;br /&gt;
''Note: The notification area is loaded with a delay, so you have to wait a bit.''&lt;br /&gt;
&lt;br /&gt;
After it'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: 'I am a notification', description: 'Hello world!'},&lt;br /&gt;
                         {title: 'I am a notification too', description: 'Hooray!'}];&lt;br /&gt;
            //trigger the event to add them&lt;br /&gt;
            $(myEventTriggerer).trigger('set-tutorial-notification', [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 ''notifications.collection''.&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 ''notifications.collection.reset(new Backbone.Model());'' 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;
''Note: notifications.collection.add() does not trigger the add event. You need to do this manually, this seems to be a backbone issue.''&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('set-tutorial-notification', 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('io.ox/core/notifications/tutorial/item')&lt;br /&gt;
        .invoke('draw', this.$('.notifications'), 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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//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 ''data-cid'' 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;
    'click .item': 'openPopup',&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This calls the ''openPopup'' 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 ''openPopup'' 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 = $('#io-ox-notifications-overlay'),//the overlay we draw our sidepopup in&lt;br /&gt;
        cid = $(e.currentTarget).attr('data-cid'),//getting the right model&lt;br /&gt;
        model = this.collection.getByCid(cid);&lt;br /&gt;
            &lt;br /&gt;
    require(['io.ox/core/tk/dialogs'], function (dialogs) {//require dialogs&lt;br /&gt;
        //create the popup&lt;br /&gt;
        new dialogs.SidePopup({ arrow: false, side: 'right' })&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($('&amp;lt;div&amp;gt;').text(model.get('title')),&lt;br /&gt;
                             $('&amp;lt;br&amp;gt;'),&lt;br /&gt;
                             $('&amp;lt;div&amp;gt;').text(model.get('description')));&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. ''overlay'' is the div we append our popup to, ''cid'' is the cid we added as an attribute to our notification div earlier and ''model'' is the model we get from our collection by this cid.&lt;br /&gt;
After this we require our ''dialogs'' 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't want our notifications to stay there forever, so we need a way to remove them.&lt;br /&gt;
Let'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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//some text to fill it&lt;br /&gt;
                $('&amp;lt;button class=&amp;quot;mybutton btn btn-primary&amp;quot; data-action=&amp;quot;close&amp;quot;&amp;gt;').text('close')//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;
    'click .item': 'openPopup',&lt;br /&gt;
    'click [data-action=&amp;quot;close&amp;quot;]': 'closeNotification'&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('.item').attr('data-cid'),//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;
''e.stopPropagation()'' 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;/div&gt;</summary>
		<author><name>David.bauer</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=13790</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=13790"/>
		<updated>2013-04-12T11:31:27Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Triggering the events */&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;
'''Abstract:''' 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 ''apps.plugins/notifications/'' .&lt;br /&gt;
For this tutorial we will create ''plugins/notifications/tutorial/'' .&lt;br /&gt;
&lt;br /&gt;
Now add a new file to your folder and name it ''register.js'' .&lt;br /&gt;
Then add the basic markup such as copyright, define for ''require.js'', 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('plugins/notifications/tutorial/register',&lt;br /&gt;
    ['io.ox/core/extensions'], function (ext) {&lt;br /&gt;
&lt;br /&gt;
    'use strict';&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;
''Note: We need to use extensions so we need to require the needed resources with&lt;br /&gt;
  ['io.ox/core/extensions'], function (ext)&lt;br /&gt;
as seen above.''&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 ''manifest.json'' 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 ''src/manifests.js''&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: ['io.ox/core/notifications'],&lt;br /&gt;
    path: 'plugins/notifications/tutorial/register'&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 ''io.ox/core/notifications/register'' .&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('io.ox/core/notifications/register').extend({&lt;br /&gt;
    id: 'tutorial',//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('io.ox/tutorial', NotificationsView);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''io.ox/tutorial'' is the id the controller should use to refer to our plugin and ''NotificationsView'' 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: 'notifications',&lt;br /&gt;
    id: 'io-ox-notifications-tutorial',&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;
''Note: Events and render function are empty at the moment, but we will fix that soon.''&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('io.ox/core/notifications/tutorial/header').invoke('draw', this.$el.empty(), baton);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''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.''&lt;br /&gt;
&lt;br /&gt;
Now extend the point with a simple draw method for our header and container.&lt;br /&gt;
''this'' 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('io.ox/core/notifications/tutorial/header').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
            $('&amp;lt;legend class=&amp;quot;section-title&amp;quot;&amp;gt;').text('Hello World'),//header&lt;br /&gt;
            $('&amp;lt;div class=&amp;quot;notifications&amp;quot;&amp;gt;')//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 ''&amp;amp;customManifests=true'' to the url to load your plugin and reload the page.&lt;br /&gt;
&lt;br /&gt;
''Note: The notification area is loaded with a delay, so you have to wait a bit.''&lt;br /&gt;
&lt;br /&gt;
After it'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: 'I am a notification', description: 'Hello world!'},&lt;br /&gt;
                         {title: 'I am a notification too', description: 'Hooray!'}];&lt;br /&gt;
            //trigger the event to add them&lt;br /&gt;
            $(myEventTriggerer).trigger('set-tutorial-notification', [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 ''notifications.collection''.&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 ''notifications.collection.reset(new Backbone.Model());'' 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;
''Note: notifications.collection.add() does not trigger the add event. You need to do this manually, this seems to be a backbone issue.''&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('set-tutorial-notification', 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('io.ox/core/notifications/tutorial/item')&lt;br /&gt;
        .invoke('draw', this.$('.notifications'), 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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//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 ''data-cid'' 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;
    'click .item': 'openPopup',&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This calls the ''openPopup'' 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 ''openPopup'' 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 = $('#io-ox-notifications-overlay'),//the overlay we draw our sidepopup in&lt;br /&gt;
        cid = $(e.currentTarget).attr('data-cid'),//getting the right model&lt;br /&gt;
        model = this.collection.getByCid(cid);&lt;br /&gt;
            &lt;br /&gt;
    require(['io.ox/core/tk/dialogs'], function (dialogs) {//require dialogs&lt;br /&gt;
        //create the popup&lt;br /&gt;
        new dialogs.SidePopup({ arrow: false, side: 'right' })&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($('&amp;lt;div&amp;gt;').text(model.get('title')),&lt;br /&gt;
                             $('&amp;lt;br&amp;gt;'),&lt;br /&gt;
                             $('&amp;lt;div&amp;gt;').text(model.get('description')));&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. ''overlay'' is the div we append our popup to, ''cid'' is the cid we added as an attribute to our notification div earlier and ''model'' is the model we get from our collection by this cid.&lt;br /&gt;
After this we require our ''dialogs'' 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't want our notifications to stay there forever, so we need a way to remove them.&lt;br /&gt;
Let'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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//some text to fill it&lt;br /&gt;
                $('&amp;lt;button class=&amp;quot;mybutton btn btn-primary&amp;quot; data-action=&amp;quot;close&amp;quot;&amp;gt;').text('close')//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;
    'click .item': 'openPopup',&lt;br /&gt;
    'click [data-action=&amp;quot;close&amp;quot;]': 'closeNotification'&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('.item').attr('data-cid'),//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;
''e.stopPropagation()'' 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;/div&gt;</summary>
		<author><name>David.bauer</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=13789</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=13789"/>
		<updated>2013-04-12T11:31:10Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Helper functions */&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;
'''Abstract:''' 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 ''apps.plugins/notifications/'' .&lt;br /&gt;
For this tutorial we will create ''plugins/notifications/tutorial/'' .&lt;br /&gt;
&lt;br /&gt;
Now add a new file to your folder and name it ''register.js'' .&lt;br /&gt;
Then add the basic markup such as copyright, define for ''require.js'', 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('plugins/notifications/tutorial/register',&lt;br /&gt;
    ['io.ox/core/extensions'], function (ext) {&lt;br /&gt;
&lt;br /&gt;
    'use strict';&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;
''Note: We need to use extensions so we need to require the needed resources with&lt;br /&gt;
  ['io.ox/core/extensions'], function (ext)&lt;br /&gt;
as seen above.''&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 ''manifest.json'' 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 ''src/manifests.js''&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: ['io.ox/core/notifications'],&lt;br /&gt;
    path: 'plugins/notifications/tutorial/register'&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 ''io.ox/core/notifications/register'' .&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('io.ox/core/notifications/register').extend({&lt;br /&gt;
    id: 'tutorial',//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('io.ox/tutorial', NotificationsView);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''io.ox/tutorial'' is the id the controller should use to refer to our plugin and ''NotificationsView'' 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: 'notifications',&lt;br /&gt;
    id: 'io-ox-notifications-tutorial',&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;
''Note: Events and render function are empty at the moment, but we will fix that soon.''&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('io.ox/core/notifications/tutorial/header').invoke('draw', this.$el.empty(), baton);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''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.''&lt;br /&gt;
&lt;br /&gt;
Now extend the point with a simple draw method for our header and container.&lt;br /&gt;
''this'' 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('io.ox/core/notifications/tutorial/header').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
            $('&amp;lt;legend class=&amp;quot;section-title&amp;quot;&amp;gt;').text('Hello World'),//header&lt;br /&gt;
            $('&amp;lt;div class=&amp;quot;notifications&amp;quot;&amp;gt;')//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 ''&amp;amp;customManifests=true'' to the url to load your plugin and reload the page.&lt;br /&gt;
&lt;br /&gt;
''Note: The notification area is loaded with a delay, so you have to wait a bit.''&lt;br /&gt;
&lt;br /&gt;
After it'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: 'I am a notification', description: 'Hello world!'},&lt;br /&gt;
                         {title: 'I am a notification too', description: 'Hooray!'}];&lt;br /&gt;
            //trigger the event to add them&lt;br /&gt;
            $(myEventTriggerer).trigger('set-tutorial-notification', [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 ''notifications.collection''.&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 ''notifications.collection.reset(new Backbone.Model());'' 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;
''Note: notifications.collection.add() does not trigger the add event. You need to do this manually, this seems to be a backbone issue.''&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('set-tutorial-notification', 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('io.ox/core/notifications/tutorial/item')&lt;br /&gt;
        .invoke('draw', this.$('.notifications'), 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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//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 ''data-cid'' 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;
    'click .item': 'openPopup',&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This calls the ''openPopup'' 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 ''openPopup'' 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 = $('#io-ox-notifications-overlay'),//the overlay we draw our sidepopup in&lt;br /&gt;
        cid = $(e.currentTarget).attr('data-cid'),//getting the right model&lt;br /&gt;
        model = this.collection.getByCid(cid);&lt;br /&gt;
            &lt;br /&gt;
    require(['io.ox/core/tk/dialogs'], function (dialogs) {//require dialogs&lt;br /&gt;
        //create the popup&lt;br /&gt;
        new dialogs.SidePopup({ arrow: false, side: 'right' })&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($('&amp;lt;div&amp;gt;').text(model.get('title')),&lt;br /&gt;
                             $('&amp;lt;br&amp;gt;'),&lt;br /&gt;
                             $('&amp;lt;div&amp;gt;').text(model.get('description')));&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. ''overlay'' is the div we append our popup to, ''cid'' is the cid we added as an attribute to our notification div earlier and ''model'' is the model we get from our collection by this cid.&lt;br /&gt;
After this we require our ''dialogs'' 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't want our notifications to stay there forever, so we need a way to remove them.&lt;br /&gt;
Let'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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//some text to fill it&lt;br /&gt;
                $('&amp;lt;button class=&amp;quot;mybutton btn btn-primary&amp;quot; data-action=&amp;quot;close&amp;quot;&amp;gt;').text('close')//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;
    'click .item': 'openPopup',&lt;br /&gt;
    'click [data-action=&amp;quot;close&amp;quot;]': 'closeNotification'&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('.item').attr('data-cid'),//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;
''e.stopPropagation()'' 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;/div&gt;</summary>
		<author><name>David.bauer</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=13788</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=13788"/>
		<updated>2013-04-12T11:30:52Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Helper functions */&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;
'''Abstract:''' 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 ''apps.plugins/notifications/'' .&lt;br /&gt;
For this tutorial we will create ''plugins/notifications/tutorial/'' .&lt;br /&gt;
&lt;br /&gt;
Now add a new file to your folder and name it ''register.js'' .&lt;br /&gt;
Then add the basic markup such as copyright, define for ''require.js'', 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('plugins/notifications/tutorial/register',&lt;br /&gt;
    ['io.ox/core/extensions'], function (ext) {&lt;br /&gt;
&lt;br /&gt;
    'use strict';&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;
''Note: We need to use extensions so we need to require the needed resources with&lt;br /&gt;
  ['io.ox/core/extensions'], function (ext)&lt;br /&gt;
as seen above.''&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 ''manifest.json'' 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 ''src/manifests.js''&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: ['io.ox/core/notifications'],&lt;br /&gt;
    path: 'plugins/notifications/tutorial/register'&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 ''io.ox/core/notifications/register'' .&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('io.ox/core/notifications/register').extend({&lt;br /&gt;
    id: 'tutorial',//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('io.ox/tutorial', NotificationsView);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''io.ox/tutorial'' is the id the controller should use to refer to our plugin and ''NotificationsView'' 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: 'notifications',&lt;br /&gt;
    id: 'io-ox-notifications-tutorial',&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;
''Note: Events and render function are empty at the moment, but we will fix that soon.''&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('io.ox/core/notifications/tutorial/header').invoke('draw', this.$el.empty(), baton);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''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.''&lt;br /&gt;
&lt;br /&gt;
Now extend the point with a simple draw method for our header and container.&lt;br /&gt;
''this'' 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('io.ox/core/notifications/tutorial/header').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
            $('&amp;lt;legend class=&amp;quot;section-title&amp;quot;&amp;gt;').text('Hello World'),//header&lt;br /&gt;
            $('&amp;lt;div class=&amp;quot;notifications&amp;quot;&amp;gt;')//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 ''&amp;amp;customManifests=true'' to the url to load your plugin and reload the page.&lt;br /&gt;
&lt;br /&gt;
''Note: The notification area is loaded with a delay, so you have to wait a bit.''&lt;br /&gt;
&lt;br /&gt;
After it'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: 'I am a notification', description: 'Hello world!'},&lt;br /&gt;
                         {title: 'I am a notification too', description: 'Hooray!'}];&lt;br /&gt;
            //trigger the event to add them&lt;br /&gt;
            $(myEventTriggerer).trigger('set-tutorial-notification', [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 ''notifications.collection''.&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 ''notifications.collection.reset(new Backbone.Model());'' 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;
/*&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;
*/&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;
''Note: notifications.collection.add() does not trigger the add event. You need to do this manually, this seems to be a backbone issue.''&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('set-tutorial-notification', 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('io.ox/core/notifications/tutorial/item')&lt;br /&gt;
        .invoke('draw', this.$('.notifications'), 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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//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 ''data-cid'' 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;
    'click .item': 'openPopup',&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This calls the ''openPopup'' 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 ''openPopup'' 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 = $('#io-ox-notifications-overlay'),//the overlay we draw our sidepopup in&lt;br /&gt;
        cid = $(e.currentTarget).attr('data-cid'),//getting the right model&lt;br /&gt;
        model = this.collection.getByCid(cid);&lt;br /&gt;
            &lt;br /&gt;
    require(['io.ox/core/tk/dialogs'], function (dialogs) {//require dialogs&lt;br /&gt;
        //create the popup&lt;br /&gt;
        new dialogs.SidePopup({ arrow: false, side: 'right' })&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($('&amp;lt;div&amp;gt;').text(model.get('title')),&lt;br /&gt;
                             $('&amp;lt;br&amp;gt;'),&lt;br /&gt;
                             $('&amp;lt;div&amp;gt;').text(model.get('description')));&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. ''overlay'' is the div we append our popup to, ''cid'' is the cid we added as an attribute to our notification div earlier and ''model'' is the model we get from our collection by this cid.&lt;br /&gt;
After this we require our ''dialogs'' 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't want our notifications to stay there forever, so we need a way to remove them.&lt;br /&gt;
Let'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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//some text to fill it&lt;br /&gt;
                $('&amp;lt;button class=&amp;quot;mybutton btn btn-primary&amp;quot; data-action=&amp;quot;close&amp;quot;&amp;gt;').text('close')//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;
    'click .item': 'openPopup',&lt;br /&gt;
    'click [data-action=&amp;quot;close&amp;quot;]': 'closeNotification'&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('.item').attr('data-cid'),//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;
''e.stopPropagation()'' 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;/div&gt;</summary>
		<author><name>David.bauer</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=13787</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=13787"/>
		<updated>2013-04-12T11:30:21Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Helper functions */&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;
'''Abstract:''' 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 ''apps.plugins/notifications/'' .&lt;br /&gt;
For this tutorial we will create ''plugins/notifications/tutorial/'' .&lt;br /&gt;
&lt;br /&gt;
Now add a new file to your folder and name it ''register.js'' .&lt;br /&gt;
Then add the basic markup such as copyright, define for ''require.js'', 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('plugins/notifications/tutorial/register',&lt;br /&gt;
    ['io.ox/core/extensions'], function (ext) {&lt;br /&gt;
&lt;br /&gt;
    'use strict';&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;
''Note: We need to use extensions so we need to require the needed resources with&lt;br /&gt;
  ['io.ox/core/extensions'], function (ext)&lt;br /&gt;
as seen above.''&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 ''manifest.json'' 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 ''src/manifests.js''&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: ['io.ox/core/notifications'],&lt;br /&gt;
    path: 'plugins/notifications/tutorial/register'&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 ''io.ox/core/notifications/register'' .&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('io.ox/core/notifications/register').extend({&lt;br /&gt;
    id: 'tutorial',//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('io.ox/tutorial', NotificationsView);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''io.ox/tutorial'' is the id the controller should use to refer to our plugin and ''NotificationsView'' 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: 'notifications',&lt;br /&gt;
    id: 'io-ox-notifications-tutorial',&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;
''Note: Events and render function are empty at the moment, but we will fix that soon.''&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('io.ox/core/notifications/tutorial/header').invoke('draw', this.$el.empty(), baton);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''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.''&lt;br /&gt;
&lt;br /&gt;
Now extend the point with a simple draw method for our header and container.&lt;br /&gt;
''this'' 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('io.ox/core/notifications/tutorial/header').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
            $('&amp;lt;legend class=&amp;quot;section-title&amp;quot;&amp;gt;').text('Hello World'),//header&lt;br /&gt;
            $('&amp;lt;div class=&amp;quot;notifications&amp;quot;&amp;gt;')//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 ''&amp;amp;customManifests=true'' to the url to load your plugin and reload the page.&lt;br /&gt;
&lt;br /&gt;
''Note: The notification area is loaded with a delay, so you have to wait a bit.''&lt;br /&gt;
&lt;br /&gt;
After it'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: 'I am a notification', description: 'Hello world!'},&lt;br /&gt;
                         {title: 'I am a notification too', description: 'Hooray!'}];&lt;br /&gt;
            //trigger the event to add them&lt;br /&gt;
            $(myEventTriggerer).trigger('set-tutorial-notification', [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 ''notifications.collection''.&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 ''notifications.collection.reset(new Backbone.Model());'' 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;
''Note: notifications.collection.add() does not trigger the add event. You need to do this manually, this seems to be a backbone issue.''&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('set-tutorial-notification', 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('io.ox/core/notifications/tutorial/item')&lt;br /&gt;
        .invoke('draw', this.$('.notifications'), 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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//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 ''data-cid'' 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;
    'click .item': 'openPopup',&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This calls the ''openPopup'' 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 ''openPopup'' 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 = $('#io-ox-notifications-overlay'),//the overlay we draw our sidepopup in&lt;br /&gt;
        cid = $(e.currentTarget).attr('data-cid'),//getting the right model&lt;br /&gt;
        model = this.collection.getByCid(cid);&lt;br /&gt;
            &lt;br /&gt;
    require(['io.ox/core/tk/dialogs'], function (dialogs) {//require dialogs&lt;br /&gt;
        //create the popup&lt;br /&gt;
        new dialogs.SidePopup({ arrow: false, side: 'right' })&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($('&amp;lt;div&amp;gt;').text(model.get('title')),&lt;br /&gt;
                             $('&amp;lt;br&amp;gt;'),&lt;br /&gt;
                             $('&amp;lt;div&amp;gt;').text(model.get('description')));&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. ''overlay'' is the div we append our popup to, ''cid'' is the cid we added as an attribute to our notification div earlier and ''model'' is the model we get from our collection by this cid.&lt;br /&gt;
After this we require our ''dialogs'' 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't want our notifications to stay there forever, so we need a way to remove them.&lt;br /&gt;
Let'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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//some text to fill it&lt;br /&gt;
                $('&amp;lt;button class=&amp;quot;mybutton btn btn-primary&amp;quot; data-action=&amp;quot;close&amp;quot;&amp;gt;').text('close')//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;
    'click .item': 'openPopup',&lt;br /&gt;
    'click [data-action=&amp;quot;close&amp;quot;]': 'closeNotification'&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('.item').attr('data-cid'),//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;
''e.stopPropagation()'' 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;/div&gt;</summary>
		<author><name>David.bauer</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=13786</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=13786"/>
		<updated>2013-04-12T11:30:02Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Listen for events */&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;
'''Abstract:''' 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 ''apps.plugins/notifications/'' .&lt;br /&gt;
For this tutorial we will create ''plugins/notifications/tutorial/'' .&lt;br /&gt;
&lt;br /&gt;
Now add a new file to your folder and name it ''register.js'' .&lt;br /&gt;
Then add the basic markup such as copyright, define for ''require.js'', 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('plugins/notifications/tutorial/register',&lt;br /&gt;
    ['io.ox/core/extensions'], function (ext) {&lt;br /&gt;
&lt;br /&gt;
    'use strict';&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;
''Note: We need to use extensions so we need to require the needed resources with&lt;br /&gt;
  ['io.ox/core/extensions'], function (ext)&lt;br /&gt;
as seen above.''&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 ''manifest.json'' 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 ''src/manifests.js''&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: ['io.ox/core/notifications'],&lt;br /&gt;
    path: 'plugins/notifications/tutorial/register'&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 ''io.ox/core/notifications/register'' .&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('io.ox/core/notifications/register').extend({&lt;br /&gt;
    id: 'tutorial',//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('io.ox/tutorial', NotificationsView);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''io.ox/tutorial'' is the id the controller should use to refer to our plugin and ''NotificationsView'' 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: 'notifications',&lt;br /&gt;
    id: 'io-ox-notifications-tutorial',&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;
''Note: Events and render function are empty at the moment, but we will fix that soon.''&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('io.ox/core/notifications/tutorial/header').invoke('draw', this.$el.empty(), baton);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''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.''&lt;br /&gt;
&lt;br /&gt;
Now extend the point with a simple draw method for our header and container.&lt;br /&gt;
''this'' 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('io.ox/core/notifications/tutorial/header').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
            $('&amp;lt;legend class=&amp;quot;section-title&amp;quot;&amp;gt;').text('Hello World'),//header&lt;br /&gt;
            $('&amp;lt;div class=&amp;quot;notifications&amp;quot;&amp;gt;')//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 ''&amp;amp;customManifests=true'' to the url to load your plugin and reload the page.&lt;br /&gt;
&lt;br /&gt;
''Note: The notification area is loaded with a delay, so you have to wait a bit.''&lt;br /&gt;
&lt;br /&gt;
After it'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: 'I am a notification', description: 'Hello world!'},&lt;br /&gt;
                         {title: 'I am a notification too', description: 'Hooray!'}];&lt;br /&gt;
            //trigger the event to add them&lt;br /&gt;
            $(myEventTriggerer).trigger('set-tutorial-notification', [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 ''notifications.collection''.&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 ''notifications.collection.reset(new Backbone.Model());'' 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;
''Note: notifications.collection.add() does not trigger the add event. You need to do this manually, this seems to be a backbone issue.''&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('set-tutorial-notification', 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('io.ox/core/notifications/tutorial/item')&lt;br /&gt;
        .invoke('draw', this.$('.notifications'), 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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//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 ''data-cid'' 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;
    'click .item': 'openPopup',&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This calls the ''openPopup'' 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 ''openPopup'' 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 = $('#io-ox-notifications-overlay'),//the overlay we draw our sidepopup in&lt;br /&gt;
        cid = $(e.currentTarget).attr('data-cid'),//getting the right model&lt;br /&gt;
        model = this.collection.getByCid(cid);&lt;br /&gt;
            &lt;br /&gt;
    require(['io.ox/core/tk/dialogs'], function (dialogs) {//require dialogs&lt;br /&gt;
        //create the popup&lt;br /&gt;
        new dialogs.SidePopup({ arrow: false, side: 'right' })&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($('&amp;lt;div&amp;gt;').text(model.get('title')),&lt;br /&gt;
                             $('&amp;lt;br&amp;gt;'),&lt;br /&gt;
                             $('&amp;lt;div&amp;gt;').text(model.get('description')));&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. ''overlay'' is the div we append our popup to, ''cid'' is the cid we added as an attribute to our notification div earlier and ''model'' is the model we get from our collection by this cid.&lt;br /&gt;
After this we require our ''dialogs'' 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't want our notifications to stay there forever, so we need a way to remove them.&lt;br /&gt;
Let'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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//some text to fill it&lt;br /&gt;
                $('&amp;lt;button class=&amp;quot;mybutton btn btn-primary&amp;quot; data-action=&amp;quot;close&amp;quot;&amp;gt;').text('close')//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;
    'click .item': 'openPopup',&lt;br /&gt;
    'click [data-action=&amp;quot;close&amp;quot;]': 'closeNotification'&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('.item').attr('data-cid'),//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;
''e.stopPropagation()'' 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;/div&gt;</summary>
		<author><name>David.bauer</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=13785</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=13785"/>
		<updated>2013-04-12T11:29:38Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Drawing the Notifications */&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;
'''Abstract:''' 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 ''apps.plugins/notifications/'' .&lt;br /&gt;
For this tutorial we will create ''plugins/notifications/tutorial/'' .&lt;br /&gt;
&lt;br /&gt;
Now add a new file to your folder and name it ''register.js'' .&lt;br /&gt;
Then add the basic markup such as copyright, define for ''require.js'', 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('plugins/notifications/tutorial/register',&lt;br /&gt;
    ['io.ox/core/extensions'], function (ext) {&lt;br /&gt;
&lt;br /&gt;
    'use strict';&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;
''Note: We need to use extensions so we need to require the needed resources with&lt;br /&gt;
  ['io.ox/core/extensions'], function (ext)&lt;br /&gt;
as seen above.''&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 ''manifest.json'' 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 ''src/manifests.js''&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: ['io.ox/core/notifications'],&lt;br /&gt;
    path: 'plugins/notifications/tutorial/register'&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 ''io.ox/core/notifications/register'' .&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('io.ox/core/notifications/register').extend({&lt;br /&gt;
    id: 'tutorial',//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('io.ox/tutorial', NotificationsView);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''io.ox/tutorial'' is the id the controller should use to refer to our plugin and ''NotificationsView'' 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: 'notifications',&lt;br /&gt;
    id: 'io-ox-notifications-tutorial',&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;
''Note: Events and render function are empty at the moment, but we will fix that soon.''&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('io.ox/core/notifications/tutorial/header').invoke('draw', this.$el.empty(), baton);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''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.''&lt;br /&gt;
&lt;br /&gt;
Now extend the point with a simple draw method for our header and container.&lt;br /&gt;
''this'' 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('io.ox/core/notifications/tutorial/header').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
            $('&amp;lt;legend class=&amp;quot;section-title&amp;quot;&amp;gt;').text('Hello World'),//header&lt;br /&gt;
            $('&amp;lt;div class=&amp;quot;notifications&amp;quot;&amp;gt;')//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 ''&amp;amp;customManifests=true'' to the url to load your plugin and reload the page.&lt;br /&gt;
&lt;br /&gt;
''Note: The notification area is loaded with a delay, so you have to wait a bit.''&lt;br /&gt;
&lt;br /&gt;
After it'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: 'I am a notification', description: 'Hello world!'},&lt;br /&gt;
                         {title: 'I am a notification too', description: 'Hooray!'}];&lt;br /&gt;
            //trigger the event to add them&lt;br /&gt;
            $(myEventTriggerer).trigger('set-tutorial-notification', [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 ''notifications.collection''.&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 ''notifications.collection.reset(new Backbone.Model());'' 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;
''Note: notifications.collection.add() does not trigger the add event. You need to do this manually, this seems to be a backbone issue.''&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('set-tutorial-notification', 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('io.ox/core/notifications/tutorial/item')&lt;br /&gt;
        .invoke('draw', this.$('.notifications'), 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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//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 ''data-cid'' 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;
    'click .item': 'openPopup',&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This calls the ''openPopup'' 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 ''openPopup'' 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 = $('#io-ox-notifications-overlay'),//the overlay we draw our sidepopup in&lt;br /&gt;
        cid = $(e.currentTarget).attr('data-cid'),//getting the right model&lt;br /&gt;
        model = this.collection.getByCid(cid);&lt;br /&gt;
            &lt;br /&gt;
    require(['io.ox/core/tk/dialogs'], function (dialogs) {//require dialogs&lt;br /&gt;
        //create the popup&lt;br /&gt;
        new dialogs.SidePopup({ arrow: false, side: 'right' })&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($('&amp;lt;div&amp;gt;').text(model.get('title')),&lt;br /&gt;
                             $('&amp;lt;br&amp;gt;'),&lt;br /&gt;
                             $('&amp;lt;div&amp;gt;').text(model.get('description')));&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. ''overlay'' is the div we append our popup to, ''cid'' is the cid we added as an attribute to our notification div earlier and ''model'' is the model we get from our collection by this cid.&lt;br /&gt;
After this we require our ''dialogs'' 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't want our notifications to stay there forever, so we need a way to remove them.&lt;br /&gt;
Let'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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//some text to fill it&lt;br /&gt;
                $('&amp;lt;button class=&amp;quot;mybutton btn btn-primary&amp;quot; data-action=&amp;quot;close&amp;quot;&amp;gt;').text('close')//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;
    'click .item': 'openPopup',&lt;br /&gt;
    'click [data-action=&amp;quot;close&amp;quot;]': 'closeNotification'&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('.item').attr('data-cid'),//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;
''e.stopPropagation()'' 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;/div&gt;</summary>
		<author><name>David.bauer</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=13784</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=13784"/>
		<updated>2013-04-12T11:29:25Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Adding a Sidepopup */&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;
'''Abstract:''' 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 ''apps.plugins/notifications/'' .&lt;br /&gt;
For this tutorial we will create ''plugins/notifications/tutorial/'' .&lt;br /&gt;
&lt;br /&gt;
Now add a new file to your folder and name it ''register.js'' .&lt;br /&gt;
Then add the basic markup such as copyright, define for ''require.js'', 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('plugins/notifications/tutorial/register',&lt;br /&gt;
    ['io.ox/core/extensions'], function (ext) {&lt;br /&gt;
&lt;br /&gt;
    'use strict';&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;
''Note: We need to use extensions so we need to require the needed resources with&lt;br /&gt;
  ['io.ox/core/extensions'], function (ext)&lt;br /&gt;
as seen above.''&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 ''manifest.json'' 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 ''src/manifests.js''&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: ['io.ox/core/notifications'],&lt;br /&gt;
    path: 'plugins/notifications/tutorial/register'&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 ''io.ox/core/notifications/register'' .&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('io.ox/core/notifications/register').extend({&lt;br /&gt;
    id: 'tutorial',//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('io.ox/tutorial', NotificationsView);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''io.ox/tutorial'' is the id the controller should use to refer to our plugin and ''NotificationsView'' 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: 'notifications',&lt;br /&gt;
    id: 'io-ox-notifications-tutorial',&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;
''Note: Events and render function are empty at the moment, but we will fix that soon.''&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('io.ox/core/notifications/tutorial/header').invoke('draw', this.$el.empty(), baton);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''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.''&lt;br /&gt;
&lt;br /&gt;
Now extend the point with a simple draw method for our header and container.&lt;br /&gt;
''this'' 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('io.ox/core/notifications/tutorial/header').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
            $('&amp;lt;legend class=&amp;quot;section-title&amp;quot;&amp;gt;').text('Hello World'),//header&lt;br /&gt;
            $('&amp;lt;div class=&amp;quot;notifications&amp;quot;&amp;gt;')//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 ''&amp;amp;customManifests=true'' to the url to load your plugin and reload the page.&lt;br /&gt;
&lt;br /&gt;
''Note: The notification area is loaded with a delay, so you have to wait a bit.''&lt;br /&gt;
&lt;br /&gt;
After it'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: 'I am a notification', description: 'Hello world!'},&lt;br /&gt;
                         {title: 'I am a notification too', description: 'Hooray!'}];&lt;br /&gt;
            //trigger the event to add them&lt;br /&gt;
            $(myEventTriggerer).trigger('set-tutorial-notification', [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 ''notifications.collection''.&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 ''notifications.collection.reset(new Backbone.Model());'' 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;
''Note: notifications.collection.add() does not trigger the add event. You need to do this manually, this seems to be a backbone issue.''&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('set-tutorial-notification', 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('io.ox/core/notifications/tutorial/item')&lt;br /&gt;
        .invoke('draw', this.$('.notifications'), 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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//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 ''data-cid'' 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;
    'click .item': 'openPopup',&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This calls the ''openPopup'' 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 ''openPopup'' 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 = $('#io-ox-notifications-overlay'),//the overlay we draw our sidepopup in&lt;br /&gt;
        cid = $(e.currentTarget).attr('data-cid'),//getting the right model&lt;br /&gt;
        model = this.collection.getByCid(cid);&lt;br /&gt;
            &lt;br /&gt;
    require(['io.ox/core/tk/dialogs'], function (dialogs) {//require dialogs&lt;br /&gt;
        //create the popup&lt;br /&gt;
        new dialogs.SidePopup({ arrow: false, side: 'right' })&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($('&amp;lt;div&amp;gt;').text(model.get('title')),&lt;br /&gt;
                             $('&amp;lt;br&amp;gt;'),&lt;br /&gt;
                             $('&amp;lt;div&amp;gt;').text(model.get('description')));&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. ''overlay'' is the div we append our popup to, ''cid'' is the cid we added as an attribute to our notification div earlier and ''model'' is the model we get from our collection by this cid.&lt;br /&gt;
After this we require our ''dialogs'' 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't want our notifications to stay there forever, so we need a way to remove them.&lt;br /&gt;
Let'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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//some text to fill it&lt;br /&gt;
                $('&amp;lt;button class=&amp;quot;mybutton btn btn-primary&amp;quot; data-action=&amp;quot;close&amp;quot;&amp;gt;').text('close')//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;
    'click .item': 'openPopup',&lt;br /&gt;
    'click [data-action=&amp;quot;close&amp;quot;]': 'closeNotification'&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('.item').attr('data-cid'),//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;
''e.stopPropagation()'' 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;/div&gt;</summary>
		<author><name>David.bauer</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=13783</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=13783"/>
		<updated>2013-04-12T11:29:14Z</updated>

		<summary type="html">&lt;p&gt;David.bauer: /* Adding a Sidepopup */&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;
'''Abstract:''' 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 ''apps.plugins/notifications/'' .&lt;br /&gt;
For this tutorial we will create ''plugins/notifications/tutorial/'' .&lt;br /&gt;
&lt;br /&gt;
Now add a new file to your folder and name it ''register.js'' .&lt;br /&gt;
Then add the basic markup such as copyright, define for ''require.js'', 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('plugins/notifications/tutorial/register',&lt;br /&gt;
    ['io.ox/core/extensions'], function (ext) {&lt;br /&gt;
&lt;br /&gt;
    'use strict';&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;
''Note: We need to use extensions so we need to require the needed resources with&lt;br /&gt;
  ['io.ox/core/extensions'], function (ext)&lt;br /&gt;
as seen above.''&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 ''manifest.json'' 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 ''src/manifests.js''&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: ['io.ox/core/notifications'],&lt;br /&gt;
    path: 'plugins/notifications/tutorial/register'&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 ''io.ox/core/notifications/register'' .&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('io.ox/core/notifications/register').extend({&lt;br /&gt;
    id: 'tutorial',//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('io.ox/tutorial', NotificationsView);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''io.ox/tutorial'' is the id the controller should use to refer to our plugin and ''NotificationsView'' 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: 'notifications',&lt;br /&gt;
    id: 'io-ox-notifications-tutorial',&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;
''Note: Events and render function are empty at the moment, but we will fix that soon.''&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('io.ox/core/notifications/tutorial/header').invoke('draw', this.$el.empty(), baton);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''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.''&lt;br /&gt;
&lt;br /&gt;
Now extend the point with a simple draw method for our header and container.&lt;br /&gt;
''this'' 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('io.ox/core/notifications/tutorial/header').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
            $('&amp;lt;legend class=&amp;quot;section-title&amp;quot;&amp;gt;').text('Hello World'),//header&lt;br /&gt;
            $('&amp;lt;div class=&amp;quot;notifications&amp;quot;&amp;gt;')//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 ''&amp;amp;customManifests=true'' to the url to load your plugin and reload the page.&lt;br /&gt;
&lt;br /&gt;
''Note: The notification area is loaded with a delay, so you have to wait a bit.''&lt;br /&gt;
&lt;br /&gt;
After it'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: 'I am a notification', description: 'Hello world!'},&lt;br /&gt;
                         {title: 'I am a notification too', description: 'Hooray!'}];&lt;br /&gt;
            //trigger the event to add them&lt;br /&gt;
            $(myEventTriggerer).trigger('set-tutorial-notification', [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 ''notifications.collection''.&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 ''notifications.collection.reset(new Backbone.Model());'' 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;
''Note: notifications.collection.add() does not trigger the add event. You need to do this manually, this seems to be a backbone issue.''&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('set-tutorial-notification', 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('io.ox/core/notifications/tutorial/item')&lt;br /&gt;
        .invoke('draw', this.$('.notifications'), 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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//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 ''data-cid'' 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;
    'click .item': 'openPopup',&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This calls the ''openPopup'' 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 ''openPopup'' 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 = $('#io-ox-notifications-overlay'),//the overlay we draw our sidepopup in&lt;br /&gt;
        cid = $(e.currentTarget).attr('data-cid'),//getting the right model&lt;br /&gt;
        model = this.collection.getByCid(cid);&lt;br /&gt;
            &lt;br /&gt;
    require(['io.ox/core/tk/dialogs'], function (dialogs) {//require dialogs&lt;br /&gt;
        //create the popup&lt;br /&gt;
        new dialogs.SidePopup({ arrow: false, side: 'right' })&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($('&amp;lt;div&amp;gt;').text(model.get('title')),&lt;br /&gt;
                             $('&amp;lt;br&amp;gt;'),&lt;br /&gt;
                             $('&amp;lt;div&amp;gt;').text(model.get('description')));&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. ''overlay'' is the div we append our popup to, ''cid'' is the cid we added as an attribute to our notification div earlier and ''model'' is the model we get from our collection by this cid.&lt;br /&gt;
After this we require our ''dialogs'' 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't want our notifications to stay there forever, so we need a way to remove them.&lt;br /&gt;
Let'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('io.ox/core/notifications/tutorial/item').extend({&lt;br /&gt;
    draw: function (baton) {&lt;br /&gt;
        this.append(&lt;br /&gt;
        $('&amp;lt;div class=&amp;quot;tutorial item&amp;quot;&amp;gt;')&lt;br /&gt;
        .attr('data-cid', baton.model.cid)//needed for identification&lt;br /&gt;
            .append(&lt;br /&gt;
                $('&amp;lt;div class=&amp;quot;mytext&amp;quot;&amp;gt;').text(baton.model.get('title')),//some text to fill it&lt;br /&gt;
                $('&amp;lt;button class=&amp;quot;mybutton btn btn-primary&amp;quot; data-action=&amp;quot;close&amp;quot;&amp;gt;').text('close')//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;
    'click .item': 'openPopup',&lt;br /&gt;
    'click [data-action=&amp;quot;close&amp;quot;]': 'closeNotification'&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('.item').attr('data-cid'),//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;
''e.stopPropagation()'' 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;/div&gt;</summary>
		<author><name>David.bauer</name></author>
	</entry>
</feed>