a895ff2eb275fd438553757f42eebbbd6d19b815
[civicrm-core.git] / ext / afform / README.md
1 # org.civicrm.afform (Early Proof of Concept)
2
3 ![Screenshot](/images/screenshot.png)
4
5 The Affable Administrative Angular Form Framework (`afform`) is a system for administering AngularJS-based forms
6 in CiviCRM which:
7
8 1. Allows developers to declaratively define a canonical, baseline form.
9 2. Allows administrators (or administrative tools) to use the CRUD API to customize the forms.
10 3. Allows developers to embed these forms in other CiviCRM-AngularJS apps.
11 4. (WIP; pending upstream support) Allow developers to apply [change sets](https://docs.civicrm.org/dev/en/latest/framework/angular/changeset/).
12
13 The extension is licensed under [AGPL-3.0](LICENSE.txt).
14
15 ## Requirements
16
17 * PHP v5.4+
18 * CiviCRM v5.3+
19
20 ## Installation (Web UI)
21
22 This extension has not yet been published for installation via the web UI.
23
24 ## Installation (CLI, Zip)
25
26 Sysadmins and developers may download the `.zip` file for this extension and
27 install it with the command-line tool [cv](https://github.com/civicrm/cv).
28
29 ```bash
30 cd <extension-dir>
31 cv dl org.civicrm.afform@https://github.com/FIXME/org.civicrm.afform/archive/master.zip
32 ```
33
34 ## Installation (CLI, Git)
35
36 Sysadmins and developers may clone the [Git](https://en.wikipedia.org/wiki/Git) repo for this extension and
37 install it with the command-line tool [cv](https://github.com/civicrm/cv).
38
39 ```bash
40 git clone https://github.com/FIXME/org.civicrm.afform.git
41 cv en afform
42 ```
43
44 ## Development: Quick Start
45
46 As an extension author, you can define a form along with its default,
47 canonical content. Simply create a folder named `afform/<MY-FORM>`. In
48 this example, we create a form named `foobar`:
49
50 ```
51 $ cd /path/to/my/own/extension
52 $ mkdir -p afform/foobar
53 $ echo '{"server_route": "civicrm/pretty-page"}' > afform/foobar/meta.json
54 $ echo '<div>Hello {{routeParams.name}}</div>' > afform/foobar/layout.html
55 $ cv flush
56 ```
57
58 A few things to note:
59
60 * We defined a route `civicrm/pretty-page`. This is defined in the same routing system used by CiviCRM forms.
61 * The file `layout.html` is an AngularJS HTML document. It has access to all the general features of Angular HTML (discussed more later).
62 * After creating a new form or file, we should flush the cache.
63 * If you're going to actively edit/revise the content of the file, then you should navigate
64 to **Administer > System Settings > Debugging** and disable asset caching.
65
66 Now that we've created a form, we'll want to determine its URL. As with most
67 CiviCRM forms, the URL depends on the CMS configuration. Here is an example
68 from a local Drupal 7 site:
69
70 ```
71 $ cv url "civicrm/pretty-page"
72 "http://dmaster.localhost/civicrm/pretty-page"
73 $ cv url "civicrm/pretty-page/#/?name=world"
74 "http://dmaster.localhost/civicrm/pretty-page/#/?name=world"
75 ```
76
77 Open the URLs and see what you get.
78
79 ## Development: Scopes, variables, and directives - oh my!
80
81 In AngularJS, the primary language for orchestrating a screen is HTML. You can embed code in here.
82
83 One key concept *scope* -- the *scope* defines the list of variables which you can access. By default, `afform`
84 provides a few variables within the scope of every form:
85
86 * `routeParams`: This is a reference to the [$routeParams](https://docs.angularjs.org/api/ngRoute/service/$routeParams)
87 service. In the example, we used `routeParams` to get a reference to a `name` from the URL.
88 * `meta`: The stored meta data (`meta.json`) for this form.
89 * `ts`: This is a utility function which translates strings, as in `{{ts('Hello world')}}`.
90
91 Additionally, AngularJS allows *directives* -- these are extra HTML attributes and HTML tags. For example:
92
93 * `ng-if` will conditionally create or destroy elements in the page.
94 * `ng-repeat` will loop through data.
95 * `ng-style` and `ng-class` will conditionally apply styling.
96
97 A full explanation of these features is out-of-scope for this document, but the key point is that you can use standard
98 AngularJS features.
99
100 ## Development: Display a contact record
101
102 Let's say we want `foobar` to become a basic "View Contact" page. A user
103 would request a URL like:
104
105 ```
106 http://dmaster.localhost/civicrm/pretty-page/#/?cid=123
107 ```
108
109 How do we use the `cid` to get information about the contact? Update `layout.html` to include data from APIv3:
110
111 ```html
112 <div ng-if="routeParams.cid"
113 afform-api3="['Contact', 'get', {id: routeParams.cid}]"
114 afform-api3-ctrl="apiData">
115
116 <div ng-repeat="contact in apiData.result.values">
117 <h1 crm-page-title="">{{contact.display_name}}</h1>
118
119 <h3>Key Contact Fields</h3>
120
121 <div><strong>Contact ID</strong>: {{contact.contact_id}}</div>
122 <div><strong>Contact Type</strong>: {{contact.contact_type}}</div>
123 <div><strong>Display Name</strong>: {{contact.display_name}}</div>
124 <div><strong>First Name</strong>: {{contact.first_name}}</div>
125 <div><strong>Last Name</strong>: {{contact.last_name}}</div>
126
127 <h3>Full Contact record</h3>
128
129 <pre>{{contact|json}}</pre>
130 </div>
131 </div>
132 ```
133
134 This example is useful pedagogically and may be useful in a crunch -- but in the longer term,
135 we should have a richer library of directives so that typical user-managed forms don't drill-down
136 at this level of detail.
137
138 ## Development: Form CRUD API
139
140 Now that we've defined a baseline form, it's possible for administrators and
141 GUI applications to inspect the form using the API:
142
143 ```
144 $ cv api afform.getsingle name=foobar
145 {
146 "name": "foobar",
147 "requires": [
148 "afformCore"
149 ],
150 "title": "",
151 "description": "",
152 "layout": {
153 "#tag": "div",
154 "#children": [
155 "Hello {{routeParams.name}}"
156 ]
157 },
158 "id": "foobar"
159 }
160 ```
161
162 Additionally, you can also update the forms:
163
164 ```
165 $ cv api afform.create name=foobar title="The Foo Bar Screen"
166 {
167 "is_error": 0,
168 "version": 3,
169 "count": 2,
170 "values": {
171 "name": "foobar",
172 "title": "The Foo Bar Screen"
173 }
174 }
175 ```
176
177 A few important things to note about this:
178
179 * The changes are only applied on this site.
180 * Once you make a change with the CRUD API, there will be two copies of the form:
181 * `[myextension]/afform/foobar/` is the default, canonical version.
182 * `[civicrm.files]/afform/foobar/` is the locally customized version.
183 * The `layout` field is stored as an Angular-style HTML document (`layout.html`), so you can edit it on disk like
184 normal Angular code. However, when CRUD'ing the `layout` through the API, it is presented in JSON-style.
185
186
187 ## Development: Every form is an AngularJS directive
188
189 In the quick-start example, we registered a new route (`"server_route": "civicrm/pretty-page"`) -- this created a
190 simple, standalone page with the sole purpose of displaying the `foobar` form. But that had very limited information.
191 What if we want control the environment a bit more?
192
193 There's no obligation to use the simple, standalone version of `foobar`. Think of `foobar` as a *re-usable sub-form*
194 or as a *directive*. If you've created an AngluarJS application, then you can embed `foobar` with two small steps:
195
196 1. In your application, declare a dependency on module `afformFoobar`. This is usually done in `ang/MYMODULE.ang.php`
197 and/or `ang/MYMODULE.js`.
198 2. In your HTML template, use the directive `<div afform-foobar=""></div>`.
199
200 This technique is particularly useful if you want to provide extra data for the form author to use. For example, your
201 application might pass in the current phase of the moon:
202
203 ```html
204 <div afform-foobar="{phaseOfMoon: 'waxing'}"></div>
205 ```
206
207 Now, in `afform/foobar/layout.html`, you can use the `phaseOfMoon`:
208
209 ```html
210 Hello, {{routeParams.name}}. The moon is currently {{options.phaseOfMoon}}.
211 ```
212
213 Or if you're not sure data will actually be provided:
214
215 ```html
216 Hello, {{routeParams.name ? routeParams.name : 'anonymous'}}. The moon is currently {{options.phaseOfMoon ? options.phaseOfMoon : 'on hiatus'}}.
217 ```
218
219 ## Known Issues
220
221 * The code is currently written as a proof-of-concept. There are several `FIXME`/`TODO` declarations in the code
222 for checking pre-conditions, reporting errors, handling edge-cases, etc.
223 * Although afforms are can be used in AngularJS, they don't fully support tooling like `cv ang:html:list`
224 and `hook_civicrm_alterAngular` changesets. We'll need a core patch to allow that.
225 * We generally need to provide more services for managing/accessing data (e.g. `crm-api3`).
226 * Need to implement the `Afform.revert` API to undo local customizations.
227 * Haven't decided if we should support a `client_route` property (i.e. defining a skeletal controller and route for any form).
228 On the plus side, make it easier to add items to the `civicrm/a` base-page. On the flipside, we don't currently have
229 a strong use-case, and developers can get the same effect with `civix generate:angular-page` and embedding `<div afform-foobar/>`.