# wiki

name: TracLinks
author: trac
ipnr: 127.0.0.1
text:
  = Trac Links =
  [[TracGuideToc]]

  TracLinks are a fundamental feature of Trac, because they allow easy hyperlinking between the various entities in the system—such as tickets, reports, changesets, Wiki pages, milestones, and source files—from anywhere WikiFormatting is used.

  TracLinks are generally of the form '''type:id''' (where ''id'' represents the
  number, name or path of the item) though some frequently used kinds of items
  also have short-hand notations.

  == Where to use TracLinks ==
  You can use TracLinks in:

   * Source code (Subversion) commit messages
   * Wiki pages
   * Full descriptions for tickets, reports and milestones

  and any other text fields explicitly marked as supporting WikiFormatting.

  == Overview ==

  ||= Wiki Markup =||= Display =||
  {{{#!td
   Wiki pages :: `CamelCase` or `wiki:CamelCase`
   Parent page :: `[..]`
   Tickets :: `#1` or `ticket:1`
   Ticket comments :: `comment:1:ticket:2`
   Reports :: `{1}` or `report:1`
   Milestones :: `milestone:1.0`
   Attachment :: `attachment:example.tgz` (for current page attachment), `attachment:attachment.1073.diff:ticket:944` (absolute path)
   Changesets :: `r1`, `[1]`, `changeset:1` or (restricted) `[1/trunk]`, `changeset:1/trunk`
   Revision log :: `r1:3`, `[1:3]` or `log:@1:3`, `log:trunk@1:3`, `[2:5/trunk]`
   Diffs :: `diff:@1:3`, `diff:plugins/0.12/mercurial-plugin@9128:9953`,
            `diff:tags/trac-0.9.2/wiki-default//tags/trac-0.9.3/wiki-default` 
            or `diff:trunk/trac@3538//sandbox/vc-refactoring@3539`
   Files :: `source:trunk/COPYING`, `source:/trunk/COPYING@200` (at version 200), `source:/trunk/COPYING@200#L25` (at version 200, line 25)
  }}}
  {{{#!td
   Wiki pages :: CamelCase or wiki:CamelCase
   Parent page :: [..]
   Tickets :: #1 or ticket:1
   Ticket comments :: comment:1:ticket:2 
   Reports :: {1} or report:1
   Milestones :: milestone:1.0
   Attachment :: attachment:example.tgz (for current page attachment), attachment:attachment.1073.diff:ticket:944 (absolute path)
   Changesets :: r1, [1], changeset:1 or (restricted) [1/trunk], changeset:1/trunk
   Revision log :: r1:3, [1:3] or log:@1:3, log:trunk@1:3, [2:5/trunk]
   Diffs :: diff:@1:3, diff:plugins/0.12/mercurial-plugin@9128:9953,
            diff:tags/trac-0.9.2/wiki-default//tags/trac-0.9.3/wiki-default 
            or diff:trunk/trac@3538//sandbox/vc-refactoring@3539
   Files :: source:trunk/COPYING, source:/trunk/COPYING@200 (at version 200), source:/trunk/COPYING@200#L25 (at version 200, line 25)
  }}}

  '''Note:''' The wiki:CamelCase form is rarely used, but it can be convenient to refer to
  pages whose names do not follow WikiPageNames rules, i.e., single words,
  non-alphabetic characters, etc. See WikiPageNames for more about features specific
  to links to Wiki page names.


  {{{#!table class=""
  |||| Trac links using the full (non-shorthand) notation can also be given a custom link title like this: ||
  {{{#!td
  {{{
  [ticket:1 This is a link to ticket number one] or
  [[ticket:1|This is another link to ticket number one]].
  }}}
  }}}
  {{{#!td
  [ticket:1 This is a link to ticket number one] or
  [[ticket:1|This is another link to ticket number one]].
  }}}
  |--------------------------------------------------------------------------------------
  |||| If the title is omitted, only the id (the part after the colon) is displayed:  ||
  {{{#!td
  {{{
  [ticket:1] or [[ticket:2]]
  }}}
  }}}
  {{{#!td
  [ticket:1] or [[ticket:2]]
  }}}
  |--------------------------------------------------------------------------------------
  |||| `wiki` is the default if the namespace part of a full link is omitted:  || 
  {{{#!td
  {{{
  [SandBox the sandbox] or
  [[SandBox|the sandbox]]
  }}}
  }}}
  {{{#!td
  [SandBox the sandbox] or
  [[SandBox|the sandbox]]
  }}}
  |--------------------------------------------------------------------------------------
  |||| The short form ''realm:target'' can also be wrapped within a <...> pair, [[br]] which allow for arbitrary characters (i.e. anything but >)  ||
  {{{#!td
  {{{
  <wiki:Strange(page@!)>
  }}}
  }}}
  {{{#!td
  <wiki:Strange(page@!)>
  }}}
  }}}

  TracLinks are a very simple idea, but actually allow quite a complex network of information. In practice, it's very intuitive and simple to use, and we've found the "link trail" extremely helpful to better understand what's happening in a project or why a particular change was made.


  == Advanced use of TracLinks ==

  === Relative links ===

  To create a link to a specific anchor in a page, use '#':
  {{{
   [#Relativelinks relative links] or [[#Relativelinks|relative links]]
  }}}
  Displays:
    [#Relativelinks relative links] or [[#Relativelinks|relative links]]

  Hint: when you move your mouse over the title of a section, a '¶' character will be displayed. This is a link to that specific section and you can use this to copy the `#...` part inside a relative link to an anchor.

  To create a link to a [trac:SubWiki SubWiki]-page just use a '/':
  {{{
   WikiPage/SubWikiPage or ./SubWikiPage
  }}}

  To link from a [trac:SubWiki SubWiki] page to a parent, simply use a '..':
  {{{
    [..] or [[..]]
  }}}
    [..] or [[..]]

  To link from a [trac:SubWiki SubWiki] page to a [=#sibling sibling] page, use a '../':
  {{{
    [../Sibling see next sibling] or [[../Sibling|see next sibling]]
  }}}
    [../Sibling see next sibling] or [[../Sibling|see next sibling]]

  But in practice you often won't need to add the `../` prefix to link to a sibling page.
  For resolving the location of a wiki link, it's the target page closest in the hierarchy
  to the page where the link is written which will be selected. So for example, within 
  a sub-hierarchy, a sibling page will be targeted in preference to a toplevel page.
  This makes it easy to copy or move pages to a sub-hierarchy by [[WikiNewPage#renaming|renaming]] without having to adapt the links.

  In order to link explicitly to a [=#toplevel toplevel] Wiki page,
  use the `wiki:/` prefix.
  Be careful **not** to use the `/` prefix alone, as this corresponds to the
  [#Server-relativelinks] syntax and with such a link you will lack the `/wiki/` 
  part in the resulting URL.

  ''(Changed in 0.11)'' Note that in Trac 0.10, using e.g. `[../newticket]`  may have worked for linking to the `/newticket` top-level URL, but since 0.11, such a link will stay in the wiki namespace and therefore link to a sibling page. 
  See [#Server-relativelinks] for the new syntax.

  === InterWiki links ===

  Other prefixes can be defined freely and made to point to resources in other Web applications. The definition of those prefixes as well as the URLs of the corresponding Web applications is defined in a special Wiki page, the InterMapTxt page. Note that while this could be used to create links to other Trac environments, there's a more specialized way to register other Trac environments which offers greater flexibility.

  === InterTrac links ===

  This can be seen as a kind of InterWiki link specialized for targeting other Trac projects.

  Any type of Trac link can be written in one Trac environment and actually refer to resources in another Trac environment. All that is required is to prefix the Trac link with the name of the other Trac environment followed by a colon. The other Trac environment must be registered on the InterTrac page. 

  A distinctive advantage of InterTrac links over InterWiki links is that the shorthand form of Trac links (e.g. `{}`, `r`, `#`) can also be used. For example if T was set as an alias for Trac, links to Trac tickets can be written #T234, links to Trac changesets can be written [trac 1508].
  See InterTrac for the complete details. 

  === Server-relative links ===

  It is often useful to be able to link to objects in your project that
  have no built-in Trac linking mechanism, such as static resources, `newticket`,
  a shared `/register` page on the server, etc.

  To link to resources inside the project, use either an absolute path from the project root, 
  or a relative link from the URL of the current page (''Changed in 0.11''):

  {{{
  [/newticket Create a new ticket] or [[//newticket|Create a new ticket]]
  [/ home] or [[/|home]]
  }}}

  Display: [/newticket Create a new ticket] or [[//newticket|Create a new ticket]]
  [/ home] or [[/|home]]

  To link to another location on the server (possibly outside the project but on the same host), use the `//` prefix (''Changed in 0.11''):

  {{{
  [//register Register Here] or [[//register|Register Here]]
  }}}

  Display: [//register Register Here] or [[//register|Register Here]]

  === Quoting space in TracLinks ===

  Immediately after a TracLinks prefix, targets containing space characters should
  be enclosed in a pair of quotes or double quotes.
  Examples:
   * !wiki:"The whitespace convention"
   * !attachment:'the file.txt' or
   * !attachment:"the file.txt" 
   * !attachment:"the file.txt:ticket:123" 

  Note that by using [trac:WikiCreole] style links, it's quite natural to write links containing spaces:
   * ![[The whitespace convention]]
   * ![[attachment:the file.txt]]

  === Escaping Links ===

  To prevent parsing of a !TracLink, you can escape it by preceding it with a '!' (exclamation mark).
  {{{
   !NoLinkHere.
   ![42] is not a link either.
  }}}

  Display:
   !NoLinkHere.
   ![42] is not a link either.


  === Parameterized Trac links ===

  Many Trac resources have more than one way to be rendered, depending on some extra parameters. For example, a Wiki page can accept a `version` or a `format` parameter, a report can make use of dynamic variables, etc.

  Trac links can support an arbitrary set of parameters, written in the same way as they would be for the corresponding URL. Some examples:
   - `wiki:WikiStart?format=txt`
   - `ticket:1?version=1`
   - `[/newticket?component=module1 create a ticket for module1]`
   - `[/newticket?summary=Add+short+description+here create a ticket with URL with spaces]`


  == TracLinks Reference ==
  The following sections describe the individual link types in detail, as well as notes on advanced usage of links.

  === attachment: links ===

  The link syntax for attachments is as follows:
   * !attachment:the_file.txt creates a link to the attachment the_file.txt of the current object
   * !attachment:the_file.txt:wiki:MyPage creates a link to the attachment the_file.txt of the !MyPage wiki page
   * !attachment:the_file.txt:ticket:753 creates a link to the attachment the_file.txt of the ticket 753

  Note that the older way, putting the filename at the end, is still supported: !attachment:ticket:753:the_file.txt.

  If you'd like to create a direct link to the content of the attached file instead of a link to the attachment page, simply use `raw-attachment:` instead of `attachment:`.

  This can be useful for pointing directly to an HTML document, for example. Note that for this use case, you'd have to allow the web browser to render the content by setting `[attachment] render_unsafe_content = yes` (see TracIni#attachment-section). Caveat: only do that in environments for which you're 100% confident you can trust the people who are able to attach files, as otherwise this would open up your site to [wikipedia:Cross-site_scripting cross-site scripting] attacks.

  See also [#export:links].

  === comment: links ===

  When you're inside a given ticket, you can simply write e.g. !comment:3 to link to the third change comment.
  It is possible to link to a comment of a specific ticket from anywhere using one of the following syntax:
   - `comment:3:ticket:123` 
   - `ticket:123#comment:3` (note that you can't write `#123#!comment:3`!)
  It is also possible to link to the ticket's description using one of the following syntax:
   - `comment:description` (within the ticket)
   - `comment:description:ticket:123`
   - `ticket:123#comment:description`

  === htdocs: links ===

  Use `htdocs:path/to/file` to reference files in the `htdocs` directory of the Trac environment, the [TracEnvironment#DirectoryStructure web resource directory].

  === query: links ===

  See TracQuery#UsingTracLinks and [#ticket:links].

  === search: links ===

  See TracSearch#SearchLinks 

  === ticket: links ===
   ''alias:'' `bug:`

  Besides the obvious `ticket:id` form, it is also possible to specify a list of tickets or even a range of tickets instead of the `id`. This generates a link to a custom query view containing this fixed set of tickets.

  Example: 
   - `ticket:5000-6000`
   - `ticket:1,150`

  ''(since Trac 0.11)''

  === timeline: links ===

  Links to the timeline can be created by specifying a date in the ISO:8601 format. The date can be optionally followed by a time specification. The time is interpreted as being UTC time, but alternatively you can specify your local time, followed by your timezone if you don't want to compute the UTC time.

  Examples:
   - `timeline:2008-01-29`
   - `timeline:2008-01-29T15:48`
   - `timeline:2008-01-29T15:48Z`
   - `timeline:2008-01-29T16:48+01`

  ''(since Trac 0.11)''

  === wiki: links ===

  See WikiPageNames and [#QuotingspaceinTracLinks quoting space in TracLinks] above. It is possible to create a link to a specific page revision using the syntax WikiStart@1.

  === Version Control related links ===

  It should be noted that multiple repository support works by creating a kind of virtual namespace for versioned files in which the toplevel folders correspond to the repository names. Therefore, in presence of multiple repositories, a ''/path'' specification in the syntax of links detailed below should start with the name of the repository. If omitted, the default repository is used. In case a toplevel folder of the default repository has the same name as a repository, the latter "wins". One can always access such folder by fully qualifying it (the default repository can be an alias of a named repository, or conversely, it is always possible to create an alias for the default repository, ask your Trac administrator).

  For example, `source:/trunk/COPYING` targets the path `/trunk/COPYING` in the default repository, whereas `source:/projectA/trunk/COPYING` targets the path `/trunk/COPYING` in the repository named `projectA`. This can be the same file if `'projectA'` is an alias to the default repository or if `''` (the default repository) is an alias to `'projectA'`.

  ==== source: links ====
   ''aliases:'' `browser:`, `repos:`

  The default behavior for a source:/some/path link is to open the browser in that directory directory 
  if the path points to a directory or to show the latest content of the file.

  It's also possible to link directly to a specific revision of a file like this:
   - `source:/some/file@123` - link to the file's revision 123
   - `source:/some/file@head` - link explicitly to the latest revision of the file

  If the revision is specified, one can even link to a specific line number:
   - `source:/some/file@123#L10`
   - `source:/tag/0.10@head#L10`

  Finally, one can also highlight an arbitrary set of lines:
   - `source:/some/file@123:10-20,100,103#L99` - highlight lines 10 to 20, and lines 100 and 103.
     ''(since 0.11)''

  Note that in presence of multiple repositories, the name of the repository is simply integrated in the path you specify for `source:` (e.g. `source:reponame/trunk/README`). ''(since 0.12)''

  ==== export: links ====

  To force the download of a file in the repository, as opposed to displaying it in the browser, use the `export` link.  Several forms are available:
   * `export:/some/file` - get the HEAD revision of the specified file
   * `export:123:/some/file` - get revision 123 of the specified file
   * `export:/some/file@123` - get revision 123 of the specified file

  This can be very useful for displaying XML or HTML documentation with correct stylesheets and images, in case that has been checked in into the repository. Note that for this use case, you'd have to allow the web browser to render the content by setting `[browser] render_unsafe_content = yes` (see TracIni#browser-section), otherwise Trac will force the files to be downloaded as attachments for security concerns. 

  If the path is to a directory in the repository instead of a specific file, the source browser will be used to display the directory (identical to the result of `source:/some/dir`).

  ==== log: links ====

  The `log:` links are used to display revision ranges. In its simplest form, it can link to the latest revisions of the specified path, but it can also support displaying an arbitrary set of revisions.
   - `log:/` - the latest revisions starting at the root of the repository
   - `log:/trunk/tools` - the latest revisions in `trunk/tools`
   - `log:/trunk/tools@10000` - the revisions in `trunk/tools` starting from  revision 10000
   - `log:@20788,20791:20795` - list revision 20788 and the revisions from 20791 to 20795 
   - `log:/trunk/tools@20788,20791:20795` - list revision 20788 and the revisions from 20791 to 20795 which affect the given path

  There are short forms for revision ranges as well:
   - `[20788,20791:20795]`
   - `[20788,20791:20795/trunk/tools]`
   - `r20791:20795` (but not `r20788,20791:20795` nor `r20791:20795/trunk`)

  Finally, note that in all of the above, a revision range can be written either as `x:y` or `x-y`.

  In the presence of multiple repositories, the name of the repository should be specified as the first part of the path, e.g. `log:repos/branches` or `[20-40/repos]`.

  ----
  See also: WikiFormatting, TracWiki, WikiPageNames, InterTrac, InterWiki
   


name: PageTemplates
author: trac
ipnr: 127.0.0.1
text:
  = Wiki Page Templates = 

    ''(since [http://trac.edgewall.org/milestone/0.11 0.11])''

  The default content for a new wiki page can be chosen from a list of page templates. 

  That list is made up from all the existing wiki pages having a name starting with ''PageTemplates/''.
  The initial content of a new page will simply be the content of the chosen template page, or a blank page if the special ''(blank page)'' entry is selected. When there's actually no wiki pages matching that prefix, the initial content will always be the blank page and the list selector will not be shown (i.e. this matches the behavior we had up to now).

  To create a new template, simply create a new page having a name starting with ''PageTemplates/''.

  (Hint: one could even create a ''!PageTemplates/Template'' for facilitating the creation of new templates!)

  After you have created your new template, a drop-down selection box will automatically appear on any new wiki pages that are created.  By default it is located on the right side of the 'Create this page' button.

  Available templates: 
  [[TitleIndex(PageTemplates/)]]
  ----
  See also: TracWiki


name: TracStartingPage
author: mnehme
ipnr: 212.36.209.181
text:
  = Welcome to Trac 0.12.3 =
  [wiki:TracStartingPage Documentation for this ticketing system] -- Documentation for Trac bug-tracking and ticket-tracking system

  = Welcome to Trac 0.12.3 =

  Trac is a '''minimalistic''' approach to '''web-based''' management of
  '''software projects'''. Its goal is to simplify effective tracking and handling of software issues, enhancements and overall progress.

  All aspects of Trac have been designed with the single goal to 
  '''help developers write great software''' while '''staying out of the way'''
  and imposing as little as possible on a team's established process and
  culture.

  As all Wiki pages, this page is editable, this means that you can
  modify the contents of this page simply by using your
  web-browser. Simply click on the "Edit this page" link at the bottom
  of the page. WikiFormatting will give you a detailed description of
  available Wiki formatting commands.

  "[wiki:TracAdmin trac-admin] ''yourenvdir'' initenv" created
  a new Trac environment, containing a default set of wiki pages and some sample
  data. This newly created environment also contains 
  [wiki:TracGuide documentation] to help you get started with your project.

  You can use [wiki:TracAdmin trac-admin] to configure
  [http://trac.edgewall.org/ Trac] to better fit your project, especially in
  regard to ''components'', ''versions'' and ''milestones''. 


  TracGuide is a good place to start.

  Enjoy! [[BR]]
  ''The Trac Team''

  == Starting Points ==

   * TracGuide --  Built-in Documentation
   * [http://trac.edgewall.org/ The Trac project] -- Trac Open Source Project
   * [http://trac.edgewall.org/wiki/TracFaq Trac FAQ] -- Frequently Asked Questions
   * TracSupport --  Trac Support

  For a complete list of local wiki pages, see TitleIndex.


name: TracStartingPage
author: mnehme
ipnr: 212.36.209.181
text:
  = Welcome to the Cedrus Development Environment Wiki =

  Contents:
  *[wiki:TracStartingPage Documentation for this ticketing system] -- Documentation for Trac bug-tracking and ticket-tracking system

  = Welcome to Trac 0.12.3 =

  Trac is a '''minimalistic''' approach to '''web-based''' management of
  '''software projects'''. Its goal is to simplify effective tracking and handling of software issues, enhancements and overall progress.

  All aspects of Trac have been designed with the single goal to 
  '''help developers write great software''' while '''staying out of the way'''
  and imposing as little as possible on a team's established process and
  culture.

  As all Wiki pages, this page is editable, this means that you can
  modify the contents of this page simply by using your
  web-browser. Simply click on the "Edit this page" link at the bottom
  of the page. WikiFormatting will give you a detailed description of
  available Wiki formatting commands.

  "[wiki:TracAdmin trac-admin] ''yourenvdir'' initenv" created
  a new Trac environment, containing a default set of wiki pages and some sample
  data. This newly created environment also contains 
  [wiki:TracGuide documentation] to help you get started with your project.

  You can use [wiki:TracAdmin trac-admin] to configure
  [http://trac.edgewall.org/ Trac] to better fit your project, especially in
  regard to ''components'', ''versions'' and ''milestones''. 


  TracGuide is a good place to start.

  Enjoy! [[BR]]
  ''The Trac Team''

  == Starting Points ==

   * TracGuide --  Built-in Documentation
   * [http://trac.edgewall.org/ The Trac project] -- Trac Open Source Project
   * [http://trac.edgewall.org/wiki/TracFaq Trac FAQ] -- Frequently Asked Questions
   * TracSupport --  Trac Support

  For a complete list of local wiki pages, see TitleIndex.


name: TracStartingPage
author: mnehme
ipnr: 212.36.209.181
text:
  = Welcome to Trac 0.12.3 =

  Trac is a '''minimalistic''' approach to '''web-based''' management of
  '''software projects'''. Its goal is to simplify effective tracking and handling of software issues, enhancements and overall progress.

  All aspects of Trac have been designed with the single goal to 
  '''help developers write great software''' while '''staying out of the way'''
  and imposing as little as possible on a team's established process and
  culture.

  As all Wiki pages, this page is editable, this means that you can
  modify the contents of this page simply by using your
  web-browser. Simply click on the "Edit this page" link at the bottom
  of the page. WikiFormatting will give you a detailed description of
  available Wiki formatting commands.

  "[wiki:TracAdmin trac-admin] ''yourenvdir'' initenv" created
  a new Trac environment, containing a default set of wiki pages and some sample
  data. This newly created environment also contains 
  [wiki:TracGuide documentation] to help you get started with your project.

  You can use [wiki:TracAdmin trac-admin] to configure
  [http://trac.edgewall.org/ Trac] to better fit your project, especially in
  regard to ''components'', ''versions'' and ''milestones''. 


  TracGuide is a good place to start.

  Enjoy! [[BR]]
  ''The Trac Team''

  == Starting Points ==

   * TracGuide --  Built-in Documentation
   * [http://trac.edgewall.org/ The Trac project] -- Trac Open Source Project
   * [http://trac.edgewall.org/wiki/TracFaq Trac FAQ] -- Frequently Asked Questions
   * TracSupport --  Trac Support

  For a complete list of local wiki pages, see TitleIndex.


name: WikiStart
author: mnehme
ipnr: 212.36.209.181
text:
  = Welcome to the Cedrus Development Environment Wiki =

  == About ==
  Cedrus Development Environment is a platform to allow easy and fast development of mobile and web Apps. "Fast" means 20-30x faster than normal development.
  Though primarily targeting developers, CDE can also be used by product managers to speedily create prototypes with zero-coding. These prototypes can be used as both specs and first release, and speed up the production process by breaking down coding into simple tasks.

  == How it works ==
   * '''Fast design process:''' design is done via simple drag-and-drop.
   * '''Fast coding:''' CDE makes it easy and natural to break down code into small self-contained components, reducing complexity, while making coding very fast. 
   * '''Cloud-enabled:''' Automatically creates server-side databases, automatically creates access functions for you to use which even work offline, and provide intelligent syncing abilities.
   * '''Multi-platform:''' CDE creates apps written in Ajax/HTML5 for their front-ends, and PHP for their back-ends. As a result, it can be used with Phonegap to immediately port the App to all platforms.
   * '''Iterative prototyping:''' Naturally encourages iterative development and MVP, because iterating is blindingly fast.
   * '''Infinitely extensible:''' CDE of course includes the ability to actually code, so anything that can be done by coding can be done with CDE.

  == Limitations ==
  CDE works for most Apps, except for media-rich gaming Apps. For the latter, you might want to consider using a platform such as Unity3D.

  == Starting Points ==
   * [wiki:TracStartingPage Documentation for this ticketing system] -- Documentation for Trac bug-tracking and ticket-tracking system


name: WikiStart
author: mnehme
ipnr: 212.36.209.181
text:
  = Welcome to the Cedrus Development Environment Wiki =

  == About ==
  Cedrus Development Environment is a platform to allow easy and fast development of mobile and web Apps. "Fast" means 20-30x faster than normal development.
  Though primarily targeting developers, CDE can also be used by product managers to speedily create prototypes with zero-coding. These prototypes can be used as both specs and first release, and speed up the production process by breaking down coding into simple tasks.

  == How it works ==
   * '''Fast design process:''' design is done via simple drag-and-drop.
   * '''Fast coding:''' CDE makes it easy and natural to break down code into small self-contained components, reducing complexity, while making coding very fast. 
   * '''Cloud-enabled:''' Automatically creates server-side databases, automatically creates access functions for you to use which even work offline, and provide intelligent syncing abilities.
   * '''Multi-platform:''' CDE creates apps written in Ajax/HTML5 for their front-ends, and PHP for their back-ends. As a result, it can be used with Phonegap to immediately port the App to all platforms.
   * '''Iterative prototyping:''' Naturally encourages iterative development and MVP, because iterating is blindingly fast.
   * '''Infinitely extensible:''' CDE of course includes the ability to actually code, so anything that can be done by coding can be done with CDE.

  == Limitations ==
  CDE works for most Apps, except for media-rich gaming Apps. For the latter, you might want to consider using a platform such as Unity3D.

  == Starting Points ==
   * [wiki:FAQ Trac: FAQ] -- Cedrus Development Environment FAQ
   * [wiki:TracStartingPage Trac: the bug-reporting system] -- You can report bugs and request features using Trac. This links to the Trac documentation.


name: FAQ
author: mnehme
ipnr: 212.36.209.181
text:
  = Designing Views =
  == How can I create an element that resizes based on screen-size? ==
  This is not recommended. In order to make sure fonts are legible on all platforms, you should create one version of a view per platform. (For example, instead of creating a view called "Menu" you should create one called "Menu/iphone", then copy it into another called "Menu/ipad" and adjust as necessary).
  The differences in device screen-sizes are so great that if you do not do this, your text will not be legible.

  ==== But then I will have to duplicate scripts! ====
  No: simply create a view called "Menu" and write the script within that view. The views "Menu/iphone" and "Menu/ipad" will inherit from it. So any script that you put in "Menu" will apply to "Menu/iphone", and any design elements you put in "Menu" will be visible as a background to the view "Menu/iphone"

  ==== But I still want to do it. ====
  If you insist:
  Alternative 1: Create a script fired by the viewOpenComplete event, which automatically resizes the div based on screen size. For this you’ll find it helpful to give the div a classname via the fill dialogue box.
  Alternative 2: 
   * Create the app for one screen size. In the following, we will assume it is the ipad screen size.
   * Once it is created, download the html, and edit the scale in the viewport meta-tag. Set it as follows: <meta name="viewport" id="viewport" content="width=device-width, initial-scale=0.42, maximum-scale=0.42, minimum-scale=0.42" />. This will create an html file that is resized to fit in the iphone. You may want to adjust the 0.42 factor slightly
   * The above will suffice to resize to iphone size. But in order for it to look good, you’ll also want to center it. Do so by adding <div style='position:relative;left:-5px;width:1024px;margin:0 auto;'>  around the entire content (but within the <body> tags) (An explanation of why his works is provided at [http://buildinternet.com/2008/11/how-to-center-a-website-with-css/])
   * Use the new html file to create a separate binary for the iphone. (Apple App Store Universal apps are generally a bad idea: they are useless since high ranking on ipad does not help iphone rank, or vice-versa. Furthermore, they force you to price your ipad app at the same price as the iphone one, even though willingness to pay is generally different.)

  == How do I align/distribute elements? ==
  First group them (by rapidly clicking on one after the other. Then select the group (it appears as a beige-bordered transparent container). Finally, open the "Position" menu and select a menu option. Optionally, you can ungroup with the "Ungroup" button (upper-right).

  = Creating Tables and Dialogue Boxes =
  == How can I create a database table? ==
  Simply create a dialogue box by following the instructions in the "Data" tab.
  Once you click "Go!", you will be taken to a design interface for the dialogue box.
   * Add graphic elements as desired.
   * Add user-input elements: one per intended table column. You can create these elements via the dialogue box in the menu: "Shapes, Images, Buttons" -> "User Input"
   * Create the "ok" button: create another user input element and give it the type “submit” and the name “_submit”, and the default value “Ok” (or whatever other default value you want)
   * Create the cancel button: Create another user input element and give it the type “submit” and the name “_cancel”, and the default value “Cancel” (or whatever other default value you want)
  And you’re done! If the dialogue was called (for example) dialogues/cld_Restaurants_insert_table.php, then you can programmatically open it via: dialogues_cld_Restaurants_insert_table()
  Once the user clicks “Ok” on the dialogue box, the data is automatically stored to be sent to the remote database.
  Whenever you want to send, simply execute cld_Restaurants.sendPendingAjax() (This works even if you don’t have internet, it will simply do a smart sync once you get back online.) This step is separate in order to avoid causing bandwidth problems. Often users will make minor changes to data they just input.
  '''Best practice:''' on viewOpenComplete of the top view, use a setInterval call to execute cld_Restaurants.sendPendingAjax() once every 60000 milliseconds (and do the same for every table you create.) This way you will not have to manually execute sendPendingAjax() every time.

  == How can I create a dialogue box? ==
  Do it by creating a table, as described above.
  If you want to create a second dialogue box corresponding to the same table, here is how:
   * From any one of the tabs ("Design", "Data" or "Dynamic"), click on the name of the current view on the top left of the screen. This takes you to the view selection page.
   * Click on the "New Dialogue" button.
   * Design the dialogue box, as described above.

  = Scripting =
  == How can I programatically access the cloud data? ==
  ==== To access the data in the table ====
  For example, let us assume you named the dialogue box "Restaurants" (when creating the table). Your cloud object is called cld_Restaurants_insert_table.  Here is the script.
   * If you want the script to get the data from the remote database: use the cloud’s reload method:
  {{{
  var cfg = {};
  cfg.successFunc = function(list){
  // Do something with “list”
  // “list” is an array of records. Each record is an object of the form
  // { colName1: "value of column 1", colName2: "value of column 2", colName3: "value of column 3", [etc……]}
  };
  // cfg.where = [["location","=",theLocation], ["type","=","Lebanese"]]; // An optional where clause
  cld_Restaurants_insert_table.act['select'].reload(cfg);
  }}}
   * If you want the script, while waiting for the data to arrive from the remote database, to draw on the local cache: use the cloud’s data access method (getData) 
  {{{
  var cfg = {};
  cfg.successFunc = function(list){
  // Do something with “list”
  // “list” is an array of records. Each record is an object of the form
  // { colName1: "value of column 1", colName2: "value of column 2", colName3: "value of column 3", [etc……]}
  };
  // cfg.where = [["location","=",theLocation], ["type","=","Lebanese"]]; // An optional where clause
  var list0 = cld_Restaurants_insert_table.act['select'].getData(cfg);
  // Then do something with list0, which contains the local cache data
  }}}

  === To input data into the table ===
  The easiest way is to create a dialogue box (as described elsewhere) and do no scripting. If the dialogue was called (for example) dialogues/cld_Restaurants_insert_table.php, then you can programmatically open it via: dialogues_cld_Restaurants_insert_table()
  Once the user clicks “Ok” on the dialogue box, the data is automatically stored to be sent to the remote database.
  Whenever you want to send, simply execute cld_Restaurants.sendPendingAjax() (This works even if you don’t have internet, it will simply do a smart sync once you get back online.) This step is separate in order to avoid causing bandwidth problems. Often users will make minor changes to data they just input.

  Or you can do it programmatically as follows:
  {{{
  var data={};
  data["ajaxAction"] = 'insert'; /* Other options: update, delete, undelete, deleteFinal. You can also use select and selectdistinct, however the reload and getData methods do this a lot better */
  data["Column 1 name"] = aValue;
  data["Column 2 name"] = aValue2;
  data["Column 3 name"] = aValue3;
  cld_Restaurants.sendPending(data);//This stores the data in the local cache, ready to be sent to the server
  cld_Restaurants.sendPendingAjax();//This forces all pending data to be synced with the server.
  /* For advanced users: set up an event handler to handle event sendPendingFailure. Do this via the following code:
  addEvent(cld_Restaurants.events, 'sendPendingFailure', function(ev){
  // “ev” has the form: {type:"sendPendingFailure",item:i,attempts:numberOfattempts,status:statusCode,
  // code:ajaxStatusCode,msg:"The error message."}. 
  // The “attempts” field is not there for all types of errors.
  });
  */
  }}}

  Finally, a more complex option is to create a dialogue box as described, but instead of just simply letting it do its job, intercept the user input, modify it, and then send it on. Here is how:
   * Create the dialogue box as described above
   * Create a function to handle the user input – something like the below:
  {{{
  //function(ev){ // Commented out because, as you’ll see in a bit, it’s already there
        // Event handler for a dialogue box
        if(!(ev.value=='submit')) return;// If it’s not the submit button, do nothing
        if(!(ev.id== dialogues_cld_Restaurants_insert_table_1')) return; /* If it’s not instance 1 of the dialogue box dialogues_cld_Restaurants_insert_table then do nothing */
        
        var data = currentView.data; // “data” is now an associative array containing the dialogue box data
        
        // Now do something with the data
  /* For example, the below combines two dialogue box fields, called dialogueField1 and dialogueField2, into a single field */
        data['newField'] = data['dialogueField1']+" "+ data['dialogueField2'];
        delete data['dialogueField1'];
        delete data['dialogueField2'];

        currentView.data = data;// Once the data has been processed, overwrite the original data with the modified data
  //}// Commented out because, as you’ll see in a bit, it’s already there
  }}}
  Now all you need to do is go to the view in which the dialogue appears, go to the "Dynamics" tab, click on “Current View”, select the event dialogueButtonPress and copy-paste the function in there.
  WARNING: the lines “function(ev){” and the closing “}” are already there, so they shouldn’t be in your function!


name: WikiStart
author: mnehme
ipnr: 212.36.209.181
text:
  = Welcome to the Cedrus Development Environment Wiki =

  == About ==
  Cedrus Development Environment is a platform to allow easy and fast development of mobile and web Apps. "Fast" means 20-30x faster than normal development.
  Though primarily targeting developers, CDE can also be used by product managers to speedily create prototypes with zero-coding. These prototypes can be used as both specs and first release, and speed up the production process by breaking down coding into simple tasks.

  == How it works ==
   * '''Fast design process:''' design is done via simple drag-and-drop.
   * '''Fast coding:''' CDE makes it easy and natural to break down code into small self-contained components, reducing complexity, while making coding very fast. 
   * '''Cloud-enabled:''' Automatically creates server-side databases, automatically creates access functions for you to use which even work offline, and provide intelligent syncing abilities.
   * '''Multi-platform:''' CDE creates apps written in Ajax/HTML5 for their front-ends, and PHP for their back-ends. As a result, it can be used with Phonegap to immediately port the App to all platforms.
   * '''Iterative prototyping:''' Naturally encourages iterative development and MVP, because iterating is blindingly fast.
   * '''Infinitely extensible:''' CDE of course includes the ability to actually code, so anything that can be done by coding can be done with CDE.

  == Limitations ==
  CDE works for most Apps, except for media-rich gaming Apps. For the latter, you might want to consider using a platform such as Unity3D.

  == Starting Points ==
   * [wiki:FAQ Cedrus Development Environment FAQ] -- Frequently Asked Questions
   * [wiki:TracStartingPage Trac: the bug-reporting system] -- You can report bugs and request features using Trac. This links to the Trac documentation.


name: FAQ
author: mnehme
ipnr: 212.36.209.181
text:
  [[PageOutline]]

  = Designing Views =
  == How can I create an element that resizes based on screen-size? ==
  This is not recommended. In order to make sure fonts are legible on all platforms, you should create one version of a view per platform. (For example, instead of creating a view called "Menu" you should create one called "Menu/iphone", then copy it into another called "Menu/ipad" and adjust as necessary).
  The differences in device screen-sizes are so great that if you do not do this, your text will not be legible.

  ==== But then I will have to duplicate scripts! ====
  No: simply create a view called "Menu" and write the script within that view. The views "Menu/iphone" and "Menu/ipad" will inherit from it. So any script that you put in "Menu" will apply to "Menu/iphone", and any design elements you put in "Menu" will be visible as a background to the view "Menu/iphone"

  ==== But I still want to do it. ====
  If you insist:
  Alternative 1: Create a script fired by the viewOpenComplete event, which automatically resizes the div based on screen size. For this you’ll find it helpful to give the div a classname via the fill dialogue box.
  Alternative 2: 
   * Create the app for one screen size. In the following, we will assume it is the ipad screen size.
   * Once it is created, download the html, and edit the scale in the viewport meta-tag. Set it as follows: <meta name="viewport" id="viewport" content="width=device-width, initial-scale=0.42, maximum-scale=0.42, minimum-scale=0.42" />. This will create an html file that is resized to fit in the iphone. You may want to adjust the 0.42 factor slightly
   * The above will suffice to resize to iphone size. But in order for it to look good, you’ll also want to center it. Do so by adding <div style='position:relative;left:-5px;width:1024px;margin:0 auto;'>  around the entire content (but within the <body> tags) (An explanation of why his works is provided at [http://buildinternet.com/2008/11/how-to-center-a-website-with-css/])
   * Use the new html file to create a separate binary for the iphone. (Apple App Store Universal apps are generally a bad idea: they are useless since high ranking on ipad does not help iphone rank, or vice-versa. Furthermore, they force you to price your ipad app at the same price as the iphone one, even though willingness to pay is generally different.)

  == How do I align/distribute elements? ==
  First group them (by rapidly clicking on one after the other. Then select the group (it appears as a beige-bordered transparent container). Finally, open the "Position" menu and select a menu option. Optionally, you can ungroup with the "Ungroup" button (upper-right).

  = Creating Tables and Dialogue Boxes =
  == How can I create a database table? ==
  Simply create a dialogue box by following the instructions in the "Data" tab.
  Once you click "Go!", you will be taken to a design interface for the dialogue box.
   * Add graphic elements as desired.
   * Add user-input elements: one per intended table column. You can create these elements via the dialogue box in the menu: "Shapes, Images, Buttons" -> "User Input"
   * Create the "ok" button: create another user input element and give it the type “submit” and the name “_submit”, and the default value “Ok” (or whatever other default value you want)
   * Create the cancel button: Create another user input element and give it the type “submit” and the name “_cancel”, and the default value “Cancel” (or whatever other default value you want)
  And you’re done! If the dialogue was called (for example) dialogues/cld_Restaurants_insert_table.php, then you can programmatically open it via: dialogues_cld_Restaurants_insert_table()
  Once the user clicks “Ok” on the dialogue box, the data is automatically stored to be sent to the remote database.
  Whenever you want to send, simply execute cld_Restaurants.sendPendingAjax() (This works even if you don’t have internet, it will simply do a smart sync once you get back online.) This step is separate in order to avoid causing bandwidth problems. Often users will make minor changes to data they just input.
  '''Best practice:''' on viewOpenComplete of the top view, use a setInterval call to execute cld_Restaurants.sendPendingAjax() once every 60000 milliseconds (and do the same for every table you create.) This way you will not have to manually execute sendPendingAjax() every time.

  == How can I create a dialogue box? ==
  Do it by creating a table, as described above.
  If you want to create a second dialogue box corresponding to the same table, here is how:
   * From any one of the tabs ("Design", "Data" or "Dynamic"), click on the name of the current view on the top left of the screen. This takes you to the view selection page.
   * Click on the "New Dialogue" button.
   * Design the dialogue box, as described above.

  = Scripting =
  == How can I programatically access the cloud data? ==
  ==== To access the data in the table ====
  For example, let us assume you named the dialogue box "Restaurants" (when creating the table). Your cloud object is called cld_Restaurants_insert_table.  Here is the script.
   * If you want the script to get the data from the remote database: use the cloud’s reload method:
  {{{
  var cfg = {};
  cfg.successFunc = function(list){
  // Do something with “list”
  // “list” is an array of records. Each record is an object of the form
  // { colName1: "value of column 1", colName2: "value of column 2", colName3: "value of column 3", [etc……]}
  };
  // cfg.where = [["location","=",theLocation], ["type","=","Lebanese"]]; // An optional where clause
  cld_Restaurants_insert_table.act['select'].reload(cfg);
  }}}
   * If you want the script, while waiting for the data to arrive from the remote database, to draw on the local cache: use the cloud’s data access method (getData) 
  {{{
  var cfg = {};
  cfg.successFunc = function(list){
  // Do something with “list”
  // “list” is an array of records. Each record is an object of the form
  // { colName1: "value of column 1", colName2: "value of column 2", colName3: "value of column 3", [etc……]}
  };
  // cfg.where = [["location","=",theLocation], ["type","=","Lebanese"]]; // An optional where clause
  var list0 = cld_Restaurants_insert_table.act['select'].getData(cfg);
  // Then do something with list0, which contains the local cache data
  }}}

  === To input data into the table ===
  The easiest way is to create a dialogue box (as described elsewhere) and do no scripting. If the dialogue was called (for example) dialogues/cld_Restaurants_insert_table.php, then you can programmatically open it via: dialogues_cld_Restaurants_insert_table()
  Once the user clicks “Ok” on the dialogue box, the data is automatically stored to be sent to the remote database.
  Whenever you want to send, simply execute cld_Restaurants.sendPendingAjax() (This works even if you don’t have internet, it will simply do a smart sync once you get back online.) This step is separate in order to avoid causing bandwidth problems. Often users will make minor changes to data they just input.

  Or you can do it programmatically as follows:
  {{{
  var data={};
  data["ajaxAction"] = 'insert'; /* Other options: update, delete, undelete, deleteFinal. You can also use select and selectdistinct, however the reload and getData methods do this a lot better */
  data["Column 1 name"] = aValue;
  data["Column 2 name"] = aValue2;
  data["Column 3 name"] = aValue3;
  cld_Restaurants.sendPending(data);//This stores the data in the local cache, ready to be sent to the server
  cld_Restaurants.sendPendingAjax();//This forces all pending data to be synced with the server.
  /* For advanced users: set up an event handler to handle event sendPendingFailure. Do this via the following code:
  addEvent(cld_Restaurants.events, 'sendPendingFailure', function(ev){
  // “ev” has the form: {type:"sendPendingFailure",item:i,attempts:numberOfattempts,status:statusCode,
  // code:ajaxStatusCode,msg:"The error message."}. 
  // The “attempts” field is not there for all types of errors.
  });
  */
  }}}

  Finally, a more complex option is to create a dialogue box as described, but instead of just simply letting it do its job, intercept the user input, modify it, and then send it on. Here is how:
   * Create the dialogue box as described above
   * Create a function to handle the user input – something like the below:
  {{{
  //function(ev){ // Commented out because, as you’ll see in a bit, it’s already there
        // Event handler for a dialogue box
        if(!(ev.value=='submit')) return;// If it’s not the submit button, do nothing
        if(!(ev.id== dialogues_cld_Restaurants_insert_table_1')) return; /* If it’s not instance 1 of the dialogue box dialogues_cld_Restaurants_insert_table then do nothing */
        
        var data = currentView.data; // “data” is now an associative array containing the dialogue box data
        
        // Now do something with the data
  /* For example, the below combines two dialogue box fields, called dialogueField1 and dialogueField2, into a single field */
        data['newField'] = data['dialogueField1']+" "+ data['dialogueField2'];
        delete data['dialogueField1'];
        delete data['dialogueField2'];

        currentView.data = data;// Once the data has been processed, overwrite the original data with the modified data
  //}// Commented out because, as you’ll see in a bit, it’s already there
  }}}
  Now all you need to do is go to the view in which the dialogue appears, go to the "Dynamics" tab, click on “Current View”, select the event dialogueButtonPress and copy-paste the function in there.
  WARNING: the lines “function(ev){” and the closing “}” are already there, so they shouldn’t be in your function!


name: FAQ
author: mnehme
ipnr: 212.36.209.181
text:
  [[PageOutline]]

  = Designing Views =
  == How can I create an element that resizes based on screen-size? ==
  This is not recommended. In order to make sure fonts are legible on all platforms, you should create one version of a view per platform. (For example, instead of creating a view called "Menu" you should create one called "Menu/iphone", then copy it into another called "Menu/ipad" and adjust as necessary).
  The differences in device screen-sizes are so great that if you do not do this, your text will not be legible.

  ==== But then I will have to duplicate scripts! ====
  No: simply create a view called "Menu" and write the script within that view. The views "Menu/iphone" and "Menu/ipad" will inherit from it. So any script that you put in "Menu" will apply to "Menu/iphone", and any design elements you put in "Menu" will be visible as a background to the view "Menu/iphone"

  ==== But I still want to do it. ====
  If you insist:
  Alternative 1: Create a script fired by the viewOpenComplete event, which automatically resizes the div based on screen size. For this you’ll find it helpful to give the div a classname via the fill dialogue box.
  Alternative 2: 
   * Create the app for one screen size. In the following, we will assume it is the ipad screen size.
   * Once it is created, download the html, and edit the scale in the viewport meta-tag. Set it as follows: <meta name="viewport" id="viewport" content="width=device-width, initial-scale=0.42, maximum-scale=0.42, minimum-scale=0.42" />. This will create an html file that is resized to fit in the iphone. You may want to adjust the 0.42 factor slightly
   * The above will suffice to resize to iphone size. But in order for it to look good, you’ll also want to center it. Do so by adding <div style='position:relative;left:-5px;width:1024px;margin:0 auto;'>  around the entire content (but within the <body> tags) (An explanation of why his works is provided at [http://buildinternet.com/2008/11/how-to-center-a-website-with-css/])
   * Use the new html file to create a separate binary for the iphone. (Apple App Store Universal apps are generally a bad idea: they are useless since high ranking on ipad does not help iphone rank, or vice-versa. Furthermore, they force you to price your ipad app at the same price as the iphone one, even though willingness to pay is generally different.)

  == How do I align/distribute elements? ==
  First group them (by rapidly clicking on one after the other. Then select the group (it appears as a beige-bordered transparent container). Finally, open the "Position" menu and select a menu option. Optionally, you can ungroup with the "Ungroup" button (upper-right).

  = Creating Tables and Dialogue Boxes =
  == How can I create a database table? ==
  Simply create a dialogue box by following the instructions in the "Data" tab.
  Once you click "Go!", you will be taken to a design interface for the dialogue box.
   * Add graphic elements as desired.
   * Add user-input elements: one per intended table column. You can create these elements via the dialogue box in the menu: "Shapes, Images, Buttons" -> "User Input"
   * Create the "ok" button: create another user input element and give it the type “submit” and the name “_submit”, and the default value “Ok” (or whatever other default value you want)
   * Create the cancel button: Create another user input element and give it the type “submit” and the name “_cancel”, and the default value “Cancel” (or whatever other default value you want)
  And you’re done! If the dialogue was called (for example) dialogues/cld_Restaurants_insert_table.php, then you can programmatically open it via: dialogues_cld_Restaurants_insert_table()
  Once the user clicks “Ok” on the dialogue box, the data is automatically stored to be sent to the remote database.
  Whenever you want to send, simply execute cld_Restaurants.sendPendingAjax() (This works even if you don’t have internet, it will simply do a smart sync once you get back online.) This step is separate in order to avoid causing bandwidth problems. Often users will make minor changes to data they just input.
  '''Best practice:''' on viewOpenComplete of the top view, use a setInterval call to execute cld_Restaurants.sendPendingAjax() once every 60000 milliseconds (and do the same for every table you create.) This way you will not have to manually execute sendPendingAjax() every time.

  == How can I create a dialogue box? ==
  Do it by creating a table, as described above.
  If you want to create a second dialogue box corresponding to the same table, here is how:
   * From any one of the tabs ("Design", "Data" or "Dynamic"), click on the name of the current view on the top left of the screen. This takes you to the view selection page.
   * Click on the "New Dialogue" button.
   * Design the dialogue box, as described above.

  = Scripting =
  == How can I programatically access the cloud data? ==
  ==== To access the data in the table ====
  For example, let us assume you named the dialogue box "Restaurants" (when creating the table). Your cloud object is called cld_Restaurants_insert_table.  Here is the script.
   * If you want the script to get the data from the remote database: use the cloud’s reload method:
  {{{
  #!java
  var cfg = {};
  cfg.successFunc = function(list){
  // Do something with “list”
  // “list” is an array of records. Each record is an object of the form
  // { colName1: "value of column 1", colName2: "value of column 2", colName3: "value of column 3", [etc……]}
  };
  // cfg.where = [["location","=",theLocation], ["type","=","Lebanese"]]; // An optional where clause
  cld_Restaurants_insert_table.act['select'].reload(cfg);
  }}}
   * If you want the script, while waiting for the data to arrive from the remote database, to draw on the local cache: use the cloud’s data access method (getData) 
  {{{
  #!java
  var cfg = {};
  cfg.successFunc = function(list){
  // Do something with “list”
  // “list” is an array of records. Each record is an object of the form
  // { colName1: "value of column 1", colName2: "value of column 2", colName3: "value of column 3", [etc……]}
  };
  // cfg.where = [["location","=",theLocation], ["type","=","Lebanese"]]; // An optional where clause
  var list0 = cld_Restaurants_insert_table.act['select'].getData(cfg);
  // Then do something with list0, which contains the local cache data
  }}}

  === To input data into the table ===
  The easiest way is to create a dialogue box (as described elsewhere) and do no scripting. If the dialogue was called (for example) dialogues/cld_Restaurants_insert_table.php, then you can programmatically open it via: dialogues_cld_Restaurants_insert_table()
  Once the user clicks “Ok” on the dialogue box, the data is automatically stored to be sent to the remote database.
  Whenever you want to send, simply execute cld_Restaurants.sendPendingAjax() (This works even if you don’t have internet, it will simply do a smart sync once you get back online.) This step is separate in order to avoid causing bandwidth problems. Often users will make minor changes to data they just input.

  Or you can do it programmatically as follows:
  {{{
  #!java
  var data={};
  data["ajaxAction"] = 'insert'; /* Other options: update, delete, undelete, deleteFinal. You can also use select and selectdistinct, however the reload and getData methods do this a lot better */
  data["Column 1 name"] = aValue;
  data["Column 2 name"] = aValue2;
  data["Column 3 name"] = aValue3;
  cld_Restaurants.sendPending(data);//This stores the data in the local cache, ready to be sent to the server
  cld_Restaurants.sendPendingAjax();//This forces all pending data to be synced with the server.
  /* For advanced users: set up an event handler to handle event sendPendingFailure. Do this via the following code:
  addEvent(cld_Restaurants.events, 'sendPendingFailure', function(ev){
  // “ev” has the form: {type:"sendPendingFailure",item:i,attempts:numberOfattempts,status:statusCode,
  // code:ajaxStatusCode,msg:"The error message."}. 
  // The “attempts” field is not there for all types of errors.
  });
  */
  }}}

  Finally, a more complex option is to create a dialogue box as described, but instead of just simply letting it do its job, intercept the user input, modify it, and then send it on. Here is how:
   * Create the dialogue box as described above
   * Create a function to handle the user input – something like the below:
  {{{
  #!java
  //function(ev){ // Commented out because, as you’ll see in a bit, it’s already there
        // Event handler for a dialogue box
        if(!(ev.value=='submit')) return;// If it’s not the submit button, do nothing
        if(!(ev.id== dialogues_cld_Restaurants_insert_table_1')) return; /* If it’s not instance 1 of the dialogue box dialogues_cld_Restaurants_insert_table then do nothing */
        
        var data = currentView.data; // “data” is now an associative array containing the dialogue box data
        
        // Now do something with the data
  /* For example, the below combines two dialogue box fields, called dialogueField1 and dialogueField2, into a single field */
        data['newField'] = data['dialogueField1']+" "+ data['dialogueField2'];
        delete data['dialogueField1'];
        delete data['dialogueField2'];

        currentView.data = data;// Once the data has been processed, overwrite the original data with the modified data
  //}// Commented out because, as you’ll see in a bit, it’s already there
  }}}
  Now all you need to do is go to the view in which the dialogue appears, go to the "Dynamics" tab, click on “Current View”, select the event dialogueButtonPress and copy-paste the function in there.
  WARNING: the lines “function(ev){” and the closing “}” are already there, so they shouldn’t be in your function!


name: WikiStart
author: mnehme
ipnr: 212.36.209.181
text:
  = Welcome to the Cedrus Development Environment Wiki =

  == About ==
  Cedrus Development Environment is a platform to allow easy and fast development of mobile and web Apps. "Fast" means 20-30x faster than normal development.
  Though primarily targeting developers, CDE can also be used by product managers to speedily create prototypes with zero-coding. These prototypes can be used as both specs and first release, and speed up the production process by breaking down coding into simple tasks.

  == How it works ==
   * '''Fast design process:''' design is done via simple drag-and-drop.
   * '''Fast coding:''' CDE makes it easy and natural to break down code into small self-contained components, reducing complexity, while making coding very fast. 
   * '''Cloud-enabled:''' Automatically creates server-side databases, automatically creates access functions for you to use which even work offline, and provide intelligent syncing abilities.
   * '''Multi-platform:''' CDE creates apps written in Ajax/HTML5 for their front-ends, and PHP for their back-ends. As a result, it can be used with Phonegap to immediately port the App to all platforms.
   * '''Iterative prototyping:''' Naturally encourages iterative development and MVP, because iterating is blindingly fast.
   * '''Infinitely extensible:''' CDE of course includes the ability to actually code, so anything that can be done by coding can be done with CDE.

  == Limitations ==
  CDE works for most Apps, except for media-rich gaming Apps. For the latter, you might want to consider using a platform such as Unity3D.

  == Starting Points ==
   * [wiki:Starting Getting Started] -- A quick introduction
   * [wiki:FAQ Cedrus Development Environment FAQ] -- Frequently Asked Questions
   * [wiki:TracStartingPage Bug-reporting assistance] -- You can report bugs and request features using Trac. This links to the Trac documentation.


name: WikiStart
author: mnehme
ipnr: 178.135.40.76
text:
  == Mission: To speed App development by 20-30x ==
  Cedrus Development Environment is a platform to allow easy and fast development of mobile and web Apps. "Fast" means 20-30x faster than normal development.
  Though primarily targeting developers, CDE can also be used by product managers to speedily create prototypes with zero-coding. These prototypes can be used as both specs and first release, and speed up the production process by breaking down coding into simple tasks.

  == How it works ==
   * '''Fast design process:''' design is done via simple drag-and-drop.
   * '''Fast coding:''' CDE makes it easy and natural to break down code into small self-contained components, reducing complexity, while making coding very fast. 
   * '''Cloud-enabled:''' Automatically creates server-side databases, automatically creates access functions for you to use which even work offline, and provide intelligent syncing abilities.
   * '''Multi-platform:''' CDE creates apps written in Ajax/HTML5 for their front-ends, and PHP for their back-ends. As a result, it can be used with Phonegap to immediately port the App to all platforms.
   * '''Iterative prototyping:''' Naturally encourages iterative development and MVP, because iterating is blindingly fast.
   * '''Infinitely extensible:''' CDE of course includes the ability to actually code, so anything that can be done by coding can be done with CDE.

  == Limitations ==
  CDE works for most Apps, except for media-rich gaming Apps. For the latter, you might want to consider using a platform such as Unity3D.

  == Starting Points ==
   * [wiki:Starting Getting Started] -- A quick introduction
   * [wiki:FAQ Cedrus Development Environment FAQ] -- Frequently Asked Questions
   * [wiki:TracStartingPage Bug-reporting assistance] -- You can report bugs and request features using Trac. This links to the Trac documentation.


name: WikiStart
author: mnehme
ipnr: 178.135.40.76
text:
  == Mission: To speed App development by 20-30x ==
  Cedrus Development Environment is a platform to allow easy and fast development of mobile and web Apps. "Fast" means 20-30x faster than normal development.
  Though primarily targeting developers, CDE can also be used by product managers to speedily create prototypes with zero-coding. These prototypes can be used as both specs and first release, and speed up the production process by breaking down coding into simple tasks.

  == How it works ==
   * '''Fast design process:''' design is done via simple drag-and-drop.
   * '''Fast coding:''' CDE makes it easy and natural to break down code into small self-contained components, reducing complexity, while making coding very fast. 
   * '''Cloud-enabled:''' Automatically creates server-side databases, automatically creates access functions for you to use which even work offline, and provide intelligent syncing abilities.
   * '''Multi-platform:''' CDE creates apps written in Ajax/HTML5 for their front-ends, and PHP for their back-ends. As a result, it can be used with Phonegap to immediately port the App to all platforms.
   * '''Iterative prototyping:''' Naturally encourages iterative development and MVP, because iterating is blindingly fast.
   * '''Infinitely extensible:''' CDE of course includes the ability to actually code, so anything that can be done by coding can be done with CDE.

  == Limitations ==
  CDE works for most Apps, except for media-rich gaming Apps. For the latter, you might want to consider using a platform such as Unity3D.

  == Starting Points ==
   * [wiki:GettingStarted Getting Started] -- A quick introduction
   * [wiki:FAQ Cedrus Development Environment FAQ] -- Frequently Asked Questions
   * [wiki:TracStartingPage Bug-reporting assistance] -- You can report bugs and request features using Trac. This links to the Trac documentation.


name: GettingStarted
author: mnehme
ipnr: 178.135.40.76
text:
  = Quick Start =

  '''Step 1:''' Connect using Chrome browser, and login:

  [[Image(0001login.jpg)]]

  '''Step 2:'''
   * If you are a new user, [GettingStartedQuickStart click here]
   * If you are a returning user, [GettingStartedAdvanced click here]


name: GettingStartedQuickStart
author: mnehme
ipnr: 178.135.40.76
text:
  = Quick Start (continued) = 

  '''Step 2''': Click on "Quick Start":
  [[Image(0002landingPage.jpg​)]]

  '''Step 3''': Name your App. This is enough for CDE to create a very basic App.
  [[Image(0003namingPage.jpg)]]


name: GettingStartedQuickStart
author: mnehme
ipnr: 178.135.40.76
text:
  = Quick Start (continued) = 

  '''Step 2''': Click on "Quick Start":

  [[Image(0002landingPage.jpg​)]]

  '''Step 3''': Name your App. This is enough for CDE to create a very basic App.

  [[Image(0003namingPage.jpg)]]


name: GettingStartedQuickStart
author: mnehme
ipnr: 178.135.40.76
text:
  = Quick Start (continued) = 

  '''Step 2''': Click on "Quick Start":

  [[Image(0002landingPage.jpg)]]

  '''Step 3''': Name your App. This is enough for CDE to create a very basic App.

  [[Image(0003namingPage.jpg)]]


name: FAQ
author: mnehme
ipnr: 178.135.217.68
text:
  [[PageOutline]]

  = Designing Views =
  == How can I create an element that resizes based on screen-size? ==
  This is not recommended. In order to make sure fonts are legible on all platforms, you should create one version of a view per platform. (For example, instead of creating a view called "Menu" you should create one called "Menu/iphone", then copy it into another called "Menu/ipad" and adjust as necessary).
  The differences in device screen-sizes are so great that if you do not do this, your text will not be legible.

  ==== But then I will have to duplicate scripts! ====
  No: simply create a view called "Menu" and write the script within that view. The views "Menu/iphone" and "Menu/ipad" will inherit from it. So any script that you put in "Menu" will apply to "Menu/iphone", and any design elements you put in "Menu" will be visible as a background to the view "Menu/iphone"

  ==== But I still want to do it. ====
  If you insist:
  Alternative 1: Create a script fired by the viewOpenComplete event, which automatically resizes the div based on screen size. For this you’ll find it helpful to give the div a classname via the fill dialogue box.
  Alternative 2: 
   * Create the app for one screen size. In the following, we will assume it is the ipad screen size.
   * Once it is created, download the html, and edit the scale in the viewport meta-tag. Set it as follows: <meta name="viewport" id="viewport" content="width=device-width, initial-scale=0.42, maximum-scale=0.42, minimum-scale=0.42" />. This will create an html file that is resized to fit in the iphone. You may want to adjust the 0.42 factor slightly
   * The above will suffice to resize to iphone size. But in order for it to look good, you’ll also want to center it. Do so by adding <div style='position:relative;left:-5px;width:1024px;margin:0 auto;'>  around the entire content (but within the <body> tags) (An explanation of why his works is provided at [http://buildinternet.com/2008/11/how-to-center-a-website-with-css/])
   * Use the new html file to create a separate binary for the iphone. (Apple App Store Universal apps are generally a bad idea: they are useless since high ranking on ipad does not help iphone rank, or vice-versa. Furthermore, they force you to price your ipad app at the same price as the iphone one, even though willingness to pay is generally different.)

  == How do I align/distribute elements? ==
  First group them (by rapidly clicking on one after the other. Then select the group (it appears as a beige-bordered transparent container). Finally, open the "Position" menu and select a menu option. Optionally, you can ungroup with the "Ungroup" button (upper-right).

  = Creating Tables and Dialogue Boxes =
  == How can I create a database table? ==
  Simply create a dialogue box by following the instructions in the "Data" tab.
  Once you click "Go!", you will be taken to a design interface for the dialogue box.
   * Add graphic elements as desired.
   * Add user-input elements: one per intended table column. You can create these elements via the dialogue box in the menu: "Shapes, Images, Buttons" -> "User Input"
   * Create the "ok" button: create another user input element and give it the type “submit” and the name “_submit”, and the default value “Ok” (or whatever other default value you want)
   * Create the cancel button: Create another user input element and give it the type “submit” and the name “_cancel”, and the default value “Cancel” (or whatever other default value you want)
  And you’re done! If the dialogue was called (for example) dialogues/cld_Restaurants_insert_table.php, then you can programmatically open it via: dialogues_cld_Restaurants_insert_table()
  Once the user clicks “Ok” on the dialogue box, the data is automatically stored to be sent to the remote database.
  Whenever you want to send, simply execute cld_Restaurants.sendPendingAjax() (This works even if you don’t have internet, it will simply do a smart sync once you get back online.) This step is separate in order to avoid causing bandwidth problems. Often users will make minor changes to data they just input.
  '''Best practice:''' on viewOpenComplete of the top view, use a setInterval call to execute cld_Restaurants.sendPendingAjax() once every 60000 milliseconds (and do the same for every table you create.) This way you will not have to manually execute sendPendingAjax() every time.

  == How can I create a dialogue box? ==
  Do it by creating a table, as described above.
  If you want to create a second dialogue box corresponding to the same table, here is how:
   * From any one of the tabs ("Design", "Data" or "Dynamic"), click on the name of the current view on the top left of the screen. This takes you to the view selection page.
   * Click on the "New Dialogue" button.
   * Design the dialogue box, as described above.

  = Scripting =
  == How can I programatically access the cloud data? ==
  ==== To access the data in the table ====
  For example, let us assume you named the dialogue box "Restaurants" (when creating the table). Your cloud object is called cld_Restaurants.  Here is the script.
   * If you want the script to get the data from the remote database: use the cloud’s reload method:
  {{{
  #!java
  var cfg = {};
  cfg.successFunc = function(list){
  // Do something with “list”
  // “list” is an array of records. Each record is an object of the form
  // { colName1: "value of column 1", colName2: "value of column 2", colName3: "value of column 3", [etc……]}
  };
  // cfg.where = [["location","=",theLocation], ["type","=","Lebanese"]]; // An optional where clause
  cld_Restaurants.act['select'].reload(cfg);
  }}}
   * If you want the script, while waiting for the data to arrive from the remote database, to draw on the local cache: use the cloud’s data access method (getData) 
  {{{
  #!java
  var cfg = {};
  cfg.successFunc = function(list){
  // Do something with “list”
  // “list” is an array of records. Each record is an object of the form
  // { colName1: "value of column 1", colName2: "value of column 2", colName3: "value of column 3", [etc……]}
  };
  // cfg.where = [["location","=",theLocation], ["type","=","Lebanese"]]; // An optional where clause
  var list0 = cld_Restaurants.act['select'].getData(cfg);
  // Then do something with list0, which contains the local cache data
  }}}

  === To input data into the table ===
  The easiest way is to create a dialogue box (as described elsewhere) and do no scripting. If the dialogue was called (for example) dialogues/cld_Restaurants_insert_table.php, then you can programmatically open it via: dialogues_cld_Restaurants_insert_table()
  Once the user clicks “Ok” on the dialogue box, the data is automatically stored to be sent to the remote database.
  Whenever you want to send, simply execute cld_Restaurants.sendPendingAjax() (This works even if you don’t have internet, it will simply do a smart sync once you get back online.) This step is separate in order to avoid causing bandwidth problems. Often users will make minor changes to data they just input.

  Or you can do it programmatically as follows:
  {{{
  #!java
  var data={};
  data["ajaxAction"] = 'insert'; /* Other options: update, delete, undelete, deleteFinal. You can also use select and selectdistinct, however the reload and getData methods do this a lot better */
  data["Column 1 name"] = aValue;
  data["Column 2 name"] = aValue2;
  data["Column 3 name"] = aValue3;
  cld_Restaurants.sendPending(data);//This stores the data in the local cache, ready to be sent to the server
  cld_Restaurants.sendPendingAjax();//This forces all pending data to be synced with the server.
  /* For advanced users: set up an event handler to handle event sendPendingFailure. Do this via the following code:
  addEvent(cld_Restaurants.events, 'sendPendingFailure', function(ev){
  // “ev” has the form: {type:"sendPendingFailure",item:i,attempts:numberOfattempts,status:statusCode,
  // code:ajaxStatusCode,msg:"The error message."}. 
  // The “attempts” field is not there for all types of errors.
  });
  */
  }}}

  Finally, a more complex option is to create a dialogue box as described, but instead of just simply letting it do its job, intercept the user input, modify it, and then send it on. Here is how:
   * Create the dialogue box as described above
   * Create a function to handle the user input – something like the below:
  {{{
  #!java
  //function(ev){ // Commented out because, as you’ll see in a bit, it’s already there
        // Event handler for a dialogue box
        if(!(ev.value=='submit')) return;// If it’s not the submit button, do nothing
        if(!(ev.id== dialogues_cld_Restaurants_insert_table_1')) return; /* If it’s not instance 1 of the dialogue box dialogues_cld_Restaurants_insert_table then do nothing */
        
        var data = currentView.data; // “data” is now an associative array containing the dialogue box data
        
        // Now do something with the data
  /* For example, the below combines two dialogue box fields, called dialogueField1 and dialogueField2, into a single field */
        data['newField'] = data['dialogueField1']+" "+ data['dialogueField2'];
        delete data['dialogueField1'];
        delete data['dialogueField2'];

        currentView.data = data;// Once the data has been processed, overwrite the original data with the modified data
  //}// Commented out because, as you’ll see in a bit, it’s already there
  }}}
  Now all you need to do is go to the view in which the dialogue appears, go to the "Dynamics" tab, click on “Current View”, select the event dialogueButtonPress and copy-paste the function in there.
  WARNING: the lines “function(ev){” and the closing “}” are already there, so they shouldn’t be in your function!


name: FAQ
author: mnehme
ipnr: 178.135.44.248
text:
  [[PageOutline(1-5,Contents,inline)]]

  = Designing Views =
  == How can I create an element that resizes based on screen-size? ==
  This is not recommended. In order to make sure fonts are legible on all platforms, you should create one version of a view per platform. (For example, instead of creating a view called "Menu" you should create one called "Menu/iphone", then copy it into another called "Menu/ipad" and adjust as necessary).
  The differences in device screen-sizes are so great that if you do not do this, your text will not be legible.

  ==== But then I will have to duplicate scripts! ====
  No: simply create a view called "Menu" and write the script within that view. The views "Menu/iphone" and "Menu/ipad" will inherit from it. So any script that you put in "Menu" will apply to "Menu/iphone", and any design elements you put in "Menu" will be visible as a background to the view "Menu/iphone"

  ==== But I still want to do it. ====
  If you insist:
  Alternative 1: Create a script fired by the viewOpenComplete event, which automatically resizes the div based on screen size. For this you’ll find it helpful to give the div a classname via the fill dialogue box.
  Alternative 2: 
   * Create the app for one screen size. In the following, we will assume it is the ipad screen size.
   * Once it is created, download the html, and edit the scale in the viewport meta-tag. Set it as follows: <meta name="viewport" id="viewport" content="width=device-width, initial-scale=0.42, maximum-scale=0.42, minimum-scale=0.42" />. This will create an html file that is resized to fit in the iphone. You may want to adjust the 0.42 factor slightly
   * The above will suffice to resize to iphone size. But in order for it to look good, you’ll also want to center it. Do so by adding <div style='position:relative;left:-5px;width:1024px;margin:0 auto;'>  around the entire content (but within the <body> tags) (An explanation of why his works is provided at [http://buildinternet.com/2008/11/how-to-center-a-website-with-css/])
   * Use the new html file to create a separate binary for the iphone. (Apple App Store Universal apps are generally a bad idea: they are useless since high ranking on ipad does not help iphone rank, or vice-versa. Furthermore, they force you to price your ipad app at the same price as the iphone one, even though willingness to pay is generally different.)

  == How do I align/distribute elements? ==
  First group them (by rapidly clicking on one after the other. Then select the group (it appears as a beige-bordered transparent container). Finally, open the "Position" menu and select a menu option. Optionally, you can ungroup with the "Ungroup" button (upper-right).

  = Creating Tables and Dialogue Boxes =
  == How can I create a database table? ==
  Simply create a dialogue box by following the instructions in the "Data" tab.
  Once you click "Go!", you will be taken to a design interface for the dialogue box.
   * Add graphic elements as desired.
   * Add user-input elements: one per intended table column. You can create these elements via the dialogue box in the menu: "Shapes, Images, Buttons" -> "User Input"
   * Create the "ok" button: create another user input element and give it the type “submit” and the name “_submit”, and the default value “Ok” (or whatever other default value you want)
   * Create the cancel button: Create another user input element and give it the type “submit” and the name “_cancel”, and the default value “Cancel” (or whatever other default value you want)
  And you’re done! If the dialogue was called (for example) dialogues/cld_Restaurants_insert_table.php, then you can programmatically open it via: dialogues_cld_Restaurants_insert_table()
  Once the user clicks “Ok” on the dialogue box, the data is automatically stored to be sent to the remote database.
  Whenever you want to send, simply execute cld_Restaurants.sendPendingAjax() (This works even if you don’t have internet, it will simply do a smart sync once you get back online.) This step is separate in order to avoid causing bandwidth problems. Often users will make minor changes to data they just input.
  '''Best practice:''' on viewOpenComplete of the top view, use a setInterval call to execute cld_Restaurants.sendPendingAjax() once every 60000 milliseconds (and do the same for every table you create.) This way you will not have to manually execute sendPendingAjax() every time.

  == How can I create a dialogue box? ==
  Do it by creating a table, as described above.
  If you want to create a second dialogue box corresponding to the same table, here is how:
   * From any one of the tabs ("Design", "Data" or "Dynamic"), click on the name of the current view on the top left of the screen. This takes you to the view selection page.
   * Click on the "New Dialogue" button.
   * Design the dialogue box, as described above.

  = Scripting =
  == How can I programatically access the cloud data? ==
  ==== To access the data in the table ====
  For example, let us assume you named the dialogue box "Restaurants" (when creating the table). Your cloud object is called cld_Restaurants.  Here is the script.
   * If you want the script to get the data from the remote database: use the cloud’s reload method:
  {{{
  #!java
  var cfg = {};
  cfg.successFunc = function(list){
  // Do something with “list”
  // “list” is an array of records. Each record is an object of the form
  // { colName1: "value of column 1", colName2: "value of column 2", colName3: "value of column 3", [etc……]}
  };
  // cfg.where = [["location","=",theLocation], ["type","=","Lebanese"]]; // An optional where clause
  cld_Restaurants.act['select'].reload(cfg);
  }}}
   * If you want the script, while waiting for the data to arrive from the remote database, to draw on the local cache: use the cloud’s data access method (getData) 
  {{{
  #!java
  var cfg = {};
  cfg.successFunc = function(list){
  // Do something with “list”
  // “list” is an array of records. Each record is an object of the form
  // { colName1: "value of column 1", colName2: "value of column 2", colName3: "value of column 3", [etc……]}
  };
  // cfg.where = [["location","=",theLocation], ["type","=","Lebanese"]]; // An optional where clause
  var list0 = cld_Restaurants.act['select'].getData(cfg);
  // Then do something with list0, which contains the local cache data
  }}}

  === To input data into the table ===
  The easiest way is to create a dialogue box (as described elsewhere) and do no scripting. If the dialogue was called (for example) dialogues/cld_Restaurants_insert_table.php, then you can programmatically open it via: dialogues_cld_Restaurants_insert_table()
  Once the user clicks “Ok” on the dialogue box, the data is automatically stored to be sent to the remote database.
  Whenever you want to send, simply execute cld_Restaurants.sendPendingAjax() (This works even if you don’t have internet, it will simply do a smart sync once you get back online.) This step is separate in order to avoid causing bandwidth problems. Often users will make minor changes to data they just input.

  Or you can do it programmatically as follows:
  {{{
  #!java
  var data={};
  data["ajaxAction"] = 'insert'; /* Other options: update, delete, undelete, deleteFinal. You can also use select and selectdistinct, however the reload and getData methods do this a lot better */
  data["Column 1 name"] = aValue;
  data["Column 2 name"] = aValue2;
  data["Column 3 name"] = aValue3;
  cld_Restaurants.sendPending(data);//This stores the data in the local cache, ready to be sent to the server
  cld_Restaurants.sendPendingAjax();//This forces all pending data to be synced with the server.
  /* For advanced users: set up an event handler to handle event sendPendingFailure. Do this via the following code:
  addEvent(cld_Restaurants.events, 'sendPendingFailure', function(ev){
  // “ev” has the form: {type:"sendPendingFailure",item:i,attempts:numberOfattempts,status:statusCode,
  // code:ajaxStatusCode,msg:"The error message."}. 
  // The “attempts” field is not there for all types of errors.
  });
  */
  }}}

  Finally, a more complex option is to create a dialogue box as described, but instead of just simply letting it do its job, intercept the user input, modify it, and then send it on. Here is how:
   * Create the dialogue box as described above
   * Create a function to handle the user input – something like the below:
  {{{
  #!java
  //function(ev){ // Commented out because, as you’ll see in a bit, it’s already there
        // Event handler for a dialogue box
        if(!(ev.value=='submit')) return;// If it’s not the submit button, do nothing
        if(!(ev.id== dialogues_cld_Restaurants_insert_table_1')) return; /* If it’s not instance 1 of the dialogue box dialogues_cld_Restaurants_insert_table then do nothing */
        
        var data = currentView.data; // “data” is now an associative array containing the dialogue box data
        
        // Now do something with the data
  /* For example, the below combines two dialogue box fields, called dialogueField1 and dialogueField2, into a single field */
        data['newField'] = data['dialogueField1']+" "+ data['dialogueField2'];
        delete data['dialogueField1'];
        delete data['dialogueField2'];

        currentView.data = data;// Once the data has been processed, overwrite the original data with the modified data
  //}// Commented out because, as you’ll see in a bit, it’s already there
  }}}
  Now all you need to do is go to the view in which the dialogue appears, go to the "Dynamics" tab, click on “Current View”, select the event dialogueButtonPress and copy-paste the function in there.
  WARNING: the lines “function(ev){” and the closing “}” are already there, so they shouldn’t be in your function!


name: FAQ
author: janette
ipnr: 60.229.178.61
text:
  [[PageOutline(1-5,Contents,inline)]]

  = Designing Views =
  == How can I create an element that resizes based on screen-size? ==
  This is not recommended. In order to make sure fonts are legible on all platforms, you should create one version of a view per platform. (For example, instead of creating a view called "Menu" you should create one called "Menu/iphone", then copy it into another called "Menu/ipad" and adjust as necessary).
  The differences in device screen-sizes are so great that if you do not do this, your text will not be legible.

  ==== But then I will have to duplicate scripts! ====
  No: simply create a view called "Menu" and write the script within that view. The views "Menu/iphone" and "Menu/ipad" will inherit from it. So any script that you put in "Menu" will apply to "Menu/iphone", and any design elements you put in "Menu" will be visible as a background to the view "Menu/iphone"

  ==== But I still want to do it. ====
  If you insist:
  Alternative 1: Create a script fired by the viewOpenComplete event, which automatically resizes the div based on screen size. For this you’ll find it helpful to give the div a classname via the fill dialogue box.
  Alternative 2: 
   * Create the app for one screen size. In the following, we will assume it is the ipad screen size.
   * Once it is created, download the html, and edit the scale in the viewport meta-tag. Set it as follows: <meta name="viewport" id="viewport" content="width=device-width, initial-scale=0.42, maximum-scale=0.42, minimum-scale=0.42" />. This will create an html file that is resized to fit in the iphone. You may want to adjust the 0.42 factor slightly
   * The above will suffice to resize to iphone size. But in order for it to look good, you’ll also want to center it. Do so by adding <div style='position:relative;left:-5px;width:1024px;margin:0 auto;'>  around the entire content (but within the <body> tags) (An explanation of why his works is provided at [http://buildinternet.com/2008/11/how-to-center-a-website-with-css/])
   * Use the new html file to create a separate binary for the iphone. (Apple App Store Universal apps are generally a bad idea: they are useless since high ranking on ipad does not help iphone rank, or vice-versa. Furthermore, they force you to price your ipad app at the same price as the iphone one, even though willingness to pay is generally different.)

  == How do I align/distribute elements? ==
  First group them (by rapidly clicking on one after the other. Then select the group (it appears as a beige-bordered transparent container). Finally, open the "Position" menu and select a menu option. Optionally, you can ungroup with the "Ungroup" button (upper-right).

  = Creating Tables and Dialogue Boxes =
  == How can I create a database table? ==
  Simply create a dialogue box by following the instructions in the "Data" tab.
  Once you click "Go!", you will be taken to a design interface for the dialogue box.
   * Add graphic elements as desired.
   * Add user-input elements: one per intended table column. You can create these elements via the dialogue box in the menu: "Shapes, Images, Buttons" -> "User Input"
   * Create the "ok" button: create another user input element and give it the type “submit” and the name “_submit”, and the default value “Ok” (or whatever other default value you want)
   * Create the cancel button: Create another user input element and give it the type “submit” and the name “_cancel”, and the default value “Cancel” (or whatever other default value you want)
  And you’re done! If the dialogue was called (for example) dialogues/cld_Restaurants_insert_table.php, then you can programmatically open it via: dialogues_cld_Restaurants_insert_table()
  Once the user clicks “Ok” on the dialogue box, the data is automatically stored to be sent to the remote database.
  Whenever you want to send, simply execute cld_Restaurants.sendPendingAjax() (This works even if you don’t have internet, it will simply do a smart sync once you get back online.) This step is separate in order to avoid causing bandwidth problems. Often users will make minor changes to data they just input.
  '''Best practice:''' on viewOpenComplete of the top view, use a setInterval call to execute cld_Restaurants.sendPendingAjax() once every 60000 milliseconds (and do the same for every table you create.) This way you will not have to manually execute sendPendingAjax() every time.

  == How can I create a dialogue box? ==
  Do it by creating a table, as described above.
  If you want to create a second dialogue box corresponding to the same table, here is how:
   * From any one of the tabs ("Design", "Data" or "Dynamic"), click on the name of the current view on the top left of the screen. This takes you to the view selection page.
   * Click on the "New Dialogue" button.
   * Design the dialogue box, as described above.

  = Scripting =
  == How can I programatically access the cloud data? ==
  ==== To access the data in the table ====
  For example, let us assume you named the dialogue box "Restaurants" (when creating the table). Your cloud object is called cld_Restaurants.  Here is the script.
   * If you want the script to get the data from the remote database: use the cloud’s reload method:
  {{{
  #!java
  var cfg = {};
  cfg.successFunc = function(list){
  // Do something with “list”
  // “list” is an array of records. Each record is an object of the form
  // { colName1: "value of column 1", colName2: "value of column 2", colName3: "value of column 3", [etc……]}
  };
  // cfg.where = [["location","=",theLocation], ["type","=","Lebanese"]]; // An optional where clause
  cld_Restaurants.act['select'].reload(cfg);
  }}}
   * If you want the script, while waiting for the data to arrive from the remote database, to draw on the local cache: use the cloud’s data access method (getData) 
  {{{
  #!java
  var cfg = {};
  cfg.successFunc = function(list){
  // Do something with “list”
  // “list” is an array of records. Each record is an object of the form
  // { colName1: "value of column 1", colName2: "value of column 2", colName3: "value of column 3", [etc……]}
  };
  // cfg.where = [["location","=",theLocation], ["type","=","Lebanese"]]; // An optional where clause
  var list0 = cld_Restaurants.act['select'].getData(cfg);
  // Then do something with list0, which contains the local cache data
  }}}

  === To input data into the table ===
  The easiest way is to create a dialogue box (as described elsewhere) and do no scripting. If the dialogue was called (for example) dialogues/cld_Restaurants_insert_table.php, then you can programmatically open it via: dialogues_cld_Restaurants_insert_table()
  Once the user clicks “Ok” on the dialogue box, the data is automatically stored to be sent to the remote database.
  Whenever you want to send, simply execute cld_Restaurants.sendPendingAjax() (This works even if you don’t have internet, it will simply do a smart sync once you get back online.) This step is separate in order to avoid causing bandwidth problems. Often users will make minor changes to data they just input.

  Or you can do it programmatically as follows:
  {{{
  #!java
  var data={};
  data["ajaxAction"] = 'insert'; /* Other options: update, delete, undelete, deleteFinal. You can also use select and selectdistinct, however the reload and getData methods do this a lot better */
  data["Column 1 name"] = aValue;
  data["Column 2 name"] = aValue2;
  data["Column 3 name"] = aValue3;
  cld_Restaurants.sendPending(data);//This stores the data in the local cache, ready to be sent to the server
  cld_Restaurants.sendPendingAjax();//This forces all pending data to be synced with the server.
  /* For advanced users: set up an event handler to handle event sendPendingFailure. Do this via the following code:
  addEvent(cld_Restaurants.events, 'sendPendingFailure', function(ev){
  // “ev” has the form: {type:"sendPendingFailure",item:i,attempts:numberOfattempts,status:statusCode,
  // code:ajaxStatusCode,msg:"The error message."}. 
  // The “attempts” field is not there for all types of errors.
  });
  */
  }}}

  Finally, a more complex option is to create a dialogue box as described, but instead of just simply letting it do its job, intercept the user input, modify it, and then send it on. Here is how:
   * Create the dialogue box as described above
   * Create a function to handle the user input – something like the below:
  {{{
  #!java
  //function(ev){ // Commented out because, as you’ll see in a bit, it’s already there
        // Event handler for a dialogue box
        if(!(ev.value=='submit')) return;// If it’s not the submit button, do nothing
        if(!(ev.id== dialogues_cld_Restaurants_insert_table_1')) return; /* If it’s not instance 1 of the dialogue box dialogues_cld_Restaurants_insert_table then do nothing */
        
        var data = currentView.data; // “data” is now an associative array containing the dialogue box data
        
        // Now do something with the data
  /* For example, the below combines two dialogue box fields, called dialogueField1 and dialogueField2, into a single field */
        data['newField'] = data['dialogueField1']+" "+ data['dialogueField2'];
        delete data['dialogueField1'];
        delete data['dialogueField2'];

        currentView.data = data;// Once the data has been processed, overwrite the original data with the modified data
  //}// Commented out because, as you’ll see in a bit, it’s already there
  }}}
  Now all you need to do is go to the view in which the dialogue appears, go to the "Dynamics" tab, click on “Current View”, select the event dialogueButtonPress and copy-paste the function in there.
  WARNING: the lines “function(ev){” and the closing “}” are already there, so they shouldn’t be in your function!

  == How can I programatically control rejected insert/update/deletes of data by a user? ==
  The use of _group and _altUser when inserting/updating/deleting data tells the app who should have access to data so if a person tries to insert a record into a table using 

  {{{
  #!java
  cfg["_group"]='admin';
  }}}
   
  and the user doesn't have the admin write user rights, 2 events will fire, one is the sendPendingFailure and the other NoAuthForThisContent. You can choose to catch either or both events. In the example of trying to insert something without the appropriate permissions, you may want to catch the event NoAuthForThisContent, delete the pending record from the cache and inform the user that they were unsuccessful at inserting as they didn't have the appropriate permissions ie something like:

  {{{
  #!java
  var data = {};
  data["ajaxAction"] = 'insert';
  data["departmentName"]= 'blah';
  data["_group"] = "admin"; 
  cld_department.sendPending(data);
  cld_department.sendPendingAjax();

  addEvent(authEvents, "NoAuthForThisContent", function(ev){
     localStorage.removeItem(ev['recordKey']); // deletes the record from the cache
     alert("Your insert failed as you do not have permission to insert these records"); // you would replace this with a message displayed within your app as alerts look ugly
  });

  }}}


name: WikiStart
author: mnehme
ipnr: 77.42.171.236
text:
  == Mission: To speed App development by 20-30x ==
  Cedrus Development Environment is a platform to allow easy and fast development of mobile and web Apps. "Fast" means 20-30x faster than normal development.
  Though primarily targeting developers, CDE can also be used by product managers to speedily create prototypes with zero-coding. These prototypes can be used as both specs and first release, and speed up the production process by breaking down coding into simple tasks.

  == How it works ==
   * '''Fast design process:''' design is done via simple drag-and-drop.
   * '''Fast coding:''' CDE makes it easy and natural to break down code into small self-contained components, reducing complexity, while making coding very fast. 
   * '''Cloud-enabled:''' Automatically creates server-side databases, automatically creates access functions for you to use which even work offline, and provide intelligent syncing abilities.
   * '''Multi-platform:''' CDE creates apps written in Ajax/HTML5 for their front-ends, and PHP for their back-ends. As a result, it can be used with Phonegap to immediately port the App to all platforms.
   * '''Iterative prototyping:''' Naturally encourages iterative development and MVP, because iterating is blindingly fast.
   * '''Infinitely extensible:''' CDE of course includes the ability to actually code, so anything that can be done by coding can be done with CDE.

  == Limitations ==
  CDE works for most Apps, except for media-rich gaming Apps. For the latter, you might want to consider using a platform such as Unity3D.

  == Starting Points ==
   * [wiki:GettingStarted Getting Started] -- A quick introduction
   * [wiki:FAQ Cedrus Development Environment FAQ] -- Frequently Asked Questions
   * [wiki:Advanced Advanced Features] -- Advanced Features
   * [wiki:TracStartingPage Bug-reporting assistance] -- You can report bugs and request features using Trac. This links to the Trac documentation.


name: ScriptingHacks
author: mnehme
ipnr: 77.42.171.236
text:
  == Warning ==
  Features described here are *advanced*, *experimental* and *unsupported*. Some will make it to subsequent versions, and some will be dropped.

  == Script-Defined Parameters ==
  '''localStorage.sendPendingCycle''': Sets the interval at which all pending data is sent to the server (in milliseconds). When not set, this happens every minute.

  '''localStorage.reloadCycle''': Sets the interval at which the entire app auto-refreshes (in milliseconds). When not set, auto-refresh happens every 2 hours. A negative value will de-activate the reloading.

  '''localStorage.reloadMsg''': Sets the warning message that appears 30 seconds before the app auto-refreshes. When not set, the message is 'Warning: refreshing in less than 30 seconds. Please save your work!'


name: WikiStart
author: mnehme
ipnr: 77.42.171.236
text:
  == Mission: To speed App development by 20-30x ==
  Cedrus Development Environment is a platform to allow easy and fast development of mobile and web Apps. "Fast" means 20-30x faster than normal development.
  Though primarily targeting developers, CDE can also be used by product managers to speedily create prototypes with zero-coding. These prototypes can be used as both specs and first release, and speed up the production process by breaking down coding into simple tasks.

  == How it works ==
   * '''Fast design process:''' design is done via simple drag-and-drop.
   * '''Fast coding:''' CDE makes it easy and natural to break down code into small self-contained components, reducing complexity, while making coding very fast. 
   * '''Cloud-enabled:''' Automatically creates server-side databases, automatically creates access functions for you to use which even work offline, and provide intelligent syncing abilities.
   * '''Multi-platform:''' CDE creates apps written in Ajax/HTML5 for their front-ends, and PHP for their back-ends. As a result, it can be used with Phonegap to immediately port the App to all platforms.
   * '''Iterative prototyping:''' Naturally encourages iterative development and MVP, because iterating is blindingly fast.
   * '''Infinitely extensible:''' CDE of course includes the ability to actually code, so anything that can be done by coding can be done with CDE.

  == Limitations ==
  CDE works for most Apps, except for media-rich gaming Apps. For the latter, you might want to consider using a platform such as Unity3D.

  == Starting Points ==
   * [wiki:GettingStarted Getting Started] -- A quick introduction
   * [wiki:FAQ Cedrus Development Environment FAQ] -- Frequently Asked Questions
   * [wiki:ScriptingHacks Hacks] -- Scripting hacks, allowing access to some special features.
   * [wiki:TracStartingPage Bug-reporting assistance] -- You can report bugs and request features using Trac. This links to the Trac documentation.


name: ScriptingHacks
author: mnehme
ipnr: 77.42.171.236
text:
  == Warning ==
  Features described here are *advanced*, *experimental* and *unsupported*. Some will make it to subsequent versions, and some will be dropped.

  == Script-Defined Parameters ==

  === Startup ===
  ''The following will '''ONLY WORK IF''' they are set within a user-defined function called currentView._render() in view views_0000_index. This is because they must be set before the app does its start-up routine.''

  '''localStorage.sendPendingCycle''': Sets the interval at which all pending data is sent to the server (in milliseconds). When not set, this happens every minute.

  '''localStorage.reloadCycle''': Sets the interval at which the entire app auto-refreshes (in milliseconds). When not set, auto-refresh happens every 2 hours. A negative value will de-activate the reloading.

  '''localStorage.reloadMsg''': Sets the warning message that appears 30 seconds before the app auto-refreshes. When not set, the message is 'Warning: refreshing in less than 30 seconds. Please save your work!'


name: ScriptingHacks
author: mnehme
ipnr: 77.42.171.236
text:
  == Warning ==
  Features described here are *advanced*, *experimental* and *unsupported*. Some will make it to subsequent versions, and some will be dropped.

  == Script-Defined Parameters ==

  === Startup ===
  ''These start-up features will '''ONLY WORK''' prior to start-up of the app. Therefore they must be set within a user-defined function called currentView._render() in view views_0000_index. Otherwise they will be ignored.''

  '''localStorage.sendPendingCycle''': Sets the interval at which all pending data is sent to the server (in milliseconds). When not set, this happens every minute.

  '''localStorage.reloadCycle''': Sets the interval at which the entire app auto-refreshes (in milliseconds). When not set, auto-refresh happens every 2 hours. A negative value will de-activate the reloading.

  '''localStorage.reloadMsg''': Sets the warning message that appears 30 seconds before the app auto-refreshes. When not set, the message is 'Warning: refreshing in less than 30 seconds. Please save your work!'


name: FAQ
author: mnehme
ipnr: 77.42.171.236
text:
  [[PageOutline(1-5,Contents,inline)]]

  = Designing Views =
  == How can I create an element that resizes based on screen-size? ==
  This is not recommended. In order to make sure fonts are legible on all platforms, you should create one version of a view per platform. (For example, instead of creating a view called "Menu" you should create one called "Menu/iphone", then copy it into another called "Menu/ipad" and adjust as necessary).
  The differences in device screen-sizes are so great that if you do not do this, your text will not be legible.

  ==== But then I will have to duplicate scripts! ====
  No: simply create a view called "Menu" and write the script within that view. The views "Menu/iphone" and "Menu/ipad" will inherit from it. So any script that you put in "Menu" will apply to "Menu/iphone", and any design elements you put in "Menu" will be visible as a background to the view "Menu/iphone"

  ==== But I still want to do it. ====
  If you insist:
  Alternative 1: Create a script fired by the viewOpenComplete event, which automatically resizes the div based on screen size. For this you’ll find it helpful to give the div a classname via the fill dialogue box.
  Alternative 2: 
   * Create the app for one screen size. In the following, we will assume it is the ipad screen size.
   * Once it is created, download the html, and edit the scale in the viewport meta-tag. Set it as follows: <meta name="viewport" id="viewport" content="width=device-width, initial-scale=0.42, maximum-scale=0.42, minimum-scale=0.42" />. This will create an html file that is resized to fit in the iphone. You may want to adjust the 0.42 factor slightly
   * The above will suffice to resize to iphone size. But in order for it to look good, you’ll also want to center it. Do so by adding <div style='position:relative;left:-5px;width:1024px;margin:0 auto;'>  around the entire content (but within the <body> tags) (An explanation of why his works is provided at [http://buildinternet.com/2008/11/how-to-center-a-website-with-css/])
   * Use the new html file to create a separate binary for the iphone. (Apple App Store Universal apps are generally a bad idea: they are useless since high ranking on ipad does not help iphone rank, or vice-versa. Furthermore, they force you to price your ipad app at the same price as the iphone one, even though willingness to pay is generally different.)

  == How do I align/distribute elements? ==
  First group them (by rapidly clicking on one after the other. Then select the group (it appears as a beige-bordered transparent container). Finally, open the "Position" menu and select a menu option. Optionally, you can ungroup with the "Ungroup" button (upper-right).

  = Creating Tables and Dialogue Boxes =
  == How can I create a database table? ==
  Simply create a dialogue box by following the instructions in the "Data" tab.
  Once you click "Go!", you will be taken to a design interface for the dialogue box.
   * Add graphic elements as desired.
   * Add user-input elements: one per intended table column. You can create these elements via the dialogue box in the menu: "Shapes, Images, Buttons" -> "User Input"
   * Create the "ok" button: create another user input element and give it the type “submit” and the name “_submit”, and the default value “Ok” (or whatever other default value you want)
   * Create the cancel button: Create another user input element and give it the type “submit” and the name “_cancel”, and the default value “Cancel” (or whatever other default value you want)
  And you’re done! If the dialogue was called (for example) dialogues/cld_Restaurants_insert_table.php, then you can programmatically open it via: dialogues_cld_Restaurants_insert_table()
  Once the user clicks “Ok” on the dialogue box, the data is automatically stored to be sent to the remote database.
  Whenever you want to send, simply execute cld_Restaurants.sendPendingAjax() (This works even if you don’t have internet, it will simply do a smart sync once you get back online.) This step is separate in order to avoid causing bandwidth problems. Often users will make minor changes to data they just input.
  '''Best practice:''' on viewOpenComplete of the top view, use a setInterval call to execute cld_Restaurants.sendPendingAjax() once every 60000 milliseconds (and do the same for every table you create.) This way you will not have to manually execute sendPendingAjax() every time.

  == How can I create a dialogue box? ==
  Do it by creating a table, as described above.
  If you want to create a second dialogue box corresponding to the same table, here is how:
   * From any one of the tabs ("Design", "Data" or "Dynamic"), click on the name of the current view on the top left of the screen. This takes you to the view selection page.
   * Click on the "New Dialogue" button.
   * Design the dialogue box, as described above.

  = Scripting =
  == How can I programatically access the cloud data? ==
  ==== To access the data in the table ====
  For example, let us assume you named the dialogue box "Restaurants" (when creating the table). Your cloud object is called cld_Restaurants.  Here is the script.
   * If you want the script to get the data from the remote database: use the cloud’s reload method:
  {{{
  #!java
  var cfg = {};
  cfg.successFunc = function(list){
  // Do something with “list”
  // “list” is an array of records. Each record is an object of the form
  // { colName1: "value of column 1", colName2: "value of column 2", colName3: "value of column 3", [etc……]}
  };
  // cfg.where = [["location","=",theLocation], ["type","=","Lebanese"]]; // An optional where clause
  cld_Restaurants.act['select'].reload(cfg);
  }}}
   * If you want the script, while waiting for the data to arrive from the remote database, to draw on the local cache: use the cloud’s data access method (getData) 
  {{{
  #!java
  var cfg = {};
  cfg.successFunc = function(list){
  // Do something with “list”
  // “list” is an array of records. Each record is an object of the form
  // { colName1: "value of column 1", colName2: "value of column 2", colName3: "value of column 3", [etc……]}
  };
  // cfg.where = [["location","=",theLocation], ["type","=","Lebanese"]]; // An optional where clause
  var list0 = cld_Restaurants.act['select'].getData(cfg);
  // Then do something with list0, which contains the local cache data
  }}}

  === To input data into the table ===
  The easiest way is to create a dialogue box (as described elsewhere) and do no scripting. If the dialogue was called (for example) dialogues/cld_Restaurants_insert_table.php, then you can programmatically open it via: dialogues_cld_Restaurants_insert_table()
  Once the user clicks “Ok” on the dialogue box, the data is automatically stored to be sent to the remote database.
  Whenever you want to send, simply execute cld_Restaurants.sendPendingAjax() (This works even if you don’t have internet, it will simply do a smart sync once you get back online.) This step is separate in order to avoid causing bandwidth problems. Often users will make minor changes to data they just input.

  Or you can do it programmatically as follows:
  {{{
  #!java
  var data={};
  data["ajaxAction"] = 'insert'; /* Other options: update, delete, undelete, deleteFinal. You can also use select and selectdistinct, however the reload and getData methods do this a lot better */
  data["Column 1 name"] = aValue;
  data["Column 2 name"] = aValue2;
  data["Column 3 name"] = aValue3;
  cld_Restaurants.sendPending(data);//This stores the data in the local cache, ready to be sent to the server
  cld_Restaurants.sendPendingAjax();//This forces all pending data to be synced with the server.
  /* For advanced users: set up an event handler to handle event sendPendingFailure. Do this via the following code:
  addEvent(cld_Restaurants.events, 'sendPendingFailure', function(ev){
  // “ev” has the form: {type:"sendPendingFailure",item:i,attempts:numberOfattempts,status:statusCode,
  // code:ajaxStatusCode,msg:"The error message."}. 
  // The “attempts” field is not there for all types of errors.
  });
  */
  }}}

  Finally, a more complex option is to create a dialogue box as described, but instead of just simply letting it do its job, intercept the user input, modify it, and then send it on. Here is how:
   * Create the dialogue box as described above
   * Create a function to handle the user input – something like the below:
  {{{
  #!java
  //function(ev){ // Commented out because, as you’ll see in a bit, it’s already there
        // Event handler for a dialogue box
        if(!(ev.value=='submit')) return;// If it’s not the submit button, do nothing
        if(!(ev.id== dialogues_cld_Restaurants_insert_table_1')) return; /* If it’s not instance 1 of the dialogue box dialogues_cld_Restaurants_insert_table then do nothing */
        
        var data = currentView.data; // “data” is now an associative array containing the dialogue box data
        
        // Now do something with the data
  /* For example, the below combines two dialogue box fields, called dialogueField1 and dialogueField2, into a single field */
        data['newField'] = data['dialogueField1']+" "+ data['dialogueField2'];
        delete data['dialogueField1'];
        delete data['dialogueField2'];

        currentView.data = data;// Once the data has been processed, overwrite the original data with the modified data
  //}// Commented out because, as you’ll see in a bit, it’s already there
  }}}
  Now all you need to do is go to the view in which the dialogue appears, go to the "Dynamics" tab, click on “Current View”, select the event dialogueButtonPress and copy-paste the function in there.
  WARNING: the lines “function(ev){” and the closing “}” are already there, so they shouldn’t be in your function!

  == How can I programatically control rejected insert/update/deletes of data by a user? ==
  Note: to understand the below, you will need to be familiar with advanced user-access rights management using _group and _altUser.

  When the app tries to access data and the user (or _group or _altUser) does not have access rights to the requested data, two events will fire: sendPendingFailure and NoAuthForThisContent. You can choose to catch either or both events. In the example of trying to insert something without the appropriate permissions, you may want to catch the event NoAuthForThisContent, delete the pending record from the cache and inform the user that they were unsuccessful at inserting as they didn't have the appropriate permissions with something like:

  {{{
  #!java
  var data = {};
  data["ajaxAction"] = 'insert';
  data["departmentName"]= 'blah';
  data["_group"] = "admin"; 
  cld_department.sendPending(data);
  cld_department.sendPendingAjax();

  addEvent(authEvents, "NoAuthForThisContent", function(ev){
     localStorage.removeItem(ev['recordKey']); // deletes the record from the cache
     alert("Your insert failed as you do not have permission to insert these records"); // you would replace this with a message displayed within your app as alerts look ugly
  });

  }}}


name: WikiStart
author: mnehme
ipnr: 178.135.204.20
text:
  == Mission: To speed App development by 20-30x ==
  Cedrus Development Environment is a platform to allow easy and fast development of mobile and web Apps. "Fast" means 20-30x faster than normal development.
  Though primarily targeting developers, CDE can also be used by product managers to speedily create prototypes with zero-coding. These prototypes can be used as both specs and first release, and speed up the production process by breaking down coding into simple tasks.

  == How it works ==
   * '''Fast design process:''' design is done via simple drag-and-drop.
   * '''Fast coding:''' CDE makes it easy and natural to break down code into small self-contained components, reducing complexity, while making coding very fast. 
   * '''Cloud-enabled:''' Automatically creates server-side databases, automatically creates access functions for you to use which even work offline, and provide intelligent syncing abilities.
   * '''Multi-platform:''' CDE creates apps written in Ajax/HTML5 for their front-ends, and PHP for their back-ends. As a result, it can be used with Phonegap to immediately port the App to all platforms.
   * '''Iterative prototyping:''' Naturally encourages iterative development and MVP, because iterating is blindingly fast.
   * '''Infinitely extensible:''' CDE of course includes the ability to actually code, so anything that can be done by coding can be done with CDE.

  == Limitations ==
  CDE works for most Apps, except for media-rich gaming Apps. For the latter, you might want to consider using a platform such as Unity3D.

  == Starting Points ==
   * [wiki:GettingStarted Getting Started] -- A quick introduction
   * [wiki:FAQ Cedrus Development Environment FAQ] -- Frequently Asked Questions
   * [wiki:NewApp Creating and Configuring a New App] -- Creating and Configuring a New App
   * [wiki:ScriptingHacks Hacks] -- Scripting hacks, allowing access to some special features.
   * [wiki:Tips Tips and Best Practices] -- Tips and Best Practices
   * [wiki:TracStartingPage Bug-reporting assistance] -- You can report bugs and request features using Trac. This links to the Trac documentation.


name: WikiStart
author: mnehme
ipnr: 178.135.204.20
text:
  == Mission: To speed App development by 20-30x ==
  Cedrus Development Environment is a platform to allow easy and fast development of mobile and web Apps. "Fast" means 20-30x faster than normal development.
  Though primarily targeting developers, CDE can also be used by product managers to speedily create prototypes with zero-coding. These prototypes can be used as both specs and first release, and speed up the production process by breaking down coding into simple tasks.

  == How it works ==
   * '''Fast design process:''' design is done via simple drag-and-drop.
   * '''Fast coding:''' CDE makes it easy and natural to break down code into small self-contained components, reducing complexity, while making coding very fast. 
   * '''Cloud-enabled:''' Automatically creates server-side databases, automatically creates access functions for you to use which even work offline, and provide intelligent syncing abilities.
   * '''Multi-platform:''' CDE creates apps written in Ajax/HTML5 for their front-ends, and PHP for their back-ends. As a result, it can be used with Phonegap to immediately port the App to all platforms.
   * '''Iterative prototyping:''' Naturally encourages iterative development and MVP, because iterating is blindingly fast.
   * '''Infinitely extensible:''' CDE of course includes the ability to actually code, so anything that can be done by coding can be done with CDE.

  == Limitations ==
  CDE works for most Apps, except for media-rich gaming Apps. For the latter, you might want to consider using a platform such as Unity3D.

  == Starting Points ==
   * [wiki:GettingStarted Getting Started] -- A quick introduction
   * [wiki:FAQ Cedrus Development Environment FAQ] -- Frequently Asked Questions
   * [wiki:NewApp I. Creating and Configuring a New App] -- Creating and Configuring a New App
   * [wiki:ScriptingHacks Hacks] -- Scripting hacks, allowing access to some special features.
   * [wiki:Tips Tips and Best Practices] -- Tips and Best Practices
   * [wiki:TracStartingPage Bug-reporting assistance] -- You can report bugs and request features using Trac. This links to the Trac documentation.


name: NewApp
author: mnehme
ipnr: 178.135.204.20
text:
  = Using Quick Start =
  Quick Start is an easy and fast way to create a new app. See [QuickStart here] for more details.

  = From the Main Panel =
  1. Go to the main panel (either by clicking on "Advanced" button, or by clicking on the logo of a house)
  2. Simply click on the "New...", and name your app
  3. ... and you're done!

  = Advanced Features =
  You can redesign (overwrite) the default login dialogue box and the registration dialogue box, as follows:
   * Login dialogue box: Create a dialogue box called dialogues/login__.php
   * Registration dialogue box: Create a dialogue box called dialogues/register__.php

  Note: By default, these names are not allowed. You will need to create the dialogue box, open it from the "Design" tab, and rename it (File menu -> Rename)

  = Advanced Scripting =
  '''Warning:''' use only if you are an advanced programmer, and at your own risk. The following allows you to overwrite CDE functionality with some of your own. It is very powerful, but it also gives you the power to mess things up. This functionality is unsupported, and may break backward compatibility.

  First, you need to give yourself access to the advanced scripts:
  1. Click on the "Design" tab
  2. Create a new page with a blank name
  3. In the tree, click on views//index.php
  4. Rename it to views/index.php (File menu -> Rename)
  5. Open this view in the "Dynamics" tab

  Once you have done this, you can overwrite any one of the following default CDE functions with one of your own:
   * currentView.setLogin(): Sets up login functionality. The default version (which you may overwrite) does the following:
     * Defines the callback function on authentication failure. This callback launches the login dialogue box (and also enables Facebook login where relevant), and attaches the relevant callbacks to buttons of this dialogue box. If registration by new users is allowed, it also adds a link to a Registration dialogue box, and adds callbacks to the buttons of that box.
     * Adds event listeners to authEvents (for events AuthFail, UserNotRegistered and UserUndefined)
     * Executes auth();
   * currentView.loginHandle(): Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setRegistration(): Called by default when user clicks on the registration link in the Login dialogue box. (Of course, it will not be called if you overwrote currentView.setLogin())
   * currentView.registrationHandle: Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.registrationValidation(): Function for validation of user registration dialogue box fields. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setPeriodic: Function defining any housekeeping tasks that must be executed on a regular schedule. By default, these are:
     * Refreshing the app every 2 hours
     * Sending any pending data to the server every minute

  In order to overwrite, from the Dynamics tab go to the script view by clicking on any element of the page.


  Define userDefined.js
  Upload css/base.css, css/ie.css, css/non_ie.css

  window.onerror=function(){
    document.body.style.backgroundColor='#fff5f5';
  };


name: WikiStart
author: mnehme
ipnr: 178.135.204.20
text:
  == Mission: To speed App development by 20-30x ==
  Cedrus Development Environment is a platform to allow easy and fast development of mobile and web Apps. "Fast" means 20-30x faster than normal development.
  Though primarily targeting developers, CDE can also be used by product managers to speedily create prototypes with zero-coding. These prototypes can be used as both specs and first release, and speed up the production process by breaking down coding into simple tasks.

  == How it works ==
   * '''Fast design process:''' design is done via simple drag-and-drop.
   * '''Fast coding:''' CDE makes it easy and natural to break down code into small self-contained components, reducing complexity, while making coding very fast. 
   * '''Cloud-enabled:''' Automatically creates server-side databases, automatically creates access functions for you to use which even work offline, and provide intelligent syncing abilities.
   * '''Multi-platform:''' CDE creates apps written in Ajax/HTML5 for their front-ends, and PHP for their back-ends. As a result, it can be used with Phonegap to immediately port the App to all platforms.
   * '''Iterative prototyping:''' Naturally encourages iterative development and MVP, because iterating is blindingly fast.
   * '''Infinitely extensible:''' CDE of course includes the ability to actually code, so anything that can be done by coding can be done with CDE.

  == Limitations ==
  CDE works for most Apps, except for media-rich gaming Apps. For the latter, you might want to consider using a platform such as Unity3D.

  == Starting Points ==
   * [wiki:GettingStarted Getting Started] -- A quick introduction
   * [wiki:FAQ Cedrus Development Environment FAQ] -- Frequently Asked Questions
   * [wiki:NewApp I. Creating and Configuring a New App] -- Creating and Configuring a New App
   * [wiki:NewApp IV. Advanced Features] -- Advanced Features
   * [wiki:ScriptingHacks Hacks] -- Scripting hacks, allowing access to some special features.
   * [wiki:Tips Tips and Best Practices] -- Tips and Best Practices
   * [wiki:TracStartingPage Bug-reporting assistance] -- You can report bugs and request features using Trac. This links to the Trac documentation.


name: WikiStart
author: mnehme
ipnr: 178.135.204.20
text:
  == Mission: To speed App development by 20-30x ==
  Cedrus Development Environment is a platform to allow easy and fast development of mobile and web Apps. "Fast" means 20-30x faster than normal development.
  Though primarily targeting developers, CDE can also be used by product managers to speedily create prototypes with zero-coding. These prototypes can be used as both specs and first release, and speed up the production process by breaking down coding into simple tasks.

  == How it works ==
   * '''Fast design process:''' design is done via simple drag-and-drop.
   * '''Fast coding:''' CDE makes it easy and natural to break down code into small self-contained components, reducing complexity, while making coding very fast. 
   * '''Cloud-enabled:''' Automatically creates server-side databases, automatically creates access functions for you to use which even work offline, and provide intelligent syncing abilities.
   * '''Multi-platform:''' CDE creates apps written in Ajax/HTML5 for their front-ends, and PHP for their back-ends. As a result, it can be used with Phonegap to immediately port the App to all platforms.
   * '''Iterative prototyping:''' Naturally encourages iterative development and MVP, because iterating is blindingly fast.
   * '''Infinitely extensible:''' CDE of course includes the ability to actually code, so anything that can be done by coding can be done with CDE.

  == Limitations ==
  CDE works for most Apps, except for media-rich gaming Apps. For the latter, you might want to consider using a platform such as Unity3D.

  == Starting Points ==
   * [wiki:GettingStarted Getting Started] -- A quick introduction
   * [wiki:FAQ Cedrus Development Environment FAQ] -- Frequently Asked Questions
   * [wiki:NewApp I. Creating and Configuring a New App] -- Creating and Configuring a New App
   * [wiki:Advanced IV. Advanced Features] -- Advanced Features
   * [wiki:ScriptingHacks Hacks] -- Scripting hacks, allowing access to some special features.
   * [wiki:Tips Tips and Best Practices] -- Tips and Best Practices
   * [wiki:TracStartingPage Bug-reporting assistance] -- You can report bugs and request features using Trac. This links to the Trac documentation.


name: Tips
author: mnehme
ipnr: 178.135.204.20
text:
  Define userDefined.js
  Upload css/base.css, css/ie.css, css/non_ie.css

  window.onerror=function(){
    document.body.style.backgroundColor='#fff5f5';
  };


name: Advanced
author: mnehme
ipnr: 178.135.204.20
text:
  = Advanced Configuration =
  == Modifying Default Dialogue Boxes ==
  You can redesign (overwrite) the default login dialogue box and the registration dialogue box, as follows:
   * Login dialogue box: Create a dialogue box called dialogues/login__.php
   * Registration dialogue box: Create a dialogue box called dialogues/register__.php

  Note: By default, these names are not allowed. You will need to create the dialogue box, open it from the "Design" tab, and rename it (File menu -> Rename)

  = Advanced Programming =
  == Login and Registration ==
  '''Warning:''' use only if you are an advanced programmer, and at your own risk. The following allows you to overwrite CDE functionality with some of your own. It is very powerful, but it also gives you the power to mess things up. This functionality is unsupported, and may break backward compatibility.

  First, you need to give yourself access to the advanced scripts:
  1. Click on the "Design" tab
  2. Create a new page with a blank name
  3. In the tree, click on views//index.php
  4. Rename it to views/index.php (File menu -> Rename)
  5. Open this view in the "Dynamics" tab

  Once you have done this, you can overwrite any one of the following default CDE functions with one of your own:
   * currentView.setLogin(): Sets up login functionality. The default version (which you may overwrite) does the following:
     * Defines the callback function on authentication failure. This callback launches the login dialogue box (and also enables Facebook login where relevant), and attaches the relevant callbacks to buttons of this dialogue box. If registration by new users is allowed, it also adds a link to a Registration dialogue box, and adds callbacks to the buttons of that box.
     * Adds event listeners to authEvents (for events AuthFail, UserNotRegistered and UserUndefined)
     * Executes auth();
   * currentView.loginHandle(): Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setRegistration(): Called by default when user clicks on the registration link in the Login dialogue box. (Of course, it will not be called if you overwrote currentView.setLogin())
   * currentView.registrationHandle: Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.registrationValidation(): Function for validation of user registration dialogue box fields. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setPeriodic: Function defining any housekeeping tasks that must be executed on a regular schedule. By default, these are:
     * Refreshing the app every 2 hours
     * Sending any pending data to the server every minute

  In order to overwrite, from the Dynamics tab go to the script view by clicking on any element of the page.


name: NewApp
author: mnehme
ipnr: 178.135.204.20
text:
  = Using Quick Start =
  Quick Start is an easy and fast way to create a new app. See [QuickStart here] for more details.

  = From the Main Panel =
  1. Go to the main panel (either by clicking on "Advanced" button, or by clicking on the logo of a house)
  2. Simply click on the "New...", and name your app
  3. ... and you're done!


name: NewApp
author: mnehme
ipnr: 178.135.204.20
text:
  = Using Quick Start =
  Quick Start is an easy and fast way to create a new app. From the start-up page, simply click on the "Quick Start" button then follow the tutorial.

  = From the Main Panel =
  1. Go to the main panel (either by clicking on "Advanced" button, or by clicking on the logo of a house)
  2. Simply click on the "New...", and name your app
  3. ... and you're done!


name: Advanced
author: mnehme
ipnr: 178.135.204.20
text:
  = Advanced Configuration =
  == Adding CSS ==
  A CSS file can be uploaded from the main panel, in the "Upload Files" section.

  == Modifying Default Dialogue Boxes ==
  You can redesign (overwrite) the default login dialogue box and the registration dialogue box, as follows:
   * Login dialogue box: Create a dialogue box called dialogues/login__.php
   * Registration dialogue box: Create a dialogue box called dialogues/register__.php

  Note: By default, these names are not allowed. You will need to create the dialogue box, open it from the "Design" tab, and rename it (File menu -> Rename)

  = Advanced Programming =
  == Custom Functions ==
  In order to define your own custom functions and libraries, you can upload a javascript file from the main panel, in the "Upload Files" section. However it is recommended to work via the CDE platform whenever possible: CDE reduces bugs by giving functions local scope instead of global scope.

  == Login and Registration ==
  '''Warning:''' use only if you are an advanced programmer, and at your own risk. The following allows you to overwrite CDE functionality with some of your own. It is very powerful, but it also gives you the power to mess things up. This functionality is unsupported, and may break backward compatibility.

  First, you need to give yourself access to the advanced scripts:
  1. Click on the "Design" tab
  2. Create a new page with a blank name
  3. In the tree, click on views//index.php
  4. Rename it to views/index.php (File menu -> Rename)
  5. Open this view in the "Dynamics" tab

  Once you have done this, you can overwrite any one of the following default CDE functions with one of your own:
   * currentView.setLogin(): Sets up login functionality. The default version (which you may overwrite) does the following:
     * Defines the callback function on authentication failure. This callback launches the login dialogue box (and also enables Facebook login where relevant), and attaches the relevant callbacks to buttons of this dialogue box. If registration by new users is allowed, it also adds a link to a Registration dialogue box, and adds callbacks to the buttons of that box.
     * Adds event listeners to authEvents (for events AuthFail, UserNotRegistered and UserUndefined)
     * Executes auth();
   * currentView.loginHandle(): Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setRegistration(): Called by default when user clicks on the registration link in the Login dialogue box. (Of course, it will not be called if you overwrote currentView.setLogin())
   * currentView.registrationHandle: Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.registrationValidation(): Function for validation of user registration dialogue box fields. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setPeriodic: Function defining any housekeeping tasks that must be executed on a regular schedule. By default, these are:
     * Refreshing the app every 2 hours
     * Sending any pending data to the server every minute

  In order to overwrite, from the Dynamics tab go to the script view by clicking on any element of the page.


name: Tips
author: mnehme
ipnr: 178.135.204.20
text:
  = Javascript Tips for Experienced Programmers =
  1. alert() can subtly modify program flow. So don't use it for debugging, and better yet, don't use it at all. This is due to javascript being an asynchronous language.
  2. [addEvent] and [removeEvent] are the most important specificities of the language. Javascript is event-driven.
  3. There is no block scope. The only scopes are global; and local (current function). Bugs often crop up because any variable defined within a function as in "a = 1;" rather than "var a = 1;" is global.
   * Corollary 1: in for loops use "for(var i=0;" not "for(i=0;". Otherwise you will get the weirdest bugs.
   * Corollary 2: avoid global variables at all costs. The bugs they cause can be remarkably difficult to track.
  4. Be careful when setting visibility of HTML nodes: 
  {{{
  nd.style.visibility = "visible"; // Avoid this. Element will be visible even if parents are hidden
  nd.style.visibility = "inherit"; // Use this instead
  }}}
  5. Debugging tools in javascript are less advanced than in other languages. The two most advanced are Chrome console and Firebug. They allow you to:
   * Inspect an html element, and modify it's css
   * Type code from the console
   * Write to the console with CDE's logg() function or their console.log(), console.warn() and console.error() functions.
   * Add breakpoints, step into/out of, etc.

  = Best Practices =

  window.onerror=function(){
    document.body.style.backgroundColor='#fff5f5';
  };


name: Tips
author: mnehme
ipnr: 178.135.204.20
text:
  = Javascript Tips for Experienced Programmers =
  1. alert() can subtly modify program flow. So don't use it for debugging, and better yet, don't use it at all. This is due to javascript being an asynchronous language.
  2. [addEvent addEvent] and [removeEvent removeEvent] are the most important specificities of the language. Javascript is event-driven.
  3. There is no block scope. The only scopes are global; and local (current function). Bugs often crop up because any variable defined within a function as in "a = 1;" rather than "var a = 1;" is global.
   * Corollary 1: in for loops use "for(var i=0;" not "for(i=0;". Otherwise you will get the weirdest bugs.
   * Corollary 2: avoid global variables at all costs. The bugs they cause can be remarkably difficult to track.
  4. Be careful when setting visibility of HTML nodes: 
  {{{
  nd.style.visibility = "visible"; // Avoid this. Element will be visible even if parents are hidden
  nd.style.visibility = "inherit"; // Use this instead
  }}}
  5. Debugging tools in javascript are less advanced than in other languages. The two most advanced are Chrome console and Firebug. They allow you to:
   * Inspect an html element, and modify it's css
   * Type code from the console
   * Write to the console with CDE's logg() function or their console.log(), console.warn() and console.error() functions.
   * Add breakpoints, step into/out of, etc.

  = Best Practices =

  window.onerror=function(){
    document.body.style.backgroundColor='#fff5f5';
  };


name: WikiStart
author: mnehme
ipnr: 178.135.204.20
text:
  == Mission: To speed App development by 20-30x ==
  Cedrus Development Environment is a platform to allow easy and fast development of mobile and web Apps. "Fast" means 20-30x faster than normal development.
  Though primarily targeting developers, CDE can also be used by product managers to speedily create prototypes with zero-coding. These prototypes can be used as both specs and first release, and speed up the production process by breaking down coding into simple tasks.

  == How it works ==
   * '''Fast design process:''' design is done via simple drag-and-drop.
   * '''Fast coding:''' CDE makes it easy and natural to break down code into small self-contained components, reducing complexity, while making coding very fast. 
   * '''Cloud-enabled:''' Automatically creates server-side databases, automatically creates access functions for you to use which even work offline, and provide intelligent syncing abilities.
   * '''Multi-platform:''' CDE creates apps written in Ajax/HTML5 for their front-ends, and PHP for their back-ends. As a result, it can be used with Phonegap to immediately port the App to all platforms.
   * '''Iterative prototyping:''' Naturally encourages iterative development and MVP, because iterating is blindingly fast.
   * '''Infinitely extensible:''' CDE of course includes the ability to actually code, so anything that can be done by coding can be done with CDE.

  == Limitations ==
  CDE works for most Apps, except for media-rich gaming Apps. For the latter, you might want to consider using a platform such as Unity3D.

  == Starting Points ==
   * [wiki:GettingStarted Getting Started] -- A quick introduction
   * [wiki:FAQ Cedrus Development Environment FAQ] -- Frequently Asked Questions
   * [wiki:NewApp I. Creating and Configuring a New App] -- Creating and Configuring a New App
   * [wiki:Advanced IV. Advanced Features] -- Advanced Features
   * [wiki:ScriptingHacks Hacks] -- Scripting hacks, allowing access to some special features.
   * [wiki:Tips Tips and Best Practices] -- Tips and Best Practices
   * [wiki:TracStartingPage Bug-reporting assistance] -- You can report bugs and request features using Trac. This links to the Trac documentation. See also its [TitleIndex Index]


name: Tips
author: mnehme
ipnr: 178.135.204.20
text:
  = Javascript Tips for Experienced Programmers =
  1. alert() can subtly modify program flow. So don't use it for debugging, and better yet, don't use it at all. This is due to javascript being an asynchronous language.
  2. [AddEvent addEvent] and [RemoveEvent removeEvent] are the most important specificities of the language. Javascript is event-driven.
  3. There is no block scope. The only scopes are global; and local (current function). Bugs often crop up because any variable defined within a function as in "a = 1;" rather than "var a = 1;" is global.
   * Corollary 1: in for loops use "for(var i=0;" not "for(i=0;". Otherwise you will get the weirdest bugs.
   * Corollary 2: avoid global variables at all costs. The bugs they cause can be remarkably difficult to track.
  4. Be careful when setting visibility of HTML nodes: 
  {{{
  nd.style.visibility = "visible"; // Avoid this. Element will be visible even if parents are hidden
  nd.style.visibility = "inherit"; // Use this instead
  }}}
  5. Debugging tools in javascript are less advanced than in other languages. The two most advanced are Chrome console and Firebug. They allow you to:
   * Inspect an html element, and modify it's css
   * Type code from the console
   * Write to the console with CDE's logg() function or their console.log(), console.warn() and console.error() functions.
   * Add breakpoints, step into/out of, etc.

  = Best Practices =

  window.onerror=function(){
    document.body.style.backgroundColor='#fff5f5';
  };


name: AddEvent
author: mnehme
ipnr: 178.135.204.20
text:
  = Summary =
  addEvent() registers an event listener on the target. It is recommended in place of javascript's [https://developer.mozilla.org/en-US/docs/DOM/element.addEventListener addEventListener] because:
   * It recognizes [CustomEvents custom events]
   * It keeps a list of all events added. (Helpful for debugging purposes)

  = Syntax =
  {{{
  addEvent(target, type, listener[, useCapture]);
  }}}
  Each element is defined exactly as in [https://developer.mozilla.org/en-US/docs/DOM/element.addEventListener addEventListener]


name: RemoveEvent
author: mnehme
ipnr: 178.135.204.20
text:
  = Summary =
  removeEvent() removes a registered event listener from the target. It is recommended in place of javascript's [https://developer.mozilla.org/en-US/docs/DOM/element.removeEventListener removeEventListener] because:
   * It recognizes [CustomEvents custom events]
   * It keeps track of events added and removed. (Helpful for debugging purposes)

  = Syntax =
  {{{
  removeEvent(target, type, listener[, useCapture]);
  }}}
  Each parameter is defined exactly as in [https://developer.mozilla.org/en-US/docs/DOM/element.removeEventListener removeEventListener]


name: AddEvent
author: mnehme
ipnr: 178.135.204.20
text:
  = Summary =
  addEvent() registers an event listener on the target. It is recommended in place of javascript's [https://developer.mozilla.org/en-US/docs/DOM/element.addEventListener addEventListener] because:
   * It recognizes [CustomEvents custom events] such as tapend (a combination of mousend and touchend) and AuthFail (authentication failure)
   * It keeps track of events added and removed. (Helpful for debugging purposes)

  = Syntax =
  {{{
  addEvent(target, type, listener[, useCapture]);
  }}}
  Each parameter is defined exactly as in [https://developer.mozilla.org/en-US/docs/DOM/element.addEventListener addEventListener]


name: RemoveEvent
author: mnehme
ipnr: 178.135.204.20
text:
  = Summary =
  removeEvent() removes a registered event listener from the target. It is recommended in place of javascript's [https://developer.mozilla.org/en-US/docs/DOM/element.removeEventListener removeEventListener] because:
   * It recognizes [CustomEvents custom events] such as tapend (a combination of mousend and touchend) and AuthFail (authentication failure)
   * It keeps track of events added and removed. (Helpful for debugging purposes)

  = Syntax =
  {{{
  removeEvent(target, type, listener[, useCapture]);
  }}}
  Each parameter is defined exactly as in [https://developer.mozilla.org/en-US/docs/DOM/element.removeEventListener removeEventListener]


name: Tips
author: mnehme
ipnr: 178.135.204.20
text:
  = Javascript Tips for Experienced Programmers =
  1. alert() can subtly modify program flow. So don't use it for debugging, and better yet, don't use it at all. This is due to javascript being an asynchronous language.
  2. [AddEvent addEvent] and [RemoveEvent removeEvent] are the most important specificities of the language. Javascript is event-driven.
  3. There is no block scope. The only scopes are global; and local (current function). Bugs often crop up because any variable defined within a function as in "a = 1;" rather than "var a = 1;" is global.
   * Corollary 1: in for loops use "for(var i=0;" not "for(i=0;". Otherwise you will get the weirdest bugs.
   * Corollary 2: avoid global variables at all costs. The bugs they cause can be remarkably difficult to track.
  4. Be careful when setting visibility of HTML nodes: 
  {{{
  nd.style.visibility = "visible"; // Avoid this. Element will be visible even if parents are hidden
  nd.style.visibility = "inherit"; // Use this instead
  }}}
  5. Debugging tools in javascript are less advanced than in other languages. The two most advanced are Chrome console and Firebug. They allow you to:
   * Inspect an html element, and modify it's css
   * Type code from the console
   * Write to the console with CDE's logg() function or their console.log(), console.warn() and console.error() functions.
   * Add breakpoints, step into/out of, etc.

  = Best Practices =
  The following are best-practices you may want to consider. They are merely suggestions.
   * Take advantage of the folder structure which CDE allows grouping related pages under one folder.
   * Consider creating a seperate page for each device (e.g. one for iPad, one for a regular browser etc.), and grouping all of these into one folder that shares scripts and some graphical elements among them.
   * When a page uses dialogue boxes, you may need a way to automatically turn off any page interactivity every time a dialogue box opens. To do this, instead of creating just one page (for example, views/thePage/index.php), it is better to:
     * Create the page (views/thePage/index.php)
     * Design the page (that way both subpages inherit the design), but without adding the scripts
     * Create two subfolders, one for the page itself (views/thePage/main/index.php) and one for the page with dialogue boxes open (views/thePage/dia/index.php)
     * Add any scripts to (views/thePage/main/index.php). Also, whenever the script calls a dialogue box, go to (views/thePage/dia/index.php), and whenever the dialogue box is closed, return to (views/thePage/main/index.php)
   * Create an easy way to detect errors. For example, adding the following causes any subsequent error to cause the page background color to become light salmon. This probably won't bother a regular user, but it will help you identify issues.

  {{{
  window.onerror=function(){
    document.body.style.backgroundColor='#fff5f5';
  };
  }}}


name: Tips
author: mnehme
ipnr: 178.135.204.20
text:
  = Javascript Tips for Experienced Programmers =
  1. alert() can subtly modify program flow. So don't use it for debugging, and better yet, don't use it at all. This is due to javascript being an asynchronous language.
  2. [AddEvent addEvent] and [RemoveEvent removeEvent] are the most important specificities of the language. Javascript is event-driven.
  3. There is no block scope. The only scopes are global; and local (current function). Bugs often crop up because any variable defined within a function as in "a = 1;" rather than "var a = 1;" is global.
   * Corollary 1: in for loops use "for(var i=0;" not "for(i=0;". Otherwise you will get the weirdest bugs.
   * Corollary 2: avoid global variables at all costs. The bugs they cause can be remarkably difficult to track.
  4. Be careful when setting visibility of HTML nodes: 
  {{{
  nd.style.visibility = "visible"; // Avoid this. Element will be visible even if parents are hidden
  nd.style.visibility = "inherit"; // Use this instead
  }}}
  5. Debugging tools in javascript are less advanced than in other languages. The two most advanced are Chrome console and Firebug. They allow you to:
   * Inspect an html element, and modify it's css
   * Type code from the console
   * Write to the console with CDE's logg() function or their console.log(), console.warn() and console.error() functions.
   * Add breakpoints, step into/out of, etc.
  6. Understand how keyword "this" behaves. (It is a little unusual)
   * Info can be found [https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/this here]
   * Often, in an object, it is useful to have a line "var that = this;". Such a line allows you to use the variable "that" in order to access the original value of "this", from within methods and callbacks. (This trick is based on closures - see below).
  6. Check up on two important and unusual features of javascript: 
   * Asynchronous javascript and callbacks
   * Anonymous functions
   * Closures
  7. If you have more time, check out javascript objects (really nothing more than an unordered list or hash) and prototypical inheritance.

  = Best Practices =
  The following are best-practices you may want to consider. They are merely suggestions.
   * Take advantage of the folder structure which CDE allows grouping related pages under one folder.
   * Consider creating a seperate page for each device (e.g. one for iPad, one for a regular browser etc.), and grouping all of these into one folder that shares scripts and some graphical elements among them.
   * When a page uses dialogue boxes, you may need a way to automatically turn off any page interactivity every time a dialogue box opens. To do this, instead of creating just one page (for example, views/thePage/index.php), it is better to:
     * Create the page (views/thePage/index.php)
     * Design the page (that way both subpages inherit the design), but without adding the scripts
     * Create two subfolders, one for the page itself (views/thePage/main/index.php) and one for the page with dialogue boxes open (views/thePage/dia/index.php)
     * Add any scripts to (views/thePage/main/index.php). Also, whenever the script calls a dialogue box, go to (views/thePage/dia/index.php), and whenever the dialogue box is closed, return to (views/thePage/main/index.php)
   * Create an easy way to detect errors. For example, adding the following causes any subsequent error to cause the page background color to become light salmon. This probably won't bother a regular user, but it will help you identify issues.

  {{{
  window.onerror=function(){
    document.body.style.backgroundColor='#fff5f5';
  };
  }}}


name: Tips
author: mnehme
ipnr: 178.135.204.20
text:
  = Javascript Tips for Experienced Programmers =
  1. alert() can subtly modify program flow. So don't use it for debugging, and better yet, don't use it at all. This is due to javascript being an asynchronous language.
  2. [AddEvent addEvent] and [RemoveEvent removeEvent] are the most important specificities of the language. Javascript is event-driven.
  3. There is no block scope. The only scopes are global; and local (current function). Bugs often crop up because any variable defined within a function as in "a = 1;" rather than "var a = 1;" is global.
   * Corollary 1: in for loops use "for(var i=0;" not "for(i=0;". Otherwise you will get the weirdest bugs.
   * Corollary 2: avoid global variables at all costs. The bugs they cause can be remarkably difficult to track.
  4. Be careful when setting visibility of HTML nodes: 
  {{{
  nd.style.visibility = "visible"; // Avoid this. Element will be visible even if parents are hidden
  nd.style.visibility = "inherit"; // Use this instead
  }}}
  5. Debugging tools in javascript are less advanced than in other languages. The two most advanced are Chrome console and Firebug. They allow you to:
   * Inspect an html element, and modify it's css
   * Type code from the console
   * Write to the console with CDE's logg() function or their console.log(), console.warn() and console.error() functions.
   * Add breakpoints, step into/out of, etc.
  6. Understand how keyword "this" behaves. (It is a little unusual)
   * Info can be found [https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/this here]
   * Often, in an object, it is useful to have a line "var that = this;". Such a line allows you to use the variable "that" in order to access the original value of "this", from within methods and callbacks. (This trick is based on closures - see below).
  6. Check up on two important and unusual features of javascript: 
   * Asynchronous javascript and callbacks
   * Anonymous functions
   * Closures
  7. If you have more time, check out javascript objects (really nothing more than an unordered list or hash) and prototypical inheritance.

  = CDE Best Practices =
  The following are best-practices you may want to consider. They are merely suggestions.
   * Take advantage of the folder structure which CDE allows grouping related pages under one folder.
   * Consider creating a seperate page for each device (e.g. one for iPad, one for a regular browser etc.), and grouping all of these into one folder that shares scripts and some graphical elements among them.
   * When a page uses dialogue boxes, you may need a way to automatically turn off any page interactivity every time a dialogue box opens. To do this, instead of creating just one page (for example, views/thePage/index.php), it is better to:
     * Create the page (views/thePage/index.php)
     * Design the page (that way both subpages inherit the design), but without adding the scripts
     * Create two subfolders, one for the page itself (views/thePage/main/index.php) and one for the page with dialogue boxes open (views/thePage/dia/index.php)
     * Add any scripts to (views/thePage/main/index.php). Also, whenever the script calls a dialogue box, go to (views/thePage/dia/index.php), and whenever the dialogue box is closed, return to (views/thePage/main/index.php)
   * Create an easy way to detect errors. For example, adding the following causes any subsequent error to cause the page background color to become light salmon. This probably won't bother a regular user, but it will help you identify issues.

  {{{
  window.onerror=function(){
    document.body.style.backgroundColor='#fff5f5';
  };
  }}}


name: FAQ
author: mnehme
ipnr: 178.135.204.20
text:
  [[PageOutline(1-5,Contents,inline)]]

  = Designing Views =
  == How can I create an element that resizes based on screen-size? ==
  This is not recommended. In order to make sure fonts are legible on all platforms, you should create one version of a view per platform. (For example, instead of creating a view called "Menu" you should create one called "Menu/iphone", then copy it into another called "Menu/ipad" and adjust as necessary).
  The differences in device screen-sizes are so great that if you do not do this, your text will not be legible.

  ==== But then I will have to duplicate scripts! ====
  No: simply create a view called "Menu" and write the script within that view. The views "Menu/iphone" and "Menu/ipad" will inherit from it. So any script that you put in "Menu" will apply to "Menu/iphone", and any design elements you put in "Menu" will be visible as a background to the view "Menu/iphone"

  ==== But I still want to do it. ====
  If you insist:
  Alternative 1: Create a script fired by the viewOpenComplete event, which automatically resizes the div based on screen size. For this you’ll find it helpful to give the div a classname via the fill dialogue box.
  Alternative 2: 
   * Create the app for one screen size. In the following, we will assume it is the ipad screen size.
   * Once it is created, download the html, and edit the scale in the viewport meta-tag. Set it as follows: <meta name="viewport" id="viewport" content="width=device-width, initial-scale=0.42, maximum-scale=0.42, minimum-scale=0.42" />. This will create an html file that is resized to fit in the iphone. You may want to adjust the 0.42 factor slightly
   * The above will suffice to resize to iphone size. But in order for it to look good, you’ll also want to center it. Do so by adding <div style='position:relative;left:-5px;width:1024px;margin:0 auto;'>  around the entire content (but within the <body> tags) (An explanation of why his works is provided at [http://buildinternet.com/2008/11/how-to-center-a-website-with-css/])
   * Use the new html file to create a separate binary for the iphone. (Apple App Store Universal apps are generally a bad idea: they are useless since high ranking on ipad does not help iphone rank, or vice-versa. Furthermore, they force you to price your ipad app at the same price as the iphone one, even though willingness to pay is generally different.)

  == How do I align/distribute elements? ==
  First group them (by rapidly clicking on one after the other. Then select the group (it appears as a beige-bordered transparent container). Finally, open the "Position" menu and select a menu option. Optionally, you can ungroup with the "Ungroup" button (upper-right).

  = Creating Tables and Dialogue Boxes =
  == How can I create a database table? ==
  Simply create a dialogue box by following the instructions in the "Data" tab.
  Once you click "Go!", you will be taken to a design interface for the dialogue box.
   * Add graphic elements as desired.
   * Add user-input elements: one per intended table column. You can create these elements via the dialogue box in the menu: "Shapes, Images, Buttons" -> "User Input"
   * Create the "ok" button: create another user input element and give it the type “submit” and the name “_submit”, and the default value “Ok” (or whatever other default value you want)
   * Create the cancel button: Create another user input element and give it the type “submit” and the name “_cancel”, and the default value “Cancel” (or whatever other default value you want)
  And you’re done! If the dialogue was called (for example) dialogues/cld_Restaurants_insert_table.php, then you can programmatically open it via: dialogues_cld_Restaurants_insert_table()
  Once the user clicks “Ok” on the dialogue box, the data is automatically stored to be sent to the remote database.
  Whenever you want to send, simply execute cld_Restaurants.sendPendingAjax() (This works even if you don’t have internet, it will simply do a smart sync once you get back online.) This step is separate in order to avoid causing bandwidth problems. Often users will make minor changes to data they just input.
  '''Best practice:''' on viewOpenComplete of the top view, use a setInterval call to execute cld_Restaurants.sendPendingAjax() once every 60000 milliseconds (and do the same for every table you create.) This way you will not have to manually execute sendPendingAjax() every time.

  == How can I create a dialogue box? ==
  Do it by creating a table, as described above.
  If you want to create a second dialogue box corresponding to the same table, here is how:
   * From any one of the tabs ("Design", "Data" or "Dynamic"), click on the name of the current view on the top left of the screen. This takes you to the view selection page.
   * Click on the "New Dialogue" button.
   * Design the dialogue box, as described above.

  = Scripting =
  == How can I programatically access the cloud data? ==
  ==== To access the data in the table ====
  For example, let us assume you named the dialogue box "Restaurants" (when creating the table). Your cloud object is called cld_Restaurants.  Here is the script.
   * If you want the script to get the data from the remote database: use the cloud’s reload method:
  {{{
  #!java
  var cfg = {};
  cfg.successFunc = function(list){
  // Do something with “list”
  // “list” is an array of records. Each record is an object of the form
  // { colName1: "value of column 1", colName2: "value of column 2", colName3: "value of column 3", [etc……]}
  };
  // cfg.where = [["location","=",theLocation], ["type","=","Lebanese"]]; // An optional where clause
  cld_Restaurants.act['select'].reload(cfg);
  }}}
   * If you want the script, while waiting for the data to arrive from the remote database, to draw on the local cache: use the cloud’s data access method (getData) 
  {{{
  #!java
  var cfg = {};
  cfg.successFunc = function(list){
  // Do something with “list”
  // “list” is an array of records. Each record is an object of the form
  // { colName1: "value of column 1", colName2: "value of column 2", colName3: "value of column 3", [etc……]}
  };
  // cfg.where = [["location","=",theLocation], ["type","=","Lebanese"]]; // An optional where clause
  var list0 = cld_Restaurants.act['select'].getData(cfg);
  // Then do something with list0, which contains the local cache data
  }}}

  === To input data into the table ===
  The easiest way is to create a dialogue box (as described elsewhere) and do no scripting. If the dialogue was called (for example) dialogues/cld_Restaurants_insert_table.php, then you can programmatically open it via: dialogues_cld_Restaurants_insert_table()
  Once the user clicks “Ok” on the dialogue box, the data is automatically stored to be sent to the remote database.
  Whenever you want to send, simply execute cld_Restaurants.sendPendingAjax() (This works even if you don’t have internet, it will simply do a smart sync once you get back online.) This step is separate in order to avoid causing bandwidth problems. Often users will make minor changes to data they just input.

  Or you can do it programmatically as follows:
  {{{
  #!java
  var data={};
  data["ajaxAction"] = 'insert'; /* Other options: update, delete, undelete, deleteFinal. You can also use select and selectdistinct, however the reload and getData methods do this a lot better */
  data["Column 1 name"] = aValue;
  data["Column 2 name"] = aValue2;
  data["Column 3 name"] = aValue3;
  cld_Restaurants.sendPending(data);//This stores the data in the local cache, ready to be sent to the server
  cld_Restaurants.sendPendingAjax();//This forces all pending data to be synced with the server.
  /* For advanced users: set up an event handler to handle event sendPendingFailure. Do this via the following code:
  addEvent(cld_Restaurants.events, 'sendPendingFailure', function(ev){
  // “ev” has the form: {type:"sendPendingFailure",item:i,attempts:numberOfattempts,status:statusCode,
  // code:ajaxStatusCode,msg:"The error message."}. 
  // The “attempts” field is not there for all types of errors.
  });
  */
  }}}

  Finally, a more complex option is to create a dialogue box as described, but instead of just simply letting it do its job, intercept the user input, modify it, and then send it on. Here is how:
   * Create the dialogue box as described above
   * Create a function to handle the user input – something like the below:
  {{{
  #!java
  //function(ev){ // Commented out because, as you’ll see in a bit, it’s already there
        // Event handler for a dialogue box
        if(!(ev.value=='submit')) return;// If it’s not the submit button, do nothing
        if(!(ev.id== dialogues_cld_Restaurants_insert_table_1')) return; /* If it’s not instance 1 of the dialogue box dialogues_cld_Restaurants_insert_table then do nothing */
        
        var data = currentView.data; // “data” is now an associative array containing the dialogue box data
        
        // Now do something with the data
  /* For example, the below combines two dialogue box fields, called dialogueField1 and dialogueField2, into a single field */
        data['newField'] = data['dialogueField1']+" "+ data['dialogueField2'];
        delete data['dialogueField1'];
        delete data['dialogueField2'];

        currentView.data = data;// Once the data has been processed, overwrite the original data with the modified data
  //}// Commented out because, as you’ll see in a bit, it’s already there
  }}}
  Now all you need to do is go to the view in which the dialogue appears, go to the "Dynamics" tab, click on “Current View”, select the event dialogueButtonPress and copy-paste the function in there.
  WARNING: the lines “function(ev){” and the closing “}” are already there, so they shouldn’t be in your function!

  == How can I programatically control rejected insert/update/deletes of data by a user? ==
  Note: to understand the below, you will need to be familiar with advanced user-access rights management using _group and _altUser.

  When the app tries to access data and the user (or _group or _altUser) does not have access rights to the requested data, two events will fire: sendPendingFailure and NoAuthForThisContent. You can choose to catch either or both events. In the example of trying to insert something without the appropriate permissions, you may want to catch the event NoAuthForThisContent, delete the pending record from the cache and inform the user that they were unsuccessful at inserting as they didn't have the appropriate permissions with something like:

  {{{
  #!java
  var data = {};
  data["ajaxAction"] = 'insert';
  data["departmentName"]= 'blah';
  data["_group"] = "admin"; 
  cld_department.sendPending(data);
  cld_department.sendPendingAjax();

  addEvent(authEvents, "NoAuthForThisContent", function(ev){
     localStorage.removeItem(ev['recordKey']); // deletes the record from the cache
     alert("Your insert failed as you do not have permission to insert these records"); // you would replace this with a message displayed within your app as alerts look ugly
  });

  }}}

  = Facebook Integration =
  == How can I integrate with Facebook? ==
  To enable Facebook login, simply go to the main panel, and in the "Facebook" section fill in the App ID and App Secret. It's as easy as that! See Advanced section for some additional things you can do.


name: RemoveEvent
author: mnehme
ipnr: 178.135.204.20
text:
  = Summary =
  removeEvent() removes a registered event listener from the target. It is recommended in place of javascript's [https://developer.mozilla.org/en-US/docs/DOM/element.removeEventListener removeEventListener] because:
   * It recognizes [CustomEvents custom events] such as tapend (a combination of mousend and touchend) and !AuthFail (authentication failure)
   * It keeps track of events added and removed. (Helpful for debugging purposes)

  = Syntax =
  {{{
  removeEvent(target, type, listener[, useCapture]);
  }}}
  Each parameter is defined exactly as in [https://developer.mozilla.org/en-US/docs/DOM/element.removeEventListener removeEventListener]


name: AddEvent
author: mnehme
ipnr: 178.135.204.20
text:
  = Summary =
  addEvent() registers an event listener on the target. It is recommended in place of javascript's [https://developer.mozilla.org/en-US/docs/DOM/element.addEventListener addEventListener] because:
   * It recognizes [CustomEvents custom events] such as tapend (a combination of mousend and touchend) and !AuthFail (authentication failure)
   * It keeps track of events added and removed. (Helpful for debugging purposes)

  = Syntax =
  {{{
  addEvent(target, type, listener[, useCapture]);
  }}}
  Each parameter is defined exactly as in [https://developer.mozilla.org/en-US/docs/DOM/element.addEventListener addEventListener]


name: Advanced
author: mnehme
ipnr: 178.135.204.20
text:
  = Advanced Configuration =
  == Adding CSS ==
  A CSS file can be uploaded from the main panel, in the "Upload Files" section.

  == Modifying Default Dialogue Boxes ==
  You can redesign (overwrite) the default login dialogue box and the registration dialogue box, as follows:
   * Login dialogue box: Create a dialogue box called dialogues/login__.php
   * Registration dialogue box: Create a dialogue box called dialogues/register__.php

  Note: By default, these names are not allowed. You will need to create the dialogue box, open it from the "Design" tab, and rename it (File menu -> Rename)

  == Facebook Integration ==
  To enable Facebook login, simply go to the main panel, and in the "Facebook" section fill in the App ID and App Secret. It's as easy as that!

  To automatically save user data on access to the app, go to “Data” tab, and create a table called fbuserdata and another called fbusergraph with a selection of fields from the below. (These are the supported fields.) '''Make sure your app requests permission from the user to access any needed field, otherwise it will not work.'''
  User data is automatically saved into the two tables as described below.

  fbuserdata:
    `fbid` varchar(256) NOT NULL,
    `firstname` varchar(256) NOT NULL,
    `lastname` varchar(256) NOT NULL,
    `email` varchar(256) NOT NULL,
    `location` varchar(256) NOT NULL,
    `gender` varchar(256) NOT NULL,
    `locale` varchar(256) NOT NULL

  fbusergraph:
    `name` varchar(256) NOT NULL,
    `category` varchar(256) NOT NULL


  = Advanced Programming =
  == Custom Functions ==
  In order to define your own custom functions and libraries, you can upload a javascript file from the main panel, in the "Upload Files" section. However it is recommended to work via the CDE platform whenever possible: CDE reduces bugs by giving functions local scope instead of global scope.

  == Login and Registration ==
  '''Warning:''' use only if you are an advanced programmer, and at your own risk. The following allows you to overwrite CDE functionality with some of your own. It is very powerful, but it also gives you the power to mess things up. This functionality is unsupported, and may break backward compatibility.

  First, you need to give yourself access to the advanced scripts:
  1. Click on the "Design" tab
  2. Create a new page with a blank name
  3. In the tree, click on views//index.php
  4. Rename it to views/index.php (File menu -> Rename)
  5. Open this view in the "Dynamics" tab

  Once you have done this, you can overwrite any one of the following default CDE functions with one of your own:
   * currentView.setLogin(): Sets up login functionality. The default version (which you may overwrite) does the following:
     * Defines the callback function on authentication failure. This callback launches the login dialogue box (and also enables Facebook login where relevant), and attaches the relevant callbacks to buttons of this dialogue box. If registration by new users is allowed, it also adds a link to a Registration dialogue box, and adds callbacks to the buttons of that box.
     * Adds event listeners to authEvents (for events AuthFail, UserNotRegistered and UserUndefined)
     * Executes auth();
   * currentView.loginHandle(): Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setRegistration(): Called by default when user clicks on the registration link in the Login dialogue box. (Of course, it will not be called if you overwrote currentView.setLogin())
   * currentView.registrationHandle: Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.registrationValidation(): Function for validation of user registration dialogue box fields. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setPeriodic: Function defining any housekeeping tasks that must be executed on a regular schedule. By default, these are:
     * Refreshing the app every 2 hours
     * Sending any pending data to the server every minute

  In order to overwrite, from the Dynamics tab go to the script view by clicking on any element of the page.


name: Advanced
author: mnehme
ipnr: 178.135.204.20
text:
  = Advanced Configuration =
  == Adding CSS ==
  A CSS file can be uploaded from the main panel, in the "Upload Files" section.

  == Modifying Default Dialogue Boxes ==
  You can redesign (overwrite) the default login dialogue box and the registration dialogue box, as follows:
   * Login dialogue box: Create a dialogue box called dialogues/login__.php
   * Registration dialogue box: Create a dialogue box called dialogues/register__.php

  Note: By default, these names are not allowed. You will need to create the dialogue box, open it from the "Design" tab, and rename it (File menu -> Rename)

  == Facebook Integration ==
  To enable Facebook login, simply go to the main panel, and in the "Facebook" section fill in the App ID and App Secret. It's as easy as that!

  To automatically save user data on access to the app, go to “Data” tab, and create a table called fbuserdata and another called fbusergraph with a selection of fields from the below. (These are the supported fields.) '''Make sure your app requests permission from the user to access any needed field, otherwise it will not work.'''

  User data is automatically saved into the two tables as described below.

  '''fbuserdata Table'''[[BR]]
  Fields: fbid, firstname, lastname, email, location, gender, locale, locale, ageMin, ageMax, funnelStage[[BR]]
  All of these are defined exactly as in the Facebook API, with the exception of:[[BR]]
  fbid: the user's facebook id[[BR]]
  funnelStage (beta feature): Keeps track of whether the user is a new user or a returning user[[BR]]

  '''fbusergraph Table''' (beta feature)[[BR]]
  Fields: name, category[[BR]]
  Each row of this table corresponds to a user like/interest/group.[[BR]]
  name: name of the like/interest/group[[BR]]
  category: a broad category of the like/interest/group

  = Advanced Programming =
  == Custom Functions ==
  In order to define your own custom functions and libraries, you can upload a javascript file from the main panel, in the "Upload Files" section. However it is recommended to work via the CDE platform whenever possible: CDE reduces bugs by giving functions local scope instead of global scope.

  == Login and Registration ==
  '''Warning:''' use only if you are an advanced programmer, and at your own risk. The following allows you to overwrite CDE functionality with some of your own. It is very powerful, but it also gives you the power to mess things up. This functionality is unsupported, and may break backward compatibility.

  First, you need to give yourself access to the advanced scripts:
  1. Click on the "Design" tab
  2. Create a new page with a blank name
  3. In the tree, click on views//index.php
  4. Rename it to views/index.php (File menu -> Rename)
  5. Open this view in the "Dynamics" tab

  Once you have done this, you can overwrite any one of the following default CDE functions with one of your own:
   * currentView.setLogin(): Sets up login functionality. The default version (which you may overwrite) does the following:
     * Defines the callback function on authentication failure. This callback launches the login dialogue box (and also enables Facebook login where relevant), and attaches the relevant callbacks to buttons of this dialogue box. If registration by new users is allowed, it also adds a link to a Registration dialogue box, and adds callbacks to the buttons of that box.
     * Adds event listeners to authEvents (for events AuthFail, UserNotRegistered and UserUndefined)
     * Executes auth();
   * currentView.loginHandle(): Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setRegistration(): Called by default when user clicks on the registration link in the Login dialogue box. (Of course, it will not be called if you overwrote currentView.setLogin())
   * currentView.registrationHandle: Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.registrationValidation(): Function for validation of user registration dialogue box fields. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setPeriodic: Function defining any housekeeping tasks that must be executed on a regular schedule. By default, these are:
     * Refreshing the app every 2 hours
     * Sending any pending data to the server every minute

  In order to overwrite, from the Dynamics tab go to the script view by clicking on any element of the page.


name: WikiStart
author: mnehme
ipnr: 178.135.204.20
text:
  == Mission: To speed App development by 20-30x ==
  Cedrus Development Environment is a platform to allow easy and fast development of mobile and web Apps. "Fast" means 20-30x faster than normal development.
  Though primarily targeting developers, CDE can also be used by product managers to speedily create prototypes with zero-coding. These prototypes can be used as both specs and first release, and speed up the production process by breaking down coding into simple tasks.

  == How it works ==
   * '''Fast design process:''' design is done via simple drag-and-drop.
   * '''Fast coding:''' CDE makes it easy and natural to break down code into small self-contained components, reducing complexity, while making coding very fast. 
   * '''Cloud-enabled:''' Automatically creates server-side databases, automatically creates access functions for you to use which even work offline, and provide intelligent syncing abilities.
   * '''Multi-platform:''' CDE creates apps written in Ajax/HTML5 for their front-ends, and PHP for their back-ends. As a result, it can be used with Phonegap to immediately port the App to all platforms.
   * '''Iterative prototyping:''' Naturally encourages iterative development and MVP, because iterating is blindingly fast.
   * '''Infinitely extensible:''' CDE of course includes the ability to actually code, so anything that can be done by coding can be done with CDE.

  == Limitations ==
  CDE works for most Apps, except for media-rich gaming Apps. For the latter, you might want to consider using a platform such as Unity3D.

  == Starting Points ==
   * [wiki:GettingStarted Getting Started] -- A quick introduction
   * [wiki:FAQ Cedrus Development Environment FAQ] -- Frequently Asked Questions
   * [wiki:NewApp I. Creating and Configuring a New App] -- Creating and Configuring a New App
   * [wiki:Advanced IV. Advanced Features] -- Advanced Features
   * [wiki:ScriptingHacks Hacks] -- Scripting hacks, allowing access to some special features.
   * [wiki:Tips Tips and Best Practices] -- Tips and Best Practices
   * [wiki:Troubleshooting Troubleshooting] -- Troubleshooting
   * [wiki:TracStartingPage Bug-reporting assistance] -- You can report bugs and request features using Trac. This links to the Trac documentation. See also its [TitleIndex Index]


name: Troubleshooting
author: mnehme
ipnr: 178.135.204.20
text:
  = First Thing To Do =
  The first thing to do if you have an issue is to:
   * Close the app
   * Clear the browser history (usually with ctrl-shift-del)
   * Re-open the app
   * Force refresh (ctrl-F5)

  = Common Issues =
  '''When I open the app I'm creating, I have an unexpected request to login! '''

  Unless you disabled authentication from the main panel, this is normal. The reason you didn't get a login request earlier is because, if you are logged into CDE, it automatically makes you be logged into any apps you are working on. In order to fix this issue, either log back into CDE, or log into the app.

  = Troubleshooting Questions =
  '''Did you use _ (or any other special character such as -, +, etc) in any variable names? Or page or dialogue name? Or script name? Or database field name?'''

  "_" is a reserved character. Using it is likely to cause malfunctions.


name: WikiStart
author: mnehme
ipnr: 178.135.204.20
text:
  == Mission: To speed App development by 20-30x ==
  Cedrus Development Environment is a platform to allow easy and fast development of mobile and web Apps. "Fast" means 20-30x faster than normal development.
  Though primarily targeting developers, CDE can also be used by product managers to speedily create prototypes with zero-coding. These prototypes can be used as both specs and first release, and speed up the production process by breaking down coding into simple tasks.

  == How it works ==
   * '''Fast design process:''' design is done via simple drag-and-drop.
   * '''Fast coding:''' CDE makes it easy and natural to break down code into small self-contained components, reducing complexity, while making coding very fast. 
   * '''Cloud-enabled:''' Automatically creates server-side databases, automatically creates access functions for you to use which even work offline, and provide intelligent syncing abilities.
   * '''Multi-platform:''' CDE creates apps written in Ajax/HTML5 for their front-ends, and PHP for their back-ends. As a result, it can be used with Phonegap to immediately port the App to all platforms.
   * '''Iterative prototyping:''' Naturally encourages iterative development and MVP, because iterating is blindingly fast.
   * '''Infinitely extensible:''' CDE of course includes the ability to actually code, so anything that can be done by coding can be done with CDE.

  == Limitations ==
  CDE works for most Apps, except for media-rich gaming Apps. For the latter, you might want to consider using a platform such as Unity3D.

  == Starting Points ==
   * [wiki:GettingStarted Getting Started] -- A quick introduction
   * [wiki:FAQ Cedrus Development Environment FAQ] -- Frequently Asked Questions
   * [wiki:NewApp I. Creating and Configuring a New App] -- Creating and Configuring a New App
   * [wiki:Advanced IV. Advanced Features] -- Advanced Features
   * [wiki:ScriptingHacks Hacks] -- Scripting hacks, allowing access to some special features.
   * [wiki:Tips Tips and Best Practices] -- Tips and Best Practices
   * [wiki:Troubleshooting Troubleshooting] -- Troubleshooting
   * [wiki:TracStartingPage Bug-reporting assistance] -- You can report bugs and request features using Trac. This links to the Trac documentation.


name: CustomEvents
author: mnehme
ipnr: 178.135.204.20
text:
  '''JSON Events'''
  JSONParseError (attached to JSONlogg): triggers when JSONParse() fails. JSONParse() is a more sophisticated version of JSON.parse(). Among other advantages, it tests the string for JSON-ness prior to attempting to parse it, thus avoiding irrelevant syntax errors.

  '''Authentication'''
  All of the following events are attached to the authEvents objects.
  AuthFail, AuthSuccess
  UserUndefined
  UserNotRegistered
  RegistrationSuccess
  NoAuthForThisContent

  '''Server response'''
  All of the following events are attached to the .events method of a DataObject:
  sendPendingSuccess, sendPendingFailure, allSent, thrownUnspecifiedError

  All of the following events are attached to the .events method of a DataObject:
  reloadSuccess, reloadFailure, thrownUnspecifiedError

  '''Views'''

  All of the following events are attached to the .events method of a view (i.e. a Rendering object)
  viewOpenComplete: fires as soon as the view has finished opening and all relevant events have been added. If you need some processing to occur before events are added, create a function currentView._render()[[BR]]
  viewClose

  '''Dialogue.events'''

  dialogueOpen: attached to the Dialogue class prototype. Since it is attached to the prototype and not to a specific dialogue object, it is necessary to verify the dialogue id at beginning of the callback script.

  Other view.events attached events:
  dialogueButtonPress // In case of one of the pre-defined buttons (_cancel, _submit), the triggered processing occurs before the default processing but after having collected date input into the dialogue, as well as generated data such as date and unique data id (index). window.currentView.data contains the data to be sent, and window.currentView.ev contains the event that triggered the dialogue. Former will be read back when default function resumes processing. Tip: can be used to do some actions/processing prior to submission of dialogue data. Note: In this case, it is necessary in the triggered function to use window.currentView.ev to access the event that launched the dialogue box, as opposed to the dialogueButtonPress event. Note: If no index is defined via the dialogue box, one is automatically generated and stored in window.currentView.data.index. Fires as: {type:'dialogueButtonPress', value:'submit', id:id, sendIndex:sendIndex} with latter being the returned value of sendPending
  dialogueCreate, dialogueClose

  Attached to draggable. Also attached to window.currentView.events:
  dragstart, dragend

  ajaxLoad, ajaxSuccess, ajaxFail
  cache events, etc. (review list of library scripts)
  onTimeElapsed e.g. to launch a settimeout command

  Composite events and gestures
  tapdown, tapmove, tapend
  doubletap, tripletap, taphold


name: CustomEvents
author: mnehme
ipnr: 178.135.204.20
text:
  '''JSON Events'''

  JSONParseError (attached to JSONlogg): triggers when JSONParse() fails. JSONParse() is a more sophisticated version of JSON.parse(). Among other advantages, it tests the string for JSON-ness prior to attempting to parse it, thus avoiding irrelevant syntax errors.

  '''Authentication'''

  All of the following events are attached to the authEvents objects.[[BR]]
  !AuthFail, !AuthSuccess[[BR]]
  !UserUndefined[[BR]]
  !UserNotRegistered[[BR]]
  !RegistrationSuccess[[BR]]
  !NoAuthForThisContent[[BR]]

  '''Server response'''

  All of the following events are attached to the .events method of a DataObject:[[BR]]
  sendPendingSuccess, sendPendingFailure, allSent, thrownUnspecifiedError

  All of the following events are attached to the .events method of a DataObject:[[BR]]
  reloadSuccess, reloadFailure, thrownUnspecifiedError

  '''Views'''

  All of the following events are attached to the .events method of a view (i.e. a Rendering object)[[BR]]
  viewOpenComplete: fires as soon as the view has finished opening and all relevant events have been added. If you need some processing to occur before events are added, create a function currentView._render()[[BR]]
  viewClose

  '''Dialogue events'''

  dialogueOpen (attached to the Dialogue.events, the class prototype): Since it is attached to the prototype and not to a specific dialogue object, it is necessary to verify the dialogue 'id' property at beginning of the callback script.[[BR]]
  dialogueButtonPress (attached to the .events method of a view): Fires when a dialogue button is pressed, within the view. From within the callback function, you can access:
   * currentView.data: the data that will be sent to the server. You can edit it before it is sent. Also, you can prevent it from being sent to the server by adding the line: 
  {{{
  currentView.data['_abort'] = true;
  }}}
   * currentView.ev: the event (click or touch) that triggered the pressing of the button

  '''Drag events'''

  The following are attached to a DragItem, and also to currentView.events:[[BR]]
  dragstart, dragend

  '''Composite events and gestures'''

  tapdown, tapmove, tapend
  doubletap, tripletap, taphold


name: CustomEvents
author: mnehme
ipnr: 178.135.204.20
text:
  = Custom Events Dictionary =

  '''JSON Events'''

  JSONParseError (attached to JSONlogg): triggers when JSONParse() fails. JSONParse() is a more sophisticated version of JSON.parse(). Among other advantages, it tests the string for JSON-ness prior to attempting to parse it, thus avoiding irrelevant syntax errors.

  '''Authentication'''

  All of the following events are attached to the authEvents objects.[[BR]]
  !AuthFail, !AuthSuccess[[BR]]
  !UserUndefined[[BR]]
  !UserNotRegistered[[BR]]
  !RegistrationSuccess[[BR]]
  !NoAuthForThisContent[[BR]]

  '''Server response'''

  All of the following events are attached to the .events method of a DataObject:[[BR]]
  sendPendingSuccess, sendPendingFailure, allSent, thrownUnspecifiedError

  All of the following events are attached to the .events method of a DataObject:[[BR]]
  reloadSuccess, reloadFailure, thrownUnspecifiedError

  '''Views'''

  All of the following events are attached to the .events method of a view (i.e. a Rendering object)[[BR]]
  viewOpenComplete: fires as soon as the view has finished opening and all relevant events have been added. If you need some processing to occur before events are added, create a function currentView._render()[[BR]]
  viewClose

  '''Dialogue events'''

  dialogueOpen (attached to the Dialogue.events, the class prototype): Since it is attached to the prototype and not to a specific dialogue object, it is necessary to verify the dialogue 'id' property at beginning of the callback script.[[BR]]
  dialogueButtonPress (attached to the .events method of a view): Fires when a dialogue button is pressed, within the view. From within the callback function, you can access:
   * currentView.data: the data that will be sent to the server. You can edit it before it is sent. Also, you can prevent it from being sent to the server by adding the line: 
  {{{
  currentView.data['_abort'] = true;
  }}}
   * currentView.ev: the event (click or touch) that triggered the pressing of the button

  '''Drag events'''

  The following are attached to a !DragItem, and also to currentView.events:[[BR]]
  dragstart, dragend

  '''Composite events and gestures'''

  tapdown, tapmove, tapend
  doubletap, tripletap, taphold


name: Advanced
author: mnehme
ipnr: 178.135.204.20
text:
  = Advanced Configuration =
  == Adding CSS ==
  A CSS file can be uploaded from the main panel, in the "Upload Files" section.

  == Modifying Default Dialogue Boxes ==
  You can redesign (overwrite) the default login dialogue box and the registration dialogue box, as follows:
   * Login dialogue box: Create a dialogue box called dialogues/login__.php
   * Registration dialogue box: Create a dialogue box called dialogues/register__.php

  Note: By default, these names are not allowed. You will need to create the dialogue box, open it from the "Design" tab, and rename it (File menu -> Rename)

  == Facebook Integration ==
  To enable Facebook login, simply go to the main panel, and in the "Facebook" section fill in the App ID and App Secret. It's as easy as that!

  To automatically save user data on access to the app, go to “Data” tab, and create a table called fbuserdata and another called fbusergraph with a selection of fields from the below. (These are the supported fields.) '''Make sure your app requests permission from the user to access any needed field, otherwise it will not work.'''

  User data is automatically saved into the two tables as described below.

  '''fbuserdata Table'''[[BR]]
  Fields: fbid, firstname, lastname, email, location, gender, locale, locale, ageMin, ageMax, funnelStage[[BR]]
  All of these are defined exactly as in the Facebook API, with the exception of:[[BR]]
  fbid: the user's facebook id[[BR]]
  funnelStage (beta feature): Keeps track of whether the user is a new user or a returning user[[BR]]

  '''fbusergraph Table''' (beta feature)[[BR]]
  Fields: name, category[[BR]]
  Each row of this table corresponds to a user like/interest/group.[[BR]]
  name: name of the like/interest/group[[BR]]
  category: a broad category of the like/interest/group

  = Advanced Programming =
  == Custom Functions ==
  In order to define your own custom functions and libraries, you can upload a javascript file from the main panel, in the "Upload Files" section. However it is recommended to work via the CDE platform whenever possible: CDE reduces bugs by giving functions local scope instead of global scope.

  This custom file is loaded after all of the default CDE scripts (but before execution begins). As a result, it can be used to over-write/replace CDE functionality.

  == Login and Registration ==
  '''Warning:''' use only if you are an advanced programmer, and at your own risk. The following allows you to overwrite CDE functionality with some of your own. It is very powerful, but it also gives you the power to mess things up. This functionality is unsupported, and may break backward compatibility.

  First, you need to give yourself access to the advanced scripts:
  1. Click on the "Design" tab
  2. Create a new page with a blank name
  3. In the tree, click on views//index.php
  4. Rename it to views/index.php (File menu -> Rename)
  5. Open this view in the "Dynamics" tab

  Once you have done this, you can overwrite any one of the following default CDE functions with one of your own:
   * currentView.setLogin(): Sets up login functionality. The default version (which you may overwrite) does the following:
     * Defines the callback function on authentication failure. This callback launches the login dialogue box (and also enables Facebook login where relevant), and attaches the relevant callbacks to buttons of this dialogue box. If registration by new users is allowed, it also adds a link to a Registration dialogue box, and adds callbacks to the buttons of that box.
     * Adds event listeners to authEvents (for events AuthFail, UserNotRegistered and UserUndefined)
     * Executes auth();
   * currentView.loginHandle(): Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setRegistration(): Called by default when user clicks on the registration link in the Login dialogue box. (Of course, it will not be called if you overwrote currentView.setLogin())
   * currentView.registrationHandle: Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.registrationValidation(): Function for validation of user registration dialogue box fields. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setPeriodic: Function defining any housekeeping tasks that must be executed on a regular schedule. By default, these are:
     * Refreshing the app every 2 hours
     * Sending any pending data to the server every minute

  In order to overwrite, from the Dynamics tab go to the script view by clicking on any element of the page.


name: AddEvent
author: mnehme
ipnr: 178.135.204.20
text:
  = Summary =
  addEvent() registers an event listener on the target. It is recommended in place of javascript's [https://developer.mozilla.org/en-US/docs/DOM/element.addEventListener addEventListener] because:
   * It recognizes [CustomEvents custom events] such as tapend (a combination of mousend and touchend) and !AuthFail (authentication failure)
   * It keeps track of events added and removed. (Helpful for debugging purposes)

  = Syntax =
  {{{
  addEvent(target, type, listener[, useCapture]);
  }}}
  Each parameter is defined exactly as in [https://developer.mozilla.org/en-US/docs/DOM/element.addEventListener addEventListener]

  If ''target'' is a NodeList, then addEvent will be applied to each of its elements.


name: AddEvent
author: mnehme
ipnr: 178.135.204.20
text:
  = Summary =
  addEvent() registers an event listener on the target. It is recommended in place of javascript's [https://developer.mozilla.org/en-US/docs/DOM/element.addEventListener addEventListener] because:
   * It recognizes [CustomEvents custom events] such as tapend (a combination of mousend and touchend) and !AuthFail (authentication failure)
   * It keeps track of events added and removed. (Helpful for debugging purposes)

  = Syntax =
  {{{
  addEvent(target, type, listener[, useCapture]);
  }}}
  Each parameter is defined exactly as in [https://developer.mozilla.org/en-US/docs/DOM/element.addEventListener addEventListener]

  If ''target'' is a !NodeList, then addEvent will be applied to each of its elements.


name: AddEvent
author: mnehme
ipnr: 178.135.204.20
text:
  = Summary =
  addEvent() registers an event listener on the target. It is recommended in place of javascript's [https://developer.mozilla.org/en-US/docs/DOM/element.addEventListener addEventListener] because:
   * It recognizes [CustomEvents custom events] such as tapend (a combination of mousend and touchend) and !AuthFail (authentication failure)
   * It keeps track of events added and removed. (Helpful for debugging purposes)

  = Syntax =
  {{{
  addEvent(target, type, listener[, useCapture]);
  }}}
  Each parameter is defined exactly as in [https://developer.mozilla.org/en-US/docs/DOM/element.addEventListener addEventListener]

  If ''target'' is a !NodeList, then addEvent will be applied to each of its elements.

  = See Also =
  [RemoveEvent removeEvent()]


name: RemoveEvent
author: mnehme
ipnr: 178.135.204.20
text:
  = Summary =
  removeEvent() removes a registered event listener from the target. It is recommended in place of javascript's [https://developer.mozilla.org/en-US/docs/DOM/element.removeEventListener removeEventListener] because:
   * It recognizes [CustomEvents custom events] such as tapend (a combination of mousend and touchend) and !AuthFail (authentication failure)
   * It keeps track of events added and removed. (Helpful for debugging purposes)

  = Syntax =
  {{{
  removeEvent(target, type, listener[, useCapture]);
  }}}
  Each parameter is defined exactly as in [https://developer.mozilla.org/en-US/docs/DOM/element.removeEventListener removeEventListener]

  If ''target'' is a !NodeList, then removeEvent will be applied to each of its elements.

  = See Also =
  [AddEvent addEvent()]


name: WikiStart
author: mnehme
ipnr: 178.135.204.20
text:
  == CDE speeds up App development by 20-30x ==
  Cedrus Development Environment is a platform to allow easy and fast development of mobile and web Apps. "Fast" means 20-30x faster than normal development.
  Though primarily targeting developers, CDE can also be used by product managers to speedily create prototypes with zero-coding. These prototypes can be used as both specs and first release, and speed up the production process by breaking down coding into simple tasks.

  == How it works ==
   * '''Fast design process:''' design is done via simple drag-and-drop.
   * '''Fast coding:''' CDE makes it easy and natural to break down code into small self-contained components, reducing complexity, while making coding very fast. 
   * '''Cloud-enabled:''' Automatically creates server-side databases, automatically creates access functions for you to use which even work offline, and provide intelligent syncing abilities.
   * '''Multi-platform:''' CDE creates apps written in Ajax/HTML5 for their front-ends, and PHP for their back-ends. As a result, it can be used with Phonegap to immediately port the App to all platforms.
   * '''Iterative prototyping:''' Naturally encourages iterative development and MVP, because iterating is blindingly fast.
   * '''Infinitely extensible:''' CDE of course includes the ability to actually code, so anything that can be done by coding can be done with CDE.

  == Limitations ==
  CDE works for most Apps, except for media-rich gaming Apps. For the latter, you might want to consider using a platform such as Unity3D.

  == Starting Points ==
   * [wiki:GettingStarted Getting Started] -- A quick introduction
   * [wiki:FAQ Cedrus Development Environment FAQ] -- Frequently Asked Questions
   * [wiki:NewApp I. Creating and Configuring a New App] -- Creating and Configuring a New App
   * [wiki:Advanced IV. Advanced Features] -- Advanced Features
   * [wiki:ScriptingHacks Hacks] -- Scripting hacks, allowing access to some special features.
   * [wiki:Tips Tips and Best Practices] -- Tips and Best Practices
   * [wiki:Troubleshooting Troubleshooting] -- Troubleshooting
   * [wiki:TracStartingPage Bug-reporting assistance] -- You can report bugs and request features using Trac. This links to the Trac documentation.


name: Tips
author: mnehme
ipnr: 178.135.204.20
text:
  = Javascript Tips for Experienced Programmers =
  1. alert() can subtly modify program flow. So don't use it for debugging, and better yet, don't use it at all. This is due to javascript being an asynchronous language.
  2. [AddEvent addEvent] and [RemoveEvent removeEvent] are the most important specificities of the language. Javascript is event-driven.
  3. There is no block scope. The only scopes are global; and local (current function). Bugs often crop up because any variable defined within a function as in "a = 1;" rather than "var a = 1;" is global.
   * Corollary 1: in for loops use "for(var i=0;" not "for(i=0;". Otherwise you will get the weirdest bugs.
   * Corollary 2: avoid global variables at all costs. The bugs they cause can be remarkably difficult to track.
  4. For-in loops (the equivalent of for-each) don't only loop through properties of the object. They also loop through properties of the prototype (such as length, etc.). In order to avoid this, use hasOwnProperty:

  {{{
  #!java
  for(var i in theObj){
    if(theObj.hasOwnProperty(i)){
      // Do something
    }
  }
  }}}
  4. Be careful when setting visibility of HTML nodes: 
  {{{
  nd.style.visibility = "visible"; // Avoid this. Element will be visible even if parents are hidden
  nd.style.visibility = "inherit"; // Use this instead
  }}}
  5. Debugging tools in javascript are less advanced than in other languages. The two most advanced are Chrome console and Firebug. They allow you to:
   * Inspect an html element, and modify it's css
   * Type code from the console
   * Write to the console with CDE's logg() function or their console.log(), console.warn() and console.error() functions.
   * Add breakpoints, step into/out of, etc.
  6. Understand how keyword "this" behaves. (It is a little unusual)
   * Info can be found [https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/this here]
   * Often, in an object, it is useful to have a line "var that = this;". Such a line allows you to use the variable "that" in order to access the original value of "this", from within methods and callbacks. (This trick is based on closures - see below).
  6. Check up on two important and unusual features of javascript: 
   * Asynchronous javascript and callbacks
   * Anonymous functions
   * Closures
  7. If you have more time, check out javascript objects (really nothing more than an unordered list or hash) and prototypical inheritance.

  = CDE Best Practices =
  The following are best-practices you may want to consider. They are merely suggestions.
   * Take advantage of the folder structure which CDE allows grouping related pages under one folder.
   * Consider creating a seperate page for each device (e.g. one for iPad, one for a regular browser etc.), and grouping all of these into one folder that shares scripts and some graphical elements among them.
   * When a page uses dialogue boxes, you may need a way to automatically turn off any page interactivity every time a dialogue box opens. To do this, instead of creating just one page (for example, views/thePage/index.php), it is better to:
     * Create the page (views/thePage/index.php)
     * Design the page (that way both subpages inherit the design), but without adding the scripts
     * Create two subfolders, one for the page itself (views/thePage/main/index.php) and one for the page with dialogue boxes open (views/thePage/dia/index.php)
     * Add any scripts to (views/thePage/main/index.php). Also, whenever the script calls a dialogue box, go to (views/thePage/dia/index.php), and whenever the dialogue box is closed, return to (views/thePage/main/index.php)
   * Create an easy way to detect errors. For example, adding the following causes any subsequent error to cause the page background color to become light salmon. This probably won't bother a regular user, but it will help you identify issues.

  {{{
  window.onerror=function(){
    document.body.style.backgroundColor='#fff5f5';
  };
  }}}


name: Advanced
author: mnehme
ipnr: 94.187.8.204
text:
  = Advanced Configuration =
  == Adding CSS ==
  A CSS file can be uploaded from the main panel, in the "Upload Files" section.

  == Modifying Default Dialogue Boxes ==
  You can redesign (overwrite) the default login dialogue box and the registration dialogue box, as follows:
   * Login dialogue box: Create a dialogue box called dialogues/login__.php
   * Registration dialogue box: Create a dialogue box called dialogues/register__.php

  Note: By default, these names are not allowed. You will need to create the dialogue box, open it from the "Design" tab, and rename it (File menu -> Rename)

  == Facebook Integration ==
  To enable Facebook login, simply go to the main panel, and in the "Facebook" section fill in the App ID and App Secret. It's as easy as that!

  To automatically save user data on access to the app, go to “Data” tab, and create a table called fbuserdata and another called fbusergraph with a selection of fields from the below. (These are the supported fields.) '''Make sure your app requests permission from the user to access any needed field, otherwise it will not work.'''

  User data is automatically saved into the two tables as described below.

  '''fbuserdata Table'''[[BR]]
  Fields: fbid, firstname, lastname, email, location, gender, locale, locale, ageMin, ageMax, funnelStage[[BR]]
  All of these are defined exactly as in the Facebook API, with the exception of:[[BR]]
  fbid: the user's facebook id[[BR]]
  funnelStage (beta feature): Keeps track of whether the user is a new user or a returning user[[BR]]

  '''fbusergraph Table''' (beta feature)[[BR]]
  Fields: name, category[[BR]]
  Each row of this table corresponds to a user like/interest/group.[[BR]]
  name: name of the like/interest/group[[BR]]
  category: a broad category of the like/interest/group

  = Advanced Programming =
  == Custom Functions ==
  In order to define your own custom functions and libraries, you can upload a javascript file from the main panel, in the "Upload Files" section. However it is recommended to work via the CDE platform whenever possible: CDE reduces bugs by giving functions local scope instead of global scope.

  This custom file is loaded after all of the default CDE scripts (but before execution begins). As a result, it can be used to over-write/replace CDE functionality.

  == Login and Registration ==
  '''Warning:''' use only if you are an advanced programmer, and at your own risk. The following allows you to overwrite CDE functionality with some of your own. It is very powerful, but it also gives you the power to mess things up. This functionality is unsupported, and may break backward compatibility.

  First, you need to give yourself access to the advanced scripts:
  1. Click on the "Design" tab
  2. Create a new page with a blank name
  3. In the tree, click on views//index.php
  4. Rename it to views/index.php (File menu -> Rename)
  5. Open this view in the "Dynamics" tab

  Once you have done this, you can overwrite any one of the following default CDE functions with one of your own:
   * currentView.setLogin(): Sets up login functionality. The default version (which you may overwrite) does the following:
     * Defines the callback function on authentication failure. This callback launches the login dialogue box (and also enables Facebook login where relevant), and attaches the relevant callbacks to buttons of this dialogue box. If registration by new users is allowed, it also adds a link to a Registration dialogue box, and adds callbacks to the buttons of that box.
     * Adds event listeners to authEvents (for events AuthFail, UserNotRegistered and UserUndefined)
     * Executes auth();
   * currentView.loginHandle(): Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setRegistration(): Called by default when user clicks on the registration link in the Login dialogue box. (Of course, it will not be called if you overwrote currentView.setLogin())
   * currentView.registrationHandle: Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.registrationValidation(): Function for validation of user registration dialogue box fields. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setPeriodic: Function defining any housekeeping tasks that must be executed on a regular schedule. By default, these are:
     * Refreshing the app every 2 hours
     * Sending any pending data to the server every minute

  In order to overwrite, from the Dynamics tab go to the script view by clicking on any element of the page.

  == User Access Rights ==
  === Basic ===
  By default, each user has access to his own data, and to no other person's. So generally, there is no need to do anything at all!

  === Advanced ===
  CDE's default user rights management is sufficient for most apps, so you generally need not do anything. However certain apps require advanced user management capabilities. CDE allows for this. Below is some example code showing how this is done:


  {{{
  #!java

  // Following code is done as an admin user such as 'janette' or ‘admin’ (if exists)
  // Add 'admin', 'deptA', two hostesses, and some employees. Be careful not to add a user twice as no warning occurs (and also nothing bad occurs either)
  // Minor bug: Strangely, successFunc executes only for the last user added even though all users are added.
  var cld_permissions = new DataObject('permissions');
  cld_permissions.addAction('newUser');
  var cfg = {};
  cfg.data = {};
  cfg.data['newUser'] = 'admin';
  cfg.data['pass'] = 'passAdmin';
  cfg.successFunc = function(list, allData){
  console.warn('Done adding user '+cfg.data['newUser']);
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'deptA';
  cfg.data['pass'] = 'passDeptA';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'host1';
  cfg.data['pass'] = 'passHost1';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'host2';
  cfg.data['pass'] = 'passHost2';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'jack';
  cfg.data['pass'] = 'passJack';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'jill';
  cfg.data['pass'] = 'passJill';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'john';
  cfg.data['pass'] = 'passJohn';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'jane';
  cfg.data['pass'] = 'passJane';
  cld_permissions.act['newUser'].reload(cfg);

  // Assign them to relevant groups
  cld_permissions.addAction('addRights');
  var cfg = {};
  cfg.data = {};
  cfg.data['addedUser'] = 'jack';
  cfg.data['addedGroup'] = 'admin';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cfg.successFunc = function(list, allData){
  console.warn('Done granting rights to user '+cfg.data['addingUser']);
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['addRights'].reload(cfg);
  cfg.data['addedUser'] = 'jill';
  cfg.data['addedGroup'] = 'admin';
  cld_permissions.act['addRights'].reload(cfg);
  cfg.data['addedUser'] = 'john';
  cfg.data['addedGroup'] = 'deptA';
  cld_permissions.act['addRights'].reload(cfg);
  cfg.data['addedUser'] = 'jane';
  cfg.data['addedGroup'] = 'deptA';
  cld_permissions.act['addRights'].reload(cfg);

  // Change password of jane
  cld_permissions.addAction('changePass');
  var cfg = {};
  cfg.data = {};
  cfg.data['changedUser'] = 'jane';
  cfg.data['pass'] = 'changedpass';
  cfg.successFunc = function(list, allData){
  console.warn('Done changing pass of user '+cfg.data['changedUser']);
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['changePass'].reload(cfg);

  // Get list of users
  cld_permissions.addAction('userList');
  var cfg = {};
  cfg.data = {};
  cfg.successFunc = function(list, allData){
  alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['userList'].reload(cfg);

  // Get all groups to which jane has rw access
  cld_permissions.addAction('checkAllPermissions');
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'jane';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
  alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['checkAllPermissions'].reload(cfg);

  // Check if jane has access to deptA
  cld_permissions.addAction('checkPermissions');
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'jane';
  cfg.data['checkedGroup'] = 'deptA';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
  alert(dump(allData));
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['checkPermissions'].reload(cfg);

  //Create new respondent. Hostess can do this if respondent does not already exist.
  cld_permissions.addAction('newRespondent');
  var cfg = {};
  cfg.data = {};
  cfg.data['dept'] = 'deptA';
  cfg.data['addedRespondent'] = 'respA'; // Will not work for an existing respondent!
  // User permissions. (dept and admin permissions can't be set here. They are resp read and rw.)
  cfg.data['read'] = 1;
  cfg.data['write'] = 1;
  cfg.successFunc = function(list, allData){
  alert('Done.');
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['newRespondent'].reload(cfg);
  }}}


name: Advanced
author: mnehme
ipnr: 94.187.8.204
text:
  = Advanced Configuration =
  == Adding CSS ==
  A CSS file can be uploaded from the main panel, in the "Upload Files" section.

  == Modifying Default Dialogue Boxes ==
  You can redesign (overwrite) the default login dialogue box and the registration dialogue box, as follows:
   * Login dialogue box: Create a dialogue box called dialogues/login__.php
   * Registration dialogue box: Create a dialogue box called dialogues/register__.php

  Note: By default, these names are not allowed. You will need to create the dialogue box, open it from the "Design" tab, and rename it (File menu -> Rename)

  == Facebook Integration ==
  To enable Facebook login, simply go to the main panel, and in the "Facebook" section fill in the App ID and App Secret. It's as easy as that!

  To automatically save user data on access to the app, go to “Data” tab, and create a table called fbuserdata and another called fbusergraph with a selection of fields from the below. (These are the supported fields.) '''Make sure your app requests permission from the user to access any needed field, otherwise it will not work.'''

  User data is automatically saved into the two tables as described below.

  '''fbuserdata Table'''[[BR]]
  Fields: fbid, firstname, lastname, email, location, gender, locale, locale, ageMin, ageMax, funnelStage[[BR]]
  All of these are defined exactly as in the Facebook API, with the exception of:[[BR]]
  fbid: the user's facebook id[[BR]]
  funnelStage (beta feature): Keeps track of whether the user is a new user or a returning user[[BR]]

  '''fbusergraph Table''' (beta feature)[[BR]]
  Fields: name, category[[BR]]
  Each row of this table corresponds to a user like/interest/group.[[BR]]
  name: name of the like/interest/group[[BR]]
  category: a broad category of the like/interest/group

  = Advanced Programming =
  == Custom Functions ==
  In order to define your own custom functions and libraries, you can upload a javascript file from the main panel, in the "Upload Files" section. However it is recommended to work via the CDE platform whenever possible: CDE reduces bugs by giving functions local scope instead of global scope.

  This custom file is loaded after all of the default CDE scripts (but before execution begins). As a result, it can be used to over-write/replace CDE functionality.

  == Login and Registration ==
  '''Warning:''' use only if you are an advanced programmer, and at your own risk. The following allows you to overwrite CDE functionality with some of your own. It is very powerful, but it also gives you the power to mess things up. This functionality is unsupported, and may break backward compatibility.

  First, you need to give yourself access to the advanced scripts:
  1. Click on the "Design" tab
  2. Create a new page with a blank name
  3. In the tree, click on views//index.php
  4. Rename it to views/index.php (File menu -> Rename)
  5. Open this view in the "Dynamics" tab

  Once you have done this, you can overwrite any one of the following default CDE functions with one of your own:
   * currentView.setLogin(): Sets up login functionality. The default version (which you may overwrite) does the following:
     * Defines the callback function on authentication failure. This callback launches the login dialogue box (and also enables Facebook login where relevant), and attaches the relevant callbacks to buttons of this dialogue box. If registration by new users is allowed, it also adds a link to a Registration dialogue box, and adds callbacks to the buttons of that box.
     * Adds event listeners to authEvents (for events AuthFail, UserNotRegistered and UserUndefined)
     * Executes auth();
   * currentView.loginHandle(): Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setRegistration(): Called by default when user clicks on the registration link in the Login dialogue box. (Of course, it will not be called if you overwrote currentView.setLogin())
   * currentView.registrationHandle: Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.registrationValidation(): Function for validation of user registration dialogue box fields. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setPeriodic: Function defining any housekeeping tasks that must be executed on a regular schedule. By default, these are:
     * Refreshing the app every 2 hours
     * Sending any pending data to the server every minute

  In order to overwrite, from the Dynamics tab go to the script view by clicking on any element of the page.

  == User Access Rights ==
  === Basic ===
  By default, each user has access to his own data, and to no other person's. So generally, there is no need to do anything at all!

  === Advanced ===
  CDE's default user rights management is sufficient for most apps, so you generally need not do anything. However certain apps require advanced user management capabilities. CDE allows for this, with some powerful but complex functionality. 

  The fundamental principle is that you can use javascript to grant one account (Account A) access to the contents of another account (Account B). This access can be read, write or read-write

  Once this is done, when logged in as Account A, you will have the ability to read and/or write to account B. Depending on how you configure this access, it can: 
   * Either be restricted to the Account B itself, as though the current user (Account A) were using Account B temporarily (similar to using su or sudo in a linux system)
   * Or, it can give access not only to Account B but also to all accounts that Account B has the same access rights to. So, in this case, if for example Account A has read-write access to Account B, and Account B has read access to Account C, then Account A will be enabled to have read access (but not write access) to Account C. Note that this allows simulation of groups. To do this, simply create an account to act as group, and give it the relevant rights to other accounts. Thus, the group has been created. To add, for instance, Account A to the group as group member, simply give Account A the relevant read-write access to the group account.

  Below is some code illustrating how all this is done.

  In addition, there exist some general-purpose accounts which have pre-defined rights:
   * admin: has read-write access to all accounts
   * _public_r: all accounts can read data written into this account
   * _public_w: all accounts can write (but not read) data into this account
   * _public_rw: all accounts can read and write data into this account

  Below is some example code:

  {{{
  #!java

  /*
  The following code creates some new users, modifies their access rights, and then deletes them.

  For this code to work, you will need to:
  - Contact CDE to request that it be enabled. (It is not enabled by default for security reasons.)
  - Enable authentication for the app (from CDE's main panel)
  - Be logged in as an admin user, such as "admin". (You will receive the password of the account "admin" from us. You can subsequently create additional admin accounts.)
  */
   
  // Add 'admin2', 'deptA' (a department of your company), and some employees. Be careful not to add a user twice as no warning occurs (and also nothing bad occurs either)
  // Minor bug: Strangely, successFunc executes only for the last user added even though all users are added.

  var cld_permissions = new DataObject('permissions');
  cld_permissions.addAction('newUser');
  var cfg = {};
  cfg.data = {};
  cfg.data['newUser'] = 'admin';
  cfg.data['pass'] = 'passAdmin';
  cfg.successFunc = function(list, allData){
  console.warn('Done adding user '+cfg.data['newUser']);
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'deptA';
  cfg.data['pass'] = 'passDeptA';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'host1';
  cfg.data['pass'] = 'passHost1';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'host2';
  cfg.data['pass'] = 'passHost2';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'jack';
  cfg.data['pass'] = 'passJack';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'jill';
  cfg.data['pass'] = 'passJill';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'john';
  cfg.data['pass'] = 'passJohn';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'jane';
  cfg.data['pass'] = 'passJane';
  cld_permissions.act['newUser'].reload(cfg);

  // Assign them to relevant groups
  cld_permissions.addAction('addRights');
  var cfg = {};
  cfg.data = {};
  cfg.data['addedUser'] = 'jack';
  cfg.data['addedGroup'] = 'admin';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cfg.successFunc = function(list, allData){
  console.warn('Done granting rights to user '+cfg.data['addingUser']);
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['addRights'].reload(cfg);
  cfg.data['addedUser'] = 'jill';
  cfg.data['addedGroup'] = 'admin';
  cld_permissions.act['addRights'].reload(cfg);
  cfg.data['addedUser'] = 'john';
  cfg.data['addedGroup'] = 'deptA';
  cld_permissions.act['addRights'].reload(cfg);
  cfg.data['addedUser'] = 'jane';
  cfg.data['addedGroup'] = 'deptA';
  cld_permissions.act['addRights'].reload(cfg);

  // Change password of jane
  cld_permissions.addAction('changePass');
  var cfg = {};
  cfg.data = {};
  cfg.data['changedUser'] = 'jane';
  cfg.data['pass'] = 'changedpass';
  cfg.successFunc = function(list, allData){
  console.warn('Done changing pass of user '+cfg.data['changedUser']);
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['changePass'].reload(cfg);

  // Get list of users
  cld_permissions.addAction('userList');
  var cfg = {};
  cfg.data = {};
  cfg.successFunc = function(list, allData){
  alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['userList'].reload(cfg);

  // Get all groups to which jane has rw access
  cld_permissions.addAction('checkAllPermissions');
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'jane';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
  alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['checkAllPermissions'].reload(cfg);

  // Check if jane has access to deptA
  cld_permissions.addAction('checkPermissions');
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'jane';
  cfg.data['checkedGroup'] = 'deptA';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
  alert(dump(allData));
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['checkPermissions'].reload(cfg);

  }}}


name: Advanced
author: mnehme
ipnr: 94.187.8.204
text:
  = Advanced Configuration =
  == Adding CSS ==
  A CSS file can be uploaded from the main panel, in the "Upload Files" section.

  == Modifying Default Dialogue Boxes ==
  You can redesign (overwrite) the default login dialogue box and the registration dialogue box, as follows:
   * Login dialogue box: Create a dialogue box called dialogues/login__.php
   * Registration dialogue box: Create a dialogue box called dialogues/register__.php

  Note: By default, these names are not allowed. You will need to create the dialogue box, open it from the "Design" tab, and rename it (File menu -> Rename)

  == Facebook Integration ==
  To enable Facebook login, simply go to the main panel, and in the "Facebook" section fill in the App ID and App Secret. It's as easy as that!

  To automatically save user data on access to the app, go to “Data” tab, and create a table called fbuserdata and another called fbusergraph with a selection of fields from the below. (These are the supported fields.) '''Make sure your app requests permission from the user to access any needed field, otherwise it will not work.'''

  User data is automatically saved into the two tables as described below.

  '''fbuserdata Table'''[[BR]]
  Fields: fbid, firstname, lastname, email, location, gender, locale, locale, ageMin, ageMax, funnelStage[[BR]]
  All of these are defined exactly as in the Facebook API, with the exception of:[[BR]]
  fbid: the user's facebook id[[BR]]
  funnelStage (beta feature): Keeps track of whether the user is a new user or a returning user[[BR]]

  '''fbusergraph Table''' (beta feature)[[BR]]
  Fields: name, category[[BR]]
  Each row of this table corresponds to a user like/interest/group.[[BR]]
  name: name of the like/interest/group[[BR]]
  category: a broad category of the like/interest/group

  = Advanced Programming =
  == Custom Functions ==
  In order to define your own custom functions and libraries, you can upload a javascript file from the main panel, in the "Upload Files" section. However it is recommended to work via the CDE platform whenever possible: CDE reduces bugs by giving functions local scope instead of global scope.

  This custom file is loaded after all of the default CDE scripts (but before execution begins). As a result, it can be used to over-write/replace CDE functionality.

  == Login and Registration ==
  '''Warning:''' use only if you are an advanced programmer, and at your own risk. The following allows you to overwrite CDE functionality with some of your own. It is very powerful, but it also gives you the power to mess things up. This functionality is unsupported, and may break backward compatibility.

  First, you need to give yourself access to the advanced scripts:
  1. Click on the "Design" tab
  2. Create a new page with a blank name
  3. In the tree, click on views//index.php
  4. Rename it to views/index.php (File menu -> Rename)
  5. Open this view in the "Dynamics" tab

  Once you have done this, you can overwrite any one of the following default CDE functions with one of your own:
   * currentView.setLogin(): Sets up login functionality. The default version (which you may overwrite) does the following:
     * Defines the callback function on authentication failure. This callback launches the login dialogue box (and also enables Facebook login where relevant), and attaches the relevant callbacks to buttons of this dialogue box. If registration by new users is allowed, it also adds a link to a Registration dialogue box, and adds callbacks to the buttons of that box.
     * Adds event listeners to authEvents (for events AuthFail, UserNotRegistered and UserUndefined)
     * Executes auth();
   * currentView.loginHandle(): Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setRegistration(): Called by default when user clicks on the registration link in the Login dialogue box. (Of course, it will not be called if you overwrote currentView.setLogin())
   * currentView.registrationHandle: Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.registrationValidation(): Function for validation of user registration dialogue box fields. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setPeriodic: Function defining any housekeeping tasks that must be executed on a regular schedule. By default, these are:
     * Refreshing the app every 2 hours
     * Sending any pending data to the server every minute

  In order to overwrite, from the Dynamics tab go to the script view by clicking on any element of the page.

  == User Access Rights ==
  === Basic ===
  By default, each user has access to his own data, and to no other person's. So generally, there is no need to do anything at all!

  === Advanced ===
  CDE's default user rights management is sufficient for most apps, so you generally need not do anything. However certain apps require advanced user management capabilities. CDE allows for this, with some powerful but complex functionality. 

  The fundamental principle is that you can use javascript to grant one account (Account A) access to the contents of another account (Account B). This access can be read, write or read-write

  Once this is done, when logged in as Account A, you will have the ability to read and/or write to account B. Depending on how you configure this access, it can: 
   * Either be restricted to the Account B itself, as though the current user (Account A) were using Account B temporarily (similar to using su or sudo in a linux system)
   * Or, it can give access not only to Account B but also to all accounts that Account B has the same access rights to. So, in this case, if for example Account A has read-write access to Account B, and Account B has read access to Account C, then Account A will be enabled to have read access (but not write access) to Account C. Note that this allows simulation of groups. To do this, simply create an account to act as group, and give it the relevant rights to other accounts. Thus, the group has been created. To add, for instance, Account A to the group as group member, simply give Account A the relevant read-write access to the group account.

  Below is some code illustrating how all this is done.

  In addition, there exist some general-purpose accounts which have pre-defined rights:
   * admin: has read-write access to all accounts
   * _public_r: all accounts can read data written into this account
   * _public_w: all accounts can write (but not read) data into this account
   * _public_rw: all accounts can read and write data into this account

  Below is some example code:

  {{{
  #!java

  /*
  The following code creates some new users, modifies their access rights, and then deletes them.

  For this code to work, you will need to:
  - Contact CDE to request that it be enabled. (It is not enabled by default for security reasons.)
  - Enable authentication for the app (from CDE's main panel)
  - Be logged in as an admin user, such as "admin". (You will receive the password of the account "admin" from us. You 
  can subsequently create additional admin accounts.)
  */
   
  // Add 'admin2', 'deptA' (a department of your company), and some employees. Be careful not to add a user 
  // twice as no warning occurs (and also nothing bad occurs either)

  var cld_permissions = new DataObject('permissions');
  cld_permissions.addAction('newUser');
  var cfg = {};
  cfg.data = {};
  cfg.data['newUser'] = 'admin';
  cfg.data['pass'] = 'passAdmin';
  cfg.successFunc = function(list, allData){
  console.warn('Done adding user '+cfg.data['newUser']);
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'deptA';
  cfg.data['pass'] = 'passDeptA';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'host1';
  cfg.data['pass'] = 'passHost1';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'host2';
  cfg.data['pass'] = 'passHost2';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'jack';
  cfg.data['pass'] = 'passJack';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'jill';
  cfg.data['pass'] = 'passJill';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'john';
  cfg.data['pass'] = 'passJohn';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'jane';
  cfg.data['pass'] = 'passJane';
  cld_permissions.act['newUser'].reload(cfg);

  // Assign them to relevant groups
  cld_permissions.addAction('addRights');
  var cfg = {};
  cfg.data = {};
  cfg.data['addedUser'] = 'jack';
  cfg.data['addedGroup'] = 'admin';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cfg.successFunc = function(list, allData){
  console.warn('Done granting rights to user '+cfg.data['addingUser']);
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['addRights'].reload(cfg);
  cfg.data['addedUser'] = 'jill';
  cfg.data['addedGroup'] = 'admin';
  cld_permissions.act['addRights'].reload(cfg);
  cfg.data['addedUser'] = 'john';
  cfg.data['addedGroup'] = 'deptA';
  cld_permissions.act['addRights'].reload(cfg);
  cfg.data['addedUser'] = 'jane';
  cfg.data['addedGroup'] = 'deptA';
  cld_permissions.act['addRights'].reload(cfg);

  // Change password of jane
  cld_permissions.addAction('changePass');
  var cfg = {};
  cfg.data = {};
  cfg.data['changedUser'] = 'jane';
  cfg.data['pass'] = 'changedpass';
  cfg.successFunc = function(list, allData){
  console.warn('Done changing pass of user '+cfg.data['changedUser']);
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['changePass'].reload(cfg);

  // Get list of users
  cld_permissions.addAction('userList');
  var cfg = {};
  cfg.data = {};
  cfg.successFunc = function(list, allData){
  alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['userList'].reload(cfg);

  // Get all groups to which jane has rw access
  cld_permissions.addAction('checkAllPermissions');
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'jane';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
  alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['checkAllPermissions'].reload(cfg);

  // Check if jane has access to deptA
  cld_permissions.addAction('checkPermissions');
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'jane';
  cfg.data['checkedGroup'] = 'deptA';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
  alert(dump(allData));
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['checkPermissions'].reload(cfg);

  // Get all groups to which user has read access INCLUDING secondary access
  if(typeof cld_permissions === 'undefined') var cld_permissions = new DataObject('permissions');
  cld_permissions.addAction('checkSecondaryPermissions');
  var cfg = {};
  cfg.params = {};
  cfg.params['checkedUser'] = 'john';
  cfg.params['checkedGroup'] = 'Sales';
  cfg.params['type'] = 'read';
  cfg.successFunc = function(list, allData){
  alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['checkSecondaryPermissions'].reload(cfg);

  }}}


name: Advanced
author: mnehme
ipnr: 94.187.8.204
text:
  = Advanced Configuration =
  == Adding CSS ==
  A CSS file can be uploaded from the main panel, in the "Upload Files" section.

  == Modifying Default Dialogue Boxes ==
  You can redesign (overwrite) the default login dialogue box and the registration dialogue box, as follows:
   * Login dialogue box: Create a dialogue box called dialogues/login__.php
   * Registration dialogue box: Create a dialogue box called dialogues/register__.php

  Note: By default, these names are not allowed. You will need to create the dialogue box, open it from the "Design" tab, and rename it (File menu -> Rename)

  == Facebook Integration ==
  To enable Facebook login, simply go to the main panel, and in the "Facebook" section fill in the App ID and App Secret. It's as easy as that!

  To automatically save user data on access to the app, go to “Data” tab, and create a table called fbuserdata and another called fbusergraph with a selection of fields from the below. (These are the supported fields.) '''Make sure your app requests permission from the user to access any needed field, otherwise it will not work.'''

  User data is automatically saved into the two tables as described below.

  '''fbuserdata Table'''[[BR]]
  Fields: fbid, firstname, lastname, email, location, gender, locale, locale, ageMin, ageMax, funnelStage[[BR]]
  All of these are defined exactly as in the Facebook API, with the exception of:[[BR]]
  fbid: the user's facebook id[[BR]]
  funnelStage (beta feature): Keeps track of whether the user is a new user or a returning user[[BR]]

  '''fbusergraph Table''' (beta feature)[[BR]]
  Fields: name, category[[BR]]
  Each row of this table corresponds to a user like/interest/group.[[BR]]
  name: name of the like/interest/group[[BR]]
  category: a broad category of the like/interest/group

  = Advanced Programming =
  == Custom Functions ==
  In order to define your own custom functions and libraries, you can upload a javascript file from the main panel, in the "Upload Files" section. However it is recommended to work via the CDE platform whenever possible: CDE reduces bugs by giving functions local scope instead of global scope.

  This custom file is loaded after all of the default CDE scripts (but before execution begins). As a result, it can be used to over-write/replace CDE functionality.

  == Login and Registration ==
  '''Warning:''' use only if you are an advanced programmer, and at your own risk. The following allows you to overwrite CDE functionality with some of your own. It is very powerful, but it also gives you the power to mess things up. This functionality is unsupported, and may break backward compatibility.

  First, you need to give yourself access to the advanced scripts:
  1. Click on the "Design" tab
  2. Create a new page with a blank name
  3. In the tree, click on views//index.php
  4. Rename it to views/index.php (File menu -> Rename)
  5. Open this view in the "Dynamics" tab

  Once you have done this, you can overwrite any one of the following default CDE functions with one of your own:
   * currentView.setLogin(): Sets up login functionality. The default version (which you may overwrite) does the following:
     * Defines the callback function on authentication failure. This callback launches the login dialogue box (and also enables Facebook login where relevant), and attaches the relevant callbacks to buttons of this dialogue box. If registration by new users is allowed, it also adds a link to a Registration dialogue box, and adds callbacks to the buttons of that box.
     * Adds event listeners to authEvents (for events AuthFail, UserNotRegistered and UserUndefined)
     * Executes auth();
   * currentView.loginHandle(): Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setRegistration(): Called by default when user clicks on the registration link in the Login dialogue box. (Of course, it will not be called if you overwrote currentView.setLogin())
   * currentView.registrationHandle: Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.registrationValidation(): Function for validation of user registration dialogue box fields. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setPeriodic: Function defining any housekeeping tasks that must be executed on a regular schedule. By default, these are:
     * Refreshing the app every 2 hours
     * Sending any pending data to the server every minute

  In order to overwrite, from the Dynamics tab go to the script view by clicking on any element of the page.

  == User Access Rights ==
  === Basic ===
  By default, each user has access to his own data, and to no other person's. So generally, there is no need to do anything at all!

  === Advanced ===
  CDE's default user rights management is sufficient for most apps, so you generally need not do anything. However certain apps require advanced user management capabilities. CDE allows for this, with some powerful but complex functionality. 

  The fundamental principle is that you can use javascript to grant one account (Account A) access to the contents of another account (Account B). This access can be read, write or read-write

  Once this is done, when logged in as Account A, you will have the ability to read and/or write to account B. Depending on how you configure this access, it can: 
   * Either be restricted to the Account B itself, as though the current user (Account A) were using Account B temporarily (similar to using su or sudo in a linux system)
   * Or, it can give access not only to Account B but also to all accounts that Account B has the same access rights to. So, in this case, if for example Account A has read-write access to Account B, and Account B has read access to Account C, then Account A will be enabled to have read access (but not write access) to Account C. Note that this allows simulation of groups. To do this, simply create an account to act as group, and give it the relevant rights to other accounts. Thus, the group has been created. To add, for instance, Account A to the group as group member, simply give Account A the relevant read-write access to the group account.

  Below is some code illustrating how all this is done.

  In addition, there exist some general-purpose accounts which have pre-defined rights:
   * admin: has read-write access to all accounts
   * _public_r: all accounts can read data written into this account
   * _public_w: all accounts can write (but not read) data into this account
   * _public_rw: all accounts can read and write data into this account

  Below is some example code:

  {{{
  #!java

  /*
  The following code creates some new users, modifies their access rights, and then deletes them.

  For this code to work, you will need to:
  - Contact CDE to request that it be enabled. (It is not enabled by default for security reasons.)
  - Enable authentication for the app (from CDE's main panel)
  - Be logged in as an admin user, such as "admin". (You will receive the password of the account "admin" from us. You 
  can subsequently create additional admin accounts.)
  */

  // Add 'admin2', 'deptA' (a department of your company), and some employees. Be careful not to add a user 
  // twice as no warning occurs (and also nothing bad occurs either)
   
  // Setup
  var cld_permissions = new DataObject('permissions'); // Set up the permissions object

  // Add a user called admin2.(Later, this user will be granted admin rights)
  cld_permissions.addAction('newUser');
  var cfg = {};
  cfg.data = {};
  cfg.data['newUser'] = 'admin2';
  cfg.data['pass'] = 'passAdmin2';
  cfg.successFunc = function(list, allData){
      logg('Done adding user '+cfg.data['newUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['newUser'].reload(cfg);

  // Add some more users, using the same configuration variable cfg as for admin2
  cfg.data['newUser'] = 'deptA';
  cfg.data['pass'] = 'passDeptA';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'JohnSmith';
  cfg.data['pass'] = 'passJohnSmith';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'JaneClark';
  cfg.data['pass'] = 'passJaneClark';
  cld_permissions.act['newUser'].reload(cfg);

  // Give admin2 read-write access to the admin group, thus giving it read-write access to all acounts
  cld_permissions.addAction('addRights');
  var cfg = {};
  cfg.data = {};
  cfg.data['addedUser'] = 'admin2';
  cfg.data['addedGroup'] = 'admin';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cfg.successFunc = function(list, allData){
      logg('Done granting rights to user '+cfg.data['addingUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['addRights'].reload(cfg);

  // Assign JohnSmith and JaneClark to their department (deptA)
  // JohnSmith is department head, so he has read-write access. JaneClark only has read access.
  cfg.data['addedUser'] = 'JohnSmith';
  cfg.data['addedGroup'] = 'deptA';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cld_permissions.act['addRights'].reload(cfg);

  cfg.data['addedUser'] = 'JaneClark';
  cfg.data['addedGroup'] = 'deptA';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cld_permissions.act['addRights'].reload(cfg);

  // Change password of jane
  cld_permissions.addAction('changePass');
  var cfg = {};
  cfg.data = {};
  cfg.data['changedUser'] = 'jane';
  cfg.data['pass'] = 'changedpass';
  cfg.successFunc = function(list, allData){
  console.warn('Done changing pass of user '+cfg.data['changedUser']);
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['changePass'].reload(cfg);

  // Get list of users
  cld_permissions.addAction('userList');
  var cfg = {};
  cfg.data = {};
  cfg.successFunc = function(list, allData){
  alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['userList'].reload(cfg);

  // Get all groups to which jane has rw access
  cld_permissions.addAction('checkAllPermissions');
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'jane';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
  alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['checkAllPermissions'].reload(cfg);

  // Check if jane has access to deptA
  cld_permissions.addAction('checkPermissions');
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'jane';
  cfg.data['checkedGroup'] = 'deptA';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
  alert(dump(allData));
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['checkPermissions'].reload(cfg);

  // Get all groups to which user has read access INCLUDING secondary access
  if(typeof cld_permissions === 'undefined') var cld_permissions = new DataObject('permissions');
  cld_permissions.addAction('checkSecondaryPermissions');
  var cfg = {};
  cfg.params = {};
  cfg.params['checkedUser'] = 'john';
  cfg.params['checkedGroup'] = 'Sales';
  cfg.params['type'] = 'read';
  cfg.successFunc = function(list, allData){
  alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['checkSecondaryPermissions'].reload(cfg);

  }}}


name: Advanced
author: mnehme
ipnr: 94.187.8.204
text:
  = Advanced Configuration =
  == Adding CSS ==
  A CSS file can be uploaded from the main panel, in the "Upload Files" section.

  == Modifying Default Dialogue Boxes ==
  You can redesign (overwrite) the default login dialogue box and the registration dialogue box, as follows:
   * Login dialogue box: Create a dialogue box called dialogues/login__.php
   * Registration dialogue box: Create a dialogue box called dialogues/register__.php

  Note: By default, these names are not allowed. You will need to create the dialogue box, open it from the "Design" tab, and rename it (File menu -> Rename)

  == Facebook Integration ==
  To enable Facebook login, simply go to the main panel, and in the "Facebook" section fill in the App ID and App Secret. It's as easy as that!

  To automatically save user data on access to the app, go to “Data” tab, and create a table called fbuserdata and another called fbusergraph with a selection of fields from the below. (These are the supported fields.) '''Make sure your app requests permission from the user to access any needed field, otherwise it will not work.'''

  User data is automatically saved into the two tables as described below.

  '''fbuserdata Table'''[[BR]]
  Fields: fbid, firstname, lastname, email, location, gender, locale, locale, ageMin, ageMax, funnelStage[[BR]]
  All of these are defined exactly as in the Facebook API, with the exception of:[[BR]]
  fbid: the user's facebook id[[BR]]
  funnelStage (beta feature): Keeps track of whether the user is a new user or a returning user[[BR]]

  '''fbusergraph Table''' (beta feature)[[BR]]
  Fields: name, category[[BR]]
  Each row of this table corresponds to a user like/interest/group.[[BR]]
  name: name of the like/interest/group[[BR]]
  category: a broad category of the like/interest/group

  = Advanced Programming =
  == Custom Functions ==
  In order to define your own custom functions and libraries, you can upload a javascript file from the main panel, in the "Upload Files" section. However it is recommended to work via the CDE platform whenever possible: CDE reduces bugs by giving functions local scope instead of global scope.

  This custom file is loaded after all of the default CDE scripts (but before execution begins). As a result, it can be used to over-write/replace CDE functionality.

  == Login and Registration ==
  '''Warning:''' use only if you are an advanced programmer, and at your own risk. The following allows you to overwrite CDE functionality with some of your own. It is very powerful, but it also gives you the power to mess things up. This functionality is unsupported, and may break backward compatibility.

  First, you need to give yourself access to the advanced scripts:
  1. Click on the "Design" tab
  2. Create a new page with a blank name
  3. In the tree, click on views//index.php
  4. Rename it to views/index.php (File menu -> Rename)
  5. Open this view in the "Dynamics" tab

  Once you have done this, you can overwrite any one of the following default CDE functions with one of your own:
   * currentView.setLogin(): Sets up login functionality. The default version (which you may overwrite) does the following:
     * Defines the callback function on authentication failure. This callback launches the login dialogue box (and also enables Facebook login where relevant), and attaches the relevant callbacks to buttons of this dialogue box. If registration by new users is allowed, it also adds a link to a Registration dialogue box, and adds callbacks to the buttons of that box.
     * Adds event listeners to authEvents (for events AuthFail, UserNotRegistered and UserUndefined)
     * Executes auth();
   * currentView.loginHandle(): Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setRegistration(): Called by default when user clicks on the registration link in the Login dialogue box. (Of course, it will not be called if you overwrote currentView.setLogin())
   * currentView.registrationHandle: Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.registrationValidation(): Function for validation of user registration dialogue box fields. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setPeriodic: Function defining any housekeeping tasks that must be executed on a regular schedule. By default, these are:
     * Refreshing the app every 2 hours
     * Sending any pending data to the server every minute

  In order to overwrite, from the Dynamics tab go to the script view by clicking on any element of the page.

  == User Access Rights ==
  === Basic ===
  By default, each user has access to his own data, and to no other person's. So generally, there is no need to do anything at all!

  === Advanced ===
  CDE's default user rights management is sufficient for most apps, so you generally need not do anything. However certain apps require advanced user management capabilities. CDE allows for this, with some powerful but complex functionality. 

  The fundamental principle is that you can use javascript to grant one account (Account A) access to the contents of another account (Account B). This access can be read, write or read-write

  Once this is done, when logged in as Account A, you will have the ability to read and/or write to account B. Depending on how you configure this access, it can: 
   * Either be restricted to the Account B itself, as though the current user (Account A) were using Account B temporarily (similar to using su or sudo in a linux system)
   * Or, it can give access not only to Account B but also to all accounts that Account B has the same access rights to. So, in this case, if for example Account A has read-write access to Account B, and Account B has read access to Account C, then Account A will be enabled to have read access (but not write access) to Account C. Note that this allows simulation of groups. To do this, simply create an account to act as group, and give it the relevant rights to other accounts. Thus, the group has been created. To add, for instance, Account A to the group as group member, simply give Account A the relevant read-write access to the group account.

  Below is some code illustrating how all this is done.

  In addition, there exist some general-purpose accounts which have pre-defined rights:
   * admin: has read-write access to all accounts
   * _public_r: all accounts can read data written into this account
   * _public_w: all accounts can write (but not read) data into this account
   * _public_rw: all accounts can read and write data into this account

  Below is some example code:

  {{{
  #!java

  /*
  The following code creates some new users, modifies their access rights, and then deletes them.

  For this code to work, you will need to:
  - Contact CDE to request that it be enabled. (It is not enabled by default for security reasons.)
  - Enable authentication for the app (from CDE's main panel)
  - Be logged in as an admin user, such as "admin". (You will receive the password of the account "admin" from us. You 
  can subsequently create additional admin accounts.)

  You may want to play around with this code in the chrome console, pasting it in paragraph by paragraph.
  */

  // Setup
  var cld_permissions = new DataObject('permissions'); // Set up the permissions object
  cld_permissions.addAction('newUser'); // Add the ability to add a new user
  cld_permissions.addAction('addRights'); // Add the ability to grant new rights to an existing user
  cld_permissions.addAction('changePass'); // Add the ability to set a user's password from the admin account
  cld_permissions.addAction('userList'); // Add the ability to list all users
  cld_permissions.addAction('checkAllPermissions'); // Add the ability to check all direct (_altUser) permissions a given account has
  cld_permissions.addAction('checkPermissions'); // Add the ability to check whether a given account has direct (_altUser) access to another
  cld_permissions.addAction('checkSecondaryPermissions'); // Add the ability to check whether a given account has secondary (_group) access to another

  // Add a user called admin2.(Later, this user will be granted admin rights)
  var cfg = {};
  cfg.data = {};
  cfg.data['newUser'] = 'admin2';
  cfg.data['pass'] = 'passAdmin2';
  cfg.successFunc = function(list, allData){
      logg('Done adding user '+cfg.data['newUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['newUser'].reload(cfg);

  // Add some more users, using the same configuration variable cfg as for admin2
  cfg.data['newUser'] = 'deptA';
  cfg.data['pass'] = 'passDeptA';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'JohnSmith';
  cfg.data['pass'] = 'passJohnSmith';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'JaneClark';
  cfg.data['pass'] = 'passJaneClark';
  cld_permissions.act['newUser'].reload(cfg);

  // Give admin2 read-write access to the admin group, thus giving it read-write access to all acounts
  var cfg = {};
  cfg.data = {};
  cfg.data['addedUser'] = 'admin2';
  cfg.data['addedGroup'] = 'admin';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cfg.successFunc = function(list, allData){
      logg('Done granting rights to user '+cfg.data['addingUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['addRights'].reload(cfg);

  // Assign JohnSmith and JaneClark to their department (deptA)
  // JohnSmith is department head, so he has read-write access. JaneClark only has read access.
  cfg.data['addedUser'] = 'JohnSmith';
  cfg.data['addedGroup'] = 'deptA';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cld_permissions.act['addRights'].reload(cfg);

  cfg.data['addedUser'] = 'JaneClark';
  cfg.data['addedGroup'] = 'deptA';
  cfg.data['read'] = '0';
  cfg.data['write'] = '1';
  cld_permissions.act['addRights'].reload(cfg);

  // Set a new password for JaneClark
  var cfg = {};
  cfg.data = {};
  cfg.data['changedUser'] = 'JaneClark';
  cfg.data['pass'] = 'changedpass';
  cfg.successFunc = function(list, allData){
  console.warn('Done changing pass of user '+cfg.data['changedUser']);
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['changePass'].reload(cfg);

  // Get list of users
  var cfg = {};
  cfg.data = {};
  cfg.successFunc = function(list, allData){
  alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['userList'].reload(cfg);

  // Get all groups to which jane has rw access
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'jane';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
  alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['checkAllPermissions'].reload(cfg);

  // Check if jane has access to deptA
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'jane';
  cfg.data['checkedGroup'] = 'deptA';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
  alert(dump(allData));
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['checkPermissions'].reload(cfg);

  // Get all groups to which user has read access INCLUDING secondary access
  if(typeof cld_permissions === 'undefined') var cld_permissions = new DataObject('permissions');
  var cfg = {};
  cfg.params = {};
  cfg.params['checkedUser'] = 'john';
  cfg.params['checkedGroup'] = 'Sales';
  cfg.params['type'] = 'read';
  cfg.successFunc = function(list, allData){
  alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
  // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
  var msg = '';
  if(out) msg = out['replyCode'] + ': ' + out['replyText'];
  alert('Failed: '+msg);
  };
  cld_permissions.act['checkSecondaryPermissions'].reload(cfg);

  }}}


name: Advanced
author: mnehme
ipnr: 94.187.8.204
text:
  = Advanced Configuration =
  == Adding CSS ==
  A CSS file can be uploaded from the main panel, in the "Upload Files" section.

  == Modifying Default Dialogue Boxes ==
  You can redesign (overwrite) the default login dialogue box and the registration dialogue box, as follows:
   * Login dialogue box: Create a dialogue box called dialogues/login__.php
   * Registration dialogue box: Create a dialogue box called dialogues/register__.php

  Note: By default, these names are not allowed. You will need to create the dialogue box, open it from the "Design" tab, and rename it (File menu -> Rename)

  == Facebook Integration ==
  To enable Facebook login, simply go to the main panel, and in the "Facebook" section fill in the App ID and App Secret. It's as easy as that!

  To automatically save user data on access to the app, go to “Data” tab, and create a table called fbuserdata and another called fbusergraph with a selection of fields from the below. (These are the supported fields.) '''Make sure your app requests permission from the user to access any needed field, otherwise it will not work.'''

  User data is automatically saved into the two tables as described below.

  '''fbuserdata Table'''[[BR]]
  Fields: fbid, firstname, lastname, email, location, gender, locale, locale, ageMin, ageMax, funnelStage[[BR]]
  All of these are defined exactly as in the Facebook API, with the exception of:[[BR]]
  fbid: the user's facebook id[[BR]]
  funnelStage (beta feature): Keeps track of whether the user is a new user or a returning user[[BR]]

  '''fbusergraph Table''' (beta feature)[[BR]]
  Fields: name, category[[BR]]
  Each row of this table corresponds to a user like/interest/group.[[BR]]
  name: name of the like/interest/group[[BR]]
  category: a broad category of the like/interest/group

  = Advanced Programming =
  == Custom Functions ==
  In order to define your own custom functions and libraries, you can upload a javascript file from the main panel, in the "Upload Files" section. However it is recommended to work via the CDE platform whenever possible: CDE reduces bugs by giving functions local scope instead of global scope.

  This custom file is loaded after all of the default CDE scripts (but before execution begins). As a result, it can be used to over-write/replace CDE functionality.

  == Login and Registration ==
  '''Warning:''' use only if you are an advanced programmer, and at your own risk. The following allows you to overwrite CDE functionality with some of your own. It is very powerful, but it also gives you the power to mess things up. This functionality is unsupported, and may break backward compatibility.

  First, you need to give yourself access to the advanced scripts:
  1. Click on the "Design" tab
  2. Create a new page with a blank name
  3. In the tree, click on views//index.php
  4. Rename it to views/index.php (File menu -> Rename)
  5. Open this view in the "Dynamics" tab

  Once you have done this, you can overwrite any one of the following default CDE functions with one of your own:
   * currentView.setLogin(): Sets up login functionality. The default version (which you may overwrite) does the following:
     * Defines the callback function on authentication failure. This callback launches the login dialogue box (and also enables Facebook login where relevant), and attaches the relevant callbacks to buttons of this dialogue box. If registration by new users is allowed, it also adds a link to a Registration dialogue box, and adds callbacks to the buttons of that box.
     * Adds event listeners to authEvents (for events AuthFail, UserNotRegistered and UserUndefined)
     * Executes auth();
   * currentView.loginHandle(): Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setRegistration(): Called by default when user clicks on the registration link in the Login dialogue box. (Of course, it will not be called if you overwrote currentView.setLogin())
   * currentView.registrationHandle: Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.registrationValidation(): Function for validation of user registration dialogue box fields. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setPeriodic: Function defining any housekeeping tasks that must be executed on a regular schedule. By default, these are:
     * Refreshing the app every 2 hours
     * Sending any pending data to the server every minute

  In order to overwrite, from the Dynamics tab go to the script view by clicking on any element of the page.

  == User Access Rights ==
  === Basic ===
  By default, each user has access to his own data, and to no other person's. So generally, there is no need to do anything at all!

  === Advanced ===
  CDE's default user rights management is sufficient for most apps, so you generally need not do anything. However certain apps require advanced user management capabilities. CDE allows for this, with some powerful but complex functionality. 

  The fundamental principle is that you can use javascript to grant one account (Account A) access to the contents of another account (Account B). This access can be read, write or read-write

  Once this is done, when logged in as Account A, you will have the ability to read and/or write to account B. Depending on how you configure this access, it can: 
   * Either be restricted to the Account B itself, as though the current user (Account A) were using Account B temporarily (similar to using su or sudo in a linux system)
   * Or, it can give access not only to Account B but also to all accounts that Account B has the same access rights to. So, in this case, if for example Account A has read-write access to Account B, and Account B has read access to Account C, then Account A will be enabled to have read access (but not write access) to Account C. Note that this allows simulation of groups. To do this, simply create an account to act as group, and give it the relevant rights to other accounts. Thus, the group has been created. To add, for instance, Account A to the group as group member, simply give Account A the relevant read-write access to the group account.

  Below is some code illustrating how all this is done.

  In addition, there exist some general-purpose accounts which have pre-defined rights:
   * admin: has read-write access to all accounts
   * _public_r: all accounts can read data written into this account
   * _public_w: all accounts can write (but not read) data into this account
   * _public_rw: all accounts can read and write data into this account

  Below is some example code:

  {{{
  #!java

  /*
  The following code creates some new users, modifies their access rights, and then deletes them.

  For this code to work, you will need to:
  - Contact CDE to request that it be enabled. (It is not enabled by default for security reasons.)
  - Enable authentication for the app (from CDE's main panel)
  - Be logged in as an admin user, such as "admin". (You will receive the password of the account "admin" from us. You 
  can subsequently create additional admin accounts.)

  You may want to play around with this code in the chrome console, pasting it in paragraph by paragraph.
  */

  // Setup
  var cld_permissions = new DataObject('permissions'); // Set up the permissions object
  cld_permissions.addAction('newUser'); // Add the ability to add a new user
  cld_permissions.addAction('addRights'); // Add the ability to grant new rights to an existing user
  cld_permissions.addAction('changePass'); // Add the ability to set a user's password from the admin account
  cld_permissions.addAction('userList'); // Add the ability to list all users
  cld_permissions.addAction('checkAllPermissions'); // Add the ability to check all direct (_altUser) permissions a given account has
  cld_permissions.addAction('checkPermissions'); // Add the ability to check whether a given account has direct (_altUser) access to another
  cld_permissions.addAction('checkSecondaryPermissions'); // Add the ability to check whether a given account has secondary (_group) access to another

  // Add a user called admin2.(Later, this user will be granted admin rights)
  var cfg = {};
  cfg.data = {};
  cfg.data['newUser'] = 'admin2';
  cfg.data['pass'] = 'passAdmin2';
  cfg.successFunc = function(list, allData){
      logg('Done adding user '+cfg.data['newUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['newUser'].reload(cfg);

  // Add some more users, using the same configuration variable cfg as for admin2
  cfg.data['newUser'] = 'deptA';
  cfg.data['pass'] = 'passDeptA';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'JohnSmith';
  cfg.data['pass'] = 'passJohnSmith';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'JaneClark';
  cfg.data['pass'] = 'passJaneClark';
  cld_permissions.act['newUser'].reload(cfg);

  // Give admin2 read-write access to the admin group, thus giving it read-write access to all acounts
  var cfg = {};
  cfg.data = {};
  cfg.data['addedUser'] = 'admin2';
  cfg.data['addedGroup'] = 'admin';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cfg.successFunc = function(list, allData){
      logg('Done granting rights to user '+cfg.data['addingUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['addRights'].reload(cfg);

  // Assign JohnSmith and JaneClark to their department (deptA)
  // JohnSmith is department head, so he has read-write access. JaneClark only has read access.
  cfg.data['addedUser'] = 'JohnSmith';
  cfg.data['addedGroup'] = 'deptA';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cld_permissions.act['addRights'].reload(cfg);

  cfg.data['addedUser'] = 'JaneClark';
  cfg.data['addedGroup'] = 'deptA';
  cfg.data['read'] = '1';
  cfg.data['write'] = '0';
  cld_permissions.act['addRights'].reload(cfg);

  // Set a new password for JaneClark, to allow her to login
  var cfg = {};
  cfg.data = {};
  cfg.data['changedUser'] = 'JaneClark';
  cfg.data['pass'] = 'changedpass';
  cfg.successFunc = function(list, allData){
      logg('Done changing pass of user '+cfg.data['changedUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['changePass'].reload(cfg);

  // Get list of users
  var cfg = {};
  cfg.data = {};
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['userList'].reload(cfg);

  // Get all groups to which JaneClark has rw access
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'JaneClark';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkAllPermissions'].reload(cfg);

  // Check if JaneClark has read-write access to deptA. (She does not.)
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'jane';
  cfg.data['checkedGroup'] = 'deptA';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
      alert(dump(allData));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkPermissions'].reload(cfg);

  // Check if JohnSmith has read access to JaneClark INCLUDING secondary access. (He does not.)
  var cfg = {};
  cfg.params = {};
  cfg.params['checkedUser'] = 'JohnSmith';
  cfg.params['checkedGroup'] = 'JaneClark';
  cfg.params['type'] = 'read';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkSecondaryPermissions'].reload(cfg);

  }}}


name: Advanced
author: mnehme
ipnr: 94.187.8.204
text:
  = Advanced Configuration =
  == Adding CSS ==
  A CSS file can be uploaded from the main panel, in the "Upload Files" section.

  == Modifying Default Dialogue Boxes ==
  You can redesign (overwrite) the default login dialogue box and the registration dialogue box, as follows:
   * Login dialogue box: Create a dialogue box called dialogues/login__.php
   * Registration dialogue box: Create a dialogue box called dialogues/register__.php

  Note: By default, these names are not allowed. You will need to create the dialogue box, open it from the "Design" tab, and rename it (File menu -> Rename)

  == Facebook Integration ==
  To enable Facebook login, simply go to the main panel, and in the "Facebook" section fill in the App ID and App Secret. It's as easy as that!

  To automatically save user data on access to the app, go to “Data” tab, and create a table called fbuserdata and another called fbusergraph with a selection of fields from the below. (These are the supported fields.) '''Make sure your app requests permission from the user to access any needed field, otherwise it will not work.'''

  User data is automatically saved into the two tables as described below.

  '''fbuserdata Table'''[[BR]]
  Fields: fbid, firstname, lastname, email, location, gender, locale, locale, ageMin, ageMax, funnelStage[[BR]]
  All of these are defined exactly as in the Facebook API, with the exception of:[[BR]]
  fbid: the user's facebook id[[BR]]
  funnelStage (beta feature): Keeps track of whether the user is a new user or a returning user[[BR]]

  '''fbusergraph Table''' (beta feature)[[BR]]
  Fields: name, category[[BR]]
  Each row of this table corresponds to a user like/interest/group.[[BR]]
  name: name of the like/interest/group[[BR]]
  category: a broad category of the like/interest/group

  = Advanced Programming =
  == Custom Functions ==
  In order to define your own custom functions and libraries, you can upload a javascript file from the main panel, in the "Upload Files" section. However it is recommended to work via the CDE platform whenever possible: CDE reduces bugs by giving functions local scope instead of global scope.

  This custom file is loaded after all of the default CDE scripts (but before execution begins). As a result, it can be used to over-write/replace CDE functionality.

  == Login and Registration ==
  '''Warning:''' use only if you are an advanced programmer, and at your own risk. The following allows you to overwrite CDE functionality with some of your own. It is very powerful, but it also gives you the power to mess things up. This functionality is unsupported, and may break backward compatibility.

  First, you need to give yourself access to the advanced scripts:
  1. Click on the "Design" tab
  2. Create a new page with a blank name
  3. In the tree, click on views//index.php
  4. Rename it to views/index.php (File menu -> Rename)
  5. Open this view in the "Dynamics" tab

  Once you have done this, you can overwrite any one of the following default CDE functions with one of your own:
   * currentView.setLogin(): Sets up login functionality. The default version (which you may overwrite) does the following:
     * Defines the callback function on authentication failure. This callback launches the login dialogue box (and also enables Facebook login where relevant), and attaches the relevant callbacks to buttons of this dialogue box. If registration by new users is allowed, it also adds a link to a Registration dialogue box, and adds callbacks to the buttons of that box.
     * Adds event listeners to authEvents (for events AuthFail, UserNotRegistered and UserUndefined)
     * Executes auth();
   * currentView.loginHandle(): Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setRegistration(): Called by default when user clicks on the registration link in the Login dialogue box. (Of course, it will not be called if you overwrote currentView.setLogin())
   * currentView.registrationHandle: Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.registrationValidation(): Function for validation of user registration dialogue box fields. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setPeriodic: Function defining any housekeeping tasks that must be executed on a regular schedule. By default, these are:
     * Refreshing the app every 2 hours
     * Sending any pending data to the server every minute

  In order to overwrite, from the Dynamics tab go to the script view by clicking on any element of the page.

  == User Access Rights ==
  === Basic ===
  By default, each user has access to his own data, and to no other person's. So generally, there is no need to do anything at all!

  === Advanced ===
  CDE's default user rights management is sufficient for most apps, so you generally need not do anything. However certain apps require advanced user management capabilities. CDE allows for this, with some powerful but complex functionality. 

  The fundamental principle is that you can use javascript to grant one account (Account A) access to the contents of another account (Account B). This access can be read, write or read-write

  Once this is done, when logged in as Account A, you will have the ability to read and/or write to account B. Depending on how you configure this access, it can: 
   * Either be restricted to the Account B itself, as though the current user (Account A) were using Account B temporarily (similar to using su or sudo in a linux system)
   * Or, it can give access not only to Account B but also to all accounts that Account B has the same access rights to. So, in this case, if for example Account A has read-write access to Account B, and Account B has read access to Account C, then Account A will be enabled to have read access (but not write access) to Account C. Note that this allows simulation of groups. To do this, simply create an account to act as group, and give it the relevant rights to other accounts. Thus, the group has been created. To add, for instance, Account A to the group as group member, simply give Account A the relevant read-write access to the group account.

  Below is some code illustrating how all this is done.

  In addition, there exist some general-purpose accounts which have pre-defined rights:
   * admin: has read-write access to all accounts
   * _public_r: all accounts can read data written into this account
   * _public_w: all accounts can write (but not read) data into this account
   * _public_rw: all accounts can read and write data into this account

  Below is some example code:

  {{{
  #!java

  /*
  The following code creates some new users, modifies their access rights, and then deletes them.

  For this code to work, you will need to:
  - Contact CDE to request that it be enabled. (It is not enabled by default for security reasons.)
  - Enable authentication for the app (from CDE's main panel)
  - Be logged in as an admin user, such as "admin". (You will receive the password of the account "admin" from us. You 
  can subsequently create additional admin accounts.)

  You may want to play around with this code in the chrome console, pasting it in paragraph by paragraph.
  */

  // Setup
  var cld_permissions = new DataObject('permissions'); // Set up the permissions object
  cld_permissions.addAction('newUser'); // Add the ability to add a new user
  cld_permissions.addAction('addRights'); // Add the ability to grant new rights to an existing user
  cld_permissions.addAction('changePass'); // Add the ability to set a user's password from the admin account
  cld_permissions.addAction('userList'); // Add the ability to list all users
  cld_permissions.addAction('checkAllPermissions'); // Add the ability to check all direct (_altUser) permissions a given account has
  cld_permissions.addAction('checkPermissions'); // Add the ability to check whether a given account has direct (_altUser) access to another
  cld_permissions.addAction('checkSecondaryPermissions'); // Add the ability to check whether a given account has secondary (_group) access to another

  // Add a user called admin2.(Later, this user will be granted admin rights)
  var cfg = {};
  cfg.data = {};
  cfg.data['newUser'] = 'admin2';
  cfg.data['pass'] = 'passAdmin2';
  cfg.successFunc = function(list, allData){
      logg('Done adding user '+cfg.data['newUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['newUser'].reload(cfg);

  // Add some more users, using the same configuration variable cfg as for admin2
  cfg.data['newUser'] = 'deptA';
  cfg.data['pass'] = 'passDeptA';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'JohnSmith';
  cfg.data['pass'] = 'passJohnSmith';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'JaneClark';
  cfg.data['pass'] = 'passJaneClark';
  cld_permissions.act['newUser'].reload(cfg);

  // Give admin2 read-write access to the admin group, thus giving it read-write access to all acounts
  var cfg = {};
  cfg.data = {};
  cfg.data['addedUser'] = 'admin2';
  cfg.data['addedGroup'] = 'admin';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cfg.successFunc = function(list, allData){
      logg('Done granting rights to user '+cfg.data['addingUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['addRights'].reload(cfg);

  // Assign JohnSmith and JaneClark to their department (deptA)
  // JohnSmith is department head, so he has read-write access. JaneClark only has read access.
  cfg.data['addedUser'] = 'JohnSmith';
  cfg.data['addedGroup'] = 'deptA';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cld_permissions.act['addRights'].reload(cfg);

  cfg.data['addedUser'] = 'JaneClark';
  cfg.data['addedGroup'] = 'deptA';
  cfg.data['read'] = '1';
  cfg.data['write'] = '0';
  cld_permissions.act['addRights'].reload(cfg);

  // Set a new password for JaneClark, to allow her to login
  var cfg = {};
  cfg.data = {};
  cfg.data['changedUser'] = 'JaneClark';
  cfg.data['pass'] = 'changedpass';
  cfg.successFunc = function(list, allData){
      logg('Done changing pass of user '+cfg.data['changedUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['changePass'].reload(cfg);

  // Get list of users
  var cfg = {};
  cfg.data = {};
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['userList'].reload(cfg);

  // Get all groups to which JaneClark has rw access
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'JaneClark';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkAllPermissions'].reload(cfg);

  // Check if JaneClark has read-write access to deptA. (She does not.)
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'jane';
  cfg.data['checkedGroup'] = 'deptA';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
      alert(dump(allData));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkPermissions'].reload(cfg);

  // Check if JohnSmith has read access to JaneClark INCLUDING secondary access. (He does not.)
  var cfg = {};
  cfg.params = {};
  cfg.params['checkedUser'] = 'JohnSmith';
  cfg.params['checkedGroup'] = 'JaneClark';
  cfg.params['type'] = 'read';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkSecondaryPermissions'].reload(cfg);

  // We want JohnSmith, as department head, to have read access to the accounts of all group members
  // So we assign deptA read rights to JaneClark
  var cfg = {};
  cfg.data = {};
  cfg.data['addedUser'] = 'deptA';
  cfg.data['addedGroup'] = 'JaneClark';
  cfg.data['read'] = '1';
  cfg.data['write'] = '0';
  cfg.successFunc = function(list, allData){
      logg('Done granting rights to user '+cfg.data['addingUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['addRights'].reload(cfg);

  // Now we check again if JohnSmith has read access to JaneClark INCLUDING secondary access. (He now does.)
  var cfg = {};
  cfg.params = {};
  cfg.params['checkedUser'] = 'JohnSmith';
  cfg.params['checkedGroup'] = 'JaneClark';
  cfg.params['type'] = 'read';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkSecondaryPermissions'].reload(cfg);


  }}}


name: Advanced
author: mnehme
ipnr: 94.187.8.204
text:
  = Advanced Configuration =
  == Adding CSS ==
  A CSS file can be uploaded from the main panel, in the "Upload Files" section.

  == Modifying Default Dialogue Boxes ==
  You can redesign (overwrite) the default login dialogue box and the registration dialogue box, as follows:
   * Login dialogue box: Create a dialogue box called dialogues/login__.php
   * Registration dialogue box: Create a dialogue box called dialogues/register__.php

  Note: By default, these names are not allowed. You will need to create the dialogue box, open it from the "Design" tab, and rename it (File menu -> Rename)

  == Facebook Integration ==
  To enable Facebook login, simply go to the main panel, and in the "Facebook" section fill in the App ID and App Secret. It's as easy as that!

  To automatically save user data on access to the app, go to “Data” tab, and create a table called fbuserdata and another called fbusergraph with a selection of fields from the below. (These are the supported fields.) '''Make sure your app requests permission from the user to access any needed field, otherwise it will not work.'''

  User data is automatically saved into the two tables as described below.

  '''fbuserdata Table'''[[BR]]
  Fields: fbid, firstname, lastname, email, location, gender, locale, locale, ageMin, ageMax, funnelStage[[BR]]
  All of these are defined exactly as in the Facebook API, with the exception of:[[BR]]
  fbid: the user's facebook id[[BR]]
  funnelStage (beta feature): Keeps track of whether the user is a new user or a returning user[[BR]]

  '''fbusergraph Table''' (beta feature)[[BR]]
  Fields: name, category[[BR]]
  Each row of this table corresponds to a user like/interest/group.[[BR]]
  name: name of the like/interest/group[[BR]]
  category: a broad category of the like/interest/group

  = Advanced Programming =
  == Custom Functions ==
  In order to define your own custom functions and libraries, you can upload a javascript file from the main panel, in the "Upload Files" section. However it is recommended to work via the CDE platform whenever possible: CDE reduces bugs by giving functions local scope instead of global scope.

  This custom file is loaded after all of the default CDE scripts (but before execution begins). As a result, it can be used to over-write/replace CDE functionality.

  == Login and Registration ==
  '''Warning:''' use only if you are an advanced programmer, and at your own risk. The following allows you to overwrite CDE functionality with some of your own. It is very powerful, but it also gives you the power to mess things up. This functionality is unsupported, and may break backward compatibility.

  First, you need to give yourself access to the advanced scripts:
  1. Click on the "Design" tab
  2. Create a new page with a blank name
  3. In the tree, click on views//index.php
  4. Rename it to views/index.php (File menu -> Rename)
  5. Open this view in the "Dynamics" tab

  Once you have done this, you can overwrite any one of the following default CDE functions with one of your own:
   * currentView.setLogin(): Sets up login functionality. The default version (which you may overwrite) does the following:
     * Defines the callback function on authentication failure. This callback launches the login dialogue box (and also enables Facebook login where relevant), and attaches the relevant callbacks to buttons of this dialogue box. If registration by new users is allowed, it also adds a link to a Registration dialogue box, and adds callbacks to the buttons of that box.
     * Adds event listeners to authEvents (for events AuthFail, UserNotRegistered and UserUndefined)
     * Executes auth();
   * currentView.loginHandle(): Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setRegistration(): Called by default when user clicks on the registration link in the Login dialogue box. (Of course, it will not be called if you overwrote currentView.setLogin())
   * currentView.registrationHandle: Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.registrationValidation(): Function for validation of user registration dialogue box fields. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setPeriodic: Function defining any housekeeping tasks that must be executed on a regular schedule. By default, these are:
     * Refreshing the app every 2 hours
     * Sending any pending data to the server every minute

  In order to overwrite, from the Dynamics tab go to the script view by clicking on any element of the page.

  == User Access Rights ==
  === Basic ===
  By default, each user has access to his own data, and to no other person's. So generally, there is no need to do anything at all!

  === Advanced ===
  CDE's default user rights management is sufficient for most apps, so you generally need not do anything. However certain apps require advanced user management capabilities. CDE allows for this, with some powerful but complex functionality. 

  The fundamental principle is that you can use javascript to grant one account (Account A) access to the contents of another account (Account B). This access can be read, write or read-write

  Once this is done, when logged in as Account A, you will have the ability to read and/or write to account B. Depending on how you configure this access, it can: 
   * Either be restricted to the Account B itself, as though the current user (Account A) were using Account B temporarily (similar to using su or sudo in a linux system)
   * Or, it can give access not only to Account B but also to all accounts that Account B has the same access rights to. So, in this case, if for example Account A has read-write access to Account B, and Account B has read access to Account C, then Account A will be enabled to have read access (but not write access) to Account C. Note that this allows simulation of groups. To do this, simply create an account to act as group, and give it the relevant rights to other accounts. Thus, the group has been created. To add, for instance, Account A to the group as group member, simply give Account A the relevant read-write access to the group account.

  Below is some code illustrating how all this is done.

  In addition, there exist some general-purpose accounts which have pre-defined rights:
   * admin: has read-write access to all accounts
   * _public_r: all accounts can read data written into this account
   * _public_w: all accounts can write (but not read) data into this account
   * _public_rw: all accounts can read and write data into this account

  Below is some example code:

  {{{
  #!java

  /*
  The following code creates some new users, modifies their access rights, and then deletes them.

  For this code to work, you will need to:
  - Contact CDE to request that it be enabled. (It is not enabled by default for security reasons.)
  - Enable authentication for the app (from CDE's main panel)
  - Be logged in as an admin user, such as "admin". (You will receive the password of the account "admin" from us. You 
  can subsequently create additional admin accounts.)

  You may want to play around with this code in the chrome console, pasting it in paragraph by paragraph.
  */

  // Setup
  var cld_permissions = new DataObject('permissions'); // Set up the permissions object
  cld_permissions.addAction('newUser'); // Add the ability to add a new user
  cld_permissions.addAction('addRights'); // Add the ability to grant new rights to an existing user
  cld_permissions.addAction('changePass'); // Add the ability to set a user's password from the admin account
  cld_permissions.addAction('userList'); // Add the ability to list all users
  cld_permissions.addAction('checkAllPermissions'); // Add the ability to check all direct (_altUser) permissions a given account has
  cld_permissions.addAction('checkPermissions'); // Add the ability to check whether a given account has direct (_altUser) access to another
  cld_permissions.addAction('checkSecondaryPermissions'); // Add the ability to check whether a given account has secondary (_group) access to another
  cld_permissions.addAction('delAllRights'); // Add the ability to delete all of a user's access rights
  cld_permissions.addAction('delRights'); // Add the ability to delete all of a user's access rights to a specific account
  cld_permissions.addAction('delUser'); // Add the ability to delete a user

  // Add a user called admin2.(Later, this user will be granted admin rights)
  var cfg = {};
  cfg.data = {};
  cfg.data['newUser'] = 'admin2';
  cfg.data['pass'] = 'passAdmin2';
  cfg.successFunc = function(list, allData){
      logg('Done adding user '+cfg.data['newUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['newUser'].reload(cfg);

  // Add some more users, using the same configuration variable cfg as for admin2
  cfg.data['newUser'] = 'deptA';
  cfg.data['pass'] = 'passDeptA';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'JohnSmith';
  cfg.data['pass'] = 'passJohnSmith';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'JaneClark';
  cfg.data['pass'] = 'passJaneClark';
  cld_permissions.act['newUser'].reload(cfg);

  // Give admin2 read-write access to the admin group, thus giving it read-write access to all acounts
  var cfg = {};
  cfg.data = {};
  cfg.data['addedUser'] = 'admin2';
  cfg.data['addedGroup'] = 'admin';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cfg.successFunc = function(list, allData){
      logg('Done granting rights to user '+cfg.data['addingUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['addRights'].reload(cfg);

  // Assign JohnSmith and JaneClark to their department (deptA)
  // JohnSmith is department head, so he has read-write access. JaneClark only has read access.
  cfg.data['addedUser'] = 'JohnSmith';
  cfg.data['addedGroup'] = 'deptA';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cld_permissions.act['addRights'].reload(cfg);

  cfg.data['addedUser'] = 'JaneClark';
  cfg.data['addedGroup'] = 'deptA';
  cfg.data['read'] = '1';
  cfg.data['write'] = '0';
  cld_permissions.act['addRights'].reload(cfg);

  // Set a new password for JaneClark, to allow her to login
  var cfg = {};
  cfg.data = {};
  cfg.data['changedUser'] = 'JaneClark';
  cfg.data['pass'] = 'changedpass';
  cfg.successFunc = function(list, allData){
      logg('Done changing pass of user '+cfg.data['changedUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['changePass'].reload(cfg);

  // Get list of users
  var cfg = {};
  cfg.data = {};
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['userList'].reload(cfg);

  // Get all groups to which JaneClark has rw access
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'JaneClark';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkAllPermissions'].reload(cfg);

  // Check if JaneClark has read-write access to deptA. (She does not.)
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'jane';
  cfg.data['checkedGroup'] = 'deptA';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
      alert(dump(allData));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkPermissions'].reload(cfg);

  // Check if JohnSmith has read access to JaneClark INCLUDING secondary access. (He does not.)
  var cfg = {};
  cfg.params = {};
  cfg.params['checkedUser'] = 'JohnSmith';
  cfg.params['checkedGroup'] = 'JaneClark';
  cfg.params['type'] = 'read';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkSecondaryPermissions'].reload(cfg);

  // We want JohnSmith, as department head, to have read access to the accounts of all group members
  // So we assign deptA read rights to JaneClark
  var cfg = {};
  cfg.data = {};
  cfg.data['addedUser'] = 'deptA';
  cfg.data['addedGroup'] = 'JaneClark';
  cfg.data['read'] = '1';
  cfg.data['write'] = '0';
  cfg.successFunc = function(list, allData){
      logg('Done granting rights to user '+cfg.data['addingUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['addRights'].reload(cfg);

  // Now we check again if JohnSmith has read access to JaneClark INCLUDING secondary access. (He now does.)
  var cfg = {};
  cfg.params = {};
  cfg.params['checkedUser'] = 'JohnSmith';
  cfg.params['checkedGroup'] = 'JaneClark';
  cfg.params['type'] = 'read';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkSecondaryPermissions'].reload(cfg);

  // Policy has changed, and we no longer want deptartment employees such as JaneClark to have any access to deptA
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JaneClark';
  cfg.data['delGroup'] = 'deptA';
  cfg.successFunc = function(list, allData){
      logg('Done removing rights of user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delRights'].reload(cfg);

  // JohnSmith is fired, so it is now necessary to delete his account and all his rights
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JohnSmith';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  // JaneClark is about to be moved to a new department, so all her existing rights are revoked
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JaneClark';
  cfg.successFunc = function(list, allData){
      logg('Done removing rights of user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delAllRights'].reload(cfg);

  // Clean-up: deletion of the remaining user accounts created for this demo
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JaneClark';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'admin2';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  }}}


name: Advanced
author: mnehme
ipnr: 94.187.8.204
text:
  = Advanced Configuration =
  == Adding CSS ==
  A CSS file can be uploaded from the main panel, in the "Upload Files" section.

  == Modifying Default Dialogue Boxes ==
  You can redesign (overwrite) the default login dialogue box and the registration dialogue box, as follows:
   * Login dialogue box: Create a dialogue box called dialogues/login__.php
   * Registration dialogue box: Create a dialogue box called dialogues/register__.php

  Note: By default, these names are not allowed. You will need to create the dialogue box, open it from the "Design" tab, and rename it (File menu -> Rename)

  == Facebook Integration ==
  To enable Facebook login, simply go to the main panel, and in the "Facebook" section fill in the App ID and App Secret. It's as easy as that!

  To automatically save user data on access to the app, go to “Data” tab, and create a table called fbuserdata and another called fbusergraph with a selection of fields from the below. (These are the supported fields.) '''Make sure your app requests permission from the user to access any needed field, otherwise it will not work.'''

  User data is automatically saved into the two tables as described below.

  '''fbuserdata Table'''[[BR]]
  Fields: fbid, firstname, lastname, email, location, gender, locale, locale, ageMin, ageMax, funnelStage[[BR]]
  All of these are defined exactly as in the Facebook API, with the exception of:[[BR]]
  fbid: the user's facebook id[[BR]]
  funnelStage (beta feature): Keeps track of whether the user is a new user or a returning user[[BR]]

  '''fbusergraph Table''' (beta feature)[[BR]]
  Fields: name, category[[BR]]
  Each row of this table corresponds to a user like/interest/group.[[BR]]
  name: name of the like/interest/group[[BR]]
  category: a broad category of the like/interest/group

  = Advanced Programming =
  == Custom Functions ==
  In order to define your own custom functions and libraries, you can upload a javascript file from the main panel, in the "Upload Files" section. However it is recommended to work via the CDE platform whenever possible: CDE reduces bugs by giving functions local scope instead of global scope.

  This custom file is loaded after all of the default CDE scripts (but before execution begins). As a result, it can be used to over-write/replace CDE functionality.

  == Login and Registration ==
  '''Warning:''' use only if you are an advanced programmer, and at your own risk. The following allows you to overwrite CDE functionality with some of your own. It is very powerful, but it also gives you the power to mess things up. This functionality is unsupported, and may break backward compatibility.

  First, you need to give yourself access to the advanced scripts:
  1. Click on the "Design" tab
  2. Create a new page with a blank name
  3. In the tree, click on views//index.php
  4. Rename it to views/index.php (File menu -> Rename)
  5. Open this view in the "Dynamics" tab

  Once you have done this, you can overwrite any one of the following default CDE functions with one of your own:
   * currentView.setLogin(): Sets up login functionality. The default version (which you may overwrite) does the following:
     * Defines the callback function on authentication failure. This callback launches the login dialogue box (and also enables Facebook login where relevant), and attaches the relevant callbacks to buttons of this dialogue box. If registration by new users is allowed, it also adds a link to a Registration dialogue box, and adds callbacks to the buttons of that box.
     * Adds event listeners to authEvents (for events AuthFail, UserNotRegistered and UserUndefined)
     * Executes auth();
   * currentView.loginHandle(): Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setRegistration(): Called by default when user clicks on the registration link in the Login dialogue box. (Of course, it will not be called if you overwrote currentView.setLogin())
   * currentView.registrationHandle: Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.registrationValidation(): Function for validation of user registration dialogue box fields. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setPeriodic: Function defining any housekeeping tasks that must be executed on a regular schedule. By default, these are:
     * Refreshing the app every 2 hours
     * Sending any pending data to the server every minute

  In order to overwrite, from the Dynamics tab go to the script view by clicking on any element of the page.

  == User Access Rights ==
  === Basic ===
  By default, each user has access to his own data, and to no other person's. So generally, there is no need to do anything at all!

  === Advanced ===
  CDE's default user rights management is sufficient for most apps, so you generally need not do anything. However certain apps require advanced user management capabilities. CDE allows for this, with some powerful but complex functionality. 

  The fundamental principle is that you can use javascript to grant one account (Account A) access to the contents of another account (Account B). This access can be read, write or read-write

  Once this is done, when logged in as Account A, you will have the ability to read and/or write to account B. Depending on how you configure this access, it can: 
   * Either be restricted to the Account B itself, as though the current user (Account A) were using Account B temporarily (similar to using su or sudo in a linux system)
   * Or, it can give access not only to Account B but also to all accounts that Account B has the same access rights to. So, in this case, if for example Account A has read-write access to Account B, and Account B has read access to Account C, then Account A will be enabled to have read access (but not write access) to Account C. Note that this allows simulation of groups. To do this, simply create an account to act as group, and give it the relevant rights to other accounts. Thus, the group has been created. To add, for instance, Account A to the group as group member, simply give Account A the relevant read-write access to the group account.

  Below is some code illustrating how all this is done.

  In addition, there exist some general-purpose accounts which have pre-defined rights:
   * admin: has read-write access to all accounts
   * _public_r: all accounts can read data written into this account
   * _public_w: all accounts can write (but not read) data into this account
   * _public_rw: all accounts can read and write data into this account

  Below is some example code:

  {{{
  #!java

  /*
  The following code creates some new users, modifies their access rights, and then deletes them.

  For this code to work, you will need to:
  - Contact CDE to request that it be enabled. (It is not enabled by default for security reasons.)
  - Enable authentication for the app (from CDE's main panel)
  - Be logged in as an admin user, such as "admin". (You will receive the password of the account "admin" from us. You 
  can subsequently create additional admin accounts.)

  You may want to play around with this code in the chrome console, pasting it in paragraph by paragraph.
  */

  // Setup
  var cld_permissions = new DataObject('permissions'); // Set up the permissions object
  cld_permissions.addAction('newUser'); // Add the ability to add a new user
  cld_permissions.addAction('addRights'); // Add the ability to grant new rights to an existing user
  cld_permissions.addAction('changePass'); // Add the ability to set a user's password from the admin account
  cld_permissions.addAction('userList'); // Add the ability to list all users
  cld_permissions.addAction('checkAllPermissions'); // Add the ability to check all direct (_altUser) permissions a given account has
  cld_permissions.addAction('checkPermissions'); // Add the ability to check whether a given account has direct (_altUser) access to another
  cld_permissions.addAction('checkSecondaryPermissions'); // Add the ability to check whether a given account has secondary (_group) access to another
  cld_permissions.addAction('delAllRights'); // Add the ability to delete all of a user's access rights
  cld_permissions.addAction('delRights'); // Add the ability to delete all of a user's access rights to a specific account
  cld_permissions.addAction('delUser'); // Add the ability to delete a user

  // Add a user called admin2.(Later, this user will be granted admin rights)
  var cfg = {};
  cfg.data = {};
  cfg.data['newUser'] = 'admin2';
  cfg.data['pass'] = 'passAdmin2';
  cfg.successFunc = function(list, allData){
      logg('Done adding user '+cfg.data['newUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['newUser'].reload(cfg);

  // Add some more users, using the same configuration variable cfg as for admin2
  cfg.data['newUser'] = 'deptA';
  cfg.data['pass'] = 'passDeptA';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'JohnSmith';
  cfg.data['pass'] = 'passJohnSmith';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'JaneClark';
  cfg.data['pass'] = 'passJaneClark';
  cld_permissions.act['newUser'].reload(cfg);

  // Give admin2 read-write access to the admin group, thus giving it read-write access to all acounts
  var cfg = {};
  cfg.data = {};
  cfg.data['addedUser'] = 'admin2';
  cfg.data['addedGroup'] = 'admin';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cfg.successFunc = function(list, allData){
      logg('Done granting rights to user '+cfg.data['addingUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['addRights'].reload(cfg);

  // Assign JohnSmith and JaneClark to their department (deptA)
  // JohnSmith is department head, so he has read-write access. JaneClark only has read access.
  cfg.data['addedUser'] = 'JohnSmith';
  cfg.data['addedGroup'] = 'deptA';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cld_permissions.act['addRights'].reload(cfg);

  cfg.data['addedUser'] = 'JaneClark';
  cfg.data['addedGroup'] = 'deptA';
  cfg.data['read'] = '1';
  cfg.data['write'] = '0';
  cld_permissions.act['addRights'].reload(cfg);

  // Set a new password for JaneClark, to allow her to login
  var cfg = {};
  cfg.data = {};
  cfg.data['changedUser'] = 'JaneClark';
  cfg.data['pass'] = 'changedpass';
  cfg.successFunc = function(list, allData){
      logg('Done changing pass of user '+cfg.data['changedUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['changePass'].reload(cfg);

  // Get list of users
  var cfg = {};
  cfg.data = {};
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['userList'].reload(cfg);

  // Get all groups to which JaneClark has rw access
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'JaneClark';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkAllPermissions'].reload(cfg);

  // Check if JaneClark has read-write access to deptA. (She does not.)
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'jane';
  cfg.data['checkedGroup'] = 'deptA';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
      alert(dump(allData));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkPermissions'].reload(cfg);

  // Check if JohnSmith has read access to JaneClark INCLUDING secondary access. (He does not.)
  var cfg = {};
  cfg.params = {};
  cfg.params['checkedUser'] = 'JohnSmith';
  cfg.params['checkedGroup'] = 'JaneClark';
  cfg.params['type'] = 'read';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkSecondaryPermissions'].reload(cfg);

  // We want JohnSmith, as department head, to have read access to the accounts of all group members
  // So we assign deptA read rights to JaneClark
  var cfg = {};
  cfg.data = {};
  cfg.data['addedUser'] = 'deptA';
  cfg.data['addedGroup'] = 'JaneClark';
  cfg.data['read'] = '1';
  cfg.data['write'] = '0';
  cfg.successFunc = function(list, allData){
      logg('Done granting rights to user '+cfg.data['addingUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['addRights'].reload(cfg);

  // Now we check again if JohnSmith has read access to JaneClark INCLUDING secondary access. (He now does.)
  var cfg = {};
  cfg.params = {};
  cfg.params['checkedUser'] = 'JohnSmith';
  cfg.params['checkedGroup'] = 'JaneClark';
  cfg.params['type'] = 'read';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkSecondaryPermissions'].reload(cfg);

  // Policy has changed, and we no longer want deptartment employees such as JaneClark to have any access to deptA
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JaneClark';
  cfg.data['delGroup'] = 'deptA';
  cfg.successFunc = function(list, allData){
      logg('Done removing rights of user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delRights'].reload(cfg);

  // JohnSmith is fired, so it is now necessary to delete his account and all his rights
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JohnSmith';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  // JaneClark is about to be moved to a new department, so all her existing rights are revoked
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JaneClark';
  cfg.successFunc = function(list, allData){
      logg('Done removing rights of user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delAllRights'].reload(cfg);

  // Clean-up: deletion of the remaining user accounts created for this demo
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JaneClark';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'admin2';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'deptA';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  }}}


name: Advanced
author: mnehme
ipnr: 94.187.8.204
text:
  = Advanced Configuration =
  == Adding CSS ==
  A CSS file can be uploaded from the main panel, in the "Upload Files" section.

  == Modifying Default Dialogue Boxes ==
  You can redesign (overwrite) the default login dialogue box and the registration dialogue box, as follows:
   * Login dialogue box: Create a dialogue box called dialogues/login__.php
   * Registration dialogue box: Create a dialogue box called dialogues/register__.php

  Note: By default, these names are not allowed. You will need to create the dialogue box, open it from the "Design" tab, and rename it (File menu -> Rename)

  == Facebook Integration ==
  To enable Facebook login, simply go to the main panel, and in the "Facebook" section fill in the App ID and App Secret. It's as easy as that!

  To automatically save user data on access to the app, go to “Data” tab, and create a table called fbuserdata and another called fbusergraph with a selection of fields from the below. (These are the supported fields.) '''Make sure your app requests permission from the user to access any needed field, otherwise it will not work.'''

  User data is automatically saved into the two tables as described below.

  '''fbuserdata Table'''[[BR]]
  Fields: fbid, firstname, lastname, email, location, gender, locale, locale, ageMin, ageMax, funnelStage[[BR]]
  All of these are defined exactly as in the Facebook API, with the exception of:[[BR]]
  fbid: the user's facebook id[[BR]]
  funnelStage (beta feature): Keeps track of whether the user is a new user or a returning user[[BR]]

  '''fbusergraph Table''' (beta feature)[[BR]]
  Fields: name, category[[BR]]
  Each row of this table corresponds to a user like/interest/group.[[BR]]
  name: name of the like/interest/group[[BR]]
  category: a broad category of the like/interest/group

  = Advanced Programming =
  == Custom Functions ==
  In order to define your own custom functions and libraries, you can upload a javascript file from the main panel, in the "Upload Files" section. However it is recommended to work via the CDE platform whenever possible: CDE reduces bugs by giving functions local scope instead of global scope.

  This custom file is loaded after all of the default CDE scripts (but before execution begins). As a result, it can be used to over-write/replace CDE functionality.

  == Login and Registration ==
  '''Warning:''' use only if you are an advanced programmer, and at your own risk. The following allows you to overwrite CDE functionality with some of your own. It is very powerful, but it also gives you the power to mess things up. This functionality is unsupported, and may break backward compatibility.

  First, you need to give yourself access to the advanced scripts:
  1. Click on the "Design" tab
  2. Create a new page with a blank name
  3. In the tree, click on views//index.php
  4. Rename it to views/index.php (File menu -> Rename)
  5. Open this view in the "Dynamics" tab

  Once you have done this, you can overwrite any one of the following default CDE functions with one of your own:
   * currentView.setLogin(): Sets up login functionality. The default version (which you may overwrite) does the following:
     * Defines the callback function on authentication failure. This callback launches the login dialogue box (and also enables Facebook login where relevant), and attaches the relevant callbacks to buttons of this dialogue box. If registration by new users is allowed, it also adds a link to a Registration dialogue box, and adds callbacks to the buttons of that box.
     * Adds event listeners to authEvents (for events AuthFail, UserNotRegistered and UserUndefined)
     * Executes auth();
   * currentView.loginHandle(): Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setRegistration(): Called by default when user clicks on the registration link in the Login dialogue box. (Of course, it will not be called if you overwrote currentView.setLogin())
   * currentView.registrationHandle: Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.registrationValidation(): Function for validation of user registration dialogue box fields. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setPeriodic: Function defining any housekeeping tasks that must be executed on a regular schedule. By default, these are:
     * Refreshing the app every 2 hours
     * Sending any pending data to the server every minute

  In order to overwrite, from the Dynamics tab go to the script view by clicking on any element of the page.

  == User Access Rights ==
  === Basic ===
  By default, each user has access to his own data, and to no other person's. So generally, there is no need to do anything at all!

  === What if I need to share data among all accounts? ===
  Some special accounts have been created for allowing sharing of data. They are:
   * _public_r: all accounts can read data written into this account
   * _public_w: all accounts can write (but not read) data into this account
   * _public_rw: all accounts can read and write data into this account

  Below is some example code for reading and writing data to such accounts:

  {{{
  #!java

  /*
  The following code writes some data to a publicly accessible account, and then reads it again.

  For this code to work, you will need to:
  - Create a table called "test" from within CDE
  - Design this table with only one single user input, with id "val"

  */

  // Insert data
  var data={};
  data["ajaxAction"] = 'insert';
  data["_group"] = '_public_rw'; // In the case of groups scenario
  // Or: data["_altUser"] = 'aGroup'; // In the case of alternative users scenario
  data["val"] = 'a';
  cld_test.sendPending(data);
  cld_test.sendPendingAjax();

  // Read the data back again
  var cfg = {};
  cfg.params['_group'] = '_public_rw'; // In the case of groups scenario
  // Or: cfg.params['_altUser'] = '_public_rw'; // In the case of alternative users scenario
  cfg.successFunc = function(list){
    logg('Records: ' + dump(list));
  };
  cfg.failureFunc = function(list,out){
    alert("Error reading data.");
  }
  cld_test.act['select'].reload(cfg);

  }}}

  === Advanced ===
  CDE's default user rights management is sufficient for most apps, so you generally need not do anything. However certain apps require advanced user management capabilities. CDE allows for this, with some powerful but complex functionality. 

  The fundamental principle is that you can use javascript to grant one account (Account A) access to the contents of another account (Account B). This access can be read, write or read-write

  Once this is done, when logged in as Account A, you will have the ability to read and/or write to account B. Depending on how you configure this access, it can: 
   * Either be restricted to the Account B itself, as though the current user (Account A) were using Account B temporarily (similar to using su or sudo in a linux system) [Alternative user scenario]
   * Or, it can give access not only to Account B but also to all accounts that Account B has the same access rights to. So, in this case, if for example Account A has read-write access to Account B, and Account B has read access to Account C, then Account A will be enabled to have read access (but not write access) to Account C. Note that this allows simulation of groups. To do this, simply create an account to act as group, and give it the relevant rights to other accounts. Thus, the group has been created. To add, for instance, Account A to the group as group member, simply give Account A the relevant read-write access to the group account. [Groups scenario]

  Below is some code illustrating how all this is done.

  In addition, there exist some general-purpose accounts which have pre-defined rights:
   * admin: has read-write access to all accounts
   * _public_r: all accounts can read data written into this account
   * _public_w: all accounts can write (but not read) data into this account
   * _public_rw: all accounts can read and write data into this account

  Below is some example code for managing access rights:

  {{{
  #!java

  /*
  The following code creates some new users, modifies their access rights, and then deletes them.

  For this code to work, you will need to:
  - Contact CDE to request that it be enabled. (It is not enabled by default for security reasons.)
  - Enable authentication for the app (from CDE's main panel)
  - Be logged in as an admin user, such as "admin". (You will receive the password of the account "admin" from us. You 
  can subsequently create additional admin accounts.)

  You may want to play around with this code in the chrome console, pasting it in paragraph by paragraph.
  */

  // Setup
  var cld_permissions = new DataObject('permissions'); // Set up the permissions object
  cld_permissions.addAction('newUser'); // Add the ability to add a new user
  cld_permissions.addAction('addRights'); // Add the ability to grant new rights to an existing user
  cld_permissions.addAction('changePass'); // Add the ability to set a user's password from the admin account
  cld_permissions.addAction('userList'); // Add the ability to list all users
  cld_permissions.addAction('checkAllPermissions'); // Add the ability to check all direct (_altUser) permissions a given account has
  cld_permissions.addAction('checkPermissions'); // Add the ability to check whether a given account has direct (_altUser) access to another
  cld_permissions.addAction('checkSecondaryPermissions'); // Add the ability to check whether a given account has secondary (_group) access to another
  cld_permissions.addAction('delAllRights'); // Add the ability to delete all of a user's access rights
  cld_permissions.addAction('delRights'); // Add the ability to delete all of a user's access rights to a specific account
  cld_permissions.addAction('delUser'); // Add the ability to delete a user

  // Add a user called admin2.(Later, this user will be granted admin rights)
  var cfg = {};
  cfg.data = {};
  cfg.data['newUser'] = 'admin2';
  cfg.data['pass'] = 'passAdmin2';
  cfg.successFunc = function(list, allData){
      logg('Done adding user '+cfg.data['newUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['newUser'].reload(cfg);

  // Add some more users, using the same configuration variable cfg as for admin2
  cfg.data['newUser'] = 'deptA';
  cfg.data['pass'] = 'passDeptA';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'JohnSmith';
  cfg.data['pass'] = 'passJohnSmith';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'JaneClark';
  cfg.data['pass'] = 'passJaneClark';
  cld_permissions.act['newUser'].reload(cfg);

  // Give admin2 read-write access to the admin group, thus giving it read-write access to all acounts
  var cfg = {};
  cfg.data = {};
  cfg.data['addedUser'] = 'admin2';
  cfg.data['addedGroup'] = 'admin';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cfg.successFunc = function(list, allData){
      logg('Done granting rights to user '+cfg.data['addingUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['addRights'].reload(cfg);

  // Assign JohnSmith and JaneClark to their department (deptA)
  // JohnSmith is department head, so he has read-write access. JaneClark only has read access.
  cfg.data['addedUser'] = 'JohnSmith';
  cfg.data['addedGroup'] = 'deptA';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cld_permissions.act['addRights'].reload(cfg);

  cfg.data['addedUser'] = 'JaneClark';
  cfg.data['addedGroup'] = 'deptA';
  cfg.data['read'] = '1';
  cfg.data['write'] = '0';
  cld_permissions.act['addRights'].reload(cfg);

  // Set a new password for JaneClark, to allow her to login
  var cfg = {};
  cfg.data = {};
  cfg.data['changedUser'] = 'JaneClark';
  cfg.data['pass'] = 'changedpass';
  cfg.successFunc = function(list, allData){
      logg('Done changing pass of user '+cfg.data['changedUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['changePass'].reload(cfg);

  // Get list of users
  var cfg = {};
  cfg.data = {};
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['userList'].reload(cfg);

  // Get all groups to which JaneClark has rw access
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'JaneClark';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkAllPermissions'].reload(cfg);

  // Check if JaneClark has read-write access to deptA. (She does not.)
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'jane';
  cfg.data['checkedGroup'] = 'deptA';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
      alert(dump(allData));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkPermissions'].reload(cfg);

  // Check if JohnSmith has read access to JaneClark INCLUDING secondary access. (He does not.)
  var cfg = {};
  cfg.params = {};
  cfg.params['checkedUser'] = 'JohnSmith';
  cfg.params['checkedGroup'] = 'JaneClark';
  cfg.params['type'] = 'read';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkSecondaryPermissions'].reload(cfg);

  // We want JohnSmith, as department head, to have read access to the accounts of all group members
  // So we assign deptA read rights to JaneClark
  var cfg = {};
  cfg.data = {};
  cfg.data['addedUser'] = 'deptA';
  cfg.data['addedGroup'] = 'JaneClark';
  cfg.data['read'] = '1';
  cfg.data['write'] = '0';
  cfg.successFunc = function(list, allData){
      logg('Done granting rights to user '+cfg.data['addingUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['addRights'].reload(cfg);

  // Now we check again if JohnSmith has read access to JaneClark INCLUDING secondary access. (He now does.)
  var cfg = {};
  cfg.params = {};
  cfg.params['checkedUser'] = 'JohnSmith';
  cfg.params['checkedGroup'] = 'JaneClark';
  cfg.params['type'] = 'read';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkSecondaryPermissions'].reload(cfg);

  // Policy has changed, and we no longer want deptartment employees such as JaneClark to have any access to deptA
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JaneClark';
  cfg.data['delGroup'] = 'deptA';
  cfg.successFunc = function(list, allData){
      logg('Done removing rights of user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delRights'].reload(cfg);

  // JohnSmith is fired, so it is now necessary to delete his account and all his rights
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JohnSmith';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  // JaneClark is about to be moved to a new department, so all her existing rights are revoked
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JaneClark';
  cfg.successFunc = function(list, allData){
      logg('Done removing rights of user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delAllRights'].reload(cfg);

  // Clean-up: deletion of the remaining user accounts created for this demo
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JaneClark';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'admin2';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'deptA';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  }}}

  Below is some example code for accessing data in another account or group:

  {{{
  #!java

  /*
  The following code writes some data to a publicly accessible account, and then reads it again.

  For this code to work, you will need to:
  - Create a table called "test" from within CDE
  - Design this table with only one single user input, with id "val"

  Account _public_rw is used for this demonstration, since it exists by default

  */

  // Insert data
  var data={};
  data["ajaxAction"] = 'insert';
  data["_group"] = '_public_rw'; // In the case of groups scenario
  // Or: data["_altUser"] = 'aGroup'; // In the case of alternative users scenario
  data["val"] = 'a';
  cld_test.sendPending(data);
  cld_test.sendPendingAjax();

  // Read the data back again
  var cfg = {};
  cfg.params['_group'] = '_public_rw'; // In the case of groups scenario
  // Or: cfg.params['_altUser'] = '_public_rw'; // In the case of alternative users scenario
  cfg.successFunc = function(list){
    logg('Records: ' + dump(list));
  };
  cfg.failureFunc = function(list,out){
    alert("Error reading data.");
  }
  cld_test.act['select'].reload(cfg);

  }}}


name: Advanced
author: mnehme
ipnr: 94.187.8.186
text:
  = Advanced Configuration =
  == Adding CSS ==
  A CSS file can be uploaded from the main panel, in the "Upload Files" section.

  == Modifying Default Dialogue Boxes ==
  You can redesign (overwrite) the default login dialogue box and the registration dialogue box, as follows:
   * Login dialogue box: Create a dialogue box called dialogues/login__.php
   * Registration dialogue box: Create a dialogue box called dialogues/register__.php

  Note: By default, these names are not allowed. You will need to create the dialogue box, open it from the "Design" tab, and rename it (File menu -> Rename)

  == Facebook Integration ==
  To enable Facebook login, simply go to the main panel, and in the "Facebook" section fill in the App ID and App Secret. It's as easy as that!

  To automatically save user data on access to the app, go to “Data” tab, and create a table called fbuserdata and another called fbusergraph with a selection of fields from the below. (These are the supported fields.) '''Make sure your app requests permission from the user to access any needed field, otherwise it will not work.'''

  User data is automatically saved into the two tables as described below.

  '''fbuserdata Table'''[[BR]]
  Fields: fbid, firstname, lastname, email, location, gender, locale, locale, ageMin, ageMax, funnelStage[[BR]]
  All of these are defined exactly as in the Facebook API, with the exception of:[[BR]]
  fbid: the user's facebook id[[BR]]
  funnelStage (beta feature): Keeps track of whether the user is a new user or a returning user[[BR]]

  '''fbusergraph Table''' (beta feature)[[BR]]
  Fields: name, category[[BR]]
  Each row of this table corresponds to a user like/interest/group.[[BR]]
  name: name of the like/interest/group[[BR]]
  category: a broad category of the like/interest/group

  = Advanced Programming =
  == Custom Functions ==
  In order to define your own custom functions and libraries, you can upload a javascript file from the main panel, in the "Upload Files" section. However it is recommended to work via the CDE platform whenever possible: CDE reduces bugs by giving functions local scope instead of global scope.

  This custom file is loaded after all of the default CDE scripts (but before execution begins). As a result, it can be used to over-write/replace CDE functionality.

  == Server Response Codes ==
  Click here for [ServerResponseCode Server Response Code Reference]

  == Login and Registration ==
  '''Warning:''' use only if you are an advanced programmer, and at your own risk. The following allows you to overwrite CDE functionality with some of your own. It is very powerful, but it also gives you the power to mess things up. This functionality is unsupported, and may break backward compatibility.

  First, you need to give yourself access to the advanced scripts:
  1. Click on the "Design" tab
  2. Create a new page with a blank name
  3. In the tree, click on views//index.php
  4. Rename it to views/index.php (File menu -> Rename)
  5. Open this view in the "Dynamics" tab

  Once you have done this, you can overwrite any one of the following default CDE functions with one of your own:
   * currentView.setLogin(): Sets up login functionality. The default version (which you may overwrite) does the following:
     * Defines the callback function on authentication failure. This callback launches the login dialogue box (and also enables Facebook login where relevant), and attaches the relevant callbacks to buttons of this dialogue box. If registration by new users is allowed, it also adds a link to a Registration dialogue box, and adds callbacks to the buttons of that box.
     * Adds event listeners to authEvents (for events AuthFail, UserNotRegistered and UserUndefined)
     * Executes auth();
   * currentView.loginHandle(): Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setRegistration(): Called by default when user clicks on the registration link in the Login dialogue box. (Of course, it will not be called if you overwrote currentView.setLogin())
   * currentView.registrationHandle: Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.registrationValidation(): Function for validation of user registration dialogue box fields. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setPeriodic: Function defining any housekeeping tasks that must be executed on a regular schedule. By default, these are:
     * Refreshing the app every 2 hours
     * Sending any pending data to the server every minute

  In order to overwrite, from the Dynamics tab go to the script view by clicking on any element of the page.

  == User Access Rights ==
  === Basic ===
  By default, each user has access to his own data, and to no other person's. So generally, there is no need to do anything at all!

  === What if I need to share data among all accounts? ===
  Some special accounts have been created for allowing sharing of data. They are:
   * _public_r: all accounts can read data written into this account
   * _public_w: all accounts can write (but not read) data into this account
   * _public_rw: all accounts can read and write data into this account

  Below is some example code for reading and writing data to such accounts:

  {{{
  #!java

  /*
  The following code writes some data to a publicly accessible account, and then reads it again.

  For this code to work, you will need to:
  - Create a table called "test" from within CDE
  - Design this table with only one single user input, with id "val"

  */

  // Insert data
  var data={};
  data["ajaxAction"] = 'insert';
  data["_group"] = '_public_rw'; // In the case of groups scenario
  // Or: data["_altUser"] = 'aGroup'; // In the case of alternative users scenario
  data["val"] = 'a';
  cld_test.sendPending(data);
  cld_test.sendPendingAjax();

  // Read the data back again
  var cfg = {};
  cfg.params['_group'] = '_public_rw'; // In the case of groups scenario
  // Or: cfg.params['_altUser'] = '_public_rw'; // In the case of alternative users scenario
  cfg.successFunc = function(list){
    logg('Records: ' + dump(list));
  };
  cfg.failureFunc = function(list,out){
    alert("Error reading data.");
  }
  cld_test.act['select'].reload(cfg);

  }}}

  === Advanced ===
  CDE's default user rights management is sufficient for most apps, so you generally need not do anything. However certain apps require advanced user management capabilities. CDE allows for this, with some powerful but complex functionality. 

  The fundamental principle is that you can use javascript to grant one account (Account A) access to the contents of another account (Account B). This access can be read, write or read-write

  Once this is done, when logged in as Account A, you will have the ability to read and/or write to account B. Depending on how you configure this access, it can: 
   * Either be restricted to the Account B itself, as though the current user (Account A) were using Account B temporarily (similar to using su or sudo in a linux system) [Alternative user scenario]
   * Or, it can give access not only to Account B but also to all accounts that Account B has the same access rights to. So, in this case, if for example Account A has read-write access to Account B, and Account B has read access to Account C, then Account A will be enabled to have read access (but not write access) to Account C. Note that this allows simulation of groups. To do this, simply create an account to act as group, and give it the relevant rights to other accounts. Thus, the group has been created. To add, for instance, Account A to the group as group member, simply give Account A the relevant read-write access to the group account. [Groups scenario]

  Below is some code illustrating how all this is done.

  In addition, there exist some general-purpose accounts which have pre-defined rights:
   * admin: has read-write access to all accounts
   * _public_r: all accounts can read data written into this account
   * _public_w: all accounts can write (but not read) data into this account
   * _public_rw: all accounts can read and write data into this account

  Below is some example code for managing access rights:

  {{{
  #!java

  /*
  The following code creates some new users, modifies their access rights, and then deletes them.

  For this code to work, you will need to:
  - Contact CDE to request that it be enabled. (It is not enabled by default for security reasons.)
  - Enable authentication for the app (from CDE's main panel)
  - Be logged in as an admin user, such as "admin". (You will receive the password of the account "admin" from us. You 
  can subsequently create additional admin accounts.)

  You may want to play around with this code in the chrome console, pasting it in paragraph by paragraph.
  */

  // Setup
  var cld_permissions = new DataObject('permissions'); // Set up the permissions object
  cld_permissions.addAction('newUser'); // Add the ability to add a new user
  cld_permissions.addAction('addRights'); // Add the ability to grant new rights to an existing user
  cld_permissions.addAction('changePass'); // Add the ability to set a user's password from the admin account
  cld_permissions.addAction('userList'); // Add the ability to list all users
  cld_permissions.addAction('checkAllPermissions'); // Add the ability to check all direct (_altUser) permissions a given account has
  cld_permissions.addAction('checkPermissions'); // Add the ability to check whether a given account has direct (_altUser) access to another
  cld_permissions.addAction('checkSecondaryPermissions'); // Add the ability to check whether a given account has secondary (_group) access to another
  cld_permissions.addAction('delAllRights'); // Add the ability to delete all of a user's access rights
  cld_permissions.addAction('delRights'); // Add the ability to delete all of a user's access rights to a specific account
  cld_permissions.addAction('delUser'); // Add the ability to delete a user

  // Add a user called admin2.(Later, this user will be granted admin rights)
  var cfg = {};
  cfg.data = {};
  cfg.data['newUser'] = 'admin2';
  cfg.data['pass'] = 'passAdmin2';
  cfg.successFunc = function(list, allData){
      logg('Done adding user '+cfg.data['newUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['newUser'].reload(cfg);

  // Add some more users, using the same configuration variable cfg as for admin2
  cfg.data['newUser'] = 'deptA';
  cfg.data['pass'] = 'passDeptA';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'JohnSmith';
  cfg.data['pass'] = 'passJohnSmith';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'JaneClark';
  cfg.data['pass'] = 'passJaneClark';
  cld_permissions.act['newUser'].reload(cfg);

  // Give admin2 read-write access to the admin group, thus giving it read-write access to all acounts
  var cfg = {};
  cfg.data = {};
  cfg.data['addedUser'] = 'admin2';
  cfg.data['addedGroup'] = 'admin';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cfg.successFunc = function(list, allData){
      logg('Done granting rights to user '+cfg.data['addingUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['addRights'].reload(cfg);

  // Assign JohnSmith and JaneClark to their department (deptA)
  // JohnSmith is department head, so he has read-write access. JaneClark only has read access.
  cfg.data['addedUser'] = 'JohnSmith';
  cfg.data['addedGroup'] = 'deptA';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cld_permissions.act['addRights'].reload(cfg);

  cfg.data['addedUser'] = 'JaneClark';
  cfg.data['addedGroup'] = 'deptA';
  cfg.data['read'] = '1';
  cfg.data['write'] = '0';
  cld_permissions.act['addRights'].reload(cfg);

  // Set a new password for JaneClark, to allow her to login
  var cfg = {};
  cfg.data = {};
  cfg.data['changedUser'] = 'JaneClark';
  cfg.data['pass'] = 'changedpass';
  cfg.successFunc = function(list, allData){
      logg('Done changing pass of user '+cfg.data['changedUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['changePass'].reload(cfg);

  // Get list of users
  var cfg = {};
  cfg.data = {};
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['userList'].reload(cfg);

  // Get all groups to which JaneClark has rw access
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'JaneClark';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkAllPermissions'].reload(cfg);

  // Check if JaneClark has read-write access to deptA. (She does not.)
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'jane';
  cfg.data['checkedGroup'] = 'deptA';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
      alert(dump(allData));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkPermissions'].reload(cfg);

  // Check if JohnSmith has read access to JaneClark INCLUDING secondary access. (He does not.)
  var cfg = {};
  cfg.params = {};
  cfg.params['checkedUser'] = 'JohnSmith';
  cfg.params['checkedGroup'] = 'JaneClark';
  cfg.params['type'] = 'read';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkSecondaryPermissions'].reload(cfg);

  // We want JohnSmith, as department head, to have read access to the accounts of all group members
  // So we assign deptA read rights to JaneClark
  var cfg = {};
  cfg.data = {};
  cfg.data['addedUser'] = 'deptA';
  cfg.data['addedGroup'] = 'JaneClark';
  cfg.data['read'] = '1';
  cfg.data['write'] = '0';
  cfg.successFunc = function(list, allData){
      logg('Done granting rights to user '+cfg.data['addingUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['addRights'].reload(cfg);

  // Now we check again if JohnSmith has read access to JaneClark INCLUDING secondary access. (He now does.)
  var cfg = {};
  cfg.params = {};
  cfg.params['checkedUser'] = 'JohnSmith';
  cfg.params['checkedGroup'] = 'JaneClark';
  cfg.params['type'] = 'read';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkSecondaryPermissions'].reload(cfg);

  // Policy has changed, and we no longer want deptartment employees such as JaneClark to have any access to deptA
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JaneClark';
  cfg.data['delGroup'] = 'deptA';
  cfg.successFunc = function(list, allData){
      logg('Done removing rights of user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delRights'].reload(cfg);

  // JohnSmith is fired, so it is now necessary to delete his account and all his rights
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JohnSmith';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  // JaneClark is about to be moved to a new department, so all her existing rights are revoked
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JaneClark';
  cfg.successFunc = function(list, allData){
      logg('Done removing rights of user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delAllRights'].reload(cfg);

  // Clean-up: deletion of the remaining user accounts created for this demo
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JaneClark';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'admin2';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'deptA';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  }}}

  Below is some example code for accessing data in another account or group:

  {{{
  #!java

  /*
  The following code writes some data to a publicly accessible account, and then reads it again.

  For this code to work, you will need to:
  - Create a table called "test" from within CDE
  - Design this table with only one single user input, with id "val"

  Account _public_rw is used for this demonstration, since it exists by default

  */

  // Insert data
  var data={};
  data["ajaxAction"] = 'insert';
  data["_group"] = '_public_rw'; // In the case of groups scenario
  // Or: data["_altUser"] = 'aGroup'; // In the case of alternative users scenario
  data["val"] = 'a';
  cld_test.sendPending(data);
  cld_test.sendPendingAjax();

  // Read the data back again
  var cfg = {};
  cfg.params['_group'] = '_public_rw'; // In the case of groups scenario
  // Or: cfg.params['_altUser'] = '_public_rw'; // In the case of alternative users scenario
  cfg.successFunc = function(list){
    logg('Records: ' + dump(list));
  };
  cfg.failureFunc = function(list,out){
    alert("Error reading data.");
  }
  cld_test.act['select'].reload(cfg);

  }}}


name: ServerResponseCode
author: mnehme
ipnr: 94.187.8.186
text:
  Below are the Server Response codes which can be returned:

  200: Ok (no error)
  201: Ok but no rows affected. (Not always returned. Sometimes 200 will be returned instead.)

  Force browser actions:
  101: Force refresh

  Misc error:
  500: Misc error

  Errors in the ajax call, thrown by ajaxReq:
  601: function not defined
  602: action contains invalid chars
  603: missing action
  604: object name has invalid characters

  SQL:
  620: SQL Error
  621: SQL Duplicate index value

  Auth:
  630: Auth failed
  631: New token sent
  632: Send email invite or change password failed b/c user not registered
  633: User does not have access rights to this group, or this type of access right does not exist.
  634: Facebook auth error
  635: Registration failed: auto-registration is not authorized.
  636: User is already registered.
  637: User is not yet registered.

  Printing errors:
  640: Print of webpage or scripts or etc  failed
  641: Print of webpage or scripts or etc failed: user not defined
  642: Null index of input encountered when turning dialogue box into table.

  Thrown errors:
  610: Unspecified error
  611: E_USER_ERROR
  612: E_USER_WARNING
  No error code: E_USER_NOTICE, E_NOTICE


name: ServerResponseCode
author: mnehme
ipnr: 94.187.8.186
text:
  Below are the Server Response codes which can be returned:

  200: Ok (no error)[[BR]]
  201: Ok but no rows affected. (Not always returned. Sometimes 200 will be returned instead.)[[BR]]

  Force browser actions:[[BR]]
  101: Force refresh[[BR]]

  Misc error:[[BR]]
  500: Misc error[[BR]]

  Errors in the ajax call, thrown by ajaxReq:[[BR]]
  601: function not defined[[BR]]
  602: action contains invalid chars[[BR]]
  603: missing action[[BR]]
  604: object name has invalid characters[[BR]]

  SQL:[[BR]]
  620: SQL Error[[BR]]
  621: SQL Duplicate index value[[BR]]

  Auth:[[BR]]
  630: Auth failed[[BR]]
  631: New token sent[[BR]]
  632: Send email invite or change password failed b/c user not registered[[BR]]
  633: User does not have access rights to this group, or this type of access right does not exist.[[BR]]
  634: Facebook auth error[[BR]]
  635: Registration failed: auto-registration is not authorized.[[BR]]
  636: User is already registered.[[BR]]
  637: User is not yet registered.[[BR]]

  Printing errors:[[BR]]
  640: Print of webpage or scripts or etc  failed[[BR]]
  641: Print of webpage or scripts or etc failed: user not defined[[BR]]
  642: Null index of input encountered when turning dialogue box into table.[[BR]]

  Thrown errors:[[BR]]
  610: Unspecified error[[BR]]
  611: E_USER_ERROR[[BR]]
  612: E_USER_WARNING[[BR]]
  No error code: E_USER_NOTICE, E_NOTICE[[BR]]


name: Advanced
author: mnehme
ipnr: 94.187.8.186
text:
  = Advanced Configuration =
  == Adding CSS ==
  A CSS file can be uploaded from the main panel, in the "Upload Files" section.

  == Modifying Default Dialogue Boxes ==
  You can redesign (overwrite) the default login dialogue box and the registration dialogue box, as follows:
   * Login dialogue box: Create a dialogue box called dialogues/login__.php
   * Registration dialogue box: Create a dialogue box called dialogues/register__.php

  Note: By default, these names are not allowed. You will need to create the dialogue box, open it from the "Design" tab, and rename it (File menu -> Rename)

  == Facebook Integration ==
  To enable Facebook login, simply go to the main panel, and in the "Facebook" section fill in the App ID and App Secret. It's as easy as that!

  To automatically save user data on access to the app, go to “Data” tab, and create a table called fbuserdata and another called fbusergraph with a selection of fields from the below. (These are the supported fields.) '''Make sure your app requests permission from the user to access any needed field, otherwise it will not work.'''

  User data is automatically saved into the two tables as described below.

  '''fbuserdata Table'''[[BR]]
  Fields: fbid, firstname, lastname, email, location, gender, locale, locale, ageMin, ageMax, funnelStage[[BR]]
  All of these are defined exactly as in the Facebook API, with the exception of:[[BR]]
  fbid: the user's facebook id[[BR]]
  funnelStage (beta feature): Keeps track of whether the user is a new user or a returning user[[BR]]

  '''fbusergraph Table''' (beta feature)[[BR]]
  Fields: name, category[[BR]]
  Each row of this table corresponds to a user like/interest/group.[[BR]]
  name: name of the like/interest/group[[BR]]
  category: a broad category of the like/interest/group

  = Advanced Programming =
  == Custom Functions ==
  In order to define your own custom functions and libraries, you can upload a javascript file from the main panel, in the "Upload Files" section. However it is recommended to work via the CDE platform whenever possible: CDE reduces bugs by giving functions local scope instead of global scope.

  This custom file is loaded after all of the default CDE scripts (but before execution begins). As a result, it can be used to over-write/replace CDE functionality.

  == Direct Server Access ==
  Premium Feature
  Click here for [ServerResponseCode Server Response Code Reference]

  == Login and Registration ==
  '''Warning:''' use only if you are an advanced programmer, and at your own risk. The following allows you to overwrite CDE functionality with some of your own. It is very powerful, but it also gives you the power to mess things up. This functionality is unsupported, and may break backward compatibility.

  First, you need to give yourself access to the advanced scripts:
  1. Click on the "Design" tab
  2. Create a new page with a blank name
  3. In the tree, click on views//index.php
  4. Rename it to views/index.php (File menu -> Rename)
  5. Open this view in the "Dynamics" tab

  Once you have done this, you can overwrite any one of the following default CDE functions with one of your own:
   * currentView.setLogin(): Sets up login functionality. The default version (which you may overwrite) does the following:
     * Defines the callback function on authentication failure. This callback launches the login dialogue box (and also enables Facebook login where relevant), and attaches the relevant callbacks to buttons of this dialogue box. If registration by new users is allowed, it also adds a link to a Registration dialogue box, and adds callbacks to the buttons of that box.
     * Adds event listeners to authEvents (for events AuthFail, UserNotRegistered and UserUndefined)
     * Executes auth();
   * currentView.loginHandle(): Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setRegistration(): Called by default when user clicks on the registration link in the Login dialogue box. (Of course, it will not be called if you overwrote currentView.setLogin())
   * currentView.registrationHandle: Called by default when user clicks on the login button. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.registrationValidation(): Function for validation of user registration dialogue box fields. (Of course, it will not be called if you overwrote currentView.setLogin()) 
   * currentView.setPeriodic: Function defining any housekeeping tasks that must be executed on a regular schedule. By default, these are:
     * Refreshing the app every 2 hours
     * Sending any pending data to the server every minute

  In order to overwrite, from the Dynamics tab go to the script view by clicking on any element of the page.

  == User Access Rights ==
  === Basic ===
  By default, each user has access to his own data, and to no other person's. So generally, there is no need to do anything at all!

  === What if I need to share data among all accounts? ===
  Some special accounts have been created for allowing sharing of data. They are:
   * _public_r: all accounts can read data written into this account
   * _public_w: all accounts can write (but not read) data into this account
   * _public_rw: all accounts can read and write data into this account

  Below is some example code for reading and writing data to such accounts:

  {{{
  #!java

  /*
  The following code writes some data to a publicly accessible account, and then reads it again.

  For this code to work, you will need to:
  - Create a table called "test" from within CDE
  - Design this table with only one single user input, with id "val"

  */

  // Insert data
  var data={};
  data["ajaxAction"] = 'insert';
  data["_group"] = '_public_rw'; // In the case of groups scenario
  // Or: data["_altUser"] = 'aGroup'; // In the case of alternative users scenario
  data["val"] = 'a';
  cld_test.sendPending(data);
  cld_test.sendPendingAjax();

  // Read the data back again
  var cfg = {};
  cfg.params['_group'] = '_public_rw'; // In the case of groups scenario
  // Or: cfg.params['_altUser'] = '_public_rw'; // In the case of alternative users scenario
  cfg.successFunc = function(list){
    logg('Records: ' + dump(list));
  };
  cfg.failureFunc = function(list,out){
    alert("Error reading data.");
  }
  cld_test.act['select'].reload(cfg);

  }}}

  === Advanced ===
  CDE's default user rights management is sufficient for most apps, so you generally need not do anything. However certain apps require advanced user management capabilities. CDE allows for this, with some powerful but complex functionality. 

  The fundamental principle is that you can use javascript to grant one account (Account A) access to the contents of another account (Account B). This access can be read, write or read-write

  Once this is done, when logged in as Account A, you will have the ability to read and/or write to account B. Depending on how you configure this access, it can: 
   * Either be restricted to the Account B itself, as though the current user (Account A) were using Account B temporarily (similar to using su or sudo in a linux system) [Alternative user scenario]
   * Or, it can give access not only to Account B but also to all accounts that Account B has the same access rights to. So, in this case, if for example Account A has read-write access to Account B, and Account B has read access to Account C, then Account A will be enabled to have read access (but not write access) to Account C. Note that this allows simulation of groups. To do this, simply create an account to act as group, and give it the relevant rights to other accounts. Thus, the group has been created. To add, for instance, Account A to the group as group member, simply give Account A the relevant read-write access to the group account. [Groups scenario]

  Below is some code illustrating how all this is done.

  In addition, there exist some general-purpose accounts which have pre-defined rights:
   * admin: has read-write access to all accounts
   * _public_r: all accounts can read data written into this account
   * _public_w: all accounts can write (but not read) data into this account
   * _public_rw: all accounts can read and write data into this account

  Below is some example code for managing access rights:

  {{{
  #!java

  /*
  The following code creates some new users, modifies their access rights, and then deletes them.

  For this code to work, you will need to:
  - Contact CDE to request that it be enabled. (It is not enabled by default for security reasons.)
  - Enable authentication for the app (from CDE's main panel)
  - Be logged in as an admin user, such as "admin". (You will receive the password of the account "admin" from us. You 
  can subsequently create additional admin accounts.)

  You may want to play around with this code in the chrome console, pasting it in paragraph by paragraph.
  */

  // Setup
  var cld_permissions = new DataObject('permissions'); // Set up the permissions object
  cld_permissions.addAction('newUser'); // Add the ability to add a new user
  cld_permissions.addAction('addRights'); // Add the ability to grant new rights to an existing user
  cld_permissions.addAction('changePass'); // Add the ability to set a user's password from the admin account
  cld_permissions.addAction('userList'); // Add the ability to list all users
  cld_permissions.addAction('checkAllPermissions'); // Add the ability to check all direct (_altUser) permissions a given account has
  cld_permissions.addAction('checkPermissions'); // Add the ability to check whether a given account has direct (_altUser) access to another
  cld_permissions.addAction('checkSecondaryPermissions'); // Add the ability to check whether a given account has secondary (_group) access to another
  cld_permissions.addAction('delAllRights'); // Add the ability to delete all of a user's access rights
  cld_permissions.addAction('delRights'); // Add the ability to delete all of a user's access rights to a specific account
  cld_permissions.addAction('delUser'); // Add the ability to delete a user

  // Add a user called admin2.(Later, this user will be granted admin rights)
  var cfg = {};
  cfg.data = {};
  cfg.data['newUser'] = 'admin2';
  cfg.data['pass'] = 'passAdmin2';
  cfg.successFunc = function(list, allData){
      logg('Done adding user '+cfg.data['newUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['newUser'].reload(cfg);

  // Add some more users, using the same configuration variable cfg as for admin2
  cfg.data['newUser'] = 'deptA';
  cfg.data['pass'] = 'passDeptA';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'JohnSmith';
  cfg.data['pass'] = 'passJohnSmith';
  cld_permissions.act['newUser'].reload(cfg);
  cfg.data['newUser'] = 'JaneClark';
  cfg.data['pass'] = 'passJaneClark';
  cld_permissions.act['newUser'].reload(cfg);

  // Give admin2 read-write access to the admin group, thus giving it read-write access to all acounts
  var cfg = {};
  cfg.data = {};
  cfg.data['addedUser'] = 'admin2';
  cfg.data['addedGroup'] = 'admin';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cfg.successFunc = function(list, allData){
      logg('Done granting rights to user '+cfg.data['addingUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['addRights'].reload(cfg);

  // Assign JohnSmith and JaneClark to their department (deptA)
  // JohnSmith is department head, so he has read-write access. JaneClark only has read access.
  cfg.data['addedUser'] = 'JohnSmith';
  cfg.data['addedGroup'] = 'deptA';
  cfg.data['read'] = '1';
  cfg.data['write'] = '1';
  cld_permissions.act['addRights'].reload(cfg);

  cfg.data['addedUser'] = 'JaneClark';
  cfg.data['addedGroup'] = 'deptA';
  cfg.data['read'] = '1';
  cfg.data['write'] = '0';
  cld_permissions.act['addRights'].reload(cfg);

  // Set a new password for JaneClark, to allow her to login
  var cfg = {};
  cfg.data = {};
  cfg.data['changedUser'] = 'JaneClark';
  cfg.data['pass'] = 'changedpass';
  cfg.successFunc = function(list, allData){
      logg('Done changing pass of user '+cfg.data['changedUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['changePass'].reload(cfg);

  // Get list of users
  var cfg = {};
  cfg.data = {};
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['userList'].reload(cfg);

  // Get all groups to which JaneClark has rw access
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'JaneClark';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkAllPermissions'].reload(cfg);

  // Check if JaneClark has read-write access to deptA. (She does not.)
  var cfg = {};
  cfg.data = {};
  cfg.data['checkedUser'] = 'jane';
  cfg.data['checkedGroup'] = 'deptA';
  cfg.data['type'] = 'rw';
  cfg.successFunc = function(list, allData){
      alert(dump(allData));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkPermissions'].reload(cfg);

  // Check if JohnSmith has read access to JaneClark INCLUDING secondary access. (He does not.)
  var cfg = {};
  cfg.params = {};
  cfg.params['checkedUser'] = 'JohnSmith';
  cfg.params['checkedGroup'] = 'JaneClark';
  cfg.params['type'] = 'read';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkSecondaryPermissions'].reload(cfg);

  // We want JohnSmith, as department head, to have read access to the accounts of all group members
  // So we assign deptA read rights to JaneClark
  var cfg = {};
  cfg.data = {};
  cfg.data['addedUser'] = 'deptA';
  cfg.data['addedGroup'] = 'JaneClark';
  cfg.data['read'] = '1';
  cfg.data['write'] = '0';
  cfg.successFunc = function(list, allData){
      logg('Done granting rights to user '+cfg.data['addingUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['addRights'].reload(cfg);

  // Now we check again if JohnSmith has read access to JaneClark INCLUDING secondary access. (He now does.)
  var cfg = {};
  cfg.params = {};
  cfg.params['checkedUser'] = 'JohnSmith';
  cfg.params['checkedGroup'] = 'JaneClark';
  cfg.params['type'] = 'read';
  cfg.successFunc = function(list, allData){
      alert(dump(list));
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['checkSecondaryPermissions'].reload(cfg);

  // Policy has changed, and we no longer want deptartment employees such as JaneClark to have any access to deptA
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JaneClark';
  cfg.data['delGroup'] = 'deptA';
  cfg.successFunc = function(list, allData){
      logg('Done removing rights of user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delRights'].reload(cfg);

  // JohnSmith is fired, so it is now necessary to delete his account and all his rights
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JohnSmith';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  // JaneClark is about to be moved to a new department, so all her existing rights are revoked
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JaneClark';
  cfg.successFunc = function(list, allData){
      logg('Done removing rights of user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delAllRights'].reload(cfg);

  // Clean-up: deletion of the remaining user accounts created for this demo
  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'JaneClark';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'admin2';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  var cfg = {};
  cfg.data = {};
  cfg.data['delUser'] = 'deptA';
  cfg.successFunc = function(list, allData){
      logg('Done removing user '+cfg.data['delUser']);
  };
  cfg.failureFunc = function(list, out){
      // out: is the full response and includes out['replyCode'] and out['replyText']. Available only if there was a response
      var msg = '';
      if(out) msg = out['replyCode'] + ': ' + out['replyText'];
      alert('Failed: '+msg);
  };
  cld_permissions.act['delUser'].reload(cfg);

  }}}

  Below is some example code for accessing data in another account or group:

  {{{
  #!java

  /*
  The following code writes some data to a publicly accessible account, and then reads it again.

  For this code to work, you will need to:
  - Create a table called "test" from within CDE
  - Design this table with only one single user input, with id "val"

  Account _public_rw is used for this demonstration, since it exists by default

  */

  // Insert data
  var data={};
  data["ajaxAction"] = 'insert';
  data["_group"] = '_public_rw'; // In the case of groups scenario
  // Or: data["_altUser"] = 'aGroup'; // In the case of alternative users scenario
  data["val"] = 'a';
  cld_test.sendPending(data);
  cld_test.sendPendingAjax();

  // Read the data back again
  var cfg = {};
  cfg.params['_group'] = '_public_rw'; // In the case of groups scenario
  // Or: cfg.params['_altUser'] = '_public_rw'; // In the case of alternative users scenario
  cfg.successFunc = function(list){
    logg('Records: ' + dump(list));
  };
  cfg.failureFunc = function(list,out){
    alert("Error reading data.");
  }
  cld_test.act['select'].reload(cfg);

  }}}


