From f9722f8598985278961dc0d0619124459a02ff9b Mon Sep 17 00:00:00 2001
From: Robin Ward <robin.ward@gmail.com>
Date: Fri, 12 Feb 2016 16:57:24 -0500
Subject: [PATCH] Add custom post icons to PluginAPI

---
 app/assets/javascripts/discourse.js           |  4 +-
 .../discourse/components/poster-name.js.es6   | 79 +------------------
 .../discourse/lib/plugin-api.js.es6           | 45 ++++++++++-
 .../discourse/lib/transform-post.js.es6       |  1 +
 .../discourse/templates/badges/show.hbs       |  2 +-
 .../discourse/widgets/poster-name.js.es6      | 24 ++++++
 6 files changed, 74 insertions(+), 81 deletions(-)

diff --git a/app/assets/javascripts/discourse.js b/app/assets/javascripts/discourse.js
index 553d70fb4..ab0a42983 100644
--- a/app/assets/javascripts/discourse.js
+++ b/app/assets/javascripts/discourse.js
@@ -161,7 +161,9 @@ function methodMissing() {
                "and your plugin needs to be updated.");
 };
 
-['reopen', 'registerButton'].forEach(function(m) { RemovedObject.prototype[m] = methodMissing; });
+Discourse.RemovedObject = RemovedObject;
+
+['reopen', 'registerButton', 'on', 'off'].forEach(function(m) { RemovedObject.prototype[m] = methodMissing; });
 
 ['discourse/views/post', 'discourse/components/post-menu'].forEach(function(moduleName) {
   define(moduleName, [], function() { return new RemovedObject(moduleName); });
diff --git a/app/assets/javascripts/discourse/components/poster-name.js.es6 b/app/assets/javascripts/discourse/components/poster-name.js.es6
index 7f213f7c0..15ca54040 100644
--- a/app/assets/javascripts/discourse/components/poster-name.js.es6
+++ b/app/assets/javascripts/discourse/components/poster-name.js.es6
@@ -1,77 +1,2 @@
-import { setting } from 'discourse/lib/computed';
-
-const PosterNameComponent = Em.Component.extend({
-  classNames: ['names', 'trigger-user-card'],
-  displayNameOnPosts: setting('display_name_on_posts'),
-
-  // sanitize name for comparison
-  sanitizeName(name){
-    return name.toLowerCase().replace(/[\s_-]/g,'');
-  },
-
-  render(buffer) {
-    const post = this.get('post');
-
-    if (post) {
-      const username = post.get('username'),
-            primaryGroupName = post.get('primary_group_name'),
-            url = post.get('usernameUrl');
-
-      var linkClass = 'username',
-          name = post.get('name');
-
-      if (post.get('staff')) { linkClass += ' staff'; }
-      if (post.get('admin')) { linkClass += ' admin'; }
-      if (post.get('moderator')) { linkClass += ' moderator'; }
-      if (post.get('new_user')) { linkClass += ' new-user'; }
-
-      if (!Em.isEmpty(primaryGroupName)) {
-        linkClass += ' ' + primaryGroupName;
-      }
-      // Main link
-      buffer.push("<span class='" + linkClass + "'><a href='" + url + "' data-auto-route='true' data-user-card='" + username + "'>" + username + "</a>");
-
-      // Add a glyph if we have one
-      const glyph = this.posterGlyph(post);
-      if (!Em.isEmpty(glyph)) {
-        buffer.push(glyph);
-      }
-      buffer.push("</span>");
-
-      // Are we showing full names?
-      if (name && this.get('displayNameOnPosts') && (this.sanitizeName(name) !== this.sanitizeName(username))) {
-        name = Discourse.Utilities.escapeExpression(name);
-        buffer.push("<span class='full-name'><a href='" + url + "' data-auto-route='true' data-user-card='" + username  + "'>" + name + "</a></span>");
-      }
-
-      // User titles
-      let title = post.get('user_title');
-      if (!Em.isEmpty(title)) {
-
-        title = Discourse.Utilities.escapeExpression(title);
-        buffer.push('<span class="user-title">');
-        if (Em.isEmpty(primaryGroupName)) {
-          buffer.push(title);
-        } else {
-          buffer.push("<a href='/groups/" + post.get('primary_group_name') + "' class='user-group'>" + title + "</a>");
-        }
-        buffer.push("</span>");
-      }
-
-      PosterNameComponent.trigger('renderedName', buffer, post);
-    }
-  },
-
-  //  Overwrite this to give a user a custom font awesome glyph.
-  posterGlyph(post) {
-    if(post.get('moderator')) {
-      const desc = I18n.t('user.moderator_tooltip');
-      return '<i class="fa fa-shield" title="' + desc +  '" alt="' + desc + '"></i>';
-    }
-  }
-});
-
-// Support for event triggering
-PosterNameComponent.reopenClass(Em.Evented);
-
-export default PosterNameComponent;
+const removed = new Discourse.RemovedObject('discourse/components/poster-name');
+export default removed;
diff --git a/app/assets/javascripts/discourse/lib/plugin-api.js.es6 b/app/assets/javascripts/discourse/lib/plugin-api.js.es6
index c65b6a241..156f74a31 100644
--- a/app/assets/javascripts/discourse/lib/plugin-api.js.es6
+++ b/app/assets/javascripts/discourse/lib/plugin-api.js.es6
@@ -1,5 +1,6 @@
 import { addDecorator } from 'discourse/widgets/post-cooked';
 import ComposerEditor from 'discourse/components/composer-editor';
+import { addPosterIcon } from 'discourse/widgets/poster-name';
 
 let _decorateId = 0;
 function decorate(klass, evt, cb) {
@@ -18,21 +19,61 @@ class PluginApi {
     this.container = container;
   }
 
+  /**
+   * decorateCooked(callback)
+   *
+   * Used for decorating the `cooked` content of a post after it is rendered using
+   * jQuery.
+   *
+   * `callback` will be called when it is time to decorate with a jQuery selector.
+   *
+   * For example, to add a yellow background to all posts you could do this:
+   *
+   * ```
+   * api.decorateCooked($elem => $elem.css({ backgroundColor: 'yellow' }));
+   * ```
+   **/
   decorateCooked(cb) {
     addDecorator(cb);
     decorate(ComposerEditor, 'previewRefreshed', cb);
     decorate(this.container.lookupFactory('view:user-stream'), 'didInsertElement', cb);
   }
+
+  /**
+   * addPosterIcon(callback)
+   *
+   * This function can be used to add an icon with a link that will be displayed
+   * beside a poster's name. The `callback` is called with the post's user custom
+   * fields, and will render an icon if it receives an object back.
+   *
+   * The returned object can have the following attributes:
+   *
+   *   icon        (required) the font awesome icon to render
+   *   className   (optional) a css class to apply to the icon
+   *   url         (optional) where to link the icon
+   *   title       (optional) the tooltip title for the icon on hover
+   *
+   * ```
+   * api.addPosterIcon(cfs => {
+   *   if (cfs.customer) {
+   *     return { icon: 'user', className: 'customer', title: 'customer' };
+   *   }
+   * });
+   * ```
+   **/
+  addPosterIcon(cb) {
+    addPosterIcon(cb);
+  }
 }
 
 let _pluginv01;
-
 export function getPluginApi(version) {
   if (version === "0.1") {
     if (!_pluginv01) {
       _pluginv01 = new PluginApi(version, Discourse.__container__);
     }
     return _pluginv01;
+  } else {
+    throw `Plugin API v${version} is not supported`;
   }
 }
-
diff --git a/app/assets/javascripts/discourse/lib/transform-post.js.es6 b/app/assets/javascripts/discourse/lib/transform-post.js.es6
index 7cb398969..cad1e2633 100644
--- a/app/assets/javascripts/discourse/lib/transform-post.js.es6
+++ b/app/assets/javascripts/discourse/lib/transform-post.js.es6
@@ -96,6 +96,7 @@ export default function transformPost(currentUser, site, post, prevPost, nextPos
   postAtts.linkCounts = post.link_counts;
   postAtts.actionCode = post.action_code;
   postAtts.actionCodeWho = post.action_code_who;
+  postAtts.userCustomFields = post.user_custom_fields;
 
   const showPMMap = topic.archetype === 'private_message' && post.post_number === 1;
   if (showPMMap) {
diff --git a/app/assets/javascripts/discourse/templates/badges/show.hbs b/app/assets/javascripts/discourse/templates/badges/show.hbs
index 7cfada243..e41eb32ef 100644
--- a/app/assets/javascripts/discourse/templates/badges/show.hbs
+++ b/app/assets/javascripts/discourse/templates/badges/show.hbs
@@ -29,7 +29,7 @@
     {{#link-to 'user' user}}
         {{avatar user imageSize="extra_large"}}
         <div class="details clearfix">
-          {{poster-name post=user}}
+          <div class='username'>{{user.username}}</div>
         </div>
     {{/link-to}}
     <div class='earned'>
diff --git a/app/assets/javascripts/discourse/widgets/poster-name.js.es6 b/app/assets/javascripts/discourse/widgets/poster-name.js.es6
index 3c91a68d5..e8e7923b9 100644
--- a/app/assets/javascripts/discourse/widgets/poster-name.js.es6
+++ b/app/assets/javascripts/discourse/widgets/poster-name.js.es6
@@ -6,6 +6,11 @@ function sanitizeName(name){
   return name.toLowerCase().replace(/[\s_-]/g,'');
 }
 
+const _callbacks = [];
+export function addPosterIcon(cb) {
+  _callbacks.push(cb);
+}
+
 export default createWidget('poster-name', {
   tagName: 'div.names.trigger-user-card',
 
@@ -56,6 +61,25 @@ export default createWidget('poster-name', {
       contents.push(h('span.user-title', titleContents));
     }
 
+    const cfs = attrs.userCustomFields;
+    if (cfs) {
+      _callbacks.forEach(cb => {
+        const result = cb(cfs);
+        if (result) {
+
+          let iconBody = iconNode(result.icon);
+          if (result.url) {
+            iconBody = h('a', { attributes: { href: result.url } }, iconBody);
+          }
+
+          contents.push(h('span',
+                         { className: result.className,
+                           attributes: { title: result.title }
+                         },
+                         iconBody));
+        }
+      });
+    }
     return contents;
   }
 });