https://oxpedia.org/wiki/api.php?action=feedcontributions&user=J.ohny.b&feedformat=atomOpen-Xchange - User contributions [en]2024-03-28T17:21:45ZUser contributionsMediaWiki 1.31.0https://oxpedia.org/wiki/index.php?title=AppSuite:Debugging_production_servers&diff=21366AppSuite:Debugging production servers2016-01-29T13:09:25Z<p>J.ohny.b: generate host key without password, since there currently is no easy way to provide a password to the connect middleware and it's cumbersome anyway</p>
<hr />
<div>{{Stability-experimental}}<br />
<div class="title">Debugging production servers</div><br />
<br />
A how-to for debugging your local UI plugin in combination with production and staging servers, which use redirects, HTTPS, and other things which break with the auto-generated Grunt configuration.<br />
<br />
== Introduction ==<br />
<br />
Not everybody has access to a dedicated test server. Sometimes, a plugin requires a backend system which is hard to access outside of existing infrastructure. And sometimes, a bug appears only on a production server. There are many reasons why a server which is used by <tt>grunt dev</tt> can not be reconfigured and has to be used as-is.<br />
<br />
The most frequent reason, why debugging with a remote server fails, is a redirect to an external login page. After the login, the browser is usually redirected to the original domain, and all the cookies used for authentication are also set for that domain, not http://localhost:8337, where Grunt listens.<br />
<br />
The solution is to use grunt as an HTTP proxy server, which intercepts all requests to the server's domain.<br />
<br />
In this article, <tt>''example.com''</tt> is user for the server domain, which you should replace by the real domain of the OX server.<br />
<br />
== Configuring Grunt ==<br />
<br />
Following is an example <tt>grunt/local.conf.json</tt> file:<br />
<br />
{<br />
"proxy": true,<br />
"appserver": {<br />
"protocol": "https",<br />
"livereload": false,<br />
"server": "https://''example.com''/''custom-path''/",<br />
"path": "/''custom-path''/",<br />
"rejectUnauthorized": false,<br />
"verbose": [],<br />
"prefixes": [<br />
"build/",<br />
"../../web/ui/build"<br />
],<br />
"manifests": [<br />
"build/manifests/",<br />
"../../web/ui/build/manifests"<br />
]<br />
},<br />
"debug": true,<br />
"coreDir": "../../web/ui/build"<br />
}<br />
<br />
The relevant settings are:<br />
<br />
;<tt>"proxy": true</tt><br />
: instructs Grunt to start an HTTP proxy on port 8080. Note that the value is outside the <tt>"appserver"</tt> section.<br />
;<tt>"protocol": "https"</tt><br />
: to use HTTPS on port 8337.<br />
;<tt>"livereload": false</tt><br />
: because LiveReload can't be proxied.<br />
;<tt>"path": "/''custom-path''/"</tt><br />
: must correspond to the path in the <tt>"server"</tt> entry and will in most cases be <tt>"/appsuite/"</tt>.<br />
;<tt>"rejectUnauthorized": false</tt><br />
: is helpful with certificate problems, especially for integration tests of the login mechanism on a POC system with self-signed certificates.<br />
;<tt>"index": "/"</tt><br />
: If the main path uses an HTTP redirect for a custom login page, then the initial request should not be intercepted by Grunt. Adding this entry in the <tt>"appserver"</tt> section disables the interception. This has two side-effects:<br />
:# The main HTML file is always served by the original server, and therefore<br />
:# The timestamps used for cache-busting are not updated by Grunt.<br />
:Unfortunately, this means clearing the cache will be required more often. If that becomes too much overhead, you can log in, then remove the option and restart Grunt.<br />
<br />
== Generating certificates ==<br />
<br />
To avoid unnecessary warnings in the browser, Grunt can use a set of certificates which are trusted by the browser. For local use, a self-created CA should be enough. By default, Grunt looks for the certificates in the subdirectory <tt>ssl</tt>, so you can create them there with the following commands:<br />
<br />
mkdir ssl<br />
cd ssl<br />
openssl req -x509 -newkey rsa:4096 -keyout rootCA.key -days 3650 -out rootCA.crt<br />
openssl req -newkey rsa:4096 -nodes -keyout host.key -out host.csr<br />
<br />
While most data entered for the certificates doesn't matter, the Common Name for the certificate signing request in the second <tt>openssl</tt> command must match the domain of the <tt>"server"</tt> setting in <tt>local.conf.json</tt>. Alternatively, it can be a higher domain with a wildcard, e.g. <tt>*.example.com</tt> for <tt>"server": "https://my.example.com/appsuite/"</tt>.<br />
<br />
openssl x509 -req -in host.csr -CA rootCA.crt -CAkey rootCA.key \<br />
-CAcreateserial -out host.crt -days 365<br />
<br />
<tt>host.csr</tt> is only temporary and can be deleted now. Only the files <tt>rootCA.crt</tt>, <tt>host.key</tt> and <tt>host.crt</tt> are used by Grunt. The <tt>rootCA.*</tt> files can be reused to create multiple host certificates. The first <tt>openssl</tt> command then only needs to be executed once. The <tt>host.*</tt> files can also be reused as long as the domain(-wildcard) matches.<br />
<br />
The file <tt>rootCA.crt</tt> needs to be imported into your browser as a trusted certificate authority (only once, if you are going to reuse the <tt>rootCA.*</tt> files). For Firefox, look in Preferences &rarr; Advanced &rarr; Certificates &rarr; View Certificates &rarr; Authorities &rarr; Import...<br />
For Chrome, look in Settings &rarr; Show advanced settings... &rarr; HTTPS/SSL &rarr; Manage certificates... &rarr; Authorities &rarr; Import...<br />
Since the authority can sign certificates for any domain, and its key is not protected much, it's better to not use the browser(-profile) which trusts this authority for normal Internet browsing.<br />
<br />
== Launching the browser ==<br />
<br />
After starting the proxy with <tt>grunt dev</tt>, you just need to use it when debugging your code in the browser. Most browsers have their own proxy settings, which override system-wide settings. They also respect environment variables like <tt>http_proxy</tt>.<br />
<br />
http_proxy=http://localhost:8080 firefox<br />
<br />
Chrome does not have its own settings (it starts the global settings dialog) and it ignores <tt>http_proxy</tt>. Instead, you have to use a command line parameter:<br />
<br />
chromium --proxy-server=http://localhost:8080</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Debugging_production_servers&diff=21365AppSuite:Debugging production servers2016-01-29T13:08:04Z<p>J.ohny.b: "path" options seems to be mandatory, at least we had problems when not setting this option to any value</p>
<hr />
<div>{{Stability-experimental}}<br />
<div class="title">Debugging production servers</div><br />
<br />
A how-to for debugging your local UI plugin in combination with production and staging servers, which use redirects, HTTPS, and other things which break with the auto-generated Grunt configuration.<br />
<br />
== Introduction ==<br />
<br />
Not everybody has access to a dedicated test server. Sometimes, a plugin requires a backend system which is hard to access outside of existing infrastructure. And sometimes, a bug appears only on a production server. There are many reasons why a server which is used by <tt>grunt dev</tt> can not be reconfigured and has to be used as-is.<br />
<br />
The most frequent reason, why debugging with a remote server fails, is a redirect to an external login page. After the login, the browser is usually redirected to the original domain, and all the cookies used for authentication are also set for that domain, not http://localhost:8337, where Grunt listens.<br />
<br />
The solution is to use grunt as an HTTP proxy server, which intercepts all requests to the server's domain.<br />
<br />
In this article, <tt>''example.com''</tt> is user for the server domain, which you should replace by the real domain of the OX server.<br />
<br />
== Configuring Grunt ==<br />
<br />
Following is an example <tt>grunt/local.conf.json</tt> file:<br />
<br />
{<br />
"proxy": true,<br />
"appserver": {<br />
"protocol": "https",<br />
"livereload": false,<br />
"server": "https://''example.com''/''custom-path''/",<br />
"path": "/''custom-path''/",<br />
"rejectUnauthorized": false,<br />
"verbose": [],<br />
"prefixes": [<br />
"build/",<br />
"../../web/ui/build"<br />
],<br />
"manifests": [<br />
"build/manifests/",<br />
"../../web/ui/build/manifests"<br />
]<br />
},<br />
"debug": true,<br />
"coreDir": "../../web/ui/build"<br />
}<br />
<br />
The relevant settings are:<br />
<br />
;<tt>"proxy": true</tt><br />
: instructs Grunt to start an HTTP proxy on port 8080. Note that the value is outside the <tt>"appserver"</tt> section.<br />
;<tt>"protocol": "https"</tt><br />
: to use HTTPS on port 8337.<br />
;<tt>"livereload": false</tt><br />
: because LiveReload can't be proxied.<br />
;<tt>"path": "/''custom-path''/"</tt><br />
: must correspond to the path in the <tt>"server"</tt> entry and will in most cases be <tt>"/appsuite/"</tt>.<br />
;<tt>"rejectUnauthorized": false</tt><br />
: is helpful with certificate problems, especially for integration tests of the login mechanism on a POC system with self-signed certificates.<br />
;<tt>"index": "/"</tt><br />
: If the main path uses an HTTP redirect for a custom login page, then the initial request should not be intercepted by Grunt. Adding this entry in the <tt>"appserver"</tt> section disables the interception. This has two side-effects:<br />
:# The main HTML file is always served by the original server, and therefore<br />
:# The timestamps used for cache-busting are not updated by Grunt.<br />
:Unfortunately, this means clearing the cache will be required more often. If that becomes too much overhead, you can log in, then remove the option and restart Grunt.<br />
<br />
== Generating certificates ==<br />
<br />
To avoid unnecessary warnings in the browser, Grunt can use a set of certificates which are trusted by the browser. For local use, a self-created CA should be enough. By default, Grunt looks for the certificates in the subdirectory <tt>ssl</tt>, so you can create them there with the following commands:<br />
<br />
mkdir ssl<br />
cd ssl<br />
openssl req -x509 -newkey rsa:4096 -keyout rootCA.key -days 3650 -out rootCA.crt<br />
openssl req -newkey rsa:4096 -keyout host.key -out host.csr<br />
<br />
While most data entered for the certificates doesn't matter, the Common Name for the certificate signing request in the second <tt>openssl</tt> command must match the domain of the <tt>"server"</tt> setting in <tt>local.conf.json</tt>. Alternatively, it can be a higher domain with a wildcard, e.g. <tt>*.example.com</tt> for <tt>"server": "https://my.example.com/appsuite/"</tt>.<br />
<br />
openssl x509 -req -in host.csr -CA rootCA.crt -CAkey rootCA.key \<br />
-CAcreateserial -out host.crt -days 365<br />
<br />
<tt>host.csr</tt> is only temporary and can be deleted now. Only the files <tt>rootCA.crt</tt>, <tt>host.key</tt> and <tt>host.crt</tt> are used by Grunt. The <tt>rootCA.*</tt> files can be reused to create multiple host certificates. The first <tt>openssl</tt> command then only needs to be executed once. The <tt>host.*</tt> files can also be reused as long as the domain(-wildcard) matches.<br />
<br />
The file <tt>rootCA.crt</tt> needs to be imported into your browser as a trusted certificate authority (only once, if you are going to reuse the <tt>rootCA.*</tt> files). For Firefox, look in Preferences &rarr; Advanced &rarr; Certificates &rarr; View Certificates &rarr; Authorities &rarr; Import...<br />
For Chrome, look in Settings &rarr; Show advanced settings... &rarr; HTTPS/SSL &rarr; Manage certificates... &rarr; Authorities &rarr; Import...<br />
Since the authority can sign certificates for any domain, and its key is not protected much, it's better to not use the browser(-profile) which trusts this authority for normal Internet browsing.<br />
<br />
== Launching the browser ==<br />
<br />
After starting the proxy with <tt>grunt dev</tt>, you just need to use it when debugging your code in the browser. Most browsers have their own proxy settings, which override system-wide settings. They also respect environment variables like <tt>http_proxy</tt>.<br />
<br />
http_proxy=http://localhost:8080 firefox<br />
<br />
Chrome does not have its own settings (it starts the global settings dialog) and it ignores <tt>http_proxy</tt>. Instead, you have to use a command line parameter:<br />
<br />
chromium --proxy-server=http://localhost:8080</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:I18n&diff=20193AppSuite:I18n2015-08-10T14:31:18Z<p>J.ohny.b: /* Custom Translations */</p>
<hr />
<div>{{Stability-experimental}}<br />
<br />
<div class="title">Internationalization (i18n)</div><br />
<br />
__TOC__<br />
<br />
Providing software for users in the whole world means providing it in multiple languages. This consists of two steps:<br />
<br />
* ''Internationalization (i18n)'' - Making the software international, i. e. preparing it to be adapted to different languages and locations.<br />
* ''Localization (L10n)'' - Adapting the software to a particular language and/or location.<br />
The Open-Xchange platform offers several facilities to simplify both parts. The L10n part is mostly a concern for translators. Open-Xchange facilities for that consist mainly of using a well-established format for translations: [http://www.gnu.org/s/gettext/ GNU Gettext] Portable Object (PO) files. This allows translators to use existing dedicated translation tools or a simple UTF-8-capable text editor.<br />
<br />
The i18n part is what software developers will be mostly dealing with and is what the rest of this document describes.<br />
<br />
==Translation==<br />
<br />
The main part of i18n is the translation of various text strings which are presented to the user. For this purpose, the Open-Xchange platform provides the RequireJS plugin <tt>'gettext'</tt>. Individual translation files are specified as a module dependency of the form <tt>'gettext!module_name'</tt>. The resulting module API is a function which can be called to translate a string using the specified translation files:<br />
<br />
<pre class="language-javascript"><br />
define('com.example/example', ['gettext!com.example/example'],<br />
function (gt) {<br />
'use strict';<br />
alert(gt('Hello, world!'));<br />
});<br />
</pre><br />
<br />
A file named ox.pot is created by the <tt>ox.pot</tt> (< 7.6.0) or <tt>create_pot</tt> (>= 7.6.0) tasks of the build system. So for versions < 7.6.0 you will need to run:<br />
<br />
$ build-appsuite ox.pot<br />
<br />
to create the file in the apps root directory.<br />
<br />
Working with a version starting from 7.6.0, the command:<br />
<br />
$ grunt create_pot<br />
<br />
will generate this file within the <tt>i18n/</tt> directory. It will contain an entry for every call to one of <tt>gettext</tt> functions:<br />
<br />
<pre class="language-gettext"><br />
#: apps/com.example/example.js:4<br />
msgid "Hello, world!"<br />
msgstr ""<br />
</pre><br />
<br />
This file can be sent to the translators and during the translation process, one PO file for each language will be created. The PO files in the directory <tt>i18n</tt> shoud contain the translated entry:<br />
<br />
<pre class="language-gettext"><br />
#: apps/com.example/example.js:4<br />
msgid "Hello, world!"<br />
msgstr "Hallo, Welt!"<br />
</pre><br />
<br />
During the next build, the entries are copied from the central PO files into individual translation files. In our example, this would be <tt>apps/com.example/example.de_DE.js</tt>. Because of the added language ID, translation files can usually have the same name as the corresponding main module. Multiple related modules should use the same translation file to avoid the overhead of too many small translation files.<br />
<br />
Most modules will require more complex translations than can be provided by a simple string lookup. To handle some of these cases, the <tt>gettext</tt> module provides traditional methods in addition to being a callable function. Other cases are handled by the build system.<br />
<br />
==Composite Phrases==<br />
<br />
In most cases, the translated texts will not be static, but contain dynamic values as parts of a sentence. The straight-forward approach of translating parts of the sentence individually and then using string concatenation to compose the final text is a BAD idea. Different languages have different sentence structures, and if there are multiple dynamic values, their order might need to be reversed in some languages, and not reversed in others.<br />
<br />
The solution is to translate entire sentences and then to use the <tt>gettext</tt> function to insert dynamic values:<br />
<br />
<pre class="language-javascript"><br />
alert(<br />
//#. %1$s is the given name<br />
//#. %2$s is the family name<br />
//#, c-format<br />
gt('Welcome, %1$s %2$s!', firstName, lastName));<br />
</pre><br />
<br />
Results in:<br />
<br />
<pre class="language-gettext"><br />
#. %1$s is the given name<br />
#. %2$s is the family name<br />
#, c-format<br />
msgid "Welcome, %1$s, %2$s"<br />
msgstr ""<br />
</pre><br />
<br />
As shown in the example, it is possible to add comments for translators by starting a comment with <tt>"#."</tt>. Such comments must be placed immediately before the name of the variable which refers to the <tt>gettext</tt> module (in this case <tt>gt</tt>). They can be separated by arbitrary whitespace and newlines, but not other tokens. All such <tt>gettext</tt> calls should have comments explaining every format specifier.<br />
<br />
Comments starting with <tt>"#,"</tt> are meant for <tt>gettext</tt> tools, which in the case of <tt>"#, c-format"</tt>, can ensure that the translator did not leave out or mistype any of the format specifiers.<br />
<br />
For the cases when the format string must be translated by one of the functions described below, there is a dedicated format function <tt>gettext.format</tt> which, except for debugging, is an alias for <tt>_.printf</tt>.<br />
<br />
==Debugging==<br />
<br />
One of the most common i18n errors is forgetting to use a <tt>gettext</tt> function. To catch this kind of error, the UI can be started with the hash parameter <tt>"#debug-i18n=1"</tt>. (Reloading of the browser tab is usually required for the setting to take effect.)<br />
<br />
In this mode, every translated string is marked with invisible Unicode characters, and any DOM text without those characters gets reported on the console. The <tt>gettext.format</tt> function then also checks that every parameter is translated. This is the reason why <tt>_.printf</tt> should not be used for user-visible strings directly.<br />
<br />
Unfortunately, this method will also report any string which does not actually require translation. Examples of such strings include user data, numbers, strings already translated by the server, etc. To avoid filling the console with such false positives, every such string must be marked by passing it through the function <tt>gettext.noI18n</tt>:<br />
<br />
<pre class="language-javascript"><br />
alert(<br />
//#. %1$s is the given name<br />
//#. %2$s is the family name<br />
//#, c-format<br />
gt('Welcome, %1$s %2$s!', gt.noI18n(firstName), gt.noI18n(lastName)));<br />
</pre><br />
<br />
This results in the strings being marked as 'translated' without actually changing their visible contents. When not debugging, <tt>gettext.noI18n</tt> simply returns its first parameter.<br />
<br />
==Advanced gettext Functions==<br />
<br />
Besides <tt>gettext.format</tt> and <tt>gettext.noI18n</tt> there are several other functions which are required to cover all typical translation scenarios.<br />
<br />
===Contexts===<br />
<br />
Sometimes, the same English word or phrase has multiple meanings and must be translated differently depending on context. To be able to tell the individual translations apart, the method <tt>gettext.pgettext</tt> ('p' stands for 'particular') should be used instead of calling <tt>gettext</tt> directly. It takes the context as the first parameter and the text to translate as the second parameter:<br />
<br />
<pre class="language-javascript"><br />
alert(gt.pgettext('description', 'Title'));<br />
alert(gt.pgettext('salutation', 'Title'));<br />
</pre><br />
<br />
Results in:<br />
<br />
<pre class="language-gettext"><br />
msctxt "description"<br />
msgid "Title"<br />
msgstr "Beschreibung"<br />
<br />
msctxt "salutation"<br />
msgid "Title"<br />
msgstr "Anrede"<br />
</pre><br />
<br />
===Plural forms===<br />
<br />
In the case of numbers, the rules to select the proper plural form can be very complex. With the exception of languages with no separate plural forms, English is the second simplest language in this respect, having only two plural forms: singular and plural. Other languages can have up to four forms, and theoretically even more. The functions <tt>gettext.ngettext</tt> and <tt>gettext.npgettext</tt> (for a combination of plural forms with contexts) can select the proper plural form by using a piece of executable code embedded in the header of a PO file:<br />
<br />
<pre class="language-javascript"><br />
alert(gt.format(<br />
//#. %1$d is the number of mails<br />
//#, c-format<br />
gt.ngettext('You have %1$d mail', 'You have %1$d mails', n),<br />
gt.noI18n(n)));<br />
</pre><br />
<br />
The function <tt>gettext.ngettext</tt> accepts three parameters: the English singular and plural forms and the number which determines the chosen plural form. The function <tt>gettext.npgettext</tt> adds a context parameter before the others, similar to <tt>gettext.pgettext</tt>. They are usually used in combination with <tt>gettext.format</tt> to insert the actual number into the translated string.<br />
<br />
The above example results in the following entry:<br />
<br />
<pre class="language-gettext"><br />
#. %1$d is the number of mails<br />
#, c-format<br />
msgid "You have %1$d mail"<br />
msgid_plural "You have %1$d mails"<br />
msgstr[0] ""<br />
msgstr[1] ""<br />
</pre><br />
<br />
The number of <tt>msgstr[N]</tt> lines is determined by the number of plural forms in each language. This number is specified in the header of each PO file, together with the code to compute the index of the correct plural form the supplied number.<br />
<br />
==Custom Translations==<br />
App Suite has support for custom translations. ''gettext'' provides a function ''addTranslation(dictionary, key, value)'' that allows adding custom translation to a global dictionary ("*") as well as replacing translations in existing dictionaries. In order to use this, you have to create a plugin for the "core" namespace. With release 7.8.0, there is a special namespace for exactly this use-case. It is now possible to define plugins to be loaded for the "i18n" namespace, which is loaded right before gettext is being enabled and after that the core plugins will be loaded.<br />
<br />
<pre class="language-javascript"> <br />
require(['io.ox/core/gettext'], function (gt) {<br />
gt.addTranslation('*', 'Existing translation', 'My custom translation');<br />
});<br />
</pre><br />
<br />
To keep it simple you can use the internal escaping to build counterparts for ngettext, pgettext, as well as npgettext. A context is escaped by \x00. Singular and plural are separated by \x01:<br />
<br />
<pre class="language-javascript"> <br />
// load gettext<br />
var gt = require('io.ox/core/gettext');<br />
<br />
// replace translation in dictionary 'io.ox/core'. Apps use context "app".<br />
gt.addTranslation('io.ox/core', 'app\x00Address Book', 'My Contacts');<br />
<br />
// replace translation - counterpart to ngettext<br />
gt.addTranslation('io.ox/mail', 'Copy\x01Copies', { 0: 'Kopie', 1: 'Kopien' });<br />
<br />
// replace translation - counterpart to npgettext<br />
gt.addTranslation('io.ox/mail', 'test\x00One\x01Two', { 0: 'Eins', 1: 'Zwei' });<br />
<br />
// some checks<br />
require(['gettext!io.ox/mail'], function (gt) {<br />
<br />
console.log('Sigular:', gt.ngettext('Copy', 'Copies', 1));<br />
console.log('Plural:', gt.ngettext('Copy', 'Copies', 2));<br />
<br />
console.log('Without context', gt.gettext('Files')); // should be 'My File Box'<br />
console.log('With context', gt.pgettext('test', 'Files')); // should be 'Files'<br />
<br />
console.log('Plural with context', gt.npgettext('test', 'One', 'Two', 2)); // should be 'Two'<br />
});<br />
<br />
</pre><br />
<br />
''addTranslation()'' always works for current language. If you want to benefit from automatic POT file generation, use the following approach:<br />
<br />
<pre class="language-javascript"> <br />
define('plugins/example/register', ['io.ox/core/gettext', 'gettext!custom'], function (gettext, gt) {<br />
<br />
'use strict';<br />
<br />
/* Just list all custom translations in your plugin by standalone gt() calls.<br />
The build system recognizes these strings and collects them in a POT file, <br />
so that they can be subject to translation processes. At runtime, gt('...')<br />
returns translations for current language.<br />
*/<br />
if (false) {<br />
gt('Tree');<br />
gt('House');<br />
gt('Dog');<br />
}<br />
<br />
// get internal dictionary (for current language of course)<br />
var dictionary = gt.getDictionary();<br />
<br />
// add all translations to global dictionary. Done!<br />
gettext.addTranslation('*', dictionary);<br />
});<br />
</pre><br />
<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]<br />
[[Category:Developer]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Theming&diff=19644AppSuite:Theming2015-06-01T13:56:47Z<p>J.ohny.b: /* Providing domain based login themes */</p>
<hr />
<div>{{Stability-experimental}}<br />
<br />
<div class="title">Theming</div><br />
<br />
'''Abstract.''' In this article, you can learn how to create customized themes and use them to change the look of you appsuite installation.<br />
__TOC__<br />
== LESS.JS ==<br />
Appsuite used LESS as dynamic stylesheet language. LESS extends CSS with dynamic behavior such as variables, mixins, operations and functions.<br />
<br />
Please read [http://lesscss.org/#docs LESS.JS] documentation first.<br />
<br />
=== Using less.js ===<br />
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. <br />
<br />
Backend packages for themes and any apps which ship .less files require the following changes:<br />
<br />
1. Add "skipLess=1" to the build command in *.spec and in debian/rules:<br />
sh /opt/open-xchange-appsuite-dev/bin/build-appsuite app skipLess=1<br />
2. Add %post and %postun sections to *.spec:<br />
%post<br />
if [ "$1" = 1 ]; then<br />
UPDATE=/opt/open-xchange/appsuite/share/update-themes.sh<br />
[ -x $UPDATE ] && $UPDATE<br />
fi<br />
%postun<br />
UPDATE=/opt/open-xchange/appsuite/share/update-themes.sh<br />
[ -x $UPDATE ] && $UPDATE<br />
<br />
For multiple binary packages, the %post and %postun sections should apply only to backend packages <br />
which contain .less files.<br />
<br />
3. Add debian/postinst and debian/postrm containing the same content:<br />
#!/bin/sh<br />
UPDATE=/opt/open-xchange/appsuite/share/update-themes.sh<br />
[ -x $UPDATE ] && $UPDATE<br />
<br />
For multiple binary packages, the postinst and postrm files should apply only to backend packages which contain .less files.<br />
<br />
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.<br />
<br />
== File structure ==<br />
A theme basically consists of two files located in <code>/opt/open-xchange/appsuite/apps/themes/<var>THEME_ID</var>/</code>. These files are described in this and the following sections.<br />
<br />
<code><var>THEME_ID</var></code> 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. <code>com.example.prettytheme</code>.<br />
<br />
=== definitions.less ===<br />
This file can be used to override variables described in the "Variables" section of this article.<br />
<br />
=== style.less ===<br />
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.<br />
<br />
=== Referencing paths ===<br />
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. <br />
<br />
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.<br />
<br />
== Variables ==<br />
<br />
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 "hovered" 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.<br />
<br />
Variables are defined in variables.less from twitter-bootstrap and our default definitions.less. Variables that are defined in definitions.less always override variables from bootstrap's variables.less<br />
<br />
=== Most relevant variables ===<br />
<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x)<br />
|-<br />
| @topbar-background || @topbarBackground || #3774A8 (blue)<br />
|-<br />
| --- || @topbarBackgroundAlt || No longer used<br />
|-<br />
| @topbar-launcher-color || @topbarLauncherColor || rgba(255, 255, 255, 0.70)<br />
|-<br />
| @topbar-launcher-color-hover || @topbarLauncherColor-hover || #fff<br />
|-<br />
| @topbar-launcher-color-active || @topbarLauncherColorActive || #fff<br />
|-<br />
| @topbar-launcher-background-hover || @topbarLauncherBackgroundHover || rgba(0, 0, 0, 0.30)<br />
|-<br />
| @topbar-launcher-background-active || @topbarLauncherBackgroundActive || rgba(0, 0, 0, 0.20);<br />
|-<br />
| @topbar-icon-color || @topbarIconColor || #fff<br />
|-<br />
| @selected-background || @selectedBackground || #428BCA (blue)<br />
|-<br />
| @contact-picture-radius || @contactPictureRadius || 50%<br />
|-<br />
|}<br />
:<br />
<br />
<br />
The most significant visual change is achieved by changing the following variables:<br />
* '''@topbar-background''' (top navigation bar)<br />
* '''@selected-background''' (selection background color in list views and folder tree)<br />
* '''@link-color''' (almost all hyperlinks)<br />
<br />
=== Font ===<br />
<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x)<br />
|-<br />
| @font-family-sans-serif || @sansFontFamily || "Helvetica Neue", Helvetica, Arial, sans-serif<br />
|-<br />
| @font-family-serif || @serifFontFamily || Georgia, "Times New Roman", Times, serif<br />
|-<br />
| @font-family-monospace || @monoFontFamily || Monaco, Menlo, Consolas, "Courier New", monospace<br />
|-<br />
| @font-size-base || @baseFontSize || 14px<br />
|-<br />
| || @baseFontFamily ||<br />
|-<br />
| @line-height-base || @baseLineHeight || 1.428571429; // 20/14<br />
|-<br />
| || @altFontFamily ||<br />
|-<br />
| @font-size-touch || @touchFontSize || 15px<br />
|-<br />
| @headings-font-family || @headingsFontFamily || inherit<br />
|-<br />
| @headings-font-weight || @headingsFontWeight || 500<br />
|-<br />
| @font-size-large || @fontSizeLarge || ceil((@font-size-base * 1.25)); // ~18px<br />
|-<br />
| @font-size-small || @fontSizeSmall || ceil((@font-size-base * 0.85)); // ~12px<br />
|-<br />
| || @fontSizeMini ||<br />
|-<br />
| @vgrid-font-size || @vgridFontSize || 13px<br />
|}<br />
<br />
=== Colors ===<br />
<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x)<br />
|-<br />
| @background || @bodyBackground || #fff<br />
|-<br />
| @text-color || @textColor || @gray-dark<br />
|-<br />
| @link-color || @linkColor || @brand-primary<br />
|-<br />
| @link-hover-color || @linkColorHover || darken(@link-color, 15%)<br />
|-<br />
| @link-accent-color || @linkAccentColor || #ffad00<br />
|-<br />
| || @linkAccentColorHover ||<br />
|-<br />
| @badge-color || @badgeColor || @white<br />
|-<br />
| @badge-bg || @badgeBackground || #aaa<br />
|-<br />
| @headings-color || @headingsColor || inherit<br />
|-<br />
| @black || @black || #000<br />
|-<br />
| @gray-darker || @grayDarker || #222<br />
|-<br />
| @gray-dark || @grayDark || #333<br />
|-<br />
| @gray || @gray || #555<br />
|-<br />
| @gray-light || @grayLight || #999<br />
|-<br />
| @gray-lighter || @grayLighter || #eee<br />
|-<br />
| @white || @white || #fff<br />
|-<br />
| @blue || @blue || darken(#049cdb, 5%)<br />
|-<br />
| @blue-dark || @blueDark || #0064cd<br />
|-<br />
| @blue-light || @blueLight || lighten(#049cdb, 25%)<br />
|-<br />
| @green || @green || #1A8D1A<br />
|-<br />
| @green-light || @greenLight || #92D050<br />
|-<br />
| @red || @red || #cc0000<br />
|-<br />
| @yellow || @yellow || #F8E400<br />
|-<br />
| @orange || @orange || #f89406<br />
|-<br />
| @pink || @pink || #E01CD9<br />
|-<br />
| @purple || @purple || #7E16CF<br />
|-<br />
|}<br />
<br />
=== Space ===<br />
<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x)<br />
|-<br />
| || @paddingLarge ||<br />
|-<br />
| || @paddingSmall ||<br />
|-<br />
| || @paddingMini ||<br />
|-<br />
| @border-radius-base || @baseBorderRadius || 4px<br />
|-<br />
| @border-radius-large || @borderRadiusLarge || 6px<br />
|-<br />
| @border-radius-small || @borderRadiusSmall || 3px<br />
|}<br />
<br />
=== Pagination ===<br />
<br />
Used where pagination is done, for example in the Calendar weekview, each week is on one "page"; one can switch the week using a pagination widget styled with these variables.<br />
<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x)<br />
|-<br />
| @pagination-bg || @paginationBackground || #fff<br />
|-<br />
| @pagination-border || @paginationBorder || #ddd<br />
|-<br />
| @pagination-active-bg || @paginationActiveBackground || @brand-primary<br />
|}<br />
<br />
=== Buttons ===<br />
<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x)<br />
|-<br />
| || @btnBackground ||<br />
|-<br />
| || @btnBackgroundHighlight ||<br />
|-<br />
| || @btnBorder ||<br />
|-<br />
| @btn-primary-bg || @btnPrimaryBackground || @link-color<br />
|-<br />
| || @btnPrimaryBackgroundHighlight ||<br />
|-<br />
| @btn-info-bg || @btnInfoBackground || #5bc0de<br />
|-<br />
| || @btnInfoBackgroundHighlight ||<br />
|-<br />
| @btn-success-bg || @btnSuccessBackground || #62c462<br />
|-<br />
| || @btnSuccessBackgroundHighlight ||<br />
|-<br />
| @btn-warning-bg || @btnWarningBackground || lighten(@orange, 15%)<br />
|-<br />
| || @btnWarningBackgroundHighlight ||<br />
|-<br />
| @btn-danger-bg || @btnDangerBackground || #ee5f5b<br />
|-<br />
| || @btnDangerBackgroundHighlight ||<br />
|-<br />
| @btn-inverse-bg || @btnInverseBackground || #444<br />
|-<br />
| || @btnInverseBackgroundHighlight ||<br />
|}<br />
<br />
=== Dropdowns ===<br />
<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x)<br />
|-<br />
| @dropdown-bg || @dropdownBackground || #fff<br />
|-<br />
| @dropdown-border || @dropdownBorder || rgba(0,0,0,.15)<br />
|-<br />
| @dropdown-divider-bg || @dropdownDividerTop || #e5e5e5<br />
|-<br />
| @dropdown-divider-bg || @dropdownDividerBottom || #e5e5e5<br />
|-<br />
| @dropdown-link-color || @dropdownLinkColor || @gray-dark<br />
|-<br />
| @dropdown-link-hover-color || @dropdownLinkColorHover || darken(@gray-dark, 5%)<br />
|-<br />
| @dropdown-link-active-color || @dropdownLinkColorActive || @component-active-color<br />
|-<br />
| @dropdown-link-active-bg || @dropdownLinkBackgroundActive || @component-active-bg<br />
|-<br />
| @dropdown-link-hover-bg || @dropdownLinkBackgroundHover || #f5f5f5<br />
|}<br />
<br />
=== Foldertree ===<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x) || Description<br />
|-<br />
| @foldertree-sidepane-background || @foldertreeSidepanelBackground || #f5f5f5 ||<br />
|-<br />
| @foldertee-section-title-color || @foldertreeSectionTitleColor || #888 || Color for sectiontitles in foldertree (like "Public" folders)<br />
|-<br />
| @foldertree-active-label-color || @foldertreeActiveLabelColor || #333 || ''Active'' means, user can perform an action on this item<br />
|-<br />
|@foldertree-passive-label-color || @foldertreePassiveLabelColor ||@hc-gray || ''Passive'' means, user can '''not''' perform any action with this item <br />
|-<br />
| @foldertree-hover-background || @foldertreeHoverBackground || rgba(0, 0, 0, 0.05) ||<br />
|-<br />
| @foldertree-selected-background || @foldertreeSelectedBackground || rgba(0, 0, 0, 0.10) ||<br />
|-<br />
| @foldertree-badge-background || @foldertreeBadgeBackground || @bagde-bg || see [[#Colors]] for definition of @badge-bg<br />
|-<br />
| @foldertree-badge-color || @foldertreeBadgeColor ||@badge-color || see [[#Colors]] for definition of @badge-color<br />
|}<br />
<br />
=== Calendar ===<br />
==== Appointment ====<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x) || Description<br />
|-<br />
| @appointment-reserved || @appointmentReserved || #08c /* blue */ || Appointment status color<br />
|-<br />
| @appointment-temporary || @appointmentTemporary || #ffbb00 /* yellow */ || Appointment status color<br />
|-<br />
| @appointment-absent || @appointmentAbsent || #913f3f /* red */ || Appointment status color<br />
|-<br />
| @appointment-free || @appointmentFree || #8eb360 /* green */ || Appointment status color<br />
|-<br />
| @appointment-private || @appointmentPrivate || #555 /* gray */ || Appointment status color<br />
|-<br />
| @appointment-declined-font || @appointmentDeclinedFont || #888 /* dark gray */|| Font color for declined Appointments<br />
|-<br />
| @appointment-unconfirmed-alpha || @appointmentUnconfirmedAlpha || 0.4 || Transparency value for unconfirmed Appointments<br />
|-<br />
| @appointment-declined-alpha || @appointmentDeclinedAlpha || 0.3 || Transparency value for declined Appointments<br />
|-<br />
| @appointment-hover-pct || @appointmentHoverPct || 15% || Percentage increase of the dark pigment content on hover effect<br />
|}<br />
<br />
==== Week View ====<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x) || Description<br />
|-<br />
| @weekview-appointment-lasso || @weekviewAppointmentLasso || #aeaeff || Lasso frame color<br />
|-<br />
| @weekview-day-in || @weekviewDayIn || #fff /* white */ || Default background color in week view<br />
|-<br />
| @weekview-day-out || @weekviewDayOut || #e0e0e0 /* grey */ || Background color outside of the mean working time<br />
|-<br />
| @weekview-timeline || @weekviewTimeline || rgba(243, 15, 170, 0.4) || Color of the Line indicating the current time<br />
|-<br />
| @weekview-time-width || @weekviewTimeWith || 58px || With of the time labels on the left side<br />
|-<br />
| @calendar-toolbar-height || @calendarToolbarHeight || 47px || Height of the control toolbar <br />
|}<br />
<br />
==== Month View ====<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x) || Description<br />
|-<br />
| @monthview-appointment-out || @monthviewAppointmentOut || #aaa /* light gray */ || Color of appointments, which are not in focus<br />
|-<br />
| @monthview-today || @monthviewToday || #daefff /* light blue */|| Background color of the current day<br />
|-<br />
| || @monthview@calendarToolbarHeight || ||<br />
|}<br />
<br />
== Area names ==<br />
<br />
The variables sometimes refer to common areas. To identify which area is located where, see the following annotated screenshots.<br />
<br />
[[File:Colors_mail.png|800px||Mail application]]<br />
<br />
[[File:Colors_launchpad.png|800px||Launchpad application]]<br />
<br />
== Replacing the logo ==<br />
<br />
One of the most common theme changes which requires editing <code>style.css</code> is changing the logo in the top right corner. The logo is displayed as the background image for an element with the ID <code>io-ox-top-logo-small</code>. A theme can therefore change the size and URL of the image:<br />
#io-ox-top-logo-small {<br />
width: 60px;<br />
height: 22px;<br />
margin: 8px 13px 0 13px;<br />
background-image: url(path/to/mylogo.png);<br />
}<br />
The file <code>mylogo.png</code> is expected to be in the same directory as <code>style.css</code>. Reference the entire path to the file, relative to the folder containing your ui, but without the leading slash. For example, the Core reference would be <code>apps/themes/default/logo-small.png</code>. If you want to place the image somewhere else, then use a relative path in <code>url()</code>.<br />
<br />
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 <code>/var/www/appsuite/</code> (or similar, depending on the target platform) instead of <code>/opt/open-xchange/appsuite/</code>. Direct support for multiple packages is coming in version 7.4.1. Until then, use the build system from the <code>develop</code> branch to [[AppSuite:UI_build_system#init-packaging|initialize the packaging]] if your theme contains images.<br />
<br />
== Mixins ==<br />
In LESS, it is possible to include a bunch of properties from one ruleset into another ruleset. So say we have the following class:<br />
<br />
=== Sample ===<br />
<pre class="language-css"><br />
.border-radius(@radius: 0px) {<br />
-webkit-border-radius: @radius;<br />
-moz-border-radius: @radius;<br />
-ms-border-radius: @radius;<br />
border-radius: @radius;<br />
}<br />
<br />
#menu a {<br />
color: #111;<br />
.border-radius(5px);<br />
}<br />
</pre><br />
<br />
Read [http://www.lesscss.org/#-mixins LESS Mixins]<br />
<br />
=== global OX Mixins ===<br />
<br />
they can be found at [[#definitions.less | definitions.less]]<br />
<br />
{|<br />
! Mixin || Sample || Description <br />
|-<br />
| .box-sizing(@boxmodel) || .box-sizing(border-box) ||<br />
|-<br />
| .user-select(@select) || .user-select(none) ||<br />
|-<br />
| .border-radius(@radius) || .border-radius(3px) ||<br />
|-<br />
| .box-shadow(@shadow) || .box-shadow(3px) ||<br />
|-<br />
| .vertical-gradient(@startColor, @endColor) || .vertical-gradient(#888, #555) ||<br />
|-<br />
| .radial-gradient(@color1, @color2, @color3) || .radial-gradient(#111, #222, #333) ||<br />
|-<br />
| .transition(@transition) || .transition(background-color 0.2s linear) ||<br />
|-<br />
| .animation(@animation) || .animation(slidein 300ms) ||<br />
|-<br />
| .animation-name(@name) || .animation-name(slideright) ||<br />
|-<br />
| .ellipsis || .ellipsis ||<br />
|-<br />
| .overflow(@type) || .overflow(hidden) ||<br />
|-<br />
| .overflow-x(@type) || .overflow-x(hidden) ||<br />
|-<br />
| .overflow-y(@type) || .overflow-y(hidden) ||<br />
|-<br />
| .backface-visibility(@type) || .backface-visibility(hidden) ||<br />
|}<br />
<br />
== How to activate a theme during development ==<br />
<br />
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]].<br />
<br />
# First, you need to add the theme to the list of available themes on the backend. Simply create a new file in <code>/opt/open-xchange/etc/settings/</code> with the extension <code>.properties</code> and add a line for your theme to it: <pre>io.ox/core/settingOptions//themes/<var>ID</var>=<var>Theme Name</var></pre> Replace <var>ID</var> by the identifier (directory name) of your theme, and <var>Theme Name</var> by the human-readable name which should appear in the UI.<br />
# The server needs to be restarted to read the new settings.<br />
# Now, you can use <code>appserver</code> ([[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.<br />
# Finally, activate your theme the list in the <code>Settings -> Basic</code> view behind the option <code>Theme</code>.<br />
<br />
In case you also want to access the same backend without <code>appserver</code> while your theme is selected, that theme (or at least some empty <code>.less</code> files) should be also installed on the backend to avoid errors. To just use an empty theme, run the following as root:<br />
touch /opt/open-xchange/appsuite/apps/themes/<var>ID</var>/definitions.less<br />
touch /opt/open-xchange/appsuite/apps/themes/<var>ID</var>/style.less<br />
/opt/open-xchange/appsuite/share/update-themes.sh<br />
The value of <var>ID</var> here must be the same as in your <code>.properties</code> file.<br />
<br />
== Favicons and mobile homescreen icons ==<br />
'''Note''': This chapter is not about changing AppSuite icons which are used in the application like the brand on the upper right.<br />
<br />
'''This documentation applies for AppSuite v.7.4.2'''<br />
<br />
AppSuite ships with a standard set of icons containing a<br />
# favicon<br />
# set of touch icons which are mainly used by mobile Safari on iOS<br />
<br />
These icons are used as default for all devices and browsers as long as you don't deliver your own icons with your theme. To provide your own icons, put them into your theme's directory, e.g. <tt>apps/themes/<var>theme-name</var>/favicon.ico</tt>.<br />
<br />
=== Favicon ===<br />
All major browsers support the use of a favicon. The favicon is a pixel image with the size of 16x16 (32x32) and the "ico" file ending. (see [http://en.wikipedia.org/wiki/Favicon Wikipedia Favicon] for details).<br />
<br />
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 <tt>apps/themes/icons/default</tt> on the web server.<br />
<br />
'''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 <tt>favicon.ico</tt> located under <tt>apps/themes/icons/default</tt> on the web server must be replaced with a custom version.<br />
<br />
=== Apple touch icons ===<br />
iOS devices (iPhone/iPad/iPod) support so called "Webclips". 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 "Add to homescreen" 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.<br />
<br />
* iPhone 3: 57 x 57 px<br />
* iPhone 4 retina: 114x114 px<br />
* iPhone iPhone 5 retina: 120 x 120 px<br />
* iPad: 72 x 72 px<br />
* iPad (iOS 7): 76 x 76px<br />
* iPad retina: 144 x 144 px <br />
* iPad retina (iOS 7): 152 x 152 px <br />
<br />
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<br />
<br />
* iPhone: 320 x 460 px<br />
* iPhone 4: 640 x 920 px<br />
* iPhone 5: 640 x 1096 px<br />
<br />
'''Note''': We do not provide splashscreens for iPad<br />
<br />
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.<br />
<br />
=== Providing custom icons ===<br />
To provide custom Webclip icons locate the following path in the AppSuite installation on your web server:<br />
pathToAppSuite/apps/themes/icons/default<br />
<br />
This folder contains all OX default icons for Webclips icons and splashscreens. Use these as samples for your own versions.<br />
<br />
A clean installation will have all our default icons in the "default" 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 <tt>.htaccess</tt> file located under <tt>apps/themes</tt><br />
<br />
== Theming the login page ==<br />
<br />
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]].<br />
<br />
=== Providing domain based login themes ===<br />
<br />
This is only needed when using the default login screen shipped with the Core UI and for versions older than the 7.8.0 release. Since 7.8.0, the normal way via as-config.yml will work just fine ([[AppSuite:Theming the login page|Learn how to do this here]]).<br />
<br />
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 <tt>.htaccess</tt> is located under <tt>apps/themes</tt><br />
<br />
<pre><br />
# Sample config for domain based login theme<br />
RewriteCond %{HTTP_HOST} ^www\.domain\.com$<br />
RewriteCond %{REQUEST_FILENAME} -f<br />
RewriteRule ^login/(.*)$ domain_com_logintheme/$1 [L]<br />
</pre><br />
<br />
== Best practice ==<br />
<br />
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.<br />
<br />
Of course, using CSS in <code>style.less</code> 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.<br />
<br />
== Links ==<br />
[http://bradfrost.github.io/this-is-responsive/resources.html Responsive design]<br />
<br />
[http://lesscss.org/ LESS]<br />
<br />
== Caveats ==<br />
<br />
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.<br />
<br />
As mentioned before, changing colors should be safe.<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]<br />
[[Category:Custom development]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Test_basics&diff=19092AppSuite:Test basics2015-02-04T09:25:44Z<p>J.ohny.b: </p>
<hr />
<div>Lessons learned painfully while doing testing:<br />
* PhantomJS thinks it is a '''touch''' device (Modernizr.touch === true)<br />
* Almost no CSS is loaded. Don't rely on classes like "hidden" (bootstrap class)<br />
* Turn off CSS transitions - any DOM-based test might fail (caused by random DOM reflow delay)<br />
* Don't clear <body> (e.g. $(document.body).empty()). UI is not robust against that.<br />
** instead attach a new element to the body (or anywhere else) and remove it afterwards<br />
* The browser window is elastic. It has no fixed size. Usually affects scrolling tests.<br />
* If weird things happen, try to check if your app/window/node is really still in the DOM.<br />
* HTML fixtures cannot be loaded (don't know why yet); just rename your files to *.txt<br />
* Please mind that your fake server only works inside "description" (not across specs)<br />
* PhantomJS fails at: Date.parse("2012-01-01"); (see https://code.google.com/p/phantomjs/issues/detail?id=267#c2)<br />
* The fake server is global, if someone has registered your desired api/call already, your response will never get send<br />
** it is possible to use this.server.responses.filter to remove another problematic response implementation<br />
* Any checks for z-index will probably fail due to the lack of loaded CSS. If an element is not positioned (relative or absolute) the computed style is "auto". Just reposition affected elements in your spec.<br />
<br />
== Testing on windows ==<br />
<br />
A small and incomplete collection of things, that might work different on a windows machine:<br />
<br />
* the UI relies on zonefiles being installed on the system, this is the case for mac and linux environments, but not on windows<br />
** it should be possible to install the zonefiles manually and make karma serve those<br />
<br />
[[Category:UI]]<br />
[[Category:AppSuite]]<br />
[[Category:Developer]]<br />
[[Category:Testing]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GruntFAQ&diff=18312AppSuite:GruntFAQ2014-08-25T10:02:46Z<p>J.ohny.b: /* I get an 'local grunt not found' error message when running grunt */</p>
<hr />
<div><div class="title">FAQ regarding Grunt configuration for App Suite UI plugins</div><br />
<br />
= Building UI plugins =<br />
<br />
== I get a 'local grunt not found' error message when running grunt ==<br />
<br />
The local dependencies are not installed, run <tt>npm install</tt> to install them. After that, the error message should be gone.<br />
<br />
== Some of my files aren't copied. How can I extend a copy sub-task? ==<br />
<br />
Especially when using external libraries managed with Bower or npm, sometimes the [https://github.com/Open-Xchange-Frontend/shared-grunt-config shared grunt configuration] doesn't contain all cases for files to be copied during the build or dist tasks. Due to the [https://johnyb.github.io/2014/05/10/extending_shared_grunt_configuration.html extensibility] of our shared grunt configuration, it's quite easy to add those missing files. You can hook up into the <tt>build_*</tt> or the <tt>dist_*</tt> copy task and add your custom configuration like this:<br />
<br />
<syntaxhighlight lang="JavaScript"><br />
'use strict';<br />
<br />
module.exports = function (grunt) {<br />
grunt.config.extend('copy', {<br />
build_custom: {<br />
files: [{<br />
expand: true,<br />
src: ['apps/**/*.in', 'apps/**/manifest.json'],<br />
dest: 'build/'<br />
}]<br />
}<br />
});<br />
};<br />
</syntaxhighlight><br />
<br />
Put this in a file inside the <tt>grunt/tasks</tt> directory and you are done. From now on, all files ending with .in and all manifest.json files are put into the <tt>build/</tt> directory using the same structure as in the <tt>apps/</tt> directory. For more detailed information see [http://gruntjs.com/configuring-tasks#files the grunt documentation on files].<br />
<br />
= Using development proxy server =<br />
<br />
== Which version of appserver am I using? ==<br />
<br />
npm can be used to find out about the versions. <tt>npm ls appserver</tt> will list the version of appserver that is used.<br />
<br />
== I get an error message 'Port 8337 already in use by another process', what can I do? ==<br />
<br />
Only one instance of appserver is allowed at a time (this might be subject to change, though). You have multiple options to start developing on that plugin. Number one:<br />
<br />
# Close all other running instances of appserver and run it exclusively in one project<br />
<br />
Yes, that was the easy one. However, you might want to serve multiple projects at once. This case would need a little more configuration:<br />
<br />
# Choose one base UI project<br />
# Edit <tt>grunt/local.conf.json</tt><br />
# add all build/ directories you want to serve to "prefixes" array<br />
# Run <tt>grunt connect watch</tt> in the base directory<br />
# In all other UI projects you want to develop, only run <tt>grunt watch</tt> without the connect task<br />
<br />
Appserver will in this case do all the cache busting for you (it uses the timestamp of the "newest" directory in the prefix list) and if you didn't de-activate live-reload, it will work for all projects (the watch task sends livereload events to appserver, which will trigger the reload in the browser).</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GruntFAQ&diff=18311AppSuite:GruntFAQ2014-08-25T10:02:09Z<p>J.ohny.b: /* Building UI plugins */</p>
<hr />
<div><div class="title">FAQ regarding Grunt configuration for App Suite UI plugins</div><br />
<br />
= Building UI plugins =<br />
<br />
== I get an 'local grunt not found' error message when running grunt ==<br />
<br />
The local dependencies are not installed, run <tt>npm install</tt> to install them. After that, the error message should be gone.<br />
<br />
== Some of my files aren't copied. How can I extend a copy sub-task? ==<br />
<br />
Especially when using external libraries managed with Bower or npm, sometimes the [https://github.com/Open-Xchange-Frontend/shared-grunt-config shared grunt configuration] doesn't contain all cases for files to be copied during the build or dist tasks. Due to the [https://johnyb.github.io/2014/05/10/extending_shared_grunt_configuration.html extensibility] of our shared grunt configuration, it's quite easy to add those missing files. You can hook up into the <tt>build_*</tt> or the <tt>dist_*</tt> copy task and add your custom configuration like this:<br />
<br />
<syntaxhighlight lang="JavaScript"><br />
'use strict';<br />
<br />
module.exports = function (grunt) {<br />
grunt.config.extend('copy', {<br />
build_custom: {<br />
files: [{<br />
expand: true,<br />
src: ['apps/**/*.in', 'apps/**/manifest.json'],<br />
dest: 'build/'<br />
}]<br />
}<br />
});<br />
};<br />
</syntaxhighlight><br />
<br />
Put this in a file inside the <tt>grunt/tasks</tt> directory and you are done. From now on, all files ending with .in and all manifest.json files are put into the <tt>build/</tt> directory using the same structure as in the <tt>apps/</tt> directory. For more detailed information see [http://gruntjs.com/configuring-tasks#files the grunt documentation on files].<br />
<br />
= Using development proxy server =<br />
<br />
== Which version of appserver am I using? ==<br />
<br />
npm can be used to find out about the versions. <tt>npm ls appserver</tt> will list the version of appserver that is used.<br />
<br />
== I get an error message 'Port 8337 already in use by another process', what can I do? ==<br />
<br />
Only one instance of appserver is allowed at a time (this might be subject to change, though). You have multiple options to start developing on that plugin. Number one:<br />
<br />
# Close all other running instances of appserver and run it exclusively in one project<br />
<br />
Yes, that was the easy one. However, you might want to serve multiple projects at once. This case would need a little more configuration:<br />
<br />
# Choose one base UI project<br />
# Edit <tt>grunt/local.conf.json</tt><br />
# add all build/ directories you want to serve to "prefixes" array<br />
# Run <tt>grunt connect watch</tt> in the base directory<br />
# In all other UI projects you want to develop, only run <tt>grunt watch</tt> without the connect task<br />
<br />
Appserver will in this case do all the cache busting for you (it uses the timestamp of the "newest" directory in the prefix list) and if you didn't de-activate live-reload, it will work for all projects (the watch task sends livereload events to appserver, which will trigger the reload in the browser).</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GettingStarted_7.6.0&diff=18305AppSuite:GettingStarted 7.6.02014-08-22T12:17:21Z<p>J.ohny.b: /* Packaging */</p>
<hr />
<div><div class="title">Getting Started</div><br />
<br />
<div style="float:right; padding: 10px;">[[File:OX_AppSuite_UI_Development_Workflow.png]]</div><br />
<br />
__TOC__<br />
<br />
Hello and welcome to the world of OX App Suite development. This document is designed to get you started with developing your first app for OX App Suite as quickly and simply as possible. However, along the way we will also tempt you to learn more by linking to some more in-depth documentation about the various topics we cover. <br />
<br />
<div style="clear: both"></div><br />
<br />
== Installing the Development Tools ==<br />
<br />
First, you need to install some tools which are necessary for UI development.<br />
<br />
TL;DR version:<br />
<br />
$ npm install -g grunt-cli bower yo generator-ox-ui-module<br />
<br />
Or if needed, there is a complete article about setting up an environment for [[AppSuite:GettingStartedWithGrunt#Node | grunt]].<br />
<br />
== Create a Workspace ==<br />
<br />
All your app development can take place inside one working directory. The examples will assume you have created a directory named <tt>myapp</tt> inside your home directory:<br />
<br />
$ mkdir ~/myapp<br />
$ cd ~/myapp<br />
<br />
It doesn't need to be in your home directory, and the actual name should reflect the name of your app. The main point is that all commands will be executed in this directory and all paths will be relative to this directory from now on.<br />
<br />
Now, you can generate a grunt configuration using:<br />
<br />
$ yo ox-ui-module<br />
<br />
The source code of your app will reside in the subfolder named <tt>apps</tt>. To avoid name collisions please pick a unique subfolder inside that. The easiest and recommended way is to use a domain name that you own. Typically, the domain elements are reversed, like in Java. <tt>example.com</tt> becomes <tt>com.example</tt>:<br />
<br />
$ mkdir -p apps/com.example<br />
<br />
== Writing an App ==<br />
<br />
As an example, let's create the smallest possible app and test it. It requires only two files: <tt>apps/com.example/register.js</tt> for the source code of the app:<br />
<br />
<pre class="language-javascript"><br />
define('com.example/register', function () {<br />
'use strict';<br />
alert('Hello, World!');<br />
});<br />
</pre><br />
<br />
and <tt>apps/com.example/manifest.json</tt> for the [[AppSuite:UI_manifests_explained | manifest]] which tells the UI that your app exists and what to do with it:<br />
<br />
<pre class="language-javascript">{ "namespace": "core" }</pre><br />
<br />
== Building an App ==<br />
<br />
The source code of your app can't be used by OX App Suite as it. It first has to be processed by the [[AppSuite:UI_build_system | build system]]. This step will check the source code for syntax errors, compress it, and depending on the structure of your code, many other things. The processed code is then written to a directory named <tt>build</tt> by default. Start the build with this command:<br />
<br />
$ grunt<br />
<br />
If your editor supports it, you can configure it to call the build system after every file save. Take care to call it from the top directory of your app's workspace, not from the directory of the saved file.<br />
<br />
== Testing an App ==<br />
<br />
The freshly built code can now be tested. Instead of uploading your code to an OX App Suite server, you can use the [[AppSuite:Appserver|appserver]] proxy to inject your code into the UI code of any existing OX App Suite installation. For example, to start <tt>appserver</tt> using [https://www.ox.io/ www.ox.io] as the server, you will need a local configuration pointing to that server. You can register for a free user account on [https://www.ox.io/ www.ox.io]. <br />
<br />
You can generate it with this command:<br />
<br />
$ grunt show-config:local --output grunt/local.conf.json<br />
<br />
To use the remote www.ox.io AppSuite server, open up the file <tt>grunt/local.conf.json</tt> in your editor and add <tt><nowiki>"https://www.ox.io/appsuite/"</nowiki></tt> to the <tt>server</tt> setting of the appserver section. Also make sure that the <tt>protocol</tt> setting is set to <tt>https</tt>. Else you will get https redirect errors in your browser. <br />
<br />
INFO: If you are using a locally installed AppSuite server (in a VM or similar in your local network), just modify the server + protocol accordingly.<br />
<br />
Then start the development server:<br />
<br />
$ grunt dev<br />
<br />
This command will serve your app from the local directory <tt>build</tt>, and get everything else from the URL specified in the <tt>server</tt> setting.<br />
<br />
Once appserver is running, you can access OX App Suite by opening your browser using this address:<br />
<br />
https://localhost:8337/appsuite<br />
<br />
After logging in, the app should be loaded and display the <tt>alert</tt> message.<br />
<br />
=== Development cycle ===<br />
<br />
Once you are sure that your setup works, you can extend the example and write the actual code for your app. The <tt>dev</tt> task will detect any changes and rebuild your app and even reload all browsers connected to<br />
<tt>https://localhost:8337/appsuite</tt>.<br />
<br />
While developing always keep in mind, that there is an [[AppSuite:Debugging_the_UI | article about debugging the user interface]] which helps you avoid and fix typical errors.<br />
<br />
== Packaging ==<br />
<br />
Together with our ox-ui-module generator (from version 0.8.0), we provide you with generators for packaging information.<br />
<br />
To enable some grunt tasks to help you, you can optionally install (with npm install) <tt>grunt-contrib-compress</tt> and <tt>grunt-exec</tt>. You will need those to run the grunt tasks described below.<br />
<br />
=== Generating a source tarball ===<br />
<br />
There is a grunt tasks to create source packages. You can use it to create a tar.gz file containing all sources needed to build the project. This source package should contain all dependencies that are installed during <tt>bower install</tt> and <tt>npm install</tt> (basically containing your <tt>bower_components</tt> and <tt>node_modules</tt> directories). To generate this file, run:<br />
<br />
$ grunt dist:source --include-dependencies<br />
<br />
This file (can be found in the <tt>dist/</tt> directory) together with the distribution specific packaging information will be needed to build the package.<br />
<br />
=== RPM packages ===<br />
<br />
In order to generate a spec file for your project, run:<br />
<br />
$ yo ox-ui-module:rpm-pkg<br />
<br />
Will generate a spec file, with some default values read from your <tt>package.json</tt> file. So you might want to make this file as complete as possible. However, yeoman will ask you about the information missing.<br />
<br />
''Please remember, that the generated spec file is only a scaffold for a real one. It will work for very simple cases, but as soon as your package gets more complicated, you will have to adjust it to your needs.''<br />
<br />
Once you have the spec file in your project, you can use<br />
<br />
$ grunt rpm-build --include-dependencies # or rpm-build if you are on shared-grunt-config-0.6.0<br />
<br />
=== DEB packages ===<br />
<br />
Creating packages for the Debian distribution works similar to rpm packages. Just run:<br />
<br />
$ yo ox-ui-module:deb-pkg<br />
<br />
This will generate a debian directory containing all needed files. Some default values will be read from your <tt>package.json</tt> file. So you might want to make this file as complete as possible. However, yeoman will ask you about the information missing.<br />
<br />
''Please remember, that the generated spec file is only a scaffold for a real one. It will work for very simple cases, but as soon as your package gets more complicated, you will have to adjust it to your needs.''<br />
<br />
Once you have a debian/ directory for your project, you can go on. Now you can run:<br />
<br />
$ grunt dpkg-buildpackage --include-dependencies<br />
<br />
== i18n ==<br />
<br />
To use the proven way to translate your AppSuite application, you should now check out the [[AppSuite:I18n | documentation]] about all [[AppSuite:I18n | i18n]] tasks. Please browse to the [[AppSuite:I18n | i18n]] page.<br />
<br />
== Further Reading ==<br />
* Congratulations you have just built your first app for OX App Suite, but please keep in mind that there are [[AppSuite:Developing for the UI#What_can_i_build.3F | quite a few options]] for developing for OX App Suite.<br />
* In case you want to upgrade your existing app for OX App Suite, read [[Appsuite:Upgrade_app_using_yo | the upgrade guide]].<br />
* More information on the build system can be found on github:<br />
** [https://github.com/Open-Xchange-Frontend/shared-grunt-config See all available grunt tasks]<br />
** [https://github.com/Open-Xchange-Frontend/generator-ox-ui-module Documentation of all generator tasks]<br />
* If you're stuck somewhere, the article about [[AppSuite:Debugging_the_UI | debugging the UI]] might help you.<br />
* You can read this to get a better overview of [[AppSuite:Developing for the UI | developing the user inferface]].</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Theming&diff=18172AppSuite:Theming2014-07-25T13:08:57Z<p>J.ohny.b: /* Buttons */</p>
<hr />
<div>{{Stability-experimental}}<br />
<br />
<div class="title">Theming</div><br />
<br />
'''Abstract.''' In this article, you can learn how to create customized themes and use them to change the look of you appsuite installation.<br />
__TOC__<br />
== LESS.JS ==<br />
Appsuite used LESS as dynamic stylesheet language. LESS extends CSS with dynamic behavior such as variables, mixins, operations and functions.<br />
<br />
Please read [http://lesscss.org/#docs LESS.JS] documentation first.<br />
<br />
=== Using less.js ===<br />
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. <br />
<br />
Backend packages for themes and any apps which ship .less files require the following changes:<br />
<br />
1. Add "skipLess=1" to the build command in *.spec and in debian/rules:<br />
sh /opt/open-xchange-appsuite-dev/bin/build-appsuite app skipLess=1<br />
2. Add %post and %postun sections to *.spec:<br />
%post<br />
if [ "$1" = 1 ]; then<br />
UPDATE=/opt/open-xchange/appsuite/share/update-themes.sh<br />
[ -x $UPDATE ] && $UPDATE<br />
fi<br />
%postun<br />
UPDATE=/opt/open-xchange/appsuite/share/update-themes.sh<br />
[ -x $UPDATE ] && $UPDATE<br />
<br />
For multiple binary packages, the %post and %postun sections should apply only to backend packages <br />
which contain .less files.<br />
<br />
3. Add debian/postinst and debian/postrm containing the same content:<br />
#!/bin/sh<br />
UPDATE=/opt/open-xchange/appsuite/share/update-themes.sh<br />
[ -x $UPDATE ] && $UPDATE<br />
<br />
For multiple binary packages, the postinst and postrm files should apply only to backend packages which contain .less files.<br />
<br />
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.<br />
<br />
== File structure ==<br />
A theme basically consists of two files located in <code>apps/themes/<var>THEME_ID</var>/</code>. These files are described in this and the following sections.<br />
<br />
<code><var>THEME_ID</var></code> 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. <code>com.example.prettytheme</code>.<br />
<br />
=== definitions.less ===<br />
This file can be used to override variables described in the "Variables" section of this article.<br />
<br />
=== style.less ===<br />
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.<br />
<br />
=== Referencing paths ===<br />
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. <br />
<br />
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.<br />
<br />
== Variables ==<br />
<br />
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 "hovered" 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.<br />
<br />
Variables are defined in variables.less from twitter-bootstrap and our default definitions.less. Variables that are defined in definitions.less always override variables from bootstrap's variables.less<br />
<br />
=== Most relevant variables ===<br />
<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x)<br />
|-<br />
| @topbar-background || @topbarBackground || #3774A8 (blue)<br />
|-<br />
| --- || @topbarBackgroundAlt || No longer used<br />
|-<br />
| @topbar-launcher-color || @topbarLauncherColor || rgba(255, 255, 255, 0.70)<br />
|-<br />
| @topbar-launcher-color-hover || @topbarLauncherColor-hover || #fff<br />
|-<br />
| @topbar-launcher-color-active || @topbarLauncherColorActive || #fff<br />
|-<br />
| @topbar-launcher-background-hover || @topbarLauncherBackgroundHover || rgba(0, 0, 0, 0.30)<br />
|-<br />
| @topbar-launcher-background-active || @topbarLauncherBackgroundActive || rgba(0, 0, 0, 0.20);<br />
|-<br />
| @topbar-icon-color || @topbarIconColor || #fff<br />
|-<br />
| @selected-background || @selectedBackground || #428BCA (blue)<br />
|-<br />
| @contact-picture-radius || @contactPictureRadius || 50%<br />
|-<br />
|}<br />
:<br />
<br />
<br />
The most significant visual change is achieved by changing the following variables:<br />
* '''@topbar-background''' (top navigation bar)<br />
* '''@selected-background''' (selection background color in list views and folder tree)<br />
* '''@link-color''' (almost all hyperlinks)<br />
<br />
=== Font ===<br />
<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x)<br />
|-<br />
| @font-family-sans-serif || @sansFontFamily || "Helvetica Neue", Helvetica, Arial, sans-serif<br />
|-<br />
| @font-family-serif || @serifFontFamily || Georgia, "Times New Roman", Times, serif<br />
|-<br />
| @font-family-monospace || @monoFontFamily || Monaco, Menlo, Consolas, "Courier New", monospace<br />
|-<br />
| @font-size-base || @baseFontSize || 14px<br />
|-<br />
| || @baseFontFamily ||<br />
|-<br />
| @line-height-base || @baseLineHeight || 1.428571429; // 20/14<br />
|-<br />
| || @altFontFamily ||<br />
|-<br />
| @font-size-touch || @touchFontSize || 15px<br />
|-<br />
| @headings-font-family || @headingsFontFamily || inherit<br />
|-<br />
| @headings-font-weight || @headingsFontWeight || 500<br />
|-<br />
| @font-size-large || @fontSizeLarge || ceil((@font-size-base * 1.25)); // ~18px<br />
|-<br />
| @font-size-small || @fontSizeSmall || ceil((@font-size-base * 0.85)); // ~12px<br />
|-<br />
| || @fontSizeMini ||<br />
|-<br />
| @vgrid-font-size || @vgridFontSize || 13px<br />
|}<br />
<br />
=== Colors ===<br />
<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x)<br />
|-<br />
| @background || @bodyBackground || #fff<br />
|-<br />
| @text-color || @textColor || @gray-dark<br />
|-<br />
| @link-color || @linkColor || @brand-primary<br />
|-<br />
| @link-hover-color || @linkColorHover || darken(@link-color, 15%)<br />
|-<br />
| @link-accent-color || @linkAccentColor || #ffad00<br />
|-<br />
| || @linkAccentColorHover ||<br />
|-<br />
| @badge-color || @badgeColor || @white<br />
|-<br />
| @badge-bg || @badgeBackground || #aaa<br />
|-<br />
| @headings-color || @headingsColor || inherit<br />
|-<br />
| @black || @black || #000<br />
|-<br />
| @gray-darker || @grayDarker || #222<br />
|-<br />
| @gray-dark || @grayDark || #333<br />
|-<br />
| @gray || @gray || #555<br />
|-<br />
| @gray-light || @grayLight || #999<br />
|-<br />
| @gray-lighter || @grayLighter || #eee<br />
|-<br />
| @white || @white || #fff<br />
|-<br />
| @blue || @blue || darken(#049cdb, 5%)<br />
|-<br />
| @blue-dark || @blueDark || #0064cd<br />
|-<br />
| @blue-light || @blueLight || lighten(#049cdb, 25%)<br />
|-<br />
| @green || @green || #1A8D1A<br />
|-<br />
| @green-light || @greenLight || #92D050<br />
|-<br />
| @red || @red || #cc0000<br />
|-<br />
| @yellow || @yellow || #F8E400<br />
|-<br />
| @orange || @orange || #f89406<br />
|-<br />
| @pink || @pink || #E01CD9<br />
|-<br />
| @purple || @purple || #7E16CF<br />
|-<br />
|}<br />
<br />
=== Space ===<br />
<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x)<br />
|-<br />
| || @paddingLarge ||<br />
|-<br />
| || @paddingSmall ||<br />
|-<br />
| || @paddingMini ||<br />
|-<br />
| @border-radius-base || @baseBorderRadius || 4px<br />
|-<br />
| @border-radius-large || @borderRadiusLarge || 6px<br />
|-<br />
| @border-radius-small || @borderRadiusSmall || 3px<br />
|}<br />
<br />
=== Pagination ===<br />
<br />
Used where pagination is done, for example in the Calendar weekview, each week is on one "page"; one can switch the week using a pagination widget styled with these variables.<br />
<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x)<br />
|-<br />
| @pagination-bg || @paginationBackground || #fff<br />
|-<br />
| @pagination-border || @paginationBorder || #ddd<br />
|-<br />
| @pagination-active-bg || @paginationActiveBackground || @brand-primary<br />
|}<br />
<br />
=== Buttons ===<br />
<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x)<br />
|-<br />
| || @btnBackground ||<br />
|-<br />
| || @btnBackgroundHighlight ||<br />
|-<br />
| || @btnBorder ||<br />
|-<br />
| @btn-primary-bg || @btnPrimaryBackground || @link-color<br />
|-<br />
| || @btnPrimaryBackgroundHighlight ||<br />
|-<br />
| @btn-info-bg || @btnInfoBackground || #5bc0de<br />
|-<br />
| || @btnInfoBackgroundHighlight ||<br />
|-<br />
| @btn-success-bg || @btnSuccessBackground || #62c462<br />
|-<br />
| || @btnSuccessBackgroundHighlight ||<br />
|-<br />
| @btn-warning-bg || @btnWarningBackground || lighten(@orange, 15%)<br />
|-<br />
| || @btnWarningBackgroundHighlight ||<br />
|-<br />
| @btn-danger-bg || @btnDangerBackground || #ee5f5b<br />
|-<br />
| || @btnDangerBackgroundHighlight ||<br />
|-<br />
| @btn-inverse-bg || @btnInverseBackground || #444<br />
|-<br />
| || @btnInverseBackgroundHighlight ||<br />
|}<br />
<br />
=== Dropdowns ===<br />
<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x)<br />
|-<br />
| @dropdown-bg || @dropdownBackground || #fff<br />
|-<br />
| @dropdown-border || @dropdownBorder || rgba(0,0,0,.15)<br />
|-<br />
| @dropdown-divider-bg || @dropdownDividerTop || #e5e5e5<br />
|-<br />
| @dropdown-divider-bg || @dropdownDividerBottom || #e5e5e5<br />
|-<br />
| @dropdown-link-color || @dropdownLinkColor || @gray-dark<br />
|-<br />
| @dropdown-link-hover-color || @dropdownLinkColorHover || darken(@gray-dark, 5%)<br />
|-<br />
| @dropdown-link-active-color || @dropdownLinkColorActive || @component-active-color<br />
|-<br />
| @dropdown-link-active-bg || @dropdownLinkBackgroundActive || @component-active-bg<br />
|-<br />
| @dropdown-link-hover-bg || @dropdownLinkBackgroundHover || #f5f5f5<br />
|}<br />
<br />
=== Foldertree ===<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x) || Description<br />
|-<br />
| @foldertree-sidepane-background || @foldertreeSidepanelBackground || #f5f5f5 ||<br />
|-<br />
| @foldertee-section-title-color || @foldertreeSectionTitleColor || #888 || Color for sectiontitles in foldertree (like "Public" folders)<br />
|-<br />
| @foldertree-active-label-color || @foldertreeActiveLabelColor || #333 || ''Active'' means, user can perform an action on this item<br />
|-<br />
|@foldertree-passive-label-color || @foldertreePassiveLabelColor ||@hc-gray || ''Passive'' means, user can '''not''' perform any action with this item <br />
|-<br />
| @foldertree-hover-background || @foldertreeHoverBackground || rgba(0, 0, 0, 0.05) ||<br />
|-<br />
| @foldertree-selected-background || @foldertreeSelectedBackground || rgba(0, 0, 0, 0.10) ||<br />
|-<br />
| @foldertree-badge-background || @foldertreeBadgeBackground || @bagde-bg || see [[#Colors]] for definition of @badge-bg<br />
|-<br />
| @foldertree-badge-color || @foldertreeBadgeColor ||@badge-color || see [[#Colors]] for definition of @badge-color<br />
|}<br />
<br />
=== Calendar ===<br />
==== Appointment ====<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x) || Description<br />
|-<br />
| @appointment-reserved || @appointmentReserved || #08c /* blue */ || Appointment status color<br />
|-<br />
| @appointment-temporary || @appointmentTemporary || #ffbb00 /* yellow */ || Appointment status color<br />
|-<br />
| @appointment-absent || @appointmentAbsent || #913f3f /* red */ || Appointment status color<br />
|-<br />
| @appointment-free || @appointmentFree || #8eb360 /* green */ || Appointment status color<br />
|-<br />
| @appointment-private || @appointmentPrivate || #555 /* gray */ || Appointment status color<br />
|-<br />
| @appointment-declined-font || @appointmentDeclinedFont || #888 /* dark gray */|| Font color for declined Appointments<br />
|-<br />
| @appointment-unconfirmed-alpha || @appointmentUnconfirmedAlpha || 0.4 || Transparency value for unconfirmed Appointments<br />
|-<br />
| @appointment-declined-alpha || @appointmentDeclinedAlpha || 0.3 || Transparency value for declined Appointments<br />
|-<br />
| @appointment-hover-pct || @appointmentHoverPct || 15% || Percentage increase of the dark pigment content on hover effect<br />
|}<br />
<br />
==== Week View ====<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x) || Description<br />
|-<br />
| @weekview-appointment-lasso || @weekviewAppointmentLasso || #aeaeff || Lasso frame color<br />
|-<br />
| @weekview-day-in || @weekviewDayIn || #fff /* white */ || Default background color in week view<br />
|-<br />
| @weekview-day-out || @weekviewDayOut || #e0e0e0 /* grey */ || Background color outside of the mean working time<br />
|-<br />
| @weekview-timeline || @weekviewTimeline || rgba(243, 15, 170, 0.4) || Color of the Line indicating the current time<br />
|-<br />
| @weekview-time-width || @weekviewTimeWith || 58px || With of the time labels on the left side<br />
|-<br />
| @calendar-toolbar-height || @calendarToolbarHeight || 47px || Height of the control toolbar <br />
|}<br />
<br />
==== Month View ====<br />
{|<br />
! Variable (7.6.x) || Variable (7.4.x) || Default (7.6.x) || Description<br />
|-<br />
| @monthview-appointment-out || @monthviewAppointmentOut || #aaa /* light gray */ || Color of appointments, which are not in focus<br />
|-<br />
| @monthview-today || @monthviewToday || #daefff /* light blue */|| Background color of the current day<br />
|-<br />
| || @monthview@calendarToolbarHeight || ||<br />
|}<br />
<br />
== Area names ==<br />
<br />
The variables sometimes refer to common areas. To identify which area is located where, see the following annotated screenshots.<br />
<br />
[[File:Colors_mail.png|800px||Mail application]]<br />
<br />
[[File:Colors_launchpad.png|800px||Launchpad application]]<br />
<br />
== Replacing the logo ==<br />
<br />
One of the most common theme changes which requires editing <code>style.css</code> is changing the logo in the top right corner. The logo is displayed as the background image for an element with the ID <code>io-ox-top-logo-small</code>. A theme can therefore change the size and URL of the image:<br />
#io-ox-top-logo-small {<br />
width: 60px;<br />
height: 22px;<br />
margin: 8px 13px 0 13px;<br />
background-image: url(mylogo.png);<br />
}<br />
The file <code>mylogo.png</code> is expected to be in the same directory as <code>style.css</code>. If you want to place the image somewhere else, then use a relative path in <code>url()</code>.<br />
<br />
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 <code>/var/www/appsuite/</code> (or similar, depending on the target platform) instead of <code>/opt/open-xchange/appsuite/</code>. Direct support for multiple packages is coming in version 7.4.1. Until then, use the build system from the <code>develop</code> branch to [[AppSuite:UI_build_system#init-packaging|initialize the packaging]] if your theme contains images.<br />
<br />
== Mixins ==<br />
In LESS, it is possible to include a bunch of properties from one ruleset into another ruleset. So say we have the following class:<br />
<br />
=== Sample ===<br />
<pre class="language-css"><br />
.border-radius(@radius: 0px) {<br />
-webkit-border-radius: @radius;<br />
-moz-border-radius: @radius;<br />
-ms-border-radius: @radius;<br />
border-radius: @radius;<br />
}<br />
<br />
#menu a {<br />
color: #111;<br />
.border-radius(5px);<br />
}<br />
</pre><br />
<br />
Read [http://www.lesscss.org/#-mixins LESS Mixins]<br />
<br />
=== global OX Mixins ===<br />
<br />
they can be found at [[#definitions.less | definitions.less]]<br />
<br />
{|<br />
! Mixin || Sample || Description <br />
|-<br />
| .box-sizing(@boxmodel) || .box-sizing(border-box) ||<br />
|-<br />
| .user-select(@select) || .user-select(none) ||<br />
|-<br />
| .border-radius(@radius) || .border-radius(3px) ||<br />
|-<br />
| .box-shadow(@shadow) || .box-shadow(3px) ||<br />
|-<br />
| .vertical-gradient(@startColor, @endColor) || .vertical-gradient(#888, #555) ||<br />
|-<br />
| .radial-gradient(@color1, @color2, @color3) || .radial-gradient(#111, #222, #333) ||<br />
|-<br />
| .transition(@transition) || .transition(background-color 0.2s linear) ||<br />
|-<br />
| .animation(@animation) || .animation(slidein 300ms) ||<br />
|-<br />
| .animation-name(@name) || .animation-name(slideright) ||<br />
|-<br />
| .ellipsis || .ellipsis ||<br />
|-<br />
| .overflow(@type) || .overflow(hidden) ||<br />
|-<br />
| .overflow-x(@type) || .overflow-x(hidden) ||<br />
|-<br />
| .overflow-y(@type) || .overflow-y(hidden) ||<br />
|-<br />
| .backface-visibility(@type) || .backface-visibility(hidden) ||<br />
|}<br />
<br />
== How to activate a theme during development ==<br />
<br />
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]].<br />
<br />
# First, you need to add the theme to the list of available themes on the backend. Simply create a new file in <code>/opt/open-xchange/etc/settings/</code> with the extension <code>.properties</code> and add a line for your theme to it: <pre&lt;noinclude&gt;&lt;/noinclude&gt;>io.ox/core/settingOptions//themes/<var>ID</var>=<var>Theme Name</var></pre&lt;noinclude&gt;&lt;/noinclude&gt;> Replace <var>ID</var> by the identifier (directory name) of your theme, and <var>Theme Name</var> by the human-readable name which should appear in the UI.<br />
# The server needs to be restarted to read the new settings.<br />
# Now, you can use <code>appserver</code> ([[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.<br />
# Finally, activate your theme the list in the <code>Settings -> Basic</code> view behind the option <code>Theme</code>.<br />
<br />
In case you also want to access the same backend without <code>appserver</code> while your theme is selected, that theme (or at least some empty <code>.less</code> files) should be also installed on the backend to avoid errors. To just use an empty theme, run the following as root:<br />
touch /opt/open-xchange/appsuite/apps/themes/<var>ID</var>/definitions.less<br />
touch /opt/open-xchange/appsuite/apps/themes/<var>ID</var>/style.less<br />
/opt/open-xchange/appsuite/share/update-themes.sh<br />
The value of <var>ID</var> here must be the same as in your <code>.properties</code> file.<br />
<br />
== Favicons and mobile homescreen icons ==<br />
'''Note''': This chapter is not about changing AppSuite icons which are used in the application like the brand on the upper right.<br />
<br />
'''This documentation applies for AppSuite v.7.4.2'''<br />
<br />
AppSuite ships with a standard set of icons containing a<br />
# favicon<br />
# set of touch icons which are mainly used by mobile Safari on iOS<br />
<br />
These icons are used as default for all devices and browsers as long as you don't deliver your own icons with your theme. To provide your own icons, put them into your theme's directory, e.g. <tt>apps/themes/<var>theme-name</var>/favicon.ico</tt>.<br />
<br />
=== Favicon ===<br />
All major browsers support the use of a favicon. The favicon is a pixel image with the size of 16x16 (32x32) and the "ico" file ending. (see [http://en.wikipedia.org/wiki/Favicon Wikipedia Favicon] for details).<br />
<br />
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 <tt>apps/themes/icons/default</tt> on the web server.<br />
<br />
'''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 <tt>favicon.ico</tt> located under <tt>apps/themes/icons/default</tt> on the web server must be replaced with a custom version.<br />
<br />
=== Apple touch icons ===<br />
iOS devices (iPhone/iPad/iPod) support so called "Webclips". 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 "Add to homescreen" 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.<br />
<br />
* iPhone 3: 57 x 57 px<br />
* iPhone 4 retina: 114x114 px<br />
* iPhone iPhone 5 retina: 120 x 120 px<br />
* iPad: 72 x 72 px<br />
* iPad (iOS 7): 76 x 76px<br />
* iPad retina: 144 x 144 px <br />
* iPad retina (iOS 7): 152 x 152 px <br />
<br />
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<br />
<br />
* iPhone: 320 x 460 px<br />
* iPhone 4: 640 x 920 px<br />
* iPhone 5: 640 x 1096 px<br />
<br />
'''Note''': We do not provide splashscreens for iPad<br />
<br />
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.<br />
<br />
=== Providing custom icons ===<br />
To provide custom Webclip icons locate the following path in the AppSuite installation on your web server:<br />
pathToAppSuite/apps/themes/icons/default<br />
<br />
This folder contains all OX default icons for Webclips icons and splashscreens. Use these as samples for your own versions.<br />
<br />
A clean installation will have all our default icons in the "default" 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 <tt>.htaccess</tt> file located under <tt>apps/themes</tt><br />
<br />
== Theming the login page ==<br />
<br />
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]].<br />
<br />
=== Providing domain based login themes ===<br />
<br />
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 <tt>.htaccess</tt> is located under <tt>apps/themes</tt><br />
<br />
<pre><br />
# Sample config for domain based login theme<br />
RewriteCond %{HTTP_HOST} ^www\.domain\.com$<br />
RewriteCond %{REQUEST_FILENAME} -f<br />
RewriteRule ^login/(.*)$ domain_com_logintheme/$1 [L]<br />
</pre><br />
<br />
== Best practice ==<br />
<br />
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.<br />
<br />
Of course, using CSS in <code>style.less</code> 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.<br />
<br />
== Links ==<br />
[http://bradfrost.github.io/this-is-responsive/resources.html Responsive design]<br />
<br />
[http://lesscss.org/ LESS]<br />
<br />
== Caveats ==<br />
<br />
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.<br />
<br />
As mentioned before, changing colors should be safe.<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Testing_3rd-party_code&diff=18171AppSuite:Testing 3rd-party code2014-07-25T09:04:42Z<p>J.ohny.b: /* Running the tests */</p>
<hr />
<div><div class="title">Testing 3rd-party code</div><br />
<br />
__TOC__<br />
<br />
This article explains one way to add automatic testing to UI plugins. <br />
<br />
== Libraries ==<br />
<br />
* [http://visionmedia.github.io/mocha/ Mocha] - the fun, simple, flexible JS testing framework<br />
* [http://sinonjs.org/ Sinon.JS] - Standalone test spies, stubs and mocks for JavaScript.<br />
* [http://karma-runner.github.io/ Karma Runner] - test runner<br />
* [http://chaijs.com/ Chai.js] - Assertion framework<br />
<br />
== using karma-ox-ui ==<br />
<br />
The [[AppSuite:GettingStarted_7.6.0|shared grunt configuration]] ships with most of the parts pre-configured. Still, a little setup is needed to enable automatic testing for an external app. Since we use many standard libraries, this approach can be extended as you wish.<br />
<br />
=== Setup ===<br />
<br />
Make sure, karma executable is installed:<br />
<br />
npm install -g karma-cli<br />
<br />
Install a few more testing libraries locally:<br />
<br />
npm install --save-dev karma-mocha karma-chai karma-sinon karma-ox-ui karma-phantomjs-launcher<br />
<br />
After that, in your plugin directory generate a new karma.conf.js:<br />
<br />
jb@wiggum ~/code/appsuite/ox_pgp_mail (git)-[ding] % karma init<br />
<br />
Which testing framework do you want to use ?<br />
Press tab to list possible options. Enter to move to the next question.<br />
> mocha <br />
<br />
Do you want to use Require.js ?<br />
This will add Require.js plugin.<br />
Press tab to list possible options. Enter to move to the next question.<br />
> no <br />
<br />
Do you want to capture any browsers automatically ?<br />
Press tab to list possible options. Enter empty string to move to the next question.<br />
> PhantomJS <br />
> <br />
<br />
What is the location of your source and test files ?<br />
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Should any of the files included by the previous patterns be excluded ?<br />
You can use glob patterns, eg. "**/*.swp".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Do you want Karma to watch all the files and run the tests on change ?<br />
Press tab to list possible options.<br />
> no <br />
<br />
<br />
Config file generated at "/home/jb/code/appsuite/ox_pgp_mail/karma.conf.js".<br />
<br />
Edit the generated file and adjust the following configuration variables:<br />
<br />
<pre class="language-javascript"><br />
basePath: 'build/',<br />
frameworks: ['ox-ui', 'sinon', 'mocha', 'chai'],<br />
files: [<br />
'spec/test-main.js',<br />
{pattern: 'apps/**/*.js', included: false},<br />
{pattern: 'spec/**/*_spec.js', included: false}<br />
]<br />
</pre><br />
<br />
Generate a main loader script to start the test after App Suite Core UI<br />
has been booted. The file should be put in `spec/test-main.js`:<br />
<br />
<pre class="language-javascript"><br />
var allTestFiles = [];<br />
var TEST_REGEXP = /_spec\.js$/i;<br />
<br />
var pathToModule = function(path) {<br />
return path;<br />
// return path.replace(/^\/base\//, '').replace(/\.js$/, '');<br />
};<br />
<br />
Object.keys(window.__karma__.files).forEach(function(file) {<br />
if (TEST_REGEXP.test(file)) {<br />
// Normalize paths to RequireJS module names.<br />
allTestFiles.push(pathToModule(file));<br />
}<br />
});<br />
<br />
require(['io.ox/core/extPatterns/stage'], function (Stage) {<br />
<br />
'use strict';<br />
<br />
ox.testUtils.stubAppsuiteBody();<br />
<br />
new Stage('io.ox/core/stages', {<br />
id: 'run_tests',<br />
index: 99999,<br />
run: function (baton) {<br />
requirejs.config({<br />
// Karma serves files from '/base/apps'<br />
baseUrl: '/base/apps',<br />
<br />
// ask Require.js to load these files (all our tests)<br />
deps: allTestFiles,<br />
<br />
// start test run, once Require.js is done<br />
callback: window.__karma__.start<br />
});<br />
}<br />
});<br />
});<br />
</pre><br />
<br />
=== Running the tests ===<br />
<br />
There are multiple targets provided in<br />
[https://github.com/Open-Xchange-Frontend/shared-grunt-config shared grunt configuration].<br />
<br />
The <tt>grunt/local.conf.json</tt> needs to be configured and point to an existing build of the core UI (coreDir setting). When testing on a machine with the core UI installed from distribution packages, also the German translations need to be installed to run the tests. After that, coreDir can be set to <tt>/opt/open-xchange/appsuite/</tt>.<br />
<br />
The recommended way to start testing is to run:<br />
<br />
grunt dev<br />
<br />
This will start a connect server, the karma<br />
test server and a watcher for changes. Optionally, it is possible to connect multiple browsers to the host running the karma server (port 9876). Tests will run in those browsers, too. You can trigger a test run manually by running:<br />
<br />
grunt testrun<br />
<br />
in another terminal. This will be done automatically by the grunt watch<br />
task, after any source file of your project has been changed.<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]<br />
[[Category:Development process]]<br />
[[Category:Testing]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GruntFAQ&diff=18170AppSuite:GruntFAQ2014-07-24T09:33:33Z<p>J.ohny.b: /* Using development proxy server */</p>
<hr />
<div><div class="title">FAQ regarding Grunt configuration for App Suite UI plugins</div><br />
<br />
= Building UI plugins =<br />
<br />
== Some of my files aren't copied. How can I extend a copy sub-task? ==<br />
<br />
Especially when using external libraries managed with Bower or npm, sometimes the [https://github.com/Open-Xchange-Frontend/shared-grunt-config shared grunt configuration] doesn't contain all cases for files to be copied during the build or dist tasks. Due to the [https://johnyb.github.io/2014/05/10/extending_shared_grunt_configuration.html extensibility] of our shared grunt configuration, it's quite easy to add those missing files. You can hook up into the <tt>build_*</tt> or the <tt>dist_*</tt> copy task and add your custom configuration like this:<br />
<br />
<syntaxhighlight lang="JavaScript"><br />
'use strict';<br />
<br />
module.exports = function (grunt) {<br />
grunt.config.extend('copy', {<br />
build_custom: {<br />
files: [{<br />
expand: true,<br />
src: ['apps/**/*.in', 'apps/**/manifest.json'],<br />
dest: 'build/'<br />
}]<br />
}<br />
});<br />
};<br />
</syntaxhighlight><br />
<br />
Put this in a file inside the <tt>grunt/tasks</tt> directory and you are done. From now on, all files ending with .in and all manifest.json files are put into the <tt>build/</tt> directory using the same structure as in the <tt>apps/</tt> directory. For more detailed information see [http://gruntjs.com/configuring-tasks#files the grunt documentation on files].<br />
<br />
= Using development proxy server =<br />
<br />
== Which version of appserver am I using? ==<br />
<br />
npm can be used to find out about the versions. <tt>npm ls appserver</tt> will list the version of appserver that is used.<br />
<br />
== I get an error message 'Port 8337 already in use by another process', what can I do? ==<br />
<br />
Only one instance of appserver is allowed at a time (this might be subject to change, though). You have multiple options to start developing on that plugin. Number one:<br />
<br />
# Close all other running instances of appserver and run it exclusively in one project<br />
<br />
Yes, that was the easy one. However, you might want to serve multiple projects at once. This case would need a little more configuration:<br />
<br />
# Choose one base UI project<br />
# Edit <tt>grunt/local.conf.json</tt><br />
# add all build/ directories you want to serve to "prefixes" array<br />
# Run <tt>grunt connect watch</tt> in the base directory<br />
# In all other UI projects you want to develop, only run <tt>grunt watch</tt> without the connect task<br />
<br />
Appserver will in this case do all the cache busting for you (it uses the timestamp of the "newest" directory in the prefix list) and if you didn't de-activate live-reload, it will work for all projects (the watch task sends livereload events to appserver, which will trigger the reload in the browser).</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Debugging_the_UI&diff=18169AppSuite:Debugging the UI2014-07-23T08:13:05Z<p>J.ohny.b: /* Is a specific capability set? */</p>
<hr />
<div><div class="title">Debugging the UI</div><br />
'''Synopsis:''' A collection of hints to debug during UI development. See also [[AppSuite:UI FAQ]]. Sister page: [[AppSuite:Debugging_the_server|Debugging the server]].<br />
<br />
__TOC__<br />
<br />
== What capabilities are available? ==<br />
<pre class="language-javascript"><br />
_(ox.serverConfig.capabilities).pluck("id").sort();<br />
</pre><br />
== Is a specific capability set? ==<br />
<pre class="language-javascript"><br />
require(['io.ox/core/capabilities'], function (cap) {<br />
console.log(cap.has('certain_cap'));<br />
});<br />
</pre><br />
<br />
== Which files failed to load? ==<br />
<pre class="language-javascript"><br />
requirejs.s.contexts._.registry<br />
</pre><br />
<br />
== What portal widgets are available? ==<br />
<pre class="language-javascript"><br />
require(['io.ox/portal/widgets'], function (widgets) {<br />
console.log(widgets.getAvailablePlugins().sort());<br />
});<br />
</pre><br />
<br />
== Check settings ==<br />
<pre class="language-javascript"> <br />
// check core settings<br />
require('settings!io.ox/core').get();<br />
// check mail settings<br />
require('settings!io.ox/mail').get();<br />
</pre><br />
<br />
== Clear all persistent caches ==<br />
Please mind that this does not clear the regular browser cache! It clears localStorage, IndexedDB, and WebSQL.<br />
<pre class="language-javascript"> <br />
ox.cache.clear();<br />
</pre><br />
<br />
== Clear all portal widgets ==<br />
Sometimes you manage to build a portal widgets that messes up all kinds of things when a user adds it. This is a rather blunt way of solving the problem:<br />
<br />
<pre class="language-javascript"> <br />
require(['settings!io.ox/portal'], function(settings) {<br />
settings.set('widgets/user', '').save();<br />
}); <br />
</pre><br />
<br />
== Debug relogin ==<br />
<pre class="language-javascript"> <br />
ox.autoLogoutRestartDebug();<br />
</pre><br />
<br />
== Enable/disable capability via URL hash ==<br />
Just add the parameter "cap" to URL hash. A leading minus disables a capability. Multiple capabilities separated by comma. Example:<br />
<pre language="none"> <br />
...&cap=emoji,-calendar<br />
</pre><br />
<br />
== Changes do not apply while developing ==<br />
You did changes in your code and they don't simply don't apply?<br />
There are several possibilites, you should check in order to find a solution.<br />
<br />
* Reload AppSuite with cleared Browser Cache. Using Firefox on Linux-Distributions you can simply press <tt>Ctrl+F5</tt>. Please check the documentation of your Browser for Shortcuts and how to clear the cache.<br />
* Disable Source Caching. Therefor add the parameter <tt>"debug-js=true"</tt> to URL hash. Example:<br />
<pre language="none">...&debug-js=true</pre><br />
<br />
== Debug a specific folder ==<br />
If you want to get details of a specific folder, just inspect it via dev tools and look for data-obj-id="...". Copy the id and run the following in console:<br />
<pre class="language-javascript"> <br />
void require('io.ox/core/api/folder').get({ folder: 'default0/INBOX' }).always(_.inspect);<br />
</pre><br />
<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]<br />
[[Category:Developer]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Debugging_the_UI&diff=18168AppSuite:Debugging the UI2014-07-23T08:11:57Z<p>J.ohny.b: /* What capabilities are available? */</p>
<hr />
<div><div class="title">Debugging the UI</div><br />
'''Synopsis:''' A collection of hints to debug during UI development. See also [[AppSuite:UI FAQ]]. Sister page: [[AppSuite:Debugging_the_server|Debugging the server]].<br />
<br />
__TOC__<br />
<br />
== What capabilities are available? ==<br />
<pre class="language-javascript"><br />
_(ox.serverConfig.capabilities).pluck("id").sort();<br />
</pre><br />
== Is a specific capability set? ==<br />
<pre class="language-javascript"><br />
require('io.ox/core/capabilities').has('certain_cap');<br />
</pre><br />
<br />
== Which files failed to load? ==<br />
<pre class="language-javascript"><br />
requirejs.s.contexts._.registry<br />
</pre><br />
<br />
== What portal widgets are available? ==<br />
<pre class="language-javascript"><br />
require(['io.ox/portal/widgets'], function (widgets) {<br />
console.log(widgets.getAvailablePlugins().sort());<br />
});<br />
</pre><br />
<br />
== Check settings ==<br />
<pre class="language-javascript"> <br />
// check core settings<br />
require('settings!io.ox/core').get();<br />
// check mail settings<br />
require('settings!io.ox/mail').get();<br />
</pre><br />
<br />
== Clear all persistent caches ==<br />
Please mind that this does not clear the regular browser cache! It clears localStorage, IndexedDB, and WebSQL.<br />
<pre class="language-javascript"> <br />
ox.cache.clear();<br />
</pre><br />
<br />
== Clear all portal widgets ==<br />
Sometimes you manage to build a portal widgets that messes up all kinds of things when a user adds it. This is a rather blunt way of solving the problem:<br />
<br />
<pre class="language-javascript"> <br />
require(['settings!io.ox/portal'], function(settings) {<br />
settings.set('widgets/user', '').save();<br />
}); <br />
</pre><br />
<br />
== Debug relogin ==<br />
<pre class="language-javascript"> <br />
ox.autoLogoutRestartDebug();<br />
</pre><br />
<br />
== Enable/disable capability via URL hash ==<br />
Just add the parameter "cap" to URL hash. A leading minus disables a capability. Multiple capabilities separated by comma. Example:<br />
<pre language="none"> <br />
...&cap=emoji,-calendar<br />
</pre><br />
<br />
== Changes do not apply while developing ==<br />
You did changes in your code and they don't simply don't apply?<br />
There are several possibilites, you should check in order to find a solution.<br />
<br />
* Reload AppSuite with cleared Browser Cache. Using Firefox on Linux-Distributions you can simply press <tt>Ctrl+F5</tt>. Please check the documentation of your Browser for Shortcuts and how to clear the cache.<br />
* Disable Source Caching. Therefor add the parameter <tt>"debug-js=true"</tt> to URL hash. Example:<br />
<pre language="none">...&debug-js=true</pre><br />
<br />
== Debug a specific folder ==<br />
If you want to get details of a specific folder, just inspect it via dev tools and look for data-obj-id="...". Copy the id and run the following in console:<br />
<pre class="language-javascript"> <br />
void require('io.ox/core/api/folder').get({ folder: 'default0/INBOX' }).always(_.inspect);<br />
</pre><br />
<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]<br />
[[Category:Developer]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GettingStarted_7.6.0&diff=18167AppSuite:GettingStarted 7.6.02014-07-22T11:06:30Z<p>J.ohny.b: /* RPM packages */</p>
<hr />
<div><div class="title">Getting Started</div><br />
<br />
<div style="float:right; padding: 10px;">[[File:OX_AppSuite_UI_Development_Workflow.png]]</div><br />
<br />
__TOC__<br />
<br />
Hello and welcome to the world of OX App Suite development. This document is designed to get you started with developing your first app for OX App Suite as quickly and simply as possible. However, along the way we will also tempt you to learn more by linking to some more in-depth documentation about the various topics we cover. <br />
<br />
<div style="clear: both"></div><br />
<br />
== Installing the Development Tools ==<br />
<br />
First, you need to install some tools which are necessary for UI development.<br />
<br />
TL;DR version:<br />
<br />
$ npm install -g grunt-cli bower yo generator-ox-ui-module<br />
<br />
Or if needed, there is a complete article about setting up an environment for [[AppSuite:GettingStartedWithGrunt#Node | grunt]].<br />
<br />
== Create a Workspace ==<br />
<br />
All your app development can take place inside one working directory. The examples will assume you have created a directory named <tt>myapp</tt> inside your home directory:<br />
<br />
$ mkdir ~/myapp<br />
$ cd ~/myapp<br />
<br />
It doesn't need to be in your home directory, and the actual name should reflect the name of your app. The main point is that all commands will be executed in this directory and all paths will be relative to this directory from now on.<br />
<br />
Now, you can generate a grunt configuration using:<br />
<br />
$ yo ox-ui-module<br />
<br />
The source code of your app will reside in the subfolder named <tt>apps</tt>. To avoid name collisions please pick a unique subfolder inside that. The easiest and recommended way is to use a domain name that you own. Typically, the domain elements are reversed, like in Java. <tt>example.com</tt> becomes <tt>com.example</tt>:<br />
<br />
$ mkdir -p apps/com.example<br />
<br />
== Writing an App ==<br />
<br />
As an example, let's create the smallest possible app and test it. It requires only two files: <tt>apps/com.example/register.js</tt> for the source code of the app:<br />
<br />
<pre class="language-javascript"><br />
define('com.example/register', function () {<br />
'use strict';<br />
alert('Hello, World!');<br />
});<br />
</pre><br />
<br />
and <tt>apps/com.example/manifest.json</tt> for the [[AppSuite:UI_manifests_explained | manifest]] which tells the UI that your app exists and what to do with it:<br />
<br />
<pre class="language-javascript">{ "namespace": "core" }</pre><br />
<br />
== Building an App ==<br />
<br />
The source code of your app can't be used by OX App Suite as it. It first has to be processed by the [[AppSuite:UI_build_system | build system]]. This step will check the source code for syntax errors, compress it, and depending on the structure of your code, many other things. The processed code is then written to a directory named <tt>build</tt> by default. Start the build with this command:<br />
<br />
$ grunt<br />
<br />
If your editor supports it, you can configure it to call the build system after every file save. Take care to call it from the top directory of your app's workspace, not from the directory of the saved file.<br />
<br />
== Testing an App ==<br />
<br />
The freshly built code can now be tested. Instead of uploading your code to an OX App Suite server, you can use the [[AppSuite:Appserver|appserver]] proxy to inject your code into the UI code of any existing OX App Suite installation. For example, to start <tt>appserver</tt> using [http://ox.io/ ox.io] as the server, you will need a local configuration pointing to that server. You can generate it with this command:<br />
<br />
$ grunt show-config:local --output grunt/local.conf.json<br />
<br />
Open up the file <tt>grunt/local.conf.json</tt> in your editor and add <tt><nowiki>"https://ox.io/appsuite/"</nowiki></tt> to the <tt>server</tt> setting of the appserver section. Then start the development server:<br />
<br />
$ grunt dev<br />
<br />
This command will serve your app from the local directory <tt>build</tt>, and get everything else from the URL specified in the <tt>server</tt> setting.<br />
<br />
Once appserver is running, you can access OX App Suite by opening your browser using this address:<br />
<br />
http://localhost:8337/appsuite<br />
<br />
After logging in, the app should be loaded and display the <tt>alert</tt> message.<br />
<br />
=== Development cycle ===<br />
<br />
Once you are sure that your setup works, you can extend the example and write the actual code for your app. The <tt>dev</tt> task will detect any changes and rebuild your app and even reload all browsers connected to<br />
<tt>http://localhost:8337/appsuite</tt>.<br />
<br />
While developing always keep in mind, that there is an [[AppSuite:Debugging_the_UI | article about debugging the user interface]] which helps you avoid and fix typical errors.<br />
<br />
== Packaging ==<br />
<br />
Together with our ox-ui-module generator (from version 0.8.0), we provide you with generators for packaging information.<br />
<br />
=== Generating a source tarball ===<br />
<br />
There is a grunt tasks to create source packages. You can use it to create a tar.gz file containing all sources needed to build the project. This source package should contain all dependencies that are installed during <tt>bower install</tt> and <tt>npm install</tt> (basically containing your <tt>bower_components</tt> and <tt>node_modules</tt> directories). To generate this file, run:<br />
<br />
$ grunt dist:source --include-dependencies<br />
<br />
This file (can be found in the <tt>dist/</tt> directory) together with the distribution specific packaging information will be needed to build the package.<br />
<br />
=== RPM packages ===<br />
<br />
In order to generate a spec file for your project, run:<br />
<br />
$ yo ox-ui-module:rpm-pkg<br />
<br />
Will generate a spec file, with some default values read from your <tt>package.json</tt> file. So you might want to make this file as complete as possible. However, yeoman will ask you about the information missing.<br />
<br />
''Please remember, that the generated spec file is only a scaffold for a real one. It will work for very simple cases, but as soon as your package gets more complicated, you will have to adjust it to your needs.''<br />
<br />
Once you have the spec file in your project, you can use<br />
<br />
$ grunt rpm-build --include-dependencies # or rpm-build if you are on shared-grunt-config-0.6.0<br />
<br />
=== DEB packages ===<br />
<br />
Creating packages for the Debian distribution works similar to rpm packages. Just run:<br />
<br />
$ yo ox-ui-module:deb-pkg<br />
<br />
This will generate a debian directory containing all needed files. Some default values will be read from your <tt>package.json</tt> file. So you might want to make this file as complete as possible. However, yeoman will ask you about the information missing.<br />
<br />
''Please remember, that the generated spec file is only a scaffold for a real one. It will work for very simple cases, but as soon as your package gets more complicated, you will have to adjust it to your needs.''<br />
<br />
Once you have a debian/ directory for your project, you can go on.<br />
To enable some grunt tasks to help you, you need to install (with npm install) <tt>grunt-contrib-compress</tt> and <tt>grunt-exec</tt>. Now you can run:<br />
<br />
$ grunt dpkg-buildpackage --include-dependencies<br />
<br />
== i18n ==<br />
<br />
To use the proven way to translate your AppSuite application, you should now check out the [[AppSuite:I18n | documentation]] about all [[AppSuite:I18n | i18n]] tasks. Please browse to the [[AppSuite:I18n | i18n]] page.<br />
<br />
== Further Reading ==<br />
* Congratulations you have just built your first app for OX App Suite, but please keep in mind that there are [[AppSuite:Developing for the UI#What_can_i_build.3F | quite a few options]] for developing for OX App Suite.<br />
* In case you want to upgrade your existing app for OX App Suite, read [[Appsuite:Upgrade_app_using_yo | the upgrade guide]].<br />
* More information on the build system can be found on github:<br />
** [https://github.com/Open-Xchange-Frontend/shared-grunt-config See all available grunt tasks]<br />
** [https://github.com/Open-Xchange-Frontend/generator-ox-ui-module Documentation of all generator tasks]<br />
* If you're stuck somewhere, the article about [[AppSuite:Debugging_the_UI | debugging the UI]] might help you.<br />
* You can read this to get a better overview of [[AppSuite:Developing for the UI | developing the user inferface]].</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GettingStarted_7.6.0&diff=18166AppSuite:GettingStarted 7.6.02014-07-22T11:06:08Z<p>J.ohny.b: /* DEB packages */</p>
<hr />
<div><div class="title">Getting Started</div><br />
<br />
<div style="float:right; padding: 10px;">[[File:OX_AppSuite_UI_Development_Workflow.png]]</div><br />
<br />
__TOC__<br />
<br />
Hello and welcome to the world of OX App Suite development. This document is designed to get you started with developing your first app for OX App Suite as quickly and simply as possible. However, along the way we will also tempt you to learn more by linking to some more in-depth documentation about the various topics we cover. <br />
<br />
<div style="clear: both"></div><br />
<br />
== Installing the Development Tools ==<br />
<br />
First, you need to install some tools which are necessary for UI development.<br />
<br />
TL;DR version:<br />
<br />
$ npm install -g grunt-cli bower yo generator-ox-ui-module<br />
<br />
Or if needed, there is a complete article about setting up an environment for [[AppSuite:GettingStartedWithGrunt#Node | grunt]].<br />
<br />
== Create a Workspace ==<br />
<br />
All your app development can take place inside one working directory. The examples will assume you have created a directory named <tt>myapp</tt> inside your home directory:<br />
<br />
$ mkdir ~/myapp<br />
$ cd ~/myapp<br />
<br />
It doesn't need to be in your home directory, and the actual name should reflect the name of your app. The main point is that all commands will be executed in this directory and all paths will be relative to this directory from now on.<br />
<br />
Now, you can generate a grunt configuration using:<br />
<br />
$ yo ox-ui-module<br />
<br />
The source code of your app will reside in the subfolder named <tt>apps</tt>. To avoid name collisions please pick a unique subfolder inside that. The easiest and recommended way is to use a domain name that you own. Typically, the domain elements are reversed, like in Java. <tt>example.com</tt> becomes <tt>com.example</tt>:<br />
<br />
$ mkdir -p apps/com.example<br />
<br />
== Writing an App ==<br />
<br />
As an example, let's create the smallest possible app and test it. It requires only two files: <tt>apps/com.example/register.js</tt> for the source code of the app:<br />
<br />
<pre class="language-javascript"><br />
define('com.example/register', function () {<br />
'use strict';<br />
alert('Hello, World!');<br />
});<br />
</pre><br />
<br />
and <tt>apps/com.example/manifest.json</tt> for the [[AppSuite:UI_manifests_explained | manifest]] which tells the UI that your app exists and what to do with it:<br />
<br />
<pre class="language-javascript">{ "namespace": "core" }</pre><br />
<br />
== Building an App ==<br />
<br />
The source code of your app can't be used by OX App Suite as it. It first has to be processed by the [[AppSuite:UI_build_system | build system]]. This step will check the source code for syntax errors, compress it, and depending on the structure of your code, many other things. The processed code is then written to a directory named <tt>build</tt> by default. Start the build with this command:<br />
<br />
$ grunt<br />
<br />
If your editor supports it, you can configure it to call the build system after every file save. Take care to call it from the top directory of your app's workspace, not from the directory of the saved file.<br />
<br />
== Testing an App ==<br />
<br />
The freshly built code can now be tested. Instead of uploading your code to an OX App Suite server, you can use the [[AppSuite:Appserver|appserver]] proxy to inject your code into the UI code of any existing OX App Suite installation. For example, to start <tt>appserver</tt> using [http://ox.io/ ox.io] as the server, you will need a local configuration pointing to that server. You can generate it with this command:<br />
<br />
$ grunt show-config:local --output grunt/local.conf.json<br />
<br />
Open up the file <tt>grunt/local.conf.json</tt> in your editor and add <tt><nowiki>"https://ox.io/appsuite/"</nowiki></tt> to the <tt>server</tt> setting of the appserver section. Then start the development server:<br />
<br />
$ grunt dev<br />
<br />
This command will serve your app from the local directory <tt>build</tt>, and get everything else from the URL specified in the <tt>server</tt> setting.<br />
<br />
Once appserver is running, you can access OX App Suite by opening your browser using this address:<br />
<br />
http://localhost:8337/appsuite<br />
<br />
After logging in, the app should be loaded and display the <tt>alert</tt> message.<br />
<br />
=== Development cycle ===<br />
<br />
Once you are sure that your setup works, you can extend the example and write the actual code for your app. The <tt>dev</tt> task will detect any changes and rebuild your app and even reload all browsers connected to<br />
<tt>http://localhost:8337/appsuite</tt>.<br />
<br />
While developing always keep in mind, that there is an [[AppSuite:Debugging_the_UI | article about debugging the user interface]] which helps you avoid and fix typical errors.<br />
<br />
== Packaging ==<br />
<br />
Together with our ox-ui-module generator (from version 0.8.0), we provide you with generators for packaging information.<br />
<br />
=== Generating a source tarball ===<br />
<br />
There is a grunt tasks to create source packages. You can use it to create a tar.gz file containing all sources needed to build the project. This source package should contain all dependencies that are installed during <tt>bower install</tt> and <tt>npm install</tt> (basically containing your <tt>bower_components</tt> and <tt>node_modules</tt> directories). To generate this file, run:<br />
<br />
$ grunt dist:source --include-dependencies<br />
<br />
This file (can be found in the <tt>dist/</tt> directory) together with the distribution specific packaging information will be needed to build the package.<br />
<br />
=== RPM packages ===<br />
<br />
In order to generate a spec file for your project, run:<br />
<br />
$ yo ox-ui-module:rpm-pkg<br />
<br />
Will generate a spec file, with some default values read from your <tt>package.json</tt> file. So you might want to make this file as complete as possible. However, yeoman will ask you about the information missing.<br />
<br />
''Please remember, that the generated spec file is only a scaffold for a real one. It will work for very simple cases, but as soon as your package gets more complicated, you will have to adjust it to your needs.''<br />
<br />
Once you have the spec file in your project, you can use<br />
<br />
$ grunt rpm-buildpackage # or rpm-build if you are on shared-grunt-config-0.6.0<br />
<br />
=== DEB packages ===<br />
<br />
Creating packages for the Debian distribution works similar to rpm packages. Just run:<br />
<br />
$ yo ox-ui-module:deb-pkg<br />
<br />
This will generate a debian directory containing all needed files. Some default values will be read from your <tt>package.json</tt> file. So you might want to make this file as complete as possible. However, yeoman will ask you about the information missing.<br />
<br />
''Please remember, that the generated spec file is only a scaffold for a real one. It will work for very simple cases, but as soon as your package gets more complicated, you will have to adjust it to your needs.''<br />
<br />
Once you have a debian/ directory for your project, you can go on.<br />
To enable some grunt tasks to help you, you need to install (with npm install) <tt>grunt-contrib-compress</tt> and <tt>grunt-exec</tt>. Now you can run:<br />
<br />
$ grunt dpkg-buildpackage --include-dependencies<br />
<br />
== i18n ==<br />
<br />
To use the proven way to translate your AppSuite application, you should now check out the [[AppSuite:I18n | documentation]] about all [[AppSuite:I18n | i18n]] tasks. Please browse to the [[AppSuite:I18n | i18n]] page.<br />
<br />
== Further Reading ==<br />
* Congratulations you have just built your first app for OX App Suite, but please keep in mind that there are [[AppSuite:Developing for the UI#What_can_i_build.3F | quite a few options]] for developing for OX App Suite.<br />
* In case you want to upgrade your existing app for OX App Suite, read [[Appsuite:Upgrade_app_using_yo | the upgrade guide]].<br />
* More information on the build system can be found on github:<br />
** [https://github.com/Open-Xchange-Frontend/shared-grunt-config See all available grunt tasks]<br />
** [https://github.com/Open-Xchange-Frontend/generator-ox-ui-module Documentation of all generator tasks]<br />
* If you're stuck somewhere, the article about [[AppSuite:Debugging_the_UI | debugging the UI]] might help you.<br />
* You can read this to get a better overview of [[AppSuite:Developing for the UI | developing the user inferface]].</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GettingStarted_7.6.0&diff=18165AppSuite:GettingStarted 7.6.02014-07-22T10:50:14Z<p>J.ohny.b: /* Installing the Development Tools */</p>
<hr />
<div><div class="title">Getting Started</div><br />
<br />
<div style="float:right; padding: 10px;">[[File:OX_AppSuite_UI_Development_Workflow.png]]</div><br />
<br />
__TOC__<br />
<br />
Hello and welcome to the world of OX App Suite development. This document is designed to get you started with developing your first app for OX App Suite as quickly and simply as possible. However, along the way we will also tempt you to learn more by linking to some more in-depth documentation about the various topics we cover. <br />
<br />
<div style="clear: both"></div><br />
<br />
== Installing the Development Tools ==<br />
<br />
First, you need to install some tools which are necessary for UI development.<br />
<br />
TL;DR version:<br />
<br />
$ npm install -g grunt-cli bower yo generator-ox-ui-module<br />
<br />
Or if needed, there is a complete article about setting up an environment for [[AppSuite:GettingStartedWithGrunt#Node | grunt]].<br />
<br />
== Create a Workspace ==<br />
<br />
All your app development can take place inside one working directory. The examples will assume you have created a directory named <tt>myapp</tt> inside your home directory:<br />
<br />
$ mkdir ~/myapp<br />
$ cd ~/myapp<br />
<br />
It doesn't need to be in your home directory, and the actual name should reflect the name of your app. The main point is that all commands will be executed in this directory and all paths will be relative to this directory from now on.<br />
<br />
Now, you can generate a grunt configuration using:<br />
<br />
$ yo ox-ui-module<br />
<br />
The source code of your app will reside in the subfolder named <tt>apps</tt>. To avoid name collisions please pick a unique subfolder inside that. The easiest and recommended way is to use a domain name that you own. Typically, the domain elements are reversed, like in Java. <tt>example.com</tt> becomes <tt>com.example</tt>:<br />
<br />
$ mkdir -p apps/com.example<br />
<br />
== Writing an App ==<br />
<br />
As an example, let's create the smallest possible app and test it. It requires only two files: <tt>apps/com.example/register.js</tt> for the source code of the app:<br />
<br />
<pre class="language-javascript"><br />
define('com.example/register', function () {<br />
'use strict';<br />
alert('Hello, World!');<br />
});<br />
</pre><br />
<br />
and <tt>apps/com.example/manifest.json</tt> for the [[AppSuite:UI_manifests_explained | manifest]] which tells the UI that your app exists and what to do with it:<br />
<br />
<pre class="language-javascript">{ "namespace": "core" }</pre><br />
<br />
== Building an App ==<br />
<br />
The source code of your app can't be used by OX App Suite as it. It first has to be processed by the [[AppSuite:UI_build_system | build system]]. This step will check the source code for syntax errors, compress it, and depending on the structure of your code, many other things. The processed code is then written to a directory named <tt>build</tt> by default. Start the build with this command:<br />
<br />
$ grunt<br />
<br />
If your editor supports it, you can configure it to call the build system after every file save. Take care to call it from the top directory of your app's workspace, not from the directory of the saved file.<br />
<br />
== Testing an App ==<br />
<br />
The freshly built code can now be tested. Instead of uploading your code to an OX App Suite server, you can use the [[AppSuite:Appserver|appserver]] proxy to inject your code into the UI code of any existing OX App Suite installation. For example, to start <tt>appserver</tt> using [http://ox.io/ ox.io] as the server, you will need a local configuration pointing to that server. You can generate it with this command:<br />
<br />
$ grunt show-config:local --output grunt/local.conf.json<br />
<br />
Open up the file <tt>grunt/local.conf.json</tt> in your editor and add <tt><nowiki>"https://ox.io/appsuite/"</nowiki></tt> to the <tt>server</tt> setting of the appserver section. Then start the development server:<br />
<br />
$ grunt dev<br />
<br />
This command will serve your app from the local directory <tt>build</tt>, and get everything else from the URL specified in the <tt>server</tt> setting.<br />
<br />
Once appserver is running, you can access OX App Suite by opening your browser using this address:<br />
<br />
http://localhost:8337/appsuite<br />
<br />
After logging in, the app should be loaded and display the <tt>alert</tt> message.<br />
<br />
=== Development cycle ===<br />
<br />
Once you are sure that your setup works, you can extend the example and write the actual code for your app. The <tt>dev</tt> task will detect any changes and rebuild your app and even reload all browsers connected to<br />
<tt>http://localhost:8337/appsuite</tt>.<br />
<br />
While developing always keep in mind, that there is an [[AppSuite:Debugging_the_UI | article about debugging the user interface]] which helps you avoid and fix typical errors.<br />
<br />
== Packaging ==<br />
<br />
Together with our ox-ui-module generator (from version 0.8.0), we provide you with generators for packaging information.<br />
<br />
=== Generating a source tarball ===<br />
<br />
There is a grunt tasks to create source packages. You can use it to create a tar.gz file containing all sources needed to build the project. This source package should contain all dependencies that are installed during <tt>bower install</tt> and <tt>npm install</tt> (basically containing your <tt>bower_components</tt> and <tt>node_modules</tt> directories). To generate this file, run:<br />
<br />
$ grunt dist:source --include-dependencies<br />
<br />
This file (can be found in the <tt>dist/</tt> directory) together with the distribution specific packaging information will be needed to build the package.<br />
<br />
=== RPM packages ===<br />
<br />
In order to generate a spec file for your project, run:<br />
<br />
$ yo ox-ui-module:rpm-pkg<br />
<br />
Will generate a spec file, with some default values read from your <tt>package.json</tt> file. So you might want to make this file as complete as possible. However, yeoman will ask you about the information missing.<br />
<br />
''Please remember, that the generated spec file is only a scaffold for a real one. It will work for very simple cases, but as soon as your package gets more complicated, you will have to adjust it to your needs.''<br />
<br />
Once you have the spec file in your project, you can use<br />
<br />
$ grunt rpm-buildpackage # or rpm-build if you are on shared-grunt-config-0.6.0<br />
<br />
=== DEB packages ===<br />
<br />
Creating packages for the Debian distribution works similar to rpm packages. Just run:<br />
<br />
$ yo ox-ui-module:deb-pkg<br />
<br />
This will generate a debian directory containing all needed files. Some default values will be read from your <tt>package.json</tt> file. So you might want to make this file as complete as possible. However, yeoman will ask you about the information missing.<br />
<br />
''Please remember, that the generated spec file is only a scaffold for a real one. It will work for very simple cases, but as soon as your package gets more complicated, you will have to adjust it to your needs.''<br />
<br />
Once you have a debian/ directory for your project, you can go on.<br />
To enable some grunt tasks to help you, you need to install (with npm install) <tt>grunt-contrib-compress</tt> and <tt>grunt-exec</tt>. Now you can run:<br />
<br />
$ grunt dpkg-buildpackage<br />
<br />
== i18n ==<br />
<br />
To use the proven way to translate your AppSuite application, you should now check out the [[AppSuite:I18n | documentation]] about all [[AppSuite:I18n | i18n]] tasks. Please browse to the [[AppSuite:I18n | i18n]] page.<br />
<br />
== Further Reading ==<br />
* Congratulations you have just built your first app for OX App Suite, but please keep in mind that there are [[AppSuite:Developing for the UI#What_can_i_build.3F | quite a few options]] for developing for OX App Suite.<br />
* In case you want to upgrade your existing app for OX App Suite, read [[Appsuite:Upgrade_app_using_yo | the upgrade guide]].<br />
* More information on the build system can be found on github:<br />
** [https://github.com/Open-Xchange-Frontend/shared-grunt-config See all available grunt tasks]<br />
** [https://github.com/Open-Xchange-Frontend/generator-ox-ui-module Documentation of all generator tasks]<br />
* If you're stuck somewhere, the article about [[AppSuite:Debugging_the_UI | debugging the UI]] might help you.<br />
* You can read this to get a better overview of [[AppSuite:Developing for the UI | developing the user inferface]].</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GettingStarted_7.6.0&diff=18164AppSuite:GettingStarted 7.6.02014-07-22T10:15:34Z<p>J.ohny.b: /* DEB packages */</p>
<hr />
<div><div class="title">Getting Started</div><br />
<br />
<div style="float:right; padding: 10px;">[[File:OX_AppSuite_UI_Development_Workflow.png]]</div><br />
<br />
__TOC__<br />
<br />
Hello and welcome to the world of OX App Suite development. This document is designed to get you started with developing your first app for OX App Suite as quickly and simply as possible. However, along the way we will also tempt you to learn more by linking to some more in-depth documentation about the various topics we cover. <br />
<br />
<div style="clear: both"></div><br />
<br />
== Installing the Development Tools ==<br />
<br />
First, you need to install some tools which are necessary for UI development.<br />
<br />
$ npm install -g grunt-cli bower yo generator-ox-ui-module<br />
<br />
== Create a Workspace ==<br />
<br />
All your app development can take place inside one working directory. The examples will assume you have created a directory named <tt>myapp</tt> inside your home directory:<br />
<br />
$ mkdir ~/myapp<br />
$ cd ~/myapp<br />
<br />
It doesn't need to be in your home directory, and the actual name should reflect the name of your app. The main point is that all commands will be executed in this directory and all paths will be relative to this directory from now on.<br />
<br />
Now, you can generate a grunt configuration using:<br />
<br />
$ yo ox-ui-module<br />
<br />
The source code of your app will reside in the subfolder named <tt>apps</tt>. To avoid name collisions please pick a unique subfolder inside that. The easiest and recommended way is to use a domain name that you own. Typically, the domain elements are reversed, like in Java. <tt>example.com</tt> becomes <tt>com.example</tt>:<br />
<br />
$ mkdir -p apps/com.example<br />
<br />
== Writing an App ==<br />
<br />
As an example, let's create the smallest possible app and test it. It requires only two files: <tt>apps/com.example/register.js</tt> for the source code of the app:<br />
<br />
<pre class="language-javascript"><br />
define('com.example/register', function () {<br />
'use strict';<br />
alert('Hello, World!');<br />
});<br />
</pre><br />
<br />
and <tt>apps/com.example/manifest.json</tt> for the [[AppSuite:UI_manifests_explained | manifest]] which tells the UI that your app exists and what to do with it:<br />
<br />
<pre class="language-javascript">{ "namespace": "core" }</pre><br />
<br />
== Building an App ==<br />
<br />
The source code of your app can't be used by OX App Suite as it. It first has to be processed by the [[AppSuite:UI_build_system | build system]]. This step will check the source code for syntax errors, compress it, and depending on the structure of your code, many other things. The processed code is then written to a directory named <tt>build</tt> by default. Start the build with this command:<br />
<br />
$ grunt<br />
<br />
If your editor supports it, you can configure it to call the build system after every file save. Take care to call it from the top directory of your app's workspace, not from the directory of the saved file.<br />
<br />
== Testing an App ==<br />
<br />
The freshly built code can now be tested. Instead of uploading your code to an OX App Suite server, you can use the [[AppSuite:Appserver|appserver]] proxy to inject your code into the UI code of any existing OX App Suite installation. For example, to start <tt>appserver</tt> using [http://ox.io/ ox.io] as the server, you will need a local configuration pointing to that server. You can generate it with this command:<br />
<br />
$ grunt show-config:local --output grunt/local.conf.json<br />
<br />
Open up the file <tt>grunt/local.conf.json</tt> in your editor and add <tt><nowiki>"https://ox.io/appsuite/"</nowiki></tt> to the <tt>server</tt> setting of the appserver section. Then start the development server:<br />
<br />
$ grunt dev<br />
<br />
This command will serve your app from the local directory <tt>build</tt>, and get everything else from the URL specified in the <tt>server</tt> setting.<br />
<br />
Once appserver is running, you can access OX App Suite by opening your browser using this address:<br />
<br />
http://localhost:8337/appsuite<br />
<br />
After logging in, the app should be loaded and display the <tt>alert</tt> message.<br />
<br />
=== Development cycle ===<br />
<br />
Once you are sure that your setup works, you can extend the example and write the actual code for your app. The <tt>dev</tt> task will detect any changes and rebuild your app and even reload all browsers connected to<br />
<tt>http://localhost:8337/appsuite</tt>.<br />
<br />
While developing always keep in mind, that there is an [[AppSuite:Debugging_the_UI | article about debugging the user interface]] which helps you avoid and fix typical errors.<br />
<br />
== Packaging ==<br />
<br />
Together with our ox-ui-module generator (from version 0.8.0), we provide you with generators for packaging information.<br />
<br />
=== Generating a source tarball ===<br />
<br />
There is a grunt tasks to create source packages. You can use it to create a tar.gz file containing all sources needed to build the project. This source package should contain all dependencies that are installed during <tt>bower install</tt> and <tt>npm install</tt> (basically containing your <tt>bower_components</tt> and <tt>node_modules</tt> directories). To generate this file, run:<br />
<br />
$ grunt dist:source --include-dependencies<br />
<br />
This file (can be found in the <tt>dist/</tt> directory) together with the distribution specific packaging information will be needed to build the package.<br />
<br />
=== RPM packages ===<br />
<br />
In order to generate a spec file for your project, run:<br />
<br />
$ yo ox-ui-module:rpm-pkg<br />
<br />
Will generate a spec file, with some default values read from your <tt>package.json</tt> file. So you might want to make this file as complete as possible. However, yeoman will ask you about the information missing.<br />
<br />
''Please remember, that the generated spec file is only a scaffold for a real one. It will work for very simple cases, but as soon as your package gets more complicated, you will have to adjust it to your needs.''<br />
<br />
Once you have the spec file in your project, you can use<br />
<br />
$ grunt rpm-buildpackage # or rpm-build if you are on shared-grunt-config-0.6.0<br />
<br />
=== DEB packages ===<br />
<br />
Creating packages for the Debian distribution works similar to rpm packages. Just run:<br />
<br />
$ yo ox-ui-module:deb-pkg<br />
<br />
This will generate a debian directory containing all needed files. Some default values will be read from your <tt>package.json</tt> file. So you might want to make this file as complete as possible. However, yeoman will ask you about the information missing.<br />
<br />
''Please remember, that the generated spec file is only a scaffold for a real one. It will work for very simple cases, but as soon as your package gets more complicated, you will have to adjust it to your needs.''<br />
<br />
Once you have a debian/ directory for your project, you can go on.<br />
To enable some grunt tasks to help you, you need to install (with npm install) <tt>grunt-contrib-compress</tt> and <tt>grunt-exec</tt>. Now you can run:<br />
<br />
$ grunt dpkg-buildpackage<br />
<br />
== i18n ==<br />
<br />
To use the proven way to translate your AppSuite application, you should now check out the [[AppSuite:I18n | documentation]] about all [[AppSuite:I18n | i18n]] tasks. Please browse to the [[AppSuite:I18n | i18n]] page.<br />
<br />
== Further Reading ==<br />
* Congratulations you have just built your first app for OX App Suite, but please keep in mind that there are [[AppSuite:Developing for the UI#What_can_i_build.3F | quite a few options]] for developing for OX App Suite.<br />
* In case you want to upgrade your existing app for OX App Suite, read [[Appsuite:Upgrade_app_using_yo | the upgrade guide]].<br />
* More information on the build system can be found on github:<br />
** [https://github.com/Open-Xchange-Frontend/shared-grunt-config See all available grunt tasks]<br />
** [https://github.com/Open-Xchange-Frontend/generator-ox-ui-module Documentation of all generator tasks]<br />
* If you're stuck somewhere, the article about [[AppSuite:Debugging_the_UI | debugging the UI]] might help you.<br />
* You can read this to get a better overview of [[AppSuite:Developing for the UI | developing the user inferface]].</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GettingStarted_7.6.0&diff=18163AppSuite:GettingStarted 7.6.02014-07-22T10:15:07Z<p>J.ohny.b: /* RPM packages */</p>
<hr />
<div><div class="title">Getting Started</div><br />
<br />
<div style="float:right; padding: 10px;">[[File:OX_AppSuite_UI_Development_Workflow.png]]</div><br />
<br />
__TOC__<br />
<br />
Hello and welcome to the world of OX App Suite development. This document is designed to get you started with developing your first app for OX App Suite as quickly and simply as possible. However, along the way we will also tempt you to learn more by linking to some more in-depth documentation about the various topics we cover. <br />
<br />
<div style="clear: both"></div><br />
<br />
== Installing the Development Tools ==<br />
<br />
First, you need to install some tools which are necessary for UI development.<br />
<br />
$ npm install -g grunt-cli bower yo generator-ox-ui-module<br />
<br />
== Create a Workspace ==<br />
<br />
All your app development can take place inside one working directory. The examples will assume you have created a directory named <tt>myapp</tt> inside your home directory:<br />
<br />
$ mkdir ~/myapp<br />
$ cd ~/myapp<br />
<br />
It doesn't need to be in your home directory, and the actual name should reflect the name of your app. The main point is that all commands will be executed in this directory and all paths will be relative to this directory from now on.<br />
<br />
Now, you can generate a grunt configuration using:<br />
<br />
$ yo ox-ui-module<br />
<br />
The source code of your app will reside in the subfolder named <tt>apps</tt>. To avoid name collisions please pick a unique subfolder inside that. The easiest and recommended way is to use a domain name that you own. Typically, the domain elements are reversed, like in Java. <tt>example.com</tt> becomes <tt>com.example</tt>:<br />
<br />
$ mkdir -p apps/com.example<br />
<br />
== Writing an App ==<br />
<br />
As an example, let's create the smallest possible app and test it. It requires only two files: <tt>apps/com.example/register.js</tt> for the source code of the app:<br />
<br />
<pre class="language-javascript"><br />
define('com.example/register', function () {<br />
'use strict';<br />
alert('Hello, World!');<br />
});<br />
</pre><br />
<br />
and <tt>apps/com.example/manifest.json</tt> for the [[AppSuite:UI_manifests_explained | manifest]] which tells the UI that your app exists and what to do with it:<br />
<br />
<pre class="language-javascript">{ "namespace": "core" }</pre><br />
<br />
== Building an App ==<br />
<br />
The source code of your app can't be used by OX App Suite as it. It first has to be processed by the [[AppSuite:UI_build_system | build system]]. This step will check the source code for syntax errors, compress it, and depending on the structure of your code, many other things. The processed code is then written to a directory named <tt>build</tt> by default. Start the build with this command:<br />
<br />
$ grunt<br />
<br />
If your editor supports it, you can configure it to call the build system after every file save. Take care to call it from the top directory of your app's workspace, not from the directory of the saved file.<br />
<br />
== Testing an App ==<br />
<br />
The freshly built code can now be tested. Instead of uploading your code to an OX App Suite server, you can use the [[AppSuite:Appserver|appserver]] proxy to inject your code into the UI code of any existing OX App Suite installation. For example, to start <tt>appserver</tt> using [http://ox.io/ ox.io] as the server, you will need a local configuration pointing to that server. You can generate it with this command:<br />
<br />
$ grunt show-config:local --output grunt/local.conf.json<br />
<br />
Open up the file <tt>grunt/local.conf.json</tt> in your editor and add <tt><nowiki>"https://ox.io/appsuite/"</nowiki></tt> to the <tt>server</tt> setting of the appserver section. Then start the development server:<br />
<br />
$ grunt dev<br />
<br />
This command will serve your app from the local directory <tt>build</tt>, and get everything else from the URL specified in the <tt>server</tt> setting.<br />
<br />
Once appserver is running, you can access OX App Suite by opening your browser using this address:<br />
<br />
http://localhost:8337/appsuite<br />
<br />
After logging in, the app should be loaded and display the <tt>alert</tt> message.<br />
<br />
=== Development cycle ===<br />
<br />
Once you are sure that your setup works, you can extend the example and write the actual code for your app. The <tt>dev</tt> task will detect any changes and rebuild your app and even reload all browsers connected to<br />
<tt>http://localhost:8337/appsuite</tt>.<br />
<br />
While developing always keep in mind, that there is an [[AppSuite:Debugging_the_UI | article about debugging the user interface]] which helps you avoid and fix typical errors.<br />
<br />
== Packaging ==<br />
<br />
Together with our ox-ui-module generator (from version 0.8.0), we provide you with generators for packaging information.<br />
<br />
=== Generating a source tarball ===<br />
<br />
There is a grunt tasks to create source packages. You can use it to create a tar.gz file containing all sources needed to build the project. This source package should contain all dependencies that are installed during <tt>bower install</tt> and <tt>npm install</tt> (basically containing your <tt>bower_components</tt> and <tt>node_modules</tt> directories). To generate this file, run:<br />
<br />
$ grunt dist:source --include-dependencies<br />
<br />
This file (can be found in the <tt>dist/</tt> directory) together with the distribution specific packaging information will be needed to build the package.<br />
<br />
=== RPM packages ===<br />
<br />
In order to generate a spec file for your project, run:<br />
<br />
$ yo ox-ui-module:rpm-pkg<br />
<br />
Will generate a spec file, with some default values read from your <tt>package.json</tt> file. So you might want to make this file as complete as possible. However, yeoman will ask you about the information missing.<br />
<br />
''Please remember, that the generated spec file is only a scaffold for a real one. It will work for very simple cases, but as soon as your package gets more complicated, you will have to adjust it to your needs.''<br />
<br />
Once you have the spec file in your project, you can use<br />
<br />
$ grunt rpm-buildpackage # or rpm-build if you are on shared-grunt-config-0.6.0<br />
<br />
=== DEB packages ===<br />
<br />
Creating packages for the Debian distribution works similar to rpm packages. Just run:<br />
<br />
$ yo ox-ui-module:deb-pkg<br />
<br />
This will generate a debian directory containing all needed files. Some default values will be read from your <tt>package.json</tt> file. So you might want to make this file as complete as possible. However, yeoman will ask you about the information missing.<br />
<br />
''Please remember, that the generated spec file is only a scaffold for a real one. It will work for very simple cases, but as soon as your package gets more complicated, you will have to adjust it to your needs.''<br />
<br />
Once you have a debian/ directory for your project, you can go on.<br />
To enable some grunt tasks to help you, you need to install (with npm install) <tt>grunt-contrib-compress</tt> and <tt>grunt-exec</tt>. Now you can run:<br />
<br />
$ grunt dist:dpkg-source<br />
<br />
You can now use the files in the dist/ directory to build your packages. A simple example would look like:<br />
<br />
$ cd dist/unicorn-0.0.1/<br />
dpkg-buildpackage -b<br />
<br />
== i18n ==<br />
<br />
To use the proven way to translate your AppSuite application, you should now check out the [[AppSuite:I18n | documentation]] about all [[AppSuite:I18n | i18n]] tasks. Please browse to the [[AppSuite:I18n | i18n]] page.<br />
<br />
== Further Reading ==<br />
* Congratulations you have just built your first app for OX App Suite, but please keep in mind that there are [[AppSuite:Developing for the UI#What_can_i_build.3F | quite a few options]] for developing for OX App Suite.<br />
* In case you want to upgrade your existing app for OX App Suite, read [[Appsuite:Upgrade_app_using_yo | the upgrade guide]].<br />
* More information on the build system can be found on github:<br />
** [https://github.com/Open-Xchange-Frontend/shared-grunt-config See all available grunt tasks]<br />
** [https://github.com/Open-Xchange-Frontend/generator-ox-ui-module Documentation of all generator tasks]<br />
* If you're stuck somewhere, the article about [[AppSuite:Debugging_the_UI | debugging the UI]] might help you.<br />
* You can read this to get a better overview of [[AppSuite:Developing for the UI | developing the user inferface]].</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GettingStarted_7.6.0&diff=18162AppSuite:GettingStarted 7.6.02014-07-22T10:14:46Z<p>J.ohny.b: /* RPM packages */</p>
<hr />
<div><div class="title">Getting Started</div><br />
<br />
<div style="float:right; padding: 10px;">[[File:OX_AppSuite_UI_Development_Workflow.png]]</div><br />
<br />
__TOC__<br />
<br />
Hello and welcome to the world of OX App Suite development. This document is designed to get you started with developing your first app for OX App Suite as quickly and simply as possible. However, along the way we will also tempt you to learn more by linking to some more in-depth documentation about the various topics we cover. <br />
<br />
<div style="clear: both"></div><br />
<br />
== Installing the Development Tools ==<br />
<br />
First, you need to install some tools which are necessary for UI development.<br />
<br />
$ npm install -g grunt-cli bower yo generator-ox-ui-module<br />
<br />
== Create a Workspace ==<br />
<br />
All your app development can take place inside one working directory. The examples will assume you have created a directory named <tt>myapp</tt> inside your home directory:<br />
<br />
$ mkdir ~/myapp<br />
$ cd ~/myapp<br />
<br />
It doesn't need to be in your home directory, and the actual name should reflect the name of your app. The main point is that all commands will be executed in this directory and all paths will be relative to this directory from now on.<br />
<br />
Now, you can generate a grunt configuration using:<br />
<br />
$ yo ox-ui-module<br />
<br />
The source code of your app will reside in the subfolder named <tt>apps</tt>. To avoid name collisions please pick a unique subfolder inside that. The easiest and recommended way is to use a domain name that you own. Typically, the domain elements are reversed, like in Java. <tt>example.com</tt> becomes <tt>com.example</tt>:<br />
<br />
$ mkdir -p apps/com.example<br />
<br />
== Writing an App ==<br />
<br />
As an example, let's create the smallest possible app and test it. It requires only two files: <tt>apps/com.example/register.js</tt> for the source code of the app:<br />
<br />
<pre class="language-javascript"><br />
define('com.example/register', function () {<br />
'use strict';<br />
alert('Hello, World!');<br />
});<br />
</pre><br />
<br />
and <tt>apps/com.example/manifest.json</tt> for the [[AppSuite:UI_manifests_explained | manifest]] which tells the UI that your app exists and what to do with it:<br />
<br />
<pre class="language-javascript">{ "namespace": "core" }</pre><br />
<br />
== Building an App ==<br />
<br />
The source code of your app can't be used by OX App Suite as it. It first has to be processed by the [[AppSuite:UI_build_system | build system]]. This step will check the source code for syntax errors, compress it, and depending on the structure of your code, many other things. The processed code is then written to a directory named <tt>build</tt> by default. Start the build with this command:<br />
<br />
$ grunt<br />
<br />
If your editor supports it, you can configure it to call the build system after every file save. Take care to call it from the top directory of your app's workspace, not from the directory of the saved file.<br />
<br />
== Testing an App ==<br />
<br />
The freshly built code can now be tested. Instead of uploading your code to an OX App Suite server, you can use the [[AppSuite:Appserver|appserver]] proxy to inject your code into the UI code of any existing OX App Suite installation. For example, to start <tt>appserver</tt> using [http://ox.io/ ox.io] as the server, you will need a local configuration pointing to that server. You can generate it with this command:<br />
<br />
$ grunt show-config:local --output grunt/local.conf.json<br />
<br />
Open up the file <tt>grunt/local.conf.json</tt> in your editor and add <tt><nowiki>"https://ox.io/appsuite/"</nowiki></tt> to the <tt>server</tt> setting of the appserver section. Then start the development server:<br />
<br />
$ grunt dev<br />
<br />
This command will serve your app from the local directory <tt>build</tt>, and get everything else from the URL specified in the <tt>server</tt> setting.<br />
<br />
Once appserver is running, you can access OX App Suite by opening your browser using this address:<br />
<br />
http://localhost:8337/appsuite<br />
<br />
After logging in, the app should be loaded and display the <tt>alert</tt> message.<br />
<br />
=== Development cycle ===<br />
<br />
Once you are sure that your setup works, you can extend the example and write the actual code for your app. The <tt>dev</tt> task will detect any changes and rebuild your app and even reload all browsers connected to<br />
<tt>http://localhost:8337/appsuite</tt>.<br />
<br />
While developing always keep in mind, that there is an [[AppSuite:Debugging_the_UI | article about debugging the user interface]] which helps you avoid and fix typical errors.<br />
<br />
== Packaging ==<br />
<br />
Together with our ox-ui-module generator (from version 0.8.0), we provide you with generators for packaging information.<br />
<br />
=== Generating a source tarball ===<br />
<br />
There is a grunt tasks to create source packages. You can use it to create a tar.gz file containing all sources needed to build the project. This source package should contain all dependencies that are installed during <tt>bower install</tt> and <tt>npm install</tt> (basically containing your <tt>bower_components</tt> and <tt>node_modules</tt> directories). To generate this file, run:<br />
<br />
$ grunt dist:source --include-dependencies<br />
<br />
This file (can be found in the <tt>dist/</tt> directory) together with the distribution specific packaging information will be needed to build the package.<br />
<br />
=== RPM packages ===<br />
<br />
In order to generate a spec file for your project, run:<br />
<br />
$ yo ox-ui-module:rpm-pkg<br />
<br />
Will generate a spec file, with some default values read from your <tt>package.json</tt> file. So you might want to make this file as complete as possible. However, yeoman will ask you about the information missing.<br />
<br />
''Please remember, that the generated spec file is only a scaffold for a real one. It will work for very simple cases, but as soon as your package gets more complicated, you will have to adjust it to your needs.''<br />
<br />
Once you have the spec file in your project, you can use<br />
<br />
$ grunt rpm-buildpackage # or rpm-build if you are on shared-grunt-config-7.6.0<br />
<br />
=== DEB packages ===<br />
<br />
Creating packages for the Debian distribution works similar to rpm packages. Just run:<br />
<br />
$ yo ox-ui-module:deb-pkg<br />
<br />
This will generate a debian directory containing all needed files. Some default values will be read from your <tt>package.json</tt> file. So you might want to make this file as complete as possible. However, yeoman will ask you about the information missing.<br />
<br />
''Please remember, that the generated spec file is only a scaffold for a real one. It will work for very simple cases, but as soon as your package gets more complicated, you will have to adjust it to your needs.''<br />
<br />
Once you have a debian/ directory for your project, you can go on.<br />
To enable some grunt tasks to help you, you need to install (with npm install) <tt>grunt-contrib-compress</tt> and <tt>grunt-exec</tt>. Now you can run:<br />
<br />
$ grunt dist:dpkg-source<br />
<br />
You can now use the files in the dist/ directory to build your packages. A simple example would look like:<br />
<br />
$ cd dist/unicorn-0.0.1/<br />
dpkg-buildpackage -b<br />
<br />
== i18n ==<br />
<br />
To use the proven way to translate your AppSuite application, you should now check out the [[AppSuite:I18n | documentation]] about all [[AppSuite:I18n | i18n]] tasks. Please browse to the [[AppSuite:I18n | i18n]] page.<br />
<br />
== Further Reading ==<br />
* Congratulations you have just built your first app for OX App Suite, but please keep in mind that there are [[AppSuite:Developing for the UI#What_can_i_build.3F | quite a few options]] for developing for OX App Suite.<br />
* In case you want to upgrade your existing app for OX App Suite, read [[Appsuite:Upgrade_app_using_yo | the upgrade guide]].<br />
* More information on the build system can be found on github:<br />
** [https://github.com/Open-Xchange-Frontend/shared-grunt-config See all available grunt tasks]<br />
** [https://github.com/Open-Xchange-Frontend/generator-ox-ui-module Documentation of all generator tasks]<br />
* If you're stuck somewhere, the article about [[AppSuite:Debugging_the_UI | debugging the UI]] might help you.<br />
* You can read this to get a better overview of [[AppSuite:Developing for the UI | developing the user inferface]].</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Testing_3rd-party_code&diff=18144AppSuite:Testing 3rd-party code2014-07-18T07:20:28Z<p>J.ohny.b: /* Running the tests */</p>
<hr />
<div><div class="title">Testing 3rd party code</div><br />
<br />
__TOC__<br />
<br />
This article explains one way to add automatic testing to UI plugins. <br />
<br />
== Libraries ==<br />
<br />
* [http://visionmedia.github.io/mocha/ Mocha] - the fun, simple, flexible JS testing framework<br />
* [http://sinonjs.org/ Sinon.JS] - Standalone test spies, stubs and mocks for JavaScript.<br />
* [http://karma-runner.github.io/ Karma Runner] - test runner<br />
* [http://chaijs.com/ Chai.js] - Assertion framework<br />
<br />
== using karma-ox-ui ==<br />
<br />
The [[AppSuite:GettingStarted_7.6.0|shared grunt configuration]] ships with most of the parts pre-configured. Still, a little setup is needed to enable automatic testing for an external app. Since we use many standard libraries, this approach can be extended as you wish.<br />
<br />
=== Setup ===<br />
<br />
Make sure, karma executable is installed:<br />
<br />
npm install -g karma-cli<br />
<br />
Install a few more testing libraries locally:<br />
<br />
npm install --save-dev karma-mocha karma-chai karma-sinon karma-ox-ui karma-phantomjs-launcher<br />
<br />
After that, in your plugin directory generate a new karma.conf.js:<br />
<br />
jb@wiggum ~/code/appsuite/ox_pgp_mail (git)-[ding] % karma init<br />
<br />
Which testing framework do you want to use ?<br />
Press tab to list possible options. Enter to move to the next question.<br />
> mocha <br />
<br />
Do you want to use Require.js ?<br />
This will add Require.js plugin.<br />
Press tab to list possible options. Enter to move to the next question.<br />
> no <br />
<br />
Do you want to capture any browsers automatically ?<br />
Press tab to list possible options. Enter empty string to move to the next question.<br />
> PhantomJS <br />
> <br />
<br />
What is the location of your source and test files ?<br />
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Should any of the files included by the previous patterns be excluded ?<br />
You can use glob patterns, eg. "**/*.swp".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Do you want Karma to watch all the files and run the tests on change ?<br />
Press tab to list possible options.<br />
> no <br />
<br />
<br />
Config file generated at "/home/jb/code/appsuite/ox_pgp_mail/karma.conf.js".<br />
<br />
Edit the generated file and adjust the following configuration variables:<br />
<br />
<pre class="language-javascript"><br />
basePath: 'build/',<br />
frameworks: ['ox-ui', 'sinon', 'mocha', 'chai'],<br />
files: [<br />
'spec/test-main.js',<br />
{pattern: 'apps/**/*.js', included: false},<br />
{pattern: 'spec/**/*_spec.js', included: false}<br />
]<br />
</pre><br />
<br />
Generate a main loader script to start the test after App Suite Core UI<br />
has been booted. The file should be put in `spec/test-main.js`:<br />
<br />
<pre class="language-javascript"><br />
var allTestFiles = [];<br />
var TEST_REGEXP = /_spec\.js$/i;<br />
<br />
var pathToModule = function(path) {<br />
return path;<br />
// return path.replace(/^\/base\//, '').replace(/\.js$/, '');<br />
};<br />
<br />
Object.keys(window.__karma__.files).forEach(function(file) {<br />
if (TEST_REGEXP.test(file)) {<br />
// Normalize paths to RequireJS module names.<br />
allTestFiles.push(pathToModule(file));<br />
}<br />
});<br />
<br />
require(['io.ox/core/extPatterns/stage'], function (Stage) {<br />
<br />
'use strict';<br />
<br />
ox.testUtils.stubAppsuiteBody();<br />
<br />
new Stage('io.ox/core/stages', {<br />
id: 'run_tests',<br />
index: 99999,<br />
run: function (baton) {<br />
requirejs.config({<br />
// Karma serves files from '/base/apps'<br />
baseUrl: '/base/apps',<br />
<br />
// ask Require.js to load these files (all our tests)<br />
deps: allTestFiles,<br />
<br />
// start test run, once Require.js is done<br />
callback: window.__karma__.start<br />
});<br />
}<br />
});<br />
});<br />
</pre><br />
<br />
=== Running the tests ===<br />
<br />
There are multiple targets provided in<br />
[https://github.com/Open-Xchange-Frontend/shared-grunt-config shared grunt configuration].<br />
The recommended way is to run:<br />
<br />
grunt dev<br />
<br />
This will start a connect server, the karma<br />
test server and a watcher for changes. You can trigger a testrun manually by running:<br />
<br />
grunt testrun<br />
<br />
in another terminal. This will be done automatically by the grunt watch<br />
task, after any source file of your project has been changed.<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]<br />
[[Category:Development process]]<br />
[[Category:Testing]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Testing_3rd-party_code&diff=18143AppSuite:Testing 3rd-party code2014-07-18T07:20:19Z<p>J.ohny.b: </p>
<hr />
<div><div class="title">Testing 3rd party code</div><br />
<br />
__TOC__<br />
<br />
This article explains one way to add automatic testing to UI plugins. <br />
<br />
== Libraries ==<br />
<br />
* [http://visionmedia.github.io/mocha/ Mocha] - the fun, simple, flexible JS testing framework<br />
* [http://sinonjs.org/ Sinon.JS] - Standalone test spies, stubs and mocks for JavaScript.<br />
* [http://karma-runner.github.io/ Karma Runner] - test runner<br />
* [http://chaijs.com/ Chai.js] - Assertion framework<br />
<br />
== using karma-ox-ui ==<br />
<br />
The [[AppSuite:GettingStarted_7.6.0|shared grunt configuration]] ships with most of the parts pre-configured. Still, a little setup is needed to enable automatic testing for an external app. Since we use many standard libraries, this approach can be extended as you wish.<br />
<br />
=== Setup ===<br />
<br />
Make sure, karma executable is installed:<br />
<br />
npm install -g karma-cli<br />
<br />
Install a few more testing libraries locally:<br />
<br />
npm install --save-dev karma-mocha karma-chai karma-sinon karma-ox-ui karma-phantomjs-launcher<br />
<br />
After that, in your plugin directory generate a new karma.conf.js:<br />
<br />
jb@wiggum ~/code/appsuite/ox_pgp_mail (git)-[ding] % karma init<br />
<br />
Which testing framework do you want to use ?<br />
Press tab to list possible options. Enter to move to the next question.<br />
> mocha <br />
<br />
Do you want to use Require.js ?<br />
This will add Require.js plugin.<br />
Press tab to list possible options. Enter to move to the next question.<br />
> no <br />
<br />
Do you want to capture any browsers automatically ?<br />
Press tab to list possible options. Enter empty string to move to the next question.<br />
> PhantomJS <br />
> <br />
<br />
What is the location of your source and test files ?<br />
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Should any of the files included by the previous patterns be excluded ?<br />
You can use glob patterns, eg. "**/*.swp".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Do you want Karma to watch all the files and run the tests on change ?<br />
Press tab to list possible options.<br />
> no <br />
<br />
<br />
Config file generated at "/home/jb/code/appsuite/ox_pgp_mail/karma.conf.js".<br />
<br />
Edit the generated file and adjust the following configuration variables:<br />
<br />
<pre class="language-javascript"><br />
basePath: 'build/',<br />
frameworks: ['ox-ui', 'sinon', 'mocha', 'chai'],<br />
files: [<br />
'spec/test-main.js',<br />
{pattern: 'apps/**/*.js', included: false},<br />
{pattern: 'spec/**/*_spec.js', included: false}<br />
]<br />
</pre><br />
<br />
Generate a main loader script to start the test after App Suite Core UI<br />
has been booted. The file should be put in `spec/test-main.js`:<br />
<br />
<pre class="language-javascript"><br />
var allTestFiles = [];<br />
var TEST_REGEXP = /_spec\.js$/i;<br />
<br />
var pathToModule = function(path) {<br />
return path;<br />
// return path.replace(/^\/base\//, '').replace(/\.js$/, '');<br />
};<br />
<br />
Object.keys(window.__karma__.files).forEach(function(file) {<br />
if (TEST_REGEXP.test(file)) {<br />
// Normalize paths to RequireJS module names.<br />
allTestFiles.push(pathToModule(file));<br />
}<br />
});<br />
<br />
require(['io.ox/core/extPatterns/stage'], function (Stage) {<br />
<br />
'use strict';<br />
<br />
ox.testUtils.stubAppsuiteBody();<br />
<br />
new Stage('io.ox/core/stages', {<br />
id: 'run_tests',<br />
index: 99999,<br />
run: function (baton) {<br />
requirejs.config({<br />
// Karma serves files from '/base/apps'<br />
baseUrl: '/base/apps',<br />
<br />
// ask Require.js to load these files (all our tests)<br />
deps: allTestFiles,<br />
<br />
// start test run, once Require.js is done<br />
callback: window.__karma__.start<br />
});<br />
}<br />
});<br />
});<br />
</pre><br />
<br />
=== Running the tests ===<br />
<br />
There are multiple targets provided in<br />
[https://github.com/Open-Xchange-Frontend/shared-grunt-config shared grunt configuration].<br />
The recommended way is to run:<br />
<br />
grunt dev<br />
<br />
This will start a connect server, the karma<br />
test server and a watcher for changes. You can trigger a testrun manually by running:<br />
<br />
grunt testrun<br />
<br />
in another terminal. This will be done automatically by the grunt watch<br />
task, after any source file of your project has been changed.<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]<br />
[[Category:Development process]]<br />
[[Category:Testing]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Testing_3rd-party_code&diff=18142AppSuite:Testing 3rd-party code2014-07-18T07:18:31Z<p>J.ohny.b: /* Setup */</p>
<hr />
<div><div class="title">Testing 3rd party code</div><br />
<br />
__TOC__<br />
<br />
This article explains one way to add automatic testing to UI plugins. <br />
<br />
== Libraries ==<br />
<br />
* [http://visionmedia.github.io/mocha/ Mocha] - the fun, simple, flexible JS testing framework<br />
* [http://sinonjs.org/ Sinon.JS] - Standalone test spies, stubs and mocks for JavaScript.<br />
* [http://karma-runner.github.io/ Karma Runner] - test runner<br />
* [http://chaijs.com/ Chai.js] - Assertion framework<br />
<br />
== using karma-ox-ui ==<br />
<br />
The [[AppSuite:GettingStarted_7.6.0|shared grunt configuration]] ships with most of the parts pre-configured. Still, a little setup is needed to enable automatic testing for an external app. Since we use many standard libraries, this approach can be extended as you wish.<br />
<br />
=== Setup ===<br />
<br />
Make sure, karma executable is installed:<br />
<br />
npm install -g karma-cli<br />
<br />
Install a few more testing libraries locally:<br />
<br />
npm install --save-dev karma-mocha karma-chai karma-sinon karma-ox-ui karma-phantomjs-launcher<br />
<br />
After that, in your plugin directory generate a new karma.conf.js:<br />
<br />
jb@wiggum ~/code/appsuite/ox_pgp_mail (git)-[ding] % karma init<br />
<br />
Which testing framework do you want to use ?<br />
Press tab to list possible options. Enter to move to the next question.<br />
> mocha <br />
<br />
Do you want to use Require.js ?<br />
This will add Require.js plugin.<br />
Press tab to list possible options. Enter to move to the next question.<br />
> no <br />
<br />
Do you want to capture any browsers automatically ?<br />
Press tab to list possible options. Enter empty string to move to the next question.<br />
> PhantomJS <br />
> <br />
<br />
What is the location of your source and test files ?<br />
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Should any of the files included by the previous patterns be excluded ?<br />
You can use glob patterns, eg. "**/*.swp".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Do you want Karma to watch all the files and run the tests on change ?<br />
Press tab to list possible options.<br />
> no <br />
<br />
<br />
Config file generated at "/home/jb/code/appsuite/ox_pgp_mail/karma.conf.js".<br />
<br />
Edit the generated file and adjust the following configuration variables:<br />
<br />
<pre class="language-javascript"><br />
basePath: 'build/',<br />
frameworks: ['ox-ui', 'sinon', 'mocha', 'chai'],<br />
files: [<br />
'spec/test-main.js',<br />
{pattern: 'apps/**/*.js', included: false},<br />
{pattern: 'spec/**/*_spec.js', included: false}<br />
]<br />
</pre><br />
<br />
Generate a main loader script to start the test after App Suite Core UI<br />
has been booted. The file should be put in `spec/test-main.js`:<br />
<br />
<pre class="language-javascript"><br />
var allTestFiles = [];<br />
var TEST_REGEXP = /_spec\.js$/i;<br />
<br />
var pathToModule = function(path) {<br />
return path;<br />
// return path.replace(/^\/base\//, '').replace(/\.js$/, '');<br />
};<br />
<br />
Object.keys(window.__karma__.files).forEach(function(file) {<br />
if (TEST_REGEXP.test(file)) {<br />
// Normalize paths to RequireJS module names.<br />
allTestFiles.push(pathToModule(file));<br />
}<br />
});<br />
<br />
require(['io.ox/core/extPatterns/stage'], function (Stage) {<br />
<br />
'use strict';<br />
<br />
ox.testUtils.stubAppsuiteBody();<br />
<br />
new Stage('io.ox/core/stages', {<br />
id: 'run_tests',<br />
index: 99999,<br />
run: function (baton) {<br />
requirejs.config({<br />
// Karma serves files from '/base/apps'<br />
baseUrl: '/base/apps',<br />
<br />
// ask Require.js to load these files (all our tests)<br />
deps: allTestFiles,<br />
<br />
// start test run, once Require.js is done<br />
callback: window.__karma__.start<br />
});<br />
}<br />
});<br />
});<br />
</pre><br />
<br />
=== Running the tests ===<br />
<br />
There are multiple targets provided in<br />
[https://github.com/Open-Xchange-Frontend/shared-grunt-config shared grunt configuration].<br />
The recommended way is to run <tt>grunt dev</tt>. This will start a connect server, the karma<br />
test server and a watcher for changes. You can trigger a testrun manually by running<br />
<tt>grunt testrun</tt> in another terminal. This will be done automatically by the grunt watch<br />
task, after any source file of your project has been changed.<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]<br />
[[Category:Development process]]<br />
[[Category:Testing]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Testing_3rd-party_code&diff=18141AppSuite:Testing 3rd-party code2014-07-18T07:17:01Z<p>J.ohny.b: </p>
<hr />
<div><div class="title">Testing 3rd party code</div><br />
<br />
__TOC__<br />
<br />
This article explains one way to add automatic testing to UI plugins. <br />
<br />
== Libraries ==<br />
<br />
* [http://visionmedia.github.io/mocha/ Mocha] - the fun, simple, flexible JS testing framework<br />
* [http://sinonjs.org/ Sinon.JS] - Standalone test spies, stubs and mocks for JavaScript.<br />
* [http://karma-runner.github.io/ Karma Runner] - test runner<br />
* [http://chaijs.com/ Chai.js] - Assertion framework<br />
<br />
== using karma-ox-ui ==<br />
<br />
The [[AppSuite:GettingStarted_7.6.0|shared grunt configuration]] ships with most of the parts pre-configured. Still, a little setup is needed to enable automatic testing for an external app. Since we use many standard libraries, this approach can be extended as you wish.<br />
<br />
=== Setup ===<br />
<br />
Make sure, karma executable is installed:<br />
<br />
npm install -g karma-cli<br />
<br />
Install a few more testing libraries locally:<br />
<br />
npm install --save-dev karma-mocha karma-chai karma-sinon karma-ox-ui karma-phantomjs-launcher<br />
<br />
After that, in your plugin directory generate a new karma.conf.js:<br />
<br />
jb@wiggum ~/code/appsuite/ox_pgp_mail (git)-[karma] % karma init<br />
<br />
Which testing framework do you want to use ?<br />
Press tab to list possible options. Enter to move to the next question.<br />
> mocha <br />
<br />
Do you want to use Require.js ?<br />
This will add Require.js plugin.<br />
Press tab to list possible options. Enter to move to the next question.<br />
> no <br />
<br />
Do you want to capture any browsers automatically ?<br />
Press tab to list possible options. Enter empty string to move to the next question.<br />
> <br />
<br />
What is the location of your source and test files ?<br />
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
jb@wiggum ~/code/appsuite/ox_pgp_mail (git)-[ding] % karma init<br />
<br />
Which testing framework do you want to use ?<br />
Press tab to list possible options. Enter to move to the next question.<br />
> mocha <br />
<br />
Do you want to use Require.js ?<br />
This will add Require.js plugin.<br />
Press tab to list possible options. Enter to move to the next question.<br />
> no <br />
<br />
Do you want to capture any browsers automatically ?<br />
Press tab to list possible options. Enter empty string to move to the next question.<br />
> PhantomJS <br />
> <br />
<br />
What is the location of your source and test files ?<br />
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Should any of the files included by the previous patterns be excluded ?<br />
You can use glob patterns, eg. "**/*.swp".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Do you want Karma to watch all the files and run the tests on change ?<br />
Press tab to list possible options.<br />
> no <br />
<br />
<br />
Config file generated at "/home/jb/code/appsuite/ox_pgp_mail/karma.conf.js".<br />
<br />
Edit the generated file and adjust the following configuration variables:<br />
<br />
<pre class="language-javascript"><br />
basePath: 'build/',<br />
frameworks: ['ox-ui', 'sinon', 'mocha', 'chai'],<br />
files: [<br />
'spec/test-main.js',<br />
{pattern: 'apps/**/*.js', included: false},<br />
{pattern: 'spec/**/*_spec.js', included: false}<br />
]<br />
</pre><br />
<br />
Generate a main loader script to start the test after App Suite Core UI<br />
has been booted. The file should be put in `spec/test-main.js`:<br />
<br />
<pre class="language-javascript"><br />
var allTestFiles = [];<br />
var TEST_REGEXP = /_spec\.js$/i;<br />
<br />
var pathToModule = function(path) {<br />
return path;<br />
// return path.replace(/^\/base\//, '').replace(/\.js$/, '');<br />
};<br />
<br />
Object.keys(window.__karma__.files).forEach(function(file) {<br />
if (TEST_REGEXP.test(file)) {<br />
// Normalize paths to RequireJS module names.<br />
allTestFiles.push(pathToModule(file));<br />
}<br />
});<br />
<br />
require(['io.ox/core/extPatterns/stage'], function (Stage) {<br />
<br />
'use strict';<br />
<br />
ox.testUtils.stubAppsuiteBody();<br />
<br />
new Stage('io.ox/core/stages', {<br />
id: 'run_tests',<br />
index: 99999,<br />
run: function (baton) {<br />
requirejs.config({<br />
// Karma serves files from '/base/apps'<br />
baseUrl: '/base/apps',<br />
<br />
// ask Require.js to load these files (all our tests)<br />
deps: allTestFiles,<br />
<br />
// start test run, once Require.js is done<br />
callback: window.__karma__.start<br />
});<br />
}<br />
});<br />
});<br />
</pre><br />
<br />
=== Running the tests ===<br />
<br />
There are multiple targets provided in<br />
[https://github.com/Open-Xchange-Frontend/shared-grunt-config shared grunt configuration].<br />
The recommended way is to run <tt>grunt dev</tt>. This will start a connect server, the karma<br />
test server and a watcher for changes. You can trigger a testrun manually by running<br />
<tt>grunt testrun</tt> in another terminal. This will be done automatically by the grunt watch<br />
task, after any source file of your project has been changed.<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]<br />
[[Category:Development process]]<br />
[[Category:Testing]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Testing_3rd-party_code&diff=18140AppSuite:Testing 3rd-party code2014-07-18T07:16:08Z<p>J.ohny.b: </p>
<hr />
<div><div class="title">Testing UI plugins</div><br />
<br />
__TOC__<br />
<br />
This article explains one way to add automatic testing to UI plugins. <br />
<br />
== Libraries ==<br />
<br />
* [http://visionmedia.github.io/mocha/ Mocha] - the fun, simple, flexible JS testing framework<br />
* [http://sinonjs.org/ Sinon.JS] - Standalone test spies, stubs and mocks for JavaScript.<br />
* [http://karma-runner.github.io/ Karma Runner] - test runner<br />
* [http://chaijs.com/ Chai.js] - Assertion framework<br />
<br />
== using karma-ox-ui ==<br />
<br />
The [[AppSuite:GettingStarted_7.6.0|shared grunt configuration]] ships with most of the parts pre-configured. Still, a little setup is needed to enable automatic testing for an external app. Since we use many standard libraries, this approach can be extended as you wish.<br />
<br />
=== Setup ===<br />
<br />
Make sure, karma executable is installed:<br />
<br />
npm install -g karma-cli<br />
<br />
Install a few more testing libraries locally:<br />
<br />
npm install --save-dev karma-mocha karma-chai karma-sinon karma-ox-ui karma-phantomjs-launcher<br />
<br />
After that, in your plugin directory generate a new karma.conf.js:<br />
<br />
jb@wiggum ~/code/appsuite/ox_pgp_mail (git)-[karma] % karma init<br />
<br />
Which testing framework do you want to use ?<br />
Press tab to list possible options. Enter to move to the next question.<br />
> mocha <br />
<br />
Do you want to use Require.js ?<br />
This will add Require.js plugin.<br />
Press tab to list possible options. Enter to move to the next question.<br />
> no <br />
<br />
Do you want to capture any browsers automatically ?<br />
Press tab to list possible options. Enter empty string to move to the next question.<br />
> <br />
<br />
What is the location of your source and test files ?<br />
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
jb@wiggum ~/code/appsuite/ox_pgp_mail (git)-[ding] % karma init<br />
<br />
Which testing framework do you want to use ?<br />
Press tab to list possible options. Enter to move to the next question.<br />
> mocha <br />
<br />
Do you want to use Require.js ?<br />
This will add Require.js plugin.<br />
Press tab to list possible options. Enter to move to the next question.<br />
> no <br />
<br />
Do you want to capture any browsers automatically ?<br />
Press tab to list possible options. Enter empty string to move to the next question.<br />
> PhantomJS <br />
> <br />
<br />
What is the location of your source and test files ?<br />
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Should any of the files included by the previous patterns be excluded ?<br />
You can use glob patterns, eg. "**/*.swp".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Do you want Karma to watch all the files and run the tests on change ?<br />
Press tab to list possible options.<br />
> no <br />
<br />
<br />
Config file generated at "/home/jb/code/appsuite/ox_pgp_mail/karma.conf.js".<br />
<br />
Edit the generated file and adjust the following configuration variables:<br />
<br />
<pre class="language-javascript"><br />
basePath: 'build/',<br />
frameworks: ['ox-ui', 'sinon', 'mocha', 'chai'],<br />
files: [<br />
'spec/test-main.js',<br />
{pattern: 'apps/**/*.js', included: false},<br />
{pattern: 'spec/**/*_spec.js', included: false}<br />
]<br />
</pre><br />
<br />
Generate a main loader script to start the test after App Suite Core UI<br />
has been booted. The file should be put in `spec/test-main.js`:<br />
<br />
<pre class="language-javascript"><br />
var allTestFiles = [];<br />
var TEST_REGEXP = /_spec\.js$/i;<br />
<br />
var pathToModule = function(path) {<br />
return path;<br />
// return path.replace(/^\/base\//, '').replace(/\.js$/, '');<br />
};<br />
<br />
Object.keys(window.__karma__.files).forEach(function(file) {<br />
if (TEST_REGEXP.test(file)) {<br />
// Normalize paths to RequireJS module names.<br />
allTestFiles.push(pathToModule(file));<br />
}<br />
});<br />
<br />
require(['io.ox/core/extPatterns/stage'], function (Stage) {<br />
<br />
'use strict';<br />
<br />
ox.testUtils.stubAppsuiteBody();<br />
<br />
new Stage('io.ox/core/stages', {<br />
id: 'run_tests',<br />
index: 99999,<br />
run: function (baton) {<br />
requirejs.config({<br />
// Karma serves files from '/base/apps'<br />
baseUrl: '/base/apps',<br />
<br />
// ask Require.js to load these files (all our tests)<br />
deps: allTestFiles,<br />
<br />
// start test run, once Require.js is done<br />
callback: window.__karma__.start<br />
});<br />
}<br />
});<br />
});<br />
</pre><br />
<br />
=== Running the tests ===<br />
<br />
There are multiple targets provided in<br />
[https://github.com/Open-Xchange-Frontend/shared-grunt-config shared grunt configuration].<br />
The recommended way is to run <tt>grunt dev</tt>. This will start a connect server, the karma<br />
test server and a watcher for changes. You can trigger a testrun manually by running<br />
<tt>grunt testrun</tt> in another terminal. This will be done automatically by the grunt watch<br />
task, after any source file of your project has been changed.<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]<br />
[[Category:Development process]]<br />
[[Category:Testing]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Testing_3rd-party_code&diff=18095AppSuite:Testing 3rd-party code2014-07-16T10:29:05Z<p>J.ohny.b: /* Setup */</p>
<hr />
<div>{{Stability-experimental}}<br />
<div class="title">Testing UI plugins</div><br />
<br />
__TOC__<br />
<br />
This article explains one way to add automatic testing to UI plugins. <br />
<br />
== Libraries ==<br />
<br />
* [http://visionmedia.github.io/mocha/ Mocha] - the fun, simple, flexible JS testing framework<br />
* [http://sinonjs.org/ Sinon.JS] - Standalone test spies, stubs and mocks for JavaScript.<br />
* [http://karma-runner.github.io/ Karma Runner] - test runner<br />
* [http://chaijs.com/ Chai.js] - Assertion framework<br />
<br />
== using karma-ox-ui ==<br />
<br />
The [[AppSuite:GettingStarted_7.6.0|shared grunt configuration]] ships with most of the parts pre-configured. Still, a little setup is needed to enable automatic testing for an external app. Since we use many standard libraries, this approach can be extended as you wish.<br />
<br />
=== Setup ===<br />
<br />
Make sure, karma executable is installed:<br />
<br />
npm install -g karma-cli<br />
<br />
Install a few more testing libraries locally:<br />
<br />
npm install --save-dev karma-mocha karma-chai karma-sinon karma-ox-ui karma-phantomjs-launcher<br />
<br />
After that, in your plugin directory generate a new karma.conf.js:<br />
<br />
jb@wiggum ~/code/appsuite/ox_pgp_mail (git)-[karma] % karma init<br />
<br />
Which testing framework do you want to use ?<br />
Press tab to list possible options. Enter to move to the next question.<br />
> mocha <br />
<br />
Do you want to use Require.js ?<br />
This will add Require.js plugin.<br />
Press tab to list possible options. Enter to move to the next question.<br />
> no <br />
<br />
Do you want to capture any browsers automatically ?<br />
Press tab to list possible options. Enter empty string to move to the next question.<br />
> <br />
<br />
What is the location of your source and test files ?<br />
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
jb@wiggum ~/code/appsuite/ox_pgp_mail (git)-[ding] % karma init<br />
<br />
Which testing framework do you want to use ?<br />
Press tab to list possible options. Enter to move to the next question.<br />
> mocha <br />
<br />
Do you want to use Require.js ?<br />
This will add Require.js plugin.<br />
Press tab to list possible options. Enter to move to the next question.<br />
> no <br />
<br />
Do you want to capture any browsers automatically ?<br />
Press tab to list possible options. Enter empty string to move to the next question.<br />
> PhantomJS <br />
> <br />
<br />
What is the location of your source and test files ?<br />
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Should any of the files included by the previous patterns be excluded ?<br />
You can use glob patterns, eg. "**/*.swp".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Do you want Karma to watch all the files and run the tests on change ?<br />
Press tab to list possible options.<br />
> no <br />
<br />
<br />
Config file generated at "/home/jb/code/appsuite/ox_pgp_mail/karma.conf.js".<br />
<br />
Edit the generated file and adjust the following configuration variables:<br />
<br />
<pre class="language-javascript"><br />
basePath: 'build/',<br />
frameworks: ['ox-ui', 'sinon', 'mocha', 'chai'],<br />
files: [<br />
'spec/test-main.js',<br />
{pattern: 'apps/**/*.js', included: false},<br />
{pattern: 'spec/**/*_spec.js', included: false}<br />
]<br />
</pre><br />
<br />
Generate a main loader script to start the test after App Suite Core UI<br />
has been booted. The file should be put in `spec/test-main.js`:<br />
<br />
<pre class="language-javascript"><br />
var allTestFiles = [];<br />
var TEST_REGEXP = /_spec\.js$/i;<br />
<br />
var pathToModule = function(path) {<br />
return path;<br />
// return path.replace(/^\/base\//, '').replace(/\.js$/, '');<br />
};<br />
<br />
Object.keys(window.__karma__.files).forEach(function(file) {<br />
if (TEST_REGEXP.test(file)) {<br />
// Normalize paths to RequireJS module names.<br />
allTestFiles.push(pathToModule(file));<br />
}<br />
});<br />
<br />
require(['io.ox/core/extPatterns/stage'], function (Stage) {<br />
<br />
'use strict';<br />
<br />
ox.testUtils.stubAppsuiteBody();<br />
<br />
new Stage('io.ox/core/stages', {<br />
id: 'run_tests',<br />
index: 99999,<br />
run: function (baton) {<br />
requirejs.config({<br />
// Karma serves files from '/base/apps'<br />
baseUrl: '/base/apps',<br />
<br />
// ask Require.js to load these files (all our tests)<br />
deps: allTestFiles,<br />
<br />
// start test run, once Require.js is done<br />
callback: window.__karma__.start<br />
});<br />
}<br />
});<br />
});<br />
</pre><br />
<br />
=== Running the tests ===<br />
<br />
There are multiple targets provided in<br />
[https://github.com/Open-Xchange-Frontend/shared-grunt-config shared grunt configuration].<br />
The recommended way is to run <tt>grunt dev</tt>. This will start a connect server, the karma<br />
test server and a watcher for changes. You can trigger a testrun manually by running<br />
<tt>grunt testrun</tt> in another terminal. This will be done automatically by the grunt watch<br />
task, after any source file of your project has been changed.<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]<br />
[[Category:Development process]]<br />
[[Category:Testing]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Testing_3rd-party_code&diff=18094AppSuite:Testing 3rd-party code2014-07-16T10:28:39Z<p>J.ohny.b: /* Running the tests */</p>
<hr />
<div>{{Stability-experimental}}<br />
<div class="title">Testing UI plugins</div><br />
<br />
__TOC__<br />
<br />
This article explains one way to add automatic testing to UI plugins. <br />
<br />
== Libraries ==<br />
<br />
* [http://visionmedia.github.io/mocha/ Mocha] - the fun, simple, flexible JS testing framework<br />
* [http://sinonjs.org/ Sinon.JS] - Standalone test spies, stubs and mocks for JavaScript.<br />
* [http://karma-runner.github.io/ Karma Runner] - test runner<br />
* [http://chaijs.com/ Chai.js] - Assertion framework<br />
<br />
== using karma-ox-ui ==<br />
<br />
The [[AppSuite:GettingStarted_7.6.0|shared grunt configuration]] ships with most of the parts pre-configured. Still, a little setup is needed to enable automatic testing for an external app. Since we use many standard libraries, this approach can be extended as you wish.<br />
<br />
=== Setup ===<br />
<br />
Make sure, karma executable is installed:<br />
<br />
npm install -g karma-cli<br />
<br />
Install a few more testing libraries locally:<br />
<br />
npm install --save-dev karma-mocha karma-chai karma-sinon karma-ox-ui karma-phantomjs-launcher<br />
<br />
After that, in your plugin directory generate a new karma.conf.js:<br />
<br />
jb@wiggum ~/code/appsuite/ox_pgp_mail (git)-[karma] % karma init<br />
<br />
Which testing framework do you want to use ?<br />
Press tab to list possible options. Enter to move to the next question.<br />
> mocha <br />
<br />
Do you want to use Require.js ?<br />
This will add Require.js plugin.<br />
Press tab to list possible options. Enter to move to the next question.<br />
> no <br />
<br />
Do you want to capture any browsers automatically ?<br />
Press tab to list possible options. Enter empty string to move to the next question.<br />
> <br />
<br />
What is the location of your source and test files ?<br />
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
jb@wiggum ~/code/appsuite/ox_pgp_mail (git)-[ding] % karma init<br />
<br />
Which testing framework do you want to use ?<br />
Press tab to list possible options. Enter to move to the next question.<br />
> mocha <br />
<br />
Do you want to use Require.js ?<br />
This will add Require.js plugin.<br />
Press tab to list possible options. Enter to move to the next question.<br />
> no <br />
<br />
Do you want to capture any browsers automatically ?<br />
Press tab to list possible options. Enter empty string to move to the next question.<br />
> PhantomJS <br />
> <br />
<br />
What is the location of your source and test files ?<br />
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Should any of the files included by the previous patterns be excluded ?<br />
You can use glob patterns, eg. "**/*.swp".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Do you want Karma to watch all the files and run the tests on change ?<br />
Press tab to list possible options.<br />
> no <br />
<br />
<br />
Config file generated at "/home/jb/code/appsuite/ox_pgp_mail/karma.conf.js".<br />
<br />
Edit the generated file and adjust the following configuration variables:<br />
<br />
<pre class="language-javascript"><br />
basePath: 'build/',<br />
frameworks: ['ox-ui', 'sinon', 'mocha', 'chai'],<br />
files: [<br />
'spec/test-main.js',<br />
{pattern: 'apps/**/*.js', included: false},<br />
{pattern: 'spec/**/*_spec.js', included: false}<br />
]<br />
</pre><br />
<br />
Generate a main loader script to start the test after App Suite Core UI<br />
has been booted. The file should be put in `spec/test-main.js`:<br />
<br />
<pre class="language-javascript"><br />
var allTestFiles = [];<br />
var TEST_REGEXP = /_spec\.js$/i;<br />
<br />
var pathToModule = function(path) {<br />
return path;<br />
// return path.replace(/^\/base\//, '').replace(/\.js$/, '');<br />
};<br />
<br />
Object.keys(window.__karma__.files).forEach(function(file) {<br />
if (TEST_REGEXP.test(file)) {<br />
// Normalize paths to RequireJS module names.<br />
allTestFiles.push(pathToModule(file));<br />
}<br />
});<br />
<br />
require(['io.ox/core/extPatterns/stage'], function (Stage) {<br />
<br />
'use strict';<br />
<br />
ox.testUtils.stubAppsuiteBody();<br />
<br />
new Stage('io.ox/core/stages', {<br />
id: 'run_tests',<br />
index: 99999,<br />
run: function (baton) {<br />
requirejs.config({<br />
// Karma serves files from '/base/apps'<br />
baseUrl: '/base/apps',<br />
<br />
// ask Require.js to load these files (all our tests)<br />
deps: allTestFiles,<br />
<br />
// start test run, once Require.js is done<br />
callback: window.__karma__.start<br />
});<br />
}<br />
});<br />
});<br />
</pre><br />
<br />
=== Running the tests ===<br />
<br />
There are multiple targets provided in<br />
[https://github.com/Open-Xchange-Frontend/shared-grunt-config shared grunt configuration].<br />
The recommended way is to run <tt>grunt dev</tt>. This will start a connect server, the karma<br />
test server and a watcher for changes. You can trigger a testrun manually by running<br />
<tt>grunt testrun</tt> in another terminal. This will be done automatically by the grunt watch<br />
task, after any source file of your project has been changed.<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]<br />
[[Category:Development process]]<br />
[[Category:Testing]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Testing_3rd-party_code&diff=18093AppSuite:Testing 3rd-party code2014-07-16T10:27:52Z<p>J.ohny.b: /* Running the tests */</p>
<hr />
<div>{{Stability-experimental}}<br />
<div class="title">Testing UI plugins</div><br />
<br />
__TOC__<br />
<br />
This article explains one way to add automatic testing to UI plugins. <br />
<br />
== Libraries ==<br />
<br />
* [http://visionmedia.github.io/mocha/ Mocha] - the fun, simple, flexible JS testing framework<br />
* [http://sinonjs.org/ Sinon.JS] - Standalone test spies, stubs and mocks for JavaScript.<br />
* [http://karma-runner.github.io/ Karma Runner] - test runner<br />
* [http://chaijs.com/ Chai.js] - Assertion framework<br />
<br />
== using karma-ox-ui ==<br />
<br />
The [[AppSuite:GettingStarted_7.6.0|shared grunt configuration]] ships with most of the parts pre-configured. Still, a little setup is needed to enable automatic testing for an external app. Since we use many standard libraries, this approach can be extended as you wish.<br />
<br />
=== Setup ===<br />
<br />
Make sure, karma executable is installed:<br />
<br />
npm install -g karma-cli<br />
<br />
Install a few more testing libraries locally:<br />
<br />
npm install --save-dev karma-mocha karma-chai karma-sinon karma-ox-ui karma-phantomjs-launcher<br />
<br />
After that, in your plugin directory generate a new karma.conf.js:<br />
<br />
jb@wiggum ~/code/appsuite/ox_pgp_mail (git)-[karma] % karma init<br />
<br />
Which testing framework do you want to use ?<br />
Press tab to list possible options. Enter to move to the next question.<br />
> mocha <br />
<br />
Do you want to use Require.js ?<br />
This will add Require.js plugin.<br />
Press tab to list possible options. Enter to move to the next question.<br />
> no <br />
<br />
Do you want to capture any browsers automatically ?<br />
Press tab to list possible options. Enter empty string to move to the next question.<br />
> <br />
<br />
What is the location of your source and test files ?<br />
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
jb@wiggum ~/code/appsuite/ox_pgp_mail (git)-[ding] % karma init<br />
<br />
Which testing framework do you want to use ?<br />
Press tab to list possible options. Enter to move to the next question.<br />
> mocha <br />
<br />
Do you want to use Require.js ?<br />
This will add Require.js plugin.<br />
Press tab to list possible options. Enter to move to the next question.<br />
> no <br />
<br />
Do you want to capture any browsers automatically ?<br />
Press tab to list possible options. Enter empty string to move to the next question.<br />
> PhantomJS <br />
> <br />
<br />
What is the location of your source and test files ?<br />
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Should any of the files included by the previous patterns be excluded ?<br />
You can use glob patterns, eg. "**/*.swp".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Do you want Karma to watch all the files and run the tests on change ?<br />
Press tab to list possible options.<br />
> no <br />
<br />
<br />
Config file generated at "/home/jb/code/appsuite/ox_pgp_mail/karma.conf.js".<br />
<br />
Edit the generated file and adjust the following configuration variables:<br />
<br />
<pre class="language-javascript"><br />
basePath: 'build/',<br />
frameworks: ['ox-ui', 'sinon', 'mocha', 'chai'],<br />
files: [<br />
'spec/test-main.js',<br />
{pattern: 'apps/**/*.js', included: false},<br />
{pattern: 'spec/**/*_spec.js', included: false}<br />
]<br />
</pre><br />
<br />
Generate a main loader script to start the test after App Suite Core UI<br />
has been booted. The file should be put in `spec/test-main.js`:<br />
<br />
<pre class="language-javascript"><br />
var allTestFiles = [];<br />
var TEST_REGEXP = /_spec\.js$/i;<br />
<br />
var pathToModule = function(path) {<br />
return path;<br />
// return path.replace(/^\/base\//, '').replace(/\.js$/, '');<br />
};<br />
<br />
Object.keys(window.__karma__.files).forEach(function(file) {<br />
if (TEST_REGEXP.test(file)) {<br />
// Normalize paths to RequireJS module names.<br />
allTestFiles.push(pathToModule(file));<br />
}<br />
});<br />
<br />
require(['io.ox/core/extPatterns/stage'], function (Stage) {<br />
<br />
'use strict';<br />
<br />
ox.testUtils.stubAppsuiteBody();<br />
<br />
new Stage('io.ox/core/stages', {<br />
id: 'run_tests',<br />
index: 99999,<br />
run: function (baton) {<br />
requirejs.config({<br />
// Karma serves files from '/base/apps'<br />
baseUrl: '/base/apps',<br />
<br />
// ask Require.js to load these files (all our tests)<br />
deps: allTestFiles,<br />
<br />
// start test run, once Require.js is done<br />
callback: window.__karma__.start<br />
});<br />
}<br />
});<br />
});<br />
</pre><br />
<br />
=== Running the tests ===<br />
<br />
There are multiple targets provided in<br />
[shared-grunt-config](https://github.com/Open-Xchange-Frontend/shared-grunt-config).<br />
The recommended way is to run <tt>grunt dev</tt>. This will start a connect server, the karma<br />
test server and a watcher for changes. You can trigger a testrun manually by running<br />
<tt>grunt testrun</tt> in another terminal. This will be done automatically by the grunt watch<br />
task, after any source file of your project has been changed.<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]<br />
[[Category:Development process]]<br />
[[Category:Testing]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Testing_3rd-party_code&diff=18092AppSuite:Testing 3rd-party code2014-07-16T10:23:45Z<p>J.ohny.b: Created page with "{{Stability-experimental}} <div class="title">Testing UI plugins</div> __TOC__ This article explains one way to add automatic testing to UI plugins. == Libraries == * [ht..."</p>
<hr />
<div>{{Stability-experimental}}<br />
<div class="title">Testing UI plugins</div><br />
<br />
__TOC__<br />
<br />
This article explains one way to add automatic testing to UI plugins. <br />
<br />
== Libraries ==<br />
<br />
* [http://visionmedia.github.io/mocha/ Mocha] - the fun, simple, flexible JS testing framework<br />
* [http://sinonjs.org/ Sinon.JS] - Standalone test spies, stubs and mocks for JavaScript.<br />
* [http://karma-runner.github.io/ Karma Runner] - test runner<br />
* [http://chaijs.com/ Chai.js] - Assertion framework<br />
<br />
== using karma-ox-ui ==<br />
<br />
The [[AppSuite:GettingStarted_7.6.0|shared grunt configuration]] ships with most of the parts pre-configured. Still, a little setup is needed to enable automatic testing for an external app. Since we use many standard libraries, this approach can be extended as you wish.<br />
<br />
=== Setup ===<br />
<br />
Make sure, karma executable is installed:<br />
<br />
npm install -g karma-cli<br />
<br />
Install a few more testing libraries locally:<br />
<br />
npm install --save-dev karma-mocha karma-chai karma-sinon karma-ox-ui karma-phantomjs-launcher<br />
<br />
After that, in your plugin directory generate a new karma.conf.js:<br />
<br />
jb@wiggum ~/code/appsuite/ox_pgp_mail (git)-[karma] % karma init<br />
<br />
Which testing framework do you want to use ?<br />
Press tab to list possible options. Enter to move to the next question.<br />
> mocha <br />
<br />
Do you want to use Require.js ?<br />
This will add Require.js plugin.<br />
Press tab to list possible options. Enter to move to the next question.<br />
> no <br />
<br />
Do you want to capture any browsers automatically ?<br />
Press tab to list possible options. Enter empty string to move to the next question.<br />
> <br />
<br />
What is the location of your source and test files ?<br />
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
jb@wiggum ~/code/appsuite/ox_pgp_mail (git)-[ding] % karma init<br />
<br />
Which testing framework do you want to use ?<br />
Press tab to list possible options. Enter to move to the next question.<br />
> mocha <br />
<br />
Do you want to use Require.js ?<br />
This will add Require.js plugin.<br />
Press tab to list possible options. Enter to move to the next question.<br />
> no <br />
<br />
Do you want to capture any browsers automatically ?<br />
Press tab to list possible options. Enter empty string to move to the next question.<br />
> PhantomJS <br />
> <br />
<br />
What is the location of your source and test files ?<br />
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Should any of the files included by the previous patterns be excluded ?<br />
You can use glob patterns, eg. "**/*.swp".<br />
Enter empty string to move to the next question.<br />
> <br />
<br />
Do you want Karma to watch all the files and run the tests on change ?<br />
Press tab to list possible options.<br />
> no <br />
<br />
<br />
Config file generated at "/home/jb/code/appsuite/ox_pgp_mail/karma.conf.js".<br />
<br />
Edit the generated file and adjust the following configuration variables:<br />
<br />
<pre class="language-javascript"><br />
basePath: 'build/',<br />
frameworks: ['ox-ui', 'sinon', 'mocha', 'chai'],<br />
files: [<br />
'spec/test-main.js',<br />
{pattern: 'apps/**/*.js', included: false},<br />
{pattern: 'spec/**/*_spec.js', included: false}<br />
]<br />
</pre><br />
<br />
Generate a main loader script to start the test after App Suite Core UI<br />
has been booted. The file should be put in `spec/test-main.js`:<br />
<br />
<pre class="language-javascript"><br />
var allTestFiles = [];<br />
var TEST_REGEXP = /_spec\.js$/i;<br />
<br />
var pathToModule = function(path) {<br />
return path;<br />
// return path.replace(/^\/base\//, '').replace(/\.js$/, '');<br />
};<br />
<br />
Object.keys(window.__karma__.files).forEach(function(file) {<br />
if (TEST_REGEXP.test(file)) {<br />
// Normalize paths to RequireJS module names.<br />
allTestFiles.push(pathToModule(file));<br />
}<br />
});<br />
<br />
require(['io.ox/core/extPatterns/stage'], function (Stage) {<br />
<br />
'use strict';<br />
<br />
ox.testUtils.stubAppsuiteBody();<br />
<br />
new Stage('io.ox/core/stages', {<br />
id: 'run_tests',<br />
index: 99999,<br />
run: function (baton) {<br />
requirejs.config({<br />
// Karma serves files from '/base/apps'<br />
baseUrl: '/base/apps',<br />
<br />
// ask Require.js to load these files (all our tests)<br />
deps: allTestFiles,<br />
<br />
// start test run, once Require.js is done<br />
callback: window.__karma__.start<br />
});<br />
}<br />
});<br />
});<br />
</pre><br />
<br />
=== Running the tests ===<br />
<br />
There are multiple targets provided in<br />
[shared-grunt-config](https://github.com/Open-Xchange-Frontend/shared-grunt-config).<br />
The recommended way is to run <tt>grunt dev<tt>. This will start a connect server, the karma<br />
test server and a watcher for changes. You can trigger a testrun manually by running<br />
<tt>grunt testrun</tt> in another terminal. This will be done automatically by the grunt watch<br />
task, after any source file of your project has been changed.<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]<br />
[[Category:Development process]]<br />
[[Category:Testing]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GruntFAQ&diff=17936AppSuite:GruntFAQ2014-07-01T09:07:25Z<p>J.ohny.b: /* Using development proxy server */</p>
<hr />
<div><div class="title">FAQ regarding Grunt configuration for App Suite UI plugins</div><br />
<br />
= Building UI plugins =<br />
<br />
== Some of my files aren't copied. How can I extend a copy sub-task? ==<br />
<br />
Especially when using external libraries managed with Bower or npm, sometimes the [https://github.com/Open-Xchange-Frontend/shared-grunt-config shared grunt configuration] doesn't contain all cases for files to be copied during the build or dist tasks. Due to the [https://johnyb.github.io/2014/05/10/extending_shared_grunt_configuration.html extensibility] of our shared grunt configuration, it's quite easy to add those missing files. You can hook up into the <tt>build_*</tt> or the <tt>dist_*</tt> copy task and add your custom configuration like this:<br />
<br />
<syntaxhighlight lang="JavaScript"><br />
'use strict';<br />
<br />
module.exports = function (grunt) {<br />
grunt.config.extend('copy', {<br />
build_custom: {<br />
files: [{<br />
expand: true,<br />
src: ['apps/**/*.in', 'apps/**/manifest.json'],<br />
dest: 'build/'<br />
}]<br />
}<br />
});<br />
};<br />
</syntaxhighlight><br />
<br />
Put this in a file inside the <tt>grunt/tasks</tt> directory and you are done. From now on, all files ending with .in and all manifest.json files are put into the <tt>build/</tt> directory using the same structure as in the <tt>apps/</tt> directory. For more detailed information see [http://gruntjs.com/configuring-tasks#files the grunt documentation on files].<br />
<br />
= Using development proxy server =<br />
<br />
== I get an error message 'Port 8337 already in use by another process', what can I do? ==<br />
<br />
Only one instance of appserver is allowed at a time (this might be subject to change, though). You have multiple options to start developing on that plugin. Number one:<br />
<br />
# Close all other running instances of appserver and run it exclusively in one project<br />
<br />
Yes, that was the easy one. However, you might want to serve multiple projects at once. This case would need a little more configuration:<br />
<br />
# Choose one base UI project<br />
# Edit <tt>grunt/local.conf.json</tt><br />
# add all build/ directories you want to serve to "prefixes" array<br />
# Run <tt>grunt connect watch</tt> in the base directory<br />
# In all other UI projects you want to develop, only run <tt>grunt watch</tt> without the connect task<br />
<br />
Appserver will in this case do all the cache busting for you (it uses the timestamp of the "newest" directory in the prefix list) and if you didn't de-activate live-reload, it will work for all projects (the watch task sends livereload events to appserver, which will trigger the reload in the browser).</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GruntFAQ&diff=17934AppSuite:GruntFAQ2014-07-01T08:59:59Z<p>J.ohny.b: /* I get an error message 'Port 8337 already in use by another process' */</p>
<hr />
<div><div class="title">FAQ regarding Grunt configuration for App Suite UI plugins</div><br />
<br />
= Building UI plugins =<br />
<br />
== Some of my files aren't copied. How can I extend a copy sub-task? ==<br />
<br />
Especially when using external libraries managed with Bower or npm, sometimes the [https://github.com/Open-Xchange-Frontend/shared-grunt-config shared grunt configuration] doesn't contain all cases for files to be copied during the build or dist tasks. Due to the [https://johnyb.github.io/2014/05/10/extending_shared_grunt_configuration.html extensibility] of our shared grunt configuration, it's quite easy to add those missing files. You can hook up into the <tt>build_*</tt> or the <tt>dist_*</tt> copy task and add your custom configuration like this:<br />
<br />
<syntaxhighlight lang="JavaScript"><br />
'use strict';<br />
<br />
module.exports = function (grunt) {<br />
grunt.config.extend('copy', {<br />
build_custom: {<br />
files: [{<br />
expand: true,<br />
src: ['apps/**/*.in', 'apps/**/manifest.json'],<br />
dest: 'build/'<br />
}]<br />
}<br />
});<br />
};<br />
</syntaxhighlight><br />
<br />
Put this in a file inside the <tt>grunt/tasks</tt> directory and you are done. From now on, all files ending with .in and all manifest.json files are put into the <tt>build/</tt> directory using the same structure as in the <tt>apps/</tt> directory. For more detailed information see [http://gruntjs.com/configuring-tasks#files the grunt documentation on files].<br />
<br />
= Using development proxy server =<br />
<br />
== I get an error message 'Port 8337 already in use by another process' ==<br />
<br />
Only one instance of appserver is allowed at a time (this might be subject to change, though). You have multiple options to start developing on that plugin. Number one:<br />
<br />
# Close all other running instances of appserver and run it exclusively in one project<br />
<br />
Yes, that was the easy one. However, you might want to serve multiple projects at once. This case would need a little more configuration:<br />
<br />
# Choose one base UI project<br />
# Edit <tt>grunt/local.conf.json</tt><br />
# add all build/ directories you want to serve to "prefixes" array<br />
# Run <tt>grunt connect watch</tt> in the base directory<br />
# In all other UI projects you want to develop, only run <tt>grunt watch</tt> without the connect task<br />
<br />
Appserver will in this case do all the cache busting for you (it uses the timestamp of the "newest" directory in the prefix list) and if you didn't de-activate live-reload, it will work for all projects (the watch task sends livereload events to appserver, which will trigger the reload in the browser).</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GruntFAQ&diff=17932AppSuite:GruntFAQ2014-07-01T08:58:09Z<p>J.ohny.b: </p>
<hr />
<div><div class="title">FAQ regarding Grunt configuration for App Suite UI plugins</div><br />
<br />
= Building UI plugins =<br />
<br />
== Some of my files aren't copied. How can I extend a copy sub-task? ==<br />
<br />
Especially when using external libraries managed with Bower or npm, sometimes the [https://github.com/Open-Xchange-Frontend/shared-grunt-config shared grunt configuration] doesn't contain all cases for files to be copied during the build or dist tasks. Due to the [https://johnyb.github.io/2014/05/10/extending_shared_grunt_configuration.html extensibility] of our shared grunt configuration, it's quite easy to add those missing files. You can hook up into the <tt>build_*</tt> or the <tt>dist_*</tt> copy task and add your custom configuration like this:<br />
<br />
<syntaxhighlight lang="JavaScript"><br />
'use strict';<br />
<br />
module.exports = function (grunt) {<br />
grunt.config.extend('copy', {<br />
build_custom: {<br />
files: [{<br />
expand: true,<br />
src: ['apps/**/*.in', 'apps/**/manifest.json'],<br />
dest: 'build/'<br />
}]<br />
}<br />
});<br />
};<br />
</syntaxhighlight><br />
<br />
Put this in a file inside the <tt>grunt/tasks</tt> directory and you are done. From now on, all files ending with .in and all manifest.json files are put into the <tt>build/</tt> directory using the same structure as in the <tt>apps/</tt> directory. For more detailed information see [http://gruntjs.com/configuring-tasks#files the grunt documentation on files].<br />
<br />
= Using development proxy server =<br />
<br />
== I get an error message 'Port 8337 already in use by another process' ==<br />
<br />
Only one instance of appserver is allowed at a time (this might be subject to change, though). You have multiple options to start developing on that plugin. Number one:<br />
<br />
1. Close all other running instances of appserver and run it exclusively in one project<br />
<br />
Yes, that was the easy one. However, you might want to serve multiple projects at once. This case would need a little more configuration:<br />
<br />
1. Choose one base UI project<br />
2. Edit <tt>grunt/local.conf.json</tt><br />
3. add all build/ directories you want to serve to "prefixes" array<br />
4. Run <tt>grunt connect watch</tt> in the base directory<br />
5. In all other UI projects you want to develop, only run <tt>grunt watch</tt> without the connect task<br />
<br />
Appserver will in this case do all the cache busting for you (it uses the timestamp of the "newest" directory in the prefix list) and if you didn't de-activate live-reload, it will work for all projects (the watch task sends livereload events to appserver, which will trigger the reload in the browser).</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:UI_FAQ&diff=17928AppSuite:UI FAQ2014-07-01T08:28:17Z<p>J.ohny.b: </p>
<hr />
<div>'''Synopsis:''' Frequently asked questions about running the App Suite UI. If you need a tool set for finding things out about the UI, see [[AppSuite:Debugging the UI]].<br />
<br />
= Getting the UI started =<br />
Frequently asked by new developers. And forgetful veterans. Who therefore write documentation pieces like this.<br />
<br />
== Does show the login screen after logging in ==<br />
This means you managed to find a bug even before our error message module could be loaded. There is something rather basic broken, maybe the DB connection or some of the standard .js<br />
<br />
The only hint we can give from a UI perspective is to use the developer console on your browser and, if applicable, turn on "halt on all errors" or "preserve logging data". You might see an error message that otherwise gets eaten by a redirect.<br />
<br />
If you are running the backend yourself, you are in luck, go, look at the server error log.<br />
<br />
== Does not load, fails with error: Could not read... ==<br />
"Could not read 'io.ox/core/http.js'" <br />
"Could not read 'io.ox/core/events.js'" <br />
That's not all, the system is probably hiding even more errors. It is more probable that the whole UI cannot be found. Check the com.openexchange.apps.path, usually hidden in manifests.properties. Does it point to where your webserver serves the files from? The default is /var/www/appsuite, on OSX is is more likely to be /Library/WebServer/Documents/appsuite<br />
<br />
== App Suite loads, but no apps show up ==<br />
So bother backend and frontend are devoid of errors, the UI loads nicely, you see the top bar with settings, notifications and refresh button but no apps at all, right? This is a manifest problem. Check the capabilities (see hint in the beginning of this page). If even those are correct, check the backend's /tmp folder and the manifest.properties there: Are the paths of <tt>com.openexchange.apps.path</tt> and <tt>com.openexchange.apps.manifestPath</tt> pointing to your build directory?<br />
<br />
== Could not read 'io.ox/office/preview/app/... when loading anything but the portal ==<br />
You lack preview capabilities. Since you can choose what to use, here are your options<br />
* use the URL parameter <tt>disableFeature=document_preview%2Ctext</tt><br />
* figure out how to install office<br />
* use the existing preview bundles<br />
<br />
== After logging in I'm dumped back to the login page with a #autologin=false added to the URL ==<br />
<br />
You might not see an error message in the console then. Configure your JS console to preserve messages on navigation (so after a redirect), and you might get a hint as to what's wrong.<br />
<br />
= UI build environment =<br />
<br />
There is an explicit [[AppSuite:GruntFAQ|FAQ page]] for grunt related questions when working with external modules.<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]<br />
[[Category:Developer]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:UI_FAQ&diff=17927AppSuite:UI FAQ2014-07-01T08:27:41Z<p>J.ohny.b: </p>
<hr />
<div>'''Synopsis:''' Frequently asked questions about running the App Suite UI. If you need a tool set for finding things out about the UI, see [[AppSuite:Debugging the UI]].<br />
<br />
= Getting the UI started =<br />
Frequently asked by new developers. And forgetful veterans. Who therefore write documentation pieces like this.<br />
<br />
== Does show the login screen after logging in ==<br />
This means you managed to find a bug even before our error message module could be loaded. There is something rather basic broken, maybe the DB connection or some of the standard .js<br />
<br />
The only hint we can give from a UI perspective is to use the developer console on your browser and, if applicable, turn on "halt on all errors" or "preserve logging data". You might see an error message that otherwise gets eaten by a redirect.<br />
<br />
If you are running the backend yourself, you are in luck, go, look at the server error log.<br />
<br />
== Does not load, fails with error: Could not read... ==<br />
"Could not read 'io.ox/core/http.js'" <br />
"Could not read 'io.ox/core/events.js'" <br />
That's not all, the system is probably hiding even more errors. It is more probable that the whole UI cannot be found. Check the com.openexchange.apps.path, usually hidden in manifests.properties. Does it point to where your webserver serves the files from? The default is /var/www/appsuite, on OSX is is more likely to be /Library/WebServer/Documents/appsuite<br />
<br />
== App Suite loads, but no apps show up ==<br />
So bother backend and frontend are devoid of errors, the UI loads nicely, you see the top bar with settings, notifications and refresh button but no apps at all, right? This is a manifest problem. Check the capabilities (see hint in the beginning of this page). If even those are correct, check the backend's /tmp folder and the manifest.properties there: Are the paths of <tt>com.openexchange.apps.path</tt> and <tt>com.openexchange.apps.manifestPath</tt> pointing to your build directory?<br />
<br />
== Could not read 'io.ox/office/preview/app/... when loading anything but the portal ==<br />
You lack preview capabilities. Since you can choose what to use, here are your options<br />
* use the URL parameter <tt>disableFeature=document_preview%2Ctext</tt><br />
* figure out how to install office<br />
* use the existing preview bundles<br />
<br />
== After logging in I'm dumped back to the login page with a #autologin=false added to the URL ==<br />
<br />
You might not see an error message in the console then. Configure your JS console to preserve messages on navigation (so after a redirect), and you might get a hint as to what's wrong.<br />
<br />
= UI build environment<br />
<br />
There is an explicit [[AppSuite:GruntFAQ|FAQ page]] for grunt related questions when working with external modules.<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]<br />
[[Category:Developer]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GruntFAQ&diff=17812AppSuite:GruntFAQ2014-06-16T12:56:39Z<p>J.ohny.b: </p>
<hr />
<div><div class="title">FAQ regarding Grunt configuration for App Suite UI plugins</div><br />
<br />
== Some of my files aren't copied. How can I extend a copy sub-task? ==<br />
<br />
Especially when using external libraries managed with Bower or npm, sometimes the [https://github.com/Open-Xchange-Frontend/shared-grunt-config shared grunt configuration] doesn't contain all cases for files to be copied during the build or dist tasks. Due to the [https://johnyb.github.io/2014/05/10/extending_shared_grunt_configuration.html extensibility] of our shared grunt configuration, it's quite easy to add those missing files. You can hook up into the <tt>build_*</tt> or the <tt>dist_*</tt> copy task and add your custom configuration like this:<br />
<br />
<syntaxhighlight lang="JavaScript"><br />
'use strict';<br />
<br />
module.exports = function (grunt) {<br />
grunt.config.extend('copy', {<br />
build_custom: {<br />
files: [{<br />
expand: true,<br />
src: ['apps/**/*.in', 'apps/**/manifest.json'],<br />
dest: 'build/'<br />
}]<br />
}<br />
});<br />
};<br />
</syntaxhighlight><br />
<br />
Put this in a file inside the <tt>grunt/tasks</tt> directory and you are done. From now on, all files ending with .in and all manifest.json files are put into the <tt>build/</tt> directory using the same structure as in the <tt>apps/</tt> directory. For more detailed information see [http://gruntjs.com/configuring-tasks#files the grunt documentation on files].</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GruntFAQ&diff=17810AppSuite:GruntFAQ2014-06-16T11:21:06Z<p>J.ohny.b: </p>
<hr />
<div><div class="title">FAQ regarding Grunt</div><br />
<br />
== Some of my files aren't copied. How can I extend a copy sub-task? ==<br />
<br />
Especially when using external libraries managed with Bower or npm, sometimes the [https://github.com/Open-Xchange-Frontend/shared-grunt-config shared grunt configuration] doesn't contain all cases for files to be copied during the build or dist tasks. Due to the [https://johnyb.github.io/2014/05/10/extending_shared_grunt_configuration.html extensibility] of our shared grunt configuration, it's quite easy to add those missing files. You can hook up into the <tt>build_*</tt> or the <tt>dist_*</tt> copy task and add your custom configuration like this:<br />
<br />
<syntaxhighlight lang="JavaScript"><br />
'use strict';<br />
<br />
module.exports = function (grunt) {<br />
grunt.config.extend('copy', {<br />
build_custom: {<br />
files: [{<br />
expand: true,<br />
src: ['apps/**/*.in', 'apps/**/manifest.json'],<br />
dest: 'build/'<br />
}]<br />
}<br />
});<br />
};<br />
</syntaxhighlight><br />
<br />
Put this in a file inside the <tt>grunt/tasks</tt> directory and you are done. From now on, all files ending with .in and all manifest.json files are put into the <tt>build/</tt> directory using the same structure as in the <tt>apps/</tt> directory. For more detailed information see [http://gruntjs.com/configuring-tasks#files the grunt documentation on files].</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GruntFAQ&diff=17809AppSuite:GruntFAQ2014-06-16T11:18:47Z<p>J.ohny.b: </p>
<hr />
<div><div class="title">FAQ regarding Grunt</div><br />
<br />
== Some of my files aren't copied. How can I extend a copy sub-task? ==<br />
<br />
Especially when using external libraries managed with Bower or npm, sometimes the shared grunt configuration doesn't contain all cases for files to be copied during the build or dist tasks. Due to the [https://johnyb.github.io/2014/05/10/extending_shared_grunt_configuration.html extensibility] of our shared grunt configuration, it's quite easy to add those missing files. You can hook up into the <tt>build_*</tt> or the <tt>dist_*</tt> copy task and add your custom configuration like this:<br />
<br />
<syntaxhighlight lang="JavaScript"><br />
'use strict';<br />
<br />
module.exports = function (grunt) {<br />
grunt.config.extend('copy', {<br />
build_custom: {<br />
files: [{<br />
expand: true,<br />
src: ['apps/**/*.in', 'apps/**/manifest.json'],<br />
dest: 'build/'<br />
}]<br />
}<br />
});<br />
};<br />
</syntaxhighlight><br />
<br />
Put this in a file inside the <tt>grunt/tasks</tt> directory and you are done. From now on, all files ending with .in and all manifest.json files are put into the <tt>build/</tt> directory using the same structure as in the <tt>apps/</tt> directory. For more detailed information see [http://gruntjs.com/configuring-tasks#files the grunt documentation on files].</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GruntFAQ&diff=17808AppSuite:GruntFAQ2014-06-16T11:18:29Z<p>J.ohny.b: </p>
<hr />
<div><div class="title">FAQ regarding Grunt</div><br />
<br />
== Some of my files aren't copied. How can I extend a copy sub-task? ==<br />
<br />
Especially when using external libraries managed with Bower or npm, sometimes the shared grunt configuration doesn't contain all cases for files to be copied during the build or dist tasks. Due to the [https://johnyb.github.io/2014/05/10/extending_shared_grunt_configuration.html extensibility] of our shared grunt configuration, it's quite easy to add those missing files. You can hook up into the <tt>build_*</tt> or the <tt>dist_*</tt> copy task and add your custom configuration like this:<br />
<br />
<syntaxhighlight lang="JavaScript"><br />
'use strict';<br />
<br />
module.exports = function (grunt) {<br />
grunt.config.extend('copy', {<br />
build_custom: {<br />
files: [{<br />
expand: true,<br />
src: ['apps/**/*.in', 'apps/**/manifest.json'],<br />
dest: 'build/'<br />
}]<br />
}<br />
});<br />
};<br />
</syntaxhighlight><br />
<br />
Put this in a file inside the <tt>grunt/tasks</tt> directory and you are done. From now on, all files ending with .in and all manifest.json files are put into the <tt>build/</tt> directory using the same structure as in the <tt>apps/</tt> directory. For more detailed information see [http://gruntjs.com/configuring-tasks#files the grunt documentation on files]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GruntFAQ&diff=17807AppSuite:GruntFAQ2014-06-16T11:18:00Z<p>J.ohny.b: </p>
<hr />
<div><div class="title">FAQ regarding Grunt</div><br />
<br />
=== Some of my files aren't copied. How can I extend a copy sub-task? ===<br />
<br />
Especially when using external libraries managed with Bower or npm, sometimes the shared grunt configuration doesn't contain all cases for files to be copied during the build or dist tasks. Due to the [https://johnyb.github.io/2014/05/10/extending_shared_grunt_configuration.html extensibility] of our shared grunt configuration, it's quite easy to add those missing files. You can hook up into the <tt>build_*</tt> or the <tt>dist_*</tt> copy task and add your custom configuration like this:<br />
<br />
<syntaxhighlight lang="JavaScript"><br />
'use strict';<br />
<br />
module.exports = function (grunt) {<br />
grunt.config.extend('copy', {<br />
build_custom: {<br />
files: [{<br />
expand: true,<br />
src: ['apps/**/*.in', 'apps/**/manifest.json'],<br />
dest: 'build/'<br />
}]<br />
}<br />
});<br />
};<br />
</syntaxhighlight><br />
<br />
Put this in a file inside the <tt>grunt/tasks</tt> directory and you are done. From now on, all files ending with .in and all manifest.json files are put into the <tt>build/</tt> directory using the same structure as in the <tt>apps/</tt> directory. For more detailed information see [http://gruntjs.com/configuring-tasks#files the grunt documentation on files]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GruntFAQ&diff=17806AppSuite:GruntFAQ2014-06-16T11:16:47Z<p>J.ohny.b: Created page with "= FAQ regarding Grunt = === Some of my files aren't copied. How can I extend a copy sub-task? === Especially when using external libraries managed with Bower or npm, sometim..."</p>
<hr />
<div>= FAQ regarding Grunt =<br />
<br />
=== Some of my files aren't copied. How can I extend a copy sub-task? ===<br />
<br />
Especially when using external libraries managed with Bower or npm, sometimes the shared grunt configuration doesn't contain all cases for files to be copied during the build or dist tasks. Due to the [https://johnyb.github.io/2014/05/10/extending_shared_grunt_configuration.html extensibility] of our shared grunt configuration, it's quite easy to add those missing files. You can hook up into the <tt>build_*</tt> or the <tt>dist_*</tt> copy task and add your custom configuration like this:<br />
<br />
<syntaxhighlight lang="JavaScript"><br />
'use strict';<br />
<br />
module.exports = function (grunt) {<br />
grunt.config.extend('copy', {<br />
build_custom: {<br />
files: [{<br />
expand: true,<br />
src: ['apps/**/*.in', 'apps/**/manifest.json'],<br />
dest: 'build/'<br />
}]<br />
}<br />
});<br />
};<br />
</syntaxhighlight><br />
<br />
Put this in a file inside the <tt>grunt/tasks</tt> directory and you are done. From now on, all files ending with .in and all manifest.json files are put into the <tt>build/</tt> directory using the same structure as in the <tt>apps/</tt> directory. For more detailed information see [http://gruntjs.com/configuring-tasks#files the grunt documentation on files]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:I18n&diff=17795AppSuite:I18n2014-06-12T07:46:30Z<p>J.ohny.b: /* Translation */</p>
<hr />
<div>{{Stability-experimental}}<br />
<br />
<div class="title">Internationalization (i18n)</div><br />
<br />
__TOC__<br />
<br />
Providing software for users in the whole world means providing it in multiple languages. This consists of two steps:<br />
<br />
* ''Internationalization (i18n)'' - Making the software international, i. e. preparing it to be adapted to different languages and locations.<br />
* ''Localization (L10n)'' - Adapting the software to a particular language and/or location.<br />
The Open-Xchange platform offers several facilities to simplify both parts. The L10n part is mostly a concern for translators. Open-Xchange facilities for that consist mainly of using a well-established format for translations: [http://www.gnu.org/s/gettext/ GNU Gettext] Portable Object (PO) files. This allows translators to use existing dedicated translation tools or a simple UTF-8-capable text editor.<br />
<br />
The i18n part is what software developers will be mostly dealing with and is what the rest of this document describes.<br />
<br />
==Translation==<br />
<br />
The main part of i18n is the translation of various text strings which are presented to the user. For this purpose, the Open-Xchange platform provides the RequireJS plugin <tt>'gettext'</tt>. Individual translation files are specified as a module dependency of the form <tt>'gettext!module_name'</tt>. The resulting module API is a function which can be called to translate a string using the specified translation files:<br />
<br />
<pre class="language-javascript"><br />
define('com.example/example', ['gettext!com.example/example'],<br />
function (gt) {<br />
'use strict';<br />
alert(gt('Hello, world!'));<br />
});<br />
</pre><br />
<br />
A file named ox.pot is created by the <tt>ox.pot</tt> (< 7.6.0) or <tt>create_pot</tt> (>= 7.6.0) tasks of the build system. So for versions < 7.6.0 you will need to run:<br />
<br />
$ build-appsuite ox.pot<br />
<br />
to create the file in the apps root directory.<br />
<br />
Working with a version starting from 7.6.0, the command:<br />
<br />
$ grunt create_pot<br />
<br />
will generate this file within the <tt>i18n/</tt> directory. It will contain an entry for every call to one of <tt>gettext</tt> functions:<br />
<br />
<pre class="language-gettext"><br />
#: apps/com.example/example.js:4<br />
msgid "Hello, world!"<br />
msgstr ""<br />
</pre><br />
<br />
This file can be sent to the translators and during the translation process, one PO file for each language will be created. The PO files in the directory <tt>i18n</tt> shoud contain the translated entry:<br />
<br />
<pre class="language-gettext"><br />
#: apps/com.example/example.js:4<br />
msgid "Hello, world!"<br />
msgstr "Hallo, Welt!"<br />
</pre><br />
<br />
During the next build, the entries are copied from the central PO files into individual translation files. In our example, this would be <tt>apps/com.example/example.de_DE.js</tt>. Because of the added language ID, translation files can usually have the same name as the corresponding main module. Multiple related modules should use the same translation file to avoid the overhead of too many small translation files.<br />
<br />
Most modules will require more complex translations than can be provided by a simple string lookup. To handle some of these cases, the <tt>gettext</tt> module provides traditional methods in addition to being a callable function. Other cases are handled by the build system.<br />
<br />
==Composite Phrases==<br />
<br />
In most cases, the translated texts will not be static, but contain dynamic values as parts of a sentence. The straight-forward approach of translating parts of the sentence individually and then using string concatenation to compose the final text is a BAD idea. Different languages have different sentence structures, and if there are multiple dynamic values, their order might need to be reversed in some languages, and not reversed in others.<br />
<br />
The solution is to translate entire sentences and then to use the <tt>gettext</tt> function to insert dynamic values:<br />
<br />
<pre class="language-javascript"><br />
alert(<br />
//#. %1$s is the given name<br />
//#. %2$s is the family name<br />
//#, c-format<br />
gt('Welcome, %1$s %2$s!', firstName, lastName));<br />
</pre><br />
<br />
Results in:<br />
<br />
<pre class="language-gettext"><br />
#. %1$s is the given name<br />
#. %2$s is the family name<br />
#, c-format<br />
msgid "Welcome, %1$s, %2$s"<br />
msgstr ""<br />
</pre><br />
<br />
As shown in the example, it is possible to add comments for translators by starting a comment with <tt>"#."</tt>. Such comments must be placed immediately before the name of the variable which refers to the <tt>gettext</tt> module (in this case <tt>gt</tt>). They can be separated by arbitrary whitespace and newlines, but not other tokens. All such <tt>gettext</tt> calls should have comments explaining every format specifier.<br />
<br />
Comments starting with <tt>"#,"</tt> are meant for <tt>gettext</tt> tools, which in the case of <tt>"#, c-format"</tt>, can ensure that the translator did not leave out or mistype any of the format specifiers.<br />
<br />
For the cases when the format string must be translated by one of the functions described below, there is a dedicated format function <tt>gettext.format</tt> which, except for debugging, is an alias for <tt>_.printf</tt>.<br />
<br />
==Debugging==<br />
<br />
One of the most common i18n errors is forgetting to use a <tt>gettext</tt> function. To catch this kind of error, the UI can be started with the hash parameter <tt>"#debug-i18n=1"</tt>. (Reloading of the browser tab is usually required for the setting to take effect.)<br />
<br />
In this mode, every translated string is marked with invisible Unicode characters, and any DOM text without those characters gets reported on the console. The <tt>gettext.format</tt> function then also checks that every parameter is translated. This is the reason why <tt>_.printf</tt> should not be used for user-visible strings directly.<br />
<br />
Unfortunately, this method will also report any string which does not actually require translation. Examples of such strings include user data, numbers, strings already translated by the server, etc. To avoid filling the console with such false positives, every such string must be marked by passing it through the function <tt>gettext.noI18n</tt>:<br />
<br />
<pre class="language-javascript"><br />
alert(<br />
//#. %1$s is the given name<br />
//#. %2$s is the family name<br />
//#, c-format<br />
gt('Welcome, %1$s %2$s!', gt.noI18n(firstName), gt.noI18n(lastName)));<br />
</pre><br />
<br />
This results in the strings being marked as 'translated' without actually changing their visible contents. When not debugging, <tt>gettext.noI18n</tt> simply returns its first parameter.<br />
<br />
==Advanced gettext Functions==<br />
<br />
Besides <tt>gettext.format</tt> and <tt>gettext.noI18n</tt> there are several other functions which are required to cover all typical translation scenarios.<br />
<br />
===Contexts===<br />
<br />
Sometimes, the same English word or phrase has multiple meanings and must be translated differently depending on context. To be able to tell the individual translations apart, the method <tt>gettext.pgettext</tt> ('p' stands for 'particular') should be used instead of calling <tt>gettext</tt> directly. It takes the context as the first parameter and the text to translate as the second parameter:<br />
<br />
<pre class="language-javascript"><br />
alert(gt.pgettext('description', 'Title'));<br />
alert(gt.pgettext('salutation', 'Title'));<br />
</pre><br />
<br />
Results in:<br />
<br />
<pre class="language-gettext"><br />
msctxt "description"<br />
msgid "Title"<br />
msgstr "Beschreibung"<br />
<br />
msctxt "salutation"<br />
msgid "Title"<br />
msgstr "Anrede"<br />
</pre><br />
<br />
===Plural forms===<br />
<br />
In the case of numbers, the rules to select the proper plural form can be very complex. With the exception of languages with no separate plural forms, English is the second simplest language in this respect, having only two plural forms: singular and plural. Other languages can have up to four forms, and theoretically even more. The functions <tt>gettext.ngettext</tt> and <tt>gettext.npgettext</tt> (for a combination of plural forms with contexts) can select the proper plural form by using a piece of executable code embedded in the header of a PO file:<br />
<br />
<pre class="language-javascript"><br />
alert(gt.format(<br />
//#. %1$d is the number of mails<br />
//#, c-format<br />
gt.ngettext('You have %1$d mail', 'You have %1$d mails', n),<br />
gt.noI18n(n)));<br />
</pre><br />
<br />
The function <tt>gettext.ngettext</tt> accepts three parameters: the English singular and plural forms and the number which determines the chosen plural form. The function <tt>gettext.npgettext</tt> adds a context parameter before the others, similar to <tt>gettext.pgettext</tt>. They are usually used in combination with <tt>gettext.format</tt> to insert the actual number into the translated string.<br />
<br />
The above example results in the following entry:<br />
<br />
<pre class="language-gettext"><br />
#. %1$d is the number of mails<br />
#, c-format<br />
msgid "You have %1$d mail"<br />
msgid_plural "You have %1$d mails"<br />
msgstr[0] ""<br />
msgstr[1] ""<br />
</pre><br />
<br />
The number of <tt>msgstr[N]</tt> lines is determined by the number of plural forms in each language. This number is specified in the header of each PO file, together with the code to compute the index of the correct plural form the supplied number.<br />
<br />
==Custom Translations==<br />
App Suite has support for custom translations. ''gettext'' provides a function ''addTranslation(dictionary, key, value)'' that allows adding custom translation to a global dictionary ("*") as well as replacing translations in existing dictionaries. In order to use this, you have to create a plugin for the "core" namespace.<br />
<br />
<pre class="language-javascript"> <br />
var gt = require('io.ox/core/gettext');<br />
gt.addTranslation('*', 'Existing translation', 'My custom translation');<br />
</pre><br />
<br />
To keep it simple you can use the internal escaping to build counterparts for ngettext, pgettext, as well as npgettext. A context is escaped by \x00. Singular and plural are separated by \x01:<br />
<br />
<pre class="language-javascript"> <br />
// load gettext<br />
var gt = require('io.ox/core/gettext');<br />
<br />
// replace translation in dictionary 'io.ox/core'. Apps use context "app".<br />
gt.addTranslation('io.ox/core', 'app\x00Address Book', 'My Contacts');<br />
<br />
// replace translation - counterpart to ngettext<br />
gt.addTranslation('io.ox/mail', 'Copy\x01Copies', { 0: 'Kopie', 1: 'Kopien' });<br />
<br />
// replace translation - counterpart to npgettext<br />
gt.addTranslation('io.ox/mail', 'test\x00One\x01Two', { 0: 'Eins', 1: 'Zwei' });<br />
<br />
// some checks<br />
require(['gettext!io.ox/mail'], function (gt) {<br />
<br />
console.log('Sigular:', gt.ngettext('Copy', 'Copies', 1));<br />
console.log('Plural:', gt.ngettext('Copy', 'Copies', 2));<br />
<br />
console.log('Without context', gt.gettext('Files')); // should be 'My File Box'<br />
console.log('With context', gt.pgettext('test', 'Files')); // should be 'Files'<br />
<br />
console.log('Plural with context', gt.npgettext('test', 'One', 'Two', 2)); // should be 'Two'<br />
});<br />
<br />
</pre><br />
<br />
''addTranslation()'' always works for current language. If you want to benefit from automatic POT file generation, use the following approach:<br />
<br />
<pre class="language-javascript"> <br />
define('plugins/example/register', ['io.ox/core/gettext', 'gettext!custom'], function (gettext, gt) {<br />
<br />
'use strict';<br />
<br />
/* Just list all custom translations in your plugin by standalone gt() calls.<br />
The build system recognizes these strings and collects them in a POT file, <br />
so that they can be subject to translation processes. At runtime, gt('...')<br />
returns translations for current language.<br />
*/<br />
if (false) {<br />
gt('Tree');<br />
gt('House');<br />
gt('Dog');<br />
}<br />
<br />
// get internal dictionary (for current language of course)<br />
var dictionary = gt.getDictionary();<br />
<br />
// add all translations to global dictionary. Done!<br />
gettext.addTranslation('*', dictionary);<br />
});<br />
</pre><br />
<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]<br />
[[Category:Developer]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:I18n&diff=17793AppSuite:I18n2014-06-11T15:39:30Z<p>J.ohny.b: /* Translation */</p>
<hr />
<div>{{Stability-experimental}}<br />
<br />
<div class="title">Internationalization (i18n)</div><br />
<br />
__TOC__<br />
<br />
Providing software for users in the whole world means providing it in multiple languages. This consists of two steps:<br />
<br />
* ''Internationalization (i18n)'' - Making the software international, i. e. preparing it to be adapted to different languages and locations.<br />
* ''Localization (L10n)'' - Adapting the software to a particular language and/or location.<br />
The Open-Xchange platform offers several facilities to simplify both parts. The L10n part is mostly a concern for translators. Open-Xchange facilities for that consist mainly of using a well-established format for translations: [http://www.gnu.org/s/gettext/ GNU Gettext] Portable Object (PO) files. This allows translators to use existing dedicated translation tools or a simple UTF-8-capable text editor.<br />
<br />
The i18n part is what software developers will be mostly dealing with and is what the rest of this document describes.<br />
<br />
==Translation==<br />
<br />
The main part of i18n is the translation of various text strings which are presented to the user. For this purpose, the Open-Xchange platform provides the RequireJS plugin <tt>'gettext'</tt>. Individual translation files are specified as a module dependency of the form <tt>'gettext!module_name'</tt>. The resulting module API is a function which can be called to translate a string using the specified translation files:<br />
<br />
<pre class="language-javascript"><br />
define('com.example/example', ['gettext!com.example/example'],<br />
function (gt) {<br />
'use strict';<br />
alert(gt('Hello, world!'));<br />
});<br />
</pre><br />
<br />
A file named ox.pot is created by the <tt>ox.pot</tt> (< 7.6.0) or <tt>create_pot</tt> (>= 7.6.0) tasks of the build system. So for versions < 7.6.0 you will need to run:<br />
<br />
$ build-appsuite ox.pot<br />
<br />
to create the file in the apps root directory and starting from 7.6.0:<br />
<br />
$ grunt create_pot<br />
<br />
will generate this file within the <tt>i18n/</tt> directory. It will contain an entry for every call to one of <tt>gettext</tt> functions:<br />
<br />
<pre class="language-gettext"><br />
#: apps/com.example/example.js:4<br />
msgid "Hello, world!"<br />
msgstr ""<br />
</pre><br />
<br />
This file can be sent to the translators and during the translation process, one PO file for each language will be created. The PO files in the directory <tt>i18n</tt> shoud contain the translated entry:<br />
<br />
<pre class="language-gettext"><br />
#: apps/com.example/example.js:4<br />
msgid "Hello, world!"<br />
msgstr "Hallo, Welt!"<br />
</pre><br />
<br />
During the next build, the entries are copied from the central PO files into individual translation files. In our example, this would be <tt>apps/com.example/example.de_DE.js</tt>. Because of the added language ID, translation files can usually have the same name as the corresponding main module. Multiple related modules should use the same translation file to avoid the overhead of too many small translation files.<br />
<br />
Most modules will require more complex translations than can be provided by a simple string lookup. To handle some of these cases, the <tt>gettext</tt> module provides traditional methods in addition to being a callable function. Other cases are handled by the build system.<br />
<br />
==Composite Phrases==<br />
<br />
In most cases, the translated texts will not be static, but contain dynamic values as parts of a sentence. The straight-forward approach of translating parts of the sentence individually and then using string concatenation to compose the final text is a BAD idea. Different languages have different sentence structures, and if there are multiple dynamic values, their order might need to be reversed in some languages, and not reversed in others.<br />
<br />
The solution is to translate entire sentences and then to use the <tt>gettext</tt> function to insert dynamic values:<br />
<br />
<pre class="language-javascript"><br />
alert(<br />
//#. %1$s is the given name<br />
//#. %2$s is the family name<br />
//#, c-format<br />
gt('Welcome, %1$s %2$s!', firstName, lastName));<br />
</pre><br />
<br />
Results in:<br />
<br />
<pre class="language-gettext"><br />
#. %1$s is the given name<br />
#. %2$s is the family name<br />
#, c-format<br />
msgid "Welcome, %1$s, %2$s"<br />
msgstr ""<br />
</pre><br />
<br />
As shown in the example, it is possible to add comments for translators by starting a comment with <tt>"#."</tt>. Such comments must be placed immediately before the name of the variable which refers to the <tt>gettext</tt> module (in this case <tt>gt</tt>). They can be separated by arbitrary whitespace and newlines, but not other tokens. All such <tt>gettext</tt> calls should have comments explaining every format specifier.<br />
<br />
Comments starting with <tt>"#,"</tt> are meant for <tt>gettext</tt> tools, which in the case of <tt>"#, c-format"</tt>, can ensure that the translator did not leave out or mistype any of the format specifiers.<br />
<br />
For the cases when the format string must be translated by one of the functions described below, there is a dedicated format function <tt>gettext.format</tt> which, except for debugging, is an alias for <tt>_.printf</tt>.<br />
<br />
==Debugging==<br />
<br />
One of the most common i18n errors is forgetting to use a <tt>gettext</tt> function. To catch this kind of error, the UI can be started with the hash parameter <tt>"#debug-i18n=1"</tt>. (Reloading of the browser tab is usually required for the setting to take effect.)<br />
<br />
In this mode, every translated string is marked with invisible Unicode characters, and any DOM text without those characters gets reported on the console. The <tt>gettext.format</tt> function then also checks that every parameter is translated. This is the reason why <tt>_.printf</tt> should not be used for user-visible strings directly.<br />
<br />
Unfortunately, this method will also report any string which does not actually require translation. Examples of such strings include user data, numbers, strings already translated by the server, etc. To avoid filling the console with such false positives, every such string must be marked by passing it through the function <tt>gettext.noI18n</tt>:<br />
<br />
<pre class="language-javascript"><br />
alert(<br />
//#. %1$s is the given name<br />
//#. %2$s is the family name<br />
//#, c-format<br />
gt('Welcome, %1$s %2$s!', gt.noI18n(firstName), gt.noI18n(lastName)));<br />
</pre><br />
<br />
This results in the strings being marked as 'translated' without actually changing their visible contents. When not debugging, <tt>gettext.noI18n</tt> simply returns its first parameter.<br />
<br />
==Advanced gettext Functions==<br />
<br />
Besides <tt>gettext.format</tt> and <tt>gettext.noI18n</tt> there are several other functions which are required to cover all typical translation scenarios.<br />
<br />
===Contexts===<br />
<br />
Sometimes, the same English word or phrase has multiple meanings and must be translated differently depending on context. To be able to tell the individual translations apart, the method <tt>gettext.pgettext</tt> ('p' stands for 'particular') should be used instead of calling <tt>gettext</tt> directly. It takes the context as the first parameter and the text to translate as the second parameter:<br />
<br />
<pre class="language-javascript"><br />
alert(gt.pgettext('description', 'Title'));<br />
alert(gt.pgettext('salutation', 'Title'));<br />
</pre><br />
<br />
Results in:<br />
<br />
<pre class="language-gettext"><br />
msctxt "description"<br />
msgid "Title"<br />
msgstr "Beschreibung"<br />
<br />
msctxt "salutation"<br />
msgid "Title"<br />
msgstr "Anrede"<br />
</pre><br />
<br />
===Plural forms===<br />
<br />
In the case of numbers, the rules to select the proper plural form can be very complex. With the exception of languages with no separate plural forms, English is the second simplest language in this respect, having only two plural forms: singular and plural. Other languages can have up to four forms, and theoretically even more. The functions <tt>gettext.ngettext</tt> and <tt>gettext.npgettext</tt> (for a combination of plural forms with contexts) can select the proper plural form by using a piece of executable code embedded in the header of a PO file:<br />
<br />
<pre class="language-javascript"><br />
alert(gt.format(<br />
//#. %1$d is the number of mails<br />
//#, c-format<br />
gt.ngettext('You have %1$d mail', 'You have %1$d mails', n),<br />
gt.noI18n(n)));<br />
</pre><br />
<br />
The function <tt>gettext.ngettext</tt> accepts three parameters: the English singular and plural forms and the number which determines the chosen plural form. The function <tt>gettext.npgettext</tt> adds a context parameter before the others, similar to <tt>gettext.pgettext</tt>. They are usually used in combination with <tt>gettext.format</tt> to insert the actual number into the translated string.<br />
<br />
The above example results in the following entry:<br />
<br />
<pre class="language-gettext"><br />
#. %1$d is the number of mails<br />
#, c-format<br />
msgid "You have %1$d mail"<br />
msgid_plural "You have %1$d mails"<br />
msgstr[0] ""<br />
msgstr[1] ""<br />
</pre><br />
<br />
The number of <tt>msgstr[N]</tt> lines is determined by the number of plural forms in each language. This number is specified in the header of each PO file, together with the code to compute the index of the correct plural form the supplied number.<br />
<br />
==Custom Translations==<br />
App Suite has support for custom translations. ''gettext'' provides a function ''addTranslation(dictionary, key, value)'' that allows adding custom translation to a global dictionary ("*") as well as replacing translations in existing dictionaries. In order to use this, you have to create a plugin for the "core" namespace.<br />
<br />
<pre class="language-javascript"> <br />
var gt = require('io.ox/core/gettext');<br />
gt.addTranslation('*', 'Existing translation', 'My custom translation');<br />
</pre><br />
<br />
To keep it simple you can use the internal escaping to build counterparts for ngettext, pgettext, as well as npgettext. A context is escaped by \x00. Singular and plural are separated by \x01:<br />
<br />
<pre class="language-javascript"> <br />
// load gettext<br />
var gt = require('io.ox/core/gettext');<br />
<br />
// replace translation in dictionary 'io.ox/core'. Apps use context "app".<br />
gt.addTranslation('io.ox/core', 'app\x00Address Book', 'My Contacts');<br />
<br />
// replace translation - counterpart to ngettext<br />
gt.addTranslation('io.ox/mail', 'Copy\x01Copies', { 0: 'Kopie', 1: 'Kopien' });<br />
<br />
// replace translation - counterpart to npgettext<br />
gt.addTranslation('io.ox/mail', 'test\x00One\x01Two', { 0: 'Eins', 1: 'Zwei' });<br />
<br />
// some checks<br />
require(['gettext!io.ox/mail'], function (gt) {<br />
<br />
console.log('Sigular:', gt.ngettext('Copy', 'Copies', 1));<br />
console.log('Plural:', gt.ngettext('Copy', 'Copies', 2));<br />
<br />
console.log('Without context', gt.gettext('Files')); // should be 'My File Box'<br />
console.log('With context', gt.pgettext('test', 'Files')); // should be 'Files'<br />
<br />
console.log('Plural with context', gt.npgettext('test', 'One', 'Two', 2)); // should be 'Two'<br />
});<br />
<br />
</pre><br />
<br />
''addTranslation()'' always works for current language. If you want to benefit from automatic POT file generation, use the following approach:<br />
<br />
<pre class="language-javascript"> <br />
define('plugins/example/register', ['io.ox/core/gettext', 'gettext!custom'], function (gettext, gt) {<br />
<br />
'use strict';<br />
<br />
/* Just list all custom translations in your plugin by standalone gt() calls.<br />
The build system recognizes these strings and collects them in a POT file, <br />
so that they can be subject to translation processes. At runtime, gt('...')<br />
returns translations for current language.<br />
*/<br />
if (false) {<br />
gt('Tree');<br />
gt('House');<br />
gt('Dog');<br />
}<br />
<br />
// get internal dictionary (for current language of course)<br />
var dictionary = gt.getDictionary();<br />
<br />
// add all translations to global dictionary. Done!<br />
gettext.addTranslation('*', dictionary);<br />
});<br />
</pre><br />
<br />
<br />
[[Category:AppSuite]]<br />
[[Category:UI]]<br />
[[Category:Developer]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Upgrade_app_using_yo&diff=17783AppSuite:Upgrade app using yo2014-06-10T09:25:41Z<p>J.ohny.b: /* Development Tools */</p>
<hr />
<div><div class="title">Upgrade an app using yo</div><br />
<br />
__TOC__<br />
<br />
With the release of App Suite version 7.6.0, we moved towards more easy dependency management for UI modules. We switched to grunt as a task-runner and are using npm and bower to handle dependencies. This article should provide a short guide on what to do in order to upgrade your app to the latest and greatest version of our shared grunt configuration and other development tools.<br />
<br />
<div style="clear: both"></div><br />
<br />
== Development Tools ==<br />
<br />
First, you might want to check on the versions of the tools which are necessary for UI development.<br />
<br />
$ npm outdated -g grunt-cli bower yo generator-ox-ui-module<br />
<br />
It's good to have those up-to-date, especially generator-ox-ui-module. You can update them by running <tt>npm install -g <pkg></tt>.<br />
<br />
== Upgrading a Workspace using yo ==<br />
<br />
You can generate a new version of your workspace using:<br />
<br />
$ yo ox-ui-module<br />
<br />
It will prompt for every file that will be overwritten with a different version and ask whether you want to overwrite or not. It is possible to view a diff between both versions. If you have not changed any of the defaults, it is save to say <tt>yes</tt> in all of these cases. If you have changed any of these files, you might still want to overwrite the file and later only commit parts of the new things or just reset to the old version. This can be done easily with your favorite SCM tool, like git or cvs (okay, in this case may be not as easy as promised).<br />
<br />
Please pay special attention to <tt>package.json</tt> and <tt>bower.json</tt> files, since those are the files you might have changed before. Those contain meta-information unique to your app, so they will be different from the initially generated ones.<br />
<br />
== Manually upgrading single node modules ==<br />
<br />
If the approach above is a little to radical for you, because you have customized everything, you might only want to update to the latest shared grunt configuration. Go to the [https://github.com/Open-Xchange-Frontend/shared-grunt-config/releases releases page of shared-grunt-config] and add the name of the latest tag to your apps <tt>package.json</tt>. It should look something like<br />
<br />
<code><br />
"shared-grunt-config": "Open-Xchange-Frontend/shared-grunt-config#v0.5.0"<br />
</code><br />
<br />
where the string after the # is the tag-name that will be checked out. Save the file and run:<br />
<br />
$ npm install --no-shrinkwrap<br />
<br />
to make sure all your node_modules are up-to-date. If something fails, remove the node_modules directory and try again.<br />
<br />
== Testing the most important tasks ==<br />
<br />
A quick way to check if everything still runs fine is to run the following grunt tasks<br />
<br />
grunt dist<br />
grunt install:dist --prefix backend --htdoc www<br />
<br />
After that, check if the directories <tt>backend</tt> and <tt>www</tt> contain files that look a little familiar. If everything seems to look fine, you can remove those directories, again.<br />
<br />
During this test, keep an eye on the output and check any warning or error message. If everything looks fine, use <tt>grunt dev</tt> task to test your app in a browser.<br />
<br />
== Packaging ==<br />
<br />
For packaging it works similar as for the app mentioned above. You can re-run the yo generators<br />
<br />
$ yo ox-ui-module:deb-pkg<br />
$ yo ox-ui-module:rpm-pkg<br />
<br />
to generate new versions of the files. However, in case of packaging the chance is even higher, that you have changed the generated files. So the best advice would be to to check the differences and cherry pick the changes into your packaging information, i.e. manually add the changes you need to your files.<br />
<br />
== Further Reading ==<br />
<br />
* In case you want to create a new app for OX App Suite, read [[Appsuite:GettingStarted_7.6.0 | the getting started guide]].</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Upgrade_app_using_yo&diff=17782AppSuite:Upgrade app using yo2014-06-06T16:25:20Z<p>J.ohny.b: /* Manually upgrading single node modules */</p>
<hr />
<div><div class="title">Upgrade an app using yo</div><br />
<br />
__TOC__<br />
<br />
With the release of App Suite version 7.6.0, we moved towards more easy dependency management for UI modules. We switched to grunt as a task-runner and are using npm and bower to handle dependencies. This article should provide a short guide on what to do in order to upgrade your app to the latest and greatest version of our shared grunt configuration and other development tools.<br />
<br />
<div style="clear: both"></div><br />
<br />
== Development Tools ==<br />
<br />
First, you might want to check on the versions of the tools which are necessary for UI development.<br />
<br />
$ npm outdated -g grunt-cli bower yo generator-ox-ui-module<br />
<br />
It's good to have those up-to-date, especially generator-ox-ui-module. You can update them by running <tt>npm install -g <pkg><tt>.<br />
<br />
== Upgrading a Workspace using yo ==<br />
<br />
You can generate a new version of your workspace using:<br />
<br />
$ yo ox-ui-module<br />
<br />
It will prompt for every file that will be overwritten with a different version and ask whether you want to overwrite or not. It is possible to view a diff between both versions. If you have not changed any of the defaults, it is save to say <tt>yes</tt> in all of these cases. If you have changed any of these files, you might still want to overwrite the file and later only commit parts of the new things or just reset to the old version. This can be done easily with your favorite SCM tool, like git or cvs (okay, in this case may be not as easy as promised).<br />
<br />
Please pay special attention to <tt>package.json</tt> and <tt>bower.json</tt> files, since those are the files you might have changed before. Those contain meta-information unique to your app, so they will be different from the initially generated ones.<br />
<br />
== Manually upgrading single node modules ==<br />
<br />
If the approach above is a little to radical for you, because you have customized everything, you might only want to update to the latest shared grunt configuration. Go to the [https://github.com/Open-Xchange-Frontend/shared-grunt-config/releases releases page of shared-grunt-config] and add the name of the latest tag to your apps <tt>package.json</tt>. It should look something like<br />
<br />
<code><br />
"shared-grunt-config": "Open-Xchange-Frontend/shared-grunt-config#v0.5.0"<br />
</code><br />
<br />
where the string after the # is the tag-name that will be checked out. Save the file and run:<br />
<br />
$ npm install --no-shrinkwrap<br />
<br />
to make sure all your node_modules are up-to-date. If something fails, remove the node_modules directory and try again.<br />
<br />
== Testing the most important tasks ==<br />
<br />
A quick way to check if everything still runs fine is to run the following grunt tasks<br />
<br />
grunt dist<br />
grunt install:dist --prefix backend --htdoc www<br />
<br />
After that, check if the directories <tt>backend</tt> and <tt>www</tt> contain files that look a little familiar. If everything seems to look fine, you can remove those directories, again.<br />
<br />
During this test, keep an eye on the output and check any warning or error message. If everything looks fine, use <tt>grunt dev</tt> task to test your app in a browser.<br />
<br />
== Packaging ==<br />
<br />
For packaging it works similar as for the app mentioned above. You can re-run the yo generators<br />
<br />
$ yo ox-ui-module:deb-pkg<br />
$ yo ox-ui-module:rpm-pkg<br />
<br />
to generate new versions of the files. However, in case of packaging the chance is even higher, that you have changed the generated files. So the best advice would be to to check the differences and cherry pick the changes into your packaging information, i.e. manually add the changes you need to your files.<br />
<br />
== Further Reading ==<br />
<br />
* In case you want to create a new app for OX App Suite, read [[Appsuite:GettingStarted_7.6.0 | the getting started guide]].</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Upgrade_app_using_yo&diff=17781AppSuite:Upgrade app using yo2014-06-06T16:24:33Z<p>J.ohny.b: /* Testing the most important tasks */</p>
<hr />
<div><div class="title">Upgrade an app using yo</div><br />
<br />
__TOC__<br />
<br />
With the release of App Suite version 7.6.0, we moved towards more easy dependency management for UI modules. We switched to grunt as a task-runner and are using npm and bower to handle dependencies. This article should provide a short guide on what to do in order to upgrade your app to the latest and greatest version of our shared grunt configuration and other development tools.<br />
<br />
<div style="clear: both"></div><br />
<br />
== Development Tools ==<br />
<br />
First, you might want to check on the versions of the tools which are necessary for UI development.<br />
<br />
$ npm outdated -g grunt-cli bower yo generator-ox-ui-module<br />
<br />
It's good to have those up-to-date, especially generator-ox-ui-module. You can update them by running <tt>npm install -g <pkg><tt>.<br />
<br />
== Upgrading a Workspace using yo ==<br />
<br />
You can generate a new version of your workspace using:<br />
<br />
$ yo ox-ui-module<br />
<br />
It will prompt for every file that will be overwritten with a different version and ask whether you want to overwrite or not. It is possible to view a diff between both versions. If you have not changed any of the defaults, it is save to say <tt>yes</tt> in all of these cases. If you have changed any of these files, you might still want to overwrite the file and later only commit parts of the new things or just reset to the old version. This can be done easily with your favorite SCM tool, like git or cvs (okay, in this case may be not as easy as promised).<br />
<br />
Please pay special attention to <tt>package.json</tt> and <tt>bower.json</tt> files, since those are the files you might have changed before. Those contain meta-information unique to your app, so they will be different from the initially generated ones.<br />
<br />
== Manually upgrading single node modules ==<br />
<br />
If the approach above is a little to radical for you, because you have customized everything, you might only want to update to the latest shared grunt configuration. Go to the [https://github.com/Open-Xchange-Frontend/shared-grunt-config/releases | releases page of shared-grunt-config] and add the name of the latest tag to your apps <tt>package.json</tt>. It should look something like<br />
<br />
<code><br />
"shared-grunt-config": "Open-Xchange-Frontend/shared-grunt-config#v0.5.0"<br />
</code><br />
<br />
where the string after the # is the tag-name that will be checked out. Save the file and run:<br />
<br />
$ npm install --no-shrinkwrap<br />
<br />
to make sure all your node_modules are up-to-date. If something fails, remove the node_modules directory and try again.<br />
<br />
== Testing the most important tasks ==<br />
<br />
A quick way to check if everything still runs fine is to run the following grunt tasks<br />
<br />
grunt dist<br />
grunt install:dist --prefix backend --htdoc www<br />
<br />
After that, check if the directories <tt>backend</tt> and <tt>www</tt> contain files that look a little familiar. If everything seems to look fine, you can remove those directories, again.<br />
<br />
During this test, keep an eye on the output and check any warning or error message. If everything looks fine, use <tt>grunt dev</tt> task to test your app in a browser.<br />
<br />
== Packaging ==<br />
<br />
For packaging it works similar as for the app mentioned above. You can re-run the yo generators<br />
<br />
$ yo ox-ui-module:deb-pkg<br />
$ yo ox-ui-module:rpm-pkg<br />
<br />
to generate new versions of the files. However, in case of packaging the chance is even higher, that you have changed the generated files. So the best advice would be to to check the differences and cherry pick the changes into your packaging information, i.e. manually add the changes you need to your files.<br />
<br />
== Further Reading ==<br />
<br />
* In case you want to create a new app for OX App Suite, read [[Appsuite:GettingStarted_7.6.0 | the getting started guide]].</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=Portal:AppSuite_UI/Topics&diff=17780Portal:AppSuite UI/Topics2014-06-06T16:22:55Z<p>J.ohny.b: </p>
<hr />
<div>== Getting started ==<br />
* [[AppSuite:GettingStarted | Getting started developing an external app]]<br />
* [[AppSuite:Upgrade_app_using_yo | Upgrade your external app to latest App Suite version]]<br />
* [[AppSuite:UI developer primer| Skills needed to develop the UI]]<br />
* [[AppSuite:Getting started developing the UI | Getting started developing the UI]]<br />
* [[AppSuite:UI_Development_Style_Guide | UI Development Style Guide]]<br />
* [[AppSuite:Appserver | Appserver]]<br />
* [[AppSuite:Apache Configuration | Apache Configuration]]<br />
* [[AppSuite:Definition of done|Definition of done]]<br />
* [[Appsuite:UI_FAQ | Frequently asked questions]]<br />
<br />
== Testing ==<br />
* [[AppSuite:Test basics | Lessons learned while testing]]<br />
* [[AppSuite:RunTests | Running ui tests]]<br />
<br />
== How-to articles ==<br />
* [[AppSuite:Action links|Understanding action links]]<br />
** [[AppSuite:Files App Actions|Adding actions to the files app]]<br />
* [[AppSuite:Date_and_time|Date and time]]<br />
* [[AppSuite:i18n | Internationalization (i18n)]]<br />
* [[AppSuite:a11y | Accessibility (a11y)]]<br />
* [[AppSuite:Mediaplayer | Mediaplayer]]<br />
* [[AppSuite:Theming | Theming]]<br />
* [[AppSuite:UI manifests explained | UI manifests explained]]<br />
* [[AppSuite:Upsell | Upsell]]<br />
* [[AppSuite:VGrid | VGrid]]<br />
* [[AppSuite:Mobile | Mobile development]]<br />
* [[AppSuite:Upsell tools| Upsell tools]]<br />
* [[AppSuite:Guided tours| Guided tours]]<br />
* [[AppSuite:Browserdetection | Browser detection with form-login]]<br />
<br />
=== Server communication ===<br />
* [[AppSuite:APIs | APIs]]<br />
* [[AppSuite:API_Factory | API Factory]]<br />
* [[AppSuite:http.js | http.js]]<br />
<br />
=== Extension points ===<br />
* [[AppSuite:Extending_the_UI_(Hands-on_introduction)| Hands-on introduction]]<br />
* [[AppSuite:Extending_the_UI | General information on extension points]]<br />
* [[AppSuite:Modifying forms by using extension points | Modifying forms]]<br />
* [[AppSuite:Extension points for calendar |extension points: Calendar]]<br />
* [[AppSuite:Extension points for contact |extension points: Contact]]<br />
* [[AppSuite:Extension points for core | extension points: Core]]<br />
* [[AppSuite:Extension points for email | extension points: E-Mail]]<br />
* [[AppSuite:Extension points for files |extension points: Files]]<br />
* [[AppSuite:Extension points for tasks |extension points: Tasks]]<br />
* [[AppSuite:Extension points for miscellaneous |extension points: Miscellaneous]]<br />
<br />
=== Writing components ===<br />
* [[AppSuite:Writing a portal plugin | Writing a portal plugin]]<br />
** [[AppSuite:Configuring portal plugins|Configuring a portal plugin]]<br />
** [[AppSuite:Using the Upsell widget|Using the Upsell widget]]<br />
* [[AppSuite:Writing a simple application | Writing a simple application]]<br />
* [[AppSuite:Writing a notification area plugin | Writing a plugin for the notification area]]<br />
* [[AppSuite:Writing a wizard | Writing a wizard]]<br />
* [[AppSuite:Capabilities | Capabilities explained]] - if you want to enable or disable your plugin<br />
<br />
== Miscellaneous articles ==<br />
* [[AppSuite:External libraries for the UI | External libraries used by the UI]]<br />
* [[AppSuite:Configuration| Configuration]]<br />
* [[AppSuite:Custom configurations| Custom configurations]]<br />
* [[AppSuite:UI build system| The UI build system]]<br />
* [[AppSuite:Embedding your settings into AppSuite settings|How to embed your own settings into the Appsuite settings page]]<br />
* [[AppSuite:Debugging the UI|Debugging the UI]]<br />
* All articles regarding the UI are filed in the category [[:Category:UI]]</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:Upgrade_app_using_yo&diff=17779AppSuite:Upgrade app using yo2014-06-06T16:21:11Z<p>J.ohny.b: gh</p>
<hr />
<div><div class="title">Upgrade an app using yo</div><br />
<br />
__TOC__<br />
<br />
With the release of App Suite version 7.6.0, we moved towards more easy dependency management for UI modules. We switched to grunt as a task-runner and are using npm and bower to handle dependencies. This article should provide a short guide on what to do in order to upgrade your app to the latest and greatest version of our shared grunt configuration and other development tools.<br />
<br />
<div style="clear: both"></div><br />
<br />
== Development Tools ==<br />
<br />
First, you might want to check on the versions of the tools which are necessary for UI development.<br />
<br />
$ npm outdated -g grunt-cli bower yo generator-ox-ui-module<br />
<br />
It's good to have those up-to-date, especially generator-ox-ui-module. You can update them by running <tt>npm install -g <pkg><tt>.<br />
<br />
== Upgrading a Workspace using yo ==<br />
<br />
You can generate a new version of your workspace using:<br />
<br />
$ yo ox-ui-module<br />
<br />
It will prompt for every file that will be overwritten with a different version and ask whether you want to overwrite or not. It is possible to view a diff between both versions. If you have not changed any of the defaults, it is save to say <tt>yes</tt> in all of these cases. If you have changed any of these files, you might still want to overwrite the file and later only commit parts of the new things or just reset to the old version. This can be done easily with your favorite SCM tool, like git or cvs (okay, in this case may be not as easy as promised).<br />
<br />
Please pay special attention to <tt>package.json</tt> and <tt>bower.json</tt> files, since those are the files you might have changed before. Those contain meta-information unique to your app, so they will be different from the initially generated ones.<br />
<br />
== Manually upgrading single node modules ==<br />
<br />
If the approach above is a little to radical for you, because you have customized everything, you might only want to update to the latest shared grunt configuration. Go to the [https://github.com/Open-Xchange-Frontend/shared-grunt-config/releases | releases page of shared-grunt-config] and add the name of the latest tag to your apps <tt>package.json</tt>. It should look something like<br />
<br />
<code><br />
"shared-grunt-config": "Open-Xchange-Frontend/shared-grunt-config#v0.5.0"<br />
</code><br />
<br />
where the string after the # is the tag-name that will be checked out. Save the file and run:<br />
<br />
$ npm install --no-shrinkwrap<br />
<br />
to make sure all your node_modules are up-to-date. If something fails, remove the node_modules directory and try again.<br />
<br />
== Testing the most important tasks ==<br />
<br />
A quick way to check if everything still runs fine is to run the following grunt tasks<br />
<br />
grunt dist<br />
grunt install:dist --prefix backend --htdoc www<br />
<br />
After that, check if the directories <tt>backend</tt> and <tt>www</tt> contain files that look a little familiar. If everything seems to look fine, you can remove those directories, again.<br />
<br />
During this test, keep an eye on the output and check any warning or error message.<br />
<br />
== Packaging ==<br />
<br />
For packaging it works similar as for the app mentioned above. You can re-run the yo generators<br />
<br />
$ yo ox-ui-module:deb-pkg<br />
$ yo ox-ui-module:rpm-pkg<br />
<br />
to generate new versions of the files. However, in case of packaging the chance is even higher, that you have changed the generated files. So the best advice would be to to check the differences and cherry pick the changes into your packaging information, i.e. manually add the changes you need to your files.<br />
<br />
== Further Reading ==<br />
<br />
* In case you want to create a new app for OX App Suite, read [[Appsuite:GettingStarted_7.6.0 | the getting started guide]].</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GettingStarted_7.6.0&diff=17777AppSuite:GettingStarted 7.6.02014-06-06T15:04:25Z<p>J.ohny.b: /* Further Reading */</p>
<hr />
<div><div class="title">Getting Started</div><br />
<br />
<div style="float:right; padding: 10px;">[[File:OX_AppSuite_UI_Development_Workflow.png]]</div><br />
<br />
__TOC__<br />
<br />
Hello and welcome to the world of OX App Suite development. This document is designed to get you started with developing your first app for OX App Suite as quickly and simply as possible. However, along the way we will also tempt you to learn more by linking to some more in-depth documentation about the various topics we cover. <br />
<br />
<div style="clear: both"></div><br />
<br />
== Installing the Development Tools ==<br />
<br />
First, you need to install some tools which are necessary for UI development.<br />
<br />
$ npm install -g grunt-cli bower yo generator-ox-ui-module<br />
<br />
== Create a Workspace ==<br />
<br />
All your app development can take place inside one working directory. The examples will assume you have created a directory named <tt>myapp</tt> inside your home directory:<br />
<br />
$ mkdir ~/myapp<br />
$ cd ~/myapp<br />
<br />
It doesn't need to be in your home directory, and the actual name should reflect the name of your app. The main point is that all commands will be executed in this directory and all paths will be relative to this directory from now on.<br />
<br />
Now, you can generate a grunt configuration using:<br />
<br />
$ yo ox-ui-module<br />
<br />
The source code of your app will reside in the subfolder named <tt>apps</tt>. To avoid name collisions please pick a unique subfolder inside that. The easiest and recommended way is to use a domain name that you own. Typically, the domain elements are reversed, like in Java. <tt>example.com</tt> becomes <tt>com.example</tt>:<br />
<br />
$ mkdir -p apps/com.example<br />
<br />
== Writing an App ==<br />
<br />
As an example, let's create the smallest possible app and test it. It requires only two files: <tt>apps/com.example/register.js</tt> for the source code of the app:<br />
<br />
<pre class="language-javascript"><br />
define('com.example/register', function () {<br />
'use strict';<br />
alert('Hello, World!');<br />
});<br />
</pre><br />
<br />
and <tt>apps/com.example/manifest.json</tt> for the [[AppSuite:UI_manifests_explained | manifest]] which tells the UI that your app exists and what to do with it:<br />
<br />
<pre class="language-javascript">{ "namespace": "core" }</pre><br />
<br />
== Building an App ==<br />
<br />
The source code of your app can't be used by OX App Suite as it. It first has to be processed by the [[AppSuite:UI_build_system | build system]]. This step will check the source code for syntax errors, compress it, and depending on the structure of your code, many other things. The processed code is then written to a directory named <tt>build</tt> by default. Start the build with this command:<br />
<br />
$ grunt<br />
<br />
If your editor supports it, you can configure it to call the build system after every file save. Take care to call it from the top directory of your app's workspace, not from the directory of the saved file.<br />
<br />
== Testing an App ==<br />
<br />
The freshly built code can now be tested. Instead of uploading your code to an OX App Suite server, you can use the [[AppSuite:Appserver|appserver]] proxy to inject your code into the UI code of any existing OX App Suite installation. For example, to start <tt>appserver</tt> using [http://ox.io/ ox.io] as the server, you will need a local configuration pointing to that server. You can generate it with this command:<br />
<br />
$ grunt show-config:local --output grunt/local.conf.json<br />
<br />
Open up the file <tt>grunt/local.conf.json</tt> in your editor and add <tt><nowiki>"https://ox.io/appsuite/"</nowiki></tt> to the <tt>server</tt> setting of the appserver section. Then start the development server:<br />
<br />
$ grunt dev<br />
<br />
This command will serve your app from the local directory <tt>build</tt>, and get everything else from the URL specified in the <tt>server</tt> setting.<br />
<br />
Once appserver is running, you can access OX App Suite by opening your browser using this address:<br />
<br />
http://localhost:8337/appsuite<br />
<br />
After logging in, the app should be loaded and display the <tt>alert</tt> message.<br />
<br />
=== Development cycle ===<br />
<br />
Once you are sure that your setup works, you can extend the example and write the actual code for your app. The <tt>dev</tt> task will detect any changes and rebuild your app and even reload all browsers connected to<br />
<tt>http://localhost:8337/appsuite</tt>.<br />
<br />
While developing always keep in mind, that there is an [[AppSuite:Debugging_the_UI | article about debugging the user interface]] which helps you avoid and fix typical errors.<br />
<br />
== Packaging ==<br />
<br />
Together with our ox-ui-module generator (from version 0.8.0), we provide you with generators for packaging information.<br />
<br />
=== Generating a source tarball ===<br />
<br />
There is a grunt tasks to create source packages. You can use it to create a tar.gz file containing all sources needed to build the project. This source package should contain all dependencies that are installed during <tt>bower install</tt> and <tt>npm install</tt> (basically containing your <tt>bower_components</tt> and <tt>node_modules</tt> directories). To generate this file, run:<br />
<br />
$ grunt dist:source --include-dependencies<br />
<br />
This file (can be found in the <tt>dist/</tt> directory) together with the distribution specific packaging information will be needed to build the package.<br />
<br />
=== RPM packages ===<br />
<br />
In order to generate a spec file for your project, run:<br />
<br />
$ yo ox-ui-module:rpm-pkg<br />
<br />
Will generate a spec file, with some default values read from your <tt>package.json</tt> file. So you might want to make this file as complete as possible. However, yeoman will ask you about the information missing.<br />
<br />
''Please remember, that the generated spec file is only a scaffold for a real one. It will work for very simple cases, but as soon as your package gets more complicated, you will have to adjust it to your needs.''<br />
<br />
Once you have the spec file in your project, you can use<br />
<br />
$ grunt dist:rpm<br />
<br />
You can then use tools like rpmbuild to build the rpm file. One example would be:<br />
<br />
$ mkdir -p ~/rpmbuild/{SOURCES,SPECS,BUILD,RPMS}<br />
cp dist/*.orig.tar.gz ~/rpmbuild/SOURCES/<br />
cp dist/*.spec ~/rpmbuild/SPECS/<br />
rpmbuild -bb ~/rpmbuild/SPECS/*.spec<br />
<br />
=== DEB packages ===<br />
<br />
Creating packages for the Debian distribution works similar to rpm packages. Just run:<br />
<br />
$ yo ox-ui-module:deb-pkg<br />
<br />
This will generate a debian directory containing all needed files. Some default values will be read from your <tt>package.json</tt> file. So you might want to make this file as complete as possible. However, yeoman will ask you about the information missing.<br />
<br />
''Please remember, that the generated spec file is only a scaffold for a real one. It will work for very simple cases, but as soon as your package gets more complicated, you will have to adjust it to your needs.''<br />
<br />
Once you have a debian/ directory for your project, you can go on.<br />
To enable some grunt tasks to help you, you need to install (with npm install) <tt>grunt-contrib-compress</tt> and <tt>grunt-exec</tt>. Now you can run:<br />
<br />
$ grunt dist:dpkg-source<br />
<br />
You can now use the files in the dist/ directory to build your packages. A simple example would look like:<br />
<br />
$ cd dist/unicorn-0.0.1/<br />
dpkg-buildpackage -b<br />
<br />
== Further Reading ==<br />
* Congratulations you have just built your first app for OX App Suite, but please keep in mind that there are [[AppSuite:Developing for the UI#What_can_i_build.3F | quite a few options]] for developing for OX App Suite.<br />
* In case you want to upgrade your existing app for OX App Suite, read [[Appsuite:Upgrade_app_using_yo | the upgrade guide]].<br />
* More information on the build system can be found on github:<br />
** [https://github.com/Open-Xchange-Frontend/shared-grunt-config See all available grunt tasks]<br />
** [https://github.com/Open-Xchange-Frontend/generator-ox-ui-module Documentation of all generator tasks]<br />
* If you're stuck somewhere, the article about [[AppSuite:Debugging_the_UI | debugging the UI]] might help you.<br />
* You can read this to get a better overview of [[AppSuite:Developing for the UI | developing the user inferface]].</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&diff=17691AppSuite:GettingStartedWithGrunt2014-06-03T08:33:11Z<p>J.ohny.b: /* Local configuration */</p>
<hr />
<div><div class="title">Getting started with grunt</div><br />
__TOC__<br />
== Node ==<br />
<br />
=== Linux ===<br />
<br />
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.<br />
<br />
If you are having problems or your package manager does not provide at least nodejs in version 0.10.x please have a look at https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager<br />
<br />
Using Debian/Ubuntu you should install nodejs-legacy to get the node executable as node is some hamradio software<br />
<br />
=== Windows ===<br />
<br />
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.<br />
It is recommended to restart after the installation, as paths may not be up-to-date.<br />
<br />
Please also make sure git is installed and in your PATH.<br />
<br />
''This is sometimes a problem on windows, make sure to activate the option to put git into your path during msys-git installation.''<br />
<br />
=== Mac OS X ===<br />
<br />
We strongly encourage you to use [http://brew.sh/ homebrew]. Actually, we won't support you if you don't. It's that bad.<br />
<br />
Make sure you have an up-to-date homebrew installation.<br />
<br />
<pre><br />
brew update<br />
brew upgrade<br />
</pre><br />
<br />
Check your homebrew installation with <tt>brew doctor</tt> and resolve any issues and repeat this step until homebrews output says "Your system is ready to brew.".<br />
<br />
<pre><br />
brew doctor<br />
</pre><br />
<br />
Install node via <tt>brew install node</tt> if is not yet installed.<br />
<br />
<pre><br />
brew install node<br />
</pre><br />
<br />
Make sure you '''never <tt>sudo</tt> 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.<br />
<br />
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.<br />
<br />
You can either set this in your shell via <tt>ulimit -n 8192</tt> to a sensible value.<br />
<br />
<pre><br />
ulimit -n 8192<br />
</pre><br />
<br />
or you can set<br />
it permanently by adding <tt>limit maxfiles 8192 20480</tt> to <tt>/etc/launchd.conf</tt><br />
<br />
<pre><br />
echo "limit maxfiles 8192 20480" | sudo tee -a /etc/launchd.conf<br />
</pre><br />
<br />
''Note that you will have to restart your system for the setting to be active.''<br />
<br />
== Grunt & Bower ==<br />
<br />
With <tt>npm install -g bower grunt-cli</tt> you can install the global npm dependencies needed for grunt and bower.<br />
<br />
'''Mac OS X / Windows:'''<br />
<pre><br />
npm install -g bower grunt-cli<br />
</pre><br />
<br />
'''Linux ''only'':'''<br />
You may need root privileges to install global npm modules.<br />
<pre><br />
sudo npm install -g bower grunt-cli<br />
</pre><br />
<br />
== Cleaning development environment ==<br />
<br />
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running <tt>git clean -ndx</tt> this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run <tt>git clean -fdx</tt> to really do the cleanup.<br />
<br />
== Installing node dependencies for OX Appsuite Frontend development ==<br />
<br />
Change to the ui directory of your git workdirectory and run <tt>npm install</tt>.<br />
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,<br />
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)<br />
<br />
<pre><br />
npm install<br />
</pre><br />
<br />
== Building the UI ==<br />
<br />
Just run the default grunt task <tt>grunt</tt><br />
<br />
<pre><br />
grunt<br />
</pre><br />
<br />
== Local configuration ==<br />
<br />
You can override some options on your local build environment by creating the file <tt>grunt/local.conf.json</tt>. See <tt>grunt/local.conf.default.json</tt> as a template.<br />
<br />
Hint: use <tt>grunt show-config:local --output grunt/local.conf.json</tt> to generate <tt>grunt/local.conf.json</tt>.<br />
<br />
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.<br />
<br />
<pre><br />
{<br />
"appserver": {<br />
"server": "http://url.to.your.ser/appsuite/",<br />
"verbose": ["local:error"]<br />
}<br />
}<br />
</pre><br />
<br />
== Accessing the UI ==<br />
<br />
You can run the appserver with the connect grunt task:<br />
<br />
<pre><br />
grunt connect:server:keepalive<br />
</pre><br />
<br />
or in combination with the watch task:<br />
<br />
<pre><br />
grunt connect watch<br />
</pre><br />
<br />
You can now access the ui via <tt>http://localhost:8337/appsuite/</tt>.</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GettingStartedWithGrunt&diff=17690AppSuite:GettingStartedWithGrunt2014-06-03T08:31:48Z<p>J.ohny.b: /* Local configuration */</p>
<hr />
<div><div class="title">Getting started with grunt</div><br />
__TOC__<br />
== Node ==<br />
<br />
=== Linux ===<br />
<br />
Install the default nodejs of your distribution via your favourite package/ports manager and you are good to go.<br />
<br />
If you are having problems or your package manager does not provide at least nodejs in version 0.10.x please have a look at https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager<br />
<br />
Using Debian/Ubuntu you should install nodejs-legacy to get the node executable as node is some hamradio software<br />
<br />
=== Windows ===<br />
<br />
Head to the node.js [http://nodejs.org/ site] and download and install the latest version.<br />
It is recommended to restart after the installation, as paths may not be up-to-date.<br />
<br />
Please also make sure git is installed and in your PATH.<br />
<br />
''This is sometimes a problem on windows, make sure to activate the option to put git into your path during msys-git installation.''<br />
<br />
=== Mac OS X ===<br />
<br />
We strongly encourage you to use [http://brew.sh/ homebrew]. Actually, we won't support you if you don't. It's that bad.<br />
<br />
Make sure you have an up-to-date homebrew installation.<br />
<br />
<pre><br />
brew update<br />
brew upgrade<br />
</pre><br />
<br />
Check your homebrew installation with <tt>brew doctor</tt> and resolve any issues and repeat this step until homebrews output says "Your system is ready to brew.".<br />
<br />
<pre><br />
brew doctor<br />
</pre><br />
<br />
Install node via <tt>brew install node</tt> if is not yet installed.<br />
<br />
<pre><br />
brew install node<br />
</pre><br />
<br />
Make sure you '''never <tt>sudo</tt> 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.<br />
<br />
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.<br />
<br />
You can either set this in your shell via <tt>ulimit -n 8192</tt> to a sensible value.<br />
<br />
<pre><br />
ulimit -n 8192<br />
</pre><br />
<br />
or you can set<br />
it permanently by adding <tt>limit maxfiles 8192 20480</tt> to <tt>/etc/launchd.conf</tt><br />
<br />
<pre><br />
echo "limit maxfiles 8192 20480" | sudo tee -a /etc/launchd.conf<br />
</pre><br />
<br />
''Note that you will have to restart your system for the setting to be active.''<br />
<br />
== Grunt & Bower ==<br />
<br />
With <tt>npm install -g bower grunt-cli</tt> you can install the global npm dependencies needed for grunt and bower.<br />
<br />
'''Mac OS X / Windows:'''<br />
<pre><br />
npm install -g bower grunt-cli<br />
</pre><br />
<br />
'''Linux ''only'':'''<br />
You may need root privileges to install global npm modules.<br />
<pre><br />
sudo npm install -g bower grunt-cli<br />
</pre><br />
<br />
== Cleaning development environment ==<br />
<br />
Sometimes it is needed to completely wipe all old and ignored (by git) files. You can also do this using git by running <tt>git clean -ndx</tt> this will print a lot of files, but _not_ delete any. Check the list if you really want to remove these files. Then run <tt>git clean -fdx</tt> to really do the cleanup.<br />
<br />
== Installing node dependencies for OX Appsuite Frontend development ==<br />
<br />
Change to the ui directory of your git workdirectory and run <tt>npm install</tt>.<br />
Make sure, you have got the git binary in your path. (This is sometimes a problem on windows,<br />
make sure to activate the option to put git into your path during msys-git installation. TODO: more detail)<br />
<br />
<pre><br />
npm install<br />
</pre><br />
<br />
== Building the UI ==<br />
<br />
Just run the default grunt task <tt>grunt</tt><br />
<br />
<pre><br />
grunt<br />
</pre><br />
<br />
== Local configuration ==<br />
<br />
You can override some options on your local build environment by creating the file <tt>grunt/local.conf.json</tt>. See <tt>grunt/local.conf.default.json</tt> as a template.<br />
<br />
Hint: use <tt>grunt show-copy --outfile grunt/local.conf.json</tt> to generate <tt>grunt/local.conf.json</tt>.<br />
<br />
If you want to use the proxy function of the appserver, at least the server-variable needs to be configured.<br />
<br />
<pre><br />
{<br />
"appserver": {<br />
"server": "http://url.to.your.ser/appsuite/",<br />
"verbose": ["local:error"]<br />
}<br />
}<br />
</pre><br />
<br />
== Accessing the UI ==<br />
<br />
You can run the appserver with the connect grunt task:<br />
<br />
<pre><br />
grunt connect:server:keepalive<br />
</pre><br />
<br />
or in combination with the watch task:<br />
<br />
<pre><br />
grunt connect watch<br />
</pre><br />
<br />
You can now access the ui via <tt>http://localhost:8337/appsuite/</tt>.</div>J.ohny.bhttps://oxpedia.org/wiki/index.php?title=AppSuite:GettingStarted_7.6.0&diff=17689AppSuite:GettingStarted 7.6.02014-06-02T20:08:24Z<p>J.ohny.b: /* Testing an App */</p>
<hr />
<div><div class="title">Getting Started</div><br />
<br />
<div style="float:right; padding: 10px;">[[File:OX_AppSuite_UI_Development_Workflow.png]]</div><br />
<br />
__TOC__<br />
<br />
Hello and welcome to the world of OX App Suite development. This document is designed to get you started with developing your first app for OX App Suite as quickly and simply as possible. However, along the way we will also tempt you to learn more by linking to some more in-depth documentation about the various topics we cover. <br />
<br />
<div style="clear: both"></div><br />
<br />
== Installing the Development Tools ==<br />
<br />
First, you need to install some tools which are necessary for UI development.<br />
<br />
$ npm install -g grunt-cli bower yo generator-ox-ui-module<br />
<br />
== Create a Workspace ==<br />
<br />
All your app development can take place inside one working directory. The examples will assume you have created a directory named <tt>myapp</tt> inside your home directory:<br />
<br />
$ mkdir ~/myapp<br />
$ cd ~/myapp<br />
<br />
It doesn't need to be in your home directory, and the actual name should reflect the name of your app. The main point is that all commands will be executed in this directory and all paths will be relative to this directory from now on.<br />
<br />
Now, you can generate a grunt configuration using:<br />
<br />
$ yo ox-ui-module<br />
<br />
The source code of your app will reside in the subfolder named <tt>apps</tt>. To avoid name collisions please pick a unique subfolder inside that. The easiest and recommended way is to use a domain name that you own. Typically, the domain elements are reversed, like in Java. <tt>example.com</tt> becomes <tt>com.example</tt>:<br />
<br />
$ mkdir -p apps/com.example<br />
<br />
== Writing an App ==<br />
<br />
As an example, let's create the smallest possible app and test it. It requires only two files: <tt>apps/com.example/register.js</tt> for the source code of the app:<br />
<br />
<pre class="language-javascript"><br />
define('com.example/register', function () {<br />
'use strict';<br />
alert('Hello, World!');<br />
});<br />
</pre><br />
<br />
and <tt>apps/com.example/manifest.json</tt> for the [[AppSuite:UI_manifests_explained | manifest]] which tells the UI that your app exists and what to do with it:<br />
<br />
<pre class="language-javascript">{ "namespace": "core" }</pre><br />
<br />
== Building an App ==<br />
<br />
The source code of your app can't be used by OX App Suite as it. It first has to be processed by the [[AppSuite:UI_build_system | build system]]. This step will check the source code for syntax errors, compress it, and depending on the structure of your code, many other things. The processed code is then written to a directory named <tt>build</tt> by default. Start the build with this command:<br />
<br />
$ grunt<br />
<br />
If your editor supports it, you can configure it to call the build system after every file save. Take care to call it from the top directory of your app's workspace, not from the directory of the saved file.<br />
<br />
== Testing an App ==<br />
<br />
The freshly built code can now be tested. Instead of uploading your code to an OX App Suite server, you can use the [[AppSuite:Appserver|appserver]] proxy to inject your code into the UI code of any existing OX App Suite installation. For example, to start <tt>appserver</tt> using [http://ox.io/ ox.io] as the server, you will need a local configuration pointing to that server. You can generate it with this command:<br />
<br />
$ grunt show-config:local --output grunt/local.conf.json<br />
<br />
Open up the file <tt>grunt/local.conf.json</tt> in your editor and add <tt><nowiki>"https://ox.io/appsuite/"</nowiki></tt> to the <tt>server</tt> setting of the appserver section. Then start the development server:<br />
<br />
$ grunt dev<br />
<br />
This command will serve your app from the local directory <tt>build</tt>, and get everything else from the URL specified in the <tt>server</tt> setting.<br />
<br />
Once appserver is running, you can access OX App Suite by opening your browser using this address:<br />
<br />
http://localhost:8337/appsuite<br />
<br />
After logging in, the app should be loaded and display the <tt>alert</tt> message.<br />
<br />
=== Development cycle ===<br />
<br />
Once you are sure that your setup works, you can extend the example and write the actual code for your app. The <tt>dev</tt> task will detect any changes and rebuild your app and even reload all browsers connected to<br />
<tt>http://localhost:8337/appsuite</tt>.<br />
<br />
While developing always keep in mind, that there is an [[AppSuite:Debugging_the_UI | article about debugging the user interface]] which helps you avoid and fix typical errors.<br />
<br />
== Packaging ==<br />
<br />
Together with our ox-ui-module generator (from version 0.8.0), we provide you with generators for packaging information.<br />
<br />
=== Generating a source tarball ===<br />
<br />
There is a grunt tasks to create source packages. You can use it to create a tar.gz file containing all sources needed to build the project. This source package should contain all dependencies that are installed during <tt>bower install</tt> and <tt>npm install</tt> (basically containing your <tt>bower_components</tt> and <tt>node_modules</tt> directories). To generate this file, run:<br />
<br />
$ grunt dist:source --include-dependencies<br />
<br />
This file (can be found in the <tt>dist/</tt> directory) together with the distribution specific packaging information will be needed to build the package.<br />
<br />
=== RPM packages ===<br />
<br />
In order to generate a spec file for your project, run:<br />
<br />
$ yo ox-ui-module:rpm-pkg<br />
<br />
Will generate a spec file, with some default values read from your <tt>package.json</tt> file. So you might want to make this file as complete as possible. However, yeoman will ask you about the information missing.<br />
<br />
''Please remember, that the generated spec file is only a scaffold for a real one. It will work for very simple cases, but as soon as your package gets more complicated, you will have to adjust it to your needs.''<br />
<br />
Once you have the spec file in your project, you can use<br />
<br />
$ grunt dist:rpm<br />
<br />
You can then use tools like rpmbuild to build the rpm file. One example would be:<br />
<br />
$ mkdir -p ~/rpmbuild/{SOURCES,SPECS,BUILD,RPMS}<br />
cp dist/*.orig.tar.gz ~/rpmbuild/SOURCES/<br />
cp dist/*.spec ~/rpmbuild/SPECS/<br />
rpmbuild -bb ~/rpmbuild/SPECS/*.spec<br />
<br />
=== DEB packages ===<br />
<br />
Creating packages for the Debian distribution works similar to rpm packages. Just run:<br />
<br />
$ yo ox-ui-module:deb-pkg<br />
<br />
This will generate a debian directory containing all needed files. Some default values will be read from your <tt>package.json</tt> file. So you might want to make this file as complete as possible. However, yeoman will ask you about the information missing.<br />
<br />
''Please remember, that the generated spec file is only a scaffold for a real one. It will work for very simple cases, but as soon as your package gets more complicated, you will have to adjust it to your needs.''<br />
<br />
Once you have a debian/ directory for your project, you can go on.<br />
To enable some grunt tasks to help you, you need to install (with npm install) <tt>grunt-contrib-compress</tt> and <tt>grunt-exec</tt>. Now you can run:<br />
<br />
$ grunt dist:dpkg-source<br />
<br />
You can now use the files in the dist/ directory to build your packages. A simple example would look like:<br />
<br />
$ cd dist/unicorn-0.0.1/<br />
dpkg-buildpackage -b<br />
<br />
== Further Reading ==<br />
* Congratulations you have just built your first app for OX App Suite, but please keep in mind that there are [[AppSuite:Developing for the UI#What_can_i_build.3F | quite a few options]] for developing for OX App Suite.<br />
* More information on the build system can be found on github:<br />
** [https://github.com/Open-Xchange-Frontend/shared-grunt-config See all available grunt tasks]<br />
** [https://github.com/Open-Xchange-Frontend/generator-ox-ui-module Documentation of all generator tasks]<br />
* If you're stuck somewhere, the article about [[AppSuite:Debugging_the_UI | debugging the UI]] might help you.<br />
* You can read this to get a better overview of [[AppSuite:Developing for the UI | developing the user inferface]].</div>J.ohny.b