189 lines
5.3 KiB
JavaScript
189 lines
5.3 KiB
JavaScript
/**
|
|
A plugin outlet is an extension point for templates where other templates can
|
|
be inserted by plugins.
|
|
|
|
## Usage
|
|
|
|
If your handlebars template has:
|
|
|
|
```handlebars
|
|
{{plugin-outlet "evil-trout"}}
|
|
```
|
|
|
|
Then any handlebars files you create in the `connectors/evil-trout` directory
|
|
will automatically be appended. For example:
|
|
|
|
plugins/hello/assets/javascripts/discourse/templates/connectors/evil-trout/hello.hbs
|
|
|
|
With the contents:
|
|
|
|
```handlebars
|
|
<b>Hello World</b>
|
|
```
|
|
|
|
Will insert <b>Hello World</b> at that point in the template.
|
|
|
|
Optionally you can also define a view class for the outlet as:
|
|
|
|
plugins/hello/assets/javascripts/discourse/views/connectors/evil-trout/hello.js.es6
|
|
|
|
And it will be wired up automatically.
|
|
|
|
## The block form
|
|
|
|
If you use the block form of the outlet, its contents will be displayed
|
|
if no connectors are found. Example:
|
|
|
|
```handlebars
|
|
{{#plugin-outlet "hello-world"}}
|
|
Nobody says hello :'(
|
|
{{/plugin-outlet}}
|
|
```
|
|
|
|
## Disabling
|
|
|
|
If a plugin returns a disabled status, the outlets will not be wired up for it.
|
|
The list of disabled plugins is returned via the `Site` singleton.
|
|
|
|
**/
|
|
import { registerHelper } from 'discourse-common/lib/helpers';
|
|
|
|
let _connectorCache, _rawCache;
|
|
|
|
function findOutlets(collection, callback) {
|
|
|
|
const disabledPlugins = Discourse.Site.currentProp('disabled_plugins') || [];
|
|
|
|
Object.keys(collection).forEach(function(res) {
|
|
if (res.indexOf("/connectors/") !== -1) {
|
|
// Skip any disabled plugins
|
|
for (let i=0; i<disabledPlugins.length; i++) {
|
|
if (res.indexOf("/" + disabledPlugins[i] + "/") !== -1) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
const segments = res.split("/");
|
|
let outletName = segments[segments.length-2];
|
|
const uniqueName = segments[segments.length-1];
|
|
|
|
callback(outletName, res, uniqueName);
|
|
}
|
|
});
|
|
}
|
|
|
|
function buildConnectorCache() {
|
|
_connectorCache = {};
|
|
_rawCache = {};
|
|
|
|
const uniqueViews = {};
|
|
findOutlets(requirejs._eak_seen, function(outletName, resource, uniqueName) {
|
|
_connectorCache[outletName] = _connectorCache[outletName] || [];
|
|
|
|
const viewClass = require(resource, null, null, true).default;
|
|
uniqueViews[uniqueName] = viewClass;
|
|
_connectorCache[outletName].pushObject(viewClass);
|
|
});
|
|
|
|
findOutlets(Ember.TEMPLATES, function(outletName, resource, uniqueName) {
|
|
const mixin = {templateName: resource.replace('javascripts/', '')};
|
|
let viewClass = uniqueViews[uniqueName];
|
|
|
|
if (viewClass) {
|
|
// We are going to add it back with the proper template
|
|
_connectorCache[outletName] = _connectorCache[outletName] || [];
|
|
_connectorCache[outletName].removeObject(viewClass);
|
|
} else {
|
|
if (!/\.raw$/.test(uniqueName)) {
|
|
viewClass = Ember.View.extend({ classNames: [outletName + '-outlet', uniqueName] });
|
|
}
|
|
}
|
|
|
|
if (viewClass) {
|
|
_connectorCache[outletName] = _connectorCache[outletName] || [];
|
|
_connectorCache[outletName].pushObject(viewClass.extend(mixin));
|
|
} else {
|
|
// we have a raw template
|
|
if (!_rawCache[outletName]) {
|
|
_rawCache[outletName] = [];
|
|
}
|
|
|
|
_rawCache[outletName].push(Ember.TEMPLATES[resource]);
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
var _viewInjections;
|
|
function viewInjections(container) {
|
|
if (_viewInjections) { return _viewInjections; }
|
|
|
|
const injections = container._registry.getTypeInjections('view');
|
|
|
|
_viewInjections = {};
|
|
injections.forEach(function(i) {
|
|
_viewInjections[i.property] = container.lookup(i.fullName);
|
|
});
|
|
|
|
return _viewInjections;
|
|
}
|
|
|
|
// unbound version of outlets, only has a template
|
|
Handlebars.registerHelper('plugin-outlet', function(name){
|
|
if (!_rawCache) { buildConnectorCache(); }
|
|
|
|
const functions = _rawCache[name];
|
|
if (functions) {
|
|
var output = [];
|
|
|
|
for(var i=0; i<functions.length; i++){
|
|
output.push(functions[i]({context: this}));
|
|
}
|
|
|
|
return new Handlebars.SafeString(output.join(""));
|
|
}
|
|
|
|
});
|
|
|
|
registerHelper('plugin-outlet', function(params, hash, options, env) {
|
|
const connectionName = params[0];
|
|
|
|
if (!_connectorCache) { buildConnectorCache(); }
|
|
|
|
if (_connectorCache[connectionName]) {
|
|
const childViews = _connectorCache[connectionName];
|
|
|
|
// If there is more than one view, create a container. Otherwise
|
|
// just shove it in.
|
|
const viewClass = (childViews.length > 1) ? Ember.ContainerView : childViews[0];
|
|
|
|
const newHash = $.extend({}, viewInjections(env.data.view.container));
|
|
if (hash.tagName) { newHash.tagName = hash.tagName; }
|
|
|
|
// we don't need the default template since we have a connector
|
|
delete options.fn;
|
|
delete options.template;
|
|
env.helpers.view.helperFunction.call(this, [viewClass], newHash, options, env);
|
|
|
|
const cvs = env.data.view._childViews;
|
|
if (childViews.length > 1 && cvs && cvs.length) {
|
|
const inserted = cvs[cvs.length-1];
|
|
if (inserted) {
|
|
childViews.forEach(function(cv) {
|
|
inserted.pushObject(cv.create());
|
|
});
|
|
}
|
|
}
|
|
} else if (options.isBlock) {
|
|
const virtualView = Ember.View.extend({
|
|
isVirtual: true,
|
|
tagName: hash.tagName || '',
|
|
template: options.template
|
|
});
|
|
env.helpers.view.helperFunction.call(this, [virtualView], hash, options, env);
|
|
}
|
|
});
|
|
|
|
// No longer used
|
|
export function rewire() { }
|