Merge branch 'master' of github.com:discourse/discourse
This commit is contained in:
commit
fbb6eba252
|
@ -6,11 +6,6 @@ export default Ember.Component.extend({
|
|||
@computed('field')
|
||||
inputId(field) { return field.dasherize(); },
|
||||
|
||||
@computed('placeholder')
|
||||
placeholderValue(placeholder) {
|
||||
return placeholder ? I18n.t(placeholder) : null;
|
||||
},
|
||||
|
||||
@computed('field')
|
||||
translationKey(field) { return `admin.embedding.${field}`; },
|
||||
|
||||
|
@ -21,7 +16,7 @@ export default Ember.Component.extend({
|
|||
checked: {
|
||||
get(value) { return !!value; },
|
||||
set(value) {
|
||||
this.set('value', value);
|
||||
this.set('value', value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ export default Ember.Controller.extend({
|
|||
const model = this.get('model');
|
||||
|
||||
this.set('loading', true);
|
||||
Discourse.EmailPreview.findDigest(this.get('lastSeen')).then(email => {
|
||||
Discourse.EmailPreview.findDigest(this.get('lastSeen'), this.get('username')).then(email => {
|
||||
model.setProperties(email.getProperties('html_content', 'text_content'));
|
||||
this.set('loading', false);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
users: null,
|
||||
groupId: null,
|
||||
saving: false,
|
||||
|
||||
@computed('saving', 'users', 'groupId')
|
||||
buttonDisabled(saving, users, groupId) {
|
||||
return saving || !groupId || !users || !users.length;
|
||||
},
|
||||
|
||||
actions: {
|
||||
addToGroup() {
|
||||
if (this.get('saving')) { return; }
|
||||
|
||||
const users = this.get('users').split("\n")
|
||||
.uniq()
|
||||
.reject(x => x.length === 0);
|
||||
|
||||
this.set('saving', true);
|
||||
Discourse.ajax('/admin/groups/bulk', {
|
||||
data: { users, group_id: this.get('groupId') },
|
||||
method: 'PUT'
|
||||
}).then(() => {
|
||||
this.transitionToRoute('adminGroups.bulkComplete');
|
||||
}).catch(popupAjaxError).finally(() => {
|
||||
this.set('saving', false);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
});
|
|
@ -288,10 +288,7 @@ const AdminUser = Discourse.User.extend({
|
|||
data: { username: this.get('username') }
|
||||
}).then(function() {
|
||||
bootbox.alert( I18n.t('admin.user.activation_email_sent') );
|
||||
}).catch(function(e) {
|
||||
var error = I18n.t('admin.user.send_activation_email_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
}).catch(popupAjaxError);
|
||||
},
|
||||
|
||||
anonymizeForbidden: Em.computed.not("can_be_anonymized"),
|
||||
|
|
|
@ -9,18 +9,20 @@
|
|||
Discourse.EmailPreview = Discourse.Model.extend({});
|
||||
|
||||
Discourse.EmailPreview.reopenClass({
|
||||
findDigest: function(lastSeenAt) {
|
||||
findDigest: function(lastSeenAt, username) {
|
||||
|
||||
if (Em.isEmpty(lastSeenAt)) {
|
||||
lastSeenAt = moment().subtract(7, 'days').format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
if (Em.isEmpty(username)) {
|
||||
username = Discourse.User.current().username;
|
||||
}
|
||||
|
||||
return Discourse.ajax("/admin/email/preview-digest.json", {
|
||||
data: {last_seen_at: lastSeenAt}
|
||||
data: { last_seen_at: lastSeenAt, username: username }
|
||||
}).then(function (result) {
|
||||
return Discourse.EmailPreview.create(result);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import Group from 'discourse/models/group';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
model() {
|
||||
return Group.findAll().then(groups => {
|
||||
return groups.filter(g => !g.get('automatic'));
|
||||
});
|
||||
},
|
||||
|
||||
setupController(controller, groups) {
|
||||
controller.setProperties({ groups, groupId: null, users: null });
|
||||
}
|
||||
});
|
|
@ -49,6 +49,8 @@ export default {
|
|||
});
|
||||
|
||||
this.resource('adminGroups', { path: '/groups' }, function() {
|
||||
this.route('bulk');
|
||||
this.route('bulkComplete', { path: 'bulk-complete' });
|
||||
this.resource('adminGroupsType', { path: '/:type' }, function() {
|
||||
this.resource('adminGroup', { path: '/:name' });
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
</label>
|
||||
{{else}}
|
||||
<label for={{inputId}}>{{i18n translationKey}}</label>
|
||||
{{input value=value id=inputId placeholder=placeholderValue}}
|
||||
{{input value=value id=inputId placeholder=placeholder}}
|
||||
{{/if}}
|
||||
|
||||
<div class='clearfix'></div>
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
<div class='span7 controls'>
|
||||
<label for='last-seen'>{{i18n 'admin.email.last_seen_user'}}</label>
|
||||
{{input type="date" value=lastSeen id="last-seen"}}
|
||||
<label>{{i18n 'admin.email.user'}}:</label>
|
||||
{{user-selector single="true" usernames=username}}
|
||||
<button class='btn' {{action "refresh"}}>{{i18n 'admin.email.refresh'}}</button>
|
||||
<div class="toggle">
|
||||
<label>{{i18n 'admin.email.format'}}</label>
|
||||
|
|
|
@ -48,11 +48,11 @@
|
|||
|
||||
{{embedding-setting field="embed_whitelist_selector"
|
||||
value=embedding.embed_whitelist_selector
|
||||
placeholder="admin.embedding.whitelist_example"}}
|
||||
placeholder="article, #story, .post"}}
|
||||
|
||||
{{embedding-setting field="embed_blacklist_selector"
|
||||
value=embedding.embed_blacklist_selector
|
||||
placeholder="admin.embedding.blacklist_example"}}
|
||||
placeholder=".ad-unit, header"}}
|
||||
</div>
|
||||
|
||||
<div class='embedding-secondary'>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<p>{{i18n "admin.groups.bulk_complete"}}</p>
|
|
@ -0,0 +1,19 @@
|
|||
<div class='groups-bulk'>
|
||||
<p>{{i18n "admin.groups.bulk_paste"}}</p>
|
||||
|
||||
<div class='control'>
|
||||
{{textarea value=users class="paste-users"}}
|
||||
</div>
|
||||
|
||||
<div class='control'>
|
||||
{{combo-box content=groups valueAttribute="id" value=groupId none="admin.groups.bulk_select"}}
|
||||
</div>
|
||||
|
||||
<div class='control'>
|
||||
{{d-button disabled=buttonDisabled
|
||||
class="btn-primary"
|
||||
action="addToGroup"
|
||||
icon="plus"
|
||||
label="admin.groups.bulk"}}
|
||||
</div>
|
||||
</div>
|
|
@ -1,6 +1,7 @@
|
|||
{{#admin-nav}}
|
||||
{{nav-item route='adminGroupsType' routeParam='custom' label='admin.groups.custom'}}
|
||||
{{nav-item route='adminGroupsType' routeParam='automatic' label='admin.groups.automatic'}}
|
||||
{{nav-item route='adminGroups.bulk' label='admin.groups.bulk'}}
|
||||
{{/admin-nav}}
|
||||
|
||||
<div class="admin-container">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<p class='description'>{{model.description}}</p>
|
||||
|
||||
{{#if model.markdown}}
|
||||
{{pagedown-editor value=model.value}}
|
||||
{{d-editor value=model.value}}
|
||||
{{/if}}
|
||||
{{#if model.plainText}}
|
||||
{{textarea value=model.value class="plain"}}
|
||||
|
|
|
@ -14,7 +14,7 @@ window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, {
|
|||
if (!url) return url;
|
||||
|
||||
// if it's a non relative URL, return it.
|
||||
if (!/^\/[^\/]/.test(url)) return url;
|
||||
if (url !== '/' && !/^\/[^\/]/.test(url)) return url;
|
||||
|
||||
var u = Discourse.BaseUri === undefined ? "/" : Discourse.BaseUri;
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
import { observes, on } from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNameBindings: [':d-editor-modal', 'hidden'],
|
||||
|
||||
@observes('hidden')
|
||||
_hiddenChanged() {
|
||||
if (!this.get('hidden')) {
|
||||
Ember.run.scheduleOnce('afterRender', () => {
|
||||
const $modal = this.$();
|
||||
const $parent = this.$().closest('.d-editor');
|
||||
const w = $parent.width();
|
||||
const h = $parent.height();
|
||||
$modal.css({ left: (w / 2) - ($modal.outerWidth() / 2) });
|
||||
parent.$('.d-editor-overlay').removeClass('hidden').css({ width: w, height: h});
|
||||
this.$('input').focus();
|
||||
});
|
||||
} else {
|
||||
parent.$('.d-editor-overlay').addClass('hidden');
|
||||
}
|
||||
},
|
||||
|
||||
@on('didInsertElement')
|
||||
_listenKeys() {
|
||||
this.$().on('keydown.d-modal', key => {
|
||||
if (this.get('hidden')) { return; }
|
||||
|
||||
if (key.keyCode === 27) {
|
||||
this.send('cancel');
|
||||
}
|
||||
if (key.keyCode === 13) {
|
||||
this.send('ok');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@on('willDestroyElement')
|
||||
_stopListening() {
|
||||
this.$().off('keydown.d-modal');
|
||||
},
|
||||
|
||||
actions: {
|
||||
ok() {
|
||||
this.set('hidden', true);
|
||||
this.sendAction('okAction');
|
||||
},
|
||||
|
||||
cancel() {
|
||||
this.set('hidden', true);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,263 @@
|
|||
import loadScript from 'discourse/lib/load-script';
|
||||
import { default as property, on } from 'ember-addons/ember-computed-decorators';
|
||||
import { showSelector } from "discourse/lib/emoji/emoji-toolbar";
|
||||
|
||||
// Our head can be a static string or a function that returns a string
|
||||
// based on input (like for numbered lists).
|
||||
function getHead(head, prev) {
|
||||
if (typeof head === "string") {
|
||||
return [head, head.length];
|
||||
} else {
|
||||
return getHead(head(prev));
|
||||
}
|
||||
}
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['d-editor'],
|
||||
ready: false,
|
||||
insertLinkHidden: true,
|
||||
link: '',
|
||||
lastSel: null,
|
||||
|
||||
@on('didInsertElement')
|
||||
_loadSanitizer() {
|
||||
this._applyEmojiAutocomplete();
|
||||
loadScript('defer/html-sanitizer-bundle').then(() => this.set('ready', true));
|
||||
},
|
||||
|
||||
@property('ready', 'value')
|
||||
preview(ready, value) {
|
||||
if (!ready) { return; }
|
||||
|
||||
const text = Discourse.Dialect.cook(value || "", {sanitize: true});
|
||||
return text ? text : "";
|
||||
},
|
||||
|
||||
_applyEmojiAutocomplete() {
|
||||
if (!this.siteSettings.enable_emoji) { return; }
|
||||
|
||||
const container = this.container;
|
||||
const template = container.lookup('template:emoji-selector-autocomplete.raw');
|
||||
const self = this;
|
||||
|
||||
this.$('.d-editor-input').autocomplete({
|
||||
template: template,
|
||||
key: ":",
|
||||
|
||||
transformComplete(v) {
|
||||
if (v.code) {
|
||||
return `${v.code}:`;
|
||||
} else {
|
||||
showSelector({
|
||||
appendTo: self.$(),
|
||||
container,
|
||||
onSelect: title => self._addText(`${title}:`)
|
||||
});
|
||||
return "";
|
||||
}
|
||||
},
|
||||
|
||||
dataSource(term) {
|
||||
return new Ember.RSVP.Promise(resolve => {
|
||||
const full = `:${term}`;
|
||||
term = term.toLowerCase();
|
||||
|
||||
if (term === "") {
|
||||
return resolve(["smile", "smiley", "wink", "sunny", "blush"]);
|
||||
}
|
||||
|
||||
if (Discourse.Emoji.translations[full]) {
|
||||
return resolve([Discourse.Emoji.translations[full]]);
|
||||
}
|
||||
|
||||
const options = Discourse.Emoji.search(term, {maxResults: 5});
|
||||
|
||||
return resolve(options);
|
||||
}).then(list => list.map(code => {
|
||||
return {code, src: Discourse.Emoji.urlFor(code)};
|
||||
})).then(list => {
|
||||
if (list.length) {
|
||||
list.push({ label: I18n.t("composer.more_emoji") });
|
||||
}
|
||||
return list;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_getSelected() {
|
||||
if (!this.get('ready')) { return; }
|
||||
|
||||
const textarea = this.$('textarea.d-editor-input')[0];
|
||||
let start = textarea.selectionStart;
|
||||
let end = textarea.selectionEnd;
|
||||
|
||||
if (start === end) {
|
||||
start = end = textarea.value.length;
|
||||
}
|
||||
|
||||
const value = textarea.value.substring(start, end);
|
||||
const pre = textarea.value.slice(0, start);
|
||||
const post = textarea.value.slice(end);
|
||||
|
||||
return { start, end, value, pre, post };
|
||||
},
|
||||
|
||||
_selectText(from, length) {
|
||||
Ember.run.scheduleOnce('afterRender', () => {
|
||||
const textarea = this.$('textarea.d-editor-input')[0];
|
||||
textarea.focus();
|
||||
textarea.selectionStart = from;
|
||||
textarea.selectionEnd = textarea.selectionStart + length;
|
||||
});
|
||||
},
|
||||
|
||||
_applySurround(head, tail, exampleKey) {
|
||||
const sel = this._getSelected();
|
||||
const pre = sel.pre;
|
||||
const post = sel.post;
|
||||
|
||||
const tlen = tail.length;
|
||||
if (sel.start === sel.end) {
|
||||
if (tlen === 0) { return; }
|
||||
|
||||
const [hval, hlen] = getHead(head);
|
||||
const example = I18n.t(`composer.${exampleKey}`);
|
||||
this.set('value', `${pre}${hval}${example}${tail}${post}`);
|
||||
this._selectText(pre.length + hlen, example.length);
|
||||
} else {
|
||||
const lines = sel.value.split("\n");
|
||||
|
||||
let [hval, hlen] = getHead(head);
|
||||
if (lines.length === 1 && pre.slice(-tlen) === tail && post.slice(0, hlen) === hval) {
|
||||
this.set('value', `${pre.slice(0, -hlen)}${sel.value}${post.slice(tlen)}`);
|
||||
this._selectText(sel.start - hlen, sel.value.length);
|
||||
} else {
|
||||
const contents = lines.map(l => {
|
||||
if (l.length === 0) { return l; }
|
||||
|
||||
if (l.slice(0, hlen) === hval && tlen === 0 || l.slice(-tlen) === tail) {
|
||||
if (tlen === 0) {
|
||||
const result = l.slice(hlen);
|
||||
[hval, hlen] = getHead(head, hval);
|
||||
return result;
|
||||
} else if (l.slice(-tlen) === tail) {
|
||||
const result = l.slice(hlen, -tlen);
|
||||
[hval, hlen] = getHead(head, hval);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
const result = `${hval}${l}${tail}`;
|
||||
[hval, hlen] = getHead(head, hval);
|
||||
return result;
|
||||
}).join("\n");
|
||||
|
||||
this.set('value', `${pre}${contents}${post}`);
|
||||
if (lines.length === 1 && tlen > 0) {
|
||||
this._selectText(sel.start + hlen, contents.length - hlen - hlen);
|
||||
} else {
|
||||
this._selectText(sel.start, contents.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_applyList(head, exampleKey) {
|
||||
const sel = this._getSelected();
|
||||
if (sel.value.indexOf("\n") !== -1) {
|
||||
this._applySurround(head, '', exampleKey);
|
||||
} else {
|
||||
|
||||
const [hval, hlen] = getHead(head);
|
||||
if (sel.start === sel.end) {
|
||||
sel.value = I18n.t(`composer.${exampleKey}`);
|
||||
}
|
||||
|
||||
const trimmedPre = sel.pre.trim();
|
||||
const number = (sel.value.indexOf(hval) === 0) ? sel.value.slice(hlen) : `${hval}${sel.value}`;
|
||||
const preLines = trimmedPre.length ? `${trimmedPre}\n\n` : "";
|
||||
|
||||
const trimmedPost = sel.post.trim();
|
||||
const post = trimmedPost.length ? `\n\n${trimmedPost}` : trimmedPost;
|
||||
|
||||
this.set('value', `${preLines}${number}${post}`);
|
||||
this._selectText(preLines.length, number.length);
|
||||
}
|
||||
},
|
||||
|
||||
_addText(text, sel) {
|
||||
sel = sel || this._getSelected();
|
||||
const insert = `${sel.pre}${text}`;
|
||||
this.set('value', `${insert}${sel.post}`);
|
||||
this._selectText(insert.length, 0);
|
||||
},
|
||||
|
||||
actions: {
|
||||
bold() {
|
||||
this._applySurround('**', '**', 'bold_text');
|
||||
},
|
||||
|
||||
italic() {
|
||||
this._applySurround('*', '*', 'italic_text');
|
||||
},
|
||||
|
||||
showLinkModal() {
|
||||
this._lastSel = this._getSelected();
|
||||
this.set('insertLinkHidden', false);
|
||||
},
|
||||
|
||||
insertLink() {
|
||||
const link = this.get('link');
|
||||
|
||||
if (Ember.isEmpty(link)) { return; }
|
||||
const m = / "([^"]+)"/.exec(link);
|
||||
if (m && m.length === 2) {
|
||||
const description = m[1];
|
||||
const remaining = link.replace(m[0], '');
|
||||
this._addText(`[${description}](${remaining})`, this._lastSel);
|
||||
} else {
|
||||
this._addText(`[${link}](${link})`, this._lastSel);
|
||||
}
|
||||
|
||||
this.set('link', '');
|
||||
},
|
||||
|
||||
code() {
|
||||
const sel = this._getSelected();
|
||||
if (sel.value.indexOf("\n") !== -1) {
|
||||
this._applySurround(' ', '', 'code_text');
|
||||
} else {
|
||||
this._applySurround('`', '`', 'code_text');
|
||||
}
|
||||
},
|
||||
|
||||
quote() {
|
||||
this._applySurround('> ', "", 'code_text');
|
||||
},
|
||||
|
||||
bullet() {
|
||||
this._applyList('* ', 'list_item');
|
||||
},
|
||||
|
||||
list() {
|
||||
this._applyList(i => !i ? "1. " : `${parseInt(i) + 1}. `, 'list_item');
|
||||
},
|
||||
|
||||
heading() {
|
||||
this._applyList('## ', 'heading_text');
|
||||
},
|
||||
|
||||
rule() {
|
||||
this._addText("\n\n----------\n");
|
||||
},
|
||||
|
||||
emoji() {
|
||||
showSelector({
|
||||
appendTo: this.$(),
|
||||
container: this.container,
|
||||
onSelect: title => this._addText(`:${title}:`)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
|
@ -56,6 +56,13 @@ export default Ember.Component.extend({
|
|||
});
|
||||
},
|
||||
|
||||
@computed()
|
||||
showUserDirectoryLink() {
|
||||
if (!this.siteSettings.enable_user_directory) return false;
|
||||
if (this.siteSettings.hide_user_profiles_from_public && !this.currentUser) return false;
|
||||
return true;
|
||||
},
|
||||
|
||||
actions: {
|
||||
keyboardShortcuts() {
|
||||
this.sendAction('showKeyboardAction');
|
||||
|
|
|
@ -4,9 +4,9 @@ export default Ember.Component.extend({
|
|||
tagName: 'li',
|
||||
classNameBindings: [':header-dropdown-toggle', 'active'],
|
||||
|
||||
@computed('showUser')
|
||||
href(showUser) {
|
||||
return showUser ? this.currentUser.get('path') : '';
|
||||
@computed('showUser', 'path')
|
||||
href(showUser, path) {
|
||||
return showUser ? this.currentUser.get('path') : Discourse.getURL(path);
|
||||
},
|
||||
|
||||
active: Ember.computed.alias('toggleVisible'),
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
import { observes, on } from 'ember-addons/ember-computed-decorators';
|
||||
import loadScript from 'discourse/lib/load-script';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNameBindings: [':pagedown-editor'],
|
||||
|
||||
@on("didInsertElement")
|
||||
_initializeWmd() {
|
||||
loadScript('defer/html-sanitizer-bundle').then(() => {
|
||||
this.$('.wmd-input').data('init', true);
|
||||
this._editor = Discourse.Markdown.createEditor({ containerElement: this.element });
|
||||
this._editor.run();
|
||||
Ember.run.scheduleOnce('afterRender', this, this._refreshPreview);
|
||||
});
|
||||
},
|
||||
|
||||
@observes("value")
|
||||
observeValue() {
|
||||
Ember.run.scheduleOnce('afterRender', this, this._refreshPreview);
|
||||
},
|
||||
|
||||
_refreshPreview() {
|
||||
this._editor.refreshPreview();
|
||||
}
|
||||
});
|
|
@ -9,7 +9,7 @@ export default Ember.Component.extend({
|
|||
var self = this;
|
||||
bootbox.dialog(I18n.t("private_message_info.remove_allowed_user", {name: user.get('username')}), [
|
||||
{label: I18n.t("no_value"),
|
||||
'class': 'btn-danger rightg'},
|
||||
'class': 'btn-danger right'},
|
||||
{label: I18n.t("yes_value"),
|
||||
'class': 'btn-primary',
|
||||
callback: function() {
|
||||
|
|
|
@ -18,36 +18,6 @@ export default Ember.Component.extend({
|
|||
}
|
||||
},
|
||||
|
||||
signupMethodsTranslated: function() {
|
||||
const methods = Ember.get('Discourse.LoginMethod.all');
|
||||
const loginWithEmail = this.siteSettings.enable_local_logins;
|
||||
if (this.siteSettings.enable_sso) {
|
||||
return I18n.t('signup_cta.methods.sso');
|
||||
} else if (methods.length === 0) {
|
||||
if (loginWithEmail) {
|
||||
return I18n.t('signup_cta.methods.only_email');
|
||||
} else {
|
||||
return I18n.t('signup_cta.methods.unknown');
|
||||
}
|
||||
} else if (methods.length === 1) {
|
||||
let providerName = methods[0].name.capitalize();
|
||||
if (providerName === "Google_oauth2") {
|
||||
providerName = "Google";
|
||||
}
|
||||
if (loginWithEmail) {
|
||||
return I18n.t('signup_cta.methods.one_and_email', {provider: providerName});
|
||||
} else {
|
||||
return I18n.t('signup_cta.methods.only_other', {provider: providerName});
|
||||
}
|
||||
} else {
|
||||
if (loginWithEmail) {
|
||||
return I18n.t('signup_cta.methods.multiple', {count: methods.length});
|
||||
} else {
|
||||
return I18n.t('signup_cta.methods.multiple_no_email', {count: methods.length});
|
||||
}
|
||||
}
|
||||
}.property(),
|
||||
|
||||
_turnOffIfHidden: function() {
|
||||
if (this.session.get('hideSignupCta')) {
|
||||
this.session.set('showSignupCta', false);
|
||||
|
|
|
@ -21,8 +21,8 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
|||
},
|
||||
|
||||
@computed()
|
||||
allowImageUpload() {
|
||||
return Discourse.Utilities.allowsImages();
|
||||
allowAvatarUpload() {
|
||||
return this.siteSettings.allow_uploaded_avatars && Discourse.Utilities.allowsImages();
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import debounce from 'discourse/lib/debounce';
|
||||
import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
||||
import { setting } from 'discourse/lib/computed';
|
||||
import { on } from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Controller.extend(ModalFunctionality, {
|
||||
needs: ['login'],
|
||||
|
@ -78,10 +79,6 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
|||
|
||||
// Validate the name.
|
||||
nameValidation: function() {
|
||||
if (this.get('accountPasswordConfirm') === 0) {
|
||||
this.fetchConfirmationValue();
|
||||
}
|
||||
|
||||
if (Discourse.SiteSettings.full_name_required && Ember.isEmpty(this.get('accountName'))) {
|
||||
return Discourse.InputValidation.create({ failed: true });
|
||||
}
|
||||
|
@ -335,11 +332,11 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
|||
});
|
||||
}.property('accountPassword', 'rejectedPasswords.@each', 'accountUsername', 'accountEmail'),
|
||||
|
||||
@on('init')
|
||||
fetchConfirmationValue() {
|
||||
const createAccountController = this;
|
||||
return Discourse.ajax('/users/hp.json').then(function (json) {
|
||||
createAccountController.set('accountPasswordConfirm', json.value);
|
||||
createAccountController.set('accountChallenge', json.challenge.split("").reverse().join(""));
|
||||
return Discourse.ajax('/users/hp.json').then(json => {
|
||||
this.set('accountPasswordConfirm', json.value);
|
||||
this.set('accountChallenge', json.challenge.split("").reverse().join(""));
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ export var queryParams = {
|
|||
// Basic controller options
|
||||
var controllerOpts = {
|
||||
needs: ['discovery/topics'],
|
||||
queryParams: Ember.keys(queryParams)
|
||||
queryParams: Ember.keys(queryParams),
|
||||
};
|
||||
|
||||
// Aliases for the values
|
||||
|
|
|
@ -2,6 +2,7 @@ import DiscoveryController from 'discourse/controllers/discovery';
|
|||
import { queryParams } from 'discourse/controllers/discovery-sortable';
|
||||
import BulkTopicSelection from 'discourse/mixins/bulk-topic-selection';
|
||||
import { endWith } from 'discourse/lib/computed';
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
|
||||
const controllerOpts = {
|
||||
needs: ['discovery'],
|
||||
|
@ -66,10 +67,13 @@ const controllerOpts = {
|
|||
});
|
||||
},
|
||||
|
||||
|
||||
resetNew() {
|
||||
this.topicTrackingState.resetNew();
|
||||
Discourse.Topic.resetNew().then(() => this.send('refresh'));
|
||||
},
|
||||
|
||||
dismissReadPosts() {
|
||||
showModal('dismiss-read', { title: 'topics.bulk.dismiss_read' });
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ export default Ember.Controller.extend({
|
|||
|
||||
this.set("loading", true);
|
||||
|
||||
Discourse.Group.loadMembers(this.get("name"), this.get("model.members.length"), this.get("limit")).then(result => {
|
||||
Discourse.Group.loadMembers(this.get("model.name"), this.get("model.members.length"), this.get("limit")).then(result => {
|
||||
this.get("model.members").addObjects(result.members.map(member => Discourse.User.create(member)));
|
||||
this.setProperties({
|
||||
loading: false,
|
||||
|
|
|
@ -68,12 +68,12 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
|||
|
||||
// Show Groups? (add invited user to private group)
|
||||
showGroups: function() {
|
||||
return this.get('isAdmin') && (Discourse.Utilities.emailValid(this.get('emailOrUsername')) || this.get('isPrivateTopic') || !this.get('invitingToTopic')) && !Discourse.SiteSettings.enable_sso && !this.get('isMessage');
|
||||
return this.get('isAdmin') && (Discourse.Utilities.emailValid(this.get('emailOrUsername')) || this.get('isPrivateTopic') || !this.get('invitingToTopic')) && !Discourse.SiteSettings.enable_sso && Discourse.SiteSettings.enable_local_logins && !this.get('isMessage');
|
||||
}.property('isAdmin', 'emailOrUsername', 'isPrivateTopic', 'isMessage', 'invitingToTopic'),
|
||||
|
||||
// Instructional text for the modal.
|
||||
inviteInstructions: function() {
|
||||
if (Discourse.SiteSettings.enable_sso) {
|
||||
if (Discourse.SiteSettings.enable_sso || !Discourse.SiteSettings.enable_local_logins) {
|
||||
// inviting existing user when SSO enabled
|
||||
return I18n.t('topic.invite_reply.sso_enabled');
|
||||
} else if (this.get('isMessage')) {
|
||||
|
@ -128,7 +128,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
|||
}.property('isMessage'),
|
||||
|
||||
placeholderKey: function() {
|
||||
return Discourse.SiteSettings.enable_sso ?
|
||||
return (Discourse.SiteSettings.enable_sso || !Discourse.SiteSettings.enable_local_logins) ?
|
||||
'topic.invite_reply.username_placeholder' :
|
||||
'topic.invite_private.email_or_username_placeholder';
|
||||
}.property(),
|
||||
|
|
|
@ -78,9 +78,15 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
|||
const $hidden_login_form = $('#hidden-login-form');
|
||||
const destinationUrl = $.cookie('destination_url');
|
||||
const shouldRedirectToUrl = self.session.get("shouldRedirectToUrl");
|
||||
const ssoDestinationUrl = $.cookie('sso_destination_url');
|
||||
$hidden_login_form.find('input[name=username]').val(self.get('loginName'));
|
||||
$hidden_login_form.find('input[name=password]').val(self.get('loginPassword'));
|
||||
if (destinationUrl) {
|
||||
|
||||
if (ssoDestinationUrl) {
|
||||
$.cookie('sso_destination_url', null);
|
||||
window.location.assign(ssoDestinationUrl);
|
||||
return;
|
||||
} else if (destinationUrl) {
|
||||
// redirect client to the original URL
|
||||
$.cookie('destination_url', null);
|
||||
$hidden_login_form.find('input[name=redirect]').val(destinationUrl);
|
||||
|
|
|
@ -5,12 +5,6 @@ import computed from "ember-addons/ember-computed-decorators";
|
|||
|
||||
export default Ember.Controller.extend(CanCheckEmails, {
|
||||
|
||||
allowAvatarUpload: setting('allow_uploaded_avatars'),
|
||||
allowUserLocale: setting('allow_user_locale'),
|
||||
ssoOverridesAvatar: setting('sso_overrides_avatar'),
|
||||
allowBackgrounds: setting('allow_profile_backgrounds'),
|
||||
editHistoryVisible: setting('edit_history_visible_to_public'),
|
||||
|
||||
@computed("model.watchedCategories", "model.trackedCategories", "model.mutedCategories")
|
||||
selectedCategories(watched, tracked, muted) {
|
||||
return [].concat(watched, tracked, muted);
|
||||
|
@ -45,7 +39,7 @@ export default Ember.Controller.extend(CanCheckEmails, {
|
|||
|
||||
@computed()
|
||||
nameInstructions() {
|
||||
return I18n.t(Discourse.SiteSettings.full_name_required ? 'user.name.instructions_required' : 'user.name.instructions');
|
||||
return I18n.t(this.siteSettings.full_name_required ? 'user.name.instructions_required' : 'user.name.instructions');
|
||||
},
|
||||
|
||||
@computed("model.has_title_badges")
|
||||
|
|
|
@ -101,8 +101,10 @@ export default Ember.Controller.extend({
|
|||
// defer load if needed, if in an expanded replies section
|
||||
if (!post) {
|
||||
const postStream = this.get('controllers.topic.model.postStream');
|
||||
postStream.loadPost(postId).then(() => this.quoteText());
|
||||
return;
|
||||
return postStream.loadPost(postId).then(p => {
|
||||
this.set('post', p);
|
||||
return this.quoteText();
|
||||
});
|
||||
}
|
||||
|
||||
// If we can't create a post, delegate to reply as new topic
|
||||
|
|
|
@ -13,12 +13,13 @@ addBulkButton('closeTopics', 'close_topics');
|
|||
addBulkButton('archiveTopics', 'archive_topics');
|
||||
addBulkButton('showNotificationLevel', 'notification_level');
|
||||
addBulkButton('resetRead', 'reset_read');
|
||||
addBulkButton('unlistTopics', 'unlist_topics');
|
||||
|
||||
// Modal for performing bulk actions on topics
|
||||
export default Ember.ArrayController.extend(ModalFunctionality, {
|
||||
buttonRows: null,
|
||||
|
||||
onShow: function() {
|
||||
onShow() {
|
||||
this.set('controllers.modal.modalClass', 'topic-bulk-actions-modal small');
|
||||
|
||||
const buttonRows = [];
|
||||
|
@ -36,87 +37,80 @@ export default Ember.ArrayController.extend(ModalFunctionality, {
|
|||
this.send('changeBulkTemplate', 'modal/bulk_actions_buttons');
|
||||
},
|
||||
|
||||
perform: function(operation) {
|
||||
perform(operation) {
|
||||
this.set('loading', true);
|
||||
|
||||
var self = this,
|
||||
topics = this.get('model');
|
||||
return Discourse.Topic.bulkOperation(this.get('model'), operation).then(function(result) {
|
||||
self.set('loading', false);
|
||||
const topics = this.get('model');
|
||||
return Discourse.Topic.bulkOperation(this.get('model'), operation).then(result => {
|
||||
this.set('loading', false);
|
||||
if (result && result.topic_ids) {
|
||||
return result.topic_ids.map(function (t) {
|
||||
return topics.findBy('id', t);
|
||||
});
|
||||
return result.topic_ids.map(t => topics.findBy('id', t));
|
||||
}
|
||||
return result;
|
||||
}).catch(function() {
|
||||
}).catch(() => {
|
||||
bootbox.alert(I18n.t('generic_error'));
|
||||
self.set('loading', false);
|
||||
this.set('loading', false);
|
||||
});
|
||||
},
|
||||
|
||||
forEachPerformed: function(operation, cb) {
|
||||
var self = this;
|
||||
this.perform(operation).then(function (topics) {
|
||||
forEachPerformed(operation, cb) {
|
||||
this.perform(operation).then(topics => {
|
||||
if (topics) {
|
||||
topics.forEach(cb);
|
||||
const refreshTarget = self.get('refreshTarget');
|
||||
const refreshTarget = this.get('refreshTarget');
|
||||
if (refreshTarget) { refreshTarget.send('refresh'); }
|
||||
self.send('closeModal');
|
||||
this.send('closeModal');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
performAndRefresh: function(operation) {
|
||||
const self = this;
|
||||
return this.perform(operation).then(function() {
|
||||
const refreshTarget = self.get('refreshTarget');
|
||||
performAndRefresh(operation) {
|
||||
return this.perform(operation).then(() => {
|
||||
const refreshTarget = this.get('refreshTarget');
|
||||
if (refreshTarget) { refreshTarget.send('refresh'); }
|
||||
self.send('closeModal');
|
||||
this.send('closeModal');
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
showChangeCategory: function() {
|
||||
showChangeCategory() {
|
||||
this.send('changeBulkTemplate', 'modal/bulk_change_category');
|
||||
this.set('controllers.modal.modalClass', 'topic-bulk-actions-modal full');
|
||||
},
|
||||
|
||||
showNotificationLevel: function() {
|
||||
showNotificationLevel() {
|
||||
this.send('changeBulkTemplate', 'modal/bulk_notification_level');
|
||||
},
|
||||
|
||||
deleteTopics: function() {
|
||||
deleteTopics() {
|
||||
this.performAndRefresh({type: 'delete'});
|
||||
},
|
||||
|
||||
closeTopics: function() {
|
||||
this.forEachPerformed({type: 'close'}, function(t) {
|
||||
t.set('closed', true);
|
||||
});
|
||||
closeTopics() {
|
||||
this.forEachPerformed({type: 'close'}, t => t.set('closed', true));
|
||||
},
|
||||
|
||||
archiveTopics: function() {
|
||||
this.forEachPerformed({type: 'archive'}, function(t) {
|
||||
t.set('archived', true);
|
||||
});
|
||||
archiveTopics() {
|
||||
this.forEachPerformed({type: 'archive'}, t => t.set('archived', true));
|
||||
},
|
||||
|
||||
changeCategory: function() {
|
||||
var categoryId = parseInt(this.get('newCategoryId'), 10) || 0,
|
||||
category = Discourse.Category.findById(categoryId),
|
||||
self = this;
|
||||
this.perform({type: 'change_category', category_id: categoryId}).then(function(topics) {
|
||||
topics.forEach(function(t) {
|
||||
t.set('category', category);
|
||||
});
|
||||
const refreshTarget = self.get('refreshTarget');
|
||||
unlistTopics() {
|
||||
this.forEachPerformed({type: 'unlist'}, t => t.set('visible', false));
|
||||
},
|
||||
|
||||
changeCategory() {
|
||||
const categoryId = parseInt(this.get('newCategoryId'), 10) || 0;
|
||||
const category = Discourse.Category.findById(categoryId);
|
||||
|
||||
this.perform({type: 'change_category', category_id: categoryId}).then(topics => {
|
||||
topics.forEach(t => t.set('category', category));
|
||||
const refreshTarget = this.get('refreshTarget');
|
||||
if (refreshTarget) { refreshTarget.send('refresh'); }
|
||||
self.send('closeModal');
|
||||
this.send('closeModal');
|
||||
});
|
||||
},
|
||||
|
||||
resetRead: function() {
|
||||
resetRead() {
|
||||
this.performAndRefresh({ type: 'reset_read' });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -153,7 +153,7 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
|
|||
if (user.get('staff') && replyCount > 0) {
|
||||
bootbox.dialog(I18n.t("post.controls.delete_replies.confirm", {count: replyCount}), [
|
||||
{label: I18n.t("cancel"),
|
||||
'class': 'btn-danger rightg'},
|
||||
'class': 'btn-danger right'},
|
||||
{label: I18n.t("post.controls.delete_replies.no_value"),
|
||||
callback() {
|
||||
post.destroy(user);
|
||||
|
@ -374,10 +374,11 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
|
|||
|
||||
togglePinnedForUser() {
|
||||
if (this.get('model.pinned_at')) {
|
||||
if (this.get('pinned')) {
|
||||
this.get('content').clearPin();
|
||||
const topic = this.get('content');
|
||||
if (topic.get('pinned')) {
|
||||
topic.clearPin();
|
||||
} else {
|
||||
this.get('content').rePin();
|
||||
topic.rePin();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -39,6 +39,11 @@ export default Ember.Controller.extend({
|
|||
// XSS protection (should be encapsulated)
|
||||
username = username.toString().replace(/[^A-Za-z0-9_\.\-]/g, "");
|
||||
|
||||
// No user card for anon
|
||||
if (this.siteSettings.hide_user_profiles_from_public && !this.currentUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't show on mobile
|
||||
if (Discourse.Mobile.mobileView) {
|
||||
const url = "/users/" + username;
|
||||
|
|
|
@ -9,7 +9,15 @@ export default {
|
|||
window.PagedownCustom.appendButtons.push({
|
||||
id: 'wmd-emoji-button',
|
||||
description: I18n.t("composer.emoji"),
|
||||
execute: showSelector
|
||||
execute() {
|
||||
showSelector({
|
||||
container,
|
||||
onSelect(title) {
|
||||
const composerController = container.lookup('controller:composer');
|
||||
composerController.appendTextAtCursor(`:${title}:`, {space: true});
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
export function loadAllHelpers() {
|
||||
Ember.keys(requirejs.entries).forEach(entry => {
|
||||
if ((/\/helpers\//).test(entry)) {
|
||||
require(entry, null, null, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'load-all-helpers',
|
||||
|
||||
initialize: function() {
|
||||
Ember.keys(requirejs.entries).forEach(function(entry) {
|
||||
if ((/\/helpers\//).test(entry)) {
|
||||
require(entry, null, null, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
initialize: loadAllHelpers
|
||||
};
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
// note that these categories are copied from Slack
|
||||
// be careful, there are ~20 differences in synonyms, e.g. :boom: vs. :collision:
|
||||
// a few Emoji are actually missing from the Slack categories as well (?), and were added
|
||||
const groups = [
|
||||
{
|
||||
name: "people",
|
||||
fullname: "People",
|
||||
tabicon: "grinning",
|
||||
icons: ["grinning", "grin", "joy", "smiley", "smile", "sweat_smile", "laughing", "innocent", "smiling_imp", "imp", "wink", "blush", "relaxed", "yum", "relieved", "heart_eyes", "sunglasses", "smirk", "neutral_face", "expressionless", "unamused", "sweat", "pensive", "confused", "confounded", "kissing", "kissing_heart", "kissing_smiling_eyes", "kissing_closed_eyes", "stuck_out_tongue", "stuck_out_tongue_winking_eye", "stuck_out_tongue_closed_eyes", "disappointed", "worried", "angry", "rage", "cry", "persevere", "triumph", "disappointed_relieved", "frowning", "anguished", "fearful", "weary", "sleepy", "tired_face", "grimacing", "sob", "open_mouth", "hushed", "cold_sweat", "scream", "astonished", "flushed", "sleeping", "dizzy_face", "no_mouth", "mask", "smile_cat", "joy_cat", "smiley_cat", "heart_eyes_cat", "smirk_cat", "kissing_cat", "pouting_cat", "crying_cat_face", "scream_cat", "footprints", "bust_in_silhouette", "busts_in_silhouette", "baby", "boy", "girl", "man", "woman", "family", "couple", "two_men_holding_hands", "two_women_holding_hands", "dancers", "bride_with_veil", "person_with_blond_hair", "man_with_gua_pi_mao", "man_with_turban", "older_man", "older_woman", "cop", "construction_worker", "princess", "guardsman", "angel", "santa", "ghost", "japanese_ogre", "japanese_goblin", "hankey", "skull", "alien", "space_invader", "bow", "information_desk_person", "no_good", "ok_woman", "raising_hand", "person_with_pouting_face", "person_frowning", "massage", "haircut", "couple_with_heart", "couplekiss", "raised_hands", "clap", "hand", "ear", "eyes", "nose", "lips", "kiss", "tongue", "nail_care", "wave", "+1", "-1", "point_up", "point_up_2", "point_down", "point_left", "point_right", "ok_hand", "v", "facepunch", "fist", "raised_hand", "muscle", "open_hands", "pray"]
|
||||
},
|
||||
{
|
||||
name: "nature",
|
||||
fullname: "Nature",
|
||||
tabicon: "evergreen_tree",
|
||||
icons: ["seedling", "evergreen_tree", "deciduous_tree", "palm_tree", "cactus", "tulip", "cherry_blossom", "rose", "hibiscus", "sunflower", "blossom", "bouquet", "ear_of_rice", "herb", "four_leaf_clover", "maple_leaf", "fallen_leaf", "leaves", "mushroom", "chestnut", "rat", "mouse2", "mouse", "hamster", "ox", "water_buffalo", "cow2", "cow", "tiger2", "leopard", "tiger", "rabbit2", "rabbit", "cat2", "cat", "racehorse", "horse", "ram", "sheep", "goat", "rooster", "chicken", "baby_chick", "hatching_chick", "hatched_chick", "bird", "penguin", "elephant", "dromedary_camel", "camel", "boar", "pig2", "pig", "pig_nose", "dog2", "poodle", "dog", "wolf", "bear", "koala", "panda_face", "monkey_face", "see_no_evil", "hear_no_evil", "speak_no_evil", "monkey", "dragon", "dragon_face", "crocodile", "snake", "turtle", "frog", "whale2", "whale", "dolphin", "octopus", "fish", "tropical_fish", "blowfish", "shell", "snail", "bug", "ant", "bee", "beetle", "feet", "zap", "fire", "crescent_moon", "sunny", "partly_sunny", "cloud", "droplet", "sweat_drops", "umbrella", "dash", "snowflake", "star2", "star", "stars", "sunrise_over_mountains", "sunrise", "rainbow", "ocean", "volcano", "milky_way", "mount_fuji", "japan", "globe_with_meridians", "earth_africa", "earth_americas", "earth_asia", "new_moon", "waxing_crescent_moon", "first_quarter_moon", "moon", "full_moon", "waning_gibbous_moon", "last_quarter_moon", "waning_crescent_moon", "new_moon_with_face", "full_moon_with_face", "first_quarter_moon_with_face", "last_quarter_moon_with_face", "sun_with_face"]
|
||||
},
|
||||
{
|
||||
name: "food",
|
||||
fullname: "Food & Drink",
|
||||
tabicon: "hamburger",
|
||||
icons: ["tomato", "eggplant", "corn", "sweet_potato", "grapes", "melon", "watermelon", "tangerine", "lemon", "banana", "pineapple", "apple", "green_apple", "pear", "peach", "cherries", "strawberry", "hamburger", "pizza", "meat_on_bone", "poultry_leg", "rice_cracker", "rice_ball", "rice", "curry", "ramen", "spaghetti", "bread", "fries", "dango", "oden", "sushi", "fried_shrimp", "fish_cake", "icecream", "shaved_ice", "ice_cream", "doughnut", "cookie", "chocolate_bar", "candy", "lollipop", "custard", "honey_pot", "cake", "bento", "stew", "egg", "fork_and_knife", "tea", "coffee", "sake", "wine_glass", "cocktail", "tropical_drink", "beer", "beers", "baby_bottle"]
|
||||
},
|
||||
{
|
||||
name: "celebration",
|
||||
fullname: "Celebration",
|
||||
tabicon: "gift",
|
||||
icons: ["ribbon", "gift", "birthday", "jack_o_lantern", "christmas_tree", "tanabata_tree", "bamboo", "rice_scene", "fireworks", "sparkler", "tada", "confetti_ball", "balloon", "dizzy", "sparkles", "boom", "mortar_board", "crown", "dolls", "flags", "wind_chime", "crossed_flags", "izakaya_lantern", "ring", "heart", "broken_heart", "love_letter", "two_hearts", "revolving_hearts", "heartbeat", "heartpulse", "sparkling_heart", "cupid", "gift_heart", "heart_decoration", "purple_heart", "yellow_heart", "green_heart", "blue_heart"]
|
||||
},
|
||||
{
|
||||
name: "activity",
|
||||
fullname: "Activities",
|
||||
tabicon: "soccer",
|
||||
icons: ["runner", "walking", "dancer", "rowboat", "swimmer", "surfer", "bath", "snowboarder", "ski", "snowman", "bicyclist", "mountain_bicyclist", "horse_racing", "tent", "fishing_pole_and_fish", "soccer", "basketball", "football", "baseball", "tennis", "rugby_football", "golf", "trophy", "running_shirt_with_sash", "checkered_flag", "musical_keyboard", "guitar", "violin", "saxophone", "trumpet", "musical_note", "notes", "musical_score", "headphones", "microphone", "performing_arts", "ticket", "tophat", "circus_tent", "clapper", "art", "dart", "8ball", "bowling", "slot_machine", "game_die", "video_game", "flower_playing_cards", "black_joker", "mahjong", "carousel_horse", "ferris_wheel", "roller_coaster"]
|
||||
},
|
||||
{
|
||||
name: "travel",
|
||||
fullname: "Travel & Places",
|
||||
tabicon: "airplane",
|
||||
icons: ["train", "mountain_railway", "railway_car", "steam_locomotive", "monorail", "bullettrain_side", "bullettrain_front", "train2", "metro", "light_rail", "station", "tram", "bus", "oncoming_bus", "trolleybus", "minibus", "ambulance", "fire_engine", "police_car", "oncoming_police_car", "rotating_light", "taxi", "oncoming_taxi", "car", "oncoming_automobile", "blue_car", "truck", "articulated_lorry", "tractor", "bike", "busstop", "fuelpump", "construction", "vertical_traffic_light", "traffic_light", "rocket", "helicopter", "airplane", "seat", "anchor", "ship", "speedboat", "boat", "aerial_tramway", "mountain_cableway", "suspension_railway", "passport_control", "customs", "baggage_claim", "left_luggage", "yen", "euro", "pound", "dollar", "statue_of_liberty", "moyai", "foggy", "tokyo_tower", "fountain", "european_castle", "japanese_castle", "city_sunrise", "city_sunset", "night_with_stars", "bridge_at_night", "house", "house_with_garden", "office", "department_store", "factory", "post_office", "european_post_office", "hospital", "bank", "hotel", "love_hotel", "wedding", "church", "convenience_store", "school", "cn", "de", "es", "fr", "gb", "it", "jp", "kr", "ru", "us"]
|
||||
},
|
||||
{
|
||||
name: "objects",
|
||||
fullname: "Objects & Symbols",
|
||||
tabicon: "eyeglasses",
|
||||
icons: ["watch", "iphone", "calling", "computer", "alarm_clock", "hourglass_flowing_sand", "hourglass", "camera", "video_camera", "movie_camera", "tv", "radio", "pager", "telephone_receiver", "phone", "fax", "minidisc", "floppy_disk", "cd", "dvd", "vhs", "battery", "electric_plug", "bulb", "flashlight", "satellite", "credit_card", "money_with_wings", "moneybag", "gem", "closed_umbrella", "pouch", "purse", "handbag", "briefcase", "school_satchel", "lipstick", "eyeglasses", "womans_hat", "sandal", "high_heel", "boot", "mans_shoe", "athletic_shoe", "bikini", "dress", "kimono", "womans_clothes", "shirt", "necktie", "jeans", "door", "shower", "bathtub", "toilet", "barber", "syringe", "pill", "microscope", "telescope", "crystal_ball", "wrench", "hocho", "nut_and_bolt", "hammer", "bomb", "smoking", "gun", "bookmark", "newspaper", "key", "email", "envelope_with_arrow", "incoming_envelope", "e-mail", "inbox_tray", "outbox_tray", "package", "postal_horn", "postbox", "mailbox_closed", "mailbox", "mailbox_with_mail", "mailbox_with_no_mail", "page_facing_up", "page_with_curl", "bookmark_tabs", "chart_with_upwards_trend", "chart_with_downwards_trend", "bar_chart", "date", "calendar", "low_brightness", "high_brightness", "scroll", "clipboard", "book", "notebook", "notebook_with_decorative_cover", "ledger", "closed_book", "green_book", "blue_book", "orange_book", "books", "card_index", "link", "paperclip", "pushpin", "scissors", "triangular_ruler", "round_pushpin", "straight_ruler", "triangular_flag_on_post", "file_folder", "open_file_folder", "black_nib", "pencil2", "memo", "lock_with_ink_pen", "closed_lock_with_key", "lock", "unlock", "mega", "loudspeaker", "sound", "loud_sound", "speaker", "mute", "zzz", "bell", "no_bell", "thought_balloon", "speech_balloon", "children_crossing", "mag", "mag_right", "no_entry_sign", "no_entry", "name_badge", "no_pedestrians", "do_not_litter", "no_bicycles", "non-potable_water", "no_mobile_phones", "underage", "accept", "ideograph_advantage", "white_flower", "secret", "congratulations", "u5408", "u6e80", "u7981", "u6709", "u7121", "u7533", "u55b6", "u6708", "u5272", "u7a7a", "sa", "koko", "u6307", "chart", "sparkle", "eight_spoked_asterisk", "negative_squared_cross_mark", "white_check_mark", "eight_pointed_black_star", "vibration_mode", "mobile_phone_off", "vs", "a", "b", "ab", "cl", "o2", "sos", "id", "parking", "wc", "cool", "free", "new", "ng", "ok", "up", "atm", "aries", "taurus", "gemini", "cancer", "leo", "virgo", "libra", "scorpius", "sagittarius", "capricorn", "aquarius", "pisces", "restroom", "mens", "womens", "baby_symbol", "wheelchair", "potable_water", "no_smoking", "put_litter_in_its_place", "arrow_forward", "arrow_backward", "arrow_up_small", "arrow_down_small", "fast_forward", "rewind", "arrow_double_up", "arrow_double_down", "arrow_right", "arrow_left", "arrow_up", "arrow_down", "arrow_upper_right", "arrow_lower_right", "arrow_lower_left", "arrow_upper_left", "arrow_up_down", "left_right_arrow", "arrows_counterclockwise", "arrow_right_hook", "leftwards_arrow_with_hook", "arrow_heading_up", "arrow_heading_down", "twisted_rightwards_arrows", "repeat", "repeat_one", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "keycap_ten", "1234", "hash", "abc", "abcd", "capital_abcd", "information_source", "signal_strength", "cinema", "symbols", "heavy_plus_sign", "heavy_minus_sign", "wavy_dash", "heavy_division_sign", "heavy_multiplication_x", "heavy_check_mark", "arrows_clockwise", "tm", "copyright", "registered", "currency_exchange", "heavy_dollar_sign", "curly_loop", "loop", "part_alternation_mark", "exclamation", "bangbang", "question", "grey_exclamation", "grey_question", "interrobang", "x", "o", "100", "end", "back", "on", "top", "soon", "cyclone", "m", "ophiuchus", "six_pointed_star", "beginner", "trident", "warning", "hotsprings", "recycle", "anger", "diamond_shape_with_a_dot_inside", "spades", "clubs", "hearts", "diamonds", "ballot_box_with_check", "white_circle", "black_circle", "radio_button", "red_circle", "large_blue_circle", "small_red_triangle", "small_red_triangle_down", "small_orange_diamond", "small_blue_diamond", "large_orange_diamond", "large_blue_diamond", "black_small_square", "white_small_square", "black_large_square", "white_large_square", "black_medium_square", "white_medium_square", "black_medium_small_square", "white_medium_small_square", "black_square_button", "white_square_button", "clock1", "clock2", "clock3", "clock4", "clock5", "clock6", "clock7", "clock8", "clock9", "clock10", "clock11", "clock12", "clock130", "clock230", "clock330", "clock430", "clock530", "clock630", "clock730", "clock830", "clock930", "clock1030", "clock1130", "clock1230"]
|
||||
}
|
||||
];
|
||||
|
||||
// scrub groups
|
||||
groups.forEach(group => {
|
||||
group.icons = group.icons.reject(obj => !Discourse.Emoji.exists(obj));
|
||||
});
|
||||
|
||||
// export so others can modify
|
||||
Discourse.Emoji.groups = groups;
|
||||
|
||||
export default groups;
|
|
@ -1,101 +1,45 @@
|
|||
import groups from 'discourse/lib/emoji/emoji-groups';
|
||||
import KeyValueStore from "discourse/lib/key-value-store";
|
||||
|
||||
const keyValueStore = new KeyValueStore("discourse_emojis_");
|
||||
const EMOJI_USAGE = "emojiUsage";
|
||||
|
||||
// note that these categories are copied from Slack
|
||||
// be careful, there are ~20 differences in synonyms, e.g. :boom: vs. :collision:
|
||||
// a few Emoji are actually missing from the Slack categories as well (?), and were added
|
||||
var groups = [
|
||||
{
|
||||
name: "people",
|
||||
fullname: "People",
|
||||
tabicon: "grinning",
|
||||
icons: ["grinning", "grin", "joy", "smiley", "smile", "sweat_smile", "laughing", "innocent", "smiling_imp", "imp", "wink", "blush", "relaxed", "yum", "relieved", "heart_eyes", "sunglasses", "smirk", "neutral_face", "expressionless", "unamused", "sweat", "pensive", "confused", "confounded", "kissing", "kissing_heart", "kissing_smiling_eyes", "kissing_closed_eyes", "stuck_out_tongue", "stuck_out_tongue_winking_eye", "stuck_out_tongue_closed_eyes", "disappointed", "worried", "angry", "rage", "cry", "persevere", "triumph", "disappointed_relieved", "frowning", "anguished", "fearful", "weary", "sleepy", "tired_face", "grimacing", "sob", "open_mouth", "hushed", "cold_sweat", "scream", "astonished", "flushed", "sleeping", "dizzy_face", "no_mouth", "mask", "smile_cat", "joy_cat", "smiley_cat", "heart_eyes_cat", "smirk_cat", "kissing_cat", "pouting_cat", "crying_cat_face", "scream_cat", "footprints", "bust_in_silhouette", "busts_in_silhouette", "baby", "boy", "girl", "man", "woman", "family", "couple", "two_men_holding_hands", "two_women_holding_hands", "dancers", "bride_with_veil", "person_with_blond_hair", "man_with_gua_pi_mao", "man_with_turban", "older_man", "older_woman", "cop", "construction_worker", "princess", "guardsman", "angel", "santa", "ghost", "japanese_ogre", "japanese_goblin", "hankey", "skull", "alien", "space_invader", "bow", "information_desk_person", "no_good", "ok_woman", "raising_hand", "person_with_pouting_face", "person_frowning", "massage", "haircut", "couple_with_heart", "couplekiss", "raised_hands", "clap", "hand", "ear", "eyes", "nose", "lips", "kiss", "tongue", "nail_care", "wave", "+1", "-1", "point_up", "point_up_2", "point_down", "point_left", "point_right", "ok_hand", "v", "facepunch", "fist", "raised_hand", "muscle", "open_hands", "pray"]
|
||||
},
|
||||
{
|
||||
name: "nature",
|
||||
fullname: "Nature",
|
||||
tabicon: "evergreen_tree",
|
||||
icons: ["seedling", "evergreen_tree", "deciduous_tree", "palm_tree", "cactus", "tulip", "cherry_blossom", "rose", "hibiscus", "sunflower", "blossom", "bouquet", "ear_of_rice", "herb", "four_leaf_clover", "maple_leaf", "fallen_leaf", "leaves", "mushroom", "chestnut", "rat", "mouse2", "mouse", "hamster", "ox", "water_buffalo", "cow2", "cow", "tiger2", "leopard", "tiger", "rabbit2", "rabbit", "cat2", "cat", "racehorse", "horse", "ram", "sheep", "goat", "rooster", "chicken", "baby_chick", "hatching_chick", "hatched_chick", "bird", "penguin", "elephant", "dromedary_camel", "camel", "boar", "pig2", "pig", "pig_nose", "dog2", "poodle", "dog", "wolf", "bear", "koala", "panda_face", "monkey_face", "see_no_evil", "hear_no_evil", "speak_no_evil", "monkey", "dragon", "dragon_face", "crocodile", "snake", "turtle", "frog", "whale2", "whale", "dolphin", "octopus", "fish", "tropical_fish", "blowfish", "shell", "snail", "bug", "ant", "bee", "beetle", "feet", "zap", "fire", "crescent_moon", "sunny", "partly_sunny", "cloud", "droplet", "sweat_drops", "umbrella", "dash", "snowflake", "star2", "star", "stars", "sunrise_over_mountains", "sunrise", "rainbow", "ocean", "volcano", "milky_way", "mount_fuji", "japan", "globe_with_meridians", "earth_africa", "earth_americas", "earth_asia", "new_moon", "waxing_crescent_moon", "first_quarter_moon", "moon", "full_moon", "waning_gibbous_moon", "last_quarter_moon", "waning_crescent_moon", "new_moon_with_face", "full_moon_with_face", "first_quarter_moon_with_face", "last_quarter_moon_with_face", "sun_with_face"]
|
||||
},
|
||||
{
|
||||
name: "food",
|
||||
fullname: "Food & Drink",
|
||||
tabicon: "hamburger",
|
||||
icons: ["tomato", "eggplant", "corn", "sweet_potato", "grapes", "melon", "watermelon", "tangerine", "lemon", "banana", "pineapple", "apple", "green_apple", "pear", "peach", "cherries", "strawberry", "hamburger", "pizza", "meat_on_bone", "poultry_leg", "rice_cracker", "rice_ball", "rice", "curry", "ramen", "spaghetti", "bread", "fries", "dango", "oden", "sushi", "fried_shrimp", "fish_cake", "icecream", "shaved_ice", "ice_cream", "doughnut", "cookie", "chocolate_bar", "candy", "lollipop", "custard", "honey_pot", "cake", "bento", "stew", "egg", "fork_and_knife", "tea", "coffee", "sake", "wine_glass", "cocktail", "tropical_drink", "beer", "beers", "baby_bottle"]
|
||||
},
|
||||
{
|
||||
name: "celebration",
|
||||
fullname: "Celebration",
|
||||
tabicon: "gift",
|
||||
icons: ["ribbon", "gift", "birthday", "jack_o_lantern", "christmas_tree", "tanabata_tree", "bamboo", "rice_scene", "fireworks", "sparkler", "tada", "confetti_ball", "balloon", "dizzy", "sparkles", "boom", "mortar_board", "crown", "dolls", "flags", "wind_chime", "crossed_flags", "izakaya_lantern", "ring", "heart", "broken_heart", "love_letter", "two_hearts", "revolving_hearts", "heartbeat", "heartpulse", "sparkling_heart", "cupid", "gift_heart", "heart_decoration", "purple_heart", "yellow_heart", "green_heart", "blue_heart"]
|
||||
},
|
||||
{
|
||||
name: "activity",
|
||||
fullname: "Activities",
|
||||
tabicon: "soccer",
|
||||
icons: ["runner", "walking", "dancer", "rowboat", "swimmer", "surfer", "bath", "snowboarder", "ski", "snowman", "bicyclist", "mountain_bicyclist", "horse_racing", "tent", "fishing_pole_and_fish", "soccer", "basketball", "football", "baseball", "tennis", "rugby_football", "golf", "trophy", "running_shirt_with_sash", "checkered_flag", "musical_keyboard", "guitar", "violin", "saxophone", "trumpet", "musical_note", "notes", "musical_score", "headphones", "microphone", "performing_arts", "ticket", "tophat", "circus_tent", "clapper", "art", "dart", "8ball", "bowling", "slot_machine", "game_die", "video_game", "flower_playing_cards", "black_joker", "mahjong", "carousel_horse", "ferris_wheel", "roller_coaster"]
|
||||
},
|
||||
{
|
||||
name: "travel",
|
||||
fullname: "Travel & Places",
|
||||
tabicon: "airplane",
|
||||
icons: ["train", "mountain_railway", "railway_car", "steam_locomotive", "monorail", "bullettrain_side", "bullettrain_front", "train2", "metro", "light_rail", "station", "tram", "bus", "oncoming_bus", "trolleybus", "minibus", "ambulance", "fire_engine", "police_car", "oncoming_police_car", "rotating_light", "taxi", "oncoming_taxi", "car", "oncoming_automobile", "blue_car", "truck", "articulated_lorry", "tractor", "bike", "busstop", "fuelpump", "construction", "vertical_traffic_light", "traffic_light", "rocket", "helicopter", "airplane", "seat", "anchor", "ship", "speedboat", "boat", "aerial_tramway", "mountain_cableway", "suspension_railway", "passport_control", "customs", "baggage_claim", "left_luggage", "yen", "euro", "pound", "dollar", "statue_of_liberty", "moyai", "foggy", "tokyo_tower", "fountain", "european_castle", "japanese_castle", "city_sunrise", "city_sunset", "night_with_stars", "bridge_at_night", "house", "house_with_garden", "office", "department_store", "factory", "post_office", "european_post_office", "hospital", "bank", "hotel", "love_hotel", "wedding", "church", "convenience_store", "school", "cn", "de", "es", "fr", "gb", "it", "jp", "kr", "ru", "us"]
|
||||
},
|
||||
{
|
||||
name: "objects",
|
||||
fullname: "Objects & Symbols",
|
||||
tabicon: "eyeglasses",
|
||||
icons: ["watch", "iphone", "calling", "computer", "alarm_clock", "hourglass_flowing_sand", "hourglass", "camera", "video_camera", "movie_camera", "tv", "radio", "pager", "telephone_receiver", "phone", "fax", "minidisc", "floppy_disk", "cd", "dvd", "vhs", "battery", "electric_plug", "bulb", "flashlight", "satellite", "credit_card", "money_with_wings", "moneybag", "gem", "closed_umbrella", "pouch", "purse", "handbag", "briefcase", "school_satchel", "lipstick", "eyeglasses", "womans_hat", "sandal", "high_heel", "boot", "mans_shoe", "athletic_shoe", "bikini", "dress", "kimono", "womans_clothes", "shirt", "necktie", "jeans", "door", "shower", "bathtub", "toilet", "barber", "syringe", "pill", "microscope", "telescope", "crystal_ball", "wrench", "hocho", "nut_and_bolt", "hammer", "bomb", "smoking", "gun", "bookmark", "newspaper", "key", "email", "envelope_with_arrow", "incoming_envelope", "e-mail", "inbox_tray", "outbox_tray", "package", "postal_horn", "postbox", "mailbox_closed", "mailbox", "mailbox_with_mail", "mailbox_with_no_mail", "page_facing_up", "page_with_curl", "bookmark_tabs", "chart_with_upwards_trend", "chart_with_downwards_trend", "bar_chart", "date", "calendar", "low_brightness", "high_brightness", "scroll", "clipboard", "book", "notebook", "notebook_with_decorative_cover", "ledger", "closed_book", "green_book", "blue_book", "orange_book", "books", "card_index", "link", "paperclip", "pushpin", "scissors", "triangular_ruler", "round_pushpin", "straight_ruler", "triangular_flag_on_post", "file_folder", "open_file_folder", "black_nib", "pencil2", "memo", "lock_with_ink_pen", "closed_lock_with_key", "lock", "unlock", "mega", "loudspeaker", "sound", "loud_sound", "speaker", "mute", "zzz", "bell", "no_bell", "thought_balloon", "speech_balloon", "children_crossing", "mag", "mag_right", "no_entry_sign", "no_entry", "name_badge", "no_pedestrians", "do_not_litter", "no_bicycles", "non-potable_water", "no_mobile_phones", "underage", "accept", "ideograph_advantage", "white_flower", "secret", "congratulations", "u5408", "u6e80", "u7981", "u6709", "u7121", "u7533", "u55b6", "u6708", "u5272", "u7a7a", "sa", "koko", "u6307", "chart", "sparkle", "eight_spoked_asterisk", "negative_squared_cross_mark", "white_check_mark", "eight_pointed_black_star", "vibration_mode", "mobile_phone_off", "vs", "a", "b", "ab", "cl", "o2", "sos", "id", "parking", "wc", "cool", "free", "new", "ng", "ok", "up", "atm", "aries", "taurus", "gemini", "cancer", "leo", "virgo", "libra", "scorpius", "sagittarius", "capricorn", "aquarius", "pisces", "restroom", "mens", "womens", "baby_symbol", "wheelchair", "potable_water", "no_smoking", "put_litter_in_its_place", "arrow_forward", "arrow_backward", "arrow_up_small", "arrow_down_small", "fast_forward", "rewind", "arrow_double_up", "arrow_double_down", "arrow_right", "arrow_left", "arrow_up", "arrow_down", "arrow_upper_right", "arrow_lower_right", "arrow_lower_left", "arrow_upper_left", "arrow_up_down", "left_right_arrow", "arrows_counterclockwise", "arrow_right_hook", "leftwards_arrow_with_hook", "arrow_heading_up", "arrow_heading_down", "twisted_rightwards_arrows", "repeat", "repeat_one", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "keycap_ten", "1234", "hash", "abc", "abcd", "capital_abcd", "information_source", "signal_strength", "cinema", "symbols", "heavy_plus_sign", "heavy_minus_sign", "wavy_dash", "heavy_division_sign", "heavy_multiplication_x", "heavy_check_mark", "arrows_clockwise", "tm", "copyright", "registered", "currency_exchange", "heavy_dollar_sign", "curly_loop", "loop", "part_alternation_mark", "exclamation", "bangbang", "question", "grey_exclamation", "grey_question", "interrobang", "x", "o", "100", "end", "back", "on", "top", "soon", "cyclone", "m", "ophiuchus", "six_pointed_star", "beginner", "trident", "warning", "hotsprings", "recycle", "anger", "diamond_shape_with_a_dot_inside", "spades", "clubs", "hearts", "diamonds", "ballot_box_with_check", "white_circle", "black_circle", "radio_button", "red_circle", "large_blue_circle", "small_red_triangle", "small_red_triangle_down", "small_orange_diamond", "small_blue_diamond", "large_orange_diamond", "large_blue_diamond", "black_small_square", "white_small_square", "black_large_square", "white_large_square", "black_medium_square", "white_medium_square", "black_medium_small_square", "white_medium_small_square", "black_square_button", "white_square_button", "clock1", "clock2", "clock3", "clock4", "clock5", "clock6", "clock7", "clock8", "clock9", "clock10", "clock11", "clock12", "clock130", "clock230", "clock330", "clock430", "clock530", "clock630", "clock730", "clock830", "clock930", "clock1030", "clock1130", "clock1230"]
|
||||
}
|
||||
];
|
||||
let PER_ROW = 12;
|
||||
const PER_PAGE = 60;
|
||||
|
||||
// scrub groups
|
||||
groups.forEach(function(group){
|
||||
group.icons = _.reject(group.icons, function(obj){
|
||||
return !Discourse.Emoji.exists(obj);
|
||||
});
|
||||
});
|
||||
|
||||
// export so others can modify
|
||||
Discourse.Emoji.groups = groups;
|
||||
|
||||
var closeSelector = function(){
|
||||
$('.emoji-modal, .emoji-modal-wrapper').remove();
|
||||
$('body, textarea').off('keydown.emoji');
|
||||
};
|
||||
|
||||
var ungroupedIcons, recentlyUsedIcons;
|
||||
|
||||
var initializeUngroupedIcons = function(){
|
||||
ungroupedIcons = [];
|
||||
|
||||
var groupedIcons = {};
|
||||
_.each(groups, function(group){
|
||||
_.each(group.icons, function(icon){
|
||||
groupedIcons[icon] = true;
|
||||
});
|
||||
});
|
||||
|
||||
var emojis = Discourse.Emoji.list();
|
||||
_.each(emojis, function(emoji){
|
||||
if(groupedIcons[emoji] !== true){
|
||||
ungroupedIcons.push(emoji);
|
||||
}
|
||||
});
|
||||
|
||||
if(ungroupedIcons.length > 0){
|
||||
groups.push({name: 'ungrouped', icons: ungroupedIcons});
|
||||
}
|
||||
};
|
||||
let ungroupedIcons, recentlyUsedIcons;
|
||||
|
||||
if (!keyValueStore.getObject(EMOJI_USAGE)) {
|
||||
keyValueStore.setObject({key: EMOJI_USAGE, value: {}});
|
||||
}
|
||||
|
||||
var trackEmojiUsage = function(title) {
|
||||
var recent = keyValueStore.getObject(EMOJI_USAGE);
|
||||
function closeSelector() {
|
||||
$('.emoji-modal, .emoji-modal-wrapper').remove();
|
||||
$('body, textarea').off('keydown.emoji');
|
||||
}
|
||||
|
||||
function initializeUngroupedIcons() {
|
||||
const groupedIcons = {};
|
||||
|
||||
groups.forEach(group => {
|
||||
group.icons.forEach(icon => groupedIcons[icon] = true);
|
||||
});
|
||||
|
||||
ungroupedIcons = [];
|
||||
const emojis = Discourse.Emoji.list();
|
||||
emojis.forEach(emoji => {
|
||||
if (groupedIcons[emoji] !== true) {
|
||||
ungroupedIcons.push(emoji);
|
||||
}
|
||||
});
|
||||
|
||||
if (ungroupedIcons.length) {
|
||||
groups.push({name: 'ungrouped', icons: ungroupedIcons});
|
||||
}
|
||||
}
|
||||
|
||||
function trackEmojiUsage(title) {
|
||||
const recent = keyValueStore.getObject(EMOJI_USAGE);
|
||||
|
||||
if (!recent[title]) { recent[title] = { title: title, usage: 0 }; }
|
||||
recent[title]["usage"]++;
|
||||
|
@ -104,46 +48,40 @@ var trackEmojiUsage = function(title) {
|
|||
|
||||
// clear the cache
|
||||
recentlyUsedIcons = null;
|
||||
};
|
||||
}
|
||||
|
||||
var initializeRecentlyUsedIcons = function(){
|
||||
function sortByUsage(a, b) {
|
||||
if (a.usage > b.usage) { return -1; }
|
||||
if (b.usage > a.usage) { return 1; }
|
||||
return a.title.localeCompare(b.title);
|
||||
}
|
||||
|
||||
function initializeRecentlyUsedIcons() {
|
||||
recentlyUsedIcons = [];
|
||||
|
||||
var usage = _.map(keyValueStore.getObject(EMOJI_USAGE));
|
||||
usage.sort(function(a,b){
|
||||
if(a.usage > b.usage){
|
||||
return -1;
|
||||
const usage = _.map(keyValueStore.getObject(EMOJI_USAGE)).sort(sortByUsage);
|
||||
const recent = usage.slice(0, PER_ROW);
|
||||
|
||||
if (recent.length > 0) {
|
||||
|
||||
recent.forEach(emoji => recentlyUsedIcons.push(emoji.title));
|
||||
|
||||
const recentGroup = groups.findProperty('name', 'recent');
|
||||
if (recentGroup) {
|
||||
recentGroup.icons = recentlyUsedIcons;
|
||||
} else {
|
||||
groups.push({ name: 'recent', icons: recentlyUsedIcons });
|
||||
}
|
||||
if(b.usage > a.usage){
|
||||
return 1;
|
||||
}
|
||||
return a.title.localeCompare(b.title);
|
||||
});
|
||||
|
||||
var recent = _.take(usage, PER_ROW);
|
||||
|
||||
if(recent.length > 0){
|
||||
_.each(recent, function(emoji){
|
||||
recentlyUsedIcons.push(emoji.title);
|
||||
});
|
||||
|
||||
var recentGroup = _.find(groups, {name: 'recent'});
|
||||
if(!recentGroup){
|
||||
recentGroup = {name: 'recent', icons: []};
|
||||
groups.push(recentGroup);
|
||||
}
|
||||
|
||||
recentGroup.icons = recentlyUsedIcons;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var toolbar = function(selected){
|
||||
function toolbar(selected) {
|
||||
if (!ungroupedIcons) { initializeUngroupedIcons(); }
|
||||
if (!recentlyUsedIcons) { initializeRecentlyUsedIcons(); }
|
||||
|
||||
return _.map(groups, function(g, i){
|
||||
var icon = g.tabicon;
|
||||
var title = g.fullname;
|
||||
return groups.map((g, i) => {
|
||||
let icon = g.tabicon;
|
||||
let title = g.fullname;
|
||||
if (g.name === "recent") {
|
||||
icon = "star";
|
||||
title = "Recent";
|
||||
|
@ -151,60 +89,48 @@ var toolbar = function(selected){
|
|||
icon = g.icons[0];
|
||||
title = "Custom";
|
||||
}
|
||||
var row = {src: Discourse.Emoji.urlFor(icon), title: title, groupId: i};
|
||||
if(i === selected){
|
||||
row.selected = true;
|
||||
}
|
||||
return row;
|
||||
|
||||
return { src: Discourse.Emoji.urlFor(icon),
|
||||
title,
|
||||
groupId: i,
|
||||
selected: i === selected };
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
var PER_ROW = 12, PER_PAGE = 60;
|
||||
|
||||
var bindEvents = function(page, offset, options) {
|
||||
var composerController = Discourse.__container__.lookup('controller:composer');
|
||||
|
||||
$('.emoji-page a').click(function(){
|
||||
var title = $(this).attr('title');
|
||||
function bindEvents(page, offset, options) {
|
||||
$('.emoji-page a').click(e => {
|
||||
const title = $(e.currentTarget).attr('title');
|
||||
trackEmojiUsage(title);
|
||||
|
||||
const prefix = options.skipPrefix ? "" : ":";
|
||||
composerController.appendTextAtCursor(`${prefix}${title}:`, {space: !options.skipPrefix});
|
||||
options.onSelect(title);
|
||||
closeSelector();
|
||||
return false;
|
||||
}).hover(function(){
|
||||
var title = $(this).attr('title');
|
||||
var html = "<img src='" + Discourse.Emoji.urlFor(title) + "' class='emoji'> <span>:" + title + ":<span>";
|
||||
}).hover(e => {
|
||||
const title = $(e.currentTarget).attr('title');
|
||||
const html = "<img src='" + Discourse.Emoji.urlFor(title) + "' class='emoji'> <span>:" + title + ":<span>";
|
||||
$('.emoji-modal .info').html(html);
|
||||
},function(){
|
||||
$('.emoji-modal .info').html("");
|
||||
});
|
||||
}, () => $('.emoji-modal .info').html(""));
|
||||
|
||||
$('.emoji-modal .nav .next a').click(function(){
|
||||
render(page, offset+PER_PAGE, options);
|
||||
});
|
||||
|
||||
$('.emoji-modal .nav .prev a').click(function(){
|
||||
render(page, offset-PER_PAGE, options);
|
||||
});
|
||||
$('.emoji-modal .nav .next a').click(() => render(page, offset+PER_PAGE, options));
|
||||
$('.emoji-modal .nav .prev a').click(() => render(page, offset-PER_PAGE, options));
|
||||
|
||||
$('.emoji-modal .toolbar a').click(function(){
|
||||
var p = parseInt($(this).data('group-id'));
|
||||
const p = parseInt($(this).data('group-id'));
|
||||
render(p, 0, options);
|
||||
return false;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
var render = function(page, offset, options) {
|
||||
function render(page, offset, options) {
|
||||
keyValueStore.set({key: "emojiPage", value: page});
|
||||
keyValueStore.set({key: "emojiOffset", value: offset});
|
||||
|
||||
var toolbarItems = toolbar(page);
|
||||
var rows = [], row = [];
|
||||
var icons = groups[page].icons;
|
||||
var max = offset + PER_PAGE;
|
||||
const toolbarItems = toolbar(page);
|
||||
const rows = [];
|
||||
let row = [];
|
||||
const icons = groups[page].icons;
|
||||
const max = offset + PER_PAGE;
|
||||
|
||||
for(var i=offset; i<max; i++){
|
||||
for(let i=offset; i<max; i++){
|
||||
if(!icons[i]){ break; }
|
||||
if(row.length === PER_ROW){
|
||||
rows.push(row);
|
||||
|
@ -214,41 +140,39 @@ var render = function(page, offset, options) {
|
|||
}
|
||||
rows.push(row);
|
||||
|
||||
var model = {
|
||||
const model = {
|
||||
toolbarItems: toolbarItems,
|
||||
rows: rows,
|
||||
prevDisabled: offset === 0,
|
||||
nextDisabled: (max + 1) > icons.length
|
||||
};
|
||||
|
||||
$('body .emoji-modal').remove();
|
||||
var rendered = Ember.TEMPLATES["emoji-toolbar.raw"](model);
|
||||
$('body').append(rendered);
|
||||
$('.emoji-modal', options.appendTo).remove();
|
||||
const template = options.container.lookup('template:emoji-toolbar.raw');
|
||||
options.appendTo.append(template(model));
|
||||
|
||||
bindEvents(page, offset, options);
|
||||
};
|
||||
}
|
||||
|
||||
var showSelector = function(options) {
|
||||
function showSelector(options) {
|
||||
options = options || {};
|
||||
options.appendTo = options.appendTo || $('body');
|
||||
|
||||
$('body').append('<div class="emoji-modal-wrapper"></div>');
|
||||
|
||||
$('.emoji-modal-wrapper').click(function(){
|
||||
closeSelector();
|
||||
});
|
||||
options.appendTo.append('<div class="emoji-modal-wrapper"></div>');
|
||||
$('.emoji-modal-wrapper').click(() => closeSelector());
|
||||
|
||||
if (Discourse.Mobile.mobileView) PER_ROW = 9;
|
||||
const page = keyValueStore.getInt("emojiPage", 0);
|
||||
const offset = keyValueStore.getInt("emojiOffset", 0);
|
||||
|
||||
var page = keyValueStore.getInt("emojiPage", 0);
|
||||
var offset = keyValueStore.getInt("emojiOffset", 0);
|
||||
render(page, offset, options);
|
||||
|
||||
$('body, textarea').on('keydown.emoji', function(e){
|
||||
if(e.which === 27){
|
||||
$('body, textarea').on('keydown.emoji', e => {
|
||||
if (e.which === 27) {
|
||||
closeSelector();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export { showSelector };
|
||||
|
|
|
@ -12,6 +12,11 @@ Discourse.Dialect.registerEmoji = function(code, url) {
|
|||
extendedEmoji[code] = url;
|
||||
};
|
||||
|
||||
// This method is used by PrettyText to reset custom emojis in multisites
|
||||
Discourse.Dialect.resetEmoji = function() {
|
||||
extendedEmoji = {};
|
||||
};
|
||||
|
||||
Discourse.Emoji.list = function(){
|
||||
var list = emoji.slice(0);
|
||||
_.each(extendedEmoji, function(v,k){ list.push(k); });
|
||||
|
|
|
@ -99,7 +99,7 @@ export default {
|
|||
$('.topic-post.selected button.create').click();
|
||||
// lazy but should work for now
|
||||
setTimeout(function() {
|
||||
$('#wmd-quote-post').click();
|
||||
$('.wmd-quote-post').click();
|
||||
}, 500);
|
||||
},
|
||||
|
||||
|
@ -358,8 +358,8 @@ export default {
|
|||
},
|
||||
|
||||
_changeSection(direction) {
|
||||
const $sections = $('#navigation-bar li'),
|
||||
active = $('#navigation-bar li.active'),
|
||||
const $sections = $('.nav.nav-pills li'),
|
||||
active = $('.nav.nav-pills li.active'),
|
||||
index = $sections.index(active) + direction;
|
||||
|
||||
if (index >= 0 && index < $sections.length) {
|
||||
|
|
|
@ -42,6 +42,7 @@ export default Ember.Mixin.create({
|
|||
});
|
||||
tracker.incrementMessageCount();
|
||||
}
|
||||
self.send('closeModal');
|
||||
self.send('refresh');
|
||||
});
|
||||
}
|
||||
|
|
|
@ -201,7 +201,7 @@ const Composer = RestModel.extend({
|
|||
return this.get('canCategorize') &&
|
||||
!this.siteSettings.allow_uncategorized_topics &&
|
||||
!this.get('categoryId') &&
|
||||
!this.user.get('staff');
|
||||
!this.user.get('admin');
|
||||
}
|
||||
}.property('loading', 'canEditTitle', 'titleLength', 'targetUsernames', 'replyLength', 'categoryId', 'missingReplyCharacters'),
|
||||
|
||||
|
|
|
@ -445,8 +445,7 @@ const PostStream = RestModel.extend({
|
|||
const url = "/posts/" + postId;
|
||||
const store = this.store;
|
||||
|
||||
return Discourse.ajax(url).then((p) =>
|
||||
this.storePost(store.createRecord('post', p)));
|
||||
return Discourse.ajax(url).then(p => this.storePost(store.createRecord('post', p)));
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,6 +8,22 @@ const Topic = RestModel.extend({
|
|||
message: null,
|
||||
errorLoading: false,
|
||||
|
||||
@computed('posters.firstObject')
|
||||
creator(poster){
|
||||
return poster && poster.user;
|
||||
},
|
||||
|
||||
@computed('posters.lastObject')
|
||||
lastPoster(poster) {
|
||||
if (poster){
|
||||
if (this.last_poster_username === poster.user.username){
|
||||
return poster.user;
|
||||
} else {
|
||||
return this.get('creator');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@computed('fancy_title')
|
||||
fancyTitle(title) {
|
||||
title = title || "";
|
||||
|
|
|
@ -68,6 +68,8 @@ const User = RestModel.extend({
|
|||
|
||||
adminPath: url('username_lower', "/admin/users/%@"),
|
||||
|
||||
mutedTopicsPath: url('/latest?state=muted'),
|
||||
|
||||
@computed("username")
|
||||
username_lower(username) {
|
||||
return username.toLowerCase();
|
||||
|
|
|
@ -7,19 +7,32 @@ export default {
|
|||
name: 'dynamic-route-builders',
|
||||
|
||||
initialize(container, app) {
|
||||
app.DiscoveryCategoryController = DiscoverySortableController.extend();
|
||||
app.DiscoveryParentCategoryController = DiscoverySortableController.extend();
|
||||
app.DiscoveryCategoryNoneController = DiscoverySortableController.extend();
|
||||
|
||||
app.DiscoveryCategoryRoute = buildCategoryRoute('latest');
|
||||
app.DiscoveryParentCategoryRoute = buildCategoryRoute('latest');
|
||||
app.DiscoveryCategoryNoneRoute = buildCategoryRoute('latest', {no_subcategories: true});
|
||||
|
||||
const site = Discourse.Site.current();
|
||||
site.get('filters').forEach(filter => {
|
||||
app["Discovery" + filter.capitalize() + "Controller"] = DiscoverySortableController.extend();
|
||||
app["Discovery" + filter.capitalize() + "Route"] = buildTopicRoute(filter);
|
||||
app["Discovery" + filter.capitalize() + "CategoryRoute"] = buildCategoryRoute(filter);
|
||||
app["Discovery" + filter.capitalize() + "CategoryNoneRoute"] = buildCategoryRoute(filter, {no_subcategories: true});
|
||||
const filterCapitalized = filter.capitalize();
|
||||
app[`Discovery${filterCapitalized}Controller`] = DiscoverySortableController.extend();
|
||||
app[`Discovery${filterCapitalized}CategoryController`] = DiscoverySortableController.extend();
|
||||
app[`Discovery${filterCapitalized}ParentCategoryController`] = DiscoverySortableController.extend();
|
||||
app[`Discovery${filterCapitalized}CategoryNoneController`] = DiscoverySortableController.extend();
|
||||
app[`Discovery${filterCapitalized}Route`] = buildTopicRoute(filter);
|
||||
app[`Discovery${filterCapitalized}CategoryRoute`] = buildCategoryRoute(filter);
|
||||
app[`Discovery${filterCapitalized}ParentCategoryRoute`] = buildCategoryRoute(filter);
|
||||
app[`Discovery${filterCapitalized}CategoryNoneRoute`] = buildCategoryRoute(filter, {no_subcategories: true});
|
||||
});
|
||||
|
||||
Discourse.DiscoveryTopController = DiscoverySortableController.extend();
|
||||
Discourse.DiscoveryTopCategoryController = DiscoverySortableController.extend();
|
||||
Discourse.DiscoveryTopParentCategoryController = DiscoverySortableController.extend();
|
||||
Discourse.DiscoveryTopCategoryNoneController = DiscoverySortableController.extend();
|
||||
|
||||
Discourse.DiscoveryTopRoute = buildTopicRoute('top', {
|
||||
actions: {
|
||||
willTransition() {
|
||||
|
@ -30,13 +43,19 @@ export default {
|
|||
}
|
||||
});
|
||||
Discourse.DiscoveryTopCategoryRoute = buildCategoryRoute('top');
|
||||
Discourse.DiscoveryTopParentCategoryRoute = buildCategoryRoute('top');
|
||||
Discourse.DiscoveryTopCategoryNoneRoute = buildCategoryRoute('top', {no_subcategories: true});
|
||||
|
||||
site.get('periods').forEach(period => {
|
||||
app["DiscoveryTop" + period.capitalize() + "Controller"] = DiscoverySortableController.extend();
|
||||
app["DiscoveryTop" + period.capitalize() + "Route"] = buildTopicRoute('top/' + period);
|
||||
app["DiscoveryTop" + period.capitalize() + "CategoryRoute"] = buildCategoryRoute('top/' + period);
|
||||
app["DiscoveryTop" + period.capitalize() + "CategoryNoneRoute"] = buildCategoryRoute('top/' + period, {no_subcategories: true});
|
||||
const periodCapitalized = period.capitalize();
|
||||
app[`DiscoveryTop${periodCapitalized}Controller`] = DiscoverySortableController.extend();
|
||||
app[`DiscoveryTop${periodCapitalized}CategoryController`] = DiscoverySortableController.extend();
|
||||
app[`DiscoveryTop${periodCapitalized}ParentCategoryController`] = DiscoverySortableController.extend();
|
||||
app[`DiscoveryTop${periodCapitalized}CategoryNoneController`] = DiscoverySortableController.extend();
|
||||
app[`DiscoveryTop${periodCapitalized}Route`] = buildTopicRoute('top/' + period);
|
||||
app[`DiscoveryTop${periodCapitalized}CategoryRoute`] = buildCategoryRoute('top/' + period);
|
||||
app[`DiscoveryTop${periodCapitalized}ParentCategoryRoute`] = buildCategoryRoute('top/' + period);
|
||||
app[`DiscoveryTop${periodCapitalized}CategoryNoneRoute`] = buildCategoryRoute('top/' + period, {no_subcategories: true});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -15,26 +15,25 @@ export default function() {
|
|||
this.resource('discovery', { path: '/' }, function() {
|
||||
// top
|
||||
this.route('top');
|
||||
this.route('topCategory', { path: '/c/:slug/l/top' });
|
||||
this.route('topParentCategory', { path: '/c/:slug/l/top' });
|
||||
this.route('topCategoryNone', { path: '/c/:slug/none/l/top' });
|
||||
this.route('topCategory', { path: '/c/:parentSlug/:slug/l/top' });
|
||||
|
||||
// top by periods
|
||||
var self = this;
|
||||
Discourse.Site.currentProp('periods').forEach(function(period) {
|
||||
var top = 'top' + period.capitalize();
|
||||
self.route(top, { path: '/top/' + period });
|
||||
self.route(top + 'Category', { path: '/c/:slug/l/top/' + period });
|
||||
self.route(top + 'CategoryNone', { path: '/c/:slug/none/l/top/' + period });
|
||||
self.route(top + 'Category', { path: '/c/:parentSlug/:slug/l/top/' + period });
|
||||
Discourse.Site.currentProp('periods').forEach(period => {
|
||||
const top = 'top' + period.capitalize();
|
||||
this.route(top, { path: '/top/' + period });
|
||||
this.route(top + 'ParentCategory', { path: '/c/:slug/l/top/' + period });
|
||||
this.route(top + 'CategoryNone', { path: '/c/:slug/none/l/top/' + period });
|
||||
this.route(top + 'Category', { path: '/c/:parentSlug/:slug/l/top/' + period });
|
||||
});
|
||||
|
||||
// filters
|
||||
Discourse.Site.currentProp('filters').forEach(function(filter) {
|
||||
self.route(filter, { path: '/' + filter });
|
||||
self.route(filter + 'Category', { path: '/c/:slug/l/' + filter });
|
||||
self.route(filter + 'CategoryNone', { path: '/c/:slug/none/l/' + filter });
|
||||
self.route(filter + 'Category', { path: '/c/:parentSlug/:slug/l/' + filter });
|
||||
Discourse.Site.currentProp('filters').forEach(filter => {
|
||||
this.route(filter, { path: '/' + filter });
|
||||
this.route(filter + 'ParentCategory', { path: '/c/:slug/l/' + filter });
|
||||
this.route(filter + 'CategoryNone', { path: '/c/:slug/none/l/' + filter });
|
||||
this.route(filter + 'Category', { path: '/c/:parentSlug/:slug/l/' + filter });
|
||||
});
|
||||
|
||||
this.route('categories');
|
||||
|
@ -56,9 +55,8 @@ export default function() {
|
|||
this.resource('users');
|
||||
this.resource('user', { path: '/users/:username' }, function() {
|
||||
this.resource('userActivity', { path: '/activity' }, function() {
|
||||
var self = this;
|
||||
_.map(Discourse.UserAction.TYPES, function (id, userAction) {
|
||||
self.route(userAction, { path: userAction.replace('_', '-') });
|
||||
_.map(Discourse.UserAction.TYPES, (id, userAction) => {
|
||||
this.route(userAction, { path: userAction.replace('_', '-') });
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import { queryParams, filterQueryParams, findTopicList } from 'discourse/routes/build-topic-route';
|
||||
import { filterQueryParams, findTopicList } from 'discourse/routes/build-topic-route';
|
||||
import { queryParams } from 'discourse/controllers/discovery-sortable';
|
||||
|
||||
// A helper function to create a category route with parameters
|
||||
export default (filter, params) => {
|
||||
return Discourse.Route.extend({
|
||||
queryParams: queryParams,
|
||||
queryParams,
|
||||
|
||||
model(modelParams) {
|
||||
return Discourse.Category.findBySlug(modelParams.slug, modelParams.parentSlug);
|
||||
return { category: Discourse.Category.findBySlug(modelParams.slug, modelParams.parentSlug) };
|
||||
},
|
||||
|
||||
afterModel(model, transition) {
|
||||
|
@ -15,27 +16,27 @@ export default (filter, params) => {
|
|||
return;
|
||||
}
|
||||
|
||||
this._setupNavigation(model);
|
||||
return Em.RSVP.all([this._createSubcategoryList(model),
|
||||
this._retrieveTopicList(model, transition)]);
|
||||
this._setupNavigation(model.category);
|
||||
return Em.RSVP.all([this._createSubcategoryList(model.category),
|
||||
this._retrieveTopicList(model.category, transition)]);
|
||||
},
|
||||
|
||||
_setupNavigation(model) {
|
||||
_setupNavigation(category) {
|
||||
const noSubcategories = params && !!params.no_subcategories,
|
||||
filterMode = `c/${Discourse.Category.slugFor(model)}${noSubcategories ? "/none" : ""}/l/${filter}`;
|
||||
filterMode = `c/${Discourse.Category.slugFor(category)}${noSubcategories ? "/none" : ""}/l/${filter}`;
|
||||
|
||||
this.controllerFor('navigation/category').setProperties({
|
||||
category: model,
|
||||
category,
|
||||
filterMode: filterMode,
|
||||
noSubcategories: params && params.no_subcategories,
|
||||
canEditCategory: model.get('can_edit')
|
||||
canEditCategory: category.get('can_edit')
|
||||
});
|
||||
},
|
||||
|
||||
_createSubcategoryList(model) {
|
||||
_createSubcategoryList(category) {
|
||||
this._categoryList = null;
|
||||
if (Em.isNone(model.get('parentCategory')) && Discourse.SiteSettings.show_subcategory_list) {
|
||||
return Discourse.CategoryList.listForParent(this.store, model)
|
||||
if (Em.isNone(category.get('parentCategory')) && Discourse.SiteSettings.show_subcategory_list) {
|
||||
return Discourse.CategoryList.listForParent(this.store, category)
|
||||
.then(list => this._categoryList = list);
|
||||
}
|
||||
|
||||
|
@ -43,49 +44,60 @@ export default (filter, params) => {
|
|||
return Em.RSVP.resolve();
|
||||
},
|
||||
|
||||
_retrieveTopicList(model, transition) {
|
||||
const listFilter = `c/${Discourse.Category.slugFor(model)}/l/${filter}`,
|
||||
_retrieveTopicList(category, transition) {
|
||||
const listFilter = `c/${Discourse.Category.slugFor(category)}/l/${filter}`,
|
||||
findOpts = filterQueryParams(transition.queryParams, params),
|
||||
extras = { cached: this.isPoppedState(transition) };
|
||||
|
||||
return findTopicList(this.store, this.topicTrackingState, listFilter, findOpts, extras).then(list => {
|
||||
Discourse.TopicList.hideUniformCategory(list, model);
|
||||
Discourse.TopicList.hideUniformCategory(list, category);
|
||||
this.set('topics', list);
|
||||
return list;
|
||||
});
|
||||
},
|
||||
|
||||
titleToken() {
|
||||
const filterText = I18n.t('filters.' + filter.replace('/', '.') + '.title', { count: 0 }),
|
||||
model = this.currentModel;
|
||||
category = this.currentModel.category;
|
||||
|
||||
return I18n.t('filters.with_category', { filter: filterText, category: model.get('name') });
|
||||
return I18n.t('filters.with_category', { filter: filterText, category: category.get('name') });
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
const topics = this.get('topics'),
|
||||
periodId = topics.get('for_period') || (filter.indexOf('/') > 0 ? filter.split('/')[1] : ''),
|
||||
category = model.category,
|
||||
canCreateTopic = topics.get('can_create_topic'),
|
||||
canCreateTopicOnCategory = model.get('permission') === Discourse.PermissionType.FULL;
|
||||
canCreateTopicOnCategory = category.get('permission') === Discourse.PermissionType.FULL;
|
||||
|
||||
this.controllerFor('navigation/category').setProperties({
|
||||
canCreateTopicOnCategory: canCreateTopicOnCategory,
|
||||
cannotCreateTopicOnCategory: !canCreateTopicOnCategory,
|
||||
canCreateTopic: canCreateTopic
|
||||
});
|
||||
this.controllerFor('discovery/topics').setProperties({
|
||||
|
||||
var topicOpts = {
|
||||
model: topics,
|
||||
category: model,
|
||||
period: periodId,
|
||||
category,
|
||||
period: topics.get('for_period') || (filter.indexOf('/') > 0 ? filter.split('/')[1] : ''),
|
||||
selected: [],
|
||||
noSubcategories: params && !!params.no_subcategories,
|
||||
order: topics.get('params.order'),
|
||||
ascending: topics.get('params.ascending'),
|
||||
expandAllPinned: true,
|
||||
canCreateTopic: canCreateTopic,
|
||||
canCreateTopicOnCategory: canCreateTopicOnCategory
|
||||
});
|
||||
};
|
||||
|
||||
this.searchService.set('searchContext', model.get('searchContext'));
|
||||
const p = category.get('params');
|
||||
if (p && Object.keys(p).length) {
|
||||
if (p.order !== undefined) {
|
||||
topicOpts.order = p.order;
|
||||
}
|
||||
if (p.ascending !== undefined) {
|
||||
topicOpts.ascending = p.ascending;
|
||||
}
|
||||
}
|
||||
|
||||
this.controllerFor('discovery/topics').setProperties(topicOpts);
|
||||
this.searchService.set('searchContext', category.get('searchContext'));
|
||||
this.set('topics', null);
|
||||
|
||||
this.openTopicDraft(topics);
|
||||
|
@ -100,6 +112,12 @@ export default (filter, params) => {
|
|||
this.render('discovery/topics', { controller: 'discovery/topics', outlet: 'list-container' });
|
||||
},
|
||||
|
||||
resetController(controller, isExiting) {
|
||||
if (isExiting) {
|
||||
controller.setProperties({ order: "default", ascending: false });
|
||||
}
|
||||
},
|
||||
|
||||
deactivate() {
|
||||
this._super();
|
||||
this.searchService.set('searchContext', null);
|
||||
|
|
|
@ -84,18 +84,11 @@ export default function(filter, extras) {
|
|||
return I18n.t('filters.with_topics', {filter: filterText});
|
||||
},
|
||||
|
||||
setupController(controller, model, trans) {
|
||||
if (trans) {
|
||||
controller.setProperties(Em.getProperties(trans, _.keys(queryParams).map(function(v){
|
||||
return 'queryParams.' + v;
|
||||
})));
|
||||
}
|
||||
|
||||
const period = model.get('for_period') || (filter.indexOf('/') > 0 ? filter.split('/')[1] : '');
|
||||
setupController(controller, model) {
|
||||
const topicOpts = {
|
||||
model,
|
||||
category: null,
|
||||
period,
|
||||
period: model.get('for_period') || (filter.indexOf('/') > 0 ? filter.split('/')[1] : ''),
|
||||
selected: [],
|
||||
expandGloballyPinned: true
|
||||
};
|
||||
|
@ -115,6 +108,12 @@ export default function(filter, extras) {
|
|||
this.controllerFor('navigation/default').set('canCreateTopic', model.get('can_create_topic'));
|
||||
},
|
||||
|
||||
resetController(controller, isExiting) {
|
||||
if (isExiting) {
|
||||
controller.setProperties({ order: "default", ascending: false });
|
||||
}
|
||||
},
|
||||
|
||||
renderTemplate() {
|
||||
this.render('navigation/default', { outlet: 'navigation-bar' });
|
||||
this.render('discovery/topics', { controller: 'discovery/topics', outlet: 'list-container' });
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
import OpenComposer from "discourse/mixins/open-composer";
|
||||
import { scrollTop } from "discourse/mixins/scroll-top";
|
||||
|
||||
const DiscoveryRoute = Discourse.Route.extend(OpenComposer, {
|
||||
export default Discourse.Route.extend(OpenComposer, {
|
||||
redirect() {
|
||||
return this.redirectIfLoginRequired();
|
||||
},
|
||||
|
@ -46,9 +46,16 @@ const DiscoveryRoute = Discourse.Route.extend(OpenComposer, {
|
|||
|
||||
createTopic() {
|
||||
this.openComposer(this.controllerFor("discovery/topics"));
|
||||
},
|
||||
|
||||
dismissReadTopics(dismissTopics) {
|
||||
var operationType = dismissTopics ? "topics" : "posts";
|
||||
this.controllerFor("discovery/topics").send('dismissRead', operationType);
|
||||
},
|
||||
|
||||
dismissRead(operationType) {
|
||||
this.controllerFor("discovery/topics").send('dismissRead', operationType);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
export default DiscoveryRoute;
|
||||
|
|
|
@ -43,7 +43,7 @@ const TopicRoute = Discourse.Route.extend({
|
|||
|
||||
showFlags(model) {
|
||||
showModal('flag', { model });
|
||||
this.controllerFor('flag').setProperties({ selected: null });
|
||||
this.controllerFor('flag').setProperties({ selected: null, flagTopic: false });
|
||||
},
|
||||
|
||||
showFlagTopic(model) {
|
||||
|
|
|
@ -33,6 +33,12 @@ export default Discourse.Route.extend({
|
|||
}
|
||||
},
|
||||
|
||||
beforeModel() {
|
||||
if (this.siteSettings.hide_user_profiles_from_public && !this.currentUser) {
|
||||
this.replaceWith("discovery");
|
||||
}
|
||||
},
|
||||
|
||||
model(params) {
|
||||
// If we're viewing the currently logged in user, return that object instead
|
||||
const currentUser = this.currentUser;
|
||||
|
|
|
@ -23,6 +23,12 @@ export default Discourse.Route.extend({
|
|||
}
|
||||
},
|
||||
|
||||
beforeModel() {
|
||||
if (this.siteSettings.hide_user_profiles_from_public && !this.currentUser) {
|
||||
this.replaceWith("discovery");
|
||||
}
|
||||
},
|
||||
|
||||
model(params) {
|
||||
// If we refresh via `refreshModel` set the old model to loading
|
||||
this._params = params;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
{{yield}}
|
||||
|
||||
<div class='controls'>
|
||||
{{d-button class="btn-primary" label="composer.modal_ok" action="ok"}}
|
||||
{{d-button class="btn-danger" label="composer.modal_cancel" action="cancel"}}
|
||||
</div>
|
|
@ -0,0 +1,32 @@
|
|||
<div class='d-editor-overlay hidden'></div>
|
||||
<div class='d-editor-modals'>
|
||||
{{#d-editor-modal class="insert-link" hidden=insertLinkHidden okAction="insertLink"}}
|
||||
<h3>{{i18n "composer.link_dialog_title"}}</h3>
|
||||
{{text-field value=link placeholderKey="composer.link_placeholder"}}
|
||||
{{/d-editor-modal}}
|
||||
</div>
|
||||
|
||||
<div class='d-editor-container'>
|
||||
<div class='d-editor-button-bar'>
|
||||
{{d-button action="bold" icon="bold" class="bold"}}
|
||||
{{d-button action="italic" icon="italic" class="italic"}}
|
||||
<div class='d-editor-spacer'></div>
|
||||
{{d-button action="showLinkModal" icon="link" class="link"}}
|
||||
{{d-button action="quote" icon="quote-right" class="quote"}}
|
||||
{{d-button action="code" icon="code" class="code"}}
|
||||
<div class='d-editor-spacer'></div>
|
||||
{{d-button action="bullet" icon="list-ul" class="bullet"}}
|
||||
{{d-button action="list" icon="list-ol" class="list"}}
|
||||
{{d-button action="heading" icon="font" class="heading"}}
|
||||
{{d-button action="rule" icon="minus" class="rule"}}
|
||||
{{#if siteSettings.enable_emoji}}
|
||||
{{d-button action="emoji" icon="smile-o" class="emoji"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{textarea value=value class="d-editor-input"}}
|
||||
|
||||
<div class="d-editor-preview {{unless preview 'hidden'}}">
|
||||
{{{preview}}}
|
||||
</div>
|
||||
</div>
|
|
@ -1,2 +1,2 @@
|
|||
<label>{{i18n 'category.topic_template'}}</label>
|
||||
{{pagedown-editor value=category.topic_template}}
|
||||
{{d-editor value=category.topic_template}}
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
<li>{{d-link route="badges" class="badge-link" label="badges.title"}}</li>
|
||||
{{/if}}
|
||||
|
||||
{{#if siteSettings.enable_user_directory}}
|
||||
{{#if showUserDirectoryLink}}
|
||||
<li>{{d-link route="users" class="user-directory-link" label="directory.title"}}</li>
|
||||
{{/if}}
|
||||
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
<a href="{{unbound linkUrl}}" data-auto-route="true">
|
||||
{{#if showSmallLogo}}
|
||||
{{#if smallLogoUrl}}
|
||||
<span class="valign-helper"></span>
|
||||
<img class="logo-small" src="{{unbound smallLogoUrl}}" width="33" height="33">
|
||||
{{else}}
|
||||
<i class="fa fa-home"></i>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{#if showMobileLogo}}
|
||||
<span class="valign-helper"></span>
|
||||
<img id="site-logo" class="logo-big" src="{{unbound mobileBigLogoUrl}}" alt="{{unbound title}}">
|
||||
{{else}}
|
||||
{{#if bigLogoUrl}}
|
||||
<span class="valign-helper"></span>
|
||||
<img id="site-logo" class="logo-big" src="{{unbound bigLogoUrl}}" alt="{{unbound title}}">
|
||||
{{else}}
|
||||
<h2 id="site-text-logo" class="text-logo">{{unbound title}}</h2>
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
<div class='wmd-button-bar'></div>
|
||||
{{textarea value=value class="wmd-input"}}
|
||||
<div class="wmd-preview preview {{unless value 'hidden'}}"></div>
|
|
@ -7,7 +7,6 @@
|
|||
{{else}}
|
||||
<p>{{replace-emoji (i18n "signup_cta.intro")}}</p>
|
||||
<p>{{replace-emoji (i18n "signup_cta.value_prop")}}</p>
|
||||
<p>{{signupMethodsTranslated}}</p>
|
||||
|
||||
<div class="buttons">
|
||||
{{d-button action="showCreateAccount" label="signup_cta.sign_up" icon="check" class="btn-primary"}}
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
{{#if showDismissAtTop}}
|
||||
<div class="row">
|
||||
{{#if showDismissRead}}
|
||||
<button title="{{i18n 'topics.bulk.dismiss_topics_tooltip'}}" id='dismiss-topics-top' class='btn dismiss-read' {{action "dismissRead" "topics"}}>{{i18n 'topics.bulk.dismiss_topics'}}</button>
|
||||
<button title="{{i18n 'topics.bulk.dismiss_posts_tooltip'}}" id='dismiss-posts-top' class='btn dismiss-read' {{action "dismissRead" "posts"}}>{{i18n 'topics.bulk.dismiss_posts'}}</button>
|
||||
<button title="{{i18n 'topics.bulk.dismiss_tooltip'}}" id='dismiss-topics-top' class='btn dismiss-read' {{action "dismissReadPosts"}}>{{i18n 'topics.bulk.dismiss_button'}}</button>
|
||||
{{/if}}
|
||||
{{#if showResetNew}}
|
||||
<button id='dismiss-new-top' class='btn dismiss-read' {{action "resetNew"}}>{{i18n 'topics.bulk.dismiss_new'}}</button>
|
||||
|
@ -55,8 +54,7 @@
|
|||
{{conditional-loading-spinner condition=model.loadingMore}}
|
||||
{{#if allLoaded}}
|
||||
{{#if showDismissRead}}
|
||||
<button title="{{i18n 'topics.bulk.dismiss_topics_tooltip'}}" id='dismiss-topics' class='btn dismiss-read' {{action "dismissRead" "topics"}}>{{i18n 'topics.bulk.dismiss_topics'}}</button>
|
||||
<button title="{{i18n 'topics.bulk.dismiss_posts_tooltip'}}" id='dismiss-posts' class='btn dismiss-read' {{action "dismissRead" "posts"}}>{{i18n 'topics.bulk.dismiss_posts'}}</button>
|
||||
<button title="{{i18n 'topics.bulk.dismiss_tooltip'}}" id='dismiss-topics' class='btn dismiss-read' {{action "dismissReadPosts"}}>{{i18n 'topics.bulk.dismiss_button'}}</button>
|
||||
{{/if}}
|
||||
{{#if showResetNew}}
|
||||
<button id='dismiss-new' class='btn dismiss-read' {{action "resetNew"}}>{{i18n 'topics.bulk.dismiss_new'}}</button>
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
mobileAction="fullPageSearch"
|
||||
loginAction="showLogin"
|
||||
title="search.title"
|
||||
href="search"}}
|
||||
path="/search"}}
|
||||
{{/header-dropdown}}
|
||||
|
||||
{{#header-dropdown iconId="toggle-hamburger-menu"
|
||||
|
|
|
@ -25,8 +25,7 @@
|
|||
{{conditional-loading-spinner condition=model.loadingMore}}
|
||||
{{#if allLoaded}}
|
||||
{{#if showDismissRead}}
|
||||
<button title="{{i18n 'topics.bulk.dismiss_topics_tooltip'}}" id='dismiss-topics' class='btn dismiss-read' {{action "dismissRead" "topics"}}>{{i18n 'topics.bulk.dismiss_topics'}}</button>
|
||||
<button title="{{i18n 'topics.bulk.dismiss_posts_tooltip'}}" id='dismiss-posts' class='btn dismiss-read' {{action "dismissRead" "posts"}}>{{i18n 'topics.bulk.dismiss_posts'}}</button>
|
||||
<button title="{{i18n 'topics.bulk.dismiss_tooltip'}}" id='dismiss-topics' class='btn dismiss-read' {{action "dismissReadPosts"}}>{{i18n 'topics.bulk.dismiss_button'}}</button>
|
||||
{{/if}}
|
||||
{{#if showResetNew}}
|
||||
<button id='dismiss-new' class='btn dismiss-read' {{action "resetNew"}}>{{i18n 'topics.bulk.dismiss_new'}}</button>
|
||||
|
|
|
@ -1,28 +1,35 @@
|
|||
<td>
|
||||
<div class='main-link'>
|
||||
{{raw "topic-status" topic=content}}
|
||||
{{topic-link content}}
|
||||
{{raw "list/topic-excerpt" topic=content}}
|
||||
</div>
|
||||
<div class='pull-right'>
|
||||
{{raw "list/post-count-or-badges" topic=content postBadgesEnabled=controller.showTopicPostBadges}}
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
<div class='pull-left'>
|
||||
<a href="{{content.lastPostUrl}}">{{avatar topic.lastPoster imageSize="large"}}</a>
|
||||
</div>
|
||||
|
||||
<div class="topic-item-stats clearfix">
|
||||
{{#unless controller.hideCategory}}
|
||||
<div class='category'>
|
||||
{{category-link content.category}}
|
||||
<div class='right'>
|
||||
<div class='main-link'>
|
||||
{{raw "topic-status" topic=content}}
|
||||
{{topic-link content}}
|
||||
{{raw "list/topic-excerpt" topic=content}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
{{plugin-outlet "topic-list-tags"}}
|
||||
|
||||
<div class="pull-right">
|
||||
<div class='num activity last'>
|
||||
{{raw "list/activity-column" topic=content tagName="span" class="age"}}
|
||||
<a href="{{content.lastPostUrl}}" title='{{i18n 'last_post'}}: {{{raw-date content.bumped_at}}}'>{{content.last_poster_username}}</a>
|
||||
<div class='pull-right'>
|
||||
{{raw "list/post-count-or-badges" topic=content postBadgesEnabled=controller.showTopicPostBadges}}
|
||||
</div>
|
||||
|
||||
<div class="topic-item-stats clearfix">
|
||||
{{#unless controller.hideCategory}}
|
||||
<div class='category'>
|
||||
{{category-link content.category}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
{{plugin-outlet "topic-list-tags"}}
|
||||
|
||||
<div class="pull-right">
|
||||
<div class='num activity last'>
|
||||
<span class="age activity" title="{{content.bumpedAtTitle}}"><a href="{{content.lastPostUrl}}">{{format-date content.bumpedAt format="tiny" noTitle="true"}}</a></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<label class="radio" for="gravatar">{{bound-avatar-template gravatar_avatar_template "large"}} {{{i18n 'user.change_avatar.gravatar'}}} {{email}}</label>
|
||||
{{d-button action="refreshGravatar" title="user.change_avatar.refresh_gravatar_title" disabled=gravatarRefreshDisabled icon="refresh"}}
|
||||
</div>
|
||||
{{#if allowImageUpload}}
|
||||
{{#if allowAvatarUpload}}
|
||||
<div>
|
||||
<input type="radio" id="uploaded_avatar" name="avatar" value="uploaded" {{action "useUploadedAvatar"}}>
|
||||
<label class="radio" for="uploaded_avatar">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{{#each row in buttonRows}}
|
||||
{{#each buttonRows as |row|}}
|
||||
<p>
|
||||
{{#each button in row}}
|
||||
{{#each row as |button|}}
|
||||
{{d-button action=button.action label=button.label}}
|
||||
{{/each}}
|
||||
</p>
|
||||
|
|
|
@ -35,17 +35,20 @@
|
|||
</tr>
|
||||
{{/if}}
|
||||
|
||||
<tr class="input">
|
||||
<td style="width:80px" class="label"><label for='new-account-name'>{{i18n 'user.name.title'}}</label></td>
|
||||
<td style="width:496px">
|
||||
{{text-field value=accountName id="new-account-name"}}
|
||||
{{input-tip validation=nameValidation}}
|
||||
{{#if siteSettings.enable_names}}
|
||||
<tr class="input">
|
||||
<td style="width:80px" class="label">
|
||||
<label for='new-account-name'>{{i18n 'user.name.title'}}</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="instructions">
|
||||
<td style="width:496px">
|
||||
{{text-field value=accountName id="new-account-name"}} {{input-tip validation=nameValidation}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="instructions">
|
||||
<td></td>
|
||||
<td><label>{{nameInstructions}}</label></td>
|
||||
</tr>
|
||||
</tr>
|
||||
{{/if}}
|
||||
|
||||
{{#if passwordRequired}}
|
||||
<tr class="input">
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<div class="modal-body">
|
||||
<p>
|
||||
{{preference-checkbox labelKey="topics.bulk.also_dismiss_topics" checked=dismissTopics}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class='btn btn-primary' {{action "dismissReadTopics" dismissTopics}}>{{i18n 'topics.bulk.dismiss'}}</button>
|
||||
</div>
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
<div class='body'>
|
||||
{{#if ctrl.editing}}
|
||||
{{pagedown-editor value=ctrl.buffered.raw}}
|
||||
{{d-editor value=ctrl.buffered.raw}}
|
||||
{{else}}
|
||||
{{{cook-text ctrl.post.raw}}}
|
||||
{{/if}}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<div class="control-group">
|
||||
<label class="control-label">{{i18n 'user.bio'}}</label>
|
||||
<div class="controls">
|
||||
{{pagedown-editor value=model.bio_raw}}
|
||||
{{d-editor value=model.bio_raw}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -90,17 +90,13 @@
|
|||
<div class="controls">
|
||||
{{! we want the "huge" version even though we're downsizing it to "large" in CSS }}
|
||||
{{bound-avatar model "huge"}}
|
||||
{{#if allowAvatarUpload}}
|
||||
{{#unless siteSettings.sso_overrides_avatar}}
|
||||
{{d-button action="showAvatarSelector" class="pad-left" icon="pencil"}}
|
||||
{{else}}
|
||||
{{#unless ssoOverridesAvatar}}
|
||||
<a href="//gravatar.com/emails" target="_blank" title="{{i18n 'user.change_avatar.gravatar_title'}}" class="btn no-text">{{fa-icon "pencil"}}</a>
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if allowBackgrounds}}
|
||||
{{#if siteSettings.allow_profile_backgrounds}}
|
||||
<div class="control-group pref-profile-bg">
|
||||
<label class="control-label">{{i18n 'user.change_profile_background.title'}}</label>
|
||||
<div class="controls">
|
||||
|
@ -122,7 +118,7 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if allowUserLocale}}
|
||||
{{#if siteSettings.allow_user_locale}}
|
||||
<div class="control-group pref-locale">
|
||||
<label class="control-label">{{i18n 'user.locale.title'}}</label>
|
||||
<div class="controls">
|
||||
|
@ -137,13 +133,14 @@
|
|||
<div class="control-group pref-bio">
|
||||
<label class="control-label">{{i18n 'user.bio'}}</label>
|
||||
<div class="controls bio-composer">
|
||||
{{pagedown-editor value=model.bio_raw}}
|
||||
{{d-editor value=model.bio_raw}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#each uf in userFields}}
|
||||
{{user-field field=uf.field value=uf.value}}
|
||||
{{/each}}
|
||||
<div class='clearfix'></div>
|
||||
|
||||
<div class="control-group pref-location">
|
||||
<label class="control-label">{{i18n 'user.location'}}</label>
|
||||
|
@ -212,7 +209,7 @@
|
|||
{{preference-checkbox labelKey="user.enable_quoting" checked=model.enable_quoting}}
|
||||
{{preference-checkbox labelKey="user.dynamic_favicon" checked=model.dynamic_favicon}}
|
||||
{{preference-checkbox labelKey="user.disable_jump_reply" checked=model.disable_jump_reply}}
|
||||
{{#unless editHistoryVisible}}
|
||||
{{#unless siteSettings.edit_history_visible_to_public}}
|
||||
{{preference-checkbox labelKey="user.edit_history_public" checked=model.edit_history_public}}
|
||||
{{/unless}}
|
||||
|
||||
|
@ -238,6 +235,13 @@
|
|||
<div class="instructions">{{i18n 'user.muted_categories_instructions'}}</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group topics">
|
||||
<label class="control-label">{{i18n 'categories.topics'}}</label>
|
||||
<div class="controls topic-controls">
|
||||
<a href="{{unbound model.mutedTopicsPath}}">{{i18n 'user.muted_topics_link'}}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group muting">
|
||||
<label class="control-label">{{i18n 'user.users'}}</label>
|
||||
<div class="controls category-controls">
|
||||
|
|
|
@ -34,27 +34,27 @@
|
|||
<ul>
|
||||
{{#if model.can_send_private_message_to_user}}
|
||||
<li>
|
||||
<a class='btn btn-primary right' {{action "composePrivateMessage" model}}>
|
||||
<a class='btn btn-primary' {{action "composePrivateMessage" model}}>
|
||||
{{fa-icon "envelope"}}
|
||||
{{i18n 'user.private_message'}}
|
||||
</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if viewingSelf}}
|
||||
<li><a {{action "logout"}} href class='btn btn-danger right'>{{fa-icon "sign-out"}}{{i18n 'user.log_out'}}</a></li>
|
||||
<li><a {{action "logout"}} href class='btn btn-danger'>{{fa-icon "sign-out"}}{{i18n 'user.log_out'}}</a></li>
|
||||
{{/if}}
|
||||
{{#if currentUser.staff}}
|
||||
<li><a href={{model.adminPath}} class='btn right'>{{fa-icon "wrench"}}{{i18n 'admin.user.show_admin_profile'}}</a></li>
|
||||
<li><a href={{model.adminPath}} class="btn">{{fa-icon "wrench"}}{{i18n 'admin.user.show_admin_profile'}}</a></li>
|
||||
{{/if}}
|
||||
{{#if model.can_edit}}
|
||||
<li>{{#link-to 'preferences' class="btn right"}}{{fa-icon "cog"}}{{i18n 'user.preferences'}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'preferences' class="btn"}}{{fa-icon "cog"}}{{i18n 'user.preferences'}}{{/link-to}}</li>
|
||||
{{/if}}
|
||||
{{#if canInviteToForum}}
|
||||
<li>{{#link-to 'userInvited' class="btn right"}}{{fa-icon "user-plus"}}{{i18n 'user.invited.title'}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'userInvited' class="btn"}}{{fa-icon "user-plus"}}{{i18n 'user.invited.title'}}{{/link-to}}</li>
|
||||
{{/if}}
|
||||
{{#if collapsedInfo}}
|
||||
{{#if viewingSelf}}
|
||||
<li><a {{action "expandProfile"}} href class="btn right">{{fa-icon "angle-double-down"}}{{i18n 'user.expand_profile'}}</a></li>
|
||||
<li><a {{action "expandProfile"}} href class="btn">{{fa-icon "angle-double-down"}}{{i18n 'user.expand_profile'}}</a></li>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</ul>
|
||||
|
@ -122,9 +122,9 @@
|
|||
{{/if}}
|
||||
<dt>{{i18n 'views'}}</dt><dd>{{model.profile_view_count}}</dd>
|
||||
{{#if model.invited_by}}
|
||||
<dt>{{i18n 'user.invited_by'}}</dt><dd>{{#link-to 'user' model.invited_by}}{{model.invited_by.username}}{{/link-to}}</dd>
|
||||
<dt class="invited-by">{{i18n 'user.invited_by'}}</dt><dd class="invited-by">{{#link-to 'user' model.invited_by}}{{model.invited_by.username}}{{/link-to}}</dd>
|
||||
{{/if}}
|
||||
<dt>{{i18n 'user.trust_level'}}</dt><dd>{{model.trustLevel.name}}</dd>
|
||||
<dt class="trust-level">{{i18n 'user.trust_level'}}</dt><dd class="trust-level">{{model.trustLevel.name}}</dd>
|
||||
{{#if canCheckEmails}}
|
||||
<dt>{{i18n 'user.email.title'}}</dt>
|
||||
<dd title={{model.email}}>
|
||||
|
|
|
@ -185,7 +185,9 @@ const ComposerView = Ember.View.extend(Ember.Evented, {
|
|||
_applyEmojiAutocomplete() {
|
||||
if (!this.siteSettings.enable_emoji) { return; }
|
||||
|
||||
const template = this.container.lookup('template:emoji-selector-autocomplete.raw');
|
||||
const container = this.container;
|
||||
const template = container.lookup('template:emoji-selector-autocomplete.raw');
|
||||
const controller = this.get('controller');
|
||||
|
||||
this.$('.wmd-input').autocomplete({
|
||||
template: template,
|
||||
|
@ -195,7 +197,12 @@ const ComposerView = Ember.View.extend(Ember.Evented, {
|
|||
if (v.code) {
|
||||
return `${v.code}:`;
|
||||
} else {
|
||||
showSelector({ skipPrefix: true });
|
||||
showSelector({
|
||||
container,
|
||||
onSelect(title) {
|
||||
controller.appendTextAtCursor(title + ':', {space: false});
|
||||
}
|
||||
});
|
||||
return "";
|
||||
}
|
||||
},
|
||||
|
|
|
@ -26,5 +26,6 @@ export default ContainerView.extend({
|
|||
if (this.get('topic.details.can_create_post')) {
|
||||
this.attachViewClass('reply-button');
|
||||
}
|
||||
this.trigger('additionalButtons', this);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -27,9 +27,9 @@ export default ModalBodyView.extend({
|
|||
},
|
||||
|
||||
tip: function() {
|
||||
const source = this.get("controller.local") ? "local" : "remote",
|
||||
opts = { authorized_extensions: Discourse.Utilities.authorizedExtensions() };
|
||||
return uploadTranslate(source + "_tip", opts);
|
||||
const source = this.get("controller.local") ? "local" : "remote";
|
||||
const authorized_extensions = Discourse.Utilities.authorizesAllExtensions() ? "" : `(${Discourse.Utilities.authorizedExtensions()})`;
|
||||
return uploadTranslate(source + "_tip", { authorized_extensions });
|
||||
}.property("controller.local"),
|
||||
|
||||
hint: function() {
|
||||
|
|
|
@ -75,6 +75,7 @@
|
|||
//= require ./discourse/views/header
|
||||
//= require ./discourse/dialects/dialect
|
||||
//= require ./discourse/lib/emoji/emoji
|
||||
//= require ./discourse/lib/emoji/emoji-groups
|
||||
//= require ./discourse/lib/emoji/emoji-toolbar
|
||||
//= require ./discourse/views/composer
|
||||
//= require ./discourse/lib/show-modal
|
||||
|
|
|
@ -10,4 +10,5 @@
|
|||
@import "common/topic-entrance";
|
||||
@import "common/printer-friendly";
|
||||
@import "common/base/*";
|
||||
@import "common/d-editor";
|
||||
@import "vendor/pikaday";
|
||||
|
|
|
@ -213,6 +213,19 @@ td.flaggers td {
|
|||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
#last-seen {
|
||||
float: none;
|
||||
}
|
||||
.ac-wrap {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.paste-users {
|
||||
width: 400px;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.groups, .badges {
|
||||
|
@ -1015,6 +1028,11 @@ table.api-keys {
|
|||
}
|
||||
}
|
||||
|
||||
.groups-bulk {
|
||||
.control {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.commits-widget {
|
||||
border: solid 1px dark-light-diff($primary, $secondary, 90%, -60%);
|
||||
|
@ -1165,10 +1183,6 @@ table.api-keys {
|
|||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.pagedown-editor {
|
||||
width: 98%;
|
||||
}
|
||||
|
||||
textarea.plain {
|
||||
width: 98%;
|
||||
height: 200px;
|
||||
|
@ -1478,9 +1492,6 @@ and (max-width : 500px) {
|
|||
|
||||
.content-editor {
|
||||
width: 100%;
|
||||
.pagedown-editor {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
div.ac-wrap {
|
||||
|
|
|
@ -153,24 +153,6 @@ body {
|
|||
resize: none;
|
||||
}
|
||||
|
||||
.pagedown-editor {
|
||||
width: 540px;
|
||||
background-color: $secondary;
|
||||
padding: 0 10px 13px 10px;
|
||||
border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
||||
.preview {
|
||||
margin-top: 8px;
|
||||
border: 1px dashed dark-light-diff($primary, $secondary, 90%, -60%);
|
||||
padding: 8px 8px 0 8px;
|
||||
p {
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
}
|
||||
.preview.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-wrapper {
|
||||
background-color: $secondary;
|
||||
display: inline-block;
|
||||
|
|
|
@ -17,9 +17,10 @@ body img.emoji {
|
|||
background-color: dark-light-choose(#dadada, blend-primary-secondary(5%));
|
||||
}
|
||||
|
||||
.emoji-page td {
|
||||
table.emoji-page td {
|
||||
border: 1px solid transparent;
|
||||
background-color: dark-light-choose(white, $secondary);
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.emoji-page a {
|
||||
|
|
|
@ -19,12 +19,6 @@
|
|||
float: left;
|
||||
}
|
||||
|
||||
.valign-helper {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#site-logo {
|
||||
max-height: 40px;
|
||||
}
|
||||
|
|
|
@ -103,10 +103,6 @@
|
|||
|
||||
.modal.edit-category-modal {
|
||||
.modal-body {
|
||||
.pagedown-editor {
|
||||
width: 98%;
|
||||
}
|
||||
|
||||
textarea {
|
||||
height: 10em;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.uploaded-image-preview {
|
||||
background: $primary center;
|
||||
background-size: cover;
|
||||
background: $primary center center;
|
||||
}
|
||||
|
||||
.image-uploader.no-repeat {
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
.d-editor {
|
||||
border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
||||
}
|
||||
|
||||
.d-editor-container {
|
||||
padding: 0 10px 13px 10px;
|
||||
}
|
||||
|
||||
.d-editor-overlay {
|
||||
position: absolute;
|
||||
background-color: black;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.d-editor-modals {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.d-editor .d-editor-modal {
|
||||
min-width: 400px;
|
||||
position: absolute;
|
||||
background-color: $secondary;
|
||||
border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
||||
padding: 1em;
|
||||
top: 50px;
|
||||
|
||||
input {
|
||||
width: 98%;
|
||||
}
|
||||
h3 {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.d-editor-button-bar {
|
||||
margin: 5px;
|
||||
padding: 0;
|
||||
height: 20px;
|
||||
overflow: hidden;
|
||||
|
||||
button {
|
||||
background-color: transparent;
|
||||
padding: 2px 4px;
|
||||
float: left;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.d-editor-spacer {
|
||||
width: 1px;
|
||||
height: 20px;
|
||||
margin-right: 8px;
|
||||
margin-left: 5px;
|
||||
background-color: dark-light-diff($primary, $secondary, 90%, -60%);
|
||||
display: inline-block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.d-editor-input {
|
||||
color: $primary;
|
||||
width: 98%;
|
||||
height: 200px;
|
||||
|
||||
&:disabled {
|
||||
background-color: dark-light-diff($primary, $secondary, 90%, -60%);
|
||||
}
|
||||
}
|
||||
|
||||
.d-editor-preview {
|
||||
color: $primary;
|
||||
border: 1px dashed dark-light-diff($primary, $secondary, 90%, -60%);
|
||||
overflow: auto;
|
||||
visibility: visible;
|
||||
cursor: default;
|
||||
margin-top: 8px;
|
||||
padding: 8px 8px 0 8px;
|
||||
video {
|
||||
max-width: 100%;
|
||||
max-height: 500px;
|
||||
height: auto;
|
||||
}
|
||||
audio {
|
||||
max-width: 100%;
|
||||
}
|
||||
&.hidden {
|
||||
width: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.d-editor-preview > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
|
@ -84,7 +84,7 @@ body {
|
|||
margin: 0;
|
||||
i {
|
||||
font-size: 1.071em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i.fa-envelope {
|
||||
|
|
|
@ -298,6 +298,7 @@ button.dismiss-read {
|
|||
@media all
|
||||
and (max-width : 850px) {
|
||||
|
||||
// slightly smaller font, tighten spacing on nav pills
|
||||
.nav-pills {
|
||||
> li > a {
|
||||
font-size: 1em;
|
||||
|
@ -305,60 +306,32 @@ and (max-width : 850px) {
|
|||
}
|
||||
}
|
||||
|
||||
.list-controls {
|
||||
|
||||
.btn {
|
||||
font-size: 1em
|
||||
}
|
||||
|
||||
.category-dropdown-menu {
|
||||
min-width: 139px;
|
||||
}
|
||||
|
||||
a.badge-category {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.topic-list {
|
||||
.categories td.category {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
// tighter table header spacing
|
||||
th:first-of-type {
|
||||
padding: 12px 5px;
|
||||
}
|
||||
th {
|
||||
.btn .fa {
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
// smaller table cell font and cell spacing
|
||||
th, td {
|
||||
padding: 12px 2px;
|
||||
font-size: 0.929em;
|
||||
}
|
||||
.star {
|
||||
padding: 12px 5px;
|
||||
width: auto;
|
||||
}
|
||||
.main-link {
|
||||
font-size: 1.071em;
|
||||
padding: 12px 8px 12px 0px;
|
||||
}
|
||||
|
||||
.likes {
|
||||
width: auto;
|
||||
}
|
||||
.category {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.topic-excerpt {
|
||||
padding-right: 20px;
|
||||
}
|
||||
th.posters {
|
||||
text-align: center;
|
||||
}
|
||||
// suppress views column
|
||||
th.views {
|
||||
display: none;
|
||||
}
|
||||
td.views {
|
||||
display: none;
|
||||
}
|
||||
// show only the first poster
|
||||
.posters {
|
||||
min-width: 0;
|
||||
width: 50px;
|
||||
|
@ -368,6 +341,6 @@ and (max-width : 850px) {
|
|||
a.latest {
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,6 @@
|
|||
.image-upload-controls {
|
||||
padding: 10px;
|
||||
label.btn {
|
||||
padding: 7px 10px 5px 10px;
|
||||
padding: 6px 10px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,14 +39,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.pagedown-editor {
|
||||
width: 450px;
|
||||
|
||||
textarea {
|
||||
width: 440px;
|
||||
}
|
||||
}
|
||||
|
||||
.bio-composer #wmd-quote-post {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
// there are (n) new or updated topics, click to show
|
||||
.alert.alert-info {
|
||||
margin: 0;
|
||||
margin-bottom: -3px;
|
||||
margin-top: -5px;
|
||||
padding: 15px;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
// --------------------------------------------------
|
||||
|
||||
#banner {
|
||||
margin: 10px;
|
||||
// go full width on mobile, by extending into the 10px wrap
|
||||
// borders on left and right
|
||||
margin: 0 -10px;
|
||||
|
||||
@media all and (max-height: 499px) {
|
||||
max-height: 100px;
|
||||
|
|
|
@ -64,8 +64,7 @@ blockquote {
|
|||
}
|
||||
|
||||
.topic-statuses {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
float: left;
|
||||
.topic-status {
|
||||
i {
|
||||
color: dark-light-diff($secondary, $primary, 40%, -20%);
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
.d-header {
|
||||
|
||||
#site-logo {
|
||||
max-width: 155px;
|
||||
max-width: 130px;
|
||||
}
|
||||
|
||||
// some protection for text-only site titles
|
||||
.title {
|
||||
max-width: 160px;
|
||||
max-width: 130px;
|
||||
height: 39px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
|
|
|
@ -63,6 +63,10 @@
|
|||
|
||||
.topic-list {
|
||||
|
||||
.right {
|
||||
margin-left: 55px;
|
||||
}
|
||||
|
||||
> tbody > tr {
|
||||
&.highlighted {
|
||||
background-color: dark-light-choose(scale-color($tertiary, $lightness: 85%), scale-color($tertiary, $lightness: -55%));
|
||||
|
@ -120,7 +124,9 @@
|
|||
.age {
|
||||
white-space: nowrap;
|
||||
a {
|
||||
padding: 15px 10px 15px 5px;
|
||||
// let's make all ages dim on mobile so we're not
|
||||
// overwhelming people with info about each topic
|
||||
color: dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,6 +146,7 @@
|
|||
|
||||
.posts {
|
||||
width: 10%;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.age {
|
||||
|
@ -236,10 +243,15 @@ tr.category-topic-link {
|
|||
font-size: 110%;
|
||||
}
|
||||
|
||||
.category-topic-link .num {
|
||||
white-space: nowrap;
|
||||
.number {
|
||||
font-size: 1.071em;
|
||||
.category-topic-link {
|
||||
.num {
|
||||
white-space: nowrap;
|
||||
.number {
|
||||
font-size: 1.071em;
|
||||
}
|
||||
}
|
||||
.topic-excerpt {
|
||||
width: 110%;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -404,4 +416,10 @@ td .main-link {
|
|||
font-size: 1.071em;
|
||||
padding-top: 2px;
|
||||
}
|
||||
// so the topic excerpt is full width
|
||||
// as the containing div is 80%
|
||||
.topic-excerpt {
|
||||
padding-right: 0;
|
||||
width: 120%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,10 +67,6 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
.pagedown-editor {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
textarea {width: 100%;}
|
||||
}
|
||||
|
||||
|
@ -260,8 +256,9 @@
|
|||
}
|
||||
|
||||
.details {
|
||||
padding: 15px 15px 4px 15px;
|
||||
padding: 15px 10px 4px 10px;
|
||||
background-color: dark-light-choose(rgba($primary, .9), rgba($secondary, .9));
|
||||
opacity: 0.8;
|
||||
|
||||
h1 {
|
||||
font-size: 2.143em;
|
||||
|
@ -295,7 +292,6 @@
|
|||
|
||||
img.avatar {
|
||||
float: left;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.suspended {
|
||||
|
|
|
@ -34,7 +34,9 @@ class Admin::EmailController < Admin::AdminController
|
|||
|
||||
def preview_digest
|
||||
params.require(:last_seen_at)
|
||||
renderer = Email::Renderer.new(UserNotifications.digest(current_user, since: params[:last_seen_at]))
|
||||
params.require(:username)
|
||||
user = User.find_by_username(params[:username])
|
||||
renderer = Email::Renderer.new(UserNotifications.digest(user, since: params[:last_seen_at]))
|
||||
render json: MultiJson.dump(html_content: renderer.html, text_content: renderer.text)
|
||||
end
|
||||
|
||||
|
|
|
@ -19,6 +19,42 @@ class Admin::GroupsController < Admin::AdminController
|
|||
render nothing: true
|
||||
end
|
||||
|
||||
def bulk
|
||||
render nothing: true
|
||||
end
|
||||
|
||||
def bulk_perform
|
||||
group = Group.find(params[:group_id].to_i)
|
||||
if group.present?
|
||||
users = (params[:users] || []).map {|u| u.downcase}
|
||||
user_ids = User.where("username_lower in (:users) OR email IN (:users)", users: users).pluck(:id)
|
||||
|
||||
if user_ids.present?
|
||||
Group.exec_sql("INSERT INTO group_users
|
||||
(group_id, user_id, created_at, updated_at)
|
||||
SELECT #{group.id},
|
||||
u.id,
|
||||
CURRENT_TIMESTAMP,
|
||||
CURRENT_TIMESTAMP
|
||||
FROM users AS u
|
||||
WHERE u.id IN (#{user_ids.join(', ')})
|
||||
AND NOT EXISTS(SELECT 1 FROM group_users AS gu
|
||||
WHERE gu.user_id = u.id AND
|
||||
gu.group_id = #{group.id})")
|
||||
|
||||
if group.primary_group?
|
||||
User.where(id: user_ids).update_all(primary_group_id: group.id)
|
||||
end
|
||||
|
||||
if group.title.present?
|
||||
User.where(id: user_ids).update_all(title: group.title)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
render json: success_json
|
||||
end
|
||||
|
||||
def create
|
||||
group = Group.new
|
||||
|
||||
|
|
|
@ -121,7 +121,10 @@ class Admin::UsersController < Admin::AdminController
|
|||
def add_group
|
||||
group = Group.find(params[:group_id].to_i)
|
||||
return render_json_error group unless group && !group.automatic
|
||||
group.users << @user
|
||||
|
||||
# We don't care about duplicate group assignment
|
||||
group.users << @user rescue ActiveRecord::RecordNotUnique
|
||||
|
||||
render nothing: true
|
||||
end
|
||||
|
||||
|
|
|
@ -86,13 +86,11 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
rescue_from Discourse::NotLoggedIn do |e|
|
||||
raise e if Rails.env.test?
|
||||
|
||||
if (request.format && request.format.json?) || request.xhr? || !request.get?
|
||||
rescue_discourse_actions(:not_logged_in, 403, true)
|
||||
else
|
||||
redirect_to path("/")
|
||||
rescue_discourse_actions(:not_found, 404)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
rescue_from Discourse::NotFound do
|
||||
|
@ -243,7 +241,7 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
def can_cache_content?
|
||||
!current_user.present?
|
||||
current_user.blank? && flash[:authentication_data].blank?
|
||||
end
|
||||
|
||||
# Our custom cache method
|
||||
|
|
|
@ -16,7 +16,12 @@ class PostsController < ApplicationController
|
|||
end
|
||||
|
||||
def markdown_num
|
||||
markdown Post.find_by(topic_id: params[:topic_id].to_i, post_number: (params[:post_number] || 1).to_i)
|
||||
if params[:revision].present?
|
||||
post_revision = find_post_revision_from_topic_id
|
||||
render text: post_revision.modifications[:raw].last, content_type: 'text/plain'
|
||||
else
|
||||
markdown Post.find_by(topic_id: params[:topic_id].to_i, post_number: (params[:post_number] || 1).to_i)
|
||||
end
|
||||
end
|
||||
|
||||
def markdown(post)
|
||||
|
@ -403,6 +408,22 @@ class PostsController < ApplicationController
|
|||
post_revision
|
||||
end
|
||||
|
||||
def find_post_revision_from_topic_id
|
||||
post = Post.find_by(topic_id: params[:topic_id].to_i, post_number: (params[:post_number] || 1).to_i)
|
||||
raise Discourse::NotFound unless guardian.can_see?(post)
|
||||
|
||||
revision = params[:revision].to_i
|
||||
raise Discourse::NotFound if revision < 2
|
||||
|
||||
post_revision = PostRevision.find_by(post_id: post.id, number: revision)
|
||||
raise Discourse::NotFound unless post_revision
|
||||
|
||||
post_revision.post = post
|
||||
guardian.ensure_can_see!(post_revision)
|
||||
|
||||
post_revision
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def user_posts(guardian, user_id, opts)
|
||||
|
|
|
@ -37,7 +37,11 @@ class SessionController < ApplicationController
|
|||
sso.external_id = current_user.id.to_s
|
||||
sso.admin = current_user.admin?
|
||||
sso.moderator = current_user.moderator?
|
||||
redirect_to sso.to_url(sso.return_sso_url)
|
||||
if request.xhr?
|
||||
cookies[:sso_destination_url] = sso.to_url(sso.return_sso_url)
|
||||
else
|
||||
redirect_to sso.to_url(sso.return_sso_url)
|
||||
end
|
||||
else
|
||||
session[:sso_payload] = request.query_string
|
||||
redirect_to path('/login')
|
||||
|
@ -266,9 +270,8 @@ class SessionController < ApplicationController
|
|||
|
||||
if payload = session.delete(:sso_payload)
|
||||
sso_provider(payload)
|
||||
else
|
||||
render_serialized(user, UserSerializer)
|
||||
end
|
||||
render_serialized(user, UserSerializer)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue