Convert front end paths from `/users/` to `/u/`

This commit is contained in:
Robin Ward 2017-03-28 12:16:58 -04:00
parent 6b976433c9
commit 45a257815a
40 changed files with 129 additions and 84 deletions

View File

@ -1,6 +1,7 @@
import { ajax } from 'discourse/lib/ajax'; import { ajax } from 'discourse/lib/ajax';
import CanCheckEmails from 'discourse/mixins/can-check-emails'; import CanCheckEmails from 'discourse/mixins/can-check-emails';
import { propertyNotEqual, setting } from 'discourse/lib/computed'; import { propertyNotEqual, setting } from 'discourse/lib/computed';
import { userPath } from 'discourse/lib/url';
export default Ember.Controller.extend(CanCheckEmails, { export default Ember.Controller.extend(CanCheckEmails, {
editingTitle: false, editingTitle: false,
@ -62,7 +63,7 @@ export default Ember.Controller.extend(CanCheckEmails, {
saveTitle() { saveTitle() {
const self = this; const self = this;
return ajax(`/users/${this.get('model.username').toLowerCase()}.json`, { return ajax(userPath(`${this.get('model.username').toLowerCase()}.json`), {
data: {title: this.get('userTitleValue')}, data: {title: this.get('userTitleValue')},
type: 'PUT' type: 'PUT'
}).catch(function(e) { }).catch(function(e) {

View File

@ -1,4 +1,5 @@
import { autoUpdatingRelativeAge } from 'discourse/lib/formatter'; import { autoUpdatingRelativeAge } from 'discourse/lib/formatter';
import { userPath } from 'discourse/lib/url';
export function actionDescriptionHtml(actionCode, createdAt, username) { export function actionDescriptionHtml(actionCode, createdAt, username) {
const dt = new Date(createdAt); const dt = new Date(createdAt);
@ -9,7 +10,7 @@ export function actionDescriptionHtml(actionCode, createdAt, username) {
if (actionCode === "invited_group" || actionCode === "removed_group") { if (actionCode === "invited_group" || actionCode === "removed_group") {
who = `<a class="mention-group" href="/groups/${username}">@${username}</a>`; who = `<a class="mention-group" href="/groups/${username}">@${username}</a>`;
} else { } else {
who = `<a class="mention" href="/users/${username}">@${username}</a>`; who = `<a class="mention" href="${userPath(username)}">@${username}</a>`;
} }
} }
return I18n.t(`action_codes.${actionCode}`, { who, when }).htmlSafe(); return I18n.t(`action_codes.${actionCode}`, { who, when }).htmlSafe();

View File

@ -5,6 +5,7 @@ import afterTransition from 'discourse/lib/after-transition';
import { default as computed, observes } from 'ember-addons/ember-computed-decorators'; import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
import DiscourseURL from 'discourse/lib/url'; import DiscourseURL from 'discourse/lib/url';
import User from 'discourse/models/user'; import User from 'discourse/models/user';
import { userPath } from 'discourse/lib/url';
const clickOutsideEventName = "mousedown.outside-user-card"; const clickOutsideEventName = "mousedown.outside-user-card";
const clickDataExpand = "click.discourse-user-card"; const clickDataExpand = "click.discourse-user-card";
@ -92,7 +93,7 @@ export default Ember.Component.extend(CleansUp, {
// Don't show on mobile // Don't show on mobile
if (this.site.mobileView) { if (this.site.mobileView) {
DiscourseURL.routeTo(`/users/${username}`); DiscourseURL.routeTo(userPath(username));
return false; return false;
} }

View File

@ -1,5 +1,5 @@
import { url } from 'discourse/lib/computed';
import computed from 'ember-addons/ember-computed-decorators'; import computed from 'ember-addons/ember-computed-decorators';
import { userPath } from 'discourse/lib/url';
function normalize(name) { function normalize(name) {
return name.replace(/[\-\_ \.]/g, '').toLowerCase(); return name.replace(/[\-\_ \.]/g, '').toLowerCase();
@ -8,7 +8,11 @@ function normalize(name) {
export default Ember.Component.extend({ export default Ember.Component.extend({
classNameBindings: [':user-info', 'size'], classNameBindings: [':user-info', 'size'],
size: 'small', size: 'small',
userPath: url('user.username', '/users/%@'),
@computed('user.username')
userPath(username) {
return userPath(username);
},
// TODO: In later ember releases `hasBlock` works without this // TODO: In later ember releases `hasBlock` works without this
hasBlock: Ember.computed.alias('template'), hasBlock: Ember.computed.alias('template'),

View File

@ -6,6 +6,7 @@ import { emailValid } from 'discourse/lib/utilities';
import InputValidation from 'discourse/models/input-validation'; import InputValidation from 'discourse/models/input-validation';
import PasswordValidation from "discourse/mixins/password-validation"; import PasswordValidation from "discourse/mixins/password-validation";
import UsernameValidation from "discourse/mixins/username-validation"; import UsernameValidation from "discourse/mixins/username-validation";
import { userPath } from 'discourse/lib/url';
export default Ember.Controller.extend(ModalFunctionality, PasswordValidation, UsernameValidation, { export default Ember.Controller.extend(ModalFunctionality, PasswordValidation, UsernameValidation, {
login: Ember.inject.controller(), login: Ember.inject.controller(),
@ -164,7 +165,7 @@ export default Ember.Controller.extend(ModalFunctionality, PasswordValidation, U
@on('init') @on('init')
fetchConfirmationValue() { fetchConfirmationValue() {
return ajax('/users/hp.json').then(json => { return ajax(userPath('hp.json')).then(json => {
this.set('accountPasswordConfirm', json.value); this.set('accountPasswordConfirm', json.value);
this.set('accountChallenge', json.challenge.split("").reverse().join("")); this.set('accountChallenge', json.challenge.split("").reverse().join(""));
}); });
@ -196,7 +197,7 @@ export default Ember.Controller.extend(ModalFunctionality, PasswordValidation, U
const $hidden_login_form = $('#hidden-login-form'); const $hidden_login_form = $('#hidden-login-form');
$hidden_login_form.find('input[name=username]').val(attrs.accountUsername); $hidden_login_form.find('input[name=username]').val(attrs.accountUsername);
$hidden_login_form.find('input[name=password]').val(attrs.accountPassword); $hidden_login_form.find('input[name=password]').val(attrs.accountPassword);
$hidden_login_form.find('input[name=redirect]').val(Discourse.getURL('/users/account-created')); $hidden_login_form.find('input[name=redirect]').val(userPath('account-created'));
$hidden_login_form.submit(); $hidden_login_form.submit();
} else { } else {
self.flash(result.message || I18n.t('create_account.failed'), 'error'); self.flash(result.message || I18n.t('create_account.failed'), 'error');

View File

@ -3,6 +3,7 @@ import { queryParams } from 'discourse/controllers/discovery-sortable';
import BulkTopicSelection from 'discourse/mixins/bulk-topic-selection'; import BulkTopicSelection from 'discourse/mixins/bulk-topic-selection';
import { endWith } from 'discourse/lib/computed'; import { endWith } from 'discourse/lib/computed';
import showModal from 'discourse/lib/show-modal'; import showModal from 'discourse/lib/show-modal';
import { userPath } from 'discourse/lib/url';
const controllerOpts = { const controllerOpts = {
discovery: Ember.inject.controller(), discovery: Ember.inject.controller(),
@ -133,14 +134,14 @@ const controllerOpts = {
}.property('allLoaded', 'model.topics.length'), }.property('allLoaded', 'model.topics.length'),
footerEducation: function() { footerEducation: function() {
if (!this.get('allLoaded') || this.get('model.topics.length') > 0 || !Discourse.User.current()) { return; } if (!this.get('allLoaded') || this.get('model.topics.length') > 0 || !this.currentUser) { return; }
const split = (this.get('model.filter') || '').split('/'); const split = (this.get('model.filter') || '').split('/');
if (split[0] !== 'new' && split[0] !== 'unread') { return; } if (split[0] !== 'new' && split[0] !== 'unread') { return; }
return I18n.t("topics.none.educate." + split[0], { return I18n.t("topics.none.educate." + split[0], {
userPrefsUrl: Discourse.getURL("/users/") + (Discourse.User.currentProp("username_lower")) + "/preferences" userPrefsUrl: userPath(`${this.currentUser.get('username_lower')}/preferences`)
}); });
}.property('allLoaded', 'model.topics.length') }.property('allLoaded', 'model.topics.length')

View File

@ -1,6 +1,7 @@
import { ajax } from 'discourse/lib/ajax'; import { ajax } from 'discourse/lib/ajax';
import { popupAjaxError } from 'discourse/lib/ajax-error'; import { popupAjaxError } from 'discourse/lib/ajax-error';
import ModalFunctionality from 'discourse/mixins/modal-functionality'; import ModalFunctionality from 'discourse/mixins/modal-functionality';
import { userPath } from 'discourse/lib/url';
export default Ember.Controller.extend(ModalFunctionality, { export default Ember.Controller.extend(ModalFunctionality, {
emailSent: false, emailSent: false,
@ -11,7 +12,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
actions: { actions: {
sendActivationEmail() { sendActivationEmail() {
ajax('/users/action/send_activation_email', { ajax(userPath('action/send_activation_email'), {
data: { username: this.get('username') }, data: { username: this.get('username') },
type: 'POST' type: 'POST'
}).then(() => { }).then(() => {

View File

@ -3,6 +3,7 @@ import getUrl from 'discourse-common/lib/get-url';
import DiscourseURL from 'discourse/lib/url'; import DiscourseURL from 'discourse/lib/url';
import { ajax } from 'discourse/lib/ajax'; import { ajax } from 'discourse/lib/ajax';
import PasswordValidation from "discourse/mixins/password-validation"; import PasswordValidation from "discourse/mixins/password-validation";
import { userPath } from 'discourse/lib/url';
export default Ember.Controller.extend(PasswordValidation, { export default Ember.Controller.extend(PasswordValidation, {
isDeveloper: Ember.computed.alias('model.is_developer'), isDeveloper: Ember.computed.alias('model.is_developer'),
@ -27,7 +28,7 @@ export default Ember.Controller.extend(PasswordValidation, {
actions: { actions: {
submit() { submit() {
ajax({ ajax({
url: `/users/password-reset/${this.get('model.token')}.json`, url: userPath(`password-reset/${this.get('model.token')}.json`),
type: 'PUT', type: 'PUT',
data: { data: {
password: this.get('accountPassword') password: this.get('accountPassword')

View File

@ -1,5 +1,6 @@
import { setting, propertyEqual } from 'discourse/lib/computed'; import { setting, propertyEqual } from 'discourse/lib/computed';
import DiscourseURL from 'discourse/lib/url'; import DiscourseURL from 'discourse/lib/url';
import { userPath } from 'discourse/lib/url';
export default Ember.Controller.extend({ export default Ember.Controller.extend({
taken: false, taken: false,
@ -48,7 +49,7 @@ export default Ember.Controller.extend({
if (result) { if (result) {
this.set('saving', true); this.set('saving', true);
this.get('content').changeUsername(this.get('newUsername')).then(() => { this.get('content').changeUsername(this.get('newUsername')).then(() => {
DiscourseURL.redirectTo("/users/" + this.get('newUsername').toLowerCase() + "/preferences"); DiscourseURL.redirectTo(userPath(this.get('newUsername').toLowerCase() + "/preferences"));
}) })
.catch(() => this.set('error', true)) .catch(() => this.set('error', true))
.finally(() => this.set('saving', false)); .finally(() => this.set('saving', false));

View File

@ -1,5 +1,6 @@
import { ajax } from 'discourse/lib/ajax'; import { ajax } from 'discourse/lib/ajax';
import computed from 'ember-addons/ember-computed-decorators'; import computed from 'ember-addons/ember-computed-decorators';
import { userPath } from 'discourse/lib/url';
export default Ember.Controller.extend({ export default Ember.Controller.extend({
application: Ember.inject.controller(), application: Ember.inject.controller(),
@ -18,7 +19,7 @@ export default Ember.Controller.extend({
markFaqRead() { markFaqRead() {
const currentUser = this.currentUser; const currentUser = this.currentUser;
if (currentUser) { if (currentUser) {
ajax("/users/read-faq", { method: "POST" }).then(() => { ajax(userPath("read-faq"), { method: "POST" }).then(() => {
currentUser.set('read_faq', true); currentUser.set('read_faq', true);
}); });
} }

View File

@ -12,6 +12,7 @@ import Post from 'discourse/models/post';
import debounce from 'discourse/lib/debounce'; import debounce from 'discourse/lib/debounce';
import isElementInViewport from "discourse/lib/is-element-in-viewport"; import isElementInViewport from "discourse/lib/is-element-in-viewport";
import QuoteState from 'discourse/lib/quote-state'; import QuoteState from 'discourse/lib/quote-state';
import { userPath } from 'discourse/lib/url';
export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, { export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
composer: Ember.inject.controller(), composer: Ember.inject.controller(),
@ -126,7 +127,7 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
showCategoryChooser: Ember.computed.not("model.isPrivateMessage"), showCategoryChooser: Ember.computed.not("model.isPrivateMessage"),
gotoInbox(name) { gotoInbox(name) {
var url = '/users/' + this.get('currentUser.username_lower') + '/messages'; let url = userPath(this.get('currentUser.username_lower') + '/messages');
if (name) { if (name) {
url = url + '/group/' + name; url = url + '/group/' + name;
} }

View File

@ -1,4 +1,5 @@
import { ajax } from 'discourse/lib/ajax'; import { ajax } from 'discourse/lib/ajax';
import { userPath } from 'discourse/lib/url';
function replaceSpan($e, username, opts) { function replaceSpan($e, username, opts) {
let extra = ""; let extra = "";
@ -15,7 +16,7 @@ function replaceSpan($e, username, opts) {
extra = `data-name='${username}'`; extra = `data-name='${username}'`;
extraClass = "cannot-see"; extraClass = "cannot-see";
} }
$e.replaceWith(`<a href='${Discourse.getURL("/users/") + username.toLowerCase()}' class='mention ${extraClass}' ${extra}>@${username}</a>`); $e.replaceWith(`<a href='${userPath(username.toLowerCase())}' class='mention ${extraClass}' ${extra}>@${username}</a>`);
} }
} }
@ -54,7 +55,7 @@ export function linkSeenMentions($elem, siteSettings) {
// 'Create a New Topic' scenario is not supported (per conversation with codinghorror) // 'Create a New Topic' scenario is not supported (per conversation with codinghorror)
// https://meta.discourse.org/t/taking-another-1-7-release-task/51986/7 // https://meta.discourse.org/t/taking-another-1-7-release-task/51986/7
export function fetchUnseenMentions(usernames, topic_id) { export function fetchUnseenMentions(usernames, topic_id) {
return ajax("/users/is_local_username", { data: { usernames, topic_id } }).then(r => { return ajax(userPath("is_local_username"), { data: { usernames, topic_id } }).then(r => {
r.valid.forEach(v => found[v] = true); r.valid.forEach(v => found[v] = true);
r.valid_groups.forEach(vg => foundGroups[vg] = true); r.valid_groups.forEach(vg => foundGroups[vg] = true);
r.mentionable_groups.forEach(mg => mentionableGroups[mg.name] = mg); r.mentionable_groups.forEach(mg => mentionableGroups[mg.name] = mg);

View File

@ -5,6 +5,7 @@ import { SEPARATOR } from 'discourse/lib/category-hashtags';
import Category from 'discourse/models/category'; import Category from 'discourse/models/category';
import { search as searchCategoryTag } from 'discourse/lib/category-tag-search'; import { search as searchCategoryTag } from 'discourse/lib/category-tag-search';
import userSearch from 'discourse/lib/user-search'; import userSearch from 'discourse/lib/user-search';
import { userPath } from 'discourse/lib/url';
export function translateResults(results, opts) { export function translateResults(results, opts) {
@ -29,7 +30,7 @@ export function translateResults(results, opts) {
results.posts = results.posts.map(post => { results.posts = results.posts.map(post => {
if (post.username) { if (post.username) {
post.userPath = Discourse.getURL(`/users/${post.username.toLowerCase()}`); post.userPath = userPath(post.username.toLowerCase());
} }
post = Post.create(post); post = Post.create(post);
post.set('topic', topicMap[post.topic_id]); post.set('topic', topicMap[post.topic_id]);

View File

@ -1,3 +1,5 @@
import { userPath } from 'discourse/lib/url';
function actionDescription(action, acted, count) { function actionDescription(action, acted, count) {
if (acted) { if (acted) {
if (count <= 1) { if (count <= 1) {
@ -39,7 +41,7 @@ export function transformBasicPost(post) {
via_email: post.via_email, via_email: post.via_email,
isAutoGenerated: post.is_auto_generated, isAutoGenerated: post.is_auto_generated,
user_id: post.user_id, user_id: post.user_id,
usernameUrl: Discourse.getURL(`/users/${post.username}`), usernameUrl: userPath(post.username),
username: post.username, username: post.username,
avatar_template: post.avatar_template, avatar_template: post.avatar_template,
bookmarked: post.bookmarked, bookmarked: post.bookmarked,

View File

@ -35,6 +35,10 @@ export function clearRewrites() {
rewrites.length = 0; rewrites.length = 0;
} }
export function userPath(subPath) {
return Discourse.getURL(subPath ? `/u/${subPath}` : '/u');
}
let _jumpScheduled = false; let _jumpScheduled = false;
export function jumpToElement(elementId) { export function jumpToElement(elementId) {
if (_jumpScheduled || Ember.isEmpty(elementId)) { return; } if (_jumpScheduled || Ember.isEmpty(elementId)) { return; }
@ -190,7 +194,7 @@ const DiscourseURL = Ember.Object.extend({
if (path.indexOf('/my/') === 0) { if (path.indexOf('/my/') === 0) {
const currentUser = Discourse.User.current(); const currentUser = Discourse.User.current();
if (currentUser) { if (currentUser) {
path = path.replace('/my/', '/users/' + currentUser.get('username_lower') + "/"); path = path.replace('/my/', userPath(currentUser.get('username_lower') + "/"));
} else { } else {
document.location.href = "/404"; document.location.href = "/404";
return; return;

View File

@ -1,4 +1,5 @@
import { CANCELLED_STATUS } from 'discourse/lib/autocomplete'; import { CANCELLED_STATUS } from 'discourse/lib/autocomplete';
import { userPath } from 'discourse/lib/url';
var cache = {}, var cache = {},
cacheTopicId, cacheTopicId,
@ -14,7 +15,7 @@ function performSearch(term, topicId, includeGroups, includeMentionableGroups, a
} }
// need to be able to cancel this // need to be able to cancel this
oldSearch = $.ajax(Discourse.getURL('/users/search/users'), { oldSearch = $.ajax(userPath('search/users'), {
data: { term: term, data: { term: term,
topic_id: topicId, topic_id: topicId,
include_groups: includeGroups, include_groups: includeGroups,

View File

@ -65,10 +65,6 @@ export function postUrl(slug, topicId, postNumber) {
return url; return url;
} }
export function userUrl(username) {
return Discourse.getURL("/users/" + username.toLowerCase());
}
export function emailValid(email) { export function emailValid(email) {
// see: http://stackoverflow.com/questions/46155/validate-email-address-in-javascript // see: http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
const re = /^[a-zA-Z0-9!#$%&'*+\/=?\^_`{|}~\-]+(?:\.[a-zA-Z0-9!#$%&'\*+\/=?\^_`{|}~\-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?$/; const re = /^[a-zA-Z0-9!#$%&'*+\/=?\^_`{|}~\-]+(?:\.[a-zA-Z0-9!#$%&'\*+\/=?\^_`{|}~\-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?$/;

View File

@ -17,7 +17,6 @@ const BareRouter = Ember.Router.extend({
url = `${url}?${params[1]}`; url = `${url}?${params[1]}`;
} }
} }
console.log(url);
return this._super(url); return this._super(url);
} }
}); });

View File

@ -1,5 +1,6 @@
import { ajax } from 'discourse/lib/ajax'; import { ajax } from 'discourse/lib/ajax';
import { popupAjaxError } from 'discourse/lib/ajax-error'; import { popupAjaxError } from 'discourse/lib/ajax-error';
import { userPath } from 'discourse/lib/url';
const Invite = Discourse.Model.extend({ const Invite = Discourse.Model.extend({
@ -41,7 +42,7 @@ Invite.reopenClass({
if (!Em.isNone(search)) { data.search = search; } if (!Em.isNone(search)) { data.search = search; }
data.offset = offset || 0; data.offset = offset || 0;
return ajax("/users/" + user.get('username_lower') + "/invited.json", {data}).then(function (result) { return ajax(userPath(user.get('username_lower') + "/invited.json"), {data}).then(function (result) {
result.invites = result.invites.map(function (i) { result.invites = result.invites.map(function (i) {
return Invite.create(i); return Invite.create(i);
}); });
@ -52,7 +53,7 @@ Invite.reopenClass({
findInvitedCount(user) { findInvitedCount(user) {
if (!user) { return Em.RSVP.resolve(); } if (!user) { return Em.RSVP.resolve(); }
return ajax("/users/" + user.get('username_lower') + "/invited_count.json").then(result => Em.Object.create(result.counts)); return ajax(userPath(user.get('username_lower') + "/invited_count.json")).then(result => Em.Object.create(result.counts));
}, },
reinviteAll() { reinviteAll() {

View File

@ -2,11 +2,12 @@ import { ajax } from 'discourse/lib/ajax';
import RestModel from 'discourse/models/rest'; import RestModel from 'discourse/models/rest';
import { popupAjaxError } from 'discourse/lib/ajax-error'; import { popupAjaxError } from 'discourse/lib/ajax-error';
import ActionSummary from 'discourse/models/action-summary'; import ActionSummary from 'discourse/models/action-summary';
import { url, propertyEqual } from 'discourse/lib/computed'; import { propertyEqual } from 'discourse/lib/computed';
import Quote from 'discourse/lib/quote'; import Quote from 'discourse/lib/quote';
import computed from 'ember-addons/ember-computed-decorators'; import computed from 'ember-addons/ember-computed-decorators';
import { postUrl } from 'discourse/lib/utilities'; import { postUrl } from 'discourse/lib/utilities';
import { cook } from 'discourse/lib/text'; import { cook } from 'discourse/lib/text';
import { userPath } from 'discourse/lib/url';
const Post = RestModel.extend({ const Post = RestModel.extend({
@ -60,7 +61,10 @@ const Post = RestModel.extend({
return postNumber === 1 ? baseUrl + "/1" : baseUrl; return postNumber === 1 ? baseUrl + "/1" : baseUrl;
}, },
usernameUrl: url('username', '/users/%@'), @computed('username')
usernameUrl(username) {
return userPath(username);
},
topicOwner: propertyEqual('topic.details.created_by.id', 'user_id'), topicOwner: propertyEqual('topic.details.created_by.id', 'user_id'),

View File

@ -9,6 +9,7 @@ import { popupAjaxError } from 'discourse/lib/ajax-error';
import { censor } from 'pretty-text/censored-words'; import { censor } from 'pretty-text/censored-words';
import { emojiUnescape } from 'discourse/lib/text'; import { emojiUnescape } from 'discourse/lib/text';
import PreloadStore from 'preload-store'; import PreloadStore from 'preload-store';
import { userPath } from 'discourse/lib/url';
export function loadTopicView(topic, args) { export function loadTopicView(topic, args) {
const topicId = topic.get('id'); const topicId = topic.get('id');
@ -182,9 +183,10 @@ const Topic = RestModel.extend({
return this.urlForPostNumber(1) + (this.get('has_summary') ? "?filter=summary" : ""); return this.urlForPostNumber(1) + (this.get('has_summary') ? "?filter=summary" : "");
}.property('url'), }.property('url'),
lastPosterUrl: function() { @computed('last_poster.username')
return Discourse.getURL("/users/") + this.get("last_poster.username"); lastPosterUrl(username) {
}.property('last_poster'), return userPath(username);
},
// The amount of new posts to display. It might be different than what the server // The amount of new posts to display. It might be different than what the server
// tells us if we are still asynchronously flushing our "recently read" data. // tells us if we are still asynchronously flushing our "recently read" data.

View File

@ -1,9 +1,9 @@
import RestModel from 'discourse/models/rest'; import RestModel from 'discourse/models/rest';
import { url } from 'discourse/lib/computed';
import { on } from 'ember-addons/ember-computed-decorators'; import { on } from 'ember-addons/ember-computed-decorators';
import computed from 'ember-addons/ember-computed-decorators'; import computed from 'ember-addons/ember-computed-decorators';
import UserActionGroup from 'discourse/models/user-action-group'; import UserActionGroup from 'discourse/models/user-action-group';
import { postUrl } from 'discourse/lib/utilities'; import { postUrl } from 'discourse/lib/utilities';
import { userPath } from 'discourse/lib/url';
const UserActionTypes = { const UserActionTypes = {
likes_given: 1, likes_given: 1,
@ -79,14 +79,21 @@ const UserAction = RestModel.extend({
presentName: Ember.computed.or('name', 'username'), presentName: Ember.computed.or('name', 'username'),
targetDisplayName: Ember.computed.or('target_name', 'target_username'), targetDisplayName: Ember.computed.or('target_name', 'target_username'),
actingDisplayName: Ember.computed.or('acting_name', 'acting_username'), actingDisplayName: Ember.computed.or('acting_name', 'acting_username'),
targetUserUrl: url('target_username', '/users/%@'),
@computed('target_username')
targetUserUrl(username) {
return userPath(username);
},
@computed("username") @computed("username")
usernameLower(username) { usernameLower(username) {
return username.toLowerCase(); return username.toLowerCase();
}, },
userUrl: url('usernameLower', '/users/%@'), @computed('usernameLower')
userUrl(usernameLower) {
return userPath(usernameLower);
},
@computed() @computed()
postUrl() { postUrl() {

View File

@ -15,6 +15,7 @@ import Topic from 'discourse/models/topic';
import { emojiUnescape } from 'discourse/lib/text'; import { emojiUnescape } from 'discourse/lib/text';
import PreloadStore from 'preload-store'; import PreloadStore from 'preload-store';
import { defaultHomepage } from 'discourse/lib/utilities'; import { defaultHomepage } from 'discourse/lib/utilities';
import { userPath } from 'discourse/lib/url';
const User = RestModel.extend({ const User = RestModel.extend({
@ -71,7 +72,7 @@ const User = RestModel.extend({
@computed() @computed()
path() { path() {
// no need to observe, requires a hard refresh to update // no need to observe, requires a hard refresh to update
return Discourse.getURL(`/users/${this.get('username_lower')}`); return userPath(this.get('username_lower'));
}, },
@computed() @computed()
@ -124,11 +125,10 @@ const User = RestModel.extend({
// directly targetted so go to inbox // directly targetted so go to inbox
if (!groups || (allowedUsers && allowedUsers.findBy("id", userId))) { if (!groups || (allowedUsers && allowedUsers.findBy("id", userId))) {
return Discourse.getURL(`/users/${username}/messages`); return userPath(`${username}/messages`);
} else { } else {
if (groups && groups[0]) if (groups && groups[0]) {
{ return userPath(`${username}/messages/group/${groups[0].name}`);
return Discourse.getURL(`/users/${username}/messages/group/${groups[0].name}`);
} }
} }
@ -179,14 +179,14 @@ const User = RestModel.extend({
}, },
changeUsername(new_username) { changeUsername(new_username) {
return ajax(`/users/${this.get('username_lower')}/preferences/username`, { return ajax(userPath(`${this.get('username_lower')}/preferences/username`), {
type: 'PUT', type: 'PUT',
data: { new_username } data: { new_username }
}); });
}, },
changeEmail(email) { changeEmail(email) {
return ajax(`/users/${this.get('username_lower')}/preferences/email`, { return ajax(userPath(`${this.get('username_lower')}/preferences/email`), {
type: 'PUT', type: 'PUT',
data: { email } data: { email }
}); });
@ -254,7 +254,7 @@ const User = RestModel.extend({
// TODO: We can remove this when migrated fully to rest model. // TODO: We can remove this when migrated fully to rest model.
this.set('isSaving', true); this.set('isSaving', true);
return ajax(`/users/${this.get('username_lower')}.json`, { return ajax(userPath(`${this.get('username_lower')}.json`), {
data: data, data: data,
type: 'PUT' type: 'PUT'
}).then(result => { }).then(result => {
@ -330,7 +330,7 @@ const User = RestModel.extend({
const user = this; const user = this;
return PreloadStore.getAndRemove(`user_${user.get('username')}`, () => { return PreloadStore.getAndRemove(`user_${user.get('username')}`, () => {
return ajax(`/users/${user.get('username')}.json`, { data: options }); return ajax(userPath(`${user.get('username')}.json`), { data: options });
}).then(json => { }).then(json => {
if (!Em.isEmpty(json.user.stats)) { if (!Em.isEmpty(json.user.stats)) {
@ -375,13 +375,13 @@ const User = RestModel.extend({
findStaffInfo() { findStaffInfo() {
if (!Discourse.User.currentProp("staff")) { return Ember.RSVP.resolve(null); } if (!Discourse.User.currentProp("staff")) { return Ember.RSVP.resolve(null); }
return ajax(`/users/${this.get("username_lower")}/staff-info.json`).then(info => { return ajax(userPath(`${this.get("username_lower")}/staff-info.json`)).then(info => {
this.setProperties(info); this.setProperties(info);
}); });
}, },
pickAvatar(upload_id, type, avatar_template) { pickAvatar(upload_id, type, avatar_template) {
return ajax(`/users/${this.get("username_lower")}/preferences/avatar/pick`, { return ajax(userPath(`${this.get("username_lower")}/preferences/avatar/pick`), {
type: 'PUT', type: 'PUT',
data: { upload_id, type } data: { upload_id, type }
}).then(() => this.setProperties({ }).then(() => this.setProperties({
@ -437,7 +437,7 @@ const User = RestModel.extend({
"delete": function() { "delete": function() {
if (this.get('can_delete_account')) { if (this.get('can_delete_account')) {
return ajax("/users/" + this.get('username'), { return ajax(userPath(this.get('username')), {
type: 'DELETE', type: 'DELETE',
data: {context: window.location.pathname} data: {context: window.location.pathname}
}); });
@ -448,14 +448,14 @@ const User = RestModel.extend({
dismissBanner(bannerKey) { dismissBanner(bannerKey) {
this.set("dismissed_banner_key", bannerKey); this.set("dismissed_banner_key", bannerKey);
ajax(`/users/${this.get('username')}`, { ajax(userPath(this.get('username')), {
type: 'PUT', type: 'PUT',
data: { dismissed_banner_key: bannerKey } data: { dismissed_banner_key: bannerKey }
}); });
}, },
checkEmail() { checkEmail() {
return ajax(`/users/${this.get("username_lower")}/emails.json`, { return ajax(userPath(`${this.get("username_lower")}/emails.json`), {
data: { context: window.location.pathname } data: { context: window.location.pathname }
}).then(result => { }).then(result => {
if (result) { if (result) {
@ -468,7 +468,7 @@ const User = RestModel.extend({
}, },
summary() { summary() {
return ajax(`/users/${this.get("username_lower")}/summary.json`) return ajax(userPath(`${this.get("username_lower")}/summary.json`))
.then(json => { .then(json => {
const summary = json["user_summary"]; const summary = json["user_summary"];
const topicMap = {}; const topicMap = {};
@ -526,7 +526,7 @@ User.reopenClass(Singleton, {
}, },
checkUsername(username, email, for_user_id) { checkUsername(username, email, for_user_id) {
return ajax('/users/check_username', { return ajax(userPath('check_username'), {
data: { username, email, for_user_id } data: { username, email, for_user_id }
}); });
}, },
@ -557,7 +557,7 @@ User.reopenClass(Singleton, {
}, },
createAccount(attrs) { createAccount(attrs) {
return ajax("/users", { return ajax(userPath(), {
data: { data: {
name: attrs.accountName, name: attrs.accountName,
email: attrs.accountEmail, email: attrs.accountEmail,

View File

@ -7,6 +7,7 @@ import Category from 'discourse/models/category';
import mobile from 'discourse/lib/mobile'; import mobile from 'discourse/lib/mobile';
import { findAll } from 'discourse/models/login-method'; import { findAll } from 'discourse/models/login-method';
import { getOwner } from 'discourse-common/lib/get-owner'; import { getOwner } from 'discourse-common/lib/get-owner';
import { userPath } from 'discourse/lib/url';
function unlessReadOnly(method, message) { function unlessReadOnly(method, message) {
return function() { return function() {
@ -23,7 +24,7 @@ const ApplicationRoute = Discourse.Route.extend(OpenComposer, {
actions: { actions: {
toggleAnonymous() { toggleAnonymous() {
ajax("/users/toggle-anon", {method: 'POST'}).then(() => { ajax(userPath("toggle-anon"), {method: 'POST'}).then(() => {
window.location.reload(); window.location.reload();
}); });
}, },

View File

@ -1,5 +1,6 @@
import PreloadStore from 'preload-store'; import PreloadStore from 'preload-store';
import { ajax } from 'discourse/lib/ajax'; import { ajax } from 'discourse/lib/ajax';
import { userPath } from 'discourse/lib/url';
export default Discourse.Route.extend({ export default Discourse.Route.extend({
titleToken() { titleToken() {
@ -15,7 +16,7 @@ export default Discourse.Route.extend({
afterModel(model) { afterModel(model) {
// confirm token here so email clients who crawl URLs don't invalidate the link // confirm token here so email clients who crawl URLs don't invalidate the link
if (model) { if (model) {
return ajax({ url: `/users/confirm-email-token/${model.token}.json`, dataType: 'json' }); return ajax({ url: userPath(`confirm-email-token/${model.token}.json`), dataType: 'json' });
} }
} }
}); });

View File

@ -3,12 +3,13 @@ import { avatarFor } from 'discourse/widgets/post';
import { iconNode } from 'discourse/helpers/fa-icon-node'; import { iconNode } from 'discourse/helpers/fa-icon-node';
import { h } from 'virtual-dom'; import { h } from 'virtual-dom';
import { dateNode } from 'discourse/helpers/node'; import { dateNode } from 'discourse/helpers/node';
import { userPath } from 'discourse/lib/url';
export function avatarAtts(user) { export function avatarAtts(user) {
return { template: user.avatar_template, return { template: user.avatar_template,
username: user.username, username: user.username,
post_url: user.post_url, post_url: user.post_url,
url: Discourse.getURL('/users/') + user.username_lower }; url: userPath(user.username_lower) };
} }
createWidget('small-user-list', { createWidget('small-user-list', {

View File

@ -2,6 +2,7 @@ import { createWidget, applyDecorators } from 'discourse/widgets/widget';
import { h } from 'virtual-dom'; import { h } from 'virtual-dom';
import DiscourseURL from 'discourse/lib/url'; import DiscourseURL from 'discourse/lib/url';
import { ajax } from 'discourse/lib/ajax'; import { ajax } from 'discourse/lib/ajax';
import { userPath } from 'discourse/lib/url';
const flatten = array => [].concat.apply([], array); const flatten = array => [].concat.apply([], array);
@ -19,7 +20,7 @@ createWidget('priority-faq-link', {
click(e) { click(e) {
e.preventDefault(); e.preventDefault();
if (this.siteSettings.faq_url === this.attrs.href) { if (this.siteSettings.faq_url === this.attrs.href) {
ajax("/users/read-faq", { method: "POST" }).then(() => { ajax(userPath("read-faq"), { method: "POST" }).then(() => {
this.currentUser.set('read_faq', true); this.currentUser.set('read_faq', true);
DiscourseURL.routeToTag($(e.target).closest('a')[0]); DiscourseURL.routeToTag($(e.target).closest('a')[0]);
}); });

View File

@ -6,6 +6,7 @@ import { h } from 'virtual-dom';
import { emojiUnescape } from 'discourse/lib/text'; import { emojiUnescape } from 'discourse/lib/text';
import { postUrl, escapeExpression } from 'discourse/lib/utilities'; import { postUrl, escapeExpression } from 'discourse/lib/utilities';
import { setTransientHeader } from 'discourse/lib/ajax'; import { setTransientHeader } from 'discourse/lib/ajax';
import { userPath } from 'discourse/lib/url';
const LIKED_TYPE = 5; const LIKED_TYPE = 5;
const INVITED_TYPE = 8; const INVITED_TYPE = 8;
@ -45,11 +46,11 @@ createWidget('notification-item', {
} }
if (attrs.notification_type === INVITED_TYPE) { if (attrs.notification_type === INVITED_TYPE) {
return Discourse.getURL('/users/' + data.display_username); return userPath(data.display_username);
} }
if (data.group_id) { if (data.group_id) {
return Discourse.getURL('/users/' + data.username + '/messages/group/' + data.group_name); return userPath(data.username + '/messages/group/' + data.group_name);
} }
}, },

View File

@ -38,7 +38,7 @@ export function setup(helper) {
const type = mentionLookup && mentionLookup(name); const type = mentionLookup && mentionLookup(name);
if (type === "user") { if (type === "user") {
return ['a', {'class': 'mention', href: opts.getURL("/users/") + name.toLowerCase()}, mention]; return ['a', {'class': 'mention', href: opts.getURL("/u/") + name.toLowerCase()}, mention];
} else if (type === "group") { } else if (type === "group") {
return ['a', {'class': 'mention-group', href: opts.getURL("/groups/") + name}, mention]; return ['a', {'class': 'mention-group', href: opts.getURL("/groups/") + name}, mention];
} else { } else {

View File

@ -124,7 +124,8 @@ class PostAnalyzer
def link_is_a_mention?(l) def link_is_a_mention?(l)
html_class = l['class'] html_class = l['class']
return false if html_class.blank? return false if html_class.blank?
html_class.to_s['mention'] && l['href'].to_s[/^\/users\//] href = l['href'].to_s
html_class.to_s['mention'] && href[/^\/u\//] || href[/^\/users\//]
end end
end end

View File

@ -19,7 +19,7 @@
(function() { (function() {
function activateAccount() { function activateAccount() {
$('#activate-account-button').prop('disabled', true); $('#activate-account-button').prop('disabled', true);
$.ajax("<%= path "/users/hp" %>").then(function(hp) { $.ajax("<%= path "/u/hp" %>").then(function(hp) {
$('#password_confirmation').val(hp.value); $('#password_confirmation').val(hp.value);
$('#challenge').val(hp.challenge.split("").reverse().join("")); $('#challenge').val(hp.challenge.split("").reverse().join(""));
$('#activate-account-form').submit(); $('#activate-account-form').submit();

View File

@ -305,6 +305,7 @@ Discourse::Application.routes.draw do
get 'u/check_username' => 'users#check_username' get 'u/check_username' => 'users#check_username'
get 'u/is_local_username' => 'users#is_local_username' get 'u/is_local_username' => 'users#is_local_username'
post 'u' => 'users#create' post 'u' => 'users#create'
get "u/hp" => "users#get_honeypot_value"
get "u/admin-login" => "users#admin_login" get "u/admin-login" => "users#admin_login"
put "u/admin-login" => "users#admin_login" put "u/admin-login" => "users#admin_login"
@ -320,7 +321,6 @@ Discourse::Application.routes.draw do
get "u/activate-account/:token" => "users#activate_account" get "u/activate-account/:token" => "users#activate_account"
put "u/activate-account/:token" => "users#perform_account_activation", as: 'perform_activate_account' put "u/activate-account/:token" => "users#perform_account_activation", as: 'perform_activate_account'
get "u/authorize-email/:token" => "users_email#confirm" get "u/authorize-email/:token" => "users_email#confirm"
get "u/hp" => "users#get_honeypot_value"
get "u/:username/private-messages" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT} get "u/:username/private-messages" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT}
get "u/:username/private-messages/:filter" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT} get "u/:username/private-messages/:filter" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT}
@ -377,6 +377,7 @@ Discourse::Application.routes.draw do
get 'users/check_username' => 'users#check_username' get 'users/check_username' => 'users#check_username'
get 'users/is_local_username' => 'users#is_local_username' get 'users/is_local_username' => 'users#is_local_username'
post 'users' => 'users#create' post 'users' => 'users#create'
get "users/hp" => "users#get_honeypot_value"
get "users/admin-login" => "users#admin_login" get "users/admin-login" => "users#admin_login"
put "users/admin-login" => "users#admin_login" put "users/admin-login" => "users#admin_login"

View File

@ -952,7 +952,7 @@ describe Post do
it "will unhide the post but will keep the topic invisible/unlisted" do it "will unhide the post but will keep the topic invisible/unlisted" do
hidden_topic = Fabricate(:topic, visible: false) hidden_topic = Fabricate(:topic, visible: false)
first_post = create_post(topic: hidden_topic) _first_post = create_post(topic: hidden_topic)
second_post = create_post(topic: hidden_topic) second_post = create_post(topic: hidden_topic)
second_post.update_columns(hidden: true, hidden_at: Time.now, hidden_reason_id: 1) second_post.update_columns(hidden: true, hidden_at: Time.now, hidden_reason_id: 1)

View File

@ -12,11 +12,11 @@ acceptance("Password Reset", {
]; ];
}; };
server.get('/users/confirm-email-token/myvalidtoken.json', () => { //eslint-disable-line server.get('/u/confirm-email-token/myvalidtoken.json', () => { //eslint-disable-line
return response({success: "OK"}); return response({success: "OK"});
}); });
server.put('/users/password-reset/myvalidtoken.json', request => { //eslint-disable-line server.put('/u/password-reset/myvalidtoken.json', request => { //eslint-disable-line
const body = parsePostData(request.requestBody); const body = parsePostData(request.requestBody);
if (body.password === "jonesyAlienSlayer") { if (body.password === "jonesyAlienSlayer") {
return response({success: false, errors: {password: ["is the name of your cat"]}}); return response({success: false, errors: {password: ["is the name of your cat"]}});

View File

@ -14,7 +14,7 @@ acceptance("Search - Full Page", {
return response({results: [{text: 'monkey', count: 1}]}); return response({results: [{text: 'monkey', count: 1}]});
}); });
server.get('/users/search/users', () => { //eslint-disable-line server.get('/u/search/users', () => { //eslint-disable-line
return response({users: [{username: "admin", name: "admin", return response({users: [{username: "admin", name: "admin",
avatar_template: "/images/avatar.png"}]}); avatar_template: "/images/avatar.png"}]});
}); });

File diff suppressed because one or more lines are too long

View File

@ -63,17 +63,17 @@ export default function() {
}] }); }] });
}); });
this.get(`/users/eviltrout/emails.json`, () => { this.get(`/u/eviltrout/emails.json`, () => {
return response({ email: 'eviltrout@example.com' }); return response({ email: 'eviltrout@example.com' });
}); });
this.get('/users/eviltrout.json', () => { this.get('/u/eviltrout.json', () => {
const json = fixturesByUrl['/users/eviltrout.json']; const json = fixturesByUrl['/u/eviltrout.json'];
json.user.can_edit = loggedIn(); json.user.can_edit = loggedIn();
return response(json); return response(json);
}); });
this.get('/users/eviltrout/summary.json', () => { this.get('/u/eviltrout/summary.json', () => {
return response({ return response({
user_summary: { user_summary: {
topics: [], topics: [],
@ -85,13 +85,13 @@ export default function() {
}); });
}); });
this.get('/users/eviltrout/invited_count.json', () => { this.get('/u/eviltrout/invited_count.json', () => {
return response({ return response({
"counts": { "pending": 1, "redeemed": 0, "total": 0 } "counts": { "pending": 1, "redeemed": 0, "total": 0 }
}); });
}); });
this.get('/users/eviltrout/invited.json', () => { this.get('/u/eviltrout/invited.json', () => {
return response({ "invites": [ {id: 1} ] }); return response({ "invites": [ {id: 1} ] });
}); });
@ -113,7 +113,7 @@ export default function() {
return response({}); return response({});
}); });
this.put('/users/eviltrout.json', () => response({ user: {} })); this.put('/u/eviltrout.json', () => response({ user: {} }));
this.get("/t/280.json", () => response(fixturesByUrl['/t/280/1.json'])); this.get("/t/280.json", () => response(fixturesByUrl['/t/280/1.json']));
this.get("/t/28830.json", () => response(fixturesByUrl['/t/28830/1.json'])); this.get("/t/28830.json", () => response(fixturesByUrl['/t/28830/1.json']));
@ -134,7 +134,7 @@ export default function() {
this.delete('/draft.json', success); this.delete('/draft.json', success);
this.post('/draft.json', success); this.post('/draft.json', success);
this.get('/users/:username/staff-info.json', () => response({})); this.get('/u/:username/staff-info.json', () => response({}));
this.get('/post_action_users', () => { this.get('/post_action_users', () => {
return response({ return response({
@ -198,9 +198,9 @@ export default function() {
return response(400, {error: 'invalid login'}); return response(400, {error: 'invalid login'});
}); });
this.post('/users/action/send_activation_email', success); this.post('/u/action/send_activation_email', success);
this.get('/users/hp.json', function() { this.get('/u/hp.json', function() {
return response({"value":"32faff1b1ef1ac3","challenge":"61a3de0ccf086fb9604b76e884d75801"}); return response({"value":"32faff1b1ef1ac3","challenge":"61a3de0ccf086fb9604b76e884d75801"});
}); });
@ -208,14 +208,14 @@ export default function() {
return response({"csrf":"mgk906YLagHo2gOgM1ddYjAN4hQolBdJCqlY6jYzAYs="}); return response({"csrf":"mgk906YLagHo2gOgM1ddYjAN4hQolBdJCqlY6jYzAYs="});
}); });
this.get('/users/check_username', function(request) { this.get('/u/check_username', function(request) {
if (request.queryParams.username === 'taken') { if (request.queryParams.username === 'taken') {
return response({available: false, suggestion: 'nottaken'}); return response({available: false, suggestion: 'nottaken'});
} }
return response({available: true}); return response({available: true});
}); });
this.post('/users', () => response({success: true})); this.post('/u', () => response({success: true}));
this.get('/login.html', () => [200, {}, 'LOGIN PAGE']); this.get('/login.html', () => [200, {}, 'LOGIN PAGE']);

View File

@ -227,7 +227,7 @@ test("Mentions", function() {
const alwaysTrue = { mentionLookup: (function() { return "user"; }) }; const alwaysTrue = { mentionLookup: (function() { return "user"; }) };
cookedOptions("Hello @sam", alwaysTrue, cookedOptions("Hello @sam", alwaysTrue,
"<p>Hello <a class=\"mention\" href=\"/users/sam\">@sam</a></p>", "<p>Hello <a class=\"mention\" href=\"/u/sam\">@sam</a></p>",
"translates mentions to links"); "translates mentions to links");
cooked("[@codinghorror](https://twitter.com/codinghorror)", cooked("[@codinghorror](https://twitter.com/codinghorror)",
@ -303,11 +303,11 @@ test("Mentions", function() {
"handles mentions separated by a slash."); "handles mentions separated by a slash.");
cookedOptions("@eviltrout", alwaysTrue, cookedOptions("@eviltrout", alwaysTrue,
"<p><a class=\"mention\" href=\"/users/eviltrout\">@eviltrout</a></p>", "<p><a class=\"mention\" href=\"/u/eviltrout\">@eviltrout</a></p>",
"it doesn't onebox mentions"); "it doesn't onebox mentions");
cookedOptions("<small>a @sam c</small>", alwaysTrue, cookedOptions("<small>a @sam c</small>", alwaysTrue,
"<p><small>a <a class=\"mention\" href=\"/users/sam\">@sam</a> c</small></p>", "<p><small>a <a class=\"mention\" href=\"/u/sam\">@sam</a> c</small></p>",
"it allows mentions within HTML tags"); "it allows mentions within HTML tags");
}); });

View File

@ -1,4 +1,4 @@
import DiscourseURL from 'discourse/lib/url'; import { default as DiscourseURL, userPath } from 'discourse/lib/url';
module("lib:url"); module("lib:url");
@ -25,3 +25,9 @@ test("isInternal on subfolder install", function() {
not(DiscourseURL.isInternal("http://eviltrout.com/tophat"), "a url on the same host but on a different folder is not internal"); not(DiscourseURL.isInternal("http://eviltrout.com/tophat"), "a url on the same host but on a different folder is not internal");
ok(DiscourseURL.isInternal("http://eviltrout.com/forum/moustache"), "a url on the same host and on the same folder is internal"); ok(DiscourseURL.isInternal("http://eviltrout.com/forum/moustache"), "a url on the same host and on the same folder is internal");
}); });
test("userPath", assert => {
assert.equal(userPath(), '/u');
assert.equal(userPath('eviltrout'), '/u/eviltrout');
assert.equal(userPath('hp.json'), '/u/hp.json');
});

View File

@ -10,7 +10,7 @@ module("lib:user-search", {
]; ];
}; };
server.get('/users/search/users', () => { //eslint-disable-line server.get('/u/search/users', () => { //eslint-disable-line
return response( return response(
{ {
users: [ users: [