From 8a0ffdf9894e65e406cfb7afa9935a330e36139c Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Mon, 30 Jul 2018 16:36:52 -0700 Subject: [PATCH] Split README.md into smaller `docs/*.md` files --- ext/afform/README.md | 239 ++-------------------------------- ext/afform/docs/angular.md | 17 +++ ext/afform/docs/crud.md | 47 +++++++ ext/afform/docs/embed.md | 56 ++++++++ ext/afform/docs/quickstart.md | 34 +++++ ext/afform/docs/roadmap.md | 15 +++ ext/afform/docs/writing.md | 59 +++++++++ 7 files changed, 238 insertions(+), 229 deletions(-) create mode 100644 ext/afform/docs/angular.md create mode 100644 ext/afform/docs/crud.md create mode 100644 ext/afform/docs/embed.md create mode 100644 ext/afform/docs/quickstart.md create mode 100644 ext/afform/docs/roadmap.md create mode 100644 ext/afform/docs/writing.md diff --git a/ext/afform/README.md b/ext/afform/README.md index 0789ac8220..be92e3aec5 100644 --- a/ext/afform/README.md +++ b/ext/afform/README.md @@ -10,7 +10,9 @@ in CiviCRM which: 3. Allows developers to embed these forms in other CiviCRM-AngularJS apps. 4. (WIP; pending upstream support) Allow developers to apply [change sets](https://docs.civicrm.org/dev/en/latest/framework/angular/changeset/). -The extension is licensed under [AGPL-3.0](LICENSE.txt). +This extension is a proof-of-concept. It aims to demonstrate the core model/concept -- however, there are +[known issues and additional components](docs/roadmap.md) to address, and some documentation will be easier to approach +if you already have a basic understanding of CiviCRM API and AngularJS. It is licensed under [AGPL-3.0](LICENSE.txt). ## Requirements @@ -41,232 +43,11 @@ git clone https://github.com/FIXME/org.civicrm.afform.git cv en afform ``` -## Development: Quick Start +## Development -As an extension author, you can define a form along with its default, -canonical content. Simply create a folder named `afform/`. In -this example, we create a form named `helloworld`: - -``` -$ cd /path/to/my/own/extension -$ mkdir -p afform/helloworld -$ echo '{"server_route": "civicrm/hello-world"}' > afform/helloworld/meta.json -$ echo '
Hello {{routeParams.name}}
' > afform/helloworld/layout.html -$ cv flush -``` - -A few things to note: - -* We defined a route `civicrm/hello-world`. This is defined in the same routing system used by CiviCRM forms. -* The file `layout.html` is an AngularJS HTML document. It has access to all the general features of Angular HTML (discussed more later). -* After creating a new form or file, we should flush the cache. -* If you're going to actively edit/revise the content of the file, then you should navigate - to **Administer > System Settings > Debugging** and disable asset caching. - -Now that we've created a form, we'll want to determine its URL. As with most -CiviCRM forms, the URL depends on the CMS configuration. Here is an example -from a local Drupal 7 site: - -``` -$ cv url "civicrm/hello-world" -"http://dmaster.localhost/civicrm/hello-world" -$ cv url "civicrm/hello-world/#/?name=world" -"http://dmaster.localhost/civicrm/hello-world/#/?name=world" -``` - -Open the URLs and see what you get. - -## Development: Writing HTML templates - -In AngularJS, the primary language for orchestrating a screen is HTML. You can do interesting things in Angular -HTML, such as displaying variables and applying directives. - -One key concept is *scope* -- the *scope* determines the list of variables which you can access. By default, `afform` -creates a scope with these variables: - -* `routeParams`: This is a reference to the [$routeParams](https://docs.angularjs.org/api/ngRoute/service/$routeParams) - service. In the example, we used `routeParams` to get a reference to a `name` from the URL. -* `meta`: The stored meta data (`meta.json`) for this form. -* `ts`: This is a utility function which translates strings, as in `{{ts('Hello world')}}`. - -Additionally, AngularJS allows *directives* -- these are extra HTML attributes which create behavior. For example: - -* `ng-if` will conditionally create or destroy elements in the page. -* `ng-repeat` will loop through data. -* `ng-style` and `ng-class` will conditionally apply styling. - -A full explanation of these features is out-of-scope for this document, but the key point is that you can use standard -AngularJS markup. - -## Development: Writing HTML templates: Contact record example - -Let's say we want `helloworld` to become a basic "View Contact" page. A user -would request a URL like: - -``` -http://dmaster.localhost/civicrm/hello-world/#/?cid=123 -``` - -How do we use the `cid` to get information about the contact? Update `layout.html` to include data from APIv3: - -```html -
- -
-

{{contact.display_name}}

- -

Key Contact Fields

- -
Contact ID: {{contact.contact_id}}
-
Contact Type: {{contact.contact_type}}
-
Display Name: {{contact.display_name}}
-
First Name: {{contact.first_name}}
-
Last Name: {{contact.last_name}}
- -

Full Contact record

- -
{{contact|json}}
-
-
-``` - -This example is useful pedagogically and may be useful in a crunch -- but in the longer term, -we should have a richer library of directives so that typical user-managed forms don't drill-down -at this level of detail. - -## Development: Form CRUD API - -Now that we've defined a baseline form, it's possible for administrators and -GUI applications to inspect the form using the API: - -``` -$ cv api afform.getsingle name=helloworld -{ - "name": "helloworld", - "requires": [ - "afformCore" - ], - "title": "", - "description": "", - "layout": { - "#tag": "div", - "#children": [ - "Hello {{routeParams.name}}" - ] - }, - "id": "helloworld" -} -``` - -Additionally, you can also update the forms: - -``` -$ cv api afform.create name=helloworld title="The Foo Bar Screen" -{ - "is_error": 0, - "version": 3, - "count": 2, - "values": { - "name": "helloworld", - "title": "The Foo Bar Screen" - } -} -``` - -A few important things to note about this: - -* The changes are only applied on this site. -* Once you make a change with the CRUD API, there will be two copies of the form: - * `[myextension]/afform/helloworld/` is the default, canonical version. - * `[civicrm.files]/afform/helloworld/` is the local, custom version. -* The `layout` field is stored as an Angular-style HTML document (`layout.html`), so you can edit it on disk like - normal Angular code. However, when CRUD'ing the `layout` through the API, it is presented in JSON-style. - -## Development: Embedding forms - -In the quick-start example, we registered a new route (`"server_route": "civicrm/hello-world"`) -- this created a -simple, standalone page with the sole purpose of displaying the `helloworld` form. What if we want to embed the form -somewhere else -- e.g. as a dialog inside an event-listing or membership directory? Afforms are actualy *re-usable -sub-forms*. - -How does this work? Every `afform` is an *AngularJS directive*. For example, `helloworld` can be embedded with: - -```html -
-``` - -Moreover, you can pass options to `helloworld`: - -```html -
-``` - -Now, in `afform/helloworld/layout.html`, you can use `options.phaseOfMoon`: - -```html -Hello, {{routeParams.name}}. The moon is currently {{options.phaseOfMoon}}. -``` - -## Development: Embedding forms: Contact record example - -Is this useful? Let's suppose you're building a contact record page. - -First, we should make a few building-blocks: - -1. `afform/contactName/layout.html` displays a sub-form for editing first name, lastname, prefix, suffix, etc. -2. `afform/contactAddressess/layout.html` displays a sub-form for editing street addresses. -3. `afform/contactEmails/layout.html` displays a sub-form for editing email addresses. - -Next, we should create an overall `afform/contact/layout.html` which uses these building-blocks: - -```html -
-
-
-
-
-
-
-
-
-
-
-``` - -> *(FIXME: In the parent form's `meta.json`, we need to manually add `afformContactName`, `afformContactAddresses`, `afformContactEmails` to the `requires` list. We should autodetect these instead.)* - -What does this buy us? It means that a downstream admin (using APIs/GUIs) can fork `afform/contactName/layout.html` -- -but all the other components can cleanly track the canonical release. This significantly reduces the costs and risks -of manging upgrades and changes. - -## Development: Full AngularJS - -Afform is a subset of AngularJS -- it emphasizes the use of *directives* as a way to *choose and arrange* the parts of -your form. There is more to AngularJS -- such as client-side routing, controllers, services, etc. What to do if you -need these? Here are few tricks: - -* You can create your own applications and pages with full AngularJS. (See also: [CiviCRM Developer Guide: AngularJS: Quick Start](https://docs.civicrm.org/dev/en/latest/framework/angular/quickstart/)). - Then embed the afform (like `helloworld`) in your page with these steps: - * Declare a dependency on module (`afformHelloworld`). This is usually done in `ang/MYMODULE.ang.php` and/or `ang/MYMODULE.js`. - * In your HTML template, use the directive `
`. I - * f you want to provide extra data, services, or actions for the form author -- then pass them along. -* You can write your own directives with full AngularJS (e.g. `civix generate:angular-directive`). These directives become available for use in other afforms. -* If you start out distributing an `afform` and later find it too limiting, then you can change your mind and convert it to static code in full AngularJS. - As long as you name it consistently (e.g. `afform-helloworld`), downstream consumers can use the static version as a drop-in replacement. - -> *(FIXME: But if you do convert to static, could you still permit downstream folks customize the HTML? Let's -> re-assess after we've patched core to allow full participation in the lifecycle of HTML partials.)* - -## Known Issues - -* The code is currently written as a proof-of-concept. There are several `FIXME`/`TODO` declarations in the code - for checking pre-conditions, reporting errors, handling edge-cases, etc. -* Although afforms are can be used in AngularJS, they don't fully support tooling like `cv ang:html:list` - and `hook_civicrm_alterAngular` changesets. We'll need a core patch to allow that. -* We generally need to provide more services for managing/accessing data (e.g. `crm-api3`). -* Need to implement the `Afform.revert` API to undo local customizations. -* Haven't decided if we should support a `client_route` property (i.e. defining a skeletal controller and route for any form). - On the plus side, make it easier to add items to the `civicrm/a` base-page. On the flipside, we don't currently have - a strong use-case, and developers can get the same effect with `civix generate:angular-page` and embedding `
`. +* [Quick Start: Creating the canonical definition of a basic form](docs/quickstart.md) +* [Writing: Forms as basic AngularJS templates](docs/writing.md) (With example: *Contact Record*) +* [Embedding: Forms as reusable building-block](docs/embed.md) (With example: *Contact Record*) +* [Form CRUD: Updating forms via programmatic API](docs/crud.md) +* [Full AngularJS: Integrating between Afform and vanilla AngularJS](docs/angular.md) +* [Roadmap and Known Issues](docs/roadmap.md) diff --git a/ext/afform/docs/angular.md b/ext/afform/docs/angular.md new file mode 100644 index 0000000000..0224049603 --- /dev/null +++ b/ext/afform/docs/angular.md @@ -0,0 +1,17 @@ +## Development: Full AngularJS + +Afform is a subset of AngularJS -- it emphasizes the use of *directives* as a way to *choose and arrange* the parts of +your form. There is more to AngularJS -- such as client-side routing, controllers, services, etc. What to do if you +need these? Here are few tricks: + +* You can create your own applications and pages with full AngularJS. (See also: [CiviCRM Developer Guide: AngularJS: Quick Start](https://docs.civicrm.org/dev/en/latest/framework/angular/quickstart/)). + Then embed the afform (like `helloworld`) in your page with these steps: + * Declare a dependency on module (`afformHelloworld`). This is usually done in `ang/MYMODULE.ang.php` and/or `ang/MYMODULE.js`. + * In your HTML template, use the directive `
`. I + * f you want to provide extra data, services, or actions for the form author -- then pass them along. +* You can write your own directives with full AngularJS (e.g. `civix generate:angular-directive`). These directives become available for use in other afforms. +* If you start out distributing an `afform` and later find it too limiting, then you can change your mind and convert it to static code in full AngularJS. + As long as you name it consistently (e.g. `afform-helloworld`), downstream consumers can use the static version as a drop-in replacement. + +> *(FIXME: But if you do convert to static, could you still permit downstream folks customize the HTML? Let's +> re-assess after we've patched core to allow full participation in the lifecycle of HTML partials.)* diff --git a/ext/afform/docs/crud.md b/ext/afform/docs/crud.md new file mode 100644 index 0000000000..e1f424b3ab --- /dev/null +++ b/ext/afform/docs/crud.md @@ -0,0 +1,47 @@ +## Development: Form CRUD API + +Now that we've defined a baseline form, it's possible for administrators and +GUI applications to inspect the form using the API: + +``` +$ cv api afform.getsingle name=helloworld +{ + "name": "helloworld", + "requires": [ + "afformCore" + ], + "title": "", + "description": "", + "layout": { + "#tag": "div", + "#children": [ + "Hello {{routeParams.name}}" + ] + }, + "id": "helloworld" +} +``` + +Additionally, you can also update the forms: + +``` +$ cv api afform.create name=helloworld title="The Foo Bar Screen" +{ + "is_error": 0, + "version": 3, + "count": 2, + "values": { + "name": "helloworld", + "title": "The Foo Bar Screen" + } +} +``` + +A few important things to note about this: + +* The changes made through the API are only applied on this site. +* Once you make a change with the CRUD API, there will be two copies of the form: + * `[myextension]/afform/helloworld/` is the default, canonical version. + * `[civicrm.files]/afform/helloworld/` is the local, custom version. +* The `layout` field is stored as an Angular-style HTML document (`layout.html`), so you can edit it on disk like + normal Angular code. However, when CRUD'ing the `layout` through the API, it is presented in JSON-style. diff --git a/ext/afform/docs/embed.md b/ext/afform/docs/embed.md new file mode 100644 index 0000000000..c6708ad5eb --- /dev/null +++ b/ext/afform/docs/embed.md @@ -0,0 +1,56 @@ +## Development: Embedding forms + +In the [quick-start example](quickstart.md), we registered a new route (`"server_route": "civicrm/hello-world"`) -- this created a +simple, standalone page with the sole purpose of displaying the `helloworld` form. What if we want to embed the form +somewhere else -- e.g. as a dialog inside an event-listing or membership directory? Afforms are actualy *re-usable +sub-forms*. + +How does this work? Every `afform` is an *AngularJS directive*. For example, `helloworld` can be embedded with: + +```html +
+``` + +Moreover, you can pass options to `helloworld`: + +```html +
+``` + +Now, in `afform/helloworld/layout.html`, you can use `options.phaseOfMoon`: + +```html +Hello, {{routeParams.name}}. The moon is currently {{options.phaseOfMoon}}. +``` + +## Development: Embedding forms: Contact record example + +Is this useful? Let's suppose you're building a contact record page. + +First, we should make a few building-blocks: + +1. `afform/contactName/layout.html` displays a sub-form for editing first name, lastname, prefix, suffix, etc. +2. `afform/contactAddressess/layout.html` displays a sub-form for editing street addresses. +3. `afform/contactEmails/layout.html` displays a sub-form for editing email addresses. + +Next, we should create an overall `afform/contact/layout.html` which uses these building-blocks: + +```html +
+
+
+
+
+
+
+
+
+
+
+``` + +> *(FIXME: In the parent form's `meta.json`, we need to manually add `afformContactName`, `afformContactAddresses`, `afformContactEmails` to the `requires` list. We should autodetect these instead.)* + +What does this buy us? It means that a downstream admin (using APIs/GUIs) can fork `afform/contactName/layout.html` -- +but all the other components can cleanly track the canonical release. This significantly reduces the costs and risks +of manging upgrades and changes. diff --git a/ext/afform/docs/quickstart.md b/ext/afform/docs/quickstart.md new file mode 100644 index 0000000000..4e6b0beefb --- /dev/null +++ b/ext/afform/docs/quickstart.md @@ -0,0 +1,34 @@ +## Development: Quick Start + +As an extension author, you can define a form along with its default, +canonical content. Simply create a folder named `afform/`. In +this example, we create a form named `helloworld`: + +``` +$ cd /path/to/my/own/extension +$ mkdir -p afform/helloworld +$ echo '{"server_route": "civicrm/hello-world"}' > afform/helloworld/meta.json +$ echo '
Hello {{routeParams.name}}
' > afform/helloworld/layout.html +$ cv flush +``` + +A few things to note: + +* We defined a route `civicrm/hello-world`. This is defined in the same routing system used by CiviCRM forms. +* The file `layout.html` is an AngularJS HTML document. It has access to all the general features of Angular HTML (discussed more later). +* After creating a new form or file, we should flush the cache. +* If you're going to actively edit/revise the content of the file, then you should navigate + to **Administer > System Settings > Debugging** and disable asset caching. + +Now that we've created a form, we'll want to determine its URL. As with most +CiviCRM forms, the URL depends on the CMS configuration. Here is an example +from a local Drupal 7 site: + +``` +$ cv url "civicrm/hello-world" +"http://dmaster.localhost/civicrm/hello-world" +$ cv url "civicrm/hello-world/#/?name=world" +"http://dmaster.localhost/civicrm/hello-world/#/?name=world" +``` + +Open the URLs and see what you get. diff --git a/ext/afform/docs/roadmap.md b/ext/afform/docs/roadmap.md new file mode 100644 index 0000000000..e88090df74 --- /dev/null +++ b/ext/afform/docs/roadmap.md @@ -0,0 +1,15 @@ +## Roadmap + +The `afform` extension is a proof-of-concept. + +## Known Issues + +* The code is currently written as a proof-of-concept. There are several `FIXME`/`TODO` declarations in the code + for checking pre-conditions, reporting errors, handling edge-cases, etc. +* Although afforms are can be used in AngularJS, they don't fully support tooling like `cv ang:html:list` + and `hook_civicrm_alterAngular` changesets. We'll need a core patch to allow that. +* We generally need to provide more services for managing/accessing data (e.g. `crm-api3`). +* Need to implement the `Afform.revert` API to undo local customizations. +* Haven't decided if we should support a `client_route` property (i.e. defining a skeletal controller and route for any form). + On the plus side, make it easier to add items to the `civicrm/a` base-page. On the flipside, we don't currently have + a strong use-case, and developers can get the same effect with `civix generate:angular-page` and embedding `
`. diff --git a/ext/afform/docs/writing.md b/ext/afform/docs/writing.md new file mode 100644 index 0000000000..6fb9a3986b --- /dev/null +++ b/ext/afform/docs/writing.md @@ -0,0 +1,59 @@ +## Development: Writing HTML templates + +In AngularJS, the primary language for orchestrating a screen is HTML. You can do interesting things in Angular +HTML, such as displaying variables and applying directives. + +One key concept is *scope* -- the *scope* determines the list of variables which you can access. By default, `afform` +creates a scope with these variables: + +* `routeParams`: This is a reference to the [$routeParams](https://docs.angularjs.org/api/ngRoute/service/$routeParams) + service. In the example, we used `routeParams` to get a reference to a `name` from the URL. +* `meta`: The stored meta data (`meta.json`) for this form. +* `ts`: This is a utility function which translates strings, as in `{{ts('Hello world')}}`. + +Additionally, AngularJS allows *directives* -- these are extra HTML attributes which create behavior. For example: + +* `ng-if` will conditionally create or destroy elements in the page. +* `ng-repeat` will loop through data. +* `ng-style` and `ng-class` will conditionally apply styling. + +A full explanation of these features is out-of-scope for this document, but the key point is that you can use standard +AngularJS markup. + +## Development: Writing HTML templates: Contact record example + +Let's say we want `helloworld` to become a basic "View Contact" page. A user +would request a URL like: + +``` +http://dmaster.localhost/civicrm/hello-world/#/?cid=123 +``` + +How do we use the `cid` to get information about the contact? Update `layout.html` to include data from APIv3: + +```html +
+ +
+

{{contact.display_name}}

+ +

Key Contact Fields

+ +
Contact ID: {{contact.contact_id}}
+
Contact Type: {{contact.contact_type}}
+
Display Name: {{contact.display_name}}
+
First Name: {{contact.first_name}}
+
Last Name: {{contact.last_name}}
+ +

Full Contact record

+ +
{{contact|json}}
+
+
+``` + +This example is useful pedagogically and may be useful in a crunch -- but in the longer term, +we should have a richer library of directives so that typical user-managed forms don't drill-down +at this level of detail. -- 2.25.1