Version bump

This commit is contained in:
Neil Lalonde 2018-01-03 16:55:22 -05:00
commit 387bdadbe2
255 changed files with 3369 additions and 1370 deletions

View File

@ -41,11 +41,12 @@
"visible":true,
"invisible":true,
"asyncRender":true,
"selectDropdown":true,
"selectKit":true,
"expandSelectKit":true,
"collapseSelectKit":true,
"selectKitSelectRow":true,
"selectKitSelectRowByValue":true,
"selectKitSelectRowByName":true,
"selectKitSelectRowByIndex":true,
"selectKitSelectNoneRow":true,
"selectKitFillInFilter":true,
"asyncTestDiscourse":true,

View File

@ -21,8 +21,11 @@ addons:
matrix:
fast_finish: true
allow_failures:
- rvm: 2.5.0
rvm:
- 2.5.0
- 2.4.2
- 2.3.4
@ -45,6 +48,7 @@ before_install:
- git clone --depth=1 https://github.com/discourse/discourse-canned-replies.git plugins/discourse-canned-replies
- git clone --depth=1 https://github.com/discourse/discourse-chat-integration.git plugins/discourse-chat-integration
- git clone --depth=1 https://github.com/discourse/discourse-assign.git plugins/discourse-assign
- git clone --depth=1 https://github.com/discourse/discourse-patreon.git plugins/discourse-patreon
- export PATH=$HOME/.yarn/bin:$PATH
install:
@ -67,7 +71,7 @@ script:
bundle exec rake db:create db:migrate
if [ '$QUNIT_RUN' == '1' ]; then
bundle exec rake qunit:test['400000']
bundle exec rake qunit:test['400000'] && \
bundle exec rake plugin:spec
else
bundle exec rspec && bundle exec rake plugin:spec

View File

@ -36,7 +36,7 @@ gem 'redis-namespace'
gem 'active_model_serializers', '~> 0.8.3'
gem 'onebox', '1.8.30'
gem 'onebox', '1.8.33'
gem 'http_accept_language', '~>2.0.5', require: false

View File

@ -232,7 +232,7 @@ GEM
omniauth-twitter (1.3.0)
omniauth-oauth (~> 1.1)
rack
onebox (1.8.30)
onebox (1.8.33)
fast_blank (>= 1.0.0)
htmlentities (~> 4.3)
moneta (~> 1.0)
@ -469,7 +469,7 @@ DEPENDENCIES
omniauth-oauth2
omniauth-openid
omniauth-twitter
onebox (= 1.8.30)
onebox (= 1.8.33)
openid-redis-store
pg
pry-nav

View File

@ -16,7 +16,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
@computed('name')
nameValid(name) {
return name && name.match(/\A[a-z_][a-z0-9_-]*\z/i);
return name && name.match(/^[a-z_][a-z0-9_-]*$/i);
},
@observes('name')

View File

