Skip to content

Commit

Permalink
feature(assets): Get rid of js/ and css/ directories
Browse files Browse the repository at this point in the history
All views under these directories are implicitly "hoisted" up one level.
They are also given the appropriate extension:

 * js/view => view.js
 * css/view => view.css

The main benefit this brings is being able to co-locate related assets.
So a template (view.php) can have its CSS/JS dependencies right next to it
(view.css, view.js). Compare to the current approach which is like so:

 * Template: page/components/module.php
 * CSS: css/elements/modules.php
 * JS: None in this case, but how would you know with any confidence?

BREAKING CHANGE:
This re-configures view location storage such that full paths are stored,
not just paths to view directories. In particular, `elgg_get_view_location` and
`elgg_set_view_location` return and expect full paths respectively.

Great care has been taken to make this change as backwards-compatible as possible,
so you should not need to update any view references right away. However, you are
certainly encouraged to move your JS and CSS views to their new, canonical
locations.

Refs Elgg#8381
Fixes Elgg#8382
  • Loading branch information
ewinslow committed Jun 20, 2015
1 parent 8e58261 commit c192233
Show file tree
Hide file tree
Showing 14 changed files with 237 additions and 74 deletions.
2 changes: 1 addition & 1 deletion docs/guides/ajax.rst
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ The Ajax view system works significantly differently than the action system.
``false`` if it can't be loaded.
* There's no "wrapper" object placed around the view output.
* System messages/errors shouldn't be used, as they don't display until the user loads another page.
* If the view name begins with ``js/`` or ``css/``, a corresponding Content-Type header is added.
* Depending on the view's suffix (.js, .html, .css, etc.), a corresponding Content-Type header is added.

.. warning::

Expand Down
28 changes: 13 additions & 15 deletions docs/guides/javascript.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Here we define a basic module that alters the page, by passing a "definition fun

.. code-block:: javascript
// in views/default/js/myplugin/say_hello.js
// in views/default/myplugin/say_hello.js
define(function(require) {
var elgg = require("elgg");
Expand All @@ -64,8 +64,8 @@ Here we define a basic module that alters the page, by passing a "definition fun
$('body').append(elgg.echo('hello_world'));
});
The module's name is determined by the view name, which here is ``js/myplugin/say_hello.js``.
We strip the leading ``js/`` and the ``.js`` extension, leaving ``myplugin/say_hello``.
The module's name is determined by the view name, which here is ``myplugin/say_hello.js``.
We strip the ``.js`` extension, leaving ``myplugin/say_hello``.

.. warning::

Expand All @@ -79,7 +79,7 @@ the greeting:

