Version bump

This commit is contained in:
David Taylor 2018-11-05 11:18:34 +00:00
commit a4c03a6496
477 changed files with 7869 additions and 3787 deletions

View File

@ -34,7 +34,7 @@ gem 'redis-namespace'
gem 'active_model_serializers', '~> 0.8.3'
gem 'onebox', '1.8.63'
gem 'onebox', '1.8.65'
gem 'http_accept_language', '~>2.0.5', require: false
@ -187,7 +187,7 @@ if ENV["IMPORT"] == "1"
gem 'mysql2'
gem 'redcarpet'
gem 'sqlite3', '~> 1.3.13'
gem 'ruby-bbcode-to-md', github: 'nlalonde/ruby-bbcode-to-md'
gem 'ruby-bbcode-to-md', git: 'https://github.com/nlalonde/ruby-bbcode-to-md'
gem 'reverse_markdown'
gem 'tiny_tds'
end

View File

@ -193,7 +193,7 @@ GEM
mini_mime (>= 0.1.1)
maxminddb (0.1.21)
memory_profiler (0.9.12)
message_bus (2.1.5)
message_bus (2.1.6)
rack (>= 1.1.3)
metaclass (0.0.4)
method_source (0.8.2)
@ -258,7 +258,7 @@ GEM
omniauth-twitter (1.4.0)
omniauth-oauth (~> 1.1)
rack
onebox (1.8.63)
onebox (1.8.65)
htmlentities (~> 4.3)
moneta (~> 1.0)
multi_json (~> 1.11)
@ -512,7 +512,7 @@ DEPENDENCIES
omniauth-oauth2
omniauth-openid
omniauth-twitter
onebox (= 1.8.63)
onebox (= 1.8.65)
openid-redis-store
pg
pry-nav

View File

@ -0,0 +1,24 @@
(function() {
setTimeout(function() {
const $activateButton = $("#activate-account-button");
$activateButton.on("click", function() {
$activateButton.prop("disabled", true);
const hpPath = document.getElementById("data-activate-account").dataset
.path;
$.ajax(hpPath)
.then(function(hp) {
$("#password_confirmation").val(hp.value);
$("#challenge").val(
hp.challenge
.split("")
.reverse()
.join("")
);
$("#activate-account-form").submit();
})
.fail(function() {
$activateButton.prop("disabled", false);
});
});
}, 50);
})();

View File

