Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CRM-20600 - AngularJS - Allowing hooking and static-caching #10085

Merged
merged 16 commits into from
Jun 17, 2017

Conversation

totten
Copy link
Member

@totten totten commented Mar 31, 2017

In AngularJS, the most visible policy decisions are made by arranging/enhancing a series of HTML fragments (aka "partials"). Partials are traditionally developed by hand then packaged/distributed as a static asset. This PR introduces an extra step where server-side PHP logic can programmatically manipulate the HTML fragments before Angular initializes. It's intended to allow extensions to alter UIs (a step toward OHUPA-2).

Allowing runtime alteration of AngularJS has implications for performance. To alleviate this concern, the PR also changes the caching strategy: instead of building the full JS/JSON/HTML assets on every request, we build the assets and store them as static, web-readable assets (under [civicrm.files]). For typical users, this should remove 500-1000ms from pageload.

Example

function example_civicrm_alterAngular($angular) {
  $angular->add(\Civi\Angular\ChangeSet::create('mychanges')
    ->alterHtml('~/crmMailing/EditMailingCtrl/2step.html', function(phpQueryObject $doc) {
      $doc->find('[ng-form="crmMailingSubform"]')->attr('cat-stevens', 'ts(\'wild world\')');
    })
  );
}

Summary of changes

  1. Add phpQuery: This server-side library allows you to manipulate HTML documents the same way you do in jQuery.
  2. Purify HTML: Organic HTML documents have small discrepancies in notation (e.g. <div/> vs <div></div>; <img foo-bar> vs <img foo-bar="">). PHP's DOMDocument (and therefore phpQuery) don't always preserve these details exactly. To ensure consistent behavior, this PR adds a consistency test (PartialSyntaxTest).
  3. Add Civi\Angular\ChangeSet: This provides a way to represent changes to Angular HTML.
  4. Add hook_civicrm_alterAngular: This exposes Civi\Angular\Manager and Civi\Angular\ChangeSet so that you can modify any HTML file using phpQuery.
  5. Perform translations after the hook: Since the hook is able to change the content of the HTML, the list of strings remains open until the hook finishes.
  6. Store the computed data in a static file (eg /sites/default/files/civicrm/persist/contribute/dyn/angular-modules.12341234.json): There are a large number of partials. Doing hooks and translations creates more overhead, so this PR uses a new subsystem called AssetBuilder. On my local, this saves ~800ms when a typical user opens the Angular base page.
  7. Add setting assetCache: This allows you to opt-in or out of using cache files. By default (auto), caching is enabled production sites and disabled for debug sites.
  8. Related: cv #22 adds a command-line for browsing/inspecting Angular HTML. Ex: if you want to see how the file ~/crmMailing/BlockSummary.html has been altered, run cv ang:html:show --diff crmMailing/BlockSummary.html

Possible followups

These are non-blocker concerns that could merit follow-up:

  • Allow more hooking into client-side JS services, such as crmMailingMgr. Currently, you need to write directives which either take advantage of existing data-flow -- like CiviMail's autosave -- or setup strictly independent dataflow -- like an independent AJAX call. There's no in-between.

Related PRs


@totten totten force-pushed the master-assets branch 2 times, most recently from 77e7c82 to 513023e Compare April 1, 2017 00:19
@eileenmcnaughton
Copy link
Contributor

@totten wanna rebase this?

@totten totten force-pushed the master-assets branch 2 times, most recently from bdca50a to f487792 Compare May 16, 2017 09:59
@totten
Copy link
Member Author

totten commented May 16, 2017

I've rebased, continued the work, and updated the description to reflect current status.

@totten
Copy link
Member Author

totten commented May 16, 2017

I've been doing some evaluation, and HtmlCollection needs to be rewritten. The current interface works at OHUPA-2, but to reach OHUPA-4 we'll have use-cases with inspection, conflict-resolution, etc. The prospects of forward-compatibility seem better if we can itemize the alterations as different things.

For example, a minimal formulation might be to put each alteration in a callback:

// Pseudocode
function example_civicrm_angularPartials(HtmlCollection $html) {
  $html->alter('~/crmMailing/EditMailingCtrl/2step.html', function(phpQueryObject $doc) {
    $doc->find('[ng-form="crmMailingSubform"]')->attr('cat-stevens', 'ts(\'wild world\')');
   });
}

@totten
Copy link
Member Author

totten commented May 16, 2017

The previous commit using HtmlCollection was 2200c27. I've replaced it with a new commit, 0d6d2c6, which uses HtmlFilters.

@totten totten changed the title (WIP) Allow altering Angular HTML via hook (WIP) CRM-20600 Allow altering Angular HTML via hook May 19, 2017
@totten totten changed the title (WIP) CRM-20600 Allow altering Angular HTML via hook (WIP) CRM-20600 - Allow altering Angular HTML via hook May 19, 2017
@totten totten force-pushed the master-assets branch 4 times, most recently from 6ded087 to 3932bdf Compare May 19, 2017 05:05
@totten
Copy link
Member Author

totten commented May 19, 2017

jenkins, test this please