@ -68,7 +68,7 @@ export default Discourse.Route.extend({
function(confirmed) {
if (confirmed) {
backup.destroy().then(function() {
self.controllerFor("adminBackupsIndex").removeObject(backup);
self.controllerFor("adminBackupsIndex").get('model').removeObject(backup);
});
}
}

View File

@ -20,7 +20,9 @@
{{#if currentUser.admin}}
{{nav-item route='adminCustomize' label='admin.customize.title'}}
{{nav-item route='adminApi' label='admin.api.title'}}
{{nav-item route='admin.backups' label='admin.backups.title'}}
{{#if siteSettings.enable_backups}}
{{nav-item route='admin.backups' label='admin.backups.title'}}
{{/if}}
{{/if}}
{{nav-item route='adminPlugins' label='admin.plugins.title'}}
{{plugin-outlet name="admin-menu" connectorTagName="li"}}

View File

@ -1,5 +1,5 @@
<div class="show-current-style">
<h2>
<h1>
{{#if editingName}}
{{text-field value=model.name autofocus="true"}}
{{d-button action="finishedEditingName" class="btn-primary btn-small submit-edit" icon="check"}}
@ -7,7 +7,7 @@
{{else}}
{{model.name}} <a {{action "startEditingName"}}>{{d-icon "pencil"}}</a>
{{/if}}
</h2>
</h1>
{{#if model.remote_theme}}
<p>

View File

@ -1,6 +1,6 @@
export default Ember.Component.extend({
tagName: 'span',
classNameBindings: [':user-badge', 'badge.badgeTypeClassName'],
classNameBindings: [':user-badge', 'badge.badgeTypeClassName', 'badge.enabled::disabled'],
title: function(){
return $("<div>"+this.get('badge.description')+"</div>").text();
}.property('badge.description'),

View File

@ -1,5 +1,5 @@
import userSearch from 'discourse/lib/user-search';
import { default as computed, on } from 'ember-addons/ember-computed-decorators';
import { default as computed, observes, on } from 'ember-addons/ember-computed-decorators';
import { linkSeenMentions, fetchUnseenMentions } from 'discourse/lib/link-mentions';
import { linkSeenCategoryHashtags, fetchUnseenCategoryHashtags } from 'discourse/lib/link-category-hashtags';
import { linkSeenTagHashtags, fetchUnseenTagHashtags } from 'discourse/lib/link-tag-hashtag';
@ -36,6 +36,18 @@ export default Ember.Component.extend({
return `[${I18n.t('uploading')}]() `;
},
@observes('composer.uploadCancelled')
_cancelUpload() {
if (!this.get('composer.uploadCancelled')) { return; }
this.set('composer.uploadCancelled', false);
if (this._xhr) {
this._xhr._userCancelled = true;
this._xhr.abort();
}
this._resetUpload(true);
},
@computed
markdownOptions() {
return {
@ -363,7 +375,7 @@ export default Ember.Component.extend({
const $e = $(e);
var name = $e.data('name');
if (found.indexOf(name) === -1){
this.sendAction('groupsMentioned', [{name: name, user_count: $e.data('mentionable-user-count')}]);
this.sendAction('groupsMentioned', [{name: name, user_count: $e.data('mentionable-user-count'), max_mentions: $e.data('max-mentions')}]);
found.push(name);
}
});
@ -401,7 +413,9 @@ export default Ember.Component.extend({
},
_resetUpload(removePlaceholder) {
this._validUploads--;
if (this._validUploads > 0) {
this._validUploads--;
}
if (this._validUploads === 0) {
this.setProperties({ uploadProgress: 0, isUploading: false, isCancellable: false });
}
@ -493,7 +507,7 @@ export default Ember.Component.extend({
this._xhr = null;
if (!userCancelled) {
displayErrorForUpload(data.jqXHR.responseJSON);
displayErrorForUpload(data);
}
});
@ -624,14 +638,6 @@ export default Ember.Component.extend({
this.sendAction('importQuote', toolbarEvent);
},
cancelUpload() {
if (this._xhr) {
this._xhr._userCancelled = true;
this._xhr.abort();
}
this._resetUpload(true);
},
onExpandPopupMenuOptions(toolbarEvent) {
const selected = toolbarEvent.selected;
toolbarEvent.selectText(selected.start, selected.end - selected.start);

View File

@ -279,6 +279,7 @@ export default Ember.Component.extend({
const markdownOptions = this.get('markdownOptions') || {};
cookAsync(value, markdownOptions).then(cooked => {
if (this.get('isDestroyed')) { return; }
this.set('preview', cooked);
Ember.run.scheduleOnce('afterRender', () => {
if (this._state !== "inDOM") { return; }
@ -632,7 +633,8 @@ export default Ember.Component.extend({
if (rows.length > 1) {
const columns = rows.map(r => r.split("\t").length);
const isTable = columns.reduce((a, b) => a && columns[0] === b && b > 1);
const isTable = columns.reduce((a, b) => a && columns[0] === b && b > 1) &&
!(columns[0] === 2 && rows[0].split("\t")[0].match(/^•$|^\d+.$/)); // to skip tab delimited lists
if (isTable) {
const splitterRow = [...Array(columns[0])].map(() => "---").join("\t");
@ -662,8 +664,6 @@ export default Ember.Component.extend({
if (table) {
this.appEvents.trigger('composer:insert-text', table);
handled = true;
} else if (html && html.includes("urn:schemas-microsoft-com:office:word")) {
html = ""; // use plain text data for microsoft word
}
}

View File

@ -1,7 +1,21 @@
import { showEntrance } from "discourse/components/topic-list-item";
import { showEntrance, navigateToTopic } from "discourse/components/topic-list-item";
export default Ember.Component.extend({
click: showEntrance,
attributeBindings: ['topic.id:data-topic-id'],
classNameBindings: [':latest-topic-list-item', 'topic.archived', 'topic.visited']
classNameBindings: [':latest-topic-list-item', 'topic.archived', 'topic.visited'],
showEntrance,
navigateToTopic,
click(e) {
// for events undefined has a different meaning than false
if (this.showEntrance(e) === false) {
return false;
}
return this.unhandledRowClick(e, this.get('topic'));
},
// Can be overwritten by plugins to handle clicks on other parts of the row
unhandledRowClick() { },
});

View File

@ -20,6 +20,12 @@ export function showEntrance(e) {
}
}
export function navigateToTopic(topic, href) {
this.appEvents.trigger('header:update-topic', topic);
DiscourseURL.routeTo(href || topic.get('url'));
return false;
}
export default Ember.Component.extend(bufferedRender({
rerenderTriggers: ['bulkSelectEnabled', 'topic.pinned'],
tagName: 'tr',
@ -107,8 +113,10 @@ export default Ember.Component.extend(bufferedRender({
return false;
}.property(),
showEntrance,
click(e) {
const result = showEntrance.call(this, e);
const result = this.showEntrance(e);
if (result === false) { return result; }
const topic = this.get('topic');
@ -124,19 +132,23 @@ export default Ember.Component.extend(bufferedRender({
}
if (target.hasClass('raw-topic-link')) {
if (wantsNewWindow(e)) { return true; }
this.appEvents.trigger('header:update-topic', topic);
DiscourseURL.routeTo(target.attr('href'));
return false;
if (wantsNewWindow(e)) { return true; }
return this.navigateToTopic(topic, target.attr('href'));
}
if (target.closest('a.topic-status').length === 1) {
this.get('topic').togglePinnedForUser();
return false;
}
return this.unhandledRowClick(e, topic);
},
navigateToTopic,
// Can be overwritten by plugins to handle clicks on other parts of the row
unhandledRowClick() { },
highlight(opts = { isLastViewedTopic: false }) {
const $topic = this.$();
$topic

View File

@ -115,8 +115,7 @@ export default Ember.Component.extend(CleansUp, CanCheckEmails, {
return false;
}
// XSS protection (should be encapsulated)
username = username.toString().replace(/[^A-Za-z0-9_\.\-]/g, "");
username = Ember.Handlebars.Utils.escapeExpression(username.toString());
// Don't show on mobile
if (this.site.mobileView) {

View File

@ -49,6 +49,10 @@ function loadDraft(store, opts) {
const _popupMenuOptionsCallbacks = [];
export function clearPopupMenuOptionsCallback() {
_popupMenuOptionsCallbacks.length = 0;
}
export function addPopupMenuOptionsCallback(callback) {
_popupMenuOptionsCallbacks.push(callback);
}
@ -220,6 +224,10 @@ export default Ember.Controller.extend({
},
actions: {
cancelUpload() {
this.set('model.uploadCancelled', true);
},
onPopupMenuAction(action) {
this.send(action);
},
@ -378,11 +386,21 @@ export default Ember.Controller.extend({
groupsMentioned(groups) {
if (!this.get('model.creatingPrivateMessage') && !this.get('model.topic.isPrivateMessage')) {
groups.forEach(group => {
const body = I18n.t('composer.group_mentioned', {
group: "@" + group.name,
count: group.user_count,
group_link: Discourse.getURL(`/groups/${group.name}/members`)
});
let body;
if (group.max_mentions < group.user_count) {
body = I18n.t('composer.group_mentioned_limit', {
group: "@" + group.name,
max: group.max_mentions,
group_link: Discourse.getURL(`/groups/${group.name}/members`)
});
} else {
body = I18n.t('composer.group_mentioned', {
group: "@" + group.name,
count: group.user_count,
group_link: Discourse.getURL(`/groups/${group.name}/members`)
});
}
this.appEvents.trigger('composer-messages:create', {
extraClass: 'custom-body',

View File

@ -151,6 +151,12 @@ export default Ember.Controller.extend({
this.set("application.showFooter", !this.get("loading"));
},
@computed('resultCount', 'noSortQ')
resultCountLabel(count, term) {
const plus = (count % 50 === 0 ? "+" : "");
return I18n.t('search.result_count', {count, plus, term});
},
@observes('model.posts.length')
resultCountChanged() {
this.set("resultCount", this.get("model.posts.length"));

View File

@ -10,6 +10,7 @@ export default Ember.Controller.extend(CanCheckEmails, PreferencesTabController,
saveAttrNames: ['name'],
canEditName: setting('enable_names'),
canSaveUser: true,
newNameInput: null,

View File

@ -272,7 +272,7 @@ export default Ember.Controller.extend(BufferedContent, {
const quoteState = this.get('quoteState');
const postStream = this.get('model.postStream');
if (!postStream) return;
if (!postStream || !topic || !topic.get('details.can_create_post')) { return; }
const quotedPost = postStream.findLoadedPost(quoteState.postId);
const quotedText = Quote.build(quotedPost, quoteState.buffer);

View File

@ -55,7 +55,9 @@ export default {
node = node[segs[i]];
}
node[segs[segs.length-1]] = v;
if (typeof node === "object") {
node[segs[segs.length-1]] = v;
}
});
}

View File

@ -26,7 +26,7 @@ export default {
}
// don't track links in quotes or in elided part
if ($link.parents('aside.quote,.elided').length) { return true; }
let tracking = $link.parents('aside.quote,.elided').length === 0;
let href = $link.attr('href') || $link.data('href');
@ -39,26 +39,31 @@ export default {
const userId = $link.data('user-id') || $article.data('user-id');
const ownLink = userId && (userId === Discourse.User.currentProp('id'));
let trackingUrl = Discourse.getURL('/clicks/track?url=' + encodeURIComponent(href));
let destUrl = href;
if (postId && !$link.data('ignore-post-id')) {
trackingUrl += "&post_id=" + encodeURI(postId);
}
if (topicId) {
trackingUrl += "&topic_id=" + encodeURI(topicId);
}
if (tracking) {
// Update badge clicks unless it's our own
if (!ownLink) {
const $badge = $('span.badge', $link);
if ($badge.length === 1) {
// don't update counts in category badge nor in oneboxes (except when we force it)
if (isValidLink($link)) {
const html = $badge.html();
const key = `${new Date().toLocaleDateString()}-${postId}-${href}`;
if (/^\d+$/.test(html) && !sessionStorage.getItem(key)) {
sessionStorage.setItem(key, true);
$badge.html(parseInt(html, 10) + 1);
destUrl = Discourse.getURL('/clicks/track?url=' + encodeURIComponent(href));
if (postId && !$link.data('ignore-post-id')) {
destUrl += "&post_id=" + encodeURI(postId);
}
if (topicId) {
destUrl += "&topic_id=" + encodeURI(topicId);
}
// Update badge clicks unless it's our own
if (!ownLink) {
const $badge = $('span.badge', $link);
if ($badge.length === 1) {
// don't update counts in category badge nor in oneboxes (except when we force it)
if (isValidLink($link)) {
const html = $badge.html();
const key = `${new Date().toLocaleDateString()}-${postId}-${href}`;
if (/^\d+$/.test(html) && !sessionStorage.getItem(key)) {
sessionStorage.setItem(key, true);
$badge.html(parseInt(html, 10) + 1);
}
}
}
}
@ -66,12 +71,12 @@ export default {
// If they right clicked, change the destination href
if (e.which === 3) {
$link.attr('href', Discourse.SiteSettings.track_external_right_clicks ? trackingUrl : href);
$link.attr('href', Discourse.SiteSettings.track_external_right_clicks ? destUrl : href);
return true;
}
// if they want to open in a new tab, do an AJAX request
if (wantsNewWindow(e)) {
if (tracking && wantsNewWindow(e)) {
ajax("/clicks/track", {
data: {
url: href,
@ -109,7 +114,7 @@ export default {
}
// If we're on the same site, use the router and track via AJAX
if (DiscourseURL.isInternal(href) && !$link.hasClass('attachment')) {
if (tracking && DiscourseURL.isInternal(href) && !$link.hasClass('attachment')) {
ajax("/clicks/track", {
data: {
url: href,
@ -125,9 +130,9 @@ export default {
// Otherwise, use a custom URL with a redirect
if (Discourse.User.currentProp('external_links_in_new_tab')) {
window.open(trackingUrl, '_blank').focus();
window.open(destUrl, '_blank').focus();
} else {
DiscourseURL.redirectTo(trackingUrl);
DiscourseURL.redirectTo(destUrl);
}
return false;

View File

@ -310,8 +310,10 @@ export function number(val) {
if (val > 999999) {
formattedNumber = I18n.toNumber(val / 1000000, {precision: 1});
return I18n.t("number.short.millions", {number: formattedNumber});
}
if (val > 999) {
} else if (val > 99999) {
formattedNumber = I18n.toNumber(val / 1000, {precision: 0});
return I18n.t("number.short.thousands", {number: formattedNumber});
} else if (val > 999) {
formattedNumber = I18n.toNumber(val / 1000, {precision: 1});
return I18n.t("number.short.thousands", {number: formattedNumber});
}

View File

@ -169,7 +169,9 @@ export default {
},
createTopic() {
this.container.lookup('controller:composer').open({action: Composer.CREATE_TOPIC, draftKey: Composer.CREATE_TOPIC});
if (this.currentUser && this.currentUser.can_create_topic) {
this.container.lookup('controller:composer').open({action: Composer.CREATE_TOPIC, draftKey: Composer.CREATE_TOPIC});
}
},
pinUnpinTopic() {

View File

@ -2,13 +2,15 @@ import { ajax } from 'discourse/lib/ajax';
import { userPath } from 'discourse/lib/url';
import { formatUsername } from 'discourse/lib/utilities';
let maxGroupMention;
function replaceSpan($e, username, opts) {
let extra = "";
let extraClass = "";
if (opts && opts.group) {
if (opts.mentionable) {
extra = `data-name='${username}' data-mentionable-user-count='${opts.mentionable.user_count}'`;
extra = `data-name='${username}' data-mentionable-user-count='${opts.mentionable.user_count}' data-max-mentions='${maxGroupMention}'`;
extraClass = "notify";
}
$e.replaceWith(`<a href='${Discourse.getURL("/groups/") + username}' class='mention-group ${extraClass}' ${extra}>@${username}</a>`);
@ -61,6 +63,7 @@ export function fetchUnseenMentions(usernames, topic_id) {
r.valid_groups.forEach(vg => foundGroups[vg] = true);
r.mentionable_groups.forEach(mg => mentionableGroups[mg.name] = mg);
r.cannot_see.forEach(cs => cannotSee[cs] = true);
maxGroupMention = r.max_users_notified_per_group_mention;
usernames.forEach(u => checked[u] = true);
return r;
});

View File

@ -2,17 +2,23 @@ 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;
class Tag {
constructor(name, prefix = "", suffix = "") {
constructor(name, prefix = "", suffix = "", inline = false) {
this.name = name;
this.prefix = prefix;
this.suffix = suffix;
this.inline = inline;
}
decorate(text) {
if (this.prefix || this.suffix) {
return [this.prefix, text, this.suffix].join("");
text = [this.prefix, text, this.suffix].join("");
}
if (this.inline) {
text = " " + text + " ";
}
return text;
@ -30,7 +36,7 @@ class Tag {
static blocks() {
return ["address", "article", "aside", "dd", "div", "dl", "dt", "fieldset", "figcaption", "figure",
"footer", "form", "header", "hgroup", "hr", "main", "nav", "p", "pre", "section", "ul"];
"footer", "form", "header", "hgroup", "hr", "main", "nav", "p", "pre", "section"];
}
static headings() {
@ -38,25 +44,26 @@ class Tag {
}
static emphases() {
return [ ["b", "**"], ["strong", "**"], ["i", "_"], ["em", "_"], ["s", "~~"], ["strike", "~~"] ];
return [ ["b", "**"], ["strong", "**"], ["i", "*"], ["em", "*"], ["s", "~~"], ["strike", "~~"] ];
}
static slices() {
return ["dt", "dd", "tr", "thead", "tbody", "tfoot"];
return ["dt", "dd", "thead", "tbody", "tfoot"];
}
static trimmable() {
return [...Tag.blocks(), ...Tag.headings(), ...Tag.slices(), "li", "td", "th", "br", "hr", "blockquote", "table", "ol"];
return [...Tag.blocks(), ...Tag.headings(), ...Tag.slices(), "li", "td", "th", "br", "hr", "blockquote", "table", "ol", "tr", "ul"];
}
static block(name, prefix, suffix) {
return class extends Tag {
constructor() {
super(name, prefix, suffix);
this.gap = "\n\n";
}
decorate(text) {
return `\n\n${this.prefix}${text}${this.suffix}\n\n`;
return `${this.gap}${this.prefix}${text}${this.suffix}${this.gap}`;
}
};
}
@ -69,18 +76,21 @@ class Tag {
static emphasis(name, decorator) {
return class extends Tag {
constructor() {
super(name, decorator, decorator);
super(name, decorator, decorator, true);
}
decorate(text) {
text = text.trim();
if (text.includes("\n")) {
this.prefix = `<${this.name}>`;
this.suffix = `</${this.name}>`;
}
return super.decorate(text);
let space = text.match(/^\s/) || [""];
this.prefix = space[0] + this.prefix;
space = text.match(/\s$/) || [""];
this.suffix = this.suffix + space[0];
return super.decorate(text.trim());
}
};
}
@ -109,7 +119,7 @@ class Tag {
static link() {
return class extends Tag {
constructor() {
super("a");
super("a", "", "", true);
}
decorate(text) {
@ -128,7 +138,7 @@ class Tag {
static image() {
return class extends Tag {
constructor() {
super("img");
super("img", "", "", true);
}
toMarkdown() {
@ -143,7 +153,8 @@ class Tag {
const height = attr.height || pAttr.height;
if (width && height) {
alt = `${alt}|${width}x${height}`;
const pipe = this.element.parentNames.includes("table") ? "\\|" : "|";
alt = `${alt}${pipe}${width}x${height}`;
}
return "![" + alt + "](" + src + ")";
@ -178,14 +189,10 @@ class Tag {
toMarkdown() {
const text = this.element.innerMarkdown().trim();
if (text.includes("\n") || text.includes("[![")) {
if (text.includes("\n")) {
throw "Unsupported format inside Markdown table cells";
}
if (!this.element.next) {
this.suffix = "|";
}
return this.decorate(text);
}
};
@ -194,7 +201,7 @@ class Tag {
static li() {
return class extends Tag.slice("li", "\n") {
decorate(text) {
const indent = this.element.filterParentNames(["ol", "ul"]).slice(1).map(() => " ").join("");
const indent = this.element.filterParentNames(["ol", "ul"]).slice(1).map(() => "\t").join("");
return super.decorate(`${indent}* ${trimLeft(text)}`);
}
};
@ -210,6 +217,8 @@ class Tag {
if (this.element.parentNames.includes("pre")) {
this.prefix = '\n\n```\n';
this.suffix = '\n```\n\n';
} else {
this.inline = true;
}
text = $('<textarea />').html(text).text();
@ -234,19 +243,45 @@ class Tag {
static table() {
return class extends Tag.block("table") {
decorate(text) {
text = super.decorate(text);
const splitterRow = text.split("|\n")[0].match(/\|/g).map(() => "| --- ").join("") + "|\n";
text = text.replace("|\n", "|\n" + splitterRow).replace(/\|\n{2,}\|/g, "|\n|");
text = super.decorate(text).replace(/\|\n{2,}\|/g, "|\n|");
const rows = text.trim().split("\n");
const pipeCount = countPipes(rows[0]);
const isValid = rows.length > 1 &&
pipeCount > 2 &&
rows.reduce((a, c) => a && countPipes(c) <= pipeCount);
if (!isValid) {
throw "Unsupported table format for Markdown conversion";
}
const splitterRow = [...Array(pipeCount-1)].map(() => "| --- ").join("") + "|\n";
text = text.replace("|\n", "|\n" + splitterRow);
return text;
}
};
}
static list(name) {
return class extends Tag.block(name) {
decorate(text) {
let smallGap = "";
if (this.element.filterParentNames(["li"]).length) {
this.gap = "";
smallGap = "\n";
}
return smallGap + super.decorate(trimRight(text));
}
};
}
static ol() {
return class extends Tag.block("ol") {
return class extends Tag.list("ol") {
decorate(text) {
text = "\n" + text;
const bullet = text.match(/\n *\*/)[0];
const bullet = text.match(/\n\t*\*/)[0];
for (let i = parseInt(this.element.attributes.start || 1); text.includes(bullet); i++) {
text = text.replace(bullet, bullet.replace("*", `${i}.`));
@ -257,6 +292,17 @@ class Tag {
};
}
static tr() {
return class extends Tag.slice("tr", "|\n") {
decorate(text) {
if (!this.element.next) {
this.suffix = "|";
}
return `${text}${this.suffix}`;
}
};
}
}
const tags = [
@ -267,7 +313,7 @@ const tags = [
Tag.cell("td"), Tag.cell("th"),
Tag.replace("br", "\n"), Tag.replace("hr", "\n---\n"), Tag.replace("head", ""),
Tag.keep("ins"), Tag.keep("del"), Tag.keep("small"), Tag.keep("big"),
Tag.li(), Tag.link(), Tag.image(), Tag.code(), Tag.blockquote(), Tag.table(),, Tag.ol(),
Tag.li(), Tag.link(), Tag.image(), Tag.code(), Tag.blockquote(), Tag.table(), Tag.tr(), Tag.ol(), Tag.list("ul"),
];
class Element {
@ -364,6 +410,19 @@ class Element {
}
}
function trimUnwantedSpaces(html) {
const body = html.match(/<body[^>]*>([\s\S]*?)<\/body>/);
html = body ? body[1] : html;
html = html.replace(/\r|\n|&nbsp;/g, " ");
let match;
while (match = html.match(/<[^\s>]+[^>]*>\s{2,}<[^\s>]+[^>]*>/)) {
html = html.replace(match[0], match[0].replace(/>\s{2,}</, "> <"));
}
return html;
}
function putPlaceholders(html) {
const codeRegEx = /<code[^>]*>([\s\S]*?)<\/code>/gi;
const origHtml = html;
@ -379,7 +438,7 @@ function putPlaceholders(html) {
match = codeRegEx.exec(origHtml);
}
const elements = parseHTML(html);
const elements = parseHTML(trimUnwantedSpaces(html));
return { elements, placeholders };
}
@ -395,7 +454,7 @@ export default function toMarkdown(html) {
const { elements, placeholders } = putPlaceholders(html);
let markdown = Element.parse(elements).trim();
markdown = markdown.replace(/^<b>/, "").replace(/<\/b>$/, "").trim(); // fix for google doc copy paste
markdown = markdown.replace(/\r/g, "").replace(/\n \n/g, "\n\n").replace(/\n{3,}/g, "\n\n");
markdown = markdown.replace(/\n +/g, "\n").replace(/ +\n/g, "\n").replace(/ {2,}/g, " ").replace(/\n{3,}/g, "\n\n").replace(/\t/g, " ");
return replacePlaceholders(markdown, placeholders);
} catch(err) {
return "";

View File

@ -334,27 +334,27 @@ export function getUploadMarkdown(upload) {
}
export function displayErrorForUpload(data) {
// deal with meaningful errors first
if (data.jqXHR) {
switch (data.jqXHR.status) {
// cancelled by the user
case 0: return;
case 0:
return;
// entity too large, usually returned from the web server
// entity too large, usually returned from the web server
case 413:
var type = uploadTypeFromFileName(data.files[0].name);
var maxSizeKB = Discourse.SiteSettings['max_' + type + '_size_kb'];
bootbox.alert(I18n.t('post.errors.file_too_large', { max_size_kb: maxSizeKB }));
return;
const type = uploadTypeFromFileName(data.files[0].name);
const max_size_kb = Discourse.SiteSettings[`max_${type}_size_kb`];
bootbox.alert(I18n.t('post.errors.file_too_large', { max_size_kb }));
return;
// the error message is provided by the server
// the error message is provided by the server
case 422:
if (data.jqXHR.responseJSON.message) {
bootbox.alert(data.jqXHR.responseJSON.message);
} else {
bootbox.alert(data.jqXHR.responseJSON.join("\n"));
}
return;
if (data.jqXHR.responseJSON.message) {
bootbox.alert(data.jqXHR.responseJSON.message);
} else {
bootbox.alert(data.jqXHR.responseJSON.join("\n"));
}
return;
}
} else if (data.errors && data.errors.length > 0) {
bootbox.alert(data.errors.join("\n"));

View File

@ -56,7 +56,7 @@ export default Em.Mixin.create({
});
$upload.on("fileuploadfail", (e, data) => {
displayErrorForUpload(data.jqXHR.responseJSON);
displayErrorForUpload(data);
reset();
});
}.on("didInsertElement"),

View File

@ -32,7 +32,10 @@ export default (viewName, path) => {
},
deactivate() {
this.searchService.set('contextType', 'private_messages');
this.searchService.set(
'searchContext',
this.controllerFor("user").get("model.searchContext")
);
}
});
};

View File

@ -20,11 +20,11 @@ const TopicRoute = Discourse.Route.extend({
titleToken() {
const model = this.modelFor('topic');
if (model) {
const result = model.get('unicode_title') ? model.get('unicode_title') : model.get('title'),
const result = model.get('unicode_title') || model.get('title'),
cat = model.get('category');
// Only display uncategorized in the title tag if it was renamed
if (cat && !(cat.get('isUncategorizedCategory') && cat.get('name').toLowerCase() === "uncategorized")) {
if (this.siteSettings.topic_page_title_includes_category && cat && !(cat.get('isUncategorizedCategory') && cat.get('name').toLowerCase() === "uncategorized")) {
let catName = cat.get('name');
const parentCategory = cat.get('parentCategory');

View File

@ -17,7 +17,7 @@
{{toolbar-popup-menu-options
onPopupMenuAction=onPopupMenuAction
onExpand=(action b.action b)
title="composer.options"
title=b.title
headerIcon=b.icon
class=b.className
content=popupMenuOptions}}

View File

@ -18,7 +18,7 @@
{{#if category.availableGroups}}
{{combo-box class="available-groups"
allowInitialValueMutation=true
allowsContentReplacement=true
allowContentReplacement=true
content=category.availableGroups
value=selectedGroup}}
{{combo-box allowInitialValueMutation=true

View File

@ -51,7 +51,7 @@
<div class='search-title clearfix'>
<div class='result-count'>
<span>
{{{i18n "search.result_count" count=resultCount term=noSortQ}}}
{{{resultCountLabel}}}
</span>
</div>
<div class='sort-by'>

View File

@ -3,13 +3,15 @@
<div class="controls">
<span class='static'>{{model.username}}</span>
{{#if model.can_edit_username}}
{{#link-to "preferences.username" class="btn btn-small pad-left no-text"}}
{{#link-to "preferences.username" class="btn btn-small btn-icon pad-left no-text"}}
{{d-icon "pencil"}} {{/link-to}}
{{/if}}
</div>
<div class='instructions'>
{{{i18n 'user.username.short_instructions' username=model.username}}}
</div>
{{#if siteSettings.enable_mentions}}
<div class='instructions'>
{{{i18n 'user.username.short_instructions' username=model.username}}}
</div>
{{/if}}
</div>
{{#if canEditName}}
@ -35,7 +37,7 @@
<div class="controls">
<span class='static'>{{model.email}}</span>
{{#if model.can_edit_email}}
{{#link-to "preferences.email" class="btn btn-small pad-left no-text"}}{{d-icon "pencil"}}{{/link-to}}
{{#link-to "preferences.email" class="btn btn-small btn-icon pad-left no-text"}}{{d-icon "pencil"}}{{/link-to}}
{{/if}}
</div>
<div class='instructions'>
@ -82,22 +84,24 @@
<label class="control-label">{{i18n 'user.title.title'}}</label>
<div class="controls">
<span class="static">{{model.title}}</span>
{{#link-to "preferences.badgeTitle" class="btn btn-small pad-left no-text"}}{{d-icon "pencil"}}{{/link-to}}
{{#link-to "preferences.badgeTitle" class="btn btn-small btn-icon pad-left no-text"}}{{d-icon "pencil"}}{{/link-to}}
</div>
</div>
{{/if}}
{{plugin-outlet name="user-preferences-account" args=(hash model=model)}}
{{plugin-outlet name="user-preferences-account" args=(hash model=model save=(action "save"))}}
<br/>
{{plugin-outlet name="user-custom-controls" args=(hash model=model)}}
<div class="control-group save-button">
<div class="controls">
{{partial 'user/preferences/save-button'}}
{{#if canSaveUser}}
<div class="control-group save-button">
<div class="controls">
{{partial 'user/preferences/save-button'}}
</div>
</div>
</div>
{{/if}}
{{#if model.canDeleteAccount}}

View File

@ -1,7 +1,7 @@
<div class="control-group notifications">
<div class="controls controls-dropdown">
<label>{{i18n 'user.new_topic_duration.label'}}</label>
{{combo-box valueAttribute="value" content=considerNewTopicOptions value=model.user_option.new_topic_duration_minutes}}
{{combo-box class="duration" valueAttribute="value" content=considerNewTopicOptions value=model.user_option.new_topic_duration_minutes}}
</div>
<div class="controls controls-dropdown">

View File

@ -191,7 +191,7 @@
{{#if showBadges}}
<li>{{#link-to 'user.badges'}}{{d-icon "certificate"}}{{i18n 'badges.title'}}{{/link-to}}</li>
{{/if}}
{{plugin-outlet name="user-main-nav" connectorTagName='li' args=(hash model=model)}}
{{plugin-outlet name="user-main-nav" tagName='' connectorTagName='li' args=(hash model=model)}}
{{#if model.can_edit}}
<li>{{#link-to 'preferences'}}{{d-icon "cog"}}{{i18n 'user.preferences'}}{{/link-to}}</li>
{{/if}}

View File

@ -119,6 +119,23 @@ createWidget('actions-summary-item', {
}
});
createWidget('deleted-post', {
tagName: 'div.post-action.deleted-post',
html(attrs) {
return [
iconNode('trash-o'),
' ',
avatarFor.call(this, 'small', {
template: attrs.deletedByAvatarTemplate,
username: attrs.deletedByUsername
}),
' ',
dateNode(attrs.deleted_at)
];
}
});
export default createWidget('actions-summary', {
tagName: 'section.post-actions',
@ -131,16 +148,7 @@ export default createWidget('actions-summary', {
});
if (attrs.deleted_at) {
body.push(h('div.post-action', [
iconNode('trash-o'),
' ',
avatarFor.call(this, 'small', {
template: attrs.deletedByAvatarTemplate,
username: attrs.deletedByUsername
}),
' ',
dateNode(attrs.deleted_at)
]));
body.push(this.attach('deleted-post', attrs));
}
return body;

View File

@ -3,6 +3,12 @@ import { createWidget } from 'discourse/widgets/widget';
import { h } from 'virtual-dom';
createWidget('menu-links', {
buildClasses(attrs) {
if (attrs.name && attrs.name.length) {
return `menu-container-${attrs.name}`;
}
},
html(attrs) {
const links = [].concat(attrs.contents());
const liOpts = {};

View File

@ -16,7 +16,7 @@ export function buildManageButtons(attrs, currentUser) {
}
let contents = [];
if (attrs.canManage) {
if (currentUser.staff) {
contents.push({
icon: 'list',
label: 'admin.flags.moderation_history',

View File

@ -185,11 +185,13 @@ export default createWidget('topic-admin-menu', {
label: isPrivateMessage ? 'actions.make_public' : 'actions.make_private' });
}
buttons.push({
action: 'showModerationHistory',
icon: 'list',
fullLabel: 'admin.flags.moderation_history'
});
if (this.currentUser.get('staff')) {
buttons.push({
action: 'showModerationHistory',
icon: 'list',
fullLabel: 'admin.flags.moderation_history'
});
}
const extraButtons = applyDecorators(this, 'adminMenuButtons', this.attrs, this.state);

View File

@ -50,7 +50,9 @@ class Ruler {
getRuleForTag(tag) {
this.ensureCache();
return this.cache[tag];
if (this.cache.hasOwnProperty(tag)) {
return this.cache[tag];
}
}
ensureCache() {

View File

@ -7,10 +7,15 @@ export default MultiSelectComponent.extend({
filterable: true,
allowAny: false,
rowComponent: "category-row",
categories: null,
blacklist: null,
init() {
this._super();
if (!this.get("categories")) this.set("categories", []);
if (!this.get("blacklist")) this.set("blacklist", []);
this.get("headerComponentOptions").setProperties({
selectedNameComponent: "multi-select/selected-category"
});

View File

@ -4,6 +4,5 @@ export default SelectKitRowComponent.extend({
layoutName: "select-kit/templates/components/dropdown-select-box/dropdown-select-box-row",
classNames: "dropdown-select-box-row",
name: Ember.computed.alias("computedContent.name"),
description: Ember.computed.alias("computedContent.originalContent.description")
});

View File

@ -116,6 +116,7 @@ export default SelectKitComponent.extend({
baseHeaderComputedContent() {
return {
title: this.get("title"),
selectedComputedContents: this.get("selectedComputedContents")
};
},

View File

@ -3,14 +3,23 @@ import computed from "ember-addons/ember-computed-decorators";
import SelectKitHeaderComponent from "select-kit/components/select-kit/select-kit-header";
export default SelectKitHeaderComponent.extend({
attributeBindings: ["names:data-name"],
attributeBindings: [
"label:title",
"label:aria-label",
"names:data-name",
"values:data-value"
],
classNames: "multi-select-header",
layoutName: "select-kit/templates/components/multi-select/multi-select-header",
selectedNameComponent: Ember.computed.alias("options.selectedNameComponent"),
ariaLabel: Ember.computed.or("computedContent.ariaLabel", "title", "names"),
title: Ember.computed.or("computedContent.title", "names"),
@on("didRender")
_positionFilter() {
if (this.get("shouldDisplayFilter") === false) { return; }
if (!this.get("shouldDisplayFilter")) return;
const $filter = this.$(".filter");
$filter.width(0);
@ -26,7 +35,12 @@ export default SelectKitHeaderComponent.extend({
},
@computed("computedContent.selectedComputedContents.[]")
names(selectedComputedContents) {
return Ember.makeArray(selectedComputedContents).map(sc => sc.name).join(",");
names(selection) {
return Ember.makeArray(selection).map(s => s.name).join(",");
},
@computed("computedContent.selectedComputedContents.[]")
values(selection) {
return Ember.makeArray(selection).map(s => s.value).join(",");
}
});

View File

@ -6,8 +6,9 @@ export default SelectedNameComponent.extend({
classNames: "selected-category",
layoutName: "select-kit/templates/components/multi-select/selected-category",
@computed("content.originalContent")
@computed("computedContent.originalContent")
badge(category) {
return categoryBadgeHTML(category, {allowUncategorized: true, link: false}).htmlSafe();
return categoryBadgeHTML(category, { allowUncategorized: true, link: false })
.htmlSafe();
}
});

View File

@ -5,7 +5,7 @@ export default SelectedNameComponent.extend({
layoutName: "select-kit/templates/components/multi-select/selected-color",
didRender() {
const name = this.get("content.name");
const name = this.get("name");
this.$(".color-preview").css("background", `#${name}`.htmlSafe());
}
});

View File

@ -3,8 +3,10 @@ import computed from "ember-addons/ember-computed-decorators";
export default Ember.Component.extend({
attributeBindings: [
"tabindex",
"content.name:data-name",
"content.value:data-value",
"ariaLabel:aria-label",
"title",
"name:data-name",
"value:data-value",
"guid:data-guid"
],
classNames: ["selected-name", "choice"],
@ -13,11 +15,27 @@ export default Ember.Component.extend({
tagName: "span",
tabindex: -1,
@computed("content")
guid(content) { return Ember.guidFor(content); },
@computed("computedContent")
guid(computedContent) { return Ember.guidFor(computedContent); },
isLocked: Ember.computed("content.locked", function() {
return this.getWithDefault("content.locked", false);
ariaLabel: Ember.computed.or("computedContent.ariaLabel", "title"),
@computed("computedContent.title", "name")
title(computedContentTitle, name) {
if (computedContentTitle) return computedContentTitle;
if (name) return name;
return null;
},
label: Ember.computed.or("computedContent.label", "title", "name"),
name: Ember.computed.alias("computedContent.name"),
value: Ember.computed.alias("computedContent.value"),
isLocked: Ember.computed("computedContent.locked", function() {
return this.getWithDefault("computedContent.locked", false);
}),
click() {

View File

@ -17,8 +17,9 @@ export default DropdownSelectBoxComponent.extend({
const state = pinned ? `pinned${globally}` : "unpinned";
const title = I18n.t(`topic_statuses.${state}.title`);
content.name = `${title}${iconHTML("caret-down")}`.htmlSafe();
content.dataName = title;
content.label = `${title}${iconHTML("caret-down")}`.htmlSafe();
content.title = title;
content.name = state;
content.icon = `thumb-tack${state === "unpinned" ? " unpinned" : ''}`;
return content;
},

View File

@ -29,7 +29,6 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
renderedBodyOnce: false,
renderedFilterOnce: false,
tabindex: 0,
scrollableParentSelector: ".modal-body",
none: null,
highlightedValue: null,
noContentLabel: "select_kit.no_content",
@ -61,15 +60,13 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
computedContent: null,
limitMatches: 100,
nameChanges: false,
allowsContentReplacement: false,
allowContentReplacement: false,
collectionHeader: null,
init() {
this._super();
this.noneValue = "__none__";
this._previousScrollParentOverflow = "auto";
this._previousCSSContext = {};
this.set("headerComponentOptions", Ember.Object.create());
this.set("rowComponentOptions", Ember.Object.create());
this.set("computedContent", []);
@ -82,7 +79,7 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
this.addObserver(`content.@each.${this.get("nameProperty")}`, this, this._compute);
}
if (this.get("allowsContentReplacement")) {
if (this.get("allowContentReplacement")) {
this.addObserver(`content.[]`, this, this._compute);
}
},

View File

@ -5,32 +5,37 @@ export default Ember.Component.extend({
classNames: ["select-kit-header", "select-box-kit-header"],
classNameBindings: ["isFocused"],
attributeBindings: [
"dataName:data-name",
"tabindex",
"ariaLabel:aria-label",
"ariaHasPopup:aria-haspopup",
"title"
"title",
"value:data-value",
"name:data-name",
],
ariaHasPopup: true,
ariaLabel: Ember.computed.alias("title"),
ariaLabel: Ember.computed.or("computedContent.ariaLabel", "title"),
@computed("computedContent.title", "name")
title(computedContentTitle, name) {
if (computedContentTitle) return computedContentTitle;
if (name) return name;
return null;
},
label: Ember.computed.or("computedContent.label", "title", "name"),
name: Ember.computed.alias("computedContent.name"),
value: Ember.computed.alias("computedContent.value"),
@computed("computedContent.icon", "computedContent.icons")
icons(icon, icons) {
return Ember.makeArray(icon).concat(icons).filter(i => !Ember.isEmpty(i));
},
@computed("computedContent.dataName", "name")
dataName(dataName, name) { return dataName || name; },
@computed("title", "computedContent.title", "name")
title(title, computedContentTitle, name) {
return title || computedContentTitle || name;
},
click() {
this.sendAction("onToggle");
}

View File

@ -11,23 +11,35 @@ export default Ember.Component.extend(UtilsMixin, {
attributeBindings: [
"tabIndex",
"title",
"computedContent.value:data-value",
"computedContent.name:data-name"
"value:data-value",
"name:data-name",
"ariaLabel:aria-label"
],
classNameBindings: ["isHighlighted", "isSelected"],
@computed("computedContent.title", "computedContent.name")
title(title, name) { return title || name; },
ariaLabel: Ember.computed.or("computedContent.ariaLabel", "title"),
@computed("computedContent.title", "name")
title(computedContentTitle, name) {
if (computedContentTitle) return computedContentTitle;
if (name) return name;
return null;
},
label: Ember.computed.or("computedContent.label", "title", "name"),
name: Ember.computed.alias("computedContent.name"),
value: Ember.computed.alias("computedContent.value"),
@computed("templateForRow")
template(templateForRow) { return templateForRow(this); },
@on("didReceiveAttrs")
_setSelectionState() {
const contentValue = this.get("computedContent.value");
this.set("isSelected", this.get("computedValue") === contentValue);
this.set("isHighlighted", this.get("highlightedValue") === contentValue);
this.set("isSelected", this.get("computedValue") === this.get("value"));
this.set("isHighlighted", this.get("highlightedValue") === this.get("value"));
},
@on("willDestroyElement")

View File

@ -78,7 +78,9 @@ export default SelectKitComponent.extend({
baseHeaderComputedContent() {
return {
title: this.get("title"),
icons: Ember.makeArray(this.getWithDefault("headerIcon", [])),
value: this.get("selectedComputedContent.value"),
name: this.get("selectedComputedContent.name") || this.get("noneRowComputedContent.name")
};
},

View File

@ -10,7 +10,7 @@ export default DropdownSelectBoxComponent.extend({
@computed("title")
collectionHeader(title) {
return `<h3>${I18n.t(title)}</h3>`;
return `<h3>${title}</h3>`;
},
mutateValue(value) {

View File

@ -4,12 +4,16 @@ export default Ember.Mixin.create({
init() {
this._super();
this._previousScrollParentOverflow = null;
this._previousCSSContext = null;
this.filterInputSelector = ".filter-input";
this.rowSelector = ".select-kit-row";
this.collectionSelector = ".select-kit-collection";
this.headerSelector = ".select-kit-header";
this.bodySelector = ".select-kit-body";
this.wrapperSelector = ".select-kit-wrapper";
this.scrollableParentSelector = ".modal-body";
this.fixedPlaceholderSelector = `.select-kit-fixed-placeholder-${this.elementId}`;
},
$findRowByValue(value) { return this.$(`${this.rowSelector}[data-value='${value}']`); },
@ -18,15 +22,16 @@ export default Ember.Mixin.create({
$body() { return this.$(this.bodySelector); },
$wrapper() { return this.$(this.wrapperSelector); },
$collection() { return this.$(this.collectionSelector); },
$rows(withHidden) {
$scrollableParent() { return $(this.scrollableParentSelector); },
if (withHidden === true) {
return this.$(`${this.rowSelector}:not(.no-content)`);
} else {
return this.$(`${this.rowSelector}:not(.no-content):not(.is-hidden)`);
}
$fixedPlaceholder() { return $(this.fixedPlaceholderSelector); },
$rows() {
return this.$(`${this.rowSelector}:not(.no-content):not(.is-hidden)`);
},
$highlightedRow() { return this.$rows().filter(".is-highlighted"); },
@ -36,8 +41,7 @@ export default Ember.Mixin.create({
$filterInput() { return this.$(this.filterInputSelector); },
@on("didRender")
_ajustPosition() {
$(`.select-kit-fixed-placeholder-${this.elementId}`).remove();
_adjustPosition() {
this.$collection().css("max-height", this.get("collectionHeight"));
this._applyFixedPosition();
this._applyDirection();
@ -46,7 +50,7 @@ export default Ember.Mixin.create({
@on("willDestroyElement")
_clearState() {
$(`.select-kit-fixed-placeholder-${this.elementId}`).remove();
this.$fixedPlaceholder().remove();
},
// use to collapse and remove focus
@ -102,17 +106,14 @@ export default Ember.Mixin.create({
_applyDirection() {
let options = { left: "auto", bottom: "auto", top: "auto" };
const dHeader = $(".d-header")[0];
const dHeaderBounds = dHeader ? dHeader.getBoundingClientRect() : {top: 0, height: 0};
const dHeaderHeight = dHeaderBounds.top + dHeaderBounds.height;
const bodyHeight = this.$body()[0].getBoundingClientRect().height;
const discourseHeader = $(".d-header")[0];
const discourseHeaderHeight = discourseHeader ? (discourseHeader.getBoundingClientRect().top + this._computedStyle(discourseHeader, "height")) : 0;
const bodyHeight = this._computedStyle(this.$body()[0], "height");
const windowWidth = $(window).width();
const windowHeight = $(window).height();
const boundingRect = this.get("element").getBoundingClientRect();
const componentHeight = boundingRect.height;
const componentWidth = boundingRect.width;
const offsetTop = boundingRect.top;
const offsetBottom = boundingRect.bottom;
const componentHeight = this._computedStyle(this.get("element"), "height");
const componentWidth = this._computedStyle(this.get("element"), "width");
const offsetTop = this.get("element").getBoundingClientRect().top;
const offsetBottom = this.get("element").getBoundingClientRect().bottom;
if (this.get("fullWidthOnMobile") && (this.site && this.site.isMobileDevice)) {
const margin = 10;
@ -121,10 +122,10 @@ export default Ember.Mixin.create({
options.width = windowWidth - margin * 2;
options.maxWidth = options.minWidth = "unset";
} else {
const bodyWidth = this.$body()[0].getBoundingClientRect().width;
const bodyWidth = this._computedStyle(this.$body()[0], "width");
if ($("html").css("direction") === "rtl") {
const horizontalSpacing = boundingRect.right;
if (this._isRTL()) {
const horizontalSpacing = this.get("element").getBoundingClientRect().right;
const hasHorizontalSpace = horizontalSpacing - (this.get("horizontalOffset") + bodyWidth) > 0;
if (hasHorizontalSpace) {
this.setProperties({ isLeftAligned: true, isRightAligned: false });
@ -134,7 +135,7 @@ export default Ember.Mixin.create({
options.right = - (bodyWidth - componentWidth + this.get("horizontalOffset"));
}
} else {
const horizontalSpacing = boundingRect.left;
const horizontalSpacing = this.get("element").getBoundingClientRect().left;
const hasHorizontalSpace = (windowWidth - (this.get("horizontalOffset") + horizontalSpacing + bodyWidth) > 0);
if (hasHorizontalSpace) {
this.setProperties({ isLeftAligned: true, isRightAligned: false });
@ -147,87 +148,109 @@ export default Ember.Mixin.create({
}
const fullHeight = this.get("verticalOffset") + bodyHeight + componentHeight;
const hasBelowSpace = windowHeight - offsetBottom - fullHeight > 0;
const hasAboveSpace = offsetTop - fullHeight - dHeaderHeight > 0;
const hasBelowSpace = $(window).height() - offsetBottom - fullHeight > 0;
const hasAboveSpace = offsetTop - fullHeight - discourseHeaderHeight > 0;
const headerHeight = this._computedStyle(this.$header()[0], "height");
if (hasBelowSpace || (!hasBelowSpace && !hasAboveSpace)) {
this.setProperties({ isBelow: true, isAbove: false });
options.top = this.$header()[0].getBoundingClientRect().height + this.get("verticalOffset");
options.top = headerHeight + this.get("verticalOffset");
} else {
this.setProperties({ isBelow: false, isAbove: true });
options.bottom = this.$header()[0].getBoundingClientRect().height + this.get("verticalOffset");
options.bottom = headerHeight + this.get("verticalOffset");
}
this.$body().css(options);
},
_applyFixedPosition() {
if (this.get("isExpanded") !== true) { return; }
if (this.get("isExpanded") !== true) return;
if (this.$fixedPlaceholder().length === 1) return;
if (this.$scrollableParent().length === 0) return;
const scrollableParent = this.$().parents(this.get("scrollableParentSelector"));
if (scrollableParent.length === 0) { return; }
const width = this._computedStyle(this.get("element"), "width");
const height = this._computedStyle(this.get("element"), "height");
const boundingRect = this.get("element").getBoundingClientRect();
const width = boundingRect.width;
const height = boundingRect.height;
const $placeholder = $(`<div class='select-kit-fixed-placeholder-${this.elementId}'></div>`);
this._previousScrollParentOverflow = this._previousScrollParentOverflow ||
this.$scrollableParent().css("overflow");
this._previousScrollParentOverflow = this._previousScrollParentOverflow || scrollableParent.css("overflow");
scrollableParent.css({ overflow: "hidden" });
this._previousCSSContext = {
this._previousCSSContext = this._previousCSSContext || {
width,
minWidth: this.$().css("min-width"),
maxWidth: this.$().css("max-width")
maxWidth: this.$().css("max-width"),
top: this.$().css("top"),
left: this.$().css("left"),
marginLeft: this.$().css("margin-left"),
marginRight: this.$().css("margin-right"),
position: this.$().css("position")
};
const componentStyles = {
position: "fixed",
"margin-top": -scrollableParent.scrollTop(),
top: this.get("element").getBoundingClientRect().top,
width,
left: this.get("element").getBoundingClientRect().left,
marginLeft: 0,
marginRight: 0,
minWidth: "unset",
maxWidth: "unset"
maxWidth: "unset",
position: "fixed"
};
if ($("html").css("direction") === "rtl") {
componentStyles.marginRight = -width;
} else {
componentStyles.marginLeft = -width;
}
const $placeholderTemplate = $(`<div class='select-kit-fixed-placeholder-${this.elementId}'></div>`);
$placeholderTemplate.css({
display: "inline-block",
width,
height,
"vertical-align": "middle"
});
$placeholder.css({ display: "inline-block", width, height, "vertical-align": "middle" });
this.$()
.before($placeholderTemplate)
.css(componentStyles);
this.$().before($placeholder).css(componentStyles);
this.$scrollableParent().css({ overflow: "hidden" });
},
_removeFixedPosition() {
$(`.select-kit-fixed-placeholder-${this.elementId}`).remove();
this.$fixedPlaceholder().remove();
if (!this.element || this.isDestroying || this.isDestroyed) { return; }
if (!this.element || this.isDestroying || this.isDestroyed) return;
if (this.$scrollableParent().length === 0) return;
const scrollableParent = this.$().parents(this.get("scrollableParentSelector"));
if (scrollableParent.length === 0) { return; }
const css = jQuery.extend(
this._previousCSSContext,
{
top: "auto",
left: "auto",
"margin-left": "auto",
"margin-right": "auto",
"margin-top": "auto",
position: "relative"
}
);
this.$().css(css);
scrollableParent.css("overflow", this._previousScrollParentOverflow);
this.$().css(this._previousCSSContext || {});
this.$scrollableParent().css("overflow", this._previousScrollParentOverflow || {});
},
_positionWrapper() {
const headerBoundingRect = this.$header()[0].getBoundingClientRect();
const elementWidth = this._computedStyle(this.get("element"), "width");
const headerHeight = this._computedStyle(this.$header()[0], "height");
const bodyHeight = this._computedStyle(this.$body()[0], "height");
this.$(this.wrapperSelector).css({
width: this.get("element").getBoundingClientRect().width,
height: headerBoundingRect.height + this.$body()[0].getBoundingClientRect().height
this.$wrapper().css({
width: elementWidth,
height: headerHeight + bodyHeight
});
},
_isRTL() {
return $("html").css("direction") === "rtl";
},
_computedStyle(element, style) {
if (!element) return 0;
let value;
if (window.getComputedStyle) {
value = window.getComputedStyle(element, null)[style];
} else {
value = $(element).css(style);
}
return this._getFloat(value);
},
_getFloat(value) {
value = parseFloat(value);
return $.isNumeric(value) ? value : 0;
}
});

View File

@ -38,8 +38,12 @@ export default Ember.Mixin.create({
$(document)
.on("mousedown.select-kit", event => {
event.stopPropagation();
if (!this.get("renderedBodyOnce")) return;
if (!this.get("isFocused")) return;
if (Ember.isNone(this.get("element"))) return;
if (this.get("element").contains(event.target)) return;
if (Ember.$.contains(this.get("element"), event.target)) return;
this.didClickOutside(event);
});

View File

@ -15,5 +15,5 @@
<div class="category-desc">{{{description}}}</div>
{{/if}}
{{else}}
{{computedContent.name}}
{{{label}}}
{{/if}}

View File

@ -1,7 +1,7 @@
{{#each icons as |icon|}} {{d-icon icon}} {{/each}}
<span class="selected-name" title={{title}}>
{{{name}}}
<span class="selected-name">
{{{label}}}
</span>
{{#if shouldDisplayClearableButton}}

View File

@ -2,6 +2,6 @@
{{#if options.showFullTitle}}
<span class="d-button-label selected-name">
{{name}}
{{label}}
</span>
{{/if}}

View File

@ -9,7 +9,7 @@
{{/if}}
<div class="texts">
<span class="name">{{{name}}}</span>
<span class="name">{{{label}}}</span>
<span class="desc">{{{description}}}</span>
</div>
{{/if}}

View File

@ -5,7 +5,7 @@
{{/if}}
<span class="selected-name" title={{title}}>
{{{name}}}
{{{label}}}
</span>
{{#if computedContent.datetime}}

View File

@ -4,7 +4,7 @@
</div>
{{/if}}
<span class="name">{{computedContent.name}}</span>
<span class="name">{{label}}</span>
{{#if computedContent.datetime}}
<span class="future-date-input-selector-datetime">

View File

@ -1,6 +1,6 @@
<div class="choices">
{{#each computedContent.selectedComputedContents as |selectedComputedContent|}}
{{component selectedNameComponent onDeselect=onDeselect content=selectedComputedContent}}
{{component selectedNameComponent onDeselect=onDeselect computedContent=selectedComputedContent}}
{{/each}}
<span class="filter choice" tabindex="-1">
{{component "select-kit/select-kit-filter"

View File

@ -1,5 +1,5 @@
<span class="name">
<span class="delete-icon" {{action onDeselect content bubbles=false}}>
<span class="delete-icon" {{action onDeselect computedContent bubbles=false}}>
{{d-icon "times"}}
</span>

View File

@ -1,12 +1,12 @@
<div class="selected-color-wrapper">
<span class="name">
{{#unless isLocked}}
<span class="delete-icon" {{action onDeselect content bubbles=false}}>
<span class="delete-icon" {{action onDeselect computedContent bubbles=false}}>
{{d-icon "times"}}
</span>
{{/unless}}
#{{content.name}}
#{{{label}}}
</span>
<span class="color-preview"></span>

View File

@ -3,11 +3,11 @@
{{d-icon "lock"}}
</span>
{{else}}
<span class="locked-icon" {{action onDeselect content bubbles=false}}>
<span class="locked-icon" {{action onDeselect computedContent bubbles=false}}>
{{d-icon "times"}}
</span>
{{/if}}
<span class="name">
{{content.name}}
{{{label}}}
</span>

View File

@ -9,7 +9,6 @@
onClear=(action "onClear")
options=headerComponentOptions
shouldDisplayFilter=shouldDisplayFilter
title=(i18n title)
}}
<div class="select-kit-body">

View File

@ -12,7 +12,7 @@
highlightedValue=highlightedValue
onClear=onClear
onHighlight=onHighlight
value=computedValue
computedValue=computedValue
options=rowComponentOptions
}}
{{/if}}
@ -25,7 +25,7 @@
highlightedValue=highlightedValue
onHighlight=onHighlight
onCreate=onCreate
value=computedValue
computedValue=computedValue
options=rowComponentOptions
}}
{{/if}}

View File

@ -1,5 +1,5 @@
{{#each icons as |icon|}} {{d-icon icon}} {{/each}}
<span class="selected-name" title={{name}}>
{{{name}}}
<span class="selected-name">
{{{label}}}
</span>

View File

@ -2,5 +2,5 @@
{{{template}}}
{{else}}
{{#each icons as |icon|}} {{d-icon icon}} {{/each}}
<span class="name">{{computedContent.name}}</span>
<span class="name">{{{label}}}</span>
{{/if}}

View File

@ -363,12 +363,15 @@ $mobile-breakpoint: 700px;
.groups, .badges, .web-hook-container {
.form-horizontal {
label {
font-weight: bold;
& > div {
margin-bottom: 20px;
}
& > div {
margin-top: 10px;
.d-editor-textarea-wrapper {
max-width: 60%;
.d-editor-button-bar {
overflow: hidden;
}
}
input, textarea, select, .select-box {

View File

@ -4,6 +4,10 @@
margin-bottom: 10px;
input {
margin-bottom: 0;
font-size: 1rem;
}
.btn-small {
font-size: 1rem;
}
}

View File

@ -174,7 +174,7 @@
}
td.stats {
.unit {
font-size: .8em;
font-size: .857em;
}
}
@ -217,7 +217,7 @@
padding: 0;
border: 0;
color: $danger-medium;
font-size: 0.929em;
font-size: 1em;
cursor: default;
}
}
@ -267,7 +267,7 @@ ol.category-breadcrumb {
margin-bottom: 0;
}
a.badge-category, .dropdown-header {
font-size: 0.929em;
font-size: 0.857em;
font-weight: bold;
float: none;
text-transform: none;
@ -296,7 +296,7 @@ ol.category-breadcrumb {
float: left;
margin: 5px 0 10px;
.top-date-string {
font-size: 0.7em;
font-size: 0.857em;
}
}
@ -318,7 +318,7 @@ ol.category-breadcrumb {
@include unselectable;
font-size: 1.2em;
font-size: 1.143em;
border: 1px solid $primary-low;
padding: 5px;
background: $secondary;
@ -342,7 +342,7 @@ ol.category-breadcrumb {
}
.top-date-string {
font-weight: normal;
font-size: 0.8em;
font-size: 0.857em;
}
&:hover {
background-color: $highlight-medium;

View File

@ -75,7 +75,7 @@
.description {
padding: 0 1em 1em 1em;
text-align: center;
font-size: 1.05em;
font-size: 1em;
color: dark-light-choose($primary-medium, $secondary-high);
.overflow {
max-height: 6em;
@ -85,7 +85,7 @@
}
h3 {
font-size: 1.2em;
font-size: 1.286em;
margin-bottom: 0.5em;
margin-top: 0.25em;
line-height: 1.1em;
@ -102,7 +102,7 @@
}
h3 {
font-size: 1.2em;
font-size: 1.286em;
text-align: center;
}

View File

@ -19,7 +19,7 @@
z-index: 999;
transition: height 250ms ease, background 250ms ease, transform 250ms ease, max-width 250ms ease;
background-color: $secondary;
box-shadow: 0 -2px 40px rgba($primary, .12);
box-shadow: 0 -1px 40px rgba(0,0,0, .12);
.reply-area {
display: flex;
@ -300,7 +300,7 @@
color: $primary;
}
span.name {
font-size: .8em;
font-size: .857em;
vertical-align: middle;
}
&.selected {
@ -326,7 +326,7 @@ div.ac-wrap.disabled {
div.ac-wrap div.item a.remove, .remove-link {
margin-left: 4px;
font-size: .8em;
font-size: .857em;
line-height: 10px;
padding: 1.5px 1.5px 1.5px 2.5px;
border-radius: 12px;

View File

@ -29,7 +29,7 @@
border-bottom: 1px solid $primary-low;
.number, .time-read {
font-size: 1.4em;
font-size: 1.429em;
color: $primary-medium;
}
}

View File

@ -47,11 +47,11 @@ body {
}
big {
font-size: 2em;
font-size: 2.286em;
}
small {
font-size: .643em;
font-size: .786em;
}
//setting a static limit on big and small prevents nesting abuse
@ -423,7 +423,7 @@ select {
.content-list {
h3 {
color: $primary-medium;
font-size: 1.071em;
font-size: 1.143em;
padding-left: 5px;
margin-bottom: 10px;
}
@ -474,7 +474,7 @@ select {
.control-label {
font-weight: bold;
font-size: 1.2em;
font-size: 1.286em;
line-height: 2;
}
@ -517,7 +517,7 @@ select {
#loading-message {
position: absolute;
font-size: 2.143em;
font-size: 2.286em;
text-align: center;
top: 120px;
left: 500px;

View File

@ -49,7 +49,7 @@
h3 {
font-weight: normal;
font-size: 1.071em;
font-size: 1.143em;
}
}
}

View File

@ -7,12 +7,12 @@
height: 60px;
}
.reason {
font-size: 1.714em;
font-size: 1.857em;
height: 24px;
}
.url {
font-style: italic;
font-size: .786em;
font-size: .857em;
}
.desc {
margin-top: 16px;

View File

@ -15,7 +15,7 @@
.time,
.delete-info {
color: lighten($primary, 40%);
font-size: 0.8em;
font-size: 0.857em;
}
.group-member-info {
@ -28,11 +28,13 @@
display: flex;
width: 100%;
justify-content: space-between;
.group-post-title {
font-size: 1.143em;
}
}
.group-post-excerpt {
margin: 1em 0;
font-size: 0.929em;
word-wrap: break-word;
color: $primary;
}
@ -45,13 +47,13 @@
width: 100%;
.group-info-name {
font-size: 1.4em;
font-size: 1.429em;
font-weight: bold;
color: $primary;
}
.group-info-full-name {
font-size: 1.2em;
font-size: 1.286em;
color: dark-light-choose($primary-high, $secondary-low);
}

View File

@ -22,7 +22,7 @@
}
td.groups-user-count {
font-size: 1.2em;
font-size: 1.286em;
}
}
@ -38,7 +38,7 @@
}
.groups-info-title {
font-size: 0.9em;
font-size: 0.857em;
color: dark-light-choose($primary-medium, $secondary-medium);
}

View File

@ -28,7 +28,7 @@ $input-width: 220px;
}
.disclaimer {
font-size: 0.9em;
font-size: 0.857em;
color: dark-light-choose($primary-medium, $secondary-medium);
clear: both;
}
@ -58,7 +58,7 @@ $input-width: 220px;
.instructions {
color: dark-light-choose($primary-medium, $secondary-medium);
margin: 0;
font-size: 0.929em;
font-size: 0.857em;
font-weight: normal;
line-height: 18px;
}
@ -100,7 +100,7 @@ $input-width: 220px;
.instructions {
color: dark-light-choose($primary-medium, $secondary-medium);
margin: 0;
font-size: 0.929em;
font-size: 0.857em;
font-weight: normal;
line-height: 18px;
}

View File

@ -285,7 +285,6 @@ button {
}
padding: 0 0 18px 10px;
color: $controls-color;
font-style: normal;
font-size: 2em;
font-family: Arial, Baskerville, monospace;

View File

@ -65,7 +65,7 @@
}
.new {
font-size: 0.8em;
font-size: 0.857em;
margin-left: 0.5em;
color: dark-light-choose($primary-medium, $secondary-medium);
}
@ -174,7 +174,7 @@
}
li:not(.category):not(.heading) {
font-size: 0.929em;
font-size: 1em;
line-height: 16px;
.fa {
@ -327,9 +327,6 @@ div.menu-links-header {
color: dark-light-choose($primary-medium, $secondary-medium);
}
a {
font-size: 1.1em;
}
.d-icon-user {
margin-right: 0.2em;
}

View File

@ -153,7 +153,7 @@
}
p {
color: darken($primary, 40%);
font-size: 0.929em;
font-size: 1em;
}
.archetype-option {
margin-bottom: 20px;
@ -247,7 +247,7 @@
margin-left: 0 !important; // override needed
font-weight: bold;
.topic-title {
font-size: 0.929em;
font-size: 1em;
font-weight: normal;
}
&.btn-reply-here {

View File

@ -1,7 +1,7 @@
// Page not found styles
h1.page-not-found {
font-size: 2.25em;
font-size: 2.286em;
line-height: 1.25;
}

View File

@ -118,7 +118,7 @@ aside.onebox {
clear: both;
h3, h4 {
font-size: 1.17em;
font-size: 1.143em;
margin: 0 0 10px 0;
}
@ -395,7 +395,7 @@ aside.onebox.twitterstatus .onebox-body {
.album-title {
width: 100%;
font-size: 1.083em;
font-size: 1.143em;
line-height: 30px;
color: #ccc;
text-decoration: none;

View File

@ -4,10 +4,10 @@
input[type=text] {
width: 320px;
height: 30px;
font-size: 1.571em;
font-size: 1.429em;
}
input[type=submit] {
font-size: 1.571em;
font-size: 1.429em;
padding: 10px;
}
}

View File

@ -36,7 +36,7 @@
}
.search-link {
.topic-statuses, .topic-title {
font-size: 1.3em;
font-size: 1.286em;
line-height: 25px;
}
@ -63,7 +63,7 @@
}
.discourse-tag {
font-size: 0.8em;
font-size: 0.857em;
}
}

View File

@ -22,7 +22,7 @@
margin: 14px 0;
}
h3 {
font-size: 0.929em;
font-size: 1em;
}
.copy-text {
display: inline-block;
@ -31,7 +31,7 @@
color: $success;
opacity: 1;
transition: opacity 0.25s;
font-size: .929em;
font-size: 1em;
&:not(.success) {
opacity: 0;
}
@ -40,7 +40,7 @@
margin-left: 2px;
margin-right: 8px;
float: left;
font-size: 1.571em;
font-size: 1.857em;
}
.reply-as-new-topic {
float: left;

View File

@ -23,7 +23,7 @@
}
.tag-count {
font-size: 0.9em;
font-size: 0.857em;
}
}
@ -86,7 +86,7 @@
$tag-color: $primary-medium;
.discourse-tag-count {
font-size: 0.8em;
font-size: 0.857em;
color: $tag-color;
}
@ -153,7 +153,7 @@ $tag-color: $primary-medium;
.topic-list-item .discourse-tags {
display: block;
font-size: 0.75em;
font-size: 0.786em;
font-weight: normal;
clear: both;
margin-top: 5px;
@ -170,7 +170,7 @@ $tag-color: $primary-medium;
.mobile-view .topic-list-item .discourse-tags {
display: inline-block;
font-size: 0.9em;
font-size: 0.857em;
margin-top: 0;
.discourse-tag.box {
position:relative;
@ -185,7 +185,7 @@ $tag-color: $primary-medium;
font-family: FontAwesome;
color: $primary-low-mid;
margin-right: 5px;
font-size: 0.7em;
font-size: 0.786em;
position:relative;
top: -0.1em;
}

View File

@ -67,7 +67,7 @@
// add staff color
.moderator {
.cooked {
.regular > .cooked {
background-color: dark-light-choose($highlight-low, $highlight-medium);
padding: 10px;
img:not(.thumbnail) {
@ -75,7 +75,7 @@
height: auto;
}
}
.names {
.clearfix > .topic-meta-data > .names {
span.user-title {
background-color: dark-light-choose($highlight-low, $highlight-medium);
color: dark-light-choose($primary-high, $secondary-low);
@ -371,14 +371,14 @@ blockquote > *:last-child {
margin-top: 6px;
text-transform: uppercase;
font-weight: bold;
font-size: 0.9em;
font-size: 0.857em;
color: dark-light-choose($primary-low-mid, $secondary-high);
.custom-message {
text-transform: none;
margin: 15px 0px 5px;
font-weight: normal;
font-size: 1.11em;
font-size: 1.143em;
p {
margin: 5px 0;
}
@ -464,7 +464,7 @@ a.mention, a.mention-group {
> span.help {
display: inline-block;
color: dark-light-choose($primary-medium, $secondary-medium);
font-size: 0.929em;
font-size: 0.857em;
font-style: italic;
line-height: $base-line-height;
margin-bottom: 1px;

View File

@ -30,6 +30,14 @@
&.badge-type-bronze .fa {
color: #cd7f32 !important;
}
&.disabled {
color: $primary-medium;
background-color: $primary-very-low;
.fa {
opacity: 0.4;
}
}
}
@media all and (max-width: 750px) {
@ -60,7 +68,7 @@
.show-badge .badge-user-info {
.earned {
font-size: 1.3em;
font-size: 1.286em;
margin-bottom: 1em;
}
}
@ -71,7 +79,7 @@
.load-more {
padding-top: 30px;
display: block;
font-size: 1.2em;
font-size: 1.286em;
}
}
@ -86,11 +94,11 @@
}
.date {
display: inline-block;
font-size: 1.1em;
font-size: 1.143em;
margin-left: 10px;
}
.post-link {
font-size: 1.3em;
font-size: 1.286em;
width: 500px;
margin: 0;
padding: 0;
@ -130,7 +138,7 @@
top: 5px;
font-weight: bold;
color: $primary-medium;
font-size: 1.2em;
font-size: 1.286em;
}
.badge-contents {

View File

@ -19,7 +19,6 @@
width: 100%;
.secondary {
font-size: 0.929em;
.btn {
padding: 3px 12px;
@ -61,13 +60,13 @@
}
h1 {
font-size: 2.143em;
font-size: 2.286em;
font-weight: normal;
i {font-size: .8em;}
}
h2 {
font-size: 1.214em;
font-size: 1.286em;
font-weight: normal;
margin-top: 10px;
max-width: 100%;
@ -168,7 +167,7 @@
}
h2 {
font-size: 1.071em;
font-size: 1.143em;
margin-top: 4px;
}
@ -256,7 +255,7 @@
color: dark-light-choose($primary-medium, $secondary-medium);
margin-top: 5px;
margin-bottom: 10px;
font-size: 80%;
font-size: .857em;
line-height: 1.4em;
}
}
@ -268,7 +267,7 @@
text-align: top;
color: $danger;
font-weight: bold;
font-size: 1.3em;
font-size: 1.286em;
}
}
@ -403,7 +402,7 @@
.value {
font-weight: bold;
font-size: 1.2em;
font-size: 1.286em;
}
.label {
@ -450,7 +449,7 @@
.links-section {
.domain {
font-size: 0.714em;
font-size: 0.786em;
color: dark-light-choose($primary-medium, $secondary-high);
}
}
@ -480,7 +479,7 @@
.instructions {
color: dark-light-choose($primary-medium, $secondary-medium);
margin-bottom: 10px;
font-size: 80%;
font-size: .857em;
line-height: 1.4em;
a[href] {

View File

@ -24,9 +24,11 @@
.main-link {
@extend .topic-list-main-link;
flex: 15;
font-size: 1em;
.top-row {
margin-bottom: 0.1em;
font-size: 1.143em;
}
}
.topic-stats {

View File

@ -33,6 +33,7 @@ span.badge-posts {
}
button {
border: none;
font-size: 1.286em;
padding: 8px 10px;
vertical-align: top;
background: transparent;

View File

@ -101,11 +101,12 @@ a:hover {
========================================================================== */
/**
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
* Address styling not present in IE 8/9/10/11 & Edge
*/
abbr[title] {
border-bottom: 1px dotted;
abbr[title], acronym[title] {
text-decoration: underline;
text-decoration-style: dotted;
}
/**

View File

@ -2,6 +2,7 @@ require "backup_restore/backup_restore"
class Admin::BackupsController < Admin::AdminController
before_action :ensure_backups_enabled
skip_before_action :check_xhr, only: [:index, :show, :logs, :check_backup_chunk, :upload_backup_chunk]
def index
@ -178,4 +179,8 @@ class Admin::BackupsController < Admin::AdminController
`df -Pk #{Rails.root}/public/backups | awk 'NR==2 {print $4 * 1024;}'`.to_i > size
end
def ensure_backups_enabled
raise Discourse::InvalidAccess.new unless SiteSetting.enable_backups?
end
end

View File

@ -5,6 +5,7 @@ class StaticController < ApplicationController
skip_before_action :check_xhr, :redirect_to_login_if_required
skip_before_action :verify_authenticity_token, only: [:brotli_asset, :cdn_asset, :enter, :favicon, :service_worker_asset]
skip_before_action :preload_json, only: [:brotli_asset, :cdn_asset, :enter, :favicon, :service_worker_asset]
PAGES_WITH_EMAIL_PARAM = ['login', 'password_reset', 'signup']
@ -146,6 +147,10 @@ class StaticController < ApplicationController
def service_worker_asset
respond_to do |format|
format.js do
# we take 1 hour to give a new service worker to all users
immutable_for 1.hour
render(
plain: Rails.application.assets_manifest.find_sources('service-worker.js').first,
content_type: 'application/javascript'

View File

@ -26,17 +26,22 @@ class UploadsController < ApplicationController
# note, atm hijack is processed in its own context and has not access to controller
# longer term we may change this
hijack do
info = UploadsController.create_upload(
current_user: me,
file: file,
url: url,
type: type,
for_private_message: for_private_message,
pasted: pasted,
is_api: is_api,
retain_hours: retain_hours
)
render json: UploadsController.serialize_upload(info), status: Upload === info ? 200 : 422
begin
info = UploadsController.create_upload(
current_user: me,
file: file,
url: url,
type: type,
for_private_message: for_private_message,
pasted: pasted,
is_api: is_api,
retain_hours: retain_hours
)
rescue => e
render json: failed_json.merge(message: e.message&.split("\n")&.first), status: 422
else
render json: UploadsController.serialize_upload(info), status: Upload === info ? 200 : 422
end
end
end

View File

@ -248,7 +248,12 @@ class UsersController < ApplicationController
Group.mentionable(current_user)
.where(name: usernames)
.pluck(:name, :user_count)
.map { |name, user_count| { name: name, user_count: user_count } }
.map do |name, user_count|
{
name: name,
user_count: user_count
}
end
end
usernames -= groups
@ -267,7 +272,13 @@ class UsersController < ApplicationController
.where(username_lower: usernames)
.pluck(:username_lower)
render json: { valid: result, valid_groups: groups, mentionable_groups: mentionable_groups, cannot_see: cannot_see }
render json: {
valid: result,
valid_groups: groups,
mentionable_groups: mentionable_groups,
cannot_see: cannot_see,
max_users_notified_per_group_mention: SiteSetting.max_users_notified_per_group_mention
}
end
def render_available_true

View File

@ -38,7 +38,7 @@ module Jobs
end
end
if !post.user.staff? && !post.user.staged
if !post.user&.staff? && !post.user&.staged?
s = post.cooked
s << " #{post.topic.title}" if post.post_number == 1
if !args[:bypass_bump] && WordWatcher.new(s).should_flag?

Some files were not shown because too many files have changed in this diff Show More