FEATURE: New API to create a custom formatter for displaying usernames

This is not exhaustive right now, but a good start and we can add to
it over time.
This commit is contained in:
Robin Ward 2017-10-30 12:40:58 -04:00
parent e02ad4249e
commit a0dd75ba88
11 changed files with 57 additions and 16 deletions

View File

@ -0,0 +1,4 @@
import { registerUnbound } from 'discourse-common/lib/helpers';
import { formatUsername } from 'discourse/lib/utilities';
export default registerUnbound('format-username', formatUsername);

View File

@ -1,5 +1,6 @@
import { ajax } from 'discourse/lib/ajax';
import { userPath } from 'discourse/lib/url';
import { formatUsername } from 'discourse/lib/utilities';
function replaceSpan($e, username, opts) {
let extra = "";
@ -16,7 +17,7 @@ function replaceSpan($e, username, opts) {
extra = `data-name='${username}'`;
extraClass = "cannot-see";
}
$e.replaceWith(`<a href='${userPath(username.toLowerCase())}' class='mention ${extraClass}' ${extra}>@${username}</a>`);
$e.replaceWith(`<a href='${userPath(username.toLowerCase())}' class='mention ${extraClass}' ${extra}>@${formatUsername(username)}</a>`);
}
}

View File

@ -20,10 +20,10 @@ import { addPostTransformCallback } from 'discourse/widgets/post-stream';
import { attachAdditionalPanel } from 'discourse/widgets/header';
import { registerIconRenderer, replaceIcon } from 'discourse-common/lib/icon-library';
import { addNavItem } from 'discourse/models/nav-item';
import { replaceFormatter } from 'discourse/lib/utilities';
// If you add any methods to the API ensure you bump up this number
const PLUGIN_API_VERSION = '0.8.11';
const PLUGIN_API_VERSION = '0.8.12';
class PluginApi {
constructor(version, container) {
@ -570,6 +570,25 @@ class PluginApi {
addNavItem(item);
}
}
/**
*
* Registers a function that will format a username when displayed. This will not
* be applied when the username is used as an `id` or in URL strings.
*
* Example:
*
* ```
* // display usernames in UPPER CASE
* api.formatUsername(username => username.toUpperCase());
*
* ```
*
**/
formatUsername(fn) {
replaceFormatter(fn);
}
}
let _pluginv01;

View File

@ -3,6 +3,7 @@ import { performEmojiUnescape, buildEmojiUrl } from 'pretty-text/emoji';
import WhiteLister from 'pretty-text/white-lister';
import { sanitize as textSanitize } from 'pretty-text/sanitizer';
import loadScript from 'discourse/lib/load-script';
import { formatUsername } from 'discourse/lib/utilities';
function getOpts(opts) {
const siteSettings = Discourse.__container__.lookup('site-settings:main'),
@ -12,7 +13,8 @@ function getOpts(opts) {
getURL: Discourse.getURLWithCDN,
currentUser: Discourse.__container__.lookup('current-user:main'),
censoredWords: site.censored_words,
siteSettings
siteSettings,
formatUsername
}, opts);
return buildOptions(opts);

View File

@ -21,6 +21,17 @@ export function escapeExpression(string) {
return escape(string);
}
let _usernameFormatDelegate = username => username;
export function formatUsername(username) {
return _usernameFormatDelegate(username || '');
}
export function replaceFormatter(fn) {
_usernameFormatDelegate = fn;
}
export function avatarUrl(template, size) {
if (!template) { return ""; }
const rawSize = getRawSize(translateSize(size));

View File

@ -16,7 +16,7 @@
<div class="names">
<span>
<h1 class="{{staff}} {{new_user}} {{if nameFirst "full-name" "username"}}">
<a href={{user.path}} {{action "showUser"}}>{{if nameFirst user.name username}} {{user-status user currentUser=currentUser}}</a>
<a href={{user.path}} {{action "showUser"}}>{{if nameFirst user.name (format-username username)}} {{user-status user currentUser=currentUser}}</a>
</h1>
{{#unless nameFirst}}

View File

@ -61,7 +61,7 @@
</section>
<div class="primary-textual">
<h1 class="{{if nameFirst "full-name" "username"}}">{{if nameFirst model.name model.username}} {{user-status model currentUser=currentUser}}</h1>
<h1 class="{{if nameFirst "full-name" "username"}}">{{if nameFirst model.name (format-username model.username)}} {{user-status model currentUser=currentUser}}</h1>
<h2 class="{{if nameFirst "username" "full-name"}}">{{#if nameFirst}}{{model.username}}{{else}}{{model.name}}{{/if}}</h2>
{{#if model.title}}
<h3>{{model.title}}</h3>

View File

@ -4,7 +4,7 @@ import { createWidget } from 'discourse/widgets/widget';
import DiscourseURL from 'discourse/lib/url';
import { h } from 'virtual-dom';
import { emojiUnescape } from 'discourse/lib/text';
import { postUrl, escapeExpression } from 'discourse/lib/utilities';
import { postUrl, escapeExpression, formatUsername } from 'discourse/lib/utilities';
import { setTransientHeader } from 'discourse/lib/ajax';
import { userPath } from 'discourse/lib/url';
import { iconNode } from 'discourse-common/lib/icon-library';
@ -79,11 +79,11 @@ createWidget('notification-item', {
return I18n.t(scope, { count, group_name });
}
const username = data.display_username;
const username = formatUsername(data.display_username);
const description = this.description();
if (notificationType === LIKED_TYPE && data.count > 1) {
const count = data.count - 2;
const username2 = data.username2;
const username2 = formatUsername(data.username2);
if (count===0) {
return I18n.t('notifications.liked_2', {description, username, username2});
} else {

View File

@ -1,6 +1,7 @@
import { iconNode } from 'discourse-common/lib/icon-library';
import { createWidget } from 'discourse/widgets/widget';
import { h } from 'virtual-dom';
import { formatUsername } from 'discourse/lib/utilities';
function sanitizeName(name){
return name.toLowerCase().replace(/[\s_-]/g,'');
@ -17,10 +18,11 @@ export default createWidget('poster-name', {
},
userLink(attrs, text) {
return h('a', { attributes: {
href: attrs.usernameUrl,
'data-user-card': attrs.username
} }, text);
return h(
'a',
{ attributes: { href: attrs.usernameUrl, 'data-user-card': attrs.username } },
formatUsername(text)
);
},
html(attrs) {

View File

@ -1,5 +1,6 @@
import { createWidget } from 'discourse/widgets/widget';
import { h } from 'virtual-dom';
import { formatUsername } from 'discourse/lib/utilities';
let extraGlyphs;
@ -50,7 +51,7 @@ createWidget('user-menu-links', {
model: currentUser,
className: 'user-activity-link',
icon: 'user',
rawLabel: currentUser.username
rawLabel: formatUsername(currentUser.username)
};
if (currentUser.is_anonymous) {

View File

@ -24,7 +24,8 @@ export function buildOptions(state) {
lookupImageUrls,
previewing,
linkify,
censoredWords
censoredWords,
mentionLookup
} = state;
let features = {
@ -56,7 +57,7 @@ export function buildOptions(state) {
getCurrentUser,
currentUser,
lookupAvatarByPostNumber,
mentionLookup: state.mentionLookup,
mentionLookup,
emojiUnicodeReplacer,
lookupInlineOnebox,
lookupImageUrls,