.. code-block:: javascript
// in views/default/js/myplugin/hello.js
// in views/default/myplugin/hello.js
define(function(require) {
var elgg = require("elgg");
Expand All @@ -89,7 +89,7 @@ the greeting:
.. code-block:: javascript
// in views/default/js/myplugin/say_hello.js
// in views/default/myplugin/say_hello.js
define(function(require) {
var $ = require("jquery");
Expand All @@ -102,7 +102,7 @@ Passing plugin/Elgg settings to modules
---------------------------------------

You can use a PHP-based module to pass values from the server. To make the module ``myplugin/settings``,
create the view file ``views/default/js/myplugin/settings.js.php`` (note the double extension
create the view file ``views/default/myplugin/settings.js.php`` (note the double extension
``.js.php``).

.. code-block:: php
Expand All @@ -124,7 +124,7 @@ You must also manually register the view as an external resource:
<?php
// note the view name does not include ".php"
elgg_register_simplecache_view('js/myplugin/settings.js');
elgg_register_simplecache_view('myplugin/settings.js');
.. note::

Expand All @@ -144,7 +144,7 @@ The best way to accomplish this is by configuring the path to the file using the
<?php // views.php
return [
'js/underscore.js' => 'vendor/bower-asset/underscore/underscore.min.js',
'underscore.js' => 'vendor/bower-asset/underscore/underscore.min.js',
];
If you've copied the script directly into your plugin instead of managing it with Composer,
Expand All @@ -154,7 +154,7 @@ you can use something like this instead:
<?php // views.php
return [
'js/underscore.js' => __DIR__ . '/bower_components/underscore/underscore.min.js',
'underscore.js' => __DIR__ . '/bower_components/underscore/underscore.min.js',
];
That's it! Elgg will now load this file whenever the "underscore" module is requested.
Expand All @@ -165,21 +165,20 @@ Using traditional JS libraries as modules

It's possible to support JavaScript libraries that do not declare themselves as AMD
modules (i.e. they declare global variables instead) if you shim them by
setting ``exports`` and optionally ``deps`` in ``elgg_define_js``:
setting ``exports`` and ``deps`` in ``elgg_define_js``:

.. code-block:: php
// set the path, define its dependencies, and what value it returns
elgg_define_js('jquery.form', [
'src' => elgg_get_simplecache_url('js/jquery.form.js'),
'deps' => array('jquery'),
'deps' => ['jquery'],
'exports' => 'jQuery.fn.ajaxForm',
]);
When this is requested client-side:

#. The jQuery module is loaded, as it's marked as a dependency.
#. ``https://elgg.example.org/cache/125235034/views/default/js/jquery.form.js`` is loaded and executed.
#. ``https://elgg.example.org/cache/125235034/views/default/jquery.form.js`` is loaded and executed.
#. The value of ``window.jQuery.fn.ajaxForm`` is returned by the module.

.. warning:: Calls to ``elgg_define_js()`` must be in an ``init, system`` event handler.
Expand All @@ -190,8 +189,7 @@ Some things to note
#. Do not use ``elgg.provide()`` anymore nor other means to attach code to ``elgg`` or other
global objects. Use modules.
#. Return the value of the module instead of adding to a global variable.
#. JS and CSS views (names starting with ``js/`` or ``css/``) as well as static (.js/.css) files
are automatically minified and cached by Elgg's simplecache system.
#. Static (.js,.css,etc.) files are automatically minified and cached by Elgg's simplecache system.


Migrating JS from Elgg 1.8 to AMD / 1.9
Expand Down
13 changes: 8 additions & 5 deletions docs/guides/plugins/plugin-skeleton.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,14 @@ The following files for plugin ``example`` would go in ``/mod/example/``
example_3rd_party_lib/
views/
default/
example.png
css/
example.css
example/
component.css
component.js
component.png
forms/
example/
action.php
other_action.php
js/
example.js
object/
example.php
example/
Expand All @@ -45,7 +44,11 @@ The following files for plugin ``example`` would go in ``/mod/example/``
usersettings.php
resources/
example/
all.css
all.js
all.php
owner.css
owner.js
owner.php
widgets/
example_widget/
Expand Down
38 changes: 38 additions & 0 deletions docs/guides/upgrading.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,44 @@ See the administrator guides for :doc:`how to upgrade a live site </admin/upgrad
From 1.x to 2.0
===============

JS and CSS views have been moved out of the js/ and css/ directories
--------------------------------------------------------------------

They also have been given .js and .css extensions respectively if they didn't
already have them. For example:

================= =============
Old view New view
================= =============
``js/view`` ``view.js``
``css/view`` ``view.css``
``js/other.js`` ``other.js``
``css/other.css`` ``other.css``
``js/img.png`` ``img.png``
================= =============

The main benefit this brings is being able to co-locate related assets.
So a template (``view.php``) can have its CSS/JS dependencies right next to it
(``view.css``, ``view.js``).

Compare to the current approach which is like so for the case of ``.elgg-module``:

* Template: ``page/components/module.php``
* CSS: ``css/elements/modules.php``
* JS: None in this case, but how would you know with any confidence?



In order to make this change, we had to re-configure view location storage such
that full paths are stored, not just paths to view directories.
So `elgg_get_view_location` and `elgg_set_view_location` return and expect full
paths respectively now.

Great care has been taken to make this change as backwards-compatible as possible,
so you should not need to update any view references right away. However, you are
certainly encouraged to move your JS and CSS views to their new, canonical
locations.

``fxp/composer-asset-plugin`` is now required to install Elgg from source
-------------------------------------------------------------------------

Expand Down
8 changes: 4 additions & 4 deletions docs/guides/views/simplecache.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ You can register a view with the Simplecache with the following function at init
**Accessing the cached view**

If you registered a JavaScript or CSS file with Simplecache and put in in the view folder
``js/your_view.js`` or ``css/your_view.css`` you can very easily get the url to this cached view by calling
If you registered a JavaScript or CSS file with Simplecache and put in the view folder as
``your_view.js`` or ``your_view.css`` you can very easily get the url to this cached view by calling
``elgg_get_simplecache_url($view)``. For example:

.. code:: php
$js = elgg_get_simplecache_url('js/your_view.js');
$css = elgg_get_simplecache_url('css/your_view.css');
$js = elgg_get_simplecache_url('your_view.js');
$css = elgg_get_simplecache_url('your_view.css');
2 changes: 1 addition & 1 deletion engine/classes/Elgg/Amd/ViewFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class ViewFilter {
/**
* Given the view name, returns the AMD name.
*
* @param string $name The name of the view (e.g., 'js/elgg/module.js')
* @param string $name The name of the view (e.g., 'elgg/module.js')
*
* @return string The AMD name (e.g., 'elgg/module'), or blank for no AMD name.
*/
Expand Down
27 changes: 16 additions & 11 deletions engine/classes/Elgg/Cache/SimpleCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,21 @@
*/
class SimpleCache {

/**
* Global Elgg configuration
*
* @var \stdClass
*/
/** @var \stdClass */
private $CONFIG;

/** @var \Elgg\ViewsService */
private $views;

/**
* Constructor
*
* @param \stdClass $CONFIG Elgg's global configuration
* @param \Elgg\ViewsService $views Elgg's views registry
*/
public function __construct() {
global $CONFIG;
public function __construct(\stdClass $CONFIG, \Elgg\ViewsService $views) {
$this->CONFIG = $CONFIG;
$this->views = $views;
}

/**
Expand All @@ -45,6 +47,7 @@ public function __construct() {
* @see elgg_get_simplecache_url()
*/
function registerView($view_name) {
$view_name = $this->views->canonicalizeViewName($view_name);
elgg_register_external_view($view_name, true);
}

Expand All @@ -54,7 +57,7 @@ function registerView($view_name) {
* Recommended usage is to just pass the entire view name as the first and only arg:
*
* ```
* $blog_js = $simpleCache->getUrl('js/blog/save_draft.js');
* $blog_js = $simpleCache->getUrl('blog/save_draft.js');
* $favicon = $simpleCache->getUrl('favicon.ico');
* ```
*
Expand All @@ -76,18 +79,20 @@ function registerView($view_name) {
* @return string
*/
function getUrl($view, $subview = '') {
// handle `getUrl('js', 'js/blog/save_draft.js')`
// handle `getUrl('js', 'js/blog/save_draft')`
if (($view === 'js' || $view === 'css') && 0 === strpos($subview, $view . '/')) {
$view = $subview;
$subview = '';
}

// handle `getUrl('js', 'blog/save_draft.js')`
// handle `getUrl('js', 'blog/save_draft')`
if (!empty($subview)) {
$view = "$view/$subview";
}

// should be normalized to canonical form by now: `getUrl('js/blog/save_draft.js')`
$view = $this->views->canonicalizeViewName($view);

// should be normalized to canonical form by now: `getUrl('blog/save_draft.js')`
$this->registerView($view);
return $this->getRoot() . $view;
}
Expand Down
7 changes: 5 additions & 2 deletions engine/classes/Elgg/Di/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public function __construct(\Elgg\Config $config) {

$this->setFactory('amdConfig', function(ServiceProvider $c) {
$obj = new \Elgg\Amd\Config($c->hooks);
$obj->setBaseUrl($c->simpleCache->getRoot() . "js/");
$obj->setBaseUrl($c->simpleCache->getRoot());
return $obj;
});

Expand Down Expand Up @@ -244,7 +244,10 @@ public function __construct(\Elgg\Config $config) {
return new \ElggSession($session);
});

$this->setClassName('simpleCache', \Elgg\Cache\SimpleCache::class);
$this->setFactory('simpleCache', function(ServiceProvider $c) {
global $CONFIG;
return new \Elgg\Cache\SimpleCache($CONFIG, $c->views);
});

$this->setClassName('siteSecret', \Elgg\Database\SiteSecret::class);

Expand Down
8 changes: 8 additions & 0 deletions engine/classes/Elgg/HooksRegistrationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ public function registerHandler($name, $type, $callback, $priority = 500) {
if (empty($name) || empty($type) || !is_callable($callback, true)) {
return false;
}

if (($name == 'view' || $name == 'view_vars') && $type != 'all') {
$type = _elgg_services()->views->canonicalizeViewName($type);
}

$this->registrations[$name][$type][] = [
self::REG_KEY_PRIORITY => $priority,
Expand All @@ -78,6 +82,10 @@ public function registerHandler($name, $type, $callback, $priority = 500) {
* @access private
*/
public function unregisterHandler($name, $type, $callback) {
if (($name == 'view' || $name == 'view_vars') && $type != 'all') {
$type = _elgg_services()->views->canonicalizeViewName($type);
}

if (empty($this->registrations[$name][$type])) {
return false;
}
Expand Down
Loading

0 comments on commit c192233

Please sign in to comment.