@totten totten force-pushed the master-assets branch 3 times, most recently from b9f7b57 to 5f894cf Compare May 20, 2017 01:16
@totten
Copy link
Member Author

totten commented May 20, 2017

Some updates:

  • Reworked HtmlFilters as ChangeSet.
  • The ChangeSet can be used to modify other things (more than just HTML) -- specifically, the requires list
  • Altering the general data of CRM_Core_Resources::getSettings() is not a bad idea, but it's not sufficient for modifying the Angular requires list. There a few different processes which feed off that list, and some don't rely on CRM_Core_Resources::getSettings(). So instead ChangeSet has explicit support for requires(...).

@totten totten force-pushed the master-assets branch 4 times, most recently from 3bc9f64 to f09c8c8 Compare May 25, 2017 03:46
@totten totten changed the title (WIP) CRM-20600 - Allow altering Angular HTML via hook CRM-20600 - Allow altering Angular HTML via hook Jun 15, 2017
@totten totten changed the title CRM-20600 - Allow altering Angular HTML via hook CRM-20600 - AngularJS - Allowing hooking and static-caching Jun 15, 2017
totten added 16 commits June 16, 2017 19:03
This PR moves the metadata about Angular modules out of Civi\Angular\Manager
and into standalone files (`ang/*.ang.php`).  This uses the same structure
as `civix`.  Also, this makes it easier to navigate between the
JS/HTML/CSS/PHP content.

To test that these changes were safe, I ran the following command before and
after this commit:

$ cv ev 'var_dump(Civi::service("angular")->getModules());'

The results were substantively identical.
In the typical use-case, AssetBuilder can avoid an extra CMS+Civi bootstrap
along with any build steps.
…s instead of *.html files

Rationale: If the raw HTML can be modified via hook, then the HTML files are
not truly representative of the final output.  (e.g.  a hook might add a new
string to some HTML.)

Note: This patch means that we no longer benefit from the cache in
`CRM_Core_Resources_Strings`.  However, the typical user still pulls data
from a cache because we're using `AssetBuilder` to manage
`angular-modules.json`.

Note: `getStrings()` now requires a call to `getPartials()`, which is
expected to become more expensive (firing a hook).  To avoid building the
partials twice, we introduce a private cache.
The default behavior is to include a `bundleUrl` for a bundle of "all
Angular modules".  However, some Angular modules like `crmMailing` are only
displayed if the current user has permission to access them.  This means
that the list of "all Angular modules" can be different (depending on who
you are; albeit in practice there's a small# of permutations).

This patch ensures that two users who access a different list of modules
will see a different `bundleUrl`.
If third-parties are allowed to alter the HTML content, then they may
introduce new dependencies.  This means that they'll need to delcare those
dependencies.

Tangentially, this will also make it easier to construct more optimized
base-pages (e.g.  "make a base-page with module X plus all its
dependencies").
If third-parties are allowed to alter the HTML content, then they may
introduce new dependencies.  This means that they'll need to delcare those
dependencies.

This commit actually moves the declarations for any in-house modules to PHP.
Several modules depended on `crmResource` but didn't declare it.
…injections

This commit extracts the `Civi\Angular\Page\Main::registerResources()` and
creates a new utility-class, `AngularLoader`.  The `AngularLoader` can be
used in new Angular base-pages, e.g.

```php
class Example extends CRM_Core_Page {
  public function run() {
    $loader = new \Civi\Angular\AngularLoader();
    $loader->setPageName('civicrm/foo/bar');
    $loader->setModules(array('crmApp', '...'));
    $loader->load();
    return parent::run();
  }
}
```

The `AngularLoader` only loads the resources or assets (JS/CSS/HTML files).
To start Angular, you'll need to call `ng-app` or `angular.bootstrap(...)`.
One way to do this is to define a page-template:

```html
<!-- Example.tpl -->
<div ng-app="crmApp">
  <div ng-view></div>
</div>
```

Or you can reuse the existing template:

```php
  public function getTemplateFileName() {
    return 'Civi/Angular/Page/Main.tpl';
  }
```

Note: This is framed as a utility-class which loads the Angular resource
files.  It's not a page-class/base-class.  This approach allows us to call
the utility from other situations -- e.g.  inject AngularJS onto an
pre-existing page via hook.  Doing that may or may not be wise, but the
class-hierarchy shouldn't be the issue.
The default value (`auto`) behaves the same as the current behavior: enable
caching in production mode, disable caching in debug mode.

But it also allows you to enable/disable caching independent of the
prod/debug mode.
@totten
Copy link
Member Author

totten commented Jun 17, 2017

Rebased. Resolved conflicts. Added setting assetCache.

This has been tested implicitly for a week or so during civicase dev. Brian has done some testing and says NYSS will do more. Guanhuan also says that Compucorp will be testing Angular interfaces during RC.

@totten
Copy link
Member Author

totten commented Jun 17, 2017

Test failures are common false-negatives.

@totten totten merged commit 8b1e918 into civicrm:master Jun 17, 2017
colemanw added a commit to civicrm/civicrm-dev-docs that referenced this pull request Apr 20, 2018
colemanw added a commit to civicrm/civicrm-dev-docs that referenced this pull request Apr 22, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants