Version bump
This commit is contained in:
commit
714ea51990
@ -248,7 +248,7 @@ GEM
|
||||
omniauth-twitter (1.2.1)
|
||||
json (~> 1.3)
|
||||
omniauth-oauth (~> 1.1)
|
||||
onebox (1.5.26)
|
||||
onebox (1.5.27)
|
||||
moneta (~> 0.8)
|
||||
multi_json (~> 1.11)
|
||||
mustache
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,7 +68,8 @@ export default Ember.Controller.extend(BufferedContent, {
|
||||
model = this.get('model');
|
||||
this.get('model').save(data).then(function() {
|
||||
if (newBadge) {
|
||||
self.get('controllers.admin-badges').pushObject(model);
|
||||
var adminBadgesController = self.get('controllers.admin-badges');
|
||||
if (!adminBadgesController.contains(model)) adminBadgesController.pushObject(model);
|
||||
self.transitionToRoute('adminBadges.show', model.get('id'));
|
||||
} else {
|
||||
self.commitBuffer();
|
||||
|
||||
@ -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);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
13
app/assets/javascripts/admin/routes/admin-groups-bulk.js.es6
Normal file
13
app/assets/javascripts/admin/routes/admin-groups-bulk.js.es6
Normal file
@ -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>
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
{{plugin-outlet "admin-dashboard-top"}}
|
||||
|
||||
<div class="dashboard-left">
|
||||
{{#if showVersionChecks}}
|
||||
{{partial 'admin/templates/version-checks'}}
|
||||
|
||||
@ -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>
|
||||
19
app/assets/javascripts/admin/templates/groups-bulk.hbs
Normal file
19
app/assets/javascripts/admin/templates/groups-bulk.hbs
Normal file
@ -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">
|
||||
|
||||
@ -1,18 +1,3 @@
|
||||
/*global Mousetrap:true */
|
||||
|
||||
export default Ember.View.extend({
|
||||
classNames: ['customize'],
|
||||
|
||||
_init: function() {
|
||||
var controller = this.get('controller');
|
||||
Mousetrap.bindGlobal('mod+s', function() {
|
||||
controller.send("save");
|
||||
return false;
|
||||
});
|
||||
}.on("didInsertElement"),
|
||||
|
||||
_cleanUp: function() {
|
||||
Mousetrap.unbindGlobal('mod+s');
|
||||
}.on("willDestroyElement")
|
||||
|
||||
classNames: ['customize']
|
||||
});
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ export default Ember.Component.extend({
|
||||
});
|
||||
},
|
||||
|
||||
@on('willDestoryElement')
|
||||
@on('willDestroyElement')
|
||||
_stopListening() {
|
||||
this.$().off('keydown.d-modal');
|
||||
},
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
/*global Mousetrap:true */
|
||||
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";
|
||||
@ -12,6 +13,130 @@ function getHead(head, prev) {
|
||||
}
|
||||
}
|
||||
|
||||
const _createCallbacks = [];
|
||||
|
||||
function Toolbar() {
|
||||
this.shortcuts = {};
|
||||
|
||||
this.groups = [
|
||||
{group: 'fontStyles', buttons: []},
|
||||
{group: 'insertions', buttons: []},
|
||||
{group: 'extras', buttons: [], lastGroup: true}
|
||||
];
|
||||
|
||||
this.addButton({
|
||||
id: 'bold',
|
||||
group: 'fontStyles',
|
||||
shortcut: 'B',
|
||||
perform: e => e.applySurround('**', '**', 'bold_text')
|
||||
});
|
||||
|
||||
this.addButton({
|
||||
id: 'italic',
|
||||
group: 'fontStyles',
|
||||
shortcut: 'I',
|
||||
perform: e => e.applySurround('*', '*', 'italic_text')
|
||||
});
|
||||
|
||||
this.addButton({id: 'link', group: 'insertions', shortcut: 'K', action: 'showLinkModal'});
|
||||
|
||||
this.addButton({
|
||||
id: 'quote',
|
||||
group: 'insertions',
|
||||
icon: 'quote-right',
|
||||
shortcut: 'Shift+9',
|
||||
perform: e => e.applySurround('> ', '', 'code_text')
|
||||
});
|
||||
|
||||
this.addButton({
|
||||
id: 'code',
|
||||
group: 'insertions',
|
||||
shortcut: 'Shift+C',
|
||||
perform(e) {
|
||||
if (e.selected.value.indexOf("\n") !== -1) {
|
||||
e.applySurround(' ', '', 'code_text');
|
||||
} else {
|
||||
e.applySurround('`', '`', 'code_text');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
this.addButton({
|
||||
id: 'bullet',
|
||||
group: 'extras',
|
||||
icon: 'list-ul',
|
||||
shortcut: 'Shift+8',
|
||||
title: 'composer.ulist_title',
|
||||
perform: e => e.applyList('* ', 'list_item')
|
||||
});
|
||||
|
||||
this.addButton({
|
||||
id: 'list',
|
||||
group: 'extras',
|
||||
icon: 'list-ol',
|
||||
shortcut: 'Shift+7',
|
||||
title: 'composer.olist_title',
|
||||
perform: e => e.applyList(i => !i ? "1. " : `${parseInt(i) + 1}. `, 'list_item')
|
||||
});
|
||||
|
||||
this.addButton({
|
||||
id: 'heading',
|
||||
group: 'extras',
|
||||
icon: 'font',
|
||||
shortcut: 'Alt+1',
|
||||
perform: e => e.applyList('## ', 'heading_text')
|
||||
});
|
||||
|
||||
this.addButton({
|
||||
id: 'rule',
|
||||
group: 'extras',
|
||||
icon: 'minus',
|
||||
shortcut: 'Alt+R',
|
||||
title: 'composer.hr_title',
|
||||
perform: e => e.addText("\n\n----------\n")
|
||||
});
|
||||
};
|
||||
|
||||
Toolbar.prototype.addButton = function(button) {
|
||||
const g = this.groups.findProperty('group', button.group);
|
||||
if (!g) {
|
||||
throw `Couldn't find toolbar group ${button.group}`;
|
||||
}
|
||||
|
||||
const createdButton = {
|
||||
id: button.id,
|
||||
className: button.className || button.id,
|
||||
icon: button.icon || button.id,
|
||||
action: button.action || 'toolbarButton',
|
||||
perform: button.perform || Ember.K
|
||||
};
|
||||
|
||||
const title = I18n.t(button.title || `composer.${button.id}_title`);
|
||||
if (button.shortcut) {
|
||||
const mac = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
|
||||
const mod = mac ? 'Meta' : 'Ctrl';
|
||||
createdButton.title = `${title} (${mod}+${button.shortcut})`;
|
||||
|
||||
// Mac users are used to glyphs for shortcut keys
|
||||
if (mac) {
|
||||
createdButton.title = createdButton.title.replace('Shift', "\u21E7")
|
||||
.replace('Meta', "\u2318")
|
||||
.replace('Alt', "\u2325")
|
||||
.replace(/\+/g, '');
|
||||
}
|
||||
|
||||
this.shortcuts[`${mod}+${button.shortcut}`.toLowerCase()] = createdButton;
|
||||
} else {
|
||||
createdButton.title = title;
|
||||
}
|
||||
|
||||
g.buttons.push(createdButton);
|
||||
};
|
||||
|
||||
export function onToolbarCreate(func) {
|
||||
_createCallbacks.push(func);
|
||||
};
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['d-editor'],
|
||||
ready: false,
|
||||
@ -20,16 +145,38 @@ export default Ember.Component.extend({
|
||||
lastSel: null,
|
||||
|
||||
@on('didInsertElement')
|
||||
_loadSanitizer() {
|
||||
_startUp() {
|
||||
this._applyEmojiAutocomplete();
|
||||
loadScript('defer/html-sanitizer-bundle').then(() => this.set('ready', true));
|
||||
|
||||
const shortcuts = this.get('toolbar.shortcuts');
|
||||
Ember.keys(shortcuts).forEach(sc => {
|
||||
const button = shortcuts[sc];
|
||||
Mousetrap(this.$('.d-editor-input')[0]).bind(sc, () => {
|
||||
this.send(button.action, button);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
@on('willDestroyElement')
|
||||
_shutDown() {
|
||||
Ember.keys(this.get('toolbar.shortcuts')).forEach(sc => {
|
||||
Mousetrap(this.$('.d-editor-input')[0]).unbind(sc);
|
||||
});
|
||||
},
|
||||
|
||||
@property
|
||||
toolbar() {
|
||||
const toolbar = new Toolbar();
|
||||
_createCallbacks.forEach(cb => cb(toolbar));
|
||||
return toolbar;
|
||||
},
|
||||
|
||||
@property('ready', 'value')
|
||||
preview(ready, value) {
|
||||
if (!ready) { return; }
|
||||
|
||||
const text = Discourse.Dialect.cook(value || "", {});
|
||||
const text = Discourse.Dialect.cook(value || "", {sanitize: true});
|
||||
return text ? text : "";
|
||||
},
|
||||
|
||||
@ -51,7 +198,7 @@ export default Ember.Component.extend({
|
||||
showSelector({
|
||||
appendTo: self.$(),
|
||||
container,
|
||||
onSelect: title => self._addText(`${title}:`)
|
||||
onSelect: title => self._addText(this._getSelected(), `${title}:`)
|
||||
});
|
||||
return "";
|
||||
}
|
||||
@ -112,8 +259,7 @@ export default Ember.Component.extend({
|
||||
});
|
||||
},
|
||||
|
||||
_applySurround(head, tail, exampleKey) {
|
||||
const sel = this._getSelected();
|
||||
_applySurround(sel, head, tail, exampleKey) {
|
||||
const pre = sel.pre;
|
||||
const post = sel.post;
|
||||
|
||||
@ -162,10 +308,9 @@ export default Ember.Component.extend({
|
||||
}
|
||||
},
|
||||
|
||||
_applyList(head, exampleKey) {
|
||||
const sel = this._getSelected();
|
||||
_applyList(sel, head, exampleKey) {
|
||||
if (sel.value.indexOf("\n") !== -1) {
|
||||
this._applySurround(head, '', exampleKey);
|
||||
this._applySurround(sel, head, '', exampleKey);
|
||||
} else {
|
||||
|
||||
const [hval, hlen] = getHead(head);
|
||||
@ -185,20 +330,21 @@ export default Ember.Component.extend({
|
||||
}
|
||||
},
|
||||
|
||||
_addText(text, sel) {
|
||||
sel = sel || this._getSelected();
|
||||
_addText(sel, text) {
|
||||
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');
|
||||
toolbarButton(button) {
|
||||
const selected = this._getSelected();
|
||||
button.perform({
|
||||
selected,
|
||||
applySurround: (head, tail, exampleKey) => this._applySurround(selected, head, tail, exampleKey),
|
||||
applyList: (head, exampleKey) => this._applyList(selected, head, exampleKey),
|
||||
addText: text => this._addText(selected, text)
|
||||
});
|
||||
},
|
||||
|
||||
showLinkModal() {
|
||||
@ -214,48 +360,19 @@ export default Ember.Component.extend({
|
||||
if (m && m.length === 2) {
|
||||
const description = m[1];
|
||||
const remaining = link.replace(m[0], '');
|
||||
this._addText(`[${description}](${remaining})`, this._lastSel);
|
||||
this._addText(this._lastSel, `[${description}](${remaining})`);
|
||||
} else {
|
||||
this._addText(`[${link}](${link})`, this._lastSel);
|
||||
this._addText(this._lastSel, `[${link}](${link})`);
|
||||
}
|
||||
|
||||
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}:`)
|
||||
onSelect: title => this._addText(this._getSelected(), `:${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'),
|
||||
|
||||
@ -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(""));
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { showSelector } from "discourse/lib/emoji/emoji-toolbar";
|
||||
import { onToolbarCreate } from 'discourse/components/d-editor';
|
||||
|
||||
export default {
|
||||
name: 'enable-emoji',
|
||||
@ -6,6 +7,18 @@ export default {
|
||||
initialize(container) {
|
||||
const siteSettings = container.lookup('site-settings:main');
|
||||
if (siteSettings.enable_emoji) {
|
||||
|
||||
onToolbarCreate(toolbar => {
|
||||
toolbar.addButton({
|
||||
id: 'emoji',
|
||||
group: 'extras',
|
||||
icon: 'smile-o',
|
||||
action: 'emoji',
|
||||
shortcut: 'Alt+E',
|
||||
title: 'composer.emoji'
|
||||
});
|
||||
});
|
||||
|
||||
window.PagedownCustom.appendButtons.push({
|
||||
id: 'wmd-emoji-button',
|
||||
description: I18n.t("composer.emoji"),
|
||||
|
||||
@ -4,7 +4,9 @@ import KeyValueStore from "discourse/lib/key-value-store";
|
||||
const keyValueStore = new KeyValueStore("discourse_emojis_");
|
||||
const EMOJI_USAGE = "emojiUsage";
|
||||
|
||||
const PER_ROW = 12, PER_PAGE = 60;
|
||||
let PER_ROW = 12;
|
||||
const PER_PAGE = 60;
|
||||
|
||||
let ungroupedIcons, recentlyUsedIcons;
|
||||
|
||||
if (!keyValueStore.getObject(EMOJI_USAGE)) {
|
||||
@ -159,6 +161,7 @@ function showSelector(options) {
|
||||
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);
|
||||
|
||||
|
||||
@ -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,21 @@ const Topic = RestModel.extend({
|
||||
message: null,
|
||||
errorLoading: false,
|
||||
|
||||
@computed('posters.firstObject')
|
||||
creator(poster){
|
||||
return poster && poster.user;
|
||||
},
|
||||
|
||||
@computed('posters.@each')
|
||||
lastPoster(posters) {
|
||||
if (posters && posters.length > 0) {
|
||||
const latest = posters.filter(p => p.extras && p.extras.indexOf("latest") >= 0)[0];
|
||||
return latest.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,7 +7,7 @@ export default (filter, params) => {
|
||||
queryParams,
|
||||
|
||||
model(modelParams) {
|
||||
return Discourse.Category.findBySlug(modelParams.slug, modelParams.parentSlug);
|
||||
return { category: Discourse.Category.findBySlug(modelParams.slug, modelParams.parentSlug) };
|
||||
},
|
||||
|
||||
afterModel(model, transition) {
|
||||
@ -16,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);
|
||||
}
|
||||
|
||||
@ -44,28 +44,30 @@ 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'),
|
||||
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,
|
||||
@ -75,7 +77,7 @@ export default (filter, params) => {
|
||||
|
||||
var topicOpts = {
|
||||
model: topics,
|
||||
category: model,
|
||||
category,
|
||||
period: topics.get('for_period') || (filter.indexOf('/') > 0 ? filter.split('/')[1] : ''),
|
||||
selected: [],
|
||||
noSubcategories: params && !!params.no_subcategories,
|
||||
@ -84,7 +86,7 @@ export default (filter, params) => {
|
||||
canCreateTopicOnCategory: canCreateTopicOnCategory
|
||||
};
|
||||
|
||||
const p = model.get('params');
|
||||
const p = category.get('params');
|
||||
if (p && Object.keys(p).length) {
|
||||
if (p.order !== undefined) {
|
||||
topicOpts.order = p.order;
|
||||
@ -95,7 +97,7 @@ export default (filter, params) => {
|
||||
}
|
||||
|
||||
this.controllerFor('discovery/topics').setProperties(topicOpts);
|
||||
this.searchService.set('searchContext', model.get('searchContext'));
|
||||
this.searchService.set('searchContext', category.get('searchContext'));
|
||||
this.set('topics', null);
|
||||
|
||||
this.openTopicDraft(topics);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -8,20 +8,14 @@
|
||||
|
||||
<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}}
|
||||
{{#each toolbar.groups as |group|}}
|
||||
{{#each group.buttons as |b|}}
|
||||
{{d-button action=b.action actionParam=b translatedTitle=b.title icon=b.icon class=b.className}}
|
||||
{{/each}}
|
||||
{{#unless group.lastGroup}}
|
||||
<div class='d-editor-spacer'></div>
|
||||
{{/unless}}
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
{{textarea value=value class="d-editor-input"}}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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,38 @@
|
||||
<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="topic-item-stats clearfix">
|
||||
{{#unless controller.hideCategory}}
|
||||
<div class='category'>
|
||||
{{category-link content.category}}
|
||||
{{~#unless content.hasExcerpt}}
|
||||
<div class='pull-left'>
|
||||
<a href="{{content.lastPostUrl}}">{{avatar content.lastPoster imageSize="large"}}</a>
|
||||
</div>
|
||||
<div class='right'>
|
||||
{{else}}
|
||||
<div>
|
||||
{{/unless~}}
|
||||
<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>
|
||||
@ -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">
|
||||
@ -144,6 +140,7 @@
|
||||
{{#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}}>
|
||||
|
||||
@ -26,5 +26,6 @@ export default ContainerView.extend({
|
||||
if (this.get('topic.details.can_create_post')) {
|
||||
this.attachViewClass('reply-button');
|
||||
}
|
||||
this.trigger('additionalButtons', this);
|
||||
}
|
||||
});
|
||||
|
||||
@ -4,6 +4,7 @@ import ClickTrack from 'discourse/lib/click-track';
|
||||
import { listenForViewEvent } from 'discourse/lib/app-events';
|
||||
import { categoryBadgeHTML } from 'discourse/helpers/category-link';
|
||||
import Scrolling from 'discourse/mixins/scrolling';
|
||||
import isElementInViewport from "discourse/lib/is-element-in-viewport";
|
||||
|
||||
const TopicView = Ember.View.extend(AddCategoryClass, AddArchetypeClass, Scrolling, {
|
||||
templateName: 'topic',
|
||||
@ -117,6 +118,14 @@ const TopicView = Ember.View.extend(AddCategoryClass, AddArchetypeClass, Scrolli
|
||||
headerController.set('showExtraInfo', topic.get('postStream.firstPostNotLoaded'));
|
||||
}
|
||||
|
||||
// automatically unpin topics when the user reaches the bottom
|
||||
if (topic.get("pinned")) {
|
||||
const $topicFooterButtons = $("#topic-footer-buttons");
|
||||
if ($topicFooterButtons.length > 0 && isElementInViewport($topicFooterButtons)) {
|
||||
Em.run.next(() => topic.clearPin());
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger a scrolled event
|
||||
this.appEvents.trigger('topic:scrolled', offset);
|
||||
},
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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%);
|
||||
|
||||
@ -9,11 +9,14 @@ body img.emoji {
|
||||
}
|
||||
|
||||
.emoji-modal {
|
||||
@include transform(translate(-50%, -50%));
|
||||
z-index: 10000;
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 445px;
|
||||
height: 264px;
|
||||
margin-top: -132px;
|
||||
margin-left: -222px;
|
||||
background-color: dark-light-choose(#dadada, blend-primary-secondary(5%));
|
||||
}
|
||||
|
||||
|
||||
@ -19,12 +19,6 @@
|
||||
float: left;
|
||||
}
|
||||
|
||||
.valign-helper {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#site-logo {
|
||||
max-height: 40px;
|
||||
}
|
||||
|
||||
@ -260,3 +260,40 @@ aside.onebox.twitterstatus .onebox-body {
|
||||
padding-top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
// Onebox - Imgur - Album
|
||||
.onebox.imgur-album {
|
||||
.outer-box {
|
||||
position: absolute;
|
||||
z-index: 935;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
overflow: hidden;
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
|
||||
.inner-box {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
word-wrap: normal;
|
||||
white-space: nowrap;
|
||||
|
||||
.album-title {
|
||||
width: 100%;
|
||||
font-size: 13px;
|
||||
line-height: 30px;
|
||||
color: #ccc;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// resize stackexchange onebox image
|
||||
aside.onebox.stackexchange .onebox-body img {
|
||||
max-height: 60%;
|
||||
max-width: 10%;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
.uploaded-image-preview {
|
||||
background: $primary center;
|
||||
background-size: cover;
|
||||
background: $primary center center;
|
||||
}
|
||||
|
||||
.image-uploader.no-repeat {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -65,7 +65,6 @@ blockquote {
|
||||
|
||||
.topic-statuses {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
.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%));
|
||||
@ -112,15 +116,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.num.posts {
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.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 +142,7 @@
|
||||
|
||||
.posts {
|
||||
width: 10%;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.age {
|
||||
@ -236,10 +239,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%;
|
||||
}
|
||||
}
|
||||
|
||||
@ -400,8 +408,12 @@ td .main-link {
|
||||
}
|
||||
.topic-list {
|
||||
.posts-map {
|
||||
display: inline;
|
||||
font-size: 1.071em;
|
||||
padding-top: 2px;
|
||||
font-size: 1.15em;
|
||||
}
|
||||
// so the topic excerpt is full width
|
||||
// as the containing div is 80%
|
||||
.topic-excerpt {
|
||||
padding-right: 0;
|
||||
width: 120%;
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,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;
|
||||
@ -291,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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -57,6 +57,7 @@ class Users::OmniauthCallbacksController < ApplicationController
|
||||
complete_response_data
|
||||
|
||||
if provider && provider.full_screen_login
|
||||
cookies['_bypass_cache'] = true
|
||||
flash[:authentication_data] = @auth_result.to_client_hash.to_json
|
||||
redirect_to @origin
|
||||
else
|
||||
|
||||
@ -29,6 +29,8 @@ class UsersController < ApplicationController
|
||||
end
|
||||
|
||||
def show
|
||||
raise Discourse::InvalidAccess if SiteSetting.hide_user_profiles_from_public && !current_user
|
||||
|
||||
@user = fetch_user_from_params
|
||||
user_serializer = UserSerializer.new(@user, scope: guardian, root: 'user')
|
||||
if params[:stats].to_s == "false"
|
||||
@ -162,7 +164,6 @@ class UsersController < ApplicationController
|
||||
end
|
||||
|
||||
def my_redirect
|
||||
|
||||
raise Discourse::NotFound if params[:path] !~ /^[a-z\-\/]+$/
|
||||
|
||||
if current_user.blank?
|
||||
@ -498,8 +499,10 @@ class UsersController < ApplicationController
|
||||
end
|
||||
|
||||
def send_activation_email
|
||||
RateLimiter.new(nil, "activate-hr-#{request.remote_ip}", 30, 1.hour).performed!
|
||||
RateLimiter.new(nil, "activate-min-#{request.remote_ip}", 6, 1.minute).performed!
|
||||
if current_user.blank? || !current_user.staff?
|
||||
RateLimiter.new(nil, "activate-hr-#{request.remote_ip}", 30, 1.hour).performed!
|
||||
RateLimiter.new(nil, "activate-min-#{request.remote_ip}", 6, 1.minute).performed!
|
||||
end
|
||||
|
||||
@user = User.find_by_username_or_email(params[:username].to_s)
|
||||
|
||||
|
||||
@ -129,6 +129,8 @@ module ApplicationHelper
|
||||
if opts[:image].present? && opts[:image].start_with?("//")
|
||||
uri = URI(Discourse.base_url)
|
||||
opts[:image] = "#{uri.scheme}:#{opts[:image]}"
|
||||
elsif opts[:image].present? && opts[:image].start_with?("/uploads/")
|
||||
opts[:image] = "#{Discourse.base_url}#{opts[:image]}"
|
||||
end
|
||||
|
||||
# Add opengraph tags
|
||||
|
||||
@ -51,14 +51,66 @@ module Jobs
|
||||
end
|
||||
|
||||
def user_list_export
|
||||
query = ::AdminUserIndexQuery.new
|
||||
user_data = query.find_users_query.to_a
|
||||
user_data.map do |user|
|
||||
group_names = get_group_names(user).join(';')
|
||||
user_array = get_user_list_fields(user)
|
||||
user_array.push(group_names) if group_names != ''
|
||||
user_array
|
||||
user_array = []
|
||||
user_field_ids = UserField.pluck(:id)
|
||||
|
||||
if SiteSetting.enable_sso
|
||||
# SSO enabled
|
||||
User.includes(:user_stat, :single_sign_on_record, :groups).find_each do |user|
|
||||
user_info_string = "#{user.id},#{user.name},#{user.username},#{user.email},#{user.title},#{user.created_at},#{user.last_seen_at},#{user.last_posted_at},#{user.last_emailed_at},#{user.trust_level},#{user.approved},#{user.suspended_at},#{user.suspended_till},#{user.blocked},#{user.active},#{user.admin},#{user.moderator},#{user.ip_address},#{user.user_stat.topics_entered},#{user.user_stat.posts_read_count},#{user.user_stat.time_read},#{user.user_stat.topic_count},#{user.user_stat.post_count},#{user.user_stat.likes_given},#{user.user_stat.likes_received}"
|
||||
|
||||
# sso
|
||||
if user.single_sign_on_record
|
||||
user_info_string << ",#{user.single_sign_on_record.external_id},#{user.single_sign_on_record.external_email},#{user.single_sign_on_record.external_username},#{user.single_sign_on_record.external_name},#{user.single_sign_on_record.external_avatar_url}"
|
||||
else
|
||||
user_info_string << ",nil,nil,nil,nil,nil"
|
||||
end
|
||||
|
||||
# custom fields
|
||||
if user_field_ids.present?
|
||||
user.user_fields.each do |custom_field|
|
||||
user_info_string << ",#{custom_field[1]}"
|
||||
end
|
||||
end
|
||||
|
||||
# group names
|
||||
group_names = ""
|
||||
user.groups.each do |group|
|
||||
group_names << "#{group.name};"
|
||||
end
|
||||
user_info_string << ",#{group_names[0..-2]}" unless group_names.blank?
|
||||
group_names = nil
|
||||
|
||||
user_array.push(user_info_string.split(","))
|
||||
user_info_string = nil
|
||||
end
|
||||
else
|
||||
# SSO disabled
|
||||
User.includes(:user_stat, :groups).find_each do |user|
|
||||
user_info_string = "#{user.id},#{user.name},#{user.username},#{user.email},#{user.title},#{user.created_at},#{user.last_seen_at},#{user.last_posted_at},#{user.last_emailed_at},#{user.trust_level},#{user.approved},#{user.suspended_at},#{user.suspended_till},#{user.blocked},#{user.active},#{user.admin},#{user.moderator},#{user.ip_address},#{user.user_stat.topics_entered},#{user.user_stat.posts_read_count},#{user.user_stat.time_read},#{user.user_stat.topic_count},#{user.user_stat.post_count},#{user.user_stat.likes_given},#{user.user_stat.likes_received}"
|
||||
|
||||
# custom fields
|
||||
if user_field_ids.present?
|
||||
user.user_fields.each do |custom_field|
|
||||
user_info_string << ",#{custom_field[1]}"
|
||||
end
|
||||
end
|
||||
|
||||
# group names
|
||||
group_names = ""
|
||||
user.groups.each do |group|
|
||||
group_names << "#{group.name};"
|
||||
end
|
||||
user_info_string << ",#{group_names[0..-2]}" unless group_names.blank?
|
||||
group_names = nil
|
||||
|
||||
user_array.push(user_info_string.split(","))
|
||||
user_info_string = nil
|
||||
end
|
||||
end
|
||||
|
||||
user_field_ids = nil
|
||||
user_array
|
||||
end
|
||||
|
||||
def staff_action_export
|
||||
@ -129,15 +181,6 @@ module Jobs
|
||||
|
||||
private
|
||||
|
||||
def get_group_names(user)
|
||||
group_names = []
|
||||
groups = user.groups
|
||||
groups.each do |group|
|
||||
group_names.push(group.name)
|
||||
end
|
||||
return group_names
|
||||
end
|
||||
|
||||
def get_user_archive_fields(user_archive)
|
||||
user_archive_array = []
|
||||
topic_data = user_archive.topic
|
||||
@ -170,34 +213,6 @@ module Jobs
|
||||
user_archive_array
|
||||
end
|
||||
|
||||
def get_user_list_fields(user)
|
||||
user_array = []
|
||||
|
||||
HEADER_ATTRS_FOR['user_list'].each do |attr|
|
||||
user_array.push(user.attributes[attr])
|
||||
end
|
||||
|
||||
HEADER_ATTRS_FOR['user_stats'].each do |stat|
|
||||
user_array.push(user.user_stat.attributes[stat])
|
||||
end
|
||||
|
||||
if SiteSetting.enable_sso
|
||||
sso = user.single_sign_on_record
|
||||
HEADER_ATTRS_FOR['user_sso'].each do |stat|
|
||||
field = sso.attributes[stat] if sso
|
||||
user_array.push(field)
|
||||
end
|
||||
end
|
||||
|
||||
if user.user_fields.present?
|
||||
user.user_fields.each do |custom_field|
|
||||
user_array.push(custom_field[1])
|
||||
end
|
||||
end
|
||||
|
||||
user_array
|
||||
end
|
||||
|
||||
def get_staff_action_fields(staff_action)
|
||||
staff_action_array = []
|
||||
|
||||
|
||||
@ -3,10 +3,8 @@ module Jobs
|
||||
|
||||
def execute(args)
|
||||
# maybe it was removed by the time we are making the post
|
||||
if post = Post.find(args[:post_id])
|
||||
# maybe the topic was deleted, so skip in that case as well
|
||||
PostAlerter.post_created(post) if post.topic
|
||||
end
|
||||
post = Post.where(id: args[:post_id]).first
|
||||
PostAlerter.post_created(post) if post && post.topic
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -76,12 +76,7 @@ module Jobs
|
||||
end
|
||||
|
||||
post.reload
|
||||
if start_raw != post.raw
|
||||
# post was edited - start over (after 10 minutes)
|
||||
backoff = args.fetch(:backoff, 1) + 1
|
||||
delay = SiteSetting.ninja_edit_window * args[:backoff]
|
||||
Jobs.enqueue_in(delay.seconds.to_i, :pull_hotlinked_images, args.merge!(backoff: backoff))
|
||||
elsif raw != post.raw
|
||||
if start_raw == post.raw && raw != post.raw
|
||||
changes = { raw: raw, edit_reason: I18n.t("upload.edit_reason") }
|
||||
# we never want that job to bump the topic
|
||||
options = { bypass_bump: true }
|
||||
|
||||
@ -11,7 +11,7 @@ module Jobs
|
||||
force_aspect_ratio: SiteSetting.enforce_square_emoji
|
||||
}
|
||||
# make sure emoji aren't too big
|
||||
OptimizedImage.downsize(path, path, 100, 100, opts)
|
||||
OptimizedImage.downsize(path, path, "100x100", opts)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -42,7 +42,12 @@ class UserNotifications < ActionMailer::Base
|
||||
end
|
||||
|
||||
def short_date(dt)
|
||||
I18n.l(dt, format: :short)
|
||||
current = Time.now
|
||||
if dt.year == current.year
|
||||
dt.strftime("%B #{dt.day.ordinalize}")
|
||||
else
|
||||
dt.strftime("%B #{dt.day.ordinalize}, %Y")
|
||||
end
|
||||
end
|
||||
|
||||
def digest(user, opts={})
|
||||
|
||||
@ -142,10 +142,6 @@ class OptimizedImage < ActiveRecord::Base
|
||||
optimize("resize", from, to, "#{width}x#{height}", opts)
|
||||
end
|
||||
|
||||
def self.downsize(from, to, max_width, max_height, opts={})
|
||||
optimize("downsize", from, to, "#{max_width}x#{max_height}", opts)
|
||||
end
|
||||
|
||||
def self.downsize(from, to, dimensions, opts={})
|
||||
optimize("downsize", from, to, dimensions, opts)
|
||||
end
|
||||
|
||||
@ -253,11 +253,8 @@ class Topic < ActiveRecord::Base
|
||||
|
||||
# all users (in groups or directly targetted) that are going to get the pm
|
||||
def all_allowed_users
|
||||
# TODO we should probably change this to 1 query
|
||||
allowed_user_ids = allowed_users.select('users.id').to_a
|
||||
allowed_group_user_ids = allowed_group_users.select('users.id').to_a
|
||||
allowed_staff_ids = private_message? && has_flags? ? User.where(moderator: true).pluck(:id).to_a : []
|
||||
User.where('id IN (?)', allowed_user_ids + allowed_group_user_ids + allowed_staff_ids)
|
||||
moderators_sql = " UNION #{User.moderators.to_sql}" if private_message? && has_flags?
|
||||
User.from("(#{allowed_users.to_sql} UNION #{allowed_group_users.to_sql}#{moderators_sql}) as users")
|
||||
end
|
||||
|
||||
# Additional rate limits on topics: per day and private messages per day
|
||||
@ -585,7 +582,7 @@ class Topic < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
if username_or_email =~ /^.+@.+$/ && !SiteSetting.enable_sso
|
||||
if username_or_email =~ /^.+@.+$/ && !SiteSetting.enable_sso && SiteSetting.enable_local_logins
|
||||
# rate limit topic invite
|
||||
RateLimiter.new(invited_by, "topic-invitations-per-day", SiteSetting.max_topic_invitations_per_day, 1.day.to_i).performed!
|
||||
|
||||
|
||||
@ -8,15 +8,19 @@ class PostTimestampChanger
|
||||
|
||||
def change!
|
||||
ActiveRecord::Base.transaction do
|
||||
update_topic
|
||||
last_posted_at = @timestamp
|
||||
|
||||
@posts.each do |post|
|
||||
if post.is_first_post?
|
||||
update_post(post, @timestamp)
|
||||
else
|
||||
update_post(post, Time.at(post.created_at.to_f + @time_difference))
|
||||
new_created_at = Time.at(post.created_at.to_f + @time_difference)
|
||||
last_posted_at = new_created_at if new_created_at > last_posted_at
|
||||
update_post(post, new_created_at)
|
||||
end
|
||||
end
|
||||
|
||||
update_topic(last_posted_at)
|
||||
end
|
||||
|
||||
# Burst the cache for stats
|
||||
@ -29,11 +33,12 @@ class PostTimestampChanger
|
||||
@timestamp - @topic.created_at
|
||||
end
|
||||
|
||||
def update_topic
|
||||
def update_topic(last_posted_at)
|
||||
@topic.update_attributes(
|
||||
created_at: @timestamp,
|
||||
updated_at: @timestamp,
|
||||
bumped_at: @timestamp
|
||||
bumped_at: @timestamp,
|
||||
last_posted_at: last_posted_at
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
14
bin/docker/boot_dev
Executable file
14
bin/docker/boot_dev
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
pushd `dirname $0` > /dev/null
|
||||
SCRIPTPATH=`pwd -P`
|
||||
popd > /dev/null
|
||||
|
||||
|
||||
SOURCE_DIR=`(cd $SCRIPTPATH && cd ../../ && pwd)`
|
||||
DATA_DIR=$SOURCE_DIR/tmp/postgres
|
||||
|
||||
echo $SOURCE_DIR
|
||||
echo $DATA_DIR
|
||||
|
||||
docker run -d -p 3000:3000 -v $DATA_DIR:/shared/postgres_data -v $SOURCE_DIR:/src --hostname=discourse_dev --name=discourse_dev --restart=always discourse/dev /sbin/boot
|
||||
5
bin/docker/bundle
Executable file
5
bin/docker/bundle
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
PARAMS="$@"
|
||||
CMD="cd /src && HOME=/home/discourse chpst -u discourse:discourse bundle $PARAMS"
|
||||
docker exec -it discourse_dev /bin/bash -c "$CMD"
|
||||
5
bin/docker/psql
Executable file
5
bin/docker/psql
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
PARAMS="$@"
|
||||
CMD="chpst -u postgres psql $PARAMS"
|
||||
docker exec -it discourse_dev /bin/bash -c "$CMD"
|
||||
9
bin/docker/rails
Executable file
9
bin/docker/rails
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
PARAMS="$@"
|
||||
if [[ $# = 1 ]] && [[ "$1" =~ "s" ]];
|
||||
then
|
||||
PARAMS="$PARAMS -b 0.0.0.0"
|
||||
fi
|
||||
CMD="cd /src && HOME=/home/discourse RAILS_ENV=${RAILS_ENV:=development} chpst -u discourse:discourse rails $PARAMS"
|
||||
docker exec -it discourse_dev /bin/bash -c "$CMD"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user