@ -89,7 +89,12 @@ export default Ember.Component.extend({
yAxes: [
{
display: true,
ticks: { callback: label => number(label) }
ticks: {
userCallback: label => {
if (Math.floor(label) === label) return label;
},
callback: label => number(label)
}
}
],
xAxes: [

View File

@ -140,7 +140,7 @@ export default Ember.Component.extend({
const modes = forcedModes ? forcedModes.split(",") : reportModes;
return Ember.makeArray(modes).map(mode => {
const base = `mode-btn ${mode}`;
const base = `btn-default mode-btn ${mode}`;
const cssClass = currentMode === mode ? `${base} is-current` : base;
return {

View File

@ -5,16 +5,6 @@ import copyText from "discourse/lib/copy-text";
export default Ember.Component.extend({
classNames: ["ip-lookup"],
city: function() {
return [
this.get("location.city"),
this.get("location.region"),
this.get("location.country")
]
.filter(Boolean)
.join(", ");
}.property("location.{city,region,country}"),
otherAccountsToDelete: function() {
// can only delete up to 50 accounts at a time
var total = Math.min(50, this.get("totalOthersWithSameIP") || 0);
@ -72,24 +62,19 @@ export default Ember.Component.extend({
}
text += I18n.t("ip_lookup.location");
if (location.loc) {
text += `: ${location.loc} ${this.get("city")}\n`;
if (location.location) {
text += `: ${location.location}\n`;
} else {
text += `: ${I18n.t("ip_lookup.location_not_found")}\n`;
}
if (location.org) {
if (location.organization) {
text += I18n.t("ip_lookup.organisation");
text += `: ${location.org}\n`;
}
if (location.phone) {
text += I18n.t("ip_lookup.phone");
text += `: ${location.phone}\n`;
text += `: ${location.organization}\n`;
}
}
const copyRange = $('<p id="copy-range"></p>');
copyRange.html(text.trim().replace("\n", "<br>"));
copyRange.html(text.trim().replace(/\n/g, "<br>"));
$(document.body).append(copyRange);
if (copyText(text, copyRange[0])) {
this.set("copied", true);

View File

@ -0,0 +1,102 @@
import { on } from "ember-addons/ember-computed-decorators";
export default Ember.Component.extend({
classNameBindings: [":value-list", ":secret-value-list"],
inputDelimiter: null,
collection: null,
values: null,
validationMessage: null,
@on("didReceiveAttrs")
_setupCollection() {
const values = this.get("values");
this.set(
"collection",
this._splitValues(values, this.get("inputDelimiter") || "\n")
);
},
actions: {
changeKey(index, newValue) {
if (this._checkInvalidInput(newValue)) return;
this._replaceValue(index, newValue, "key");
},
changeSecret(index, newValue) {
if (this._checkInvalidInput(newValue)) return;
this._replaceValue(index, newValue, "secret");
},
addValue() {
if (this._checkInvalidInput([this.get("newKey"), this.get("newSecret")]))
return;
this._addValue(this.get("newKey"), this.get("newSecret"));
this.setProperties({ newKey: "", newSecret: "" });
},
removeValue(value) {
this._removeValue(value);
}
},
_checkInvalidInput(inputs) {
this.set("validationMessage", null);
for (let input of inputs) {
if (Ember.isEmpty(input) || input.includes("|")) {
this.set(
"validationMessage",
I18n.t("admin.site_settings.secret_list.invalid_input")
);
return true;
}
}
},
_addValue(value, secret) {
this.get("collection").addObject({ key: value, secret: secret });
this._saveValues();
},
_removeValue(value) {
const collection = this.get("collection");
collection.removeObject(value);
this._saveValues();
},
_replaceValue(index, newValue, keyName) {
let item = this.get("collection")[index];
Ember.set(item, keyName, newValue);
this._saveValues();
},
_saveValues() {
this.set(
"values",
this.get("collection")
.map(function(elem) {
return `${elem.key}|${elem.secret}`;
})
.join("\n")
);
},
_splitValues(values, delimiter) {
if (values && values.length) {
const keys = ["key", "secret"];
var res = [];
values.split(delimiter).forEach(function(str) {
var object = {};
str.split("|").forEach(function(a, i) {
object[keys[i]] = a;
});
res.push(object);
});
return res;
} else {
return [];
}
}
});

View File

@ -0,0 +1,19 @@
import UploadMixin from "discourse/mixins/upload";
export default Em.Component.extend(UploadMixin, {
type: "csv",
uploadUrl: "/tags/upload",
addDisabled: Em.computed.alias("uploading"),
elementId: "tag-uploader",
validateUploadedFilesOptions() {
return { csvOnly: true };
},
uploadDone() {
bootbox.alert(I18n.t("tagging.upload_successful"), () => {
this.sendAction("refresh");
this.sendAction("closeModal");
});
}
});

View File

@ -0,0 +1,53 @@
import computed from "ember-addons/ember-computed-decorators";
export default Ember.Component.extend({
tagName: "",
@computed("percentage")
showPercentage(percentage) {
return percentage.total >= 3;
},
// We do a little logic to choose which icon to display and which text
@computed("user.flags_agreed", "user.flags_disagreed", "user.flags_ignored")
percentage(agreed, disagreed, ignored) {
let total = agreed + disagreed + ignored;
let result = { total };
if (total > 0) {
result.agreed = Math.round((agreed / total) * 100);
result.disagreed = Math.round((disagreed / total) * 100);
result.ignored = Math.round((ignored / total) * 100);
}
let highest = Math.max(agreed, disagreed, ignored);
if (highest === agreed) {
result.icon = "thumbs-up";
result.className = "agreed";
result.label = `${result.agreed}%`;
} else if (highest === disagreed) {
result.icon = "thumbs-down";
result.className = "disagreed";
result.label = `${result.disagreed}%`;
} else {
result.icon = "external-link";
result.className = "ignored";
result.label = `${result.ignored}%`;
}
result.title = I18n.t("admin.flags.user_percentage.summary", {
agreed: I18n.t("admin.flags.user_percentage.agreed", {
count: result.agreed
}),
disagreed: I18n.t("admin.flags.user_percentage.disagreed", {
count: result.disagreed
}),
ignored: I18n.t("admin.flags.user_percentage.ignored", {
count: result.ignored
}),
count: total
});
return result;
}
});

View File

@ -1,9 +1,15 @@
import { ajax } from "discourse/lib/ajax";
import computed from "ember-addons/ember-computed-decorators";
export default Ember.Controller.extend({
adminBackups: Ember.inject.controller(),
status: Ember.computed.alias("adminBackups.model"),
@computed
localBackupStorage() {
return this.siteSettings.backup_location === "local";
},
uploadLabel: function() {
return I18n.t("admin.backups.upload.label");
}.property(),

View File

@ -12,6 +12,16 @@ export default Ember.Controller.extend(PeriodComputationMixin, {
};
},
@computed
mostDisagreedFlaggersOptions() {
return {
table: {
total: false,
perPage: 10
}
};
},
@computed("startDate", "endDate")
filters(startDate, endDate) {
return { startDate, endDate };

View File

@ -1,7 +1,10 @@
import ModalFunctionality from "discourse/mixins/modal-functionality";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { observes } from "ember-addons/ember-computed-decorators";
import {
default as computed,
observes
} from "ember-addons/ember-computed-decorators";
export default Ember.Controller.extend(ModalFunctionality, {
local: Ember.computed.equal("selection", "local"),
@ -11,8 +14,14 @@ export default Ember.Controller.extend(ModalFunctionality, {
loading: false,
keyGenUrl: "/admin/themes/generate_key_pair",
importUrl: "/admin/themes/import",
checkPrivate: Ember.computed.match("uploadUrl", /^git/),
localFile: null,
uploadUrl: null,
@computed("loading", "remote", "uploadUrl", "local", "localFile")
importDisabled(isLoading, isRemote, uploadUrl, isLocal, localFile) {
return isLoading || (isRemote && !uploadUrl) || (isLocal && !localFile);
},
@observes("privateChecked")
privateWasChecked() {
@ -32,6 +41,10 @@ export default Ember.Controller.extend(ModalFunctionality, {
},
actions: {
uploadLocaleFile() {
this.set("localFile", $("#file-input")[0].files[0]);
},
importTheme() {
let options = {
type: "POST"
@ -41,7 +54,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
options.processData = false;
options.contentType = false;
options.data = new FormData();
options.data.append("theme", $("#file-input")[0].files[0]);
options.data.append("theme", this.get("localFile"));
} else {
options.data = {
remote: this.get("uploadUrl"),

View File

@ -11,7 +11,8 @@ const CUSTOM_TYPES = [
"value_list",
"category",
"uploaded_image_list",
"compact_list"
"compact_list",
"secret_list"
];
export default Ember.Mixin.create({

View File

@ -1,5 +1,4 @@
import { ajax } from "discourse/lib/ajax";
import PreloadStore from "preload-store";
const Backup = Discourse.Model.extend({
destroy() {
@ -16,9 +15,9 @@ const Backup = Discourse.Model.extend({
Backup.reopenClass({
find() {
return PreloadStore.getAndRemove("backups", () =>
ajax("/admin/backups.json")
).then(backups => backups.map(backup => Backup.create(backup)));
return ajax("/admin/backups.json").then(backups =>
backups.map(backup => Backup.create(backup))
);
},
start(withUploads) {

View File

@ -304,7 +304,7 @@ const Report = Discourse.Model.extend({
avatar_template: row[properties.avatar]
});
const href = `/admin/users/${userId}/${username}`;
const href = Discourse.getURL(`/admin/users/${userId}/${username}`);
const avatarImg = renderAvatar(user, {
imageSize: "tiny",
@ -327,7 +327,7 @@ const Report = Discourse.Model.extend({
const formatedValue = () => {
const topicId = row[properties.id];
const href = `/t/-/${topicId}`;
const href = Discourse.getURL(`/t/-/${topicId}`);
return `<a href='${href}'>${topicTitle}</a>`;
};
@ -341,7 +341,7 @@ const Report = Discourse.Model.extend({
const postTitle = row[properties.truncated_raw];
const postNumber = row[properties.number];
const topicId = row[properties.topic_id];
const href = `/t/-/${topicId}/${postNumber}`;
const href = Discourse.getURL(`/t/-/${topicId}/${postNumber}`);
return {
property: properties.title,
@ -395,7 +395,7 @@ const Report = Discourse.Model.extend({
_linkLabel(properties, row) {
const property = properties[0];
const value = row[property];
const value = Discourse.getURL(row[property]);
const formatedValue = (href, anchor) => {
return `<a href="${escapeExpression(href)}">${escapeExpression(
anchor

View File

@ -151,6 +151,15 @@ export default Discourse.Route.extend({
message: message
})
);
},
remoteUploadSuccess() {
Backup.find().then(backups => {
this.controllerFor("adminBackupsIndex").set(
"model",
backups.map(backup => Backup.create(backup))
);
});
}
}
});

View File

@ -19,8 +19,8 @@
{{/if}}
</td>
<td class="key-controls">
{{d-button action="regenerateKey" actionParam=k icon="undo" label='admin.api.regenerate'}}
{{d-button action="revokeKey" actionParam=k icon="times" label='admin.api.revoke'}}
{{d-button class="btn-default" action="regenerateKey" actionParam=k icon="undo" label='admin.api.regenerate'}}
{{d-button class="btn-default" action="revokeKey" actionParam=k icon="times" label='admin.api.revoke'}}
</td>
</tr>
{{/each}}

View File

@ -1,16 +1,21 @@
<div class="backup-options">
{{resumable-upload target="/admin/backups/upload" success="uploadSuccess" error="uploadError" uploadText=uploadLabel title="admin.backups.upload.title"}}
{{#if site.isReadOnly}}
{{d-button icon="eye" action="toggleReadOnlyMode" disabled=status.isOperationRunning title="admin.backups.read_only.disable.title" label="admin.backups.read_only.disable.label"}}
{{#if localBackupStorage}}
{{resumable-upload target="/admin/backups/upload" success="uploadSuccess" error="uploadError" uploadText=uploadLabel title="admin.backups.upload.title" class="btn-default"}}
{{else}}
{{d-button icon="eye" action="toggleReadOnlyMode" disabled=status.isOperationRunning title="admin.backups.read_only.enable.title" label="admin.backups.read_only.enable.label"}}
{{backup-uploader done="remoteUploadSuccess"}}
{{/if}}
{{#if site.isReadOnly}}
{{d-button class="btn-default" icon="eye" action="toggleReadOnlyMode" disabled=status.isOperationRunning title="admin.backups.read_only.disable.title" label="admin.backups.read_only.disable.label"}}
{{else}}
{{d-button class="btn-default" icon="eye" action="toggleReadOnlyMode" disabled=status.isOperationRunning title="admin.backups.read_only.enable.title" label="admin.backups.read_only.enable.label"}}
{{/if}}
</div>
<table class="grid">
<thead>
<th width="55%">{{i18n 'admin.backups.columns.filename'}}</th>
<th width="10%">{{i18n 'admin.backups.columns.size'}}</th>
<th></th>
<th></th>
</thead>
<tbody>
{{#each model as |backup|}}
@ -19,7 +24,7 @@
<td class="backup-size">{{human-size backup.size}}</td>
<td class="backup-controls">
<div>
{{d-button class="download"
{{d-button class="btn-default download"
action="download"
actionParam=backup
icon="download"
@ -27,10 +32,10 @@
label="admin.backups.operations.download.label"}}
{{#if status.isOperationRunning}}
{{d-button icon="trash-o" action="destroyBackup" actionParam=backup class="btn-danger" disabled="true" title="admin.backups.operations.is_running"}}
{{d-button icon="play" action="startRestore" actionParam=backup disabled=status.restoreDisabled title=restoreTitle label="admin.backups.operations.restore.label"}}
{{d-button icon="play" action="startRestore" actionParam=backup disabled=status.restoreDisabled class="btn-default" title=restoreTitle label="admin.backups.operations.restore.label"}}
{{else}}
{{d-button icon="trash-o" action="destroyBackup" actionParam=backup class="btn-danger" title="admin.backups.operations.destroy.title"}}
{{d-button icon="play" action="startRestore" actionParam=backup disabled=status.restoreDisabled title=restoreTitle label="admin.backups.operations.restore.label"}}
{{d-button icon="play" action="startRestore" actionParam=backup disabled=status.restoreDisabled class="btn-default" title=restoreTitle label="admin.backups.operations.restore.label"}}
{{/if}}
</div>
</td>

View File

@ -8,7 +8,7 @@
<div class="admin-actions">
{{#if model.canRollback}}
{{d-button action="rollback"
class="btn-rollback"
class="btn-default btn-rollback"
label="admin.backups.operations.rollback.label"
title="admin.backups.operations.rollback.title"
icon="ambulance"

View File

@ -2,7 +2,7 @@
<p>{{i18n 'admin.badges.none_selected'}}</p>
<div>
{{#link-to 'adminBadges.show' 'new' class="btn"}}
{{#link-to 'adminBadges.show' 'new' class="btn btn-default"}}
{{d-icon "plus"}} {{i18n 'admin.badges.new'}}
{{/link-to}}
</div>

View File

@ -36,7 +36,7 @@
value=buffered.badge_grouping_id
content=badgeGroupings
nameProperty="name"}}
&nbsp;<button {{action "editGroupings"}} class='btn'>{{d-icon 'pencil'}}</button>
&nbsp;<button {{action "editGroupings"}} class='btn btn-icon btn-default'>{{d-icon 'pencil'}}</button>
</div>

View File

@ -14,7 +14,7 @@
</li>
{{/each}}
</ul>
{{#link-to 'adminBadges.show' 'new' class="btn"}}
{{#link-to 'adminBadges.show' 'new' class="btn btn-default"}}
{{d-icon "plus"}} {{i18n 'admin.badges.new'}}
{{/link-to}}
<br>

View File

@ -167,7 +167,7 @@
<div class="control">
<div class="input">
{{d-button
class="export-csv-btn"
class="btn-default export-csv-btn"
action="exportCsv"
label="admin.export_csv.button_text"
icon="download"}}

View File

@ -48,8 +48,8 @@
<div class='form-element controls'>
{{d-button action="edit" class="btn-default" icon="pencil" label="admin.user_fields.edit"}}
{{d-button action="destroy" class="btn-danger" icon="trash-o" label="admin.user_fields.delete"}}
{{d-button action="moveUp" icon="arrow-up" disabled=cantMoveUp}}
{{d-button action="moveDown" icon="arrow-down" disabled=cantMoveDown}}
{{d-button action="moveUp" class="btn-default" icon="arrow-up" disabled=cantMoveUp}}
{{d-button action="moveDown" class="btn-default" icon="arrow-down" disabled=cantMoveDown}}
</div>
</div>
<div class="row">{{flags}}</div>

View File

@ -8,6 +8,7 @@
<div class='flagger-flag-type'>
{{post-action-title postAction.post_action_type_id postAction.name_key}}
</div>
{{user-flag-percentage user=postAction.user}}
{{/flag-user}}
{{/each}}
</div>

View File

@ -77,21 +77,21 @@
{{#if flaggedPost.postHidden}}
{{d-button
title="admin.flags.disagree_flag_unhide_post_title"
class="disagree-flag"
class="btn-default disagree-flag"
action="disagree"
icon="thumbs-o-down"
label="admin.flags.disagree_flag_unhide_post"}}
{{else}}
{{d-button
title="admin.flags.disagree_flag_title"
class="disagree-flag"
class="btn-default disagree-flag"
action="disagree"
icon="thumbs-o-down"
label="admin.flags.disagree_flag"}}
{{/if}}
{{d-button
class="defer-flag"
class="btn-default defer-flag"
title="admin.flags.ignore_flag_title"
action="defer"
icon="external-link"
@ -103,6 +103,7 @@
{{/if}}
{{d-button
class="btn-default"
icon="list"
label="admin.flags.moderation_history"
action=(action "showModerationHistory")}}

View File

@ -2,4 +2,4 @@
{{text-field value=url disabled=formSubmitted class="permalink-url" placeholderKey="admin.permalink.url" autocorrect="off" autocapitalize="off"}}
{{combo-box content=permalinkTypes value=permalinkType}}
{{text-field value=permalink_type_value disabled=formSubmitted class="external-url" placeholderKey=permalinkTypePlaceholder autocorrect="off" autocapitalize="off"}}
{{d-button action="submit" disabled=formSubmitted label="admin.permalink.form.add"}}
{{d-button class="btn-default" action="submit" disabled=formSubmitted label="admin.permalink.form.add"}}

View File

@ -1,4 +1,4 @@
<b>{{i18n 'admin.logs.screened_ips.form.label'}}</b>
{{text-field value=ip_address disabled=formSubmitted class="ip-address-input" placeholderKey="admin.logs.screened_ips.form.ip_address" autocorrect="off" autocapitalize="off"}}
{{combo-box content=actionNames value=actionName}}
{{d-button action="submit" disabled=formSubmitted label="admin.logs.screened_ips.form.add"}}
{{d-button class="btn-default" action="submit" disabled=formSubmitted label="admin.logs.screened_ips.form.add"}}

View File

@ -0,0 +1,24 @@
{{#if collection}}
<div class="values">
{{#each collection as |value index|}}
<div class="value" data-index={{index}}>
{{d-button action="removeValue"
actionParam=value
icon="times"
class="remove-value-btn btn-small"}}
{{input value=value.key class="value-input" focus-out=(action "changeKey" index)}}
{{input value=value.secret class="value-input" focus-out=(action "changeSecret" index) type=(if isSecret "password" "text")}}
</div>
{{/each}}
</div>
{{/if}}
<div class="value">
{{text-field value=newKey class="new-value-input key" placeholder=setting.placeholder.key}}
{{input type="password" value=newSecret class="new-value-input secret" placeholder=setting.placeholder.value}}
{{d-button action="addValue"
icon="plus"
class="add-value-btn btn-small"}}
</div>
{{setting-validation-message message=validationMessage}}

View File

@ -13,5 +13,5 @@
{{#if setting.secret}}
{{d-button action="toggleSecret" icon="eye-slash"}}
{{/if}}
{{d-button class="undo" action="resetDefault" icon="undo" label="admin.settings.reset"}}
{{d-button class="btn-default undo" action="resetDefault" icon="undo" label="admin.settings.reset"}}
{{/if}}

View File

@ -0,0 +1,3 @@
{{secret-value-list setting=setting values=value isSecret=isSecret}}
{{setting-validation-message message=validationMessage}}
<div class='desc'>{{{unbound setting.description}}}</div>

View File

@ -1,4 +1,4 @@
{{d-button label="admin.site_text.edit" class='edit' action="edit"}}
{{d-button label="admin.site_text.edit" class='btn-default edit' action="edit"}}
<h3 class='site-text-id'>{{siteText.id}}</h3>
<div class='site-text-value'>{{siteText.value}}</div>

View File

@ -0,0 +1,6 @@
<label class="btn {{if addDisabled 'disabled'}}">
{{d-icon "upload"}}
{{i18n 'admin.watched_words.form.upload'}}
<input class="hidden-upload-field" disabled={{addDisabled}} type="file" accept="text/plain,text/csv" />
</label>
<span class="instructions">{{i18n 'tagging.upload_instructions'}}</span>

View File

@ -0,0 +1,6 @@
{{#if showPercentage}}
<div class='user-flag-percentage' title={{percentage.title}}>
<span class="percentage-label {{percentage.className}}">{{percentage.label}}</span>
{{d-icon percentage.icon}}
</div>
{{/if}}

View File

@ -5,7 +5,7 @@
{{d-button action="removeValue"
actionParam=value
icon="times"
class="remove-value-btn btn-small"}}
class="btn-default remove-value-btn btn-small"}}
{{input title=value value=value class="value-input" focus-out=(action "changeValue" index)}}
</div>

View File

@ -1,6 +1,6 @@
<b>{{i18n 'admin.watched_words.form.label'}}</b>
{{text-field value=word disabled=formSubmitted class="watched-word-input" autocorrect="off" autocapitalize="off" placeholderKey=placeholderKey}}
{{d-button action="submit" disabled=formSubmitted label="admin.watched_words.form.add"}}
{{d-button class="btn-default" action="submit" disabled=formSubmitted label="admin.watched_words.form.add"}}
{{#if showMessage}}
<span class="success-message">{{message}}</span>

View File

@ -1,7 +1,7 @@
<label class="btn {{if addDisabled 'disabled'}}">
<label class="btn btn-default {{if addDisabled 'disabled'}}">
{{d-icon "upload"}}
{{i18n 'admin.watched_words.form.upload'}}
<input disabled={{addDisabled}} type="file" accept="text/plain,text/csv" style="visibility: hidden; position: absolute;" />
<input class="hidden-upload-field" disabled={{addDisabled}} type="file" accept="text/plain,text/csv" />
</label>
<br/>
<span class="instructions">One word per line</span>

View File

@ -3,10 +3,10 @@
<h1>{{#if model.theme_id}}{{model.name}}{{else}}{{text-field class="style-name" value=model.name}}{{/if}}</h1>
<div class="controls">
{{#unless model.theme_id}}
<button {{action "save"}} disabled={{model.disableSave}} class='btn'>{{i18n 'admin.customize.save'}}</button>
<button {{action "save"}} disabled={{model.disableSave}} class='btn btn-primary'>{{i18n 'admin.customize.save'}}</button>
{{/unless}}
<button {{action "copy" model}} class='btn'>{{d-icon "copy"}} {{i18n 'admin.customize.copy'}}</button>
<button {{action "copyToClipboard" model}} class='btn'>{{d-icon "clipboard"}} {{i18n 'admin.customize.copy_to_clipboard'}}</button>
<button {{action "copy" model}} class='btn btn-default'>{{d-icon "copy"}} {{i18n 'admin.customize.copy'}}</button>
<button {{action "copyToClipboard" model}} class='btn btn-default'>{{d-icon "clipboard"}} {{i18n 'admin.customize.copy_to_clipboard'}}</button>
{{#if model.theme_id}}
{{i18n "admin.customize.theme_owner"}}
{{#link-to "adminCustomizeThemes.show" model.theme_id}}{{model.theme_name}}{{/link-to}}
@ -47,8 +47,8 @@
<td class="hex">{{color-input hexValue=c.hex brightnessValue=c.brightness valid=c.valid}}</td>
<td class="actions">
{{#unless model.theme_id}}
<button class="btn revert {{unless c.savedIsOverriden 'invisible'}}" {{action "revert" c}} title="{{i18n 'admin.customize.colors.revert_title'}}">{{i18n 'revert'}}</button>
<button class="btn undo {{unless c.changed 'invisible'}}" {{action "undo" c}} title="{{i18n 'admin.customize.colors.undo_title'}}">{{i18n 'undo'}}</button>
<button class="btn btn-default revert {{unless c.savedIsOverriden 'invisible'}}" {{action "revert" c}} title="{{i18n 'admin.customize.colors.revert_title'}}">{{i18n 'revert'}}</button>
<button class="btn btn-default undo {{unless c.changed 'invisible'}}" {{action "undo" c}} title="{{i18n 'admin.customize.colors.undo_title'}}">{{i18n 'undo'}}</button>
{{/unless}}
</td>
</tr>

View File

@ -9,7 +9,7 @@
{{/unless}}
{{/each}}
</ul>
<button {{action "newColorScheme"}} class='btn'>{{d-icon 'plus'}}{{i18n 'admin.customize.new'}}</button>
<button {{action "newColorScheme"}} class='btn btn-default'>{{d-icon 'plus'}}{{i18n 'admin.customize.new'}}</button>
</div>
{{outlet}}

View File

@ -50,10 +50,10 @@
icon="paint-brush"}}
{{#if colorSchemeChanged}}
{{d-button action="changeScheme" class="btn-primary btn-small submit-edit" icon="check"}}
{{d-button action="cancelChangeScheme" class="btn-small cancel-edit" icon="times"}}
{{d-button action="cancelChangeScheme" class="btn-default btn-small cancel-edit" icon="times"}}
{{/if}}
</div>
{{#link-to 'adminCustomize.colors' class="btn edit"}}{{i18n 'admin.customize.colors.edit'}}{{/link-to}}
{{#link-to 'adminCustomize.colors' class="btn btn-default edit"}}{{i18n 'admin.customize.colors.edit'}}{{/link-to}}
</div>
{{/unless}}
@ -76,11 +76,11 @@
{{#if model.remote_theme.commits_behind}}
{{#d-button action="updateToLatest" icon="download" class='btn-primary'}}{{i18n "admin.customize.theme.update_to_latest"}}{{/d-button}}
{{else}}
{{#d-button action="checkForThemeUpdates" icon="refresh"}}{{i18n "admin.customize.theme.check_for_updates"}}{{/d-button}}
{{#d-button action="checkForThemeUpdates" icon="refresh" class="btn-default"}}{{i18n "admin.customize.theme.check_for_updates"}}{{/d-button}}
{{/if}}
{{/if}}
{{#d-button action="editTheme" class="btn edit"}}{{i18n 'admin.customize.theme.edit_css_html'}}{{/d-button}}
{{#d-button action="editTheme" class="btn btn-default edit"}}{{i18n 'admin.customize.theme.edit_css_html'}}{{/d-button}}
{{#if model.remote_theme}}
<span class='status-message'>
{{#if updatingRemote}}
@ -119,7 +119,7 @@
<li>
<span class='col'>${{upload.name}}: <a href={{upload.url}} target='_blank'>{{upload.filename}}</a></span>
<span class='col'>
{{d-button action="removeUpload" actionParam=upload class="second btn-small cancel-edit" icon="times"}}
{{d-button action="removeUpload" actionParam=upload class="second btn-default btn-small cancel-edit" icon="times"}}
</span>
</li>
{{/each}}
@ -127,7 +127,7 @@
{{else}}
<div class="description">{{i18n "admin.customize.theme.no_uploads"}}</div>
{{/if}}
{{#d-button action="addUploadModal" icon="plus"}}{{i18n "admin.customize.theme.add"}}{{/d-button}}
{{#d-button action="addUploadModal" class="btn-default" icon="plus"}}{{i18n "admin.customize.theme.add"}}{{/d-button}}
</div>
{{#if hasSettings}}
@ -147,22 +147,22 @@
{{#if model.childThemes.length}}
<ul class='removable-list'>
{{#each model.childThemes as |child|}}
<li>{{#link-to 'adminCustomizeThemes.show' child replace=true class='col'}}{{child.name}}{{/link-to}} {{d-button action="removeChildTheme" actionParam=child class="btn-small cancel-edit col" icon="times"}}</li>
<li>{{#link-to 'adminCustomizeThemes.show' child replace=true class='col'}}{{child.name}}{{/link-to}} {{d-button action="removeChildTheme" actionParam=child class="btn-default btn-small cancel-edit col" icon="times"}}</li>
{{/each}}
</ul>
{{/if}}
{{#if selectableChildThemes}}
<div class="description">
{{combo-box forceEscape=true filterable=true content=selectableChildThemes value=selectedChildThemeId none="admin.customize.theme.select_component"}}
{{#d-button action="addChildTheme" icon="plus" disabled=addButtonDisabled class="add-component-button"}}{{i18n "admin.customize.theme.add"}}{{/d-button}}
{{#d-button action="addChildTheme" icon="plus" disabled=addButtonDisabled class="btn-default add-component-button"}}{{i18n "admin.customize.theme.add"}}{{/d-button}}
</div>
{{/if}}
</div>
{{/if}}
<a href='{{previewUrl}}' title="{{i18n 'admin.customize.explain_preview'}}" target='_blank' class='btn'>{{d-icon 'desktop'}}{{i18n 'admin.customize.theme.preview'}}</a>
<a class="btn export" target="_blank" href={{downloadUrl}}>{{d-icon "download"}} {{i18n 'admin.export_json.button_text'}}</a>
<a href='{{previewUrl}}' title="{{i18n 'admin.customize.explain_preview'}}" target='_blank' class='btn btn-default'>{{d-icon 'desktop'}}{{i18n 'admin.customize.theme.preview'}}</a>
<a class="btn btn-default export" target="_blank" href={{downloadUrl}}>{{d-icon "download"}} {{i18n 'admin.export_json.button_text'}}</a>
{{d-button action="switchType" label="admin.customize.theme.convert" icon=convertIcon class="btn-normal" title=convertTooltip}}
{{d-button action="switchType" label="admin.customize.theme.convert" icon=convertIcon class="btn-default btn-normal" title=convertTooltip}}
{{d-button action="destroy" label="admin.customize.delete" icon="trash" class="btn-danger"}}
</div>

View File

@ -4,7 +4,7 @@
<div class="create-actions">
{{d-button label="admin.customize.new" icon="plus" action="showCreateModal" class="btn-primary"}}
{{d-button action="importModal" icon="upload" label="admin.customize.import"}}
{{d-button action="importModal" icon="upload" label="admin.customize.import" class="btn-default"}}
</div>
</div>
{{themes-list themes=fullThemes components=childThemes currentTab=currentTab}}

View File

@ -19,7 +19,7 @@
<p class="actions">
<small>{{i18n 'admin.dashboard.last_checked'}}: {{problemsTimestamp}}</small>
{{d-button action="refreshProblems" class="btn-small" icon="refresh" label="admin.dashboard.refresh_problems"}}
{{d-button action="refreshProblems" class="btn-default btn-small" icon="refresh" label="admin.dashboard.refresh_problems"}}
</p>
{{/conditional-loading-section}}
</div>

View File

@ -137,7 +137,7 @@
<div>
<h4>{{i18n "admin.dashboard.last_updated"}} </h4>
<p>{{updatedTimestamp}}</p>
<a rel="noopener" target="_blank" href="https://meta.discourse.org/tags/release-notes" class="btn">
<a rel="noopener" target="_blank" href="https://meta.discourse.org/tags/release-notes" class="btn btn-default">
{{i18n "admin.dashboard.whats_new_in_discourse"}}
</a>
</div>

View File

@ -33,6 +33,11 @@
dataSourceName="post_edits"
filters=lastWeekfilters}}
{{admin-report
dataSourceName="most_disagreed_flaggers"
filters=lastWeekfilters
reportOptions=mostDisagreedFlaggersOptions}}
{{plugin-outlet name="admin-dashboard-moderation-bottom"}}
</div>
</div>

View File

@ -29,7 +29,7 @@
{{else}}
<label>{{i18n 'admin.email.send_digest_label'}}</label>
{{text-field value=email placeholderKey="admin.email.test_email_address"}}
<button class='btn' {{action "sendEmail"}} disabled={{sendEmailDisabled}}>{{i18n 'admin.email.send_digest'}}</button>
<button class='btn btn-default' {{action "sendEmail"}} disabled={{sendEmailDisabled}}>{{i18n 'admin.email.send_digest'}}</button>
{{#if sentEmail}}
<span class='result-message'>{{i18n 'admin.email.sent_test'}}</span>
{{/if}}

View File

@ -1,7 +1,7 @@
<p>
{{i18n 'admin.logs.screened_emails.description'}}
</p>
<button class="btn screened-email-export" {{action "exportScreenedEmailList"}} title="{{i18n 'admin.export_csv.button_title.screened_email'}}">{{d-icon "download"}}{{i18n 'admin.export_csv.button_text'}}</button>
<button class="btn btn-default screened-email-export" {{action "exportScreenedEmailList"}} title="{{i18n 'admin.export_csv.button_title.screened_email'}}">{{d-icon "download"}}{{i18n 'admin.export_csv.button_text'}}</button>
<br>

View File

@ -3,8 +3,8 @@
<div class="screened-ip-controls">
<div class="filter-screened-ip-address">
{{text-field value=filter class="ip-address-input" placeholderKey="admin.logs.screened_ips.form.filter" autocorrect="off" autocapitalize="off"}}
{{d-button action="rollUp" title="admin.logs.screened_ips.roll_up.title" label="admin.logs.screened_ips.roll_up.text"}}
{{d-button action="exportScreenedIpList" icon="download" title="admin.export_csv.button_title.screened_ip" label="admin.export_csv.button_text"}}
{{d-button class="btn-default" action="rollUp" title="admin.logs.screened_ips.roll_up.title" label="admin.logs.screened_ips.roll_up.text"}}
{{d-button class="btn-default" action="exportScreenedIpList" icon="download" title="admin.export_csv.button_title.screened_ip" label="admin.export_csv.button_text"}}
</div>
{{screened-ip-address-form action="recordAdded"}}
</div>
@ -57,15 +57,15 @@
</td>
<td class="col actions">
{{#unless item.editing}}
{{d-button action="destroy" actionParam=item icon="trash-o" class="btn-danger"}}
{{d-button action="edit" actionParam=item icon="pencil"}}
{{d-button class="btn-default" action="destroy" actionParam=item icon="trash-o" class="btn-danger"}}
{{d-button class="btn-default"action="edit" actionParam=item icon="pencil"}}
{{#if item.isBlocked}}
{{d-button action="allow" actionParam=item icon="check" label="admin.logs.screened_ips.actions.do_nothing"}}
{{d-button class="btn-default" action="allow" actionParam=item icon="check" label="admin.logs.screened_ips.actions.do_nothing"}}
{{else}}
{{d-button action="block" actionParam=item icon="ban" label="admin.logs.screened_ips.actions.block"}}
{{d-button class="btn-default" action="block" actionParam=item icon="ban" label="admin.logs.screened_ips.actions.block"}}
{{/if}}
{{else}}
{{d-button action="save" actionParam=item label="admin.logs.save"}}
{{d-button class="btn-default" action="save" actionParam=item label="admin.logs.save"}}
<a {{action "cancel" item}}>{{i18n 'cancel'}}</a>
{{/unless}}
</td>

View File

@ -1,7 +1,7 @@
<p>
{{i18n 'admin.logs.screened_urls.description'}}
</p>
<button class="btn" {{action "exportScreenedUrlList"}} title="{{i18n 'admin.export_csv.button_title.screened_url'}}">{{d-icon "download"}}{{i18n 'admin.export_csv.button_text'}}</button>
<button class="btn btn-default" {{action "exportScreenedUrlList"}} title="{{i18n 'admin.export_csv.button_title.screened_url'}}">{{d-icon "download"}}{{i18n 'admin.export_csv.button_text'}}</button>
<br>
{{#conditional-loading-spinner condition=loading}}

View File

@ -33,7 +33,7 @@
{{i18n "admin.logs.staff_actions.filter"}} {{combo-box content=userHistoryActions value=filterActionId none="admin.logs.staff_actions.all"}}
{{/if}}
{{d-button action="exportStaffActionLogs" label="admin.export_csv.button_text" icon="download"}}
{{d-button class="btn-default" action="exportStaffActionLogs" label="admin.export_csv.button_text" icon="download"}}
</div>
<div class="clearfix"></div>

View File

@ -4,7 +4,7 @@
<label class="radio" for="local">{{i18n 'upload_selector.from_my_computer'}}</label>
{{#if local}}
<div class="inputs">
<input type="file" id="file-input" accept='.dcstyle.json,application/json'><br>
<input onchange={{action "uploadLocaleFile"}} type="file" id="file-input" accept='.dcstyle.json,application/json'><br>
<span class="description">{{i18n 'admin.customize.theme.import_file_tip'}}</span>
</div>
{{/if}}
@ -44,6 +44,6 @@
{{/d-modal-body}}
<div class="modal-footer">
{{d-button action="importTheme" disabled=loading class='btn btn-primary' icon='upload' label='admin.customize.import'}}
{{d-button action="importTheme" disabled=importDisabled class='btn btn-primary' icon='upload' label='admin.customize.import'}}
{{d-modal-cancel close=(action "closeModal")}}
</div>

View File

@ -7,6 +7,7 @@
class="silence-until"
label="admin.user.silence_duration"
includeFarFuture=true
clearable=false
input=silenceUntil}}
</label>
</div>

View File

@ -8,6 +8,7 @@
class="suspend-until"
label="admin.user.suspend_duration"
includeFarFuture=true
clearable=false
input=suspendUntil}}
</label>
</div>

View File

@ -1,5 +1,7 @@
<div class="permalink-title"><h2>{{i18n 'admin.permalink.title'}}</h2></div>
<div class="pull-right">
<div class="permalink-title">
<h2>{{i18n 'admin.permalink.title'}}</h2>
</div>
<div class="permalink-search">
{{text-field value=filter class="url-input" placeholderKey="admin.permalink.form.filter" autocorrect="off" autocapitalize="off"}}
</div>
{{permalink-form action="recordAdded"}}

View File

@ -49,7 +49,7 @@
<td class="settings">
{{#if currentUser.admin}}
{{#if plugin.enabled_setting}}
{{d-button action="showSettings" actionParam=plugin icon="gear" label="admin.plugins.change_settings_short"}}
{{d-button class="btn-default" action="showSettings" actionParam=plugin icon="gear" label="admin.plugins.change_settings_short"}}
{{/if}}
{{/if}}
</td>

View File

@ -6,7 +6,7 @@
{{#if currentUser.admin}}
{{d-button label="admin.plugins.change_settings"
icon="gear"
class='settings-button'
class="btn-default settings-button"
action="showSettings"}}
{{/if}}
</div>

View File

@ -3,7 +3,7 @@
<div class='controls'>
{{d-button action="toggleMenu" class="menu-toggle" icon="bars"}}
{{text-field id="setting-filter" value=filter placeholderKey="type_to_filter" class="no-blur"}}
{{d-button id="clear-filter" action="clearFilter" label="admin.site_settings.clear_filter"}}
{{d-button class="btn-default" id="clear-filter" action="clearFilter" label="admin.site_settings.clear_filter"}}
</div>
<div class='search controls'>
<label>

View File

@ -18,10 +18,10 @@
<label>{{i18n 'admin.badges.badge'}}</label>
{{combo-box filterable=true value=selectedBadgeId content=grantableBadges}}
</div>
<label>
<div>
<label>{{i18n 'admin.badges.reason'}}</label>
{{input type="text" value=badgeReason}}<br><small>{{i18n 'admin.badges.reason_help'}}</small>
</label>
</div>
<button class='btn btn-primary' {{action "grantBadge"}}>{{i18n 'admin.badges.grant'}}</button>
</form>
{{/if}}

View File

@ -1,18 +1,18 @@
<section class="details {{unless model.active 'not-activated'}}">
<div class='user-controls'>
{{#if model.canViewProfile}}
{{#link-to 'user' model class="btn"}}
{{#link-to 'user' model class="btn btn-default"}}
{{d-icon "user"}}
{{i18n 'admin.user.show_public_profile'}}
{{/link-to}}
{{/if}}
{{#if model.can_view_action_logs}}
{{d-button action="viewActionLogs" actionParam=model.username icon="list-alt" label="admin.user.action_logs"}}
{{d-button action="viewActionLogs" class="btn-default" actionParam=model.username icon="list-alt" label="admin.user.action_logs"}}
{{/if}}
{{#if model.active}}
{{#if currentUser.admin}}
{{d-button action="logOut" icon="power-off" label="admin.user.log_out"}}
{{d-button class="btn-default" action="logOut" icon="power-off" label="admin.user.log_out"}}
{{/if}}
{{/if}}
{{plugin-outlet name="admin-user-controls-after" args=(hash model=model) tagName="" connectorTagName=""}}
@ -29,10 +29,10 @@
</div>
<div class='controls'>
{{#if editingUsername}}
{{d-button action="saveUsername" label="admin.user_fields.save"}}
{{d-button class="btn-default" action="saveUsername" label="admin.user_fields.save"}}
<a href {{action "toggleUsernameEdit"}}>{{i18n 'cancel'}}</a>
{{else}}
{{d-button action="toggleUsernameEdit" icon="pencil"}}
{{d-button class="btn-default" action="toggleUsernameEdit" icon="pencil"}}
{{/if}}
</div>
</div>
@ -48,10 +48,10 @@
</div>
<div class='controls'>
{{#if editingName}}
{{d-button action="saveName" label="admin.user_fields.save"}}
{{d-button class="btn-default" action="saveName" label="admin.user_fields.save"}}
<a href {{action "toggleNameEdit"}}>{{i18n 'cancel'}}</a>
{{else}}
{{d-button action="toggleNameEdit" icon="pencil"}}
{{d-button class="btn-default" action="toggleNameEdit" icon="pencil"}}
{{/if}}
</div>
</div>
@ -68,7 +68,7 @@
{{#if model.email}}
<a href="mailto:{{unbound model.email}}">{{model.email}}</a>
{{else}}
{{d-button action="checkEmail" actionParam=model icon="envelope-o" label="admin.users.check_email.text" title="admin.users.check_email.title"}}
{{d-button class="btn-default" action="checkEmail" actionParam=model icon="envelope-o" label="admin.users.check_email.text" title="admin.users.check_email.title"}}
{{/if}}
</div>
</div>
@ -89,6 +89,7 @@
{{/if}}
{{else}}
{{d-button action="checkEmail"
class="btn-default"
actionParam=model
icon="envelope-o"
label="admin.users.check_email.text"
@ -102,7 +103,7 @@
<div class='value'>{{model.bounceScore}}</div>
<div class='controls'>
{{#if model.canResetBounceScore}}
{{d-button action="resetBounceScore" label="admin.user.reset_bounce_score.label" title="admin.user.reset_bounce_score.title"}}
{{d-button class="btn-default" action="resetBounceScore" label="admin.user.reset_bounce_score.label" title="admin.user.reset_bounce_score.title"}}
{{/if}}
{{model.bounceScoreExplanation}}
</div>
@ -114,7 +115,7 @@
{{#if associatedAccountsLoaded}}
{{associatedAccounts}}
{{else}}
{{d-button action="checkEmail" actionParam=model icon="envelope-o" label="admin.users.check_email.text" title="admin.users.check_email.title"}}
{{d-button class="btn-default" action="checkEmail" actionParam=model icon="envelope-o" label="admin.users.check_email.text" title="admin.users.check_email.title"}}
{{/if}}
</div>
</div>
@ -139,10 +140,10 @@
</div>
<div class='controls'>
{{#if editingTitle}}
{{d-button action="saveTitle" label="admin.user_fields.save"}}
{{d-button class="btn-default" action="saveTitle" label="admin.user_fields.save"}}
<a href {{action "toggleTitleEdit"}}>{{i18n 'cancel'}}</a>
{{else}}
{{d-button action="toggleTitleEdit" icon="pencil"}}
{{d-button class="btn-default" action="toggleTitleEdit" icon="pencil"}}
{{/if}}
</div>
</div>
@ -152,7 +153,7 @@
<div class='value'>{{model.ip_address}}</div>
<div class='controls'>
{{#if currentUser.staff}}
{{d-button action="refreshBrowsers" label="admin.user.refresh_browsers"}}
{{d-button class="btn-default" action="refreshBrowsers" label="admin.user.refresh_browsers"}}
{{ip-lookup ip=model.ip_address userId=model.id}}
{{/if}}
</div>
@ -191,7 +192,7 @@
</div>
<div class='controls'>
{{#if canDisableSecondFactor}}
{{d-button action="disableSecondFactor" icon="unlock-alt" label="user.second_factor.disable"}}
{{d-button class="btn-default" action="disableSecondFactor" icon="unlock-alt" label="user.second_factor.disable"}}
{{/if}}
</div>
</div>
@ -236,7 +237,7 @@
{{i18n 'admin.user.approve_success'}}
{{else}}
{{#if model.can_approve}}
{{d-button action="approve" icon="check" label="admin.user.approve"}}
{{d-button class="btn-default" action="approve" icon="check" label="admin.user.approve"}}
{{/if}}
{{/if}}
</div>
@ -249,15 +250,15 @@
<div class='controls'>
{{#if model.active}}
{{#if model.can_deactivate}}
{{d-button action="deactivate" label="admin.user.deactivate_account"}}
{{d-button class="btn-default" action="deactivate" label="admin.user.deactivate_account"}}
{{i18n 'admin.user.deactivate_explanation'}}
{{/if}}
{{else}}
{{#if model.can_send_activation_email}}
{{d-button action="sendActivationEmail" icon="envelope" label="admin.user.send_activation_email"}}
{{d-button class="btn-default" action="sendActivationEmail" icon="envelope" label="admin.user.send_activation_email"}}
{{/if}}
{{#if model.can_activate}}
{{d-button action="activate" icon="check" label="admin.user.activate"}}
{{d-button class="btn-default" action="activate" icon="check" label="admin.user.activate"}}
{{/if}}
{{/if}}
</div>
@ -275,15 +276,15 @@
{{#if model.api_key}}
<div class='long-value'>
{{model.api_key.key}}
{{d-button action="regenerateApiKey" icon="undo" label="admin.api.regenerate"}}
{{d-button action="revokeApiKey" icon="times" label="admin.api.revoke"}}
{{d-button class="btn-default" action="regenerateApiKey" icon="undo" label="admin.api.regenerate"}}
{{d-button class="btn-default" action="revokeApiKey" icon="times" label="admin.api.revoke"}}
</div>
{{else}}
<div class='value'>
&mdash;
</div>
<div class='controls'>
{{d-button action="generateApiKey" icon="key" label="admin.api.generate"}}
{{d-button class="btn-default" action="generateApiKey" icon="key" label="admin.api.generate"}}
</div>
{{/if}}
</div>
@ -294,10 +295,10 @@
<div class='value'>{{i18n-yes-no model.admin}}</div>
<div class='controls'>
{{#if model.can_revoke_admin}}
{{d-button action="revokeAdmin" icon="shield" label="admin.user.revoke_admin"}}
{{d-button class="btn-default" action="revokeAdmin" icon="shield" label="admin.user.revoke_admin"}}
{{/if}}
{{#if model.can_grant_admin}}
{{d-button action="grantAdmin" icon="shield" label="admin.user.grant_admin"}}
{{d-button class="btn-default" action="grantAdmin" icon="shield" label="admin.user.grant_admin"}}
{{/if}}
</div>
</div>
@ -307,10 +308,10 @@
<div class='value'>{{i18n-yes-no model.moderator}}</div>
<div class='controls'>
{{#if model.can_revoke_moderation}}
{{d-button action="revokeModeration" icon="shield" label="admin.user.revoke_moderation"}}
{{d-button class="btn-default" action="revokeModeration" icon="shield" label="admin.user.revoke_moderation"}}
{{/if}}
{{#if model.can_grant_moderation}}
{{d-button action="grantModeration" icon="shield" label="admin.user.grant_moderation"}}
{{d-button class="btn-default" action="grantModeration" icon="shield" label="admin.user.grant_moderation"}}
{{/if}}
</div>
</div>
@ -330,14 +331,14 @@
{{#if model.canLockTrustLevel}}
{{#if hasLockedTrustLevel}}
{{d-icon "lock" title="admin.user.trust_level_locked_tip"}}
{{d-button action="lockTrustLevel" actionParam=false label="admin.user.unlock_trust_level"}}
{{d-button class="btn-default" action="lockTrustLevel" actionParam=false label="admin.user.unlock_trust_level"}}
{{else}}
{{d-icon "unlock" title="admin.user.trust_level_unlocked_tip"}}
{{d-button action="lockTrustLevel" actionParam=true label="admin.user.lock_trust_level"}}
{{d-button class="btn-default" action="lockTrustLevel" actionParam=true label="admin.user.lock_trust_level"}}
{{/if}}
{{/if}}
{{#if model.tl3Requirements}}
{{#link-to 'adminUser.tl3Requirements' model class="btn"}}{{i18n 'admin.user.trust_level_3_requirements'}}{{/link-to}}
{{#link-to 'adminUser.tl3Requirements' model class="btn btn-default"}}{{i18n 'admin.user.trust_level_3_requirements'}}{{/link-to}}
{{/if}}
</div>
</div>
@ -439,6 +440,7 @@
{{#if currentUser.admin}}
<div class='controls'>
{{d-button label="admin.user.clear_penalty_history.title"
class="btn-default"
icon="times"
action=(action "clearPenaltyHistory")}}
{{i18n "admin.user.clear_penalty_history.description"}}
@ -537,6 +539,7 @@
<div class='controls'>
{{#if model.flags_received_count}}
{{d-button
class="btn-default"
action=(action "showFlagsReceived")
label="admin.user.show_flags_received"
icon="flag"

View File

@ -8,7 +8,7 @@
<div class="admin-title">
<h2>{{title}}</h2>
{{#unless showEmails}}
<button {{action "showEmails"}} class="show-emails btn">{{i18n 'admin.users.show_emails'}}</button>
<button {{action "showEmails"}} class="show-emails btn btn-default">{{i18n 'admin.users.show_emails'}}</button>
{{/unless}}
</div>
<div class='username controls'>

View File

@ -14,10 +14,10 @@
{{nav-item route='groups' label='groups.index.title'}}
<div class="admin-actions">
{{#unless siteSettings.enable_sso}}
{{d-button action="sendInvites" title="admin.invite.button_title" icon="user-plus" label="admin.invite.button_text"}}
{{d-button class="btn-default" action="sendInvites" title="admin.invite.button_title" icon="user-plus" label="admin.invite.button_text"}}
{{/unless}}
{{#if currentUser.admin}}
{{d-button action="exportUsers" title="admin.export_csv.button_title.user" icon="download" label="admin.export_csv.button_text"}}
{{d-button class="btn-default" action="exportUsers" title="admin.export_csv.button_title.user" icon="download" label="admin.export_csv.button_text"}}
{{/if}}
</div>
</ul>

View File

@ -2,7 +2,7 @@
<div class='web-hooks-listing'>
<p>{{i18n 'admin.web_hooks.instruction'}}</p>
<div class='new-webhook'>
{{#link-to 'adminWebHooks.show' 'new' tagName='button' classNames='btn'}}
{{#link-to 'adminWebHooks.show' 'new' tagName='button' classNames='btn btn-default'}}
{{d-icon 'plus'}} {{i18n 'admin.web_hooks.new'}}
{{/link-to}}
</div>

View File

@ -0,0 +1,6 @@
(function() {
const path = document.getElementById("data-auto-redirect").dataset.path;
setTimeout(function() {
window.location.href = path;
}, 2000);
})();

View File

@ -0,0 +1,51 @@
import { ajax } from "discourse/lib/ajax";
import computed from "ember-addons/ember-computed-decorators";
import UploadMixin from "discourse/mixins/upload";
export default Em.Component.extend(UploadMixin, {
tagName: "span",
@computed("uploading", "uploadProgress")
uploadButtonText(uploading, progress) {
return uploading
? I18n.t("admin.backups.upload.uploading_progress", { progress })
: I18n.t("admin.backups.upload.label");
},
validateUploadedFilesOptions() {
return { skipValidation: true };
},
uploadDone() {
this.sendAction("done");
},
calculateUploadUrl() {
return "";
},
uploadOptions() {
return {
type: "PUT",
dataType: "xml",
autoUpload: false
};
},
_init: function() {
const $upload = this.$();
$upload.on("fileuploadadd", (e, data) => {
ajax("/admin/backups/upload_url", {
data: { filename: data.files[0].name }
}).then(result => {
if (!result.success) {
bootbox.alert(result.message);
} else {
data.url = result.url;
data.submit();
}
});
});
}.on("didInsertElement")
});

View File

@ -618,7 +618,6 @@ export default Ember.Component.extend({
_bindUploadTarget() {
this._unbindUploadTarget(); // in case it's still bound, let's clean it up first
this._pasted = false;
const $element = this.$();
@ -649,12 +648,23 @@ export default Ember.Component.extend({
});
$element.on("fileuploadsubmit", (e, data) => {
const max = this.siteSettings.simultaneous_uploads;
// Limit the number of simultaneous uploads
if (max > 0 && data.files.length > max) {
bootbox.alert(
I18n.t("post.errors.too_many_dragged_and_dropped_files", { max })
);
return false;
}
// Look for a matching file upload handler contributed from a plugin
const matcher = handler => {
const ext = handler.extensions.join("|");
const regex = new RegExp(`\\.(${ext})$`, "i");
return regex.test(data.files[0].name);
};
const matchingHandler = uploadHandlers.find(matcher);
if (data.files.length === 1 && matchingHandler) {
matchingHandler.method(data.files[0]);

View File

@ -13,7 +13,7 @@ export default Ember.Component.extend({
_yourselfConfirm: null,
similarTopics: null,
hidden: Ember.computed.not("composer.viewOpen"),
hidden: Ember.computed.not("composer.viewOpenOrFullscreen"),
didInsertElement() {
this._super();

View File

@ -4,18 +4,28 @@ export default Ember.Component.extend({
tagName: "",
@computed("composeState")
title(composeState) {
if (composeState === "draft" || composeState === "saving") {
return "composer.abandon";
}
return "composer.collapse";
toggleTitle(composeState) {
return composeState === "draft" || composeState === "saving"
? "composer.abandon"
: "composer.collapse";
},
@computed("composeState")
fullscreenTitle(composeState) {
return composeState === "fullscreen"
? "composer.exit_fullscreen"
: "composer.enter_fullscreen";
},
@computed("composeState")
toggleIcon(composeState) {
if (composeState === "draft" || composeState === "saving") {
return "times";
}
return "chevron-down";
return composeState === "draft" || composeState === "saving"
? "times"
: "chevron-down";
},
@computed("composeState")
fullscreenIcon(composeState) {
return composeState === "fullscreen" ? "compress" : "expand";
}
});

View File

@ -11,7 +11,7 @@ export default Ember.Component.extend(
let notices = [];
if ($.cookie("dosp") === "1") {
$.cookie("dosp", null, { path: "/" });
$.removeCookie("dosp", { path: "/" });
notices.push([I18n.t("forced_anonymous"), "forced-anonymous"]);
}

View File

@ -1,7 +1,7 @@
import Button from "discourse/components/d-button";
export default Button.extend({
classNames: ["share"],
classNames: ["btn-default", "share"],
icon: "link",
title: "topic.share.help",
label: "topic.share.title",

View File

@ -16,6 +16,12 @@ export default DropdownSelectBoxComponent.extend({
name: I18n.t("tagging.manage_groups"),
description: I18n.t("tagging.manage_groups_description"),
icon: "wrench"
},
{
id: "uploadTags",
name: I18n.t("tagging.upload"),
description: I18n.t("tagging.upload_description"),
icon: "upload"
}
];
@ -23,7 +29,8 @@ export default DropdownSelectBoxComponent.extend({
},
actionNames: {
manageGroups: "showTagGroups"
manageGroups: "showTagGroups",
uploadTags: "showUploader"
},
mutateValue(id) {

View File

@ -35,7 +35,7 @@ export default Ember.Component.extend(PanEvents, {
height -= $("#reply-control").height();
}
renderTimeline = width > 960 && height > 520;
renderTimeline = width > 924 && height > 520;
}
info.setProperties({

View File

@ -231,7 +231,7 @@ export default Ember.Controller.extend({
@computed("model.composeState", "model.creatingTopic")
popupMenuOptions(composeState) {
if (composeState === "open") {
if (composeState === "open" || composeState === "fullscreen") {
let options = [];
options.push(
@ -386,7 +386,10 @@ export default Ember.Controller.extend({
) {
this.close();
} else {
if (this.get("model.composeState") === Composer.OPEN) {
if (
this.get("model.composeState") === Composer.OPEN ||
this.get("model.composeState") === Composer.FULLSCREEN
) {
this.shrink();
} else {
this.cancelComposer();
@ -396,6 +399,11 @@ export default Ember.Controller.extend({
return false;
},
fullscreenComposer() {
this.toggleFullscreen();
return false;
},
// Import a quote from the post
importQuote(toolbarEvent) {
const postStream = this.get("topic.postStream");
@ -457,7 +465,7 @@ export default Ember.Controller.extend({
return;
}
if (this.get("model.viewOpen")) {
if (this.get("model.viewOpen") || this.get("model.viewFullscreen")) {
this.shrink();
}
},
@ -947,7 +955,20 @@ export default Ember.Controller.extend({
this.set("model.composeState", Composer.DRAFT);
},
toggleFullscreen() {
this._saveDraft();
if (this.get("model.composeState") === Composer.FULLSCREEN) {
this.set("model.composeState", Composer.OPEN);
} else {
this.set("model.composeState", Composer.FULLSCREEN);
}
},
close() {
// the 'fullscreen-composer' class is added to remove scrollbars from the
// document while in fullscreen mode. If the composer is closed for any reason
// this class should be removed
$("html").removeClass("fullscreen-composer");
this.setProperties({ model: null, lastValidatedAt: null });
},

View File

@ -9,6 +9,7 @@ import UsernameValidation from "discourse/mixins/username-validation";
import NameValidation from "discourse/mixins/name-validation";
import UserFieldsValidation from "discourse/mixins/user-fields-validation";
import { userPath } from "discourse/lib/url";
import { findAll } from "discourse/models/login-method";
export default Ember.Controller.extend(
ModalFunctionality,
@ -176,6 +177,11 @@ export default Ember.Controller.extend(
}
}.observes("emailValidation", "accountEmail"),
// Determines whether at least one login button is enabled
hasAtLeastOneLoginButton: function() {
return findAll(this.siteSettings).length > 0;
}.property(),
@on("init")
fetchConfirmationValue() {
return ajax(userPath("hp.json")).then(json => {
@ -260,12 +266,12 @@ export default Ember.Controller.extend(
this.get("rejectedPasswords").pushObject(attrs.accountPassword);
}
this.set("formSubmitted", false);
$.cookie("destination_url", null);
$.removeCookie("destination_url");
}
},
() => {
this.set("formSubmitted", false);
$.cookie("destination_url", null);
$.removeCookie("destination_url");
return this.flash(I18n.t("create_account.failed"), "error");
}
);

View File

@ -57,9 +57,10 @@ export default Ember.Controller.extend(ModalFunctionality, {
},
// Determines whether at least one login button is enabled
hasAtLeastOneLoginButton: function() {
return findAll(this.siteSettings).length > 0;
}.property(),
@computed("canLoginLocalWithEmail")
hasAtLeastOneLoginButton(canLoginLocalWithEmail) {
return findAll(this.siteSettings).length > 0 || canLoginLocalWithEmail;
},
@computed("loggingIn")
loginButtonLabel(loggingIn) {
@ -158,12 +159,12 @@ export default Ember.Controller.extend(ModalFunctionality, {
.val(self.get("loginPassword"));
if (ssoDestinationUrl) {
$.cookie("sso_destination_url", null);
$.removeCookie("sso_destination_url");
window.location.assign(ssoDestinationUrl);
return;
} else if (destinationUrl) {
// redirect client to the original URL
$.cookie("destination_url", null);
$.removeCookie("destination_url");
$hidden_login_form
.find("input[name=redirect]")
.val(destinationUrl);
@ -327,7 +328,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
$.cookie("destination_url") || options.destination_url;
if (destinationUrl) {
// redirect client to the original URL
$.cookie("destination_url", null);
$.removeCookie("destination_url");
window.location.href = destinationUrl;
} else if (window.location.pathname === Discourse.getURL("/login")) {
window.location.pathname = Discourse.getURL("/");

View File

@ -83,8 +83,12 @@ export default Ember.Controller.extend(PasswordValidation, {
}
}
})
.catch(error => {
throw new Error(error);
.catch(e => {
if (e.jqXHR && e.jqXHR.status === 429) {
this.set("errorMessage", I18n.t("user.second_factor.rate_limit"));
} else {
throw new Error(e);
}
});
},

View File

@ -107,7 +107,12 @@ export default Ember.Controller.extend(
@computed("showAllAuthTokens", "model.user_auth_tokens")
authTokens(showAllAuthTokens, tokens) {
tokens.sort(
(a, b) => (a.is_active ? -1 : b.is_active ? 1 : a.seen_at < b.seen_at)
(a, b) =>
a.is_active
? -1
: b.is_active
? 1
: b.seen_at.localeCompare(a.seen_at)
);
return showAllAuthTokens
@ -199,10 +204,6 @@ export default Ember.Controller.extend(
bootbox.dialog(message, buttons, { classes: "delete-account" });
},
showTwoFactorModal() {
showModal("second-factor-intro");
},
revokeAccount(account) {
const model = this.get("model");
this.set("revoking", true);

View File

@ -1,4 +1,5 @@
import computed from "ember-addons/ember-computed-decorators";
import showModal from "discourse/lib/show-modal";
export default Ember.Controller.extend({
sortProperties: ["totalCount:desc", "id"],
@ -33,6 +34,10 @@ export default Ember.Controller.extend({
sortedByCount: false,
sortedByName: true
});
},
showUploader() {
showModal("tag-upload");
}
}
});

View File

@ -21,24 +21,45 @@ function addBulkButton(action, key, opts) {
}
// Default buttons
addBulkButton("showChangeCategory", "change_category", { icon: "pencil" });
addBulkButton("closeTopics", "close_topics", { icon: "lock" });
addBulkButton("archiveTopics", "archive_topics", { icon: "folder" });
addBulkButton("showNotificationLevel", "notification_level", {
icon: "d-regular"
addBulkButton("showChangeCategory", "change_category", {
icon: "pencil",
class: "btn-default"
});
addBulkButton("closeTopics", "close_topics", {
icon: "lock",
class: "btn-default"
});
addBulkButton("archiveTopics", "archive_topics", {
icon: "folder",
class: "btn-default"
});
addBulkButton("showNotificationLevel", "notification_level", {
icon: "d-regular",
class: "btn-default"
});
addBulkButton("resetRead", "reset_read", {
icon: "backward",
class: "btn-default"
});
addBulkButton("resetRead", "reset_read", { icon: "backward" });
addBulkButton("unlistTopics", "unlist_topics", {
icon: "eye-slash",
class: "btn-default",
buttonVisible: topics => topics.some(t => t.visible)
});
addBulkButton("relistTopics", "relist_topics", {
icon: "eye",
class: "btn-default",
buttonVisible: topics => topics.some(t => !t.visible)
});
if (Discourse.SiteSettings.tagging_enabled) {
addBulkButton("showTagTopics", "change_tags", { icon: "tag" });
addBulkButton("showAppendTagTopics", "append_tags", { icon: "tag" });
addBulkButton("showTagTopics", "change_tags", {
icon: "tag",
class: "btn-default"
});
addBulkButton("showAppendTagTopics", "append_tags", {
icon: "tag",
class: "btn-default"
});
}
addBulkButton("deleteTopics", "delete", { icon: "trash", class: "btn-danger" });

View File

@ -13,6 +13,11 @@ export function isValidLink($link) {
export default {
trackClick(e) {
// right clicks are not tracked
if (e.which === 3) {
return true;
}
// cancel click if triggered as part of selection.
if (selectedText() !== "") {
return false;

View File

@ -65,6 +65,7 @@ const bindings = {
"shift+s": { click: "#topic-footer-buttons button.share", anonymous: true }, // share topic
"shift+u": { handler: "goToUnreadPost" },
"shift+z shift+z": { handler: "logout" },
"shift+f11": { handler: "fullscreenComposer" },
t: { postAction: "replyAsNewTopic" },
u: { handler: "goBack", anonymous: true },
"x r": {
@ -212,6 +213,13 @@ export default {
}
},
fullscreenComposer() {
const composer = this.container.lookup("controller:composer");
if (composer.get("model")) {
composer.toggleFullscreen();
}
},
pinUnpinTopic() {
this.container.lookup("controller:topic").togglePinnedState();
},

View File

@ -9,24 +9,13 @@ import Post from "discourse/models/post";
import Topic from "discourse/models/topic";
export function translateResults(results, opts) {
if (!opts) opts = {};
opts = opts || {};
// Topics might not be included
if (!results.topics) {
results.topics = [];
}
if (!results.users) {
results.users = [];
}
if (!results.posts) {
results.posts = [];
}
if (!results.categories) {
results.categories = [];
}
if (!results.tags) {
results.tags = [];
}
results.topics = results.topics || [];
results.users = results.users || [];
results.posts = results.posts || [];
results.categories = results.categories || [];
results.tags = results.tags || [];
const topicMap = {};
results.topics = results.topics.map(function(topic) {
@ -45,8 +34,7 @@ export function translateResults(results, opts) {
});
results.users = results.users.map(function(user) {
user = User.create(user);
return user;
return User.create(user);
});
results.categories = results.categories
@ -57,7 +45,7 @@ export function translateResults(results, opts) {
results.tags = results.tags
.map(function(tag) {
let tagName = Handlebars.Utils.escapeExpression(tag.name);
const tagName = Handlebars.Utils.escapeExpression(tag.name);
return Ember.Object.create({
id: tagName,
url: Discourse.getURL("/tags/" + tagName)
@ -65,31 +53,31 @@ export function translateResults(results, opts) {
})
.compact();
const r = results.grouped_search_result;
results.resultTypes = [];
// TODO: consider refactoring front end to take a better structure
if (r) {
const groupedSearchResult = results.grouped_search_result;
if (groupedSearchResult) {
[
["topic", "posts"],
["user", "users"],
["category", "categories"],
["tag", "tags"]
["tag", "tags"],
["user", "users"]
].forEach(function(pair) {
const type = pair[0],
name = pair[1];
const type = pair[0];
const name = pair[1];
if (results[name].length > 0) {
var result = {
const componentName =
opts.searchContext &&
opts.searchContext.type === "topic" &&
type === "topic"
? "post"
: type;
const result = {
results: results[name],
componentName:
"search-result-" +
(opts.searchContext &&
opts.searchContext.type === "topic" &&
type === "topic"
? "post"
: type),
componentName: `search-result-${componentName}`,
type,
more: r["more_" + name]
more: groupedSearchResult[`more_${name}`]
};
if (result.more && name === "posts" && opts.fullSearchUrl) {
@ -103,10 +91,10 @@ export function translateResults(results, opts) {
}
const noResults = !!(
results.topics.length === 0 &&
results.posts.length === 0 &&
results.users.length === 0 &&
results.categories.length === 0
!results.topics.length &&
!results.posts.length &&
!results.users.length &&
!results.categories.length
);
return noResults ? null : Em.Object.create(results);

View File

@ -38,7 +38,7 @@ export function setLocalTheme(ids, themeSeq) {
expires: 9999
});
} else {
$.cookie("theme_ids", null, { path: "/", expires: 1 });
$.removeCookie("theme_ids", { path: "/", expires: 1 });
}
}

View File

@ -227,7 +227,7 @@ export class Tag {
const src = attr.src || pAttr.src;
const cssClass = attr.class || pAttr.class;
if (cssClass === "emoji") {
if (cssClass && cssClass.includes("emoji")) {
return attr.title || pAttr.title;
}

View File

@ -109,6 +109,10 @@ function organizeResults(r, options) {
}
export default function userSearch(options) {
if (options.term && options.term.length > 0 && options.term[0] === "@") {
options.term = options.term.substring(1);
}
var term = options.term || "",
includeGroups = options.includeGroups,
includeMentionableGroups = options.includeMentionableGroups,

View File

@ -225,6 +225,7 @@ export function validateUploadedFiles(files, opts) {
}
export function validateUploadedFile(file, opts) {
if (opts.skipValidation) return true;
if (!authorizesOneOrMoreExtensions()) return false;
opts = opts || {};

View File

@ -2,6 +2,7 @@ import {
displayErrorForUpload,
validateUploadedFiles
} from "discourse/lib/utilities";
import getUrl from "discourse-common/lib/get-url";
export default Em.Mixin.create({
uploading: false,
@ -15,13 +16,28 @@ export default Em.Mixin.create({
return {};
},
calculateUploadUrl() {
return (
getUrl(this.getWithDefault("uploadUrl", "/uploads")) +
".json?client_id=" +
this.messageBus.clientId +
"&authenticity_token=" +
encodeURIComponent(Discourse.Session.currentProp("csrfToken"))
);
},
uploadOptions() {
return {};
},
_initialize: function() {
const $upload = this.$(),
csrf = Discourse.Session.currentProp("csrfToken"),
uploadUrl = Discourse.getURL(
this.getWithDefault("uploadUrl", "/uploads")
),
reset = () => this.setProperties({ uploading: false, uploadProgress: 0 });
const $upload = this.$();
const reset = () =>
this.setProperties({ uploading: false, uploadProgress: 0 });
const maxFiles = this.getWithDefault(
"maxFiles",
this.siteSettings.simultaneous_uploads
);
$upload.on("fileuploaddone", (e, data) => {
let upload = data.result;
@ -29,21 +45,26 @@ export default Em.Mixin.create({
reset();
});
$upload.fileupload({
url:
uploadUrl +
".json?client_id=" +
this.messageBus.clientId +
"&authenticity_token=" +
encodeURIComponent(csrf),
dataType: "json",
dropZone: $upload,
pasteZone: $upload
});
$upload.fileupload(
_.merge(
{
url: this.calculateUploadUrl(),
dataType: "json",
replaceFileInput: false,
dropZone: $upload,
pasteZone: $upload
},
this.uploadOptions()
)
);
$upload.on("fileuploaddrop", (e, data) => {
if (data.files.length > 10) {
bootbox.alert(I18n.t("post.errors.too_many_dragged_and_dropped_files"));
if (maxFiles > 0 && data.files.length > maxFiles) {
bootbox.alert(
I18n.t("post.errors.too_many_dragged_and_dropped_files", {
max: maxFiles
})
);
return false;
} else {
return true;
@ -56,7 +77,8 @@ export default Em.Mixin.create({
this.validateUploadedFilesOptions()
);
const isValid = validateUploadedFiles(data.files, opts);
let form = { type: this.get("type") };
const type = this.get("type");
let form = type ? { type } : {};
if (this.get("data")) {
form = $.extend(form, this.get("data"));
}

View File

@ -26,6 +26,7 @@ const CLOSED = "closed",
SAVING = "saving",
OPEN = "open",
DRAFT = "draft",
FULLSCREEN = "fullscreen",
// When creating, these fields are moved into the post model from the composer model
_create_serializer = {
raw: "reply",
@ -144,15 +145,24 @@ const Composer = RestModel.extend({
viewOpen: Em.computed.equal("composeState", OPEN),
viewDraft: Em.computed.equal("composeState", DRAFT),
viewFullscreen: Em.computed.equal("composeState", FULLSCREEN),
viewOpenOrFullscreen: Em.computed.or("viewOpen", "viewFullscreen"),
composeStateChanged: function() {
var oldOpen = this.get("composerOpened");
let oldOpen = this.get("composerOpened"),
elem = $("html");
if (this.get("composeState") === FULLSCREEN) {
elem.addClass("fullscreen-composer");
} else {
elem.removeClass("fullscreen-composer");
}
if (this.get("composeState") === OPEN) {
this.set("composerOpened", oldOpen || new Date());
} else {
if (oldOpen) {
var oldTotal = this.get("composerTotalOpened") || 0;
let oldTotal = this.get("composerTotalOpened") || 0;
this.set("composerTotalOpened", oldTotal + (new Date() - oldOpen));
}
this.set("composerOpened", null);
@ -160,9 +170,8 @@ const Composer = RestModel.extend({
}.observes("composeState"),
composerTime: function() {
var total = this.get("composerTotalOpened") || 0;
var oldOpen = this.get("composerOpened");
let total = this.get("composerTotalOpened") || 0,
oldOpen = this.get("composerOpened");
if (oldOpen) {
total += new Date() - oldOpen;
}
@ -183,7 +192,7 @@ const Composer = RestModel.extend({
// view detected user is typing
typing: _.throttle(
function() {
var typingTime = this.get("typingTime") || 0;
let typingTime = this.get("typingTime") || 0;
this.set("typingTime", typingTime + 100);
},
100,
@ -378,9 +387,14 @@ const Composer = RestModel.extend({
return SAVE_ICONS[action];
},
@computed("action", "whisper")
saveLabel(action, whisper) {
return whisper ? "composer.create_whisper" : SAVE_LABELS[action];
@computed("action", "whisper", "editConflict")
saveLabel(action, whisper, editConflict) {
if (editConflict) {
return "composer.overwrite_edit";
} else if (whisper) {
return "composer.create_whisper";
}
return SAVE_LABELS[action];
},
hasMetaData: function() {
@ -718,7 +732,8 @@ const Composer = RestModel.extend({
composerOpened: null,
composerTotalOpened: 0,
featuredLink: null,
noBump: false
noBump: false,
editConflict: false
});
},
@ -753,6 +768,7 @@ const Composer = RestModel.extend({
const props = {
raw: this.get("reply"),
raw_old: this.get("editConflict") ? null : this.get("originalText"),
edit_reason: opts.editReason,
image_sizes: opts.imageSizes,
cooked: this.getCookedHtml()
@ -760,9 +776,12 @@ const Composer = RestModel.extend({
this.set("composeState", SAVING);
let rollback = throwAjaxError(() => {
let rollback = throwAjaxError(error => {
post.set("cooked", oldCooked);
this.set("composeState", OPEN);
if (error.jqXHR && error.jqXHR.status === 409) {
this.set("editConflict", true);
}
});
return promise
@ -1041,6 +1060,7 @@ Composer.reopenClass({
SAVING,
OPEN,
DRAFT,
FULLSCREEN,
// The actions the composer can take
CREATE_TOPIC,

View File

@ -41,6 +41,10 @@ export default Discourse.Route.extend({
showTagGroups() {
this.transitionTo("tagGroups");
return true;
},
refresh() {
this.refresh();
}
}
});

View File

@ -1,6 +1,6 @@
<label class="btn" disabled={{uploading}} title="{{i18n 'user.change_avatar.upload_title'}}">
{{d-icon "picture-o"}}&nbsp;{{uploadButtonText}}
<input disabled={{uploading}} type="file" accept="image/*" style="visibility: hidden; position: absolute;" />
<input class="hidden-upload-field" disabled={{uploading}} type="file" accept="image/*" />
</label>
{{#if uploading}}
<span>{{i18n 'upload_selector.uploading'}} {{uploadProgress}}%</span>

View File

@ -0,0 +1,4 @@
<label class="btn" disabled={{uploading}} title="{{i18n 'admin.backups.upload.title'}}">
{{d-icon "upload"}}&nbsp;{{uploadButtonText}}
<input class="hidden-upload-field" disabled={{uploading}} type="file" accept=".gz" />
</label>

View File

@ -1,5 +1,5 @@
{{#if selected}}
<div id='bulk-select'>
{{d-button action="showBulkActions" icon="wrench"}}
{{d-button class="btn-default" action="showBulkActions" icon="wrench"}}
</div>
{{/if}}

View File

@ -1,35 +1,38 @@
{{#each categories as |c|}}
<div class='category-box category-box-{{unbound c.slug}}' style={{border-color c.color}} data-url={{c.url}}>
<div class='category-box-inner'>
<div class='category-box-heading'>
<div class="category-logo">
{{#if c.uploaded_logo.url}}
{{cdn-img src=c.uploaded_logo.url class="logo"}}
{{/if}}
<h3>
{{#if c.read_restricted}}
{{d-icon 'lock'}}
{{/if}}
{{c.name}}
</h3>
</div>
<div class='description'>
{{{text-overflow class="overflow" text=c.description_excerpt}}}
</div>
{{#if c.subcategories}}
<div class='subcategories'>
{{#each c.subcategories as |sc|}}
<a class="subcategory" href={{sc.url}}>
<span class="subcategory-image-placeholder">
{{cdn-img src=sc.uploaded_logo.url class="logo"}}
</span>
<span class="subcategory-link">{{sc.name}}</span>
</a>
{{/each}}
<div class="category-details">
<div class='category-box-heading'>
<h3>
{{#if c.read_restricted}}
{{d-icon 'lock'}}
{{/if}}
{{c.name}}
</h3>
</div>
{{/if}}
<div class='description'>
{{{text-overflow class="overflow" text=c.description_excerpt}}}
</div>
{{#if c.subcategories}}
<div class='subcategories'>
{{#each c.subcategories as |sc|}}
<a class="subcategory" href={{sc.url}}>
<span class="subcategory-image-placeholder">
{{cdn-img src=sc.uploaded_logo.url class="logo"}}
</span>
<span class="subcategory-link">{{sc.name}}</span>
</a>
{{/each}}
</div>
{{/if}}
</div>
</div>
</div>
{{/each}}

View File

@ -7,7 +7,7 @@
{{latest-topic-list-item topic=t}}
{{/each}}
<div class="more-topics">
<a href="/{{filter}}" class="btn pull-right">{{i18n "more"}}</a>
<a href="/{{filter}}" class="btn btn-default pull-right">{{i18n "more"}}</a>
</div>
{{else}}
<div class='no-topics'>

View File

@ -10,5 +10,13 @@
class="toggler"
icon=toggleIcon
action=toggleComposer
title=title}}
title=toggleTitle}}
{{#unless site.mobileView}}
{{flat-button
class="toggle-fullscreen"
icon=fullscreenIcon
action=toggleFullscreen
title=fullscreenTitle}}
{{/unless}}
</div>

View File

@ -1,6 +1,6 @@
<label class="btn" disabled={{uploadButtonDisabled}}>
{{d-icon "upload"}}&nbsp;{{uploadButtonText}}
<input disabled={{uploading}} type="file" accept=".csv" style="visibility: hidden; position: absolute;" />
<input class="hidden-upload-field" disabled={{uploading}} type="file" accept=".csv" />
</label>
{{#if uploading}}
<span>{{i18n 'upload_selector.uploading'}} {{uploadProgress}}%</span>

View File

@ -45,9 +45,7 @@
</div>
<div class="d-editor-preview-wrapper {{if forcePreview 'force-preview'}}">
<div class="d-editor-preview">
{{{preview}}}
</div>
<div class="d-editor-preview">{{{preview}}}</div>
{{plugin-outlet name="editor-preview" classNames="d-editor-plugin"}}
</div>
</div>

View File

@ -1,15 +1,15 @@
{{#if isNotSupported}}
{{d-button icon="bell-slash" label="user.desktop_notifications.not_supported" disabled="true"}}
{{d-button icon="bell-slash" class="btn-default" label="user.desktop_notifications.not_supported" disabled="true"}}
{{/if}}
{{#if isDeniedPermission}}
{{d-button icon="bell-slash" label="user.desktop_notifications.perm_denied_btn" action="recheckPermission" disabled='true'}}
{{d-button icon="bell-slash" class="btn-default" label="user.desktop_notifications.perm_denied_btn" action="recheckPermission" disabled='true'}}
{{i18n "user.desktop_notifications.perm_denied_expl"}}
{{else}}
{{#if isEnabled}}
{{d-button icon="bell-slash-o" label="user.desktop_notifications.disable" action="turnoff"}}
{{d-button icon="bell-slash-o" class="btn-default" label="user.desktop_notifications.disable" action="turnoff"}}
{{i18n "user.desktop_notifications.currently_enabled"}}
{{else}}
{{d-button icon="bell-o" label="user.desktop_notifications.enable" action="turnon"}}
{{d-button icon="bell-o" class="btn-default" label="user.desktop_notifications.enable" action="turnon"}}
{{i18n "user.desktop_notifications.currently_disabled"}}
{{/if}}
{{/if}}

View File

@ -32,7 +32,7 @@
{{/if}}
{{else}}
{{#unless category.is_special}}
<button {{action "editPermissions"}} class="btn btn-small edit-permission">{{i18n 'category.edit_permissions'}}</button>
<button {{action "editPermissions"}} class="btn btn-default btn-small edit-permission">{{i18n 'category.edit_permissions'}}</button>
{{/unless}}
{{/if}}
</section>

View File

@ -2,5 +2,5 @@
<label class="btn btn-primary {{if addDisabled 'disabled'}}">
{{d-icon "plus"}}
{{i18n 'admin.emoji.add'}}
<input disabled={{addDisabled}} type="file" accept=".png,.gif" style="visibility: hidden; position: absolute;" />
<input class="hidden-upload-field" disabled={{addDisabled}} type="file" accept=".png,.gif" />
</label>

View File

@ -8,6 +8,7 @@
input=input
includeWeekend=includeWeekend
includeFarFuture=includeFarFuture
clearable=clearable
none="topic.auto_update_input.none"}}
</div>

View File

@ -1,8 +1,8 @@
<div class="uploaded-image-preview input-xxlarge" style={{backgroundStyle}}>
<div class="image-upload-controls">
<label class="btn pad-left no-text {{if uploading 'disabled'}}">
<label class="btn btn-default pad-left no-text {{if uploading 'disabled'}}">
{{d-icon "picture-o"}}
<input disabled={{uploading}} type="file" accept="image/*" style="visibility: hidden; position: absolute;" />
<input class="hidden-upload-field" disabled={{uploading}} type="file" accept="image/*" />
</label>
{{#if backgroundStyle}}
<button {{action "trash"}} class="btn btn-danger pad-left no-text">{{d-icon "trash-o"}}</button>

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