diff --git a/app/assets/javascripts/admin/templates/admin.hbs b/app/assets/javascripts/admin/templates/admin.hbs
index 77904edc48b..7f34c00b772 100644
--- a/app/assets/javascripts/admin/templates/admin.hbs
+++ b/app/assets/javascripts/admin/templates/admin.hbs
@@ -23,7 +23,7 @@
{{nav-item route='admin.backups' label='admin.backups.title'}}
{{/if}}
{{nav-item route='adminPlugins' label='admin.plugins.title'}}
- {{plugin-outlet "admin-menu" tagName="li"}}
+ {{plugin-outlet name="admin-menu" connectorTagName="li"}}
diff --git a/app/assets/javascripts/admin/templates/dashboard.hbs b/app/assets/javascripts/admin/templates/dashboard.hbs
index e02a5a925ca..ec7753ac6c9 100644
--- a/app/assets/javascripts/admin/templates/dashboard.hbs
+++ b/app/assets/javascripts/admin/templates/dashboard.hbs
@@ -1,4 +1,4 @@
-{{plugin-outlet "admin-dashboard-top"}}
+{{plugin-outlet name="admin-dashboard-top"}}
{{#conditional-loading-spinner condition=loading}}
diff --git a/app/assets/javascripts/admin/templates/group.hbs b/app/assets/javascripts/admin/templates/group.hbs
index f66e0ca8557..26cee25498d 100644
--- a/app/assets/javascripts/admin/templates/group.hbs
+++ b/app/assets/javascripts/admin/templates/group.hbs
@@ -121,7 +121,7 @@
{{#if siteSettings.email_in}}
{{text-field name="incoming_email" value=model.incoming_email placeholderKey="admin.groups.incoming_email_placeholder"}}
- {{plugin-outlet "group-email-in"}}
+ {{plugin-outlet name="group-email-in" args=(hash model=model)}}
{{/if}}
{{/unless}}
diff --git a/app/assets/javascripts/discourse-common/resolver.js.es6 b/app/assets/javascripts/discourse-common/resolver.js.es6
index 7e1bf585e09..6d5d2355b16 100644
--- a/app/assets/javascripts/discourse-common/resolver.js.es6
+++ b/app/assets/javascripts/discourse-common/resolver.js.es6
@@ -137,12 +137,21 @@ export function buildResolver(baseName) {
return this._super(parsedName);
},
+ findConnectorTemplate(parsedName) {
+ const full = parsedName.fullNameWithoutType.replace('components/', '');
+ if (full.indexOf('connectors') === 0) {
+ return Ember.TEMPLATES[`javascripts/${full}`];
+ }
+
+ },
+
resolveTemplate(parsedName) {
return this.findPluginMobileTemplate(parsedName) ||
this.findPluginTemplate(parsedName) ||
this.findMobileTemplate(parsedName) ||
this.findTemplate(parsedName) ||
this.findLoadingTemplate(parsedName) ||
+ this.findConnectorTemplate(parsedName) ||
Ember.TEMPLATES.not_found;
},
diff --git a/app/assets/javascripts/discourse/components/plugin-connector.js.es6 b/app/assets/javascripts/discourse/components/plugin-connector.js.es6
new file mode 100644
index 00000000000..2773e642f7a
--- /dev/null
+++ b/app/assets/javascripts/discourse/components/plugin-connector.js.es6
@@ -0,0 +1,19 @@
+export default Ember.Component.extend({
+
+ init() {
+ this._super();
+
+ const connector = this.get('connector');
+ this.set('layoutName', connector.templateName);
+
+ const args = this.get('args') || {};
+ Object.keys(args).forEach(key => this.set(key, args[key]));
+ },
+
+ send(name, ...args) {
+ const connectorClass = this.get('connector.connectorClass');
+ const action = connectorClass.actions[name];
+ return action ? action.call(this, ...args) : this._super(name, ...args);
+ }
+
+});
diff --git a/app/assets/javascripts/discourse/components/plugin-outlet.js.es6 b/app/assets/javascripts/discourse/components/plugin-outlet.js.es6
new file mode 100644
index 00000000000..5fd2d2a77c6
--- /dev/null
+++ b/app/assets/javascripts/discourse/components/plugin-outlet.js.es6
@@ -0,0 +1,51 @@
+/**
+ 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 name="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
+
Hello World
+ ```
+
+ Will insert
Hello World at that point in the template.
+
+ ## 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 { connectorsFor } from 'discourse/lib/plugin-connectors';
+
+export default Ember.Component.extend({
+ tagName: 'span',
+ connectors: null,
+
+ init() {
+ this._super();
+ const name = this.get('name');
+
+ if (name) {
+ const args = this.get('args');
+ const connectors = connectorsFor(name).filter(con => {
+ return con.connectorClass.shouldRender(args);
+ });
+
+ this.set('connectors', connectors);
+ }
+ }
+});
diff --git a/app/assets/javascripts/discourse/helpers/plugin-outlet.js.es6 b/app/assets/javascripts/discourse/helpers/plugin-outlet.js.es6
deleted file mode 100644
index f8831bc8780..00000000000
--- a/app/assets/javascripts/discourse/helpers/plugin-outlet.js.es6
+++ /dev/null
@@ -1,141 +0,0 @@
-/**
- 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
-
Hello World
- ```
-
- Will insert
Hello World at that point in the template.
-
- ## 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.
-
-**/
-let _connectorCache, _templateCache;
-
-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
{
- const connector = _connectorCache[outletName];
- (connector || []).forEach(s => {
- _templateCache.push(s.template);
- s.templateId = parseInt(_templateCache.length - 1);
- });
- });
-}
-
-// unbound version of outlets, only has a template
-Handlebars.registerHelper('plugin-outlet', function(name) {
- if (!_connectorCache) { buildConnectorCache(); }
-
- const connector = _connectorCache[name];
- if (connector && connector.length) {
- const output = connector.map(c => c.template({context: this}));
- return new Handlebars.SafeString(output.join(""));
- }
-});
-
-export default Ember.Helper.helper(function() {
-});
-
-// const { registerKeyword } = Ember.__loader.require("ember-htmlbars/keywords");
-// const { internal } = Ember.__loader.require('htmlbars-runtime');
-//
-// registerKeyword('plugin-outlet', {
-// setupState(state, env, scope, params) {
-// if (!_connectorCache) { buildConnectorCache(); }
-// return { outletName: env.hooks.getValue(params[0]) };
-// },
-//
-// render(renderNode, env, scope, params, hash, template, inverse, visitor) {
-// let state = renderNode.getState();
-// if (!state.outletName) { return true; }
-// const connector = _connectorCache[state.outletName];
-// if (!connector || connector.length === 0) { return true; }
-//
-// const listTemplate = Ember.TEMPLATES['outlet-list'];
-// listTemplate.raw.locals = ['templateId', 'outletClasses', 'tagName'];
-//
-// internal.hostBlock(renderNode, env, scope, listTemplate.raw, null, null, visitor, function(options) {
-// connector.forEach(source => {
-// const tid = source.templateId;
-// options.templates.template.yieldItem(`d-outlet-${tid}`, [
-// tid,
-// source.classNames,
-// hash.tagName || 'div'
-// ]);
-// });
-// });
-// return true;
-// }
-// });
-//
-// registerKeyword('connector', function(morph, env, scope, params, hash, template, inverse, visitor) {
-// template = _templateCache[parseInt(env.hooks.getValue(hash.templateId))];
-//
-// env.hooks.component(morph,
-// env,
-// scope,
-// 'connector-container',
-// params,
-// hash,
-// { default: template.raw, inverse },
-// visitor);
-// return true;
-// });
diff --git a/app/assets/javascripts/discourse/helpers/raw-plugin-outlet.js.es6 b/app/assets/javascripts/discourse/helpers/raw-plugin-outlet.js.es6
new file mode 100644
index 00000000000..ccea57e1e22
--- /dev/null
+++ b/app/assets/javascripts/discourse/helpers/raw-plugin-outlet.js.es6
@@ -0,0 +1,9 @@
+import { connectorsFor } from 'discourse/lib/plugin-connectors';
+
+Handlebars.registerHelper('raw-plugin-outlet', function(args) {
+ const connectors = connectorsFor(args.hash.name);
+ if (connectors.length) {
+ const output = connectors.map(c => c.template({context: this}));
+ return new Handlebars.SafeString(output.join(""));
+ }
+});
diff --git a/app/assets/javascripts/discourse/lib/plugin-connectors.js.es6 b/app/assets/javascripts/discourse/lib/plugin-connectors.js.es6
new file mode 100644
index 00000000000..a3eb73699a7
--- /dev/null
+++ b/app/assets/javascripts/discourse/lib/plugin-connectors.js.es6
@@ -0,0 +1,67 @@
+let _connectorCache;
+let _extraConnectorClasses = {};
+
+export function resetExtraClasses() {
+ _extraConnectorClasses = {};
+}
+
+// Note: In plugins, define a class by path and it will be wired up automatically
+// eg: discourse/connectors//.js.es6
+export function extraConnectorClass(name, obj) {
+ _extraConnectorClasses[name] = obj;
+}
+
+const DefaultConnectorClass = {
+ actions: {},
+ shouldRender: () => true
+};
+
+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
@@ -20,11 +20,11 @@
{{outlet "user-card"}}
-{{plugin-outlet "above-footer"}}
+{{plugin-outlet name="above-footer"}}
{{#if showFooter}}
{{custom-html "footer"}}
{{/if}}
-{{plugin-outlet "below-footer"}}
+{{plugin-outlet name="below-footer"}}
{{outlet "modal"}}
{{topic-entrance}}
diff --git a/app/assets/javascripts/discourse/templates/components/bread-crumbs.hbs b/app/assets/javascripts/discourse/templates/components/bread-crumbs.hbs
index f737c969760..bbf8ef4caba 100644
--- a/app/assets/javascripts/discourse/templates/components/bread-crumbs.hbs
+++ b/app/assets/javascripts/discourse/templates/components/bread-crumbs.hbs
@@ -8,6 +8,6 @@
{{tag-drop firstCategory=firstCategory secondCategory=secondCategory tagId=tagId}}
{{/if}}
-{{plugin-outlet "bread-crumbs-right" tagName="li"}}
+{{plugin-outlet name="bread-crumbs-right" connectorTagName="li"}}
diff --git a/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs b/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs
index 790e61e9390..2900ab3af8f 100644
--- a/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs
+++ b/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs
@@ -56,7 +56,7 @@
- {{plugin-outlet "category-email-in"}}
+ {{plugin-outlet name="category-email-in" args=(hash category=category)}}
{{/if}}
{{#if showPositionInput}}
@@ -82,4 +82,4 @@
{{/unless}}
-{{plugin-outlet "category-custom-settings"}}
+{{plugin-outlet name="category-custom-settings" args=(hash category=category)}}
diff --git a/app/assets/javascripts/discourse/templates/components/navigation-bar.hbs b/app/assets/javascripts/discourse/templates/components/navigation-bar.hbs
index c7d381e5679..a93e42619ee 100644
--- a/app/assets/javascripts/discourse/templates/components/navigation-bar.hbs
+++ b/app/assets/javascripts/discourse/templates/components/navigation-bar.hbs
@@ -2,4 +2,4 @@
{{navigation-item content=navItem filterMode=filterMode}}
{{/each}}
{{custom-html "extraNavItem"}}
-{{plugin-outlet "extra-nav-item" tagName="li"}}
+{{plugin-outlet name="extra-nav-item" connectorTagName="li"}}
diff --git a/app/assets/javascripts/discourse/templates/components/plugin-outlet.hbs b/app/assets/javascripts/discourse/templates/components/plugin-outlet.hbs
new file mode 100644
index 00000000000..73126a2572f
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/components/plugin-outlet.hbs
@@ -0,0 +1,3 @@
+{{#each connectors as |c|}}
+ {{plugin-connector connector=c args=args class=c.classNames tagName=connectorTagName}}
+{{/each}}
diff --git a/app/assets/javascripts/discourse/templates/components/stream-item.hbs b/app/assets/javascripts/discourse/templates/components/stream-item.hbs
index 0018f8a2d84..fed530753ef 100644
--- a/app/assets/javascripts/discourse/templates/components/stream-item.hbs
+++ b/app/assets/javascripts/discourse/templates/components/stream-item.hbs
@@ -6,7 +6,7 @@
{{{item.title}}}
{{category-link item.category}}
- {{plugin-outlet "user-stream-item-header"}}
+ {{plugin-outlet name="user-stream-item-header" args=(hash item=item)}}
{{#if actionDescription}}
diff --git a/app/assets/javascripts/discourse/templates/components/topic-category.hbs b/app/assets/javascripts/discourse/templates/components/topic-category.hbs
index cf717a09922..edf11686ba9 100644
--- a/app/assets/javascripts/discourse/templates/components/topic-category.hbs
+++ b/app/assets/javascripts/discourse/templates/components/topic-category.hbs
@@ -14,4 +14,4 @@
{{topic-featured-link topic}}
{{/if}}
-{{plugin-outlet "topic-category"}}
+{{plugin-outlet name="topic-category" args=(hash topic=topic category=topic.category)}}
diff --git a/app/assets/javascripts/discourse/templates/components/topic-footer-buttons.hbs b/app/assets/javascripts/discourse/templates/components/topic-footer-buttons.hbs
index 9585bb02364..bca64f23167 100644
--- a/app/assets/javascripts/discourse/templates/components/topic-footer-buttons.hbs
+++ b/app/assets/javascripts/discourse/templates/components/topic-footer-buttons.hbs
@@ -66,8 +66,12 @@
title="topic.reply.help"}}
{{/if}}
-{{plugin-outlet "after-topic-footer-main-buttons" tagName="span"}}
+{{plugin-outlet name="after-topic-footer-main-buttons"
+ args=(hash topic=topic)
+ connectorTagName="span"}}
{{pinned-button topic=topic}}
{{topic-notifications-button topic=topic}}
-{{plugin-outlet "after-topic-footer-buttons" tagName="span"}}
+{{plugin-outlet name="after-topic-footer-buttons"
+ args=(hash topic=topic)
+ connectorTagName="span"}}
diff --git a/app/assets/javascripts/discourse/templates/components/user-card-contents.hbs b/app/assets/javascripts/discourse/templates/components/user-card-contents.hbs
index c88b7cc9d60..3bb893b38b8 100644
--- a/app/assets/javascripts/discourse/templates/components/user-card-contents.hbs
+++ b/app/assets/javascripts/discourse/templates/components/user-card-contents.hbs
@@ -30,7 +30,7 @@
- {{plugin-outlet "composer-open"}}
+ {{plugin-outlet name="composer-open" args=(hash model=model)}}
{{{model.actionTitle}}}
@@ -81,7 +81,7 @@
{{/if}}
{{/if}}
- {{plugin-outlet "composer-fields"}}
+ {{plugin-outlet name="composer-fields" args=(hash model=model)}}
{{composer-editor topic=topic
@@ -103,7 +103,7 @@
{{#if currentUser}}
- {{plugin-outlet "composer-fields-below"}}
+ {{plugin-outlet name="composer-fields-below" args=(hash model=model)}}
{{#if canEditTags}}
{{tag-chooser tags=model.tags tabIndex="4" categoryId=model.categoryId}}
{{/if}}
diff --git a/app/assets/javascripts/discourse/templates/discovery.hbs b/app/assets/javascripts/discourse/templates/discovery.hbs
index 069be994bad..0796cbecdfe 100644
--- a/app/assets/javascripts/discourse/templates/discovery.hbs
+++ b/app/assets/javascripts/discourse/templates/discovery.hbs
@@ -21,11 +21,11 @@
- {{plugin-outlet "discovery-list-container-top"}}
+ {{plugin-outlet name="discovery-list-container-top"}}
{{outlet "list-container"}}
-{{plugin-outlet "discovery-below"}}
+{{plugin-outlet name="discovery-below"}}
diff --git a/app/assets/javascripts/discourse/templates/full-page-search.hbs b/app/assets/javascripts/discourse/templates/full-page-search.hbs
index edaf9172db9..bfeab09cfe2 100644
--- a/app/assets/javascripts/discourse/templates/full-page-search.hbs
+++ b/app/assets/javascripts/discourse/templates/full-page-search.hbs
@@ -94,7 +94,7 @@
{{#each result.topic.tags as |tag|}}
{{discourse-tag tag}}
{{/each}}
- {{plugin-outlet "full-page-search-category"}}
+ {{plugin-outlet name="full-page-search-category" args=(hash result=result)}}
diff --git a/app/assets/javascripts/discourse/templates/list/topic-list-item.raw.hbs b/app/assets/javascripts/discourse/templates/list/topic-list-item.raw.hbs
index da825c4774b..bd444a8e55c 100644
--- a/app/assets/javascripts/discourse/templates/list/topic-list-item.raw.hbs
+++ b/app/assets/javascripts/discourse/templates/list/topic-list-item.raw.hbs
@@ -10,7 +10,7 @@
{{#if topic.featured_link}}
{{topic-featured-link topic}}
{{/if}}
- {{plugin-outlet "topic-list-after-title"}}
+ {{raw-plugin-outlet name="topic-list-after-title"}}
{{#if showTopicPostBadges}}
{{raw "topic-post-badges" unread=topic.unread newPosts=topic.displayNewPosts unseen=topic.unseen url=topic.lastUnreadUrl}}
{{/if}}
@@ -21,7 +21,7 @@
{{/each}}
{{/if}}
- {{plugin-outlet "topic-list-tags"}}
+ {{raw-plugin-outlet name="topic-list-tags"}}
{{#if expandPinned}}
{{raw "list/topic-excerpt" topic=topic}}
{{/if}}
diff --git a/app/assets/javascripts/discourse/templates/mobile/list/topic-list-item.raw.hbs b/app/assets/javascripts/discourse/templates/mobile/list/topic-list-item.raw.hbs
index e630889d492..204b02323d7 100644
--- a/app/assets/javascripts/discourse/templates/mobile/list/topic-list-item.raw.hbs
+++ b/app/assets/javascripts/discourse/templates/mobile/list/topic-list-item.raw.hbs
@@ -37,7 +37,7 @@
{{/if}}
- {{plugin-outlet "topic-list-tags"}}
+ {{raw-plugin-outlet name="topic-list-tags"}}