diff --git a/app/assets/javascripts/discourse/helpers/plugin-outlet.js.es6 b/app/assets/javascripts/discourse/helpers/plugin-outlet.js.es6
index 3df8b18c56b..f7a85a460e8 100644
--- a/app/assets/javascripts/discourse/helpers/plugin-outlet.js.es6
+++ b/app/assets/javascripts/discourse/helpers/plugin-outlet.js.es6
@@ -110,7 +110,7 @@ function buildConnectorCache() {
_connectorCache[outletName].removeObject(viewClass);
} else {
if (!/\.raw$/.test(uniqueName)) {
- viewClass = Em.View.extend({ classNames: [outletName + '-outlet', uniqueName] });
+ viewClass = Ember.View.extend({ classNames: [outletName + '-outlet', uniqueName] });
}
}
@@ -172,8 +172,11 @@ Ember.HTMLBars._registerHelper('plugin-outlet', function(params, hash, options,
// just shove it in.
const viewClass = (childViews.length > 1) ? Ember.ContainerView : childViews[0];
+ const newHash = viewInjections(env.data.view.container);
+ if (hash.tagName) { newHash.tagName = hash.tagName; }
+
delete options.fn; // we don't need the default template since we have a connector
- env.helpers.view.helperFunction.call(this, [viewClass], viewInjections(env.data.view.container), options, env);
+ env.helpers.view.helperFunction.call(this, [viewClass], newHash, options, env);
const cvs = env.data.view._childViews;
if (childViews.length > 1 && cvs && cvs.length) {
diff --git a/app/assets/javascripts/discourse/lib/plugin-api.js.es6 b/app/assets/javascripts/discourse/lib/plugin-api.js.es6
index 947fae6f73a..b8eeb0fe7fc 100644
--- a/app/assets/javascripts/discourse/lib/plugin-api.js.es6
+++ b/app/assets/javascripts/discourse/lib/plugin-api.js.es6
@@ -5,7 +5,7 @@ import { addButton } from 'discourse/widgets/post-menu';
import { includeAttributes } from 'discourse/lib/transform-post';
import { addToolbarCallback } from 'discourse/components/d-editor';
import { addWidgetCleanCallback } from 'discourse/components/mount-widget';
-import { decorateWidget, changeSetting } from 'discourse/widgets/widget';
+import { createWidget, decorateWidget, changeSetting } from 'discourse/widgets/widget';
import { onPageChange } from 'discourse/lib/page-tracker';
import { preventCloak } from 'discourse/widgets/post-stream';
@@ -89,6 +89,8 @@ class PluginApi {
const src = Discourse.Emoji.urlFor(emoji);
return dec.h('img', { className: 'emoji', attributes: { src } });
});
+
+ iconBody = result.emoji.split('|').map(name => dec.attach('emoji', { name }));
}
if (result.text) {
@@ -268,11 +270,20 @@ class PluginApi {
preventCloak(postId) {
preventCloak(postId);
}
+
+ /**
+ * Exposes the widget creating ability to plugins. Plugins can
+ * register their own plugins and attach them with decorators.
+ * See `createWidget` in `discourse/widgets/widget` for more info.
+ **/
+ createWidget(name, args) {
+ return createWidget(name, args);
+ }
}
let _pluginv01;
function getPluginApi(version) {
- if (version === "0.1") {
+ if (version === "0.1" || version === "0.2") {
if (!_pluginv01) {
_pluginv01 = new PluginApi(version, Discourse.__container__);
}
diff --git a/app/assets/javascripts/discourse/templates/user/user.hbs b/app/assets/javascripts/discourse/templates/user/user.hbs
index 39718247396..9c3b28877fb 100644
--- a/app/assets/javascripts/discourse/templates/user/user.hbs
+++ b/app/assets/javascripts/discourse/templates/user/user.hbs
@@ -46,11 +46,14 @@
{{#if currentUser.staff}}
{{fa-icon "wrench"}}{{i18n 'admin.user.show_admin_profile'}}
{{/if}}
+ {{plugin-outlet "user-profile-controls" tagName="li"}}
+
{{#if collapsedInfo}}
{{#if viewingSelf}}
{{fa-icon "angle-double-down"}}{{i18n 'user.expand_profile'}}
{{/if}}
{{/if}}
+
diff --git a/app/assets/javascripts/discourse/widgets/decorator-helper.js.es6 b/app/assets/javascripts/discourse/widgets/decorator-helper.js.es6
index 6ea4eb7d2bc..13db7ce8364 100644
--- a/app/assets/javascripts/discourse/widgets/decorator-helper.js.es6
+++ b/app/assets/javascripts/discourse/widgets/decorator-helper.js.es6
@@ -22,6 +22,20 @@ class DecoratorHelper {
**/
// h() is attached via `prototype` below
+ /**
+ * Attach another widget inside this one.
+ *
+ * ```
+ * return helper.attach('widget-name');
+ * ```
+ */
+ attach(name, attrs, state) {
+ attrs = attrs || this.widget.attrs;
+ state = state || this.widget.state;
+
+ return this.widget.attach(name, attrs, state);
+ }
+
/**
* Returns the model associated with this widget. When decorating
* posts this will normally be the post.
diff --git a/app/assets/javascripts/discourse/widgets/emoji.js.es6 b/app/assets/javascripts/discourse/widgets/emoji.js.es6
new file mode 100644
index 00000000000..6ab36c4692a
--- /dev/null
+++ b/app/assets/javascripts/discourse/widgets/emoji.js.es6
@@ -0,0 +1,9 @@
+import { createWidget } from 'discourse/widgets/widget';
+
+export default createWidget('emoji', {
+ tagName: 'img.emoji',
+
+ buildAttributes(attrs) {
+ return { src: Discourse.Emoji.urlFor(attrs.name) };
+ },
+});
diff --git a/app/models/user.rb b/app/models/user.rb
index fb865b213c4..e0663db08e1 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -148,6 +148,33 @@ class User < ActiveRecord::Base
User.where(username_lower: lower).blank? && !SiteSetting.reserved_usernames.split("|").include?(username)
end
+ def self.plugin_staff_user_custom_fields
+ @plugin_staff_user_custom_fields ||= {}
+ end
+
+ def self.register_plugin_staff_custom_field(custom_field_name, plugin)
+ plugin_staff_user_custom_fields[custom_field_name] = plugin
+ end
+
+ def self.whitelisted_user_custom_fields(guardian)
+ fields = []
+
+ if SiteSetting.public_user_custom_fields.present?
+ fields += SiteSetting.public_user_custom_fields.split('|')
+ end
+
+ if guardian.is_staff?
+ if SiteSetting.staff_user_custom_fields.present?
+ fields += SiteSetting.staff_user_custom_fields.split('|')
+ end
+ plugin_staff_user_custom_fields.each do |k, v|
+ fields << k if v.enabled?
+ end
+ end
+
+ fields.uniq
+ end
+
def effective_locale
if SiteSetting.allow_user_locale && self.locale.present?
self.locale
diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb
index 730a23da741..b1acb42fe8a 100644
--- a/app/serializers/user_serializer.rb
+++ b/app/serializers/user_serializer.rb
@@ -318,15 +318,10 @@ class UserSerializer < BasicUserSerializer
end
def custom_fields
- fields = nil
+ fields = User.whitelisted_user_custom_fields(scope)
if scope.can_edit?(object)
- fields = DiscoursePluginRegistry.serialized_current_user_fields.to_a
- end
-
- if SiteSetting.public_user_custom_fields.present?
- fields ||= []
- fields += SiteSetting.public_user_custom_fields.split('|')
+ fields += DiscoursePluginRegistry.serialized_current_user_fields.to_a
end
if fields.present?
diff --git a/lib/plugin/instance.rb b/lib/plugin/instance.rb
index f13fd226a48..7fb3735de1d 100644
--- a/lib/plugin/instance.rb
+++ b/lib/plugin/instance.rb
@@ -67,6 +67,10 @@ class Plugin::Instance
klass.send(:define_method, "include_#{attr}?") { plugin.enabled? }
end
+ def whitelist_staff_user_custom_field(field)
+ User.register_plugin_staff_custom_field(field, self)
+ end
+
# Extend a class but check that the plugin is enabled
# for class methods use `add_class_method`
def add_to_class(klass, attr, &block)
diff --git a/lib/topic_view.rb b/lib/topic_view.rb
index 97b903ec502..9f84dd8063b 100644
--- a/lib/topic_view.rb
+++ b/lib/topic_view.rb
@@ -54,13 +54,11 @@ class TopicView
filter_posts(options)
- if SiteSetting.public_user_custom_fields.present? && @posts
- @user_custom_fields = User.custom_fields_for_ids(@posts.map(&:user_id), SiteSetting.public_user_custom_fields.split('|'))
- end
-
- if @guardian.is_staff? && SiteSetting.staff_user_custom_fields.present? && @posts
- @user_custom_fields ||= {}
- @user_custom_fields.deep_merge!(User.custom_fields_for_ids(@posts.map(&:user_id), SiteSetting.staff_user_custom_fields.split('|')))
+ if @posts
+ added_fields = User.whitelisted_user_custom_fields(@guardian)
+ if added_fields.present?
+ @user_custom_fields = User.custom_fields_for_ids(@posts.map(&:user_id), added_fields)
+ end
end
whitelisted_fields = TopicView.whitelisted_post_custom_fields(@user)
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 35a9e72725f..84a992ae942 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -38,7 +38,6 @@ describe User do
Jobs.expects(:enqueue).with(:send_system_message, user_id: user.id, message_type: 'welcome_user').never
user.enqueue_welcome_message('welcome_user')
end
-
end
describe '.approve' do