Version bump
This commit is contained in:
commit
dae9d369ec
7
Gemfile
7
Gemfile
@ -36,7 +36,7 @@ gem 'redis-namespace'
|
||||
|
||||
gem 'active_model_serializers', '~> 0.8.3'
|
||||
|
||||
gem 'onebox', '1.8.36'
|
||||
gem 'onebox', '1.8.38'
|
||||
|
||||
gem 'http_accept_language', '~>2.0.5', require: false
|
||||
|
||||
@ -59,7 +59,7 @@ gem 'aws-sdk-s3', require: false
|
||||
gem 'excon', require: false
|
||||
gem 'unf', require: false
|
||||
|
||||
gem 'email_reply_trimmer', '0.1.9'
|
||||
gem 'email_reply_trimmer', '0.1.10'
|
||||
|
||||
# Forked until https://github.com/toy/image_optim/pull/149 is merged
|
||||
gem 'discourse_image_optim', require: 'image_optim'
|
||||
@ -67,9 +67,6 @@ gem 'multi_json'
|
||||
gem 'mustache'
|
||||
gem 'nokogiri'
|
||||
|
||||
# this may end up deprecating nokogiri
|
||||
gem 'oga', require: false
|
||||
|
||||
gem 'omniauth'
|
||||
gem 'omniauth-openid'
|
||||
gem 'openid-redis-store'
|
||||
|
||||
22
Gemfile.lock
22
Gemfile.lock
@ -41,7 +41,6 @@ GEM
|
||||
annotate (2.7.2)
|
||||
activerecord (>= 3.2, < 6.0)
|
||||
rake (>= 10.4, < 13.0)
|
||||
ansi (1.5.0)
|
||||
arel (8.0.0)
|
||||
ast (2.3.0)
|
||||
aws-partitions (1.24.0)
|
||||
@ -91,7 +90,7 @@ GEM
|
||||
image_size (~> 1.5)
|
||||
in_threads (~> 1.3)
|
||||
progress (~> 3.0, >= 3.0.1)
|
||||
email_reply_trimmer (0.1.9)
|
||||
email_reply_trimmer (0.1.10)
|
||||
ember-data-source (2.2.1)
|
||||
ember-source (>= 1.8, < 3.0)
|
||||
ember-handlebars-template (0.7.5)
|
||||
@ -165,7 +164,7 @@ GEM
|
||||
lru_redux (1.1.0)
|
||||
mail (2.6.6)
|
||||
mime-types (>= 1.16, < 4)
|
||||
memory_profiler (0.9.8)
|
||||
memory_profiler (0.9.10)
|
||||
message_bus (2.1.2)
|
||||
rack (>= 1.1.3)
|
||||
metaclass (0.0.4)
|
||||
@ -189,7 +188,7 @@ GEM
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.0.0)
|
||||
mustache (1.0.5)
|
||||
nokogiri (1.8.1)
|
||||
nokogiri (1.8.2)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
nokogumbo (1.4.13)
|
||||
nokogiri
|
||||
@ -200,9 +199,6 @@ GEM
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (>= 1.2, < 3)
|
||||
oga (2.10)
|
||||
ast
|
||||
ruby-ll (~> 2.1)
|
||||
oj (3.1.0)
|
||||
omniauth (1.6.1)
|
||||
hashie (>= 3.4.6, < 3.6.0)
|
||||
@ -232,7 +228,7 @@ GEM
|
||||
omniauth-twitter (1.3.0)
|
||||
omniauth-oauth (~> 1.1)
|
||||
rack
|
||||
onebox (1.8.36)
|
||||
onebox (1.8.38)
|
||||
fast_blank (>= 1.0.0)
|
||||
htmlentities (~> 4.3)
|
||||
moneta (~> 1.0)
|
||||
@ -275,7 +271,7 @@ GEM
|
||||
nokogiri (>= 1.6)
|
||||
rails-html-sanitizer (1.0.3)
|
||||
loofah (~> 2.0)
|
||||
rails_multisite (2.0.2)
|
||||
rails_multisite (2.0.4)
|
||||
activerecord (> 4.2, < 6)
|
||||
railties (> 4.2, < 6)
|
||||
railties (5.1.4)
|
||||
@ -334,9 +330,6 @@ GEM
|
||||
rainbow (>= 2.2.2, < 3.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (~> 1.0, >= 1.0.1)
|
||||
ruby-ll (2.1.2)
|
||||
ansi
|
||||
ast
|
||||
ruby-openid (2.7.0)
|
||||
ruby-prof (0.16.2)
|
||||
ruby-progressbar (1.9.0)
|
||||
@ -421,7 +414,7 @@ DEPENDENCIES
|
||||
cppjieba_rb
|
||||
discourse-qunit-rails
|
||||
discourse_image_optim
|
||||
email_reply_trimmer (= 0.1.9)
|
||||
email_reply_trimmer (= 0.1.10)
|
||||
ember-handlebars-template (= 0.7.5)
|
||||
ember-rails (= 0.18.5)
|
||||
ember-source (= 2.13.3)
|
||||
@ -459,7 +452,6 @@ DEPENDENCIES
|
||||
multi_json
|
||||
mustache
|
||||
nokogiri
|
||||
oga
|
||||
oj
|
||||
omniauth
|
||||
omniauth-facebook
|
||||
@ -469,7 +461,7 @@ DEPENDENCIES
|
||||
omniauth-oauth2
|
||||
omniauth-openid
|
||||
omniauth-twitter
|
||||
onebox (= 1.8.36)
|
||||
onebox (= 1.8.38)
|
||||
openid-redis-store
|
||||
pg (~> 0.21.0)
|
||||
pry-nav
|
||||
|
||||
@ -4,8 +4,6 @@ import computed from 'ember-addons/ember-computed-decorators';
|
||||
export default Ember.Component.extend({
|
||||
adminTools: Ember.inject.service(),
|
||||
expanded: false,
|
||||
suspended: false,
|
||||
|
||||
tagName: 'div',
|
||||
classNameBindings: [
|
||||
':flagged-post',
|
||||
@ -21,12 +19,7 @@ export default Ember.Component.extend({
|
||||
},
|
||||
|
||||
removeAfter(promise) {
|
||||
return promise.then(() => {
|
||||
this.attrs.removePost();
|
||||
}).catch(error => {
|
||||
if (error._discourse_displayed) { return; }
|
||||
bootbox.alert(I18n.t("admin.flags.error"));
|
||||
});
|
||||
return promise.then(() => this.attrs.removePost());
|
||||
},
|
||||
|
||||
_spawnModal(name, model, modalClass) {
|
||||
@ -36,7 +29,7 @@ export default Ember.Component.extend({
|
||||
|
||||
actions: {
|
||||
removeAfter(promise) {
|
||||
this.removeAfter(promise);
|
||||
return this.removeAfter(promise);
|
||||
},
|
||||
|
||||
disagree() {
|
||||
@ -58,18 +51,6 @@ export default Ember.Component.extend({
|
||||
filter: 'post',
|
||||
post_id: this.get('flaggedPost.id')
|
||||
});
|
||||
},
|
||||
|
||||
showSuspendModal() {
|
||||
let post = this.get('flaggedPost');
|
||||
let user = post.get('user');
|
||||
this.get('adminTools').showSuspendModal(
|
||||
user,
|
||||
{
|
||||
post,
|
||||
successCallback: result => this.set('suspended', result.suspended)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
const ACTIONS = ['delete', 'edit', 'none'];
|
||||
export default Ember.Component.extend({
|
||||
postAction: null,
|
||||
postEdit: null,
|
||||
|
||||
@computed
|
||||
penaltyActions() {
|
||||
return ACTIONS.map(id => {
|
||||
return { id, name: I18n.t(`admin.user.penalty_post_${id}`) };
|
||||
});
|
||||
},
|
||||
|
||||
editing: Ember.computed.equal('postAction', 'edit'),
|
||||
|
||||
actions: {
|
||||
penaltyChanged() {
|
||||
let postAction = this.get('postAction');
|
||||
|
||||
// If we switch to edit mode, jump to the edit textarea
|
||||
if (postAction === 'edit') {
|
||||
Ember.run.scheduleOnce('afterRender', () => {
|
||||
let $elem = this.$();
|
||||
let body = $elem.closest('.modal-body');
|
||||
body.scrollTop(body.height());
|
||||
$elem.find('.post-editor').focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
22
app/assets/javascripts/admin/components/staff-actions.js.es6
Normal file
22
app/assets/javascripts/admin/components/staff-actions.js.es6
Normal file
@ -0,0 +1,22 @@
|
||||
import DiscourseURL from 'discourse/lib/url';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['table', 'staff-actions'],
|
||||
|
||||
willDestroyElement() {
|
||||
this.$().off('click.discourse-staff-logs');
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super();
|
||||
|
||||
this.$().on('click.discourse-staff-logs', '[data-link-post-id]', e => {
|
||||
let postId = $(e.target).attr('data-link-post-id');
|
||||
|
||||
this.store.find('post', postId).then(p => {
|
||||
DiscourseURL.routeTo(p.get('url'));
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -1,26 +1,13 @@
|
||||
import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import PenaltyController from 'admin/mixins/penalty-controller';
|
||||
|
||||
export default Ember.Controller.extend(ModalFunctionality, {
|
||||
export default Ember.Controller.extend(PenaltyController, {
|
||||
silenceUntil: null,
|
||||
reason: null,
|
||||
message: null,
|
||||
silencing: false,
|
||||
user: null,
|
||||
post: null,
|
||||
successCallback: null,
|
||||
|
||||
onShow() {
|
||||
this.setProperties({
|
||||
silenceUntil: null,
|
||||
reason: null,
|
||||
message: null,
|
||||
silencing: false,
|
||||
loadingUser: true,
|
||||
post: null,
|
||||
successCallback: null,
|
||||
});
|
||||
this.resetModal();
|
||||
this.setProperties({ silenceUntil: null, silencing: false });
|
||||
},
|
||||
|
||||
@computed('silenceUntil', 'reason', 'silencing')
|
||||
@ -33,18 +20,16 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||
if (this.get('submitDisabled')) { return; }
|
||||
|
||||
this.set('silencing', true);
|
||||
this.get('user').silence({
|
||||
silenced_till: this.get('silenceUntil'),
|
||||
reason: this.get('reason'),
|
||||
message: this.get('message'),
|
||||
post_id: this.get('post.id')
|
||||
}).then(result => {
|
||||
this.send('closeModal');
|
||||
let callback = this.get('successCallback');
|
||||
if (callback) {
|
||||
callback(result);
|
||||
}
|
||||
}).catch(popupAjaxError).finally(() => this.set('silencing', false));
|
||||
this.penalize(() => {
|
||||
return this.get('user').silence({
|
||||
silenced_till: this.get('silenceUntil'),
|
||||
reason: this.get('reason'),
|
||||
message: this.get('message'),
|
||||
post_id: this.get('post.id'),
|
||||
post_action: this.get('postAction'),
|
||||
post_edit: this.get('postEdit')
|
||||
});
|
||||
}).finally(() => this.set('silencing', false));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,26 +1,13 @@
|
||||
import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import PenaltyController from 'admin/mixins/penalty-controller';
|
||||
|
||||
export default Ember.Controller.extend(ModalFunctionality, {
|
||||
export default Ember.Controller.extend(PenaltyController, {
|
||||
suspendUntil: null,
|
||||
reason: null,
|
||||
message: null,
|
||||
suspending: false,
|
||||
user: null,
|
||||
post: null,
|
||||
successCallback: null,
|
||||
|
||||
onShow() {
|
||||
this.setProperties({
|
||||
suspendUntil: null,
|
||||
reason: null,
|
||||
message: null,
|
||||
suspending: false,
|
||||
loadingUser: true,
|
||||
post: null,
|
||||
successCallback: null,
|
||||
});
|
||||
this.resetModal();
|
||||
this.setProperties({ suspendUntil: null, suspending: false });
|
||||
},
|
||||
|
||||
@computed('suspendUntil', 'reason', 'suspending')
|
||||
@ -33,19 +20,17 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||
if (this.get('submitDisabled')) { return; }
|
||||
|
||||
this.set('suspending', true);
|
||||
this.get('user').suspend({
|
||||
suspend_until: this.get('suspendUntil'),
|
||||
reason: this.get('reason'),
|
||||
message: this.get('message'),
|
||||
post_id: this.get('post.id')
|
||||
}).then(result => {
|
||||
this.send('closeModal');
|
||||
let callback = this.get('successCallback');
|
||||
if (callback) {
|
||||
callback(result);
|
||||
}
|
||||
}).catch(popupAjaxError).finally(() => this.set('suspending', false));
|
||||
|
||||
this.penalize(() => {
|
||||
return this.get('user').suspend({
|
||||
suspend_until: this.get('suspendUntil'),
|
||||
reason: this.get('reason'),
|
||||
message: this.get('message'),
|
||||
post_id: this.get('post.id'),
|
||||
post_action: this.get('postAction'),
|
||||
post_edit: this.get('postEdit')
|
||||
});
|
||||
}).finally(() => this.set('suspending', false));
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
|
||||
export default Ember.Mixin.create(ModalFunctionality, {
|
||||
reason: null,
|
||||
message: null,
|
||||
postEdit: null,
|
||||
postAction: null,
|
||||
user: null,
|
||||
post: null,
|
||||
successCallback: null,
|
||||
|
||||
resetModal() {
|
||||
this.setProperties({
|
||||
reason: null,
|
||||
message: null,
|
||||
loadingUser: true,
|
||||
post: null,
|
||||
postEdit: null,
|
||||
postAction: 'delete',
|
||||
before: null,
|
||||
successCallback: null
|
||||
});
|
||||
},
|
||||
|
||||
penalize(cb) {
|
||||
let before = this.get('before');
|
||||
let promise = before ? before() : Ember.RSVP.resolve();
|
||||
|
||||
return promise
|
||||
.then(() => cb())
|
||||
.then(result => {
|
||||
this.send('closeModal');
|
||||
let callback = this.get('successCallback');
|
||||
if (callback) {
|
||||
callback(result);
|
||||
}
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
}
|
||||
});
|
||||
@ -10,7 +10,7 @@ const StaffActionLog = Discourse.Model.extend({
|
||||
}.property('action_name'),
|
||||
|
||||
formattedDetails: function() {
|
||||
var formatted = "";
|
||||
let formatted = "";
|
||||
formatted += this.format('email', 'email');
|
||||
formatted += this.format('admin.logs.ip_address', 'ip_address');
|
||||
formatted += this.format('admin.logs.topic_id', 'topic_id');
|
||||
@ -26,9 +26,13 @@ const StaffActionLog = Discourse.Model.extend({
|
||||
return formatted;
|
||||
}.property('ip_address', 'email', 'topic_id', 'post_id', 'category_id'),
|
||||
|
||||
format: function(label, propertyName) {
|
||||
format(label, propertyName) {
|
||||
if (this.get(propertyName)) {
|
||||
return ('<b>' + I18n.t(label) + ':</b> ' + escapeExpression(this.get(propertyName)) + '<br/>');
|
||||
let value = escapeExpression(this.get(propertyName));
|
||||
if (propertyName === 'post_id') {
|
||||
value = `<a href data-link-post-id="${value}">${value}</a>`;
|
||||
}
|
||||
return `<b>${I18n.t(label)}:</b> ${value}<br/>`;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
||||
@ -52,7 +52,10 @@ export default Ember.Service.extend({
|
||||
modalClass: `${type}-user-modal`
|
||||
});
|
||||
if (opts.post) {
|
||||
controller.set('post', opts.post);
|
||||
controller.setProperties({
|
||||
post: opts.post,
|
||||
postEdit: opts.post.get('raw')
|
||||
});
|
||||
}
|
||||
|
||||
return (user.adminUserView ?
|
||||
@ -62,6 +65,7 @@ export default Ember.Service.extend({
|
||||
controller.setProperties({
|
||||
user: loadedUser,
|
||||
loadingUser: false,
|
||||
before: opts.before,
|
||||
successCallback: opts.successCallback
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
<div class='admin-backups'>
|
||||
<div class="admin-controls">
|
||||
<div class="span15">
|
||||
<nav>
|
||||
<ul class="nav nav-pills">
|
||||
{{nav-item route='admin.backups.index' label='admin.backups.menu.backups'}}
|
||||
{{nav-item route='admin.backups.logs' label='admin.backups.menu.logs'}}
|
||||
{{plugin-outlet name="downloader" tagName=""}}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="pull-right">
|
||||
{{#if model.canRollback}}
|
||||
{{d-button action="rollback"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{{#d-section class="current-badge span13"}}
|
||||
{{#d-section class="current-badge content-body"}}
|
||||
<p>{{i18n 'admin.badges.none_selected'}}</p>
|
||||
|
||||
<div>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{{#d-section class="current-badge span13"}}
|
||||
{{#d-section class="current-badge content-body"}}
|
||||
<form class="form-horizontal">
|
||||
<div>
|
||||
<label for="name">{{i18n 'admin.badges.name'}}</label>
|
||||
@ -144,7 +144,7 @@
|
||||
{{/d-section}}
|
||||
|
||||
{{#if grant_count}}
|
||||
<div class="span13 current-badge-actions">
|
||||
<div class="content-body current-badge-actions">
|
||||
<div>
|
||||
{{#link-to 'badges.show' this}}{{i18n 'badges.granted' count=grant_count}}{{/link-to}}
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<div class="badges">
|
||||
|
||||
<div class='content-list span6'>
|
||||
<div class='content-list'>
|
||||
<h3>{{i18n 'admin.badges.title'}}</h3>
|
||||
<ul>
|
||||
{{#each model as |badge|}}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<div class='admin-controls'>
|
||||
<div class='span15'>
|
||||
<nav>
|
||||
<ul class="nav nav-pills">
|
||||
{{yield}}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@ -68,12 +68,6 @@
|
||||
|
||||
{{flag-user-lists flaggedPost=flaggedPost showResolvedBy=showResolvedBy}}
|
||||
|
||||
{{#if suspended}}
|
||||
<div class='suspended-message'>
|
||||
{{i18n "admin.flags.suspended_for_post"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class='flagged-post-controls'>
|
||||
{{#if canAct}}
|
||||
{{admin-agree-flag-dropdown
|
||||
@ -106,15 +100,6 @@
|
||||
{{admin-delete-flag-dropdown
|
||||
post=flaggedPost
|
||||
removeAfter=(action "removeAfter")}}
|
||||
|
||||
{{#unless suspended}}
|
||||
{{d-button
|
||||
class="btn-danger suspend-user"
|
||||
icon="ban"
|
||||
label="admin.flags.suspend_user"
|
||||
title="admin.flags.suspend_user_title"
|
||||
action=(action "showSuspendModal")}}
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
|
||||
{{d-button
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
<div class='penalty-post-controls'>
|
||||
<label>
|
||||
<div class='penalty-post-label'>
|
||||
{{{i18n 'admin.user.penalty_post_actions'}}}
|
||||
</div>
|
||||
</label>
|
||||
{{combo-box value=postAction content=penaltyActions onSelect=(action "penaltyChanged")}}
|
||||
</div>
|
||||
|
||||
{{#if editing}}
|
||||
<div class='penalty-post-edit'>
|
||||
{{textarea
|
||||
value=postEdit
|
||||
class="post-editor"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
@ -1,4 +1,4 @@
|
||||
<div class='content-list span6 color-schemes'>
|
||||
<div class='content-list color-schemes'>
|
||||
<h3>{{i18n 'admin.customize.colors.long_title'}}</h3>
|
||||
<ul>
|
||||
{{#each model as |scheme|}}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<div class='row'>
|
||||
<div class='content-list span6'>
|
||||
<div class='content-list'>
|
||||
<ul>
|
||||
{{#each sortedTemplates as |et|}}
|
||||
<li>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{{#unless editingTheme}}
|
||||
<div class='content-list span6'>
|
||||
<div class='content-list'>
|
||||
<h3>{{i18n 'admin.customize.theme.long_title'}}</h3>
|
||||
<ul>
|
||||
{{#each sortedThemes as |theme|}}
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
<div class='controls'>
|
||||
{{text-field value=testEmailAddress placeholderKey="admin.email.test_email_address"}}
|
||||
</div>
|
||||
<div class='span10 controls'>
|
||||
<div class='controls'>
|
||||
<button class='btn btn-primary' {{action "sendTestEmail"}} disabled={{sendTestEmailDisabled}}>{{i18n 'admin.email.send_test'}}</button>
|
||||
{{#if sentTestEmail}}<span class='result-message'>{{i18n 'admin.email.sent_test'}}</span>{{/if}}
|
||||
</div>
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<p>{{emoji-uploader done="emojiUploaded"}}</p>
|
||||
|
||||
{{#if sortedEmojis}}
|
||||
<div class="span8">
|
||||
<div>
|
||||
<table id="custom_emoji">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<div class='row groups'>
|
||||
{{#if sortedGroups}}
|
||||
<div class='content-list span6'>
|
||||
<div class='content-list'>
|
||||
<h3>{{i18n 'admin.groups.edit'}}</h3>
|
||||
<ul>
|
||||
{{#each sortedGroups as |group|}}
|
||||
@ -25,7 +25,7 @@
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="span13">
|
||||
<div class="content-body">
|
||||
{{outlet}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div class='table staff-actions'>
|
||||
{{#staff-actions}}
|
||||
<div class="heading-container">
|
||||
<div class="col heading first staff_user">{{i18n 'admin.logs.staff_actions.staff_user'}}</div>
|
||||
<div class="col heading action">{{i18n 'admin.logs.action'}}</div>
|
||||
@ -86,4 +86,4 @@
|
||||
{{i18n 'search.no_results'}}
|
||||
{{/each}}
|
||||
{{/conditional-loading-spinner}}
|
||||
</div>
|
||||
{{/staff-actions}}
|
||||
|
||||
@ -12,6 +12,12 @@
|
||||
</div>
|
||||
|
||||
{{silence-details reason=reason message=message}}
|
||||
{{#if post}}
|
||||
{{penalty-post-action
|
||||
post=post
|
||||
postAction=postAction
|
||||
postEdit=postEdit}}
|
||||
{{/if}}
|
||||
|
||||
{{/conditional-loading-spinner}}
|
||||
|
||||
|
||||
@ -13,6 +13,13 @@
|
||||
</div>
|
||||
{{suspension-details reason=reason message=message}}
|
||||
|
||||
{{#if post}}
|
||||
{{penalty-post-action
|
||||
post=post
|
||||
postAction=postAction
|
||||
postEdit=postEdit}}
|
||||
{{/if}}
|
||||
|
||||
{{else}}
|
||||
<div class='cant-suspend'>
|
||||
{{i18n "admin.user.cant_suspend"}}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<div class='admin-controls'>
|
||||
<div class='span15'>
|
||||
<nav>
|
||||
<ul class='nav nav-pills'>
|
||||
<li>{{#link-to 'adminUser' user}}{{d-icon "caret-left"}} {{user.username}}{{/link-to}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{{#conditional-loading-spinner condition=loading}}
|
||||
|
||||
@ -124,7 +124,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='display-row'>
|
||||
<div class='display-row last-ip'>
|
||||
<div class='field'>{{i18n 'user.ip_address.title'}}</div>
|
||||
<div class='value'>{{model.ip_address}}</div>
|
||||
<div class='controls'>
|
||||
@ -135,7 +135,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='display-row'>
|
||||
<div class='display-row registration-ip'>
|
||||
<div class='field'>{{i18n 'user.registration_ip_address.title'}}</div>
|
||||
<div class='value'>{{model.registration_ip_address}}</div>
|
||||
<div class='controls'>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<div class='admin-controls'>
|
||||
<div class='span15'>
|
||||
<nav>
|
||||
<ul class="nav nav-pills">
|
||||
<li>{{#link-to 'adminUser' model}}{{d-icon "caret-left"}} {{model.username}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminUsersList.show' 'member'}}{{i18n 'admin.user.trust_level_2_users'}}{{/link-to}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="admin-container tl3-requirements">
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<div class='admin-controls'>
|
||||
<div class='span15'>
|
||||
<nav>
|
||||
<ul class="nav nav-pills">
|
||||
{{nav-item route='adminUsersList.show' routeParam='active' label='admin.users.nav.active'}}
|
||||
{{nav-item route='adminUsersList.show' routeParam='new' label='admin.users.nav.new'}}
|
||||
@ -11,7 +11,7 @@
|
||||
{{nav-item route='adminUsersList.show' routeParam='silenced' label='admin.users.nav.silenced'}}
|
||||
{{nav-item route='adminUsersList.show' routeParam='suspect' label='admin.users.nav.suspect'}}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="pull-right">
|
||||
{{#unless siteSettings.enable_sso}}
|
||||
{{d-button action="sendInvites" title="admin.invite.button_title" icon="user-plus" label="admin.invite.button_text"}}
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
//= require ./discourse/lib/key-value-store
|
||||
//= require ./discourse/lib/computed
|
||||
//= require ./discourse/lib/formatter
|
||||
//= require ./discourse/lib/text-direction
|
||||
//= require ./discourse/lib/eyeline
|
||||
//= require ./discourse/lib/show-modal
|
||||
//= require ./discourse/mixins/scrolling
|
||||
|
||||
@ -90,7 +90,7 @@ registerIconRenderer({
|
||||
if (params.label) { html += " aria-hidden='true'"; }
|
||||
html += `></${tagName}>`;
|
||||
if (params.label) {
|
||||
html += "<span class='sr-only'>" + I18n.t(params.label) + "</span>";
|
||||
html += `<span class='sr-only'>${params.label}</span>`;
|
||||
}
|
||||
return html;
|
||||
},
|
||||
|
||||
@ -0,0 +1,63 @@
|
||||
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
||||
import { PRIVATE_MESSAGE, CREATE_TOPIC, REPLY, EDIT } from "discourse/models/composer";
|
||||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ["composer-action-title"],
|
||||
options: Ember.computed.alias("model.replyOptions"),
|
||||
action: Ember.computed.alias("model.action"),
|
||||
isEditing: Ember.computed.equal("action", EDIT),
|
||||
|
||||
@computed("options", "action")
|
||||
actionTitle(opts, action) {
|
||||
switch (action) {
|
||||
case PRIVATE_MESSAGE:
|
||||
return I18n.t("topic.private_message");
|
||||
case CREATE_TOPIC:
|
||||
return I18n.t("topic.create_long");
|
||||
case REPLY:
|
||||
if (opts.userAvatar && opts.userLink) {
|
||||
return this._formatReplyToUserPost(opts.userAvatar, opts.userLink);
|
||||
} else if (opts.topicLink) {
|
||||
return this._formatReplyToTopic(opts.topicLink);
|
||||
}
|
||||
case EDIT:
|
||||
if (opts.userAvatar && opts.userLink && opts.postLink) {
|
||||
return this._formatEditUserPost(
|
||||
opts.userAvatar,
|
||||
opts.userLink,
|
||||
opts.postLink,
|
||||
opts.originalUser
|
||||
);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
_formatEditUserPost(userAvatar, userLink, postLink, originalUser) {
|
||||
let editTitle = `
|
||||
<a class="post-link" href="${postLink.href}">${postLink.anchor}</a>
|
||||
${userAvatar}
|
||||
<span class="username">${userLink.anchor}</span>
|
||||
${iconHTML("mail-forward", { class: "reply-to-glyph" })}
|
||||
`;
|
||||
|
||||
if (originalUser) {
|
||||
editTitle += `
|
||||
${originalUser.avatar}
|
||||
<span class="original-username">${originalUser.username}</span>
|
||||
`;
|
||||
}
|
||||
|
||||
return editTitle.htmlSafe();
|
||||
},
|
||||
|
||||
_formatReplyToTopic(link) {
|
||||
return `<a class="topic-link" href="${link.href}">${link.anchor}</a>`.htmlSafe();
|
||||
},
|
||||
|
||||
_formatReplyToUserPost(avatar, link) {
|
||||
const htmlLink = `<a class="user-link" href="${link.href}">${link.anchor}</a>`;
|
||||
return `${avatar}${htmlLink}`.htmlSafe();
|
||||
},
|
||||
|
||||
});
|
||||
@ -8,6 +8,7 @@ import { emojiSearch, isSkinTonableEmoji } from 'pretty-text/emoji';
|
||||
import { emojiUrlFor } from 'discourse/lib/text';
|
||||
import { getRegister } from 'discourse-common/lib/get-owner';
|
||||
import { findRawTemplate } from 'discourse/lib/raw-templates';
|
||||
import { siteDir } from 'discourse/lib/text-direction';
|
||||
import { determinePostReplaceSelection, clipboardData } from 'discourse/lib/utilities';
|
||||
import toMarkdown from 'discourse/lib/to-markdown';
|
||||
import deprecated from 'discourse-common/lib/deprecated';
|
||||
@ -44,7 +45,8 @@ const isInside = (text, regex) => {
|
||||
|
||||
class Toolbar {
|
||||
|
||||
constructor(site) {
|
||||
constructor(opts) {
|
||||
const { site, siteSettings } = opts;
|
||||
this.shortcuts = {};
|
||||
|
||||
this.groups = [
|
||||
@ -73,7 +75,14 @@ class Toolbar {
|
||||
perform: e => e.applySurround('_', '_', 'italic_text')
|
||||
});
|
||||
|
||||
this.addButton({id: 'link', group: 'insertions', shortcut: 'K', action: 'showLinkModal'});
|
||||
if (opts.showLink) {
|
||||
this.addButton({
|
||||
id: 'link',
|
||||
group: 'insertions',
|
||||
shortcut: 'K',
|
||||
action: 'showLinkModal'
|
||||
});
|
||||
}
|
||||
|
||||
this.addButton({
|
||||
id: 'quote',
|
||||
@ -107,6 +116,17 @@ class Toolbar {
|
||||
perform: e => e.applyList(i => !i ? "1. " : `${parseInt(i) + 1}. `, 'list_item')
|
||||
});
|
||||
|
||||
if (siteSettings.support_mixed_text_direction) {
|
||||
this.addButton({
|
||||
id: 'toggle-direction',
|
||||
group: 'extras',
|
||||
icon: 'exchange',
|
||||
shortcut: 'Shift+6',
|
||||
title: 'composer.toggle_direction',
|
||||
perform: e => e.toggleDirection(),
|
||||
});
|
||||
}
|
||||
|
||||
if (site.mobileView) {
|
||||
this.groups.push({group: 'mobileExtras', buttons: []});
|
||||
}
|
||||
@ -188,6 +208,7 @@ export default Ember.Component.extend({
|
||||
lastSel: null,
|
||||
_mouseTrap: null,
|
||||
emojiPickerIsActive: false,
|
||||
showLink: true,
|
||||
|
||||
@computed('placeholder')
|
||||
placeholderTranslated(placeholder) {
|
||||
@ -267,7 +288,9 @@ export default Ember.Component.extend({
|
||||
|
||||
@computed
|
||||
toolbar() {
|
||||
const toolbar = new Toolbar(this.site);
|
||||
const toolbar = new Toolbar(
|
||||
this.getProperties('site', 'siteSettings', 'showLink')
|
||||
);
|
||||
_createCallbacks.forEach(cb => cb(toolbar));
|
||||
this.sendAction('extraButtons', toolbar);
|
||||
return toolbar;
|
||||
@ -647,6 +670,14 @@ export default Ember.Component.extend({
|
||||
return null;
|
||||
},
|
||||
|
||||
_toggleDirection() {
|
||||
const $textArea = $(".d-editor-input");
|
||||
let currentDir = $textArea.attr('dir') ? $textArea.attr('dir') : siteDir(),
|
||||
newDir = currentDir === 'ltr' ? 'rtl' : 'ltr';
|
||||
|
||||
$textArea.attr('dir', newDir).focus();
|
||||
},
|
||||
|
||||
paste(e) {
|
||||
if (!$(".d-editor-input").is(":focus")) {
|
||||
return;
|
||||
@ -724,6 +755,7 @@ export default Ember.Component.extend({
|
||||
addText: text => this._addText(selected, text),
|
||||
replaceText: text => this._addText({pre: '', post: ''}, text),
|
||||
getText: () => this.get('value'),
|
||||
toggleDirection: () => this._toggleDirection(),
|
||||
};
|
||||
|
||||
if (button.sendAction) {
|
||||
|
||||
@ -34,13 +34,13 @@ export default Ember.Component.extend({
|
||||
|
||||
@computed('message.length')
|
||||
customMessageLengthClasses(messageLength) {
|
||||
return (messageLength < Discourse.SiteSettings.min_private_message_post_length) ? "too-short" : "ok";
|
||||
return (messageLength < Discourse.SiteSettings.min_personal_message_post_length) ? "too-short" : "ok";
|
||||
},
|
||||
|
||||
@computed('message.length')
|
||||
customMessageLength(messageLength) {
|
||||
const len = messageLength || 0;
|
||||
const minLen = Discourse.SiteSettings.min_private_message_post_length;
|
||||
const minLen = Discourse.SiteSettings.min_personal_message_post_length;
|
||||
if (len === 0) {
|
||||
return I18n.t("flagging.custom_message.at_least", { count: minLen });
|
||||
} else if (len < minLen) {
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
@computed('post.url')
|
||||
postUrl: Discourse.getURL
|
||||
});
|
||||
@ -109,12 +109,15 @@ export default Ember.TextField.extend({
|
||||
url: Discourse.getURL("/tags/filter/search"),
|
||||
dataType: 'json',
|
||||
data: function (term) {
|
||||
const selectedTags = self.get('tags');
|
||||
const d = {
|
||||
q: term,
|
||||
limit: self.siteSettings.max_tag_search_results,
|
||||
categoryId: self.get('categoryId'),
|
||||
selected_tags: self.get('tags')
|
||||
categoryId: self.get('categoryId')
|
||||
};
|
||||
if (selectedTags) {
|
||||
d.selected_tags = selectedTags.slice(0,100);
|
||||
}
|
||||
if (!self.get('everyTag')) {
|
||||
d.filterForInput = true;
|
||||
}
|
||||
|
||||
@ -1,7 +1,35 @@
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { siteDir, isRTL, isLTR } from "discourse/lib/text-direction";
|
||||
|
||||
export default Ember.TextField.extend({
|
||||
attributeBindings: ['autocorrect', 'autocapitalize', 'autofocus', 'maxLength'],
|
||||
attributeBindings: ['autocorrect', 'autocapitalize', 'autofocus', 'maxLength', 'dir'],
|
||||
|
||||
@computed
|
||||
dir() {
|
||||
if (this.siteSettings.support_mixed_text_direction) {
|
||||
let val = this.value;
|
||||
if (val) {
|
||||
return isRTL(val) ? 'rtl' : 'ltr';
|
||||
} else {
|
||||
return siteDir();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
keyUp(event) {
|
||||
this._super(event);
|
||||
|
||||
if (this.siteSettings.support_mixed_text_direction) {
|
||||
let val = this.value;
|
||||
if (isRTL(val)) {
|
||||
this.set('dir', 'rtl');
|
||||
} else if (isLTR(val)) {
|
||||
this.set('dir', 'ltr');
|
||||
} else {
|
||||
this.set('dir', siteDir());
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@computed("placeholderKey")
|
||||
placeholder(placeholderKey) {
|
||||
|
||||
@ -8,12 +8,12 @@ export default Ember.Component.extend({
|
||||
|
||||
@computed('topic.isPrivateMessage')
|
||||
canArchive(isPM) {
|
||||
return this.siteSettings.enable_private_messages && isPM;
|
||||
return this.siteSettings.enable_personal_messages && isPM;
|
||||
},
|
||||
|
||||
@computed('topic.isPrivateMessage')
|
||||
showNotificationsButton(isPM) {
|
||||
return (!isPM) || this.siteSettings.enable_private_messages;
|
||||
return (!isPM) || this.siteSettings.enable_personal_messages;
|
||||
},
|
||||
|
||||
@computed('topic.details.can_invite_to')
|
||||
|
||||
@ -8,15 +8,10 @@ export default Ember.Component.extend(bufferedRender({
|
||||
rerenderTriggers: ['topic.archived', 'topic.closed', 'topic.pinned', 'topic.visible', 'topic.unpinned', 'topic.is_warning'],
|
||||
|
||||
click(e) {
|
||||
if ($(e.target).hasClass('d-icon-thumb-tack')) {
|
||||
// only pin unpin for now
|
||||
if (this.get("canAct") && $(e.target).hasClass('d-icon-thumb-tack')) {
|
||||
const topic = this.get('topic');
|
||||
|
||||
// only pin unpin for now
|
||||
if (topic.get('pinned')) {
|
||||
topic.clearPin();
|
||||
} else {
|
||||
topic.rePin();
|
||||
}
|
||||
topic.get('pinned') ? topic.clearPin() : topic.rePin();
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@ -16,20 +16,30 @@ export default TextField.extend({
|
||||
|
||||
didInsertElement(opts) {
|
||||
this._super();
|
||||
|
||||
const bool = (n => {
|
||||
const val = this.get(n);
|
||||
return val === true || val === "true";
|
||||
});
|
||||
|
||||
var self = this,
|
||||
selected = [],
|
||||
groups = [],
|
||||
currentUser = this.currentUser,
|
||||
includeMentionableGroups = this.get('includeMentionableGroups') === 'true',
|
||||
includeMessageableGroups = this.get('includeMessageableGroups') === 'true',
|
||||
includeGroups = this.get('includeGroups') === 'true',
|
||||
allowedUsers = this.get('allowedUsers') === 'true';
|
||||
includeMentionableGroups = bool('includeMentionableGroups'),
|
||||
includeMessageableGroups = bool('includeMessageableGroups'),
|
||||
includeGroups = bool('includeGroups'),
|
||||
allowedUsers = bool('allowedUsers'),
|
||||
excludeCurrentUser = bool('excludeCurrentUser'),
|
||||
single = bool('single'),
|
||||
allowAny = bool('allowAny'),
|
||||
disabled = bool('disabled');
|
||||
|
||||
function excludedUsernames() {
|
||||
// hack works around some issues with allowAny eventing
|
||||
const usernames = self.get('single') ? [] : selected;
|
||||
const usernames = single ? [] : selected;
|
||||
|
||||
if (currentUser && self.get('excludeCurrentUser')) {
|
||||
if (currentUser && excludeCurrentUser) {
|
||||
return usernames.concat([currentUser.get('username')]);
|
||||
}
|
||||
return usernames;
|
||||
@ -37,9 +47,9 @@ export default TextField.extend({
|
||||
|
||||
this.$().val(this.get('usernames')).autocomplete({
|
||||
template: findRawTemplate('user-selector-autocomplete'),
|
||||
disabled: this.get('disabled'),
|
||||
single: this.get('single'),
|
||||
allowAny: this.get('allowAny'),
|
||||
disabled: disabled,
|
||||
single: single,
|
||||
allowAny: allowAny,
|
||||
updateData: (opts && opts.updateData) ? opts.updateData : false,
|
||||
|
||||
dataSource(term) {
|
||||
|
||||
@ -80,7 +80,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||
|
||||
if (selected.get('is_custom_flag')) {
|
||||
const len = this.get('message.length') || 0;
|
||||
return len >= Discourse.SiteSettings.min_private_message_post_length &&
|
||||
return len >= Discourse.SiteSettings.min_personal_message_post_length &&
|
||||
len <= MAX_MESSAGE_LENGTH;
|
||||
}
|
||||
return true;
|
||||
|
||||
@ -20,48 +20,54 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||
},
|
||||
|
||||
actions: {
|
||||
submit() {
|
||||
if (this.get('submitDisabled')) return false;
|
||||
|
||||
this.set('disabled', true);
|
||||
|
||||
ajax('/session/forgot_password', {
|
||||
data: { login: this.get('accountEmailOrUsername').trim() },
|
||||
type: 'POST'
|
||||
}).then(data => {
|
||||
const escaped = escapeExpression(this.get('accountEmailOrUsername'));
|
||||
const isEmail = this.get('accountEmailOrUsername').match(/@/);
|
||||
let key = 'forgot_password.complete_' + (isEmail ? 'email' : 'username');
|
||||
let extraClass;
|
||||
|
||||
if (data.user_found === true) {
|
||||
key += '_found';
|
||||
this.set('accountEmailOrUsername', '');
|
||||
this.set('offerHelp', I18n.t(key, {email: escaped, username: escaped}));
|
||||
} else {
|
||||
if (data.user_found === false) {
|
||||
key += '_not_found';
|
||||
extraClass = 'error';
|
||||
}
|
||||
|
||||
this.flash(I18n.t(key, {email: escaped, username: escaped}), extraClass);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.flash(extractError(e), 'error');
|
||||
}).finally(() => {
|
||||
setTimeout(() => this.set('disabled', false), 1000);
|
||||
});
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
ok() {
|
||||
this.send('closeModal');
|
||||
},
|
||||
|
||||
help() {
|
||||
this.setProperties({ offerHelp: I18n.t('forgot_password.help'), helpSeen: true });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
resetPassword() {
|
||||
return this._submit('/session/forgot_password', 'forgot_password.complete');
|
||||
},
|
||||
|
||||
emailLogin() {
|
||||
return this._submit('/u/email-login', 'email_login.complete');
|
||||
}
|
||||
},
|
||||
|
||||
_submit(route, translationKey) {
|
||||
if (this.get('submitDisabled')) return false;
|
||||
this.set('disabled', true);
|
||||
|
||||
ajax(route, {
|
||||
data: { login: this.get('accountEmailOrUsername').trim() },
|
||||
type: 'POST'
|
||||
}).then(data => {
|
||||
const escaped = escapeExpression(this.get('accountEmailOrUsername'));
|
||||
const isEmail = this.get('accountEmailOrUsername').match(/@/);
|
||||
let key = `${translationKey}_${isEmail ? 'email' : 'username'}`;
|
||||
let extraClass;
|
||||
|
||||
if (data.user_found === true) {
|
||||
key += '_found';
|
||||
this.set('accountEmailOrUsername', '');
|
||||
this.set('offerHelp', I18n.t(key, { email: escaped, username: escaped }));
|
||||
} else {
|
||||
if (data.user_found === false) {
|
||||
key += '_not_found';
|
||||
extraClass = 'error';
|
||||
}
|
||||
|
||||
this.flash(I18n.t(key, { email: escaped, username: escaped }), extraClass);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.flash(extractError(e), 'error');
|
||||
}).finally(() => {
|
||||
this.set('disabled', false);
|
||||
});
|
||||
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
@ -167,9 +167,9 @@ export default Ember.Controller.extend({
|
||||
return this.currentUser && this.currentUser.staff && hasResults;
|
||||
},
|
||||
|
||||
@computed('expanded', 'model.grouped_search_result.can_create_topic')
|
||||
canCreateTopic(expanded, userCanCreateTopic) {
|
||||
return this.currentUser && userCanCreateTopic && !expanded;
|
||||
@computed('model.grouped_search_result.can_create_topic')
|
||||
canCreateTopic(userCanCreateTopic) {
|
||||
return this.currentUser && userCanCreateTopic;
|
||||
},
|
||||
|
||||
@computed('expanded')
|
||||
|
||||
@ -6,6 +6,9 @@ export default Ember.Controller.extend({
|
||||
|
||||
@computed('model.is_group_user')
|
||||
showGroupMessages(isGroupUser) {
|
||||
if (!this.siteSettings.enable_personal_messages) {
|
||||
return false;
|
||||
}
|
||||
return isGroupUser || (this.currentUser && this.currentUser.admin);
|
||||
}
|
||||
});
|
||||
|
||||
@ -158,6 +158,11 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||
return Group.findAll({ term: term, ignore_automatic: true });
|
||||
},
|
||||
|
||||
@computed('isPrivateTopic', 'isMessage')
|
||||
includeMentionableGroups(isPrivateTopic, isMessage) {
|
||||
return !isPrivateTopic && !isMessage;
|
||||
},
|
||||
|
||||
@computed('isMessage', 'emailOrUsername', 'invitingExistingUserToTopic')
|
||||
successMessage(isMessage, emailOrUsername, invitingExistingUserToTopic) {
|
||||
if (this.get('hasGroups')) {
|
||||
|
||||
@ -57,16 +57,24 @@ export default Ember.Controller.extend(PreferencesTabController, {
|
||||
},
|
||||
|
||||
homeChanged() {
|
||||
const siteHome = Discourse.SiteSettings.top_menu.split("|")[0].split(",")[0];
|
||||
const siteHome = this.siteSettings.top_menu.split("|")[0].split(",")[0];
|
||||
const userHome = USER_HOMES[this.get('model.user_option.homepage_id')];
|
||||
|
||||
setDefaultHomepage(userHome || siteHome);
|
||||
},
|
||||
|
||||
@computed()
|
||||
userSelectableHome() {
|
||||
return _.map(USER_HOMES, (name, num) => {
|
||||
return {name: I18n.t('filters.' + name + '.title'), value: Number(num)};
|
||||
let homeValues = _.invert(USER_HOMES);
|
||||
|
||||
let result = [];
|
||||
this.siteSettings.top_menu.split('|').forEach(m => {
|
||||
let id = homeValues[m];
|
||||
if (id) {
|
||||
result.push({ name: I18n.t(`filters.${m}.title`), value: Number(id) });
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
||||
@ -52,7 +52,7 @@ export default Ember.Controller.extend(CanCheckEmails, {
|
||||
|
||||
@computed('viewingSelf', 'currentUser.admin')
|
||||
showPrivateMessages(viewingSelf, isAdmin) {
|
||||
return this.siteSettings.enable_private_messages && (viewingSelf || isAdmin);
|
||||
return this.siteSettings.enable_personal_messages && (viewingSelf || isAdmin);
|
||||
},
|
||||
|
||||
@computed('viewingSelf', 'currentUser.staff')
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import { htmlHelper } from 'discourse-common/lib/helpers';
|
||||
import { avatarImg } from 'discourse/lib/utilities';
|
||||
import { addExtraUserClasses } from 'discourse/helpers/user-avatar';
|
||||
|
||||
export default htmlHelper((user, size) => {
|
||||
if (Ember.isEmpty(user)) {
|
||||
return "<div class='avatar-placeholder'></div>";
|
||||
}
|
||||
|
||||
const avatarTemplate = Em.get(user, 'avatar_template');
|
||||
const avatarTemplate = Ember.get(user, 'avatar_template');
|
||||
return avatarImg({ size, avatarTemplate });
|
||||
return avatarImg(addExtraUserClasses(user, { size, avatarTemplate }));
|
||||
});
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { registerUnbound } from 'discourse-common/lib/helpers';
|
||||
import { isRTL } from "discourse/lib/text-direction";
|
||||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
|
||||
var get = Em.get,
|
||||
@ -38,6 +39,7 @@ export function categoryBadgeHTML(category, opts) {
|
||||
let color = get(category, 'color');
|
||||
let html = "";
|
||||
let parentCat = null;
|
||||
let categoryDir = "";
|
||||
|
||||
if (!opts.hideParent) {
|
||||
parentCat = Discourse.Category.findById(get(category, 'parent_category_id'));
|
||||
@ -66,10 +68,14 @@ export function categoryBadgeHTML(category, opts) {
|
||||
|
||||
let categoryName = escapeExpression(get(category, 'name'));
|
||||
|
||||
if (Discourse.SiteSettings.support_mixed_text_direction) {
|
||||
categoryDir = isRTL(categoryName) ? 'dir="rtl"' : 'dir="ltr"';
|
||||
}
|
||||
|
||||
if (restricted) {
|
||||
html += `${iconHTML('lock')}<span>${categoryName}</span>`;
|
||||
html += `${iconHTML('lock')}<span class="category-name" ${categoryDir}>${categoryName}</span>`;
|
||||
} else {
|
||||
html += `<span>${categoryName}</span>`;
|
||||
html += `<span class="category-name" ${categoryDir}>${categoryName}</span>`;
|
||||
}
|
||||
html += "</span>";
|
||||
|
||||
|
||||
15
app/assets/javascripts/discourse/helpers/dir-span.js.es6
Normal file
15
app/assets/javascripts/discourse/helpers/dir-span.js.es6
Normal file
@ -0,0 +1,15 @@
|
||||
import { registerUnbound } from "discourse-common/lib/helpers";
|
||||
import { isRTL } from 'discourse/lib/text-direction';
|
||||
|
||||
function setDir(text) {
|
||||
let content = text ? text : "";
|
||||
if (content && Discourse.SiteSettings.support_mixed_text_direction) {
|
||||
let textDir = isRTL(content) ? 'rtl' : 'ltr';
|
||||
return `<span dir="${textDir}">${content}</span>`;
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
export default registerUnbound('dir-span', function(str) {
|
||||
return new Handlebars.SafeString(setDir(str));
|
||||
});
|
||||
@ -30,7 +30,7 @@ export default htmlHelper((period, options) => {
|
||||
break;
|
||||
}
|
||||
|
||||
return `${title} <span class='top-date-string'>${dateString}</span>`;
|
||||
return `<span class="date-section">${title}</span><span class='top-date-string'>${dateString}</span>`;
|
||||
} else {
|
||||
return title;
|
||||
}
|
||||
|
||||
@ -1,6 +1,31 @@
|
||||
import { registerUnbound } from 'discourse-common/lib/helpers';
|
||||
import { avatarImg, formatUsername } from 'discourse/lib/utilities';
|
||||
|
||||
let _customAvatarHelpers;
|
||||
|
||||
export function registerCustomAvatarHelper(fn) {
|
||||
_customAvatarHelpers = _customAvatarHelpers || [];
|
||||
_customAvatarHelpers.push(fn);
|
||||
}
|
||||
|
||||
export function addExtraUserClasses(u, args) {
|
||||
let extraClasses = classesForUser(u).join(' ');
|
||||
if (extraClasses && extraClasses.length) {
|
||||
args.extraClasses = extraClasses;
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
export function classesForUser(u) {
|
||||
let result = [];
|
||||
if (_customAvatarHelpers) {
|
||||
for (let i=0; i<_customAvatarHelpers.length; i++) {
|
||||
result = result.concat(_customAvatarHelpers[i](u));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function renderAvatar(user, options) {
|
||||
options = options || {};
|
||||
|
||||
|
||||
@ -5,7 +5,6 @@ export default {
|
||||
after: "message-bus",
|
||||
|
||||
initialize(container) {
|
||||
|
||||
const banner = Em.Object.create(PreloadStore.get("banner")),
|
||||
site = container.lookup('site:main');
|
||||
|
||||
|
||||
@ -1,13 +1,18 @@
|
||||
import highlightSyntax from 'discourse/lib/highlight-syntax';
|
||||
import lightbox from 'discourse/lib/lightbox';
|
||||
import { setTextDirections } from "discourse/lib/text-direction";
|
||||
import { withPluginApi } from 'discourse/lib/plugin-api';
|
||||
|
||||
export default {
|
||||
name: "post-decorations",
|
||||
initialize() {
|
||||
initialize(container) {
|
||||
withPluginApi('0.1', api => {
|
||||
const siteSettings = container.lookup('site-settings:main');
|
||||
api.decorateCooked(highlightSyntax);
|
||||
api.decorateCooked(lightbox);
|
||||
if (siteSettings.support_mixed_text_direction) {
|
||||
api.decorateCooked(setTextDirections);
|
||||
}
|
||||
|
||||
api.decorateCooked($elem => {
|
||||
const players = $('audio', $elem);
|
||||
|
||||
@ -2,11 +2,27 @@ export default {
|
||||
name: 'register-service-worker',
|
||||
|
||||
initialize() {
|
||||
const isSecure = (document.location.protocol === 'https:') ||
|
||||
(location.hostname === "localhost");
|
||||
window.addEventListener('load', () => {
|
||||
const isSecured = (document.location.protocol === 'https:') ||
|
||||
(location.hostname === "localhost");
|
||||
|
||||
if (isSecure && ('serviceWorker' in navigator)) {
|
||||
navigator.serviceWorker.register(`${Discourse.BaseUri}/service-worker.js`);
|
||||
}
|
||||
const isSupported= isSecured && ('serviceWorker' in navigator);
|
||||
|
||||
if (isSupported) {
|
||||
if (Discourse.ServiceWorkerURL) {
|
||||
navigator.serviceWorker
|
||||
.register(`${Discourse.BaseUri}/${Discourse.ServiceWorkerURL}`)
|
||||
.catch(error => {
|
||||
Ember.Logger.info(`Failed to register Service Worker: ${error}`);
|
||||
});
|
||||
} else {
|
||||
navigator.serviceWorker.getRegistrations().then(registrations => {
|
||||
for(let registration of registrations) {
|
||||
registration.unregister();
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -54,9 +54,11 @@ export function throwAjaxError(undoCallback) {
|
||||
}
|
||||
|
||||
export function popupAjaxError(error) {
|
||||
if (error && error._discourse_displayed) { return; }
|
||||
bootbox.alert(extractError(error));
|
||||
|
||||
error._discourse_displayed = true;
|
||||
|
||||
// We re-throw in a catch to not swallow the exception
|
||||
throw error;
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ export default {
|
||||
let siteSettings = this.container.lookup('site-settings:main');
|
||||
|
||||
// Disable the shortcut if private messages are disabled
|
||||
if (!siteSettings.enable_private_messages) {
|
||||
if (!siteSettings.enable_personal_messages) {
|
||||
delete bindings['g m'];
|
||||
}
|
||||
|
||||
|
||||
@ -23,9 +23,10 @@ import { addNavItem } from 'discourse/models/nav-item';
|
||||
import { replaceFormatter } from 'discourse/lib/utilities';
|
||||
import { modifySelectKit } from "select-kit/mixins/plugin-api";
|
||||
import { addGTMPageChangedCallback } from 'discourse/lib/page-tracker';
|
||||
import { registerCustomAvatarHelper } from 'discourse/helpers/user-avatar';
|
||||
|
||||
// If you add any methods to the API ensure you bump up this number
|
||||
const PLUGIN_API_VERSION = '0.8.17';
|
||||
const PLUGIN_API_VERSION = '0.8.18';
|
||||
|
||||
class PluginApi {
|
||||
constructor(version, container) {
|
||||
@ -368,6 +369,23 @@ class PluginApi {
|
||||
appEvents.on(name, fn);
|
||||
}
|
||||
|
||||
/**
|
||||
Registers a function to generate custom avatar CSS classes
|
||||
for a particular user.
|
||||
|
||||
Takes a function that will accept a user as a parameter
|
||||
and return an array of CSS classes to apply.
|
||||
|
||||
```javascript
|
||||
api.customUserAvatarClasses(user => {
|
||||
if (Ember.get(user, 'primary_group_name') === 'managers') {
|
||||
return ['managers'];
|
||||
}
|
||||
});
|
||||
**/
|
||||
customUserAvatarClasses(fn) {
|
||||
registerCustomAvatarHelper(fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes a setting associated with a widget. For example, if
|
||||
|
||||
30
app/assets/javascripts/discourse/lib/text-direction.js.es6
Normal file
30
app/assets/javascripts/discourse/lib/text-direction.js.es6
Normal file
@ -0,0 +1,30 @@
|
||||
const ltrChars = 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF';
|
||||
const rtlChars = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC';
|
||||
const rtlDirCheck = new RegExp('^[^'+ltrChars+']*['+rtlChars+']');
|
||||
const ltrDirCheck = new RegExp('^[^'+rtlChars+']*['+ltrChars+']');
|
||||
let _siteDir;
|
||||
|
||||
export function isRTL(text) {
|
||||
return rtlDirCheck.test(text);
|
||||
}
|
||||
|
||||
export function isLTR(text) {
|
||||
return ltrDirCheck.test(text);
|
||||
}
|
||||
|
||||
export function setTextDirections($elem) {
|
||||
$elem.find('*').each((i, e) => {
|
||||
let $e = $(e),
|
||||
textContent = $e.text();
|
||||
if (textContent) {
|
||||
isRTL(textContent) ? $e.attr('dir', 'rtl') : $e.attr('dir', 'ltr');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function siteDir() {
|
||||
if (!_siteDir) {
|
||||
_siteDir = $('html').hasClass('rtl') ? 'rtl' : 'ltr';
|
||||
}
|
||||
return _siteDir;
|
||||
}
|
||||
@ -3,6 +3,7 @@ import parseHTML from 'discourse/helpers/parse-html';
|
||||
const trimLeft = text => text.replace(/^\s+/,"");
|
||||
const trimRight = text => text.replace(/\s+$/,"");
|
||||
const countPipes = text => (text.replace(/\\\|/,"").match(/\|/g) || []).length;
|
||||
const msoListClasses = ["MsoListParagraphCxSpFirst", "MsoListParagraphCxSpMiddle", "MsoListParagraphCxSpLast"];
|
||||
|
||||
class Tag {
|
||||
constructor(name, prefix = "", suffix = "", inline = false) {
|
||||
@ -207,7 +208,22 @@ class Tag {
|
||||
static li() {
|
||||
return class extends Tag.slice("li", "\n") {
|
||||
decorate(text) {
|
||||
const indent = this.element.filterParentNames(["ol", "ul"]).slice(1).map(() => "\t").join("");
|
||||
let indent = this.element.filterParentNames(["ol", "ul"]).slice(1).map(() => "\t").join("");
|
||||
const attrs = this.element.attributes;
|
||||
|
||||
if (msoListClasses.includes(attrs.class)) {
|
||||
try {
|
||||
const level = parseInt(attrs.style.match(/level./)[0].replace("level", ""));
|
||||
indent = Array(level).join("\t") + indent;
|
||||
} finally {
|
||||
if (attrs.class === "MsoListParagraphCxSpFirst") {
|
||||
indent = `\n\n${indent}`;
|
||||
} else if (attrs.class === "MsoListParagraphCxSpLast") {
|
||||
text = `${text}\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.decorate(`${indent}* ${trimLeft(text)}`);
|
||||
}
|
||||
};
|
||||
@ -356,6 +372,13 @@ class Element {
|
||||
this.parentNames = this.parentNames || [];
|
||||
this.previous = previous;
|
||||
this.next = next;
|
||||
|
||||
if (this.name === "p") {
|
||||
if (msoListClasses.includes(this.attributes.class)) {
|
||||
this.name = "li";
|
||||
this.parentNames.push("ul");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tag() {
|
||||
@ -433,7 +456,7 @@ class Element {
|
||||
}
|
||||
}
|
||||
|
||||
function trimUnwantedSpaces(html) {
|
||||
function trimUnwanted(html) {
|
||||
const body = html.match(/<body[^>]*>([\s\S]*?)<\/body>/);
|
||||
html = body ? body[1] : html;
|
||||
html = html.replace(/\r|\n| /g, " ");
|
||||
@ -443,6 +466,8 @@ function trimUnwantedSpaces(html) {
|
||||
html = html.replace(match[0], match[0].replace(/>\s{2,}</, "> <"));
|
||||
}
|
||||
|
||||
html = html.replace(/<!\[if !?\S*]>[^!]*<!\[endif]>/g, ""); // to support ms word list tags
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
@ -461,7 +486,7 @@ function putPlaceholders(html) {
|
||||
match = codeRegEx.exec(origHtml);
|
||||
}
|
||||
|
||||
const elements = parseHTML(trimUnwantedSpaces(html));
|
||||
const elements = parseHTML(trimUnwanted(html));
|
||||
return { elements, placeholders };
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,11 @@ import { defaultHomepage } from 'discourse/lib/utilities';
|
||||
const rewrites = [];
|
||||
const TOPIC_REGEXP = /\/t\/([^\/]+)\/(\d+)\/?(\d+)?/;
|
||||
|
||||
function redirectTo(url) {
|
||||
document.location = url;
|
||||
return true;
|
||||
}
|
||||
|
||||
// We can add links here that have server side responses but not client side.
|
||||
const SERVER_SIDE_ONLY = [
|
||||
/^\/assets\//,
|
||||
@ -17,6 +22,7 @@ const SERVER_SIDE_ONLY = [
|
||||
/^\/wizard/,
|
||||
/\.rss$/,
|
||||
/\.json$/,
|
||||
/^\/admin\/upgrade$/
|
||||
];
|
||||
|
||||
export function rewritePath(path) {
|
||||
@ -162,15 +168,23 @@ const DiscourseURL = Ember.Object.extend({
|
||||
if (Em.isEmpty(path)) { return; }
|
||||
|
||||
if (Discourse.get('requiresRefresh')) {
|
||||
document.location.href = Discourse.getURL(path);
|
||||
return;
|
||||
return redirectTo(Discourse.getURL(path));
|
||||
}
|
||||
|
||||
const pathname = path.replace(/(https?\:)?\/\/[^\/]+/, '');
|
||||
let baseUri = Discourse.BaseUri;
|
||||
|
||||
// If we have a baseUri and an absolute URL, make sure the baseUri
|
||||
// is the same. Otherwise we could be switching forums.
|
||||
if (baseUri &&
|
||||
path.indexOf('http') === 0 &&
|
||||
pathname.indexOf(baseUri) !== 0) {
|
||||
return redirectTo(path);
|
||||
}
|
||||
|
||||
const serverSide = SERVER_SIDE_ONLY.some(r => {
|
||||
if (pathname.match(r)) {
|
||||
document.location = path;
|
||||
return true;
|
||||
return redirectTo(path);
|
||||
}
|
||||
});
|
||||
|
||||
@ -178,8 +192,7 @@ const DiscourseURL = Ember.Object.extend({
|
||||
|
||||
// Protocol relative URLs
|
||||
if (path.indexOf('//') === 0) {
|
||||
document.location = path;
|
||||
return;
|
||||
return redirectTo(path);
|
||||
}
|
||||
|
||||
// Scroll to the same page, different anchor
|
||||
@ -193,19 +206,19 @@ const DiscourseURL = Ember.Object.extend({
|
||||
path = path.replace(/(https?\:)?\/\/[^\/]+/, '');
|
||||
|
||||
// Rewrite /my/* urls
|
||||
if (path.indexOf(Discourse.BaseUri + '/my/') === 0) {
|
||||
let myPath = `${baseUri}/my/`;
|
||||
if (path.indexOf(myPath) === 0) {
|
||||
const currentUser = Discourse.User.current();
|
||||
if (currentUser) {
|
||||
path = path.replace(Discourse.BaseUri + '/my/', userPath(currentUser.get('username_lower') + "/"));
|
||||
path = path.replace(myPath, userPath(currentUser.get('username_lower') + "/"));
|
||||
} else {
|
||||
document.location.href = "/404";
|
||||
return;
|
||||
return redirectTo('/404');
|
||||
}
|
||||
}
|
||||
|
||||
// handle prefixes
|
||||
if (path.match(/^\//)) {
|
||||
let rootURL = (Discourse.BaseUri === undefined ? "/" : Discourse.BaseUri);
|
||||
let rootURL = (baseUri === undefined ? "/" : baseUri);
|
||||
rootURL = rootURL.replace(/\/$/, '');
|
||||
path = path.replace(rootURL, '');
|
||||
}
|
||||
|
||||
@ -69,10 +69,11 @@ function organizeResults(r, options) {
|
||||
|
||||
if (r.groups) {
|
||||
r.groups.every(function(g) {
|
||||
if (results.length > limit && options.term.toLowerCase() !== g.name.toLowerCase()) return false;
|
||||
if (exclude.indexOf(g.name) === -1) {
|
||||
groups.push(g);
|
||||
results.push(g);
|
||||
if (options.term.toLowerCase() === g.name.toLowerCase() || results.length < limit) {
|
||||
if (exclude.indexOf(g.name) === -1) {
|
||||
groups.push(g);
|
||||
results.push(g);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
@ -369,7 +369,7 @@ export function displayErrorForUpload(data) {
|
||||
if (data.jqXHR.responseJSON.message) {
|
||||
bootbox.alert(data.jqXHR.responseJSON.message);
|
||||
} else {
|
||||
bootbox.alert(data.jqXHR.responseJSON.join("\n"));
|
||||
bootbox.alert(data.jqXHR.responseJSON.errors.join("\n"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -7,8 +7,8 @@ export default Ember.Mixin.create({
|
||||
this.controllerFor('composer').open({
|
||||
categoryId: controller.get('category.id'),
|
||||
action: Composer.CREATE_TOPIC,
|
||||
draftKey: controller.get('model.draft_key'),
|
||||
draftSequence: controller.get('model.draft_sequence')
|
||||
draftKey: controller.get('model.draft_key') || Composer.CREATE_TOPIC,
|
||||
draftSequence: controller.get('model.draft_sequence') || 0
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
import RestModel from 'discourse/models/rest';
|
||||
import Topic from 'discourse/models/topic';
|
||||
import { throwAjaxError } from 'discourse/lib/ajax-error';
|
||||
@ -6,45 +5,45 @@ import Quote from 'discourse/lib/quote';
|
||||
import Draft from 'discourse/models/draft';
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import { escapeExpression, tinyAvatar } from 'discourse/lib/utilities';
|
||||
import { emojiUnescape } from 'discourse/lib/text';
|
||||
|
||||
// The actions the composer can take
|
||||
export const
|
||||
CREATE_TOPIC = 'createTopic',
|
||||
PRIVATE_MESSAGE = 'privateMessage',
|
||||
NEW_PRIVATE_MESSAGE_KEY = 'new_private_message',
|
||||
REPLY = 'reply',
|
||||
EDIT = 'edit',
|
||||
REPLY_AS_NEW_TOPIC_KEY = "reply_as_new_topic",
|
||||
REPLY_AS_NEW_PRIVATE_MESSAGE_KEY = "reply_as_new_private_message";
|
||||
|
||||
const CLOSED = 'closed',
|
||||
SAVING = 'saving',
|
||||
OPEN = 'open',
|
||||
DRAFT = 'draft',
|
||||
SAVING = 'saving',
|
||||
OPEN = 'open',
|
||||
DRAFT = 'draft',
|
||||
|
||||
// The actions the composer can take
|
||||
CREATE_TOPIC = 'createTopic',
|
||||
PRIVATE_MESSAGE = 'privateMessage',
|
||||
NEW_PRIVATE_MESSAGE_KEY = 'new_private_message',
|
||||
REPLY = 'reply',
|
||||
EDIT = 'edit',
|
||||
REPLY_AS_NEW_TOPIC_KEY = "reply_as_new_topic",
|
||||
REPLY_AS_NEW_PRIVATE_MESSAGE_KEY = "reply_as_new_private_message",
|
||||
// When creating, these fields are moved into the post model from the composer model
|
||||
_create_serializer = {
|
||||
raw: 'reply',
|
||||
title: 'title',
|
||||
unlist_topic: 'unlistTopic',
|
||||
category: 'categoryId',
|
||||
topic_id: 'topic.id',
|
||||
is_warning: 'isWarning',
|
||||
whisper: 'whisper',
|
||||
archetype: 'archetypeId',
|
||||
target_usernames: 'targetUsernames',
|
||||
typing_duration_msecs: 'typingTime',
|
||||
composer_open_duration_msecs: 'composerTime',
|
||||
tags: 'tags',
|
||||
featured_link: 'featuredLink'
|
||||
},
|
||||
|
||||
// When creating, these fields are moved into the post model from the composer model
|
||||
_create_serializer = {
|
||||
raw: 'reply',
|
||||
title: 'title',
|
||||
unlist_topic: 'unlistTopic',
|
||||
category: 'categoryId',
|
||||
topic_id: 'topic.id',
|
||||
is_warning: 'isWarning',
|
||||
whisper: 'whisper',
|
||||
archetype: 'archetypeId',
|
||||
target_usernames: 'targetUsernames',
|
||||
typing_duration_msecs: 'typingTime',
|
||||
composer_open_duration_msecs: 'composerTime',
|
||||
tags: 'tags',
|
||||
featured_link: 'featuredLink'
|
||||
},
|
||||
|
||||
_edit_topic_serializer = {
|
||||
title: 'topic.title',
|
||||
categoryId: 'topic.category.id',
|
||||
tags: 'topic.tags',
|
||||
featuredLink: 'topic.featured_link'
|
||||
};
|
||||
_edit_topic_serializer = {
|
||||
title: 'topic.title',
|
||||
categoryId: 'topic.category.id',
|
||||
tags: 'topic.tags',
|
||||
featuredLink: 'topic.featured_link'
|
||||
};
|
||||
|
||||
const _saveLabels = {};
|
||||
_saveLabels[EDIT] = 'composer.save_edit';
|
||||
@ -167,52 +166,55 @@ const Composer = RestModel.extend({
|
||||
return this.get('canEditTopicFeaturedLink') ? 'composer.title_or_link_placeholder' : 'composer.title_placeholder';
|
||||
},
|
||||
|
||||
// Determine the appropriate title for this action
|
||||
actionTitle: function() {
|
||||
const topic = this.get('topic');
|
||||
@computed("action", "post", "topic", "topic.title")
|
||||
replyOptions(action, post, topic, topicTitle) {
|
||||
let options = {
|
||||
userLink: null,
|
||||
topicLink: null,
|
||||
postLink: null,
|
||||
userAvatar: null,
|
||||
originalUser: null
|
||||
};
|
||||
|
||||
let postLink, topicLink, usernameLink;
|
||||
if (topic) {
|
||||
const postNumber = this.get('post.post_number');
|
||||
postLink = "<a href='" + (topic.get('url')) + "/" + postNumber + "'>" +
|
||||
I18n.t("post.post_number", { number: postNumber }) + "</a>";
|
||||
|
||||
let title = topic.get('fancy_title') || escapeExpression(topic.get('title'));
|
||||
|
||||
topicLink = "<a href='" + (topic.get('url')) + "'> " + title + "</a>";
|
||||
usernameLink = "<a href='" + (topic.get('url')) + "/" + postNumber + "'>" + this.get('post.username') + "</a>";
|
||||
options.topicLink = {
|
||||
href: topic.get("url"),
|
||||
anchor: topic.get("fancy_title") || escapeExpression(topicTitle)
|
||||
};
|
||||
}
|
||||
|
||||
let postDescription;
|
||||
const post = this.get('post');
|
||||
|
||||
if (post) {
|
||||
postDescription = I18n.t('post.' + this.get('action'), {
|
||||
link: postLink,
|
||||
replyAvatar: tinyAvatar(post.get('avatar_template')),
|
||||
username: this.get('post.username'),
|
||||
usernameLink
|
||||
});
|
||||
options.label = I18n.t(`post.${action}`);
|
||||
options.userAvatar = tinyAvatar(post.get("avatar_template"));
|
||||
|
||||
if (!this.site.mobileView) {
|
||||
const replyUsername = post.get('reply_to_user.username');
|
||||
const replyAvatarTemplate = post.get('reply_to_user.avatar_template');
|
||||
if (replyUsername && replyAvatarTemplate && this.get('action') === EDIT) {
|
||||
postDescription += ` ${iconHTML('mail-forward', { class: 'reply-to-glyph' })} ` + tinyAvatar(replyAvatarTemplate) + " " + replyUsername;
|
||||
const originalUserName = post.get('reply_to_user.username');
|
||||
const originalUserAvatar = post.get('reply_to_user.avatar_template');
|
||||
if (originalUserName && originalUserAvatar && action === EDIT) {
|
||||
options.originalUser = {
|
||||
username: originalUserName,
|
||||
avatar: tinyAvatar(originalUserAvatar)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (this.get('action')) {
|
||||
case PRIVATE_MESSAGE: return I18n.t('topic.private_message');
|
||||
case CREATE_TOPIC: return I18n.t('topic.create_long');
|
||||
case REPLY:
|
||||
case EDIT:
|
||||
if (postDescription) return postDescription;
|
||||
if (topic) return emojiUnescape(I18n.t('post.reply_topic', { link: topicLink }));
|
||||
if (topic && post) {
|
||||
const postNumber = post.get("post_number");
|
||||
|
||||
options.postLink = {
|
||||
href: `${topic.get("url")}/${postNumber}`,
|
||||
anchor: I18n.t("post.post_number", { number: postNumber })
|
||||
};
|
||||
|
||||
options.userLink = {
|
||||
href: `${topic.get("url")}/${postNumber}`,
|
||||
anchor: post.get("username")
|
||||
};
|
||||
}
|
||||
|
||||
}.property('action', 'post', 'topic', 'topic.title'),
|
||||
return options;
|
||||
},
|
||||
|
||||
// whether to disable the post button
|
||||
cantSubmitPost: function() {
|
||||
@ -302,7 +304,7 @@ const Composer = RestModel.extend({
|
||||
@computed('privateMessage')
|
||||
minimumTitleLength(privateMessage) {
|
||||
if (privateMessage) {
|
||||
return this.siteSettings.min_private_message_title_length;
|
||||
return this.siteSettings.min_personal_message_title_length;
|
||||
} else {
|
||||
return this.siteSettings.min_topic_title_length;
|
||||
}
|
||||
@ -325,7 +327,7 @@ const Composer = RestModel.extend({
|
||||
if (pmWithNonHumanUser) {
|
||||
return 1;
|
||||
} else if (privateMessage) {
|
||||
return this.siteSettings.min_private_message_post_length;
|
||||
return this.siteSettings.min_personal_message_post_length;
|
||||
} else if (topicFirstPost) {
|
||||
// first post (topic body)
|
||||
return this.siteSettings.min_first_post_length;
|
||||
|
||||
@ -168,7 +168,8 @@ const Post = RestModel.extend({
|
||||
this.setProperties({
|
||||
deleted_at: new Date(),
|
||||
deleted_by: deletedBy,
|
||||
can_delete: false
|
||||
can_delete: false,
|
||||
can_recover: true
|
||||
});
|
||||
} else {
|
||||
promise = cookAsync(I18n.t("post.deleted_by_author", {count: Discourse.SiteSettings.delete_removed_posts_after})).then(cooked => {
|
||||
|
||||
@ -3,6 +3,7 @@ import { flushMap } from 'discourse/models/store';
|
||||
import RestModel from 'discourse/models/rest';
|
||||
import { propertyEqual } from 'discourse/lib/computed';
|
||||
import { longDate } from 'discourse/lib/formatter';
|
||||
import { isRTL } from 'discourse/lib/text-direction';
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import ActionSummary from 'discourse/models/action-summary';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
@ -58,7 +59,13 @@ const Topic = RestModel.extend({
|
||||
|
||||
@computed('fancy_title')
|
||||
fancyTitle(title) {
|
||||
return censor(emojiUnescape(title || ""), Discourse.Site.currentProp('censored_words'));
|
||||
let fancyTitle = censor(emojiUnescape(title || ""), Discourse.Site.currentProp('censored_words'));
|
||||
|
||||
if (Discourse.SiteSettings.support_mixed_text_direction) {
|
||||
let titleDir = isRTL(title) ? 'rtl' : 'ltr';
|
||||
return `<span dir="${titleDir}">${fancyTitle}</span>`;
|
||||
}
|
||||
return fancyTitle;
|
||||
},
|
||||
|
||||
// returns createdAt if there's no bumped date
|
||||
|
||||
@ -30,9 +30,20 @@ export default Discourse.Route.extend({
|
||||
afterModel(model, transition) {
|
||||
const username = transition.queryParams && transition.queryParams.username;
|
||||
|
||||
return UserBadge.findByBadgeId(model.get("id"), {username}).then(userBadges => {
|
||||
const userBadgesGrant = UserBadge.findByBadgeId(model.get("id"), {username}).then(userBadges => {
|
||||
this.userBadgesGrant = userBadges;
|
||||
});
|
||||
|
||||
const userBadgesAll = UserBadge.findByUsername(username).then(userBadges => {
|
||||
this.userBadgesAll = userBadges;
|
||||
});
|
||||
|
||||
const promises = {
|
||||
userBadgesGrant,
|
||||
userBadgesAll,
|
||||
};
|
||||
|
||||
return Ember.RSVP.hash(promises);
|
||||
},
|
||||
|
||||
titleToken() {
|
||||
|
||||
@ -1,11 +1,19 @@
|
||||
import Composer from 'discourse/models/composer';
|
||||
|
||||
const DiscourseRoute = Ember.Route.extend({
|
||||
showFooter: false,
|
||||
|
||||
// Set to true to refresh a model without a transition if a query param
|
||||
// changes
|
||||
resfreshQueryWithoutTransition: false,
|
||||
|
||||
activate() {
|
||||
this._super();
|
||||
if (this.get('showFooter')) {
|
||||
this.controllerFor('application').set('showFooter', true);
|
||||
}
|
||||
},
|
||||
|
||||
refresh() {
|
||||
if (!this.refreshQueryWithoutTransition) { return this._super(); }
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||
|
||||
export default RestrictedUserRoute.extend({
|
||||
showFooter: true,
|
||||
|
||||
model: function() {
|
||||
return this.modelFor('user');
|
||||
},
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||
|
||||
export default RestrictedUserRoute.extend({
|
||||
showFooter: true,
|
||||
|
||||
setupController(controller, user) {
|
||||
controller.reset();
|
||||
controller.setProperties({
|
||||
|
||||
@ -2,6 +2,8 @@ import UserBadge from 'discourse/models/user-badge';
|
||||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||
|
||||
export default RestrictedUserRoute.extend({
|
||||
showFooter: true,
|
||||
|
||||
model: function() {
|
||||
return UserBadge.findByUsername(this.modelFor('user').get('username'));
|
||||
},
|
||||
|
||||
@ -2,6 +2,8 @@ import UserBadge from 'discourse/models/user-badge';
|
||||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||
|
||||
export default RestrictedUserRoute.extend({
|
||||
showFooter: true,
|
||||
|
||||
model: function() {
|
||||
return UserBadge.findByUsername(this.modelFor('user').get('username'));
|
||||
},
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||
|
||||
export default RestrictedUserRoute.extend({
|
||||
showFooter: true
|
||||
});
|
||||
@ -1,6 +1,8 @@
|
||||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||
|
||||
export default RestrictedUserRoute.extend({
|
||||
showFooter: true,
|
||||
|
||||
model: function() {
|
||||
return this.modelFor('user');
|
||||
},
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||
|
||||
export default RestrictedUserRoute.extend({
|
||||
showFooter: true,
|
||||
|
||||
redirect() {
|
||||
this.transitionTo('preferences.account');
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||
|
||||
export default RestrictedUserRoute.extend({
|
||||
showFooter: true,
|
||||
|
||||
setupController(controller, user) {
|
||||
controller.setProperties({
|
||||
model: user
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||
|
||||
export default RestrictedUserRoute.extend({
|
||||
showFooter: true
|
||||
});
|
||||
@ -1,6 +1,8 @@
|
||||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||
|
||||
export default RestrictedUserRoute.extend({
|
||||
showFooter: true,
|
||||
|
||||
model: function() {
|
||||
return this.modelFor('user');
|
||||
},
|
||||
|
||||
@ -3,6 +3,7 @@ import showModal from 'discourse/lib/show-modal';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
|
||||
export default RestrictedUserRoute.extend({
|
||||
|
||||
model() {
|
||||
return this.modelFor('user');
|
||||
},
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
export default Discourse.Route.extend({
|
||||
showFooter: true,
|
||||
|
||||
model() {
|
||||
return this.modelFor("user").summary();
|
||||
},
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
<div>
|
||||
{{category-title-link category=c}}
|
||||
<div class="category-description">
|
||||
{{{c.description_excerpt}}}
|
||||
{{{dir-span c.description_excerpt}}}
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
{{d-icon 'lock'}}
|
||||
{{/if}}
|
||||
|
||||
<span class="category-name">{{category.name}}</span>
|
||||
<span class="category-name">{{dir-span category.name}}</span>
|
||||
|
||||
{{#if category.uploaded_logo.url}}
|
||||
<div>{{cdn-img src=category.uploaded_logo.url class="category-logo"}}</div>
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
{{#if isEditing}}
|
||||
{{d-icon "pencil"}}
|
||||
{{else}}
|
||||
{{composer-actions
|
||||
composerModel=model
|
||||
options=model.replyOptions
|
||||
canWhisper=canWhisper
|
||||
action=model.action}}
|
||||
{{/if}}
|
||||
|
||||
<span class="action-title">
|
||||
{{actionTitle}}
|
||||
</span>
|
||||
@ -11,6 +11,7 @@
|
||||
validation=validation
|
||||
loading=composer.loading
|
||||
forcePreview=forcePreview
|
||||
showLink=currentUser.can_post_link
|
||||
composerEvents=true
|
||||
onExpandPopupMenuOptions="onExpandPopupMenuOptions"
|
||||
onPopupMenuAction=onPopupMenuAction
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
onChangeCallback='triggerResize'
|
||||
id="private-message-users"
|
||||
includeMessageableGroups='true'
|
||||
class="span8"
|
||||
placeholderKey="composer.users_placeholder"
|
||||
tabindex="1"
|
||||
usernames=usernames
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<div class='group-post-info'>
|
||||
<div class="group-post-topic">
|
||||
<div class='group-post-title'>
|
||||
<a href={{post.url}}>{{{post.topic.fancyTitle}}}</a>
|
||||
<a href={{postUrl}}>{{{post.topic.fancyTitle}}}</a>
|
||||
</div>
|
||||
<div class="group-post-category">{{category-link post.category}}</div>
|
||||
{{#if post.user.name}}
|
||||
|
||||
@ -103,7 +103,7 @@
|
||||
{{#if hasLocationOrWebsite}}
|
||||
<div class="location-and-website">
|
||||
{{#if user.location}}
|
||||
<span class='location'>{{d-icon "map-marker"}} {{user.location}}</span>
|
||||
<span class='location'>{{d-icon "map-marker"}} <span>{{user.location}}</span></span>
|
||||
{{/if}}
|
||||
|
||||
{{#if user.website_name}}
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
{{plugin-outlet name="composer-open" args=(hash model=model)}}
|
||||
<div class='reply-to'>
|
||||
<div class="reply-details">
|
||||
{{{model.actionTitle}}}
|
||||
{{composer-action-title model=model canWhisper=canWhisper}}
|
||||
|
||||
{{#unless site.mobileView}}
|
||||
{{#if whisperOrUnlistTopicText}}
|
||||
@ -65,7 +65,7 @@
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if canEditTags}}
|
||||
{{tag-chooser tags=model.tags tabIndex="4" categoryId=model.categoryId}}
|
||||
{{mini-tag-chooser tags=model.tags tabindex="4" categoryId=model.categoryId}}
|
||||
{{/if}}
|
||||
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{{#if topic.hasExcerpt}}
|
||||
<div class="topic-excerpt">
|
||||
{{{topic.escapedExcerpt}}}
|
||||
{{{dir-span topic.escapedExcerpt}}}
|
||||
{{#if topic.excerptTruncated}}
|
||||
<a href="{{topic.url}}">{{i18n 'read_more'}}</a>
|
||||
{{/if}}
|
||||
|
||||
@ -9,10 +9,16 @@
|
||||
{{/d-modal-body}}
|
||||
<div class="modal-footer">
|
||||
{{#unless offerHelp}}
|
||||
{{d-button action="submit"
|
||||
label="forgot_password.reset"
|
||||
disabled=submitDisabled
|
||||
class="btn-primary"}}
|
||||
{{d-button action="resetPassword"
|
||||
label="forgot_password.reset"
|
||||
disabled=submitDisabled
|
||||
class="btn-primary"}}
|
||||
{{#if siteSettings.enable_local_logins_via_email}}
|
||||
{{d-button action="emailLogin"
|
||||
label="email_login.label"
|
||||
disabled=submitDisabled
|
||||
class="email-login"}}
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{d-button class="btn-large btn-primary"
|
||||
label="forgot_password.button_ok"
|
||||
|
||||
@ -14,26 +14,16 @@
|
||||
{{else}}
|
||||
<label>{{inviteInstructions}}</label>
|
||||
{{#if allowExistingMembers}}
|
||||
{{#if isPrivateTopic}}
|
||||
{{user-selector single="true"
|
||||
allowAny=true
|
||||
excludeCurrentUser="true"
|
||||
usernames=emailOrUsername
|
||||
allowedUsers="true"
|
||||
topicId=topicId
|
||||
placeholderKey=placeholderKey
|
||||
autocomplete="off"}}
|
||||
{{else}}
|
||||
{{user-selector
|
||||
single="true"
|
||||
allowAny=true
|
||||
excludeCurrentUser="true"
|
||||
includeMentionableGroups="true"
|
||||
hasGroups=hasGroups
|
||||
usernames=emailOrUsername
|
||||
placeholderKey=placeholderKey
|
||||
autocomplete="off"}}
|
||||
{{/if}}
|
||||
{{user-selector
|
||||
single=true
|
||||
allowAny=true
|
||||
excludeCurrentUser=true
|
||||
includeMentionableGroups=includeMentionableGroups
|
||||
includeMessageableGroups=isMessage
|
||||
hasGroups=hasGroups
|
||||
usernames=emailOrUsername
|
||||
placeholderKey=placeholderKey
|
||||
autocomplete="off"}}
|
||||
{{else}}
|
||||
{{text-field value=emailOrUsername placeholderKey="topic.invite_reply.email_placeholder"}}
|
||||
{{/if}}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{{#d-modal-body id="keyboard-shortcuts-help"}}
|
||||
<div class="row">
|
||||
<div class="span6">
|
||||
<div>
|
||||
<h4>{{i18n 'keyboard_shortcuts_help.jump_to.title'}}</h4>
|
||||
<ul>
|
||||
<li>{{{i18n 'keyboard_shortcuts_help.jump_to.home'}}}</li>
|
||||
@ -11,7 +11,7 @@
|
||||
<li>{{{i18n 'keyboard_shortcuts_help.jump_to.top'}}}</li>
|
||||
<li>{{{i18n 'keyboard_shortcuts_help.jump_to.bookmarks'}}}</li>
|
||||
<li>{{{i18n 'keyboard_shortcuts_help.jump_to.profile'}}}</li>
|
||||
{{#if siteSettings.enable_private_messages}}
|
||||
{{#if siteSettings.enable_personal_messages}}
|
||||
<li>{{{i18n 'keyboard_shortcuts_help.jump_to.messages'}}}</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
@ -24,7 +24,7 @@
|
||||
<li>{{{i18n 'keyboard_shortcuts_help.navigation.next_prev'}}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="span6">
|
||||
<div>
|
||||
<h4>{{i18n 'keyboard_shortcuts_help.application.title'}}</h4>
|
||||
<ul>
|
||||
<li>{{{i18n 'keyboard_shortcuts_help.application.hamburger_menu'}}}</li>
|
||||
@ -46,7 +46,7 @@
|
||||
<li>{{{i18n 'keyboard_shortcuts_help.actions.quote_post'}}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="span6">
|
||||
<div>
|
||||
<h4>{{i18n 'keyboard_shortcuts_help.actions.title'}}</h4>
|
||||
<ul>
|
||||
<li>{{{i18n 'keyboard_shortcuts_help.actions.bookmark_topic'}}}</li>
|
||||
|
||||
@ -6,24 +6,14 @@
|
||||
<div>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<label for='login-account-name'>{{i18n 'login.username'}} </label>
|
||||
</td>
|
||||
<td>
|
||||
{{text-field value=loginName placeholderKey="login.email_placeholder" id="login-account-name" autocorrect="off" autocapitalize="off" autofocus="autofocus"}}
|
||||
</td>
|
||||
<td><label for='login-account-name'>{{i18n 'login.username'}}</label></td>
|
||||
<td>{{text-field value=loginName placeholderKey="login.email_placeholder" id="login-account-name" autocorrect="off" autocapitalize="off" autofocus="autofocus"}}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<label for='login-account-password'>{{i18n 'login.password'}} </label>
|
||||
</td>
|
||||
<td>
|
||||
{{password-field value=loginPassword type="password" id="login-account-password" maxlength="200" capsLockOn=capsLockOn}}
|
||||
</td>
|
||||
<td>
|
||||
<a id="forgot-password-link" {{action "forgotPassword"}}>{{i18n 'forgot_password.action'}}</a>
|
||||
</td>
|
||||
<td><label for='login-account-password'>{{i18n 'login.password'}}</label></td>
|
||||
<td>{{password-field value=loginPassword type="password" id="login-account-password" maxlength="200" capsLockOn=capsLockOn}}</td>
|
||||
<td><a id="forgot-password-link" {{action "forgotPassword"}}>{{i18n 'forgot_password.action'}}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
@ -40,14 +30,11 @@
|
||||
|
||||
<div class="modal-footer">
|
||||
{{#if canLoginLocal}}
|
||||
<button class="btn btn-large btn-primary"
|
||||
disabled={{loginDisabled}}
|
||||
{{action "login"}}>
|
||||
{{d-icon "unlock"}} {{loginButtonText}}
|
||||
<button form="login-form" type="submit" class="btn btn-large btn-primary" disabled={{loginDisabled}} {{action "login"}}>
|
||||
{{d-icon "unlock"}} {{loginButtonText}}
|
||||
</button>
|
||||
|
||||
{{#if showSignupLink}}
|
||||
|
||||
<button class="btn btn-large" id="new-account-link" {{action "createAccount"}}>
|
||||
{{i18n 'create_account.title'}}
|
||||
</button>
|
||||
@ -55,7 +42,7 @@
|
||||
{{/if}}
|
||||
|
||||
{{#if authenticate}}
|
||||
{{i18n 'login.authenticating'}}
|
||||
{{i18n 'login.authenticating'}}
|
||||
{{/if}}
|
||||
|
||||
{{conditional-loading-spinner condition=showSpinner size="small"}}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
{{#if category.uploaded_logo.url}}
|
||||
{{cdn-img src=category.uploaded_logo.url class="category-logo"}}
|
||||
{{#if category.description}}
|
||||
<p>{{{category.description}}}</p>
|
||||
<p>{{{dir-span category.description}}}</p>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</section>
|
||||
|
||||
@ -26,15 +26,17 @@
|
||||
<div class="instructions">{{i18n 'user.desktop_notifications.each_browser_note'}}</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group private-messages">
|
||||
<label class="control-label">{{i18n 'user.private_messages'}}</label>
|
||||
{{#if siteSettings.enable_personal_messages}}
|
||||
<div class="control-group private-messages">
|
||||
<label class="control-label">{{i18n 'user.private_messages'}}</label>
|
||||
|
||||
<div class="controls">
|
||||
{{preference-checkbox
|
||||
labelKey="user.allow_private_messages"
|
||||
checked=model.user_option.allow_private_messages}}
|
||||
<div class="controls">
|
||||
{{preference-checkbox
|
||||
labelKey="user.allow_private_messages"
|
||||
checked=model.user_option.allow_private_messages}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="control-group muting">
|
||||
<label class="control-label">{{i18n 'user.users'}}</label>
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
{{else}}
|
||||
<h1>
|
||||
{{#unless model.is_warning}}
|
||||
{{#if siteSettings.enable_private_messages}}
|
||||
{{#if siteSettings.enable_personal_messages}}
|
||||
<a href={{pmPath}}>
|
||||
<span class="private-message-glyph">{{d-icon "envelope"}}</span>
|
||||
</a>
|
||||
|
||||
@ -5,13 +5,13 @@
|
||||
<h2>{{i18n 'user.invited.title'}}</h2>
|
||||
|
||||
{{#if model.can_see_invite_details}}
|
||||
<div class='user-invite-controls'>
|
||||
<div class='span15'>
|
||||
<div class='admin-controls'>
|
||||
<nav>
|
||||
<ul class="nav nav-pills">
|
||||
{{nav-item route='userInvited.show' routeParam='pending' i18nLabel=pendingLabel}}
|
||||
{{nav-item route='userInvited.show' routeParam='redeemed' i18nLabel=redeemedLabel}}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="pull-right">
|
||||
{{d-button icon="plus" action="showInvite" label="user.invited.create" class="btn"}}
|
||||
|
||||
@ -39,7 +39,16 @@
|
||||
<div class='profile-image'></div>
|
||||
<div class='details'>
|
||||
<div class='primary'>
|
||||
{{bound-avatar model "huge"}}
|
||||
<div class='user-profile-avatar'>
|
||||
{{bound-avatar model "huge"}}
|
||||
{{#if model.primary_group_name}}
|
||||
{{avatar-flair
|
||||
flairURL=model.primary_group_flair_url
|
||||
flairBgColor=model.primary_group_flair_bg_color
|
||||
flairColor=model.primary_group_flair_color
|
||||
groupName=model.primary_group_name}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<section class='controls'>
|
||||
<ul>
|
||||
{{#if model.can_send_private_message_to_user}}
|
||||
@ -73,7 +82,7 @@
|
||||
<h3>{{model.title}}</h3>
|
||||
{{/if}}
|
||||
{{plugin-outlet name="user-post-names" args=(hash model=model)}}
|
||||
<h3>
|
||||
<h3 class="location-and-website">
|
||||
{{#if model.location}}{{d-icon "map-marker"}} {{model.location}}{{/if}}
|
||||
{{#if model.website_name}}
|
||||
{{d-icon "globe"}}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user