Version bump

This commit is contained in:
Neil Lalonde 2020-11-30 17:47:48 -05:00
commit 234b5780c3
No known key found for this signature in database
GPG Key ID: FF871CA9037D0A91
384 changed files with 11136 additions and 8033 deletions

View File

@ -1,7 +1,8 @@
{
"extends": "eslint-config-discourse",
"rules": {
"discourse-ember/global-ember": 2
"discourse-ember/global-ember": 2,
"no-duplicate-imports": 2
},
"globals": {
"moduleFor": "off",

View File

@ -49,3 +49,6 @@ bc97c79a35d8acd283d4d8b79aa079bce9d127c6
# REFACTOR: Move javascript tests inside discourse app
23f24bfb510edb25b18b6a0d5485270c88df9b24
# DEV: Tidy up imports. (#11364)
1c2358ba162eb9f9ba9095c9afe30cf51dd85e04

View File

@ -172,7 +172,7 @@ GEM
logstash-logger (0.26.1)
logstash-event (~> 1.2)
logster (2.9.4)
loofah (2.7.0)
loofah (2.8.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
lru_redux (1.1.0)
@ -235,7 +235,7 @@ GEM
omniauth-twitter (1.4.0)
omniauth-oauth (~> 1.1)
rack
onebox (2.1.6)
onebox (2.1.7)
addressable (~> 2.7.0)
htmlentities (~> 4.3)
multi_json (~> 1.11)
@ -244,8 +244,8 @@ GEM
sanitize
openssl-signature_algorithm (1.0.0)
optimist (3.0.1)
parallel (1.20.0)
parallel_tests (3.3.0)
parallel (1.20.1)
parallel_tests (3.4.0)
parallel
parser (2.7.2.0)
ast (~> 2.4.1)
@ -275,8 +275,9 @@ GEM
nokogiri (>= 1.6)
rails-html-sanitizer (1.3.0)
loofah (~> 2.3)
rails_failover (0.6.0)
rails_failover (0.6.2)
activerecord (~> 6.0)
concurrent-ruby
railties (~> 6.0)
rails_multisite (2.5.0)
activerecord (> 5.0, < 7)
@ -298,10 +299,10 @@ GEM
msgpack (>= 0.4.3)
optimist (>= 3.0.0)
rchardet (1.8.0)
redis (4.2.2)
redis (4.2.5)
redis-namespace (1.8.0)
redis (>= 3.0.4)
regexp_parser (1.8.2)
regexp_parser (2.0.0)
request_store (1.5.0)
rack (>= 1.4)
rexml (3.2.4)
@ -340,7 +341,7 @@ GEM
json-schema (~> 2.2)
railties (>= 3.1, < 7.0)
rtlit (0.0.5)
rubocop (1.3.1)
rubocop (1.4.2)
parallel (~> 1.10)
parser (>= 2.7.1.5)
rainbow (>= 2.2.2, < 4.0)
@ -349,7 +350,7 @@ GEM
rubocop-ast (>= 1.1.1)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 2.0)
rubocop-ast (1.1.1)
rubocop-ast (1.2.0)
parser (>= 2.7.1.5)
rubocop-discourse (2.4.1)
rubocop (>= 1.1.0)
@ -386,10 +387,12 @@ GEM
connection_pool (>= 2.2.2)
rack (~> 2.0)
redis (>= 4.2.0)
simplecov (0.19.1)
simplecov (0.20.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.2)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)

View File

@ -6,8 +6,7 @@ import Component from "@ember/component";
import UserField from "admin/models/user-field";
import { bufferedProperty } from "discourse/mixins/buffered-content";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { propertyEqual } from "discourse/lib/computed";
import { i18n } from "discourse/lib/computed";
import { propertyEqual, i18n } from "discourse/lib/computed";
import discourseComputed, {
observes,
on,

View File

@ -1,11 +1,13 @@
import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators";
import discourseComputed, {
on,
observes,
} from "discourse-common/utils/decorators";
import { isEmpty } from "@ember/utils";
import { or } from "@ember/object/computed";
import { schedule } from "@ember/runloop";
import Component from "@ember/component";
import { bufferedProperty } from "discourse/mixins/buffered-content";
import { on, observes } from "discourse-common/utils/decorators";
import { popupAjaxError } from "discourse/lib/ajax-error";
import Category from "discourse/models/category";
import bootbox from "bootbox";

View File

@ -1,42 +1,44 @@
import I18n from "I18n";
import Component from "@ember/component";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
import { action } from "@ember/object";
import discourseComputed from "discourse-common/utils/decorators";
export default Component.extend({
classNames: ["inline-edit"],
checked: null,
checkedInternal: null,
buffer: null,
bufferModelId: null,
init() {
didReceiveAttrs() {
this._super(...arguments);
this.set("checkedInternal", this.checked);
if (this.modelId !== this.bufferModelId) {
// HACK: The condition above ensures this method is called only when its
// attributes are changed (i.e. only when `checked` changes).
//
// Reproduction steps: navigate to theme #1, switch to theme #2 from the
// left-side panel, then switch back to theme #1 and click on the <input>
// element wrapped by this component. It will call `didReceiveAttrs` even
// though none of the attributes have changed (only `buffer` does).
this.setProperties({
buffer: this.checked,
bufferModelId: this.modelId,
});
}
},
@observes("checked")
checkedChanged() {
this.set("checkedInternal", this.checked);
@discourseComputed("checked", "buffer")
changed(checked, buffer) {
return !!checked !== !!buffer;
},
@discourseComputed("labelKey")
label(key) {
return I18n.t(key);
@action
apply() {
this.set("checked", this.buffer);
this.action();
},
@discourseComputed("checked", "checkedInternal")
changed(checked, checkedInternal) {
return !!checked !== !!checkedInternal;
},
actions: {
cancelled() {
this.set("checkedInternal", this.checked);
},
finished() {
this.set("checked", this.checkedInternal);
this.action();
},
@action
cancel() {
this.set("buffer", this.checked);
},
});

View File

@ -1,8 +1,9 @@
import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators";
import discourseComputed, {
afterRender,
} from "discourse-common/utils/decorators";
import { equal } from "@ember/object/computed";
import Component from "@ember/component";
import { afterRender } from "discourse-common/utils/decorators";
const ACTIONS = ["delete", "delete_replies", "edit", "none"];

View File

@ -1,8 +1,9 @@
import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators";
import discourseComputed, { on } from "discourse-common/utils/decorators";
import { schedule } from "@ember/runloop";
import Component from "@ember/component";
import bootbox from "bootbox";
import ScreenedIpAddress from "admin/models/screened-ip-address";
/**
A form to create an IP address that will be blocked or allowed.
@ -15,9 +16,6 @@ import bootbox from "bootbox";
as an argument.
**/
import ScreenedIpAddress from "admin/models/screened-ip-address";
import { on } from "discourse-common/utils/decorators";
export default Component.extend({
classNames: ["screened-ip-address-form"],
formSubmitted: false,

View File

@ -1,8 +1,7 @@
import discourseComputed from "discourse-common/utils/decorators";
import discourseComputed, { on } from "discourse-common/utils/decorators";
import { makeArray } from "discourse-common/lib/helpers";
import { empty, reads } from "@ember/object/computed";
import Component from "@ember/component";
import { on } from "discourse-common/utils/decorators";
export default Component.extend({
classNameBindings: [":value-list"],

View File

@ -1,10 +1,9 @@
import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators";
import Controller from "@ember/controller";
import Controller, { inject as controller } from "@ember/controller";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { bufferedProperty } from "discourse/mixins/buffered-content";
import { action } from "@ember/object";
import { inject as controller } from "@ember/controller";
import bootbox from "bootbox";
export default Controller.extend(bufferedProperty("emailTemplate"), {

View File

@ -151,6 +151,15 @@ export default Controller.extend({
hasTranslations: notEmpty("translations"),
@discourseComputed(
"model.remote_theme.local_version",
"model.remote_theme.remote_version",
"model.remote_theme.commits_behind"
)
hasOverwrittenHistory(localVersion, remoteVersion, commitsBehind) {
return localVersion !== remoteVersion && commitsBehind === -1;
},
@discourseComputed("model.remoteError", "updatingRemote")
showRemoteError(errorMessage, updating) {
return errorMessage && !updating;

View File

@ -1,8 +1,7 @@
import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators";
import { makeArray } from "discourse-common/lib/helpers";
import { inject } from "@ember/controller";
import Controller from "@ember/controller";
import Controller, { inject } from "@ember/controller";
import { setting } from "discourse/lib/computed";
import AdminDashboard from "admin/models/admin-dashboard";
import Report from "admin/models/report";

View File

@ -4,11 +4,10 @@ import { inject as service } from "@ember/service";
import Controller from "@ember/controller";
import { ajax } from "discourse/lib/ajax";
import CanCheckEmails from "discourse/mixins/can-check-emails";
import { propertyNotEqual, setting } from "discourse/lib/computed";
import { propertyNotEqual, setting, fmt } from "discourse/lib/computed";
import { userPath } from "discourse/lib/url";
import { popupAjaxError } from "discourse/lib/ajax-error";
import discourseComputed from "discourse-common/utils/decorators";
import { fmt } from "discourse/lib/computed";
import { htmlSafe } from "@ember/template";
import showModal from "discourse/lib/show-modal";
import bootbox from "bootbox";

View File

@ -4,13 +4,13 @@ import discourseComputed from "discourse-common/utils/decorators";
import { makeArray } from "discourse-common/lib/helpers";
import { isEmpty } from "@ember/utils";
import EmberObject from "@ember/object";
import { escapeExpression } from "discourse/lib/utilities";
import { ajax } from "discourse/lib/ajax";
import round from "discourse/lib/round";
import {
fillMissingDates,
formatUsername,
toNumber,
escapeExpression,
} from "discourse/lib/utilities";
import { number, durationTiny } from "discourse/lib/formatter";
import { renderAvatar } from "discourse/helpers/user-avatar";

View File

@ -1,8 +1,8 @@
<label class="checkbox-label">
{{input type="checkbox" disabled=disabled checked=checkedInternal}}
{{label}}
{{input type="checkbox" disabled=disabled checked=buffer}}
{{i18n labelKey}}
</label>
{{#if changed}}
{{d-button action=(action "finished") class="btn-primary btn-small submit-edit" icon="check"}}
{{d-button action=(action "cancelled") class="btn-small cancel-edit" icon="times"}}
{{d-button action=(action "apply") class="btn-primary btn-small submit-edit" icon="check"}}
{{d-button action=(action "cancel") class="btn-small cancel-edit" icon="times"}}
{{/if}}

View File

@ -42,7 +42,7 @@
<div class="admin-controls">
{{#if model.theme_id}}
{{inline-edit-checkbox action=(action "applyUserSelectable") labelKey="admin.customize.theme.color_scheme_user_selectable" checked=model.user_selectable}}
{{inline-edit-checkbox action=(action "applyUserSelectable") labelKey="admin.customize.theme.color_scheme_user_selectable" checked=model.user_selectable modelId=model.id}}
{{else}}
<label>
{{input type="checkbox" checked=model.user_selectable}}

View File

@ -105,7 +105,11 @@
{{i18n "admin.customize.theme.updating"}}
{{else}}
{{#if model.remote_theme.commits_behind}}
{{i18n "admin.customize.theme.commits_behind" count=model.remote_theme.commits_behind}}
{{#if hasOverwrittenHistory}}
{{i18n "admin.customize.theme.has_overwritten_history"}}
{{else}}
{{i18n "admin.customize.theme.commits_behind" count=model.remote_theme.commits_behind}}
{{/if}}
{{#if model.remote_theme.github_diff_link}}
<a href={{model.remote_theme.github_diff_link}}>
{{i18n "admin.customize.theme.compare_commits"}}
@ -137,11 +141,11 @@
{{#if showCheckboxes}}
<div class="control-unit">
{{#unless model.component}}
{{inline-edit-checkbox action=(action "applyDefault") labelKey="admin.customize.theme.is_default" checked=model.default}}
{{inline-edit-checkbox action=(action "applyUserSelectable") labelKey="admin.customize.theme.user_selectable" checked=model.user_selectable}}
{{inline-edit-checkbox action=(action "applyDefault") labelKey="admin.customize.theme.is_default" checked=model.default modelId=model.id}}
{{inline-edit-checkbox action=(action "applyUserSelectable") labelKey="admin.customize.theme.user_selectable" checked=model.user_selectable modelId=model.id}}
{{/unless}}
{{#if model.remote_theme}}
{{inline-edit-checkbox action=(action "applyAutoUpdateable") labelKey="admin.customize.theme.auto_update" checked=model.auto_update}}
{{inline-edit-checkbox action=(action "applyAutoUpdateable") labelKey="admin.customize.theme.auto_update" checked=model.auto_update modelId=model.id}}
{{/if}}
</div>
{{/if}}

View File

@ -50,6 +50,13 @@ export const POPULAR_THEMES = [
meta_url: "https://meta.discourse.org/t/custom-header-links/90588",
component: true,
},
{
name: "Gifs Search",
value: "https://github.com/discourse/discourse-gifs",
description: "Adds a button to easily search and insert GIFs in posts.",
meta_url: "https://meta.discourse.org/t/discourse-gifs-component/158738",
component: true,
},
{
name: "Category Banners",
value: "https://github.com/discourse/discourse-category-banners",
@ -85,14 +92,6 @@ export const POPULAR_THEMES = [
meta_url: "https://meta.discourse.org/t/header-submenus/94584",
component: true,
},
{
name: "Alternative Logos",
value: "https://github.com/discourse/discourse-alt-logo",
description: "Add alternative logos for dark / light themes.",
meta_url:
"https://meta.discourse.org/t/alternative-logo-for-dark-themes/88502",
component: true,
},
{
name: "Automatic Table of Contents",
value: "https://github.com/discourse/DiscoTOC",

View File

@ -1,6 +1,7 @@
import I18n from "I18n";
import { h } from "virtual-dom";
import attributeHook from "discourse-common/lib/attribute-hook";
import { isDevelopment } from "discourse-common/config/environment";
const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
let _renderers = [];
@ -114,8 +115,12 @@ export function setIconList(iconList) {
_iconList = iconList;
}
export function isExistingIconId(id) {
return _iconList && _iconList.indexOf(id) >= 0;
}
function warnIfMissing(id) {
if (warnMissingIcons && _iconList && _iconList.indexOf(id) === -1) {
if (warnMissingIcons && isDevelopment() && !isExistingIconId(id)) {
console.warn(`The icon "${id}" is missing from the SVG subset.`); // eslint-disable-line no-console
}
}

View File

@ -3,8 +3,7 @@ import isDescriptor from "discourse-common/utils/is-descriptor";
import extractValue from "discourse-common/utils/extract-value";
import decoratorAlias from "discourse-common/utils/decorator-alias";
import macroAlias from "discourse-common/utils/macro-alias";
import { schedule, next } from "@ember/runloop";
import { bind as emberBind } from "@ember/runloop";
import { schedule, next, bind as emberBind } from "@ember/runloop";
export default function discourseComputedDecorator(...params) {
// determine if user called as @discourseComputed('blah', 'blah') or @discourseComputed

View File

@ -1,6 +1,5 @@
import { ajax } from "discourse/lib/ajax";
import RestAdapter from "discourse/adapters/rest";
import { Result } from "discourse/adapters/rest";
import RestAdapter, { Result } from "discourse/adapters/rest";
import { underscore } from "@ember/string";
export default RestAdapter.extend({

View File

@ -1,7 +1,6 @@
import I18n from "I18n";
import { computed } from "@ember/object";
import { computed, action } from "@ember/object";
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
import { action } from "@ember/object";
export default DropdownSelectBoxComponent.extend({
classNames: ["bookmark-actions-dropdown"],

View File

@ -0,0 +1,123 @@
import I18n from "I18n";
import Component from "@ember/component";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
import PermissionType from "discourse/models/permission-type";
import { equal, alias } from "@ember/object/computed";
const EVERYONE = "everyone";
export default Component.extend({
classNames: ["permission-row", "row-body"],
canCreate: equal("type", PermissionType.FULL),
everyonePermissionType: alias("everyonePermission.permission_type"),
@discourseComputed("type")
canReply(value) {
return (
value === PermissionType.CREATE_POST || value === PermissionType.FULL
);
},
@discourseComputed("type")
canReplyIcon() {
return this.canReply ? "check-square" : "far-square";
},
@discourseComputed("type")
canCreateIcon() {
return this.canCreate ? "check-square" : "far-square";
},
@discourseComputed("type")
replyGranted() {
return this.type <= PermissionType.CREATE_POST ? "reply-granted" : "";
},
@discourseComputed("type")
createGranted() {
return this.type === PermissionType.FULL ? "create-granted" : "";
},
@observes("everyonePermissionType")
inheritFromEveryone() {
if (this.group_name === EVERYONE) {
return;
}
// groups cannot have a lesser permission than "everyone"
if (this.everyonePermissionType < this.type) {
this.updatePermission(this.everyonePermissionType);
}
},
@discourseComputed("everyonePermissionType", "type")
replyDisabled(everyonePermissionType) {
if (
this.group_name !== EVERYONE &&
everyonePermissionType &&
everyonePermissionType <= PermissionType.CREATE_POST
) {
return true;
}
return false;
},
@discourseComputed("replyDisabled")
replyTooltip() {
return this.replyDisabled
? I18n.t("category.permissions.inherited")
: I18n.t("category.permissions.toggle_reply");
},
@discourseComputed("everyonePermissionType", "type")
createDisabled(everyonePermissionType) {
if (
this.group_name !== EVERYONE &&
everyonePermissionType &&
everyonePermissionType === PermissionType.FULL
) {
return true;
}
return false;
},
@discourseComputed("createDisabled")
createTooltip() {
return this.createDisabled
? I18n.t("category.permissions.inherited")
: I18n.t("category.permissions.toggle_full");
},
updatePermission(type) {
this.category.updatePermission(this.group_name, type);
},
actions: {
removeRow() {
this.category.removePermission(this.group_name);
},
setPermissionReply() {
if (this.type <= PermissionType.CREATE_POST) {
this.updatePermission(PermissionType.READONLY);
} else {
this.updatePermission(PermissionType.CREATE_POST);
}
},
setPermissionFull() {
if (
this.group_name !== EVERYONE &&
this.everyonePermissionType === PermissionType.FULL
) {
return;
}
if (this.type === PermissionType.FULL) {
this.updatePermission(PermissionType.CREATE_POST);
} else {
this.updatePermission(PermissionType.FULL);
}
},
},
});

View File

@ -3,8 +3,7 @@ import { next } from "@ember/runloop";
import Component from "@ember/component";
import discourseDebounce from "discourse/lib/debounce";
import { searchForTerm } from "discourse/lib/search";
import { observes } from "discourse-common/utils/decorators";
import discourseComputed from "discourse-common/utils/decorators";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
export default Component.extend({
loading: null,

View File

@ -1,6 +1,6 @@
import getURL from "discourse-common/lib/get-url";
import I18n from "I18n";
import { debounce, later, next, schedule, throttle } from "@ember/runloop";
import { run, debounce, later, next, schedule, throttle } from "@ember/runloop";
import Component from "@ember/component";
import userSearch from "discourse/lib/user-search";
import discourseComputed, {
@ -693,63 +693,73 @@ export default Component.extend({
const isUploading = validateUploadedFiles(data.files, opts);
this.setProperties({ uploadProgress: 0, isUploading });
run(() => {
this.setProperties({ uploadProgress: 0, isUploading });
});
return isUploading;
});
$element.on("fileuploadprogressall", (e, data) => {
this.set(
"uploadProgress",
parseInt((data.loaded / data.total) * 100, 10)
);
run(() => {
this.set(
"uploadProgress",
parseInt((data.loaded / data.total) * 100, 10)
);
});
});
$element.on("fileuploadsend", (e, data) => {
this._pasted = false;
this._validUploads++;
run(() => {
this._pasted = false;
this._validUploads++;
this._setUploadPlaceholderSend(data);
this._setUploadPlaceholderSend(data);
this.appEvents.trigger("composer:insert-text", this.uploadPlaceholder);
this.appEvents.trigger("composer:insert-text", this.uploadPlaceholder);
if (data.xhr && data.originalFiles.length === 1) {
this.set("isCancellable", true);
this._xhr = data.xhr();
}
if (data.xhr && data.originalFiles.length === 1) {
this.set("isCancellable", true);
this._xhr = data.xhr();
}
});
});
$element.on("fileuploaddone", (e, data) => {
let upload = data.result;
this._setUploadPlaceholderDone(data);
if (!this._xhr || !this._xhr._userCancelled) {
const markdown = uploadMarkdownResolvers.reduce(
(md, resolver) => resolver(upload) || md,
getUploadMarkdown(upload)
);
run(() => {
let upload = data.result;
this._setUploadPlaceholderDone(data);
if (!this._xhr || !this._xhr._userCancelled) {
const markdown = uploadMarkdownResolvers.reduce(
(md, resolver) => resolver(upload) || md,
getUploadMarkdown(upload)
);
cacheShortUploadUrl(upload.short_url, upload);
this.appEvents.trigger(
"composer:replace-text",
this.uploadPlaceholder.trim(),
markdown
);
this._resetUpload(false);
} else {
this._resetUpload(true);
}
cacheShortUploadUrl(upload.short_url, upload);
this.appEvents.trigger(
"composer:replace-text",
this.uploadPlaceholder.trim(),
markdown
);
this._resetUpload(false);
} else {
this._resetUpload(true);
}
});
});
$element.on("fileuploadfail", (e, data) => {
this._setUploadPlaceholderDone(data);
this._resetUpload(true);
run(() => {
this._setUploadPlaceholderDone(data);
this._resetUpload(true);
const userCancelled = this._xhr && this._xhr._userCancelled;
this._xhr = null;
const userCancelled = this._xhr && this._xhr._userCancelled;
this._xhr = null;
if (!userCancelled) {
displayErrorForUpload(data, this.siteSettings);
}
if (!userCancelled) {
displayErrorForUpload(data, this.siteSettings);
}
});
});
if (this.site.mobileView) {

View File

@ -1,8 +1,7 @@
import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
import { alias } from "@ember/object/computed";
import Component from "@ember/component";
import { observes } from "discourse-common/utils/decorators";
import LivePostCounts from "discourse/models/live-post-counts";
import { htmlSafe } from "@ember/template";

View File

@ -10,7 +10,6 @@ import discourseComputed, {
} from "discourse-common/utils/decorators";
import { categoryHashtagTriggerRule } from "discourse/lib/category-hashtags";
import { search as searchCategoryTag } from "discourse/lib/category-tag-search";
import { generateCookFunction } from "discourse/lib/text";
import { getRegister } from "discourse-common/lib/get-owner";
import { findRawTemplate } from "discourse-common/lib/raw-templates";
import { siteDir } from "discourse/lib/text-direction";
@ -26,7 +25,7 @@ import deprecated from "discourse-common/lib/deprecated";
import { wantsNewWindow } from "discourse/lib/intercept-click";
import { translations } from "pretty-text/emoji/data";
import { emojiSearch, isSkinTonableEmoji } from "pretty-text/emoji";
import { emojiUrlFor } from "discourse/lib/text";
import { emojiUrlFor, generateCookFunction } from "discourse/lib/text";
import showModal from "discourse/lib/show-modal";
import { Promise } from "rsvp";
import { isTesting } from "discourse-common/config/environment";

View File

@ -1,78 +1,38 @@
import { buildCategoryPanel } from "discourse/components/edit-category-panel";
import PermissionType from "discourse/models/permission-type";
import { on } from "discourse-common/utils/decorators";
import discourseComputed from "discourse-common/utils/decorators";
import { not } from "@ember/object/computed";
export default buildCategoryPanel("security", {
editingPermissions: false,
selectedGroup: null,
selectedPermission: null,
showPendingGroupChangesAlert: false,
interactedWithDropdowns: false,
noGroupSelected: not("selectedGroup"),
@on("init")
_setup() {
this.setProperties({
selectedGroup: this.get("category.availableGroups.firstObject"),
selectedPermission: this.get(
"category.availablePermissions.firstObject.id"
),
});
@discourseComputed("category.permissions.@each.permission_type")
everyonePermission(permissions) {
return permissions.findBy("group_name", "everyone");
},
@on("init")
_registerValidator() {
this.registerValidator(() => {
if (
!this.showPendingGroupChangesAlert &&
this.interactedWithDropdowns &&
this.activeTab
) {
this.set("showPendingGroupChangesAlert", true);
return true;
}
});
@discourseComputed("category.permissions.@each.permission_type")
everyoneGrantedFull() {
return (
this.everyonePermission &&
this.everyonePermission.permission_type === PermissionType.FULL
);
},
@discourseComputed("everyonePermission")
minimumPermission(everyonePermission) {
return everyonePermission
? everyonePermission.permission_type
: PermissionType.READONLY;
},
actions: {
onSelectGroup(selectedGroup) {
this.setProperties({
interactedWithDropdowns: true,
selectedGroup,
onSelectGroup(group_name) {
this.category.addPermission({
group_name,
permission_type: this.minimumPermission,
});
},
onSelectPermission(selectedPermission) {
this.setProperties({
interactedWithDropdowns: true,
selectedPermission,
});
},
editPermissions() {
if (!this.get("category.is_special")) {
this.set("editingPermissions", true);
}
},
addPermission(group, id) {
if (!this.get("category.is_special")) {
this.category.addPermission({
group_name: group + "",
permission: PermissionType.create({ id: parseInt(id, 10) }),
});
}
this.setProperties({
selectedGroup: this.get("category.availableGroups.firstObject"),
showPendingGroupChangesAlert: false,
interactedWithDropdowns: false,
});
},
removePermission(permission) {
if (!this.get("category.is_special")) {
this.category.removePermission(permission);
}
},
},
});

View File

@ -6,6 +6,7 @@ import { propertyEqual } from "discourse/lib/computed";
import getURL from "discourse-common/lib/get-url";
import { empty } from "@ember/object/computed";
import DiscourseURL from "discourse/lib/url";
import { underscore } from "@ember/string";
export default Component.extend({
tagName: "li",
@ -21,7 +22,7 @@ export default Component.extend({
@discourseComputed("tab")
title(tab) {
return I18n.t("category." + tab.replace("-", "_"));
return I18n.t(`category.${underscore(tab)}`);
},
didInsertElement() {

View File

@ -1,20 +1,17 @@
import { observes } from "discourse-common/utils/decorators";
import { bind } from "discourse-common/utils/decorators";
import { observes, bind } from "discourse-common/utils/decorators";
import { htmlSafe } from "@ember/template";
import { emojiUnescape } from "discourse/lib/text";
import { escapeExpression } from "discourse/lib/utilities";
import { emojiUnescape, emojiUrlFor } from "discourse/lib/text";
import { action, computed } from "@ember/object";
import { inject as service } from "@ember/service";
import { schedule, later } from "@ember/runloop";
import Component from "@ember/component";
import { emojiUrlFor } from "discourse/lib/text";
import { createPopper } from "@popperjs/core";
import {
extendedEmojiList,
isSkinTonableEmoji,
emojiSearch,
} from "pretty-text/emoji";
import { safariHacksDisabled } from "discourse/lib/utilities";
import { safariHacksDisabled, escapeExpression } from "discourse/lib/utilities";
function customEmojis() {
const list = extendedEmojiList();

View File

@ -1,9 +1,11 @@
import getURL from "discourse-common/lib/get-url";
import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators";
import discourseComputed, {
on,
observes,
} from "discourse-common/utils/decorators";
import { debounce } from "@ember/runloop";
import Component from "@ember/component";
import { on, observes } from "discourse-common/utils/decorators";
import { escapeExpression } from "discourse/lib/utilities";
import { convertIconClass } from "discourse-common/lib/icon-library";
import { ajax } from "discourse/lib/ajax";

View File

@ -1,6 +1,5 @@
import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators";
import { on } from "discourse-common/utils/decorators";
import discourseComputed, { on } from "discourse-common/utils/decorators";
import TextField from "discourse/components/text-field";
import { applySearchAutocomplete } from "discourse/lib/search";

View File

@ -7,7 +7,6 @@ import DiscourseURL from "discourse/lib/url";
import { findRawTemplate } from "discourse-common/lib/raw-templates";
import { wantsNewWindow } from "discourse/lib/intercept-click";
import { on } from "@ember/object/evented";
import { topicTitleDecorators } from "discourse/components/topic-title";
export function showEntrance(e) {

View File

@ -1,9 +1,8 @@
import I18n from "I18n";
import { schedule } from "@ember/runloop";
import { schedule, next } from "@ember/runloop";
import { and, or } from "@ember/object/computed";
import { next } from "@ember/runloop";
import { action } from "@ember/object";
import { isPresent } from "@ember/utils";
import { isPresent, isEmpty } from "@ember/utils";
import Controller from "@ember/controller";
import { Promise } from "rsvp";
import ModalFunctionality from "discourse/mixins/modal-functionality";
@ -14,7 +13,6 @@ import KeyboardShortcuts from "discourse/lib/keyboard-shortcuts";
import { formattedReminderTime, REMINDER_TYPES } from "discourse/lib/bookmark";
import { AUTO_DELETE_PREFERENCES } from "discourse/models/bookmark";
import bootbox from "bootbox";
import { isEmpty } from "@ember/utils";
// global shortcuts that interfere with these modal shortcuts, they are rebound when the
// modal is closed

View File

@ -3,8 +3,7 @@ import discourseComputed from "discourse-common/utils/decorators";
import { isEmpty } from "@ember/utils";
import { alias } from "@ember/object/computed";
import { next } from "@ember/runloop";
import { inject } from "@ember/controller";
import Controller from "@ember/controller";
import Controller, { inject } from "@ember/controller";
import ModalFunctionality from "discourse/mixins/modal-functionality";
import DiscourseURL from "discourse/lib/url";
import Topic from "discourse/models/topic";

View File

@ -2,8 +2,7 @@ import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators";
import { isEmpty } from "@ember/utils";
import { next } from "@ember/runloop";
import { inject } from "@ember/controller";
import Controller from "@ember/controller";
import Controller, { inject } from "@ember/controller";
import ModalFunctionality from "discourse/mixins/modal-functionality";
import DiscourseURL from "discourse/lib/url";
import Topic from "discourse/models/topic";

View File

@ -2,14 +2,12 @@ import getURL from "discourse-common/lib/get-url";
import I18n from "I18n";
import { isEmpty } from "@ember/utils";
import { and, or, alias, reads } from "@ember/object/computed";
import { cancel, debounce } from "@ember/runloop";
import { cancel, debounce, run } from "@ember/runloop";
import { inject as service } from "@ember/service";
import { inject } from "@ember/controller";
import Controller from "@ember/controller";
import Controller, { inject } from "@ember/controller";
import DiscourseURL from "discourse/lib/url";
import { buildQuote } from "discourse/lib/quote";
import Draft from "discourse/models/draft";
import Composer from "discourse/models/composer";
import discourseComputed, {
observes,
on,
@ -22,7 +20,7 @@ import {
} from "discourse/lib/uploads";
import { emojiUnescape } from "discourse/lib/text";
import { shortDate } from "discourse/lib/formatter";
import { SAVE_LABELS, SAVE_ICONS } from "discourse/models/composer";
import Composer, { SAVE_LABELS, SAVE_ICONS } from "discourse/models/composer";
import { Promise } from "rsvp";
import { isTesting } from "discourse-common/config/environment";
import EmberObject, { computed, action } from "@ember/object";
@ -1190,7 +1188,8 @@ export default Controller.extend({
if (Date.now() - this._lastDraftSaved > 15000) {
this._saveDraft();
} else {
this._saveDraftDebounce = debounce(this, this._saveDraft, 2000);
let method = isTesting() ? run : debounce;
this._saveDraftDebounce = method(this, this._saveDraft, 2000);
}
}
},

View File

@ -1,5 +1,4 @@
import { inject } from "@ember/controller";
import Controller from "@ember/controller";
import Controller, { inject } from "@ember/controller";
// Just add query params here to have them automatically passed to topic list filters.
export const queryParams = {

View File

@ -7,6 +7,7 @@ import DiscourseURL from "discourse/lib/url";
import { readOnly } from "@ember/object/computed";
import PermissionType from "discourse/models/permission-type";
import { NotificationLevels } from "discourse/lib/notification-levels";
import { underscore } from "@ember/string";
export default Controller.extend({
selectedTab: "general",
@ -69,6 +70,11 @@ export default Controller.extend({
: I18n.t("category.create");
},
@discourseComputed("selectedTab")
selectedTabTitle(tab) {
return I18n.t(`category.${underscore(tab)}`);
},
actions: {
registerValidator(validator) {
this.validators.push(validator);

View File

@ -1,7 +1,6 @@
import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators";
import { inject } from "@ember/controller";
import Controller from "@ember/controller";
import Controller, { inject } from "@ember/controller";
import { ajax } from "discourse/lib/ajax";
import ModalFunctionality from "discourse/mixins/modal-functionality";
import { categoryLinkHTML } from "discourse/helpers/category-link";

View File

@ -1,7 +1,6 @@
import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators";
import { inject } from "@ember/controller";
import Controller from "@ember/controller";
import Controller, { inject } from "@ember/controller";
import { extractError } from "discourse/lib/ajax-error";
import ModalFunctionality from "discourse/mixins/modal-functionality";
import GrantBadgeController from "discourse/mixins/grant-badge-controller";

View File

@ -1,11 +1,13 @@
import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators";
import discourseComputed, {
on,
observes,
} from "discourse-common/utils/decorators";
import { alias, gt, not, or, equal } from "@ember/object/computed";
import Controller from "@ember/controller";
import ModalFunctionality from "discourse/mixins/modal-functionality";
import { categoryBadgeHTML } from "discourse/helpers/category-link";
import { propertyGreaterThan, propertyLessThan } from "discourse/lib/computed";
import { on, observes } from "discourse-common/utils/decorators";
import { sanitizeAsync } from "discourse/lib/text";
import { iconHTML } from "discourse-common/lib/icon-library";
import Post from "discourse/models/post";

View File

@ -2,8 +2,7 @@ import I18n from "I18n";
import { isEmpty } from "@ember/utils";
import { alias, equal } from "@ember/object/computed";
import { next } from "@ember/runloop";
import { inject } from "@ember/controller";
import Controller from "@ember/controller";
import Controller, { inject } from "@ember/controller";
import ModalFunctionality from "discourse/mixins/modal-functionality";
import { movePosts, mergeTopic } from "discourse/models/topic";
import DiscourseURL from "discourse/lib/url";

View File

@ -3,10 +3,9 @@ import I18n from "I18n";
import { alias, or, readOnly } from "@ember/object/computed";
import Controller from "@ember/controller";
import discourseComputed from "discourse-common/utils/decorators";
import DiscourseURL from "discourse/lib/url";
import DiscourseURL, { userPath } from "discourse/lib/url";
import { ajax } from "discourse/lib/ajax";
import PasswordValidation from "discourse/mixins/password-validation";
import { userPath } from "discourse/lib/url";
import { SECOND_FACTOR_METHODS } from "discourse/models/user";
import { getWebauthnCredential } from "discourse/lib/webauthn";

View File

@ -1,7 +1,5 @@
import I18n from "I18n";
import { inject } from "@ember/controller";
import Controller from "@ember/controller";
import { setDefaultHomepage } from "discourse/lib/utilities";
import Controller, { inject } from "@ember/controller";
import discourseComputed from "discourse-common/utils/decorators";
import { listThemes, setLocalTheme } from "discourse/lib/theme-selector";
import {
@ -15,6 +13,7 @@ import {
safariHacksDisabled,
isiPad,
iOSWithVisualViewport,
setDefaultHomepage,
} from "discourse/lib/utilities";
import { computed } from "@ember/object";
import { reads } from "@ember/object/computed";

View File

@ -4,8 +4,7 @@ import { empty, or } from "@ember/object/computed";
import Controller from "@ember/controller";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
import { setting, propertyEqual } from "discourse/lib/computed";
import DiscourseURL from "discourse/lib/url";
import { userPath } from "discourse/lib/url";
import DiscourseURL, { userPath } from "discourse/lib/url";
import { popupAjaxError } from "discourse/lib/ajax-error";
import User from "discourse/models/user";
import bootbox from "bootbox";

View File

@ -6,7 +6,7 @@ import { next, schedule, later } from "@ember/runloop";
import Controller, { inject as controller } from "@ember/controller";
import { bufferedProperty } from "discourse/mixins/buffered-content";
import Composer from "discourse/models/composer";
import DiscourseURL from "discourse/lib/url";
import DiscourseURL, { userPath } from "discourse/lib/url";
import Post from "discourse/models/post";
import { buildQuote } from "discourse/lib/quote";
import QuoteState from "discourse/lib/quote-state";
@ -18,7 +18,6 @@ import discourseComputed, { observes } from "discourse-common/utils/decorators";
import { extractLinkMeta } from "discourse/lib/render-topic-featured-link";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { spinnerHTML } from "discourse/helpers/loading-spinner";
import { userPath } from "discourse/lib/url";
import showModal from "discourse/lib/show-modal";
import TopicTimer from "discourse/models/topic-timer";
import { Promise } from "rsvp";

View File

@ -1,7 +1,6 @@
import I18n from "I18n";
import Controller from "@ember/controller";
import Controller, { inject } from "@ember/controller";
import { Promise } from "rsvp";
import { inject } from "@ember/controller";
import { action } from "@ember/object";
import discourseComputed from "discourse-common/utils/decorators";
import Bookmark from "discourse/models/bookmark";

View File

@ -1,7 +1,6 @@
import discourseComputed from "discourse-common/utils/decorators";
import { alias } from "@ember/object/computed";
import { inject } from "@ember/controller";
import Controller from "@ember/controller";
import Controller, { inject } from "@ember/controller";
import { durationTiny } from "discourse/lib/formatter";
// should be kept in sync with 'UserSummary::MAX_BADGES'

View File

@ -3,8 +3,7 @@ import { isEmpty } from "@ember/utils";
import { alias, or, gt, not, and } from "@ember/object/computed";
import EmberObject, { set, computed } from "@ember/object";
import { inject as service } from "@ember/service";
import { inject } from "@ember/controller";
import Controller from "@ember/controller";
import Controller, { inject } from "@ember/controller";
import CanCheckEmails from "discourse/mixins/can-check-emails";
import User from "discourse/models/user";
import optionalService from "discourse/lib/optional-service";

View File

@ -54,8 +54,11 @@ export function categoryBadgeHTML(category, opts) {
const depth = (opts.depth || 1) + 1;
if (opts.recursive && depth <= siteSettings.max_category_nesting) {
const parentCategory = Category.findById(category.parent_category_id);
const lastSubcategory = !opts.depth;
opts.depth = depth;
return categoryBadgeHTML(parentCategory, opts) + _renderer(category, opts);
const parentBadges = categoryBadgeHTML(parentCategory, opts);
opts.lastSubcategory = lastSubcategory;
return parentBadges + _renderer(category, opts);
}
return _renderer(category, opts);
@ -183,5 +186,11 @@ function defaultCategoryLinkRenderer(category, opts) {
if (opts.topicCount && categoryStyle === "box") {
afterBadgeWrapper += buildTopicCount(opts.topicCount);
}
if (opts.plusSubcategories && opts.lastSubcategory) {
afterBadgeWrapper += `<span class="plus-subcategories">${I18n.t(
"category_row.plus_subcategories",
{ count: opts.plusSubcategories }
)}</span>`;
}
return `<${tagName} class="badge-wrapper ${extraClasses}" ${href}>${html}</${tagName}>${afterBadgeWrapper}`;
}

View File

@ -1,6 +1,5 @@
import { h } from "virtual-dom";
import { relativeAge, longDate } from "discourse/lib/formatter";
import { number } from "discourse/lib/formatter";
import { relativeAge, longDate, number } from "discourse/lib/formatter";
export function dateNode(dt) {
if (typeof dt === "string") {

View File

@ -1,5 +1,4 @@
import { isAbsoluteURL } from "discourse-common/lib/get-url";
import getAbsoluteURL from "discourse-common/lib/get-url";
import getAbsoluteURL, { isAbsoluteURL } from "discourse-common/lib/get-url";
export default {
name: "register-service-worker",

View File

@ -1,9 +1,8 @@
import { later, run } from "@ember/runloop";
import { later, run, throttle, schedule } from "@ember/runloop";
import DiscourseURL from "discourse/lib/url";
import Composer from "discourse/models/composer";
import { minimumOffset } from "discourse/lib/offset-calculator";
import { ajax } from "discourse/lib/ajax";
import { throttle, schedule } from "@ember/runloop";
import { INPUT_DELAY } from "discourse-common/config/environment";
import {
nextTopicUrl,

View File

@ -1,8 +1,11 @@
import I18n from "I18n";
import loadScript from "discourse/lib/load-script";
import { escapeExpression } from "discourse/lib/utilities";
import {
escapeExpression,
isAppWebview,
postRNWebviewMessage,
} from "discourse/lib/utilities";
import { renderIcon } from "discourse-common/lib/icon-library";
import { isAppWebview, postRNWebviewMessage } from "discourse/lib/utilities";
import { spinnerHTML } from "discourse/helpers/loading-spinner";
import User from "discourse/models/user";

View File

@ -1,4 +1,4 @@
import { default as getURL, getURLWithCDN } from "discourse-common/lib/get-url";
import getURL, { getURLWithCDN } from "discourse-common/lib/get-url";
import { run } from "@ember/runloop";
import { ajax } from "discourse/lib/ajax";
import { PUBLIC_JS_VERSIONS } from "discourse/lib/public-js-versions";

View File

@ -1,9 +1,7 @@
import deprecated from "discourse-common/lib/deprecated";
import { iconNode } from "discourse-common/lib/icon-library";
import { addDecorator } from "discourse/widgets/post-cooked";
import { addPluginOutletDecorator } from "discourse/components/plugin-connector";
import { addTopicTitleDecorator } from "discourse/components/topic-title";
import ComposerEditor from "discourse/components/composer-editor";
import DiscourseBanner from "discourse/components/discourse-banner";
import { addButton, removeButton } from "discourse/widgets/post-menu";
import { includeAttributes } from "discourse/lib/transform-post";
@ -16,8 +14,12 @@ import {
reopenWidget,
decorateWidget,
changeSetting,
queryRegistry,
} from "discourse/widgets/widget";
import { preventCloak } from "discourse/widgets/post-stream";
import {
preventCloak,
addPostTransformCallback,
} from "discourse/widgets/post-stream";
import { h } from "virtual-dom";
import { addPopupMenuOptionsCallback } from "discourse/controllers/composer";
import { extraConnectorClass } from "discourse/lib/plugin-connectors";
@ -27,7 +29,6 @@ import { addDiscoveryQueryParam } from "discourse/controllers/discovery-sortable
import { addTagsHtmlCallback } from "discourse/lib/render-tags";
import { addUserMenuGlyph } from "discourse/widgets/user-menu";
import { addPostClassesCallback } from "discourse/widgets/post";
import { addPostTransformCallback } from "discourse/widgets/post-stream";
import {
attachAdditionalPanel,
addToHeaderIcons,
@ -35,8 +36,8 @@ import {
import {
registerIconRenderer,
replaceIcon,
iconNode,
} from "discourse-common/lib/icon-library";
import { replaceCategoryLinkRenderer } from "discourse/helpers/category-link";
import { replaceTagRenderer } from "discourse/lib/render-tag";
import { addNavItem } from "discourse/models/nav-item";
import { replaceFormatter } from "discourse/lib/utilities";
@ -47,13 +48,15 @@ import { addUsernameSelectorDecorator } from "discourse/helpers/decorate-usernam
import { disableNameSuppression } from "discourse/widgets/poster-name";
import { registerCustomPostMessageCallback as registerCustomPostMessageCallback1 } from "discourse/controllers/topic";
import Sharing from "discourse/lib/sharing";
import {
import ComposerEditor, {
addComposerUploadHandler,
addComposerUploadMarkdownResolver,
} from "discourse/components/composer-editor";
import { addCategorySortCriteria } from "discourse/components/edit-category-settings";
import { addExtraIconRenderer } from "discourse/helpers/category-link";
import { queryRegistry } from "discourse/widgets/widget";
import {
addExtraIconRenderer,
replaceCategoryLinkRenderer,
} from "discourse/helpers/category-link";
import Composer from "discourse/models/composer";
import { on } from "@ember/object/evented";
import { addQuickAccessProfileItem } from "discourse/widgets/quick-access-profile";
@ -67,7 +70,7 @@ import {
} from "discourse/models/user";
// If you add any methods to the API ensure you bump up this number
const PLUGIN_API_VERSION = "0.11.0";
const PLUGIN_API_VERSION = "0.11.1";
class PluginApi {
constructor(version, container) {

View File

@ -273,7 +273,13 @@ export class Tag {
if (attr.href && text !== attr.href) {
text = text.replace(/\n{2,}/g, "\n");
return "[" + text + "](" + attr.href + ")";
let linkModifier = "";
if (attr.class && attr.class.includes("attachment")) {
linkModifier = "|attachment";
}
return "[" + text + linkModifier + "](" + attr.href + ")";
}
return text;

View File

@ -5,7 +5,7 @@ import offsetCalculator from "discourse/lib/offset-calculator";
import LockOn from "discourse/lib/lock-on";
import { defaultHomepage } from "discourse/lib/utilities";
import User from "discourse/models/user";
import { default as getURL, withoutPrefix } from "discourse-common/lib/get-url";
import getURL, { withoutPrefix } from "discourse-common/lib/get-url";
import Session from "discourse/models/session";
import { setOwner } from "@ember/application";
@ -16,6 +16,7 @@ const TOPIC_REGEXP = /\/t\/([^\/]+)\/(\d+)\/?(\d+)?/;
const SERVER_SIDE_ONLY = [
/^\/assets\//,
/^\/uploads\//,
/^\/secure-media-uploads\//,
/^\/stylesheets\//,
/^\/site_customizations\//,
/^\/raw\//,

View File

@ -2,7 +2,7 @@ import I18n from "I18n";
import { escape } from "pretty-text/sanitizer";
import toMarkdown from "discourse/lib/to-markdown";
import Handlebars from "handlebars";
import { default as getURL, getURLWithCDN } from "discourse-common/lib/get-url";
import getURL, { getURLWithCDN } from "discourse-common/lib/get-url";
import { helperContext } from "discourse-common/lib/helpers";
import { deepMerge } from "discourse-common/lib/object";

View File

@ -1,15 +1,16 @@
import getURL from "discourse-common/lib/get-url";
import discourseComputed from "discourse-common/utils/decorators";
import discourseComputed, { on } from "discourse-common/utils/decorators";
import { get } from "@ember/object";
import { ajax } from "discourse/lib/ajax";
import RestModel from "discourse/models/rest";
import { on } from "discourse-common/utils/decorators";
import PermissionType from "discourse/models/permission-type";
import { NotificationLevels } from "discourse/lib/notification-levels";
import Site from "discourse/models/site";
import User from "discourse/models/user";
import { getOwner } from "discourse-common/lib/get-owner";
const STAFF_GROUP_NAME = "staff";
const Category = RestModel.extend({
permissions: null,
@ -22,15 +23,13 @@ const Category = RestModel.extend({
this.set("availableGroups", availableGroups);
const groupPermissions = this.group_permissions;
if (groupPermissions) {
this.set(
"permissions",
groupPermissions.map((elem) => {
availableGroups.removeObject(elem.group_name);
return {
group_name: elem.group_name,
permission: PermissionType.create({ id: elem.permission_type }),
};
return elem;
})
);
}
@ -231,7 +230,12 @@ const Category = RestModel.extend({
_permissionsForUpdate() {
const permissions = this.permissions;
let rval = {};
permissions.forEach((p) => (rval[p.group_name] = p.permission.id));
if (permissions.length) {
permissions.forEach((p) => (rval[p.group_name] = p.permission_type));
} else {
// empty permissions => staff-only access
rval[STAFF_GROUP_NAME] = PermissionType.FULL;
}
return rval;
},
@ -246,9 +250,20 @@ const Category = RestModel.extend({
this.availableGroups.removeObject(permission.group_name);
},
removePermission(permission) {
this.permissions.removeObject(permission);
this.availableGroups.addObject(permission.group_name);
removePermission(group_name) {
const permission = this.permissions.findBy("group_name", group_name);
if (permission) {
this.permissions.removeObject(permission);
this.availableGroups.addObject(group_name);
}
},
updatePermission(group_name, type) {
this.permissions.forEach((p, i) => {
if (p.group_name === group_name) {
this.set(`permissions.${i}.permission_type`, type);
}
});
},
@discourseComputed("topics")

View File

@ -194,12 +194,7 @@ const Topic = RestModel.extend({
@discourseComputed("suggested_topics")
suggestedTopics(suggestedTopics) {
if (suggestedTopics) {
const store = this.store;
return this.set(
"suggested_topics",
suggestedTopics.map((st) => store.createRecord("topic", st))
);
return suggestedTopics.map((st) => this.store.createRecord("topic", st));
}
},

View File

@ -1,7 +1,6 @@
import discourseComputed from "discourse-common/utils/decorators";
import discourseComputed, { on } from "discourse-common/utils/decorators";
import { or, equal, and } from "@ember/object/computed";
import RestModel from "discourse/models/rest";
import { on } from "discourse-common/utils/decorators";
import UserActionGroup from "discourse/models/user-action-group";
import { postUrl } from "discourse/lib/utilities";
import { userPath } from "discourse/lib/url";

View File

@ -1,5 +1,4 @@
import { getURLWithCDN } from "discourse-common/lib/get-url";
import getURL from "discourse-common/lib/get-url";
import getURL, { getURLWithCDN } from "discourse-common/lib/get-url";
import I18n from "I18n";
import { A } from "@ember/array";
import { isEmpty } from "@ember/utils";
@ -22,14 +21,13 @@ import UserDraftsStream from "discourse/models/user-drafts-stream";
import Group from "discourse/models/group";
import { emojiUnescape } from "discourse/lib/text";
import PreloadStore from "discourse/lib/preload-store";
import { defaultHomepage } from "discourse/lib/utilities";
import { defaultHomepage, escapeExpression } from "discourse/lib/utilities";
import { userPath } from "discourse/lib/url";
import Category from "discourse/models/category";
import { Promise } from "rsvp";
import deprecated from "discourse-common/lib/deprecated";
import Site from "discourse/models/site";
import { NotificationLevels } from "discourse/lib/notification-levels";
import { escapeExpression } from "discourse/lib/utilities";
import { getOwner } from "discourse-common/lib/get-owner";
import cookie, { removeCookie } from "discourse/lib/cookie";

View File

@ -95,7 +95,7 @@ export default {
session.userDarkSchemeId = parseInt(setupData.userDarkSchemeId, 10) || -1;
if (isDevelopment()) {
setIconList(setupData.svgIconList);
setIconList(JSON.parse(setupData.svgIconList));
}
if (setupData.s3BaseUrl) {

View File

@ -185,7 +185,8 @@ export default DiscourseRoute.extend(FilterModeMixin, {
if (controller.get("list.draft")) {
this.openTopicDraft(controller.get("list"));
} else {
this.controllerFor("composer")
const composerController = this.controllerFor("composer");
composerController
.open({
categoryId: controller.get("category.id"),
action: Composer.CREATE_TOPIC,
@ -194,7 +195,7 @@ export default DiscourseRoute.extend(FilterModeMixin, {
})
.then(() => {
// Pre-fill the tags input field
if (controller.get("model.id")) {
if (composerController.canEditTags && controller.get("model.id")) {
const composerModel = this.controllerFor("composer").get("model");
composerModel.set(
"tags",

View File

@ -6,11 +6,10 @@ import DiscourseURL from "discourse/lib/url";
import { ID_CONSTRAINT } from "discourse/models/topic";
import { setTopicId } from "discourse/lib/topic-list-tracker";
import { inject as service } from "@ember/service";
import showModal from "discourse/lib/show-modal";
const SCROLL_DELAY = 500;
import showModal from "discourse/lib/show-modal";
const TopicRoute = DiscourseRoute.extend({
screenTrack: service(),

View File

@ -1,5 +1,4 @@
import Service from "@ember/service";
import { inject as service } from "@ember/service";
import Service, { inject as service } from "@ember/service";
import getURL from "discourse-common/lib/get-url";
import updateTabCount from "discourse/lib/update-tab-count";

View File

@ -3,7 +3,7 @@
{{category-drop
category=breadcrumb.category
categories=breadcrumb.options
tagId=tagId
tagId=tag.id
options=(hash
parentCategory=breadcrumb.parentCategory
subCategory=breadcrumb.isSubcategory

View File

@ -0,0 +1,29 @@
<span class="group-name">
<span class="group-name-label">{{group_name}}</span>
<a class="remove-permission" href {{action "removeRow"}}>
{{d-icon "far-trash-alt"}}
</a>
</span>
<span class="options actionable">
{{d-button
icon="check-square"
class="btn btn-flat see"
disabled=true
}}
{{d-button
icon=canReplyIcon
action=(action "setPermissionReply")
translatedTitle=replyTooltip
class=(concat "btn btn-flat reply-toggle " replyGranted)
disabled=replyDisabled
}}
{{d-button
icon=canCreateIcon
action=(action "setPermissionFull")
translatedTitle=createTooltip
class=(concat "btn btn-flat create-toggle " createGranted)
disabled=createDisabled
}}
</span>

View File

@ -12,14 +12,16 @@
<section class="field">
<label>{{i18n "category.parent"}}</label>
{{category-chooser
rootNone=true
value=category.parent_category_id
excludeCategoryId=category.id
categories=parentCategories
allowSubCategories=true
allowUncategorized=false
allowRestrictedCategories=true
onChange=(action (mut category.parent_category_id))
options=(hash
excludeCategoryId=category.id
none=true
)
}}
</section>
{{/if}}

View File

@ -6,62 +6,50 @@
<p class="warning">{{i18n "category.special_warning"}}</p>
{{/if}}
{{/if}}
{{#unless category.isUncategorizedCategory}}
<ul class="permission-list">
{{#unless category.is_special}}
<div class="category-permissions-table">
<div class="permission-row row-header">
<span class="group-name">{{i18n "category.permissions.group"}}</span>
<span class="options">
<span class="cell">{{i18n "category.permissions.see"}}</span>
<span class="cell">{{i18n "category.permissions.reply"}}</span>
<span class="cell">{{i18n "category.permissions.create"}}</span>
</span>
</div>
{{#each category.permissions as |p|}}
<li>
<span class="name"><span class="badge-group">{{p.group_name}}</span></span>
{{html-safe (i18n "category.can")}}
<span class="permission">{{p.permission.description}}</span>
{{#if editingPermissions}}
<a class="remove-permission" href {{action "removePermission" p}}>{{d-icon "times-circle"}}</a>
{{/if}}
</li>
{{category-permission-row group_name=p.group_name type=p.permission_type category=category everyonePermission=everyonePermission}}
{{/each}}
</ul>
{{/unless}}
{{#if editingPermissions}}
{{#if category.availableGroups}}
{{combo-box
class="available-groups"
content=category.availableGroups
onChange=(action "onSelectGroup")
value=selectedGroup
valueProperty=null
nameProperty=null
options=(hash
placementStrategy="absolute"
)
}}
{{combo-box
class="permission-selector"
nameProperty="description"
content=category.availablePermissions
onChange=(action "onSelectPermission")
value=selectedPermission
options=(hash
placementStrategy="absolute"
)
}}
{{d-button
action=(action "addPermission" selectedGroup selectedPermission)
class="btn-primary add-permission"
icon="plus"}}
{{#if showPendingGroupChangesAlert}}
<div class="pending-permission-change-alert">
<div class="arrow-div"></div>
{{i18n "category.pending_permission_change_alert" group=selectedGroup}}
{{#unless category.permissions}}
<div class="permission-row row-empty">
{{i18n "category.permissions.no_groups_selected"}}
</div>
{{/unless}}
{{#if category.availableGroups}}
<div class="add-group">
<span class="group-name">
{{combo-box
class="available-groups"
content=category.availableGroups
onChange=(action "onSelectGroup")
value=null
valueProperty=null
nameProperty=null
options=(hash
none="category.security_add_group"
)
}}
</span>
</div>
{{/if}}
</div>
{{#if everyoneGrantedFull}}
<p class="warning">{{i18n "category.permissions.everyone_has_access"}}</p>
{{/if}}
{{else}}
{{#unless category.is_special}}
{{d-button
action=(action "editPermissions")
class="btn-default edit-permission"
label="category.edit_permissions"}}
{{/unless}}
{{/if}}
{{/unless}}
</section>
{{plugin-outlet name="category-custom-security" args=(hash category=category) connectorTagName="" tagName="section"}}

View File

@ -1,6 +1,4 @@
<section>
<h3>{{i18n "category.settings_sections.general"}}</h3>
{{#if showPositionInput}}
<section class="field position-fields">
<label for="category-position">

View File

@ -1,2 +1 @@
<label>{{i18n "category.topic_template"}}</label>
{{d-editor value=category.topic_template showLink=showInsertLinkButton}}

View File

@ -0,0 +1,30 @@
{{!-- DO NOT EDIT THIS FILE!!! --}}
{{!-- Update it by running `rake javascript:update_constants` --}}
<button type="button" data-section="smileys_&_emotion" {{action onCategorySelection "smileys_&_emotion"}} class="btn btn-default category-button emoji">
{{replace-emoji ":grinning:"}}
</button>
<button type="button" data-section="people_&_body" {{action onCategorySelection "people_&_body"}} class="btn btn-default category-button emoji">
{{replace-emoji ":wave:"}}
</button>
<button type="button" data-section="animals_&_nature" {{action onCategorySelection "animals_&_nature"}} class="btn btn-default category-button emoji">
{{replace-emoji ":evergreen_tree:"}}
</button>
<button type="button" data-section="food_&_drink" {{action onCategorySelection "food_&_drink"}} class="btn btn-default category-button emoji">
{{replace-emoji ":hamburger:"}}
</button>
<button type="button" data-section="travel_&_places" {{action onCategorySelection "travel_&_places"}} class="btn btn-default category-button emoji">
{{replace-emoji ":airplane:"}}
</button>
<button type="button" data-section="activities" {{action onCategorySelection "activities"}} class="btn btn-default category-button emoji">
{{replace-emoji ":soccer:"}}
</button>
<button type="button" data-section="objects" {{action onCategorySelection "objects"}} class="btn btn-default category-button emoji">
{{replace-emoji ":eyeglasses:"}}
</button>
<button type="button" data-section="symbols" {{action onCategorySelection "symbols"}} class="btn btn-default category-button emoji">
{{replace-emoji ":white_check_mark:"}}
</button>
<button type="button" data-section="flags" {{action onCategorySelection "flags"}} class="btn btn-default category-button emoji">
{{replace-emoji ":checkered_flag:"}}
</button>

View File

@ -1,20 +1,16 @@
{{#if isActive}}
<div class="emoji-picker {{if @isActive 'opened'}}">
<div class="emoji-picker {{if @isActive "opened"}}">
<div class="emoji-picker-category-buttons">
{{#if recentEmojis.length}}
<button data-section="recent" {{action "onCategorySelection" "recent"}} class="btn btn-default category-button emoji">
<button type="button" data-section="recent" {{action "onCategorySelection" "recent"}} class="btn btn-default category-button emoji">
{{replace-emoji ":star:"}}
</button>
{{/if}}
<% JSON.parse(File.read("lib/emoji/groups.json")).each.with_index do |group, group_index| %>
<button data-section="<%= group["name"] %>" {{action "onCategorySelection" "<%= group["name"] %>"}} class="btn btn-default category-button emoji">
{{replace-emoji ":<%= group["tabicon"] %>:"}}
</button>
<% end %>
{{emoji-group-buttons onCategorySelection=(action "onCategorySelection")}}
{{#each-in customEmojis as |group emojis|}}
<button data-section={{concat "custom-" group}} {{action "onCategorySelection" (concat "custom-" group)}} class="btn btn-default category-button emoji">
<button type="button" data-section={{concat "custom-" group}} {{action "onCategorySelection" (concat "custom-" group)}} class="btn btn-default category-button emoji">
{{replace-emoji (concat ":" emojis.firstObject.code ":")}}
</button>
{{/each-in}}
@ -35,18 +31,18 @@
{{d-icon "search"}}
</div>
<div class="emoji-picker-emoji-area" {{on "click" this.onEmojiSelection}} {{on "mouseover" this.onEmojiHover}}>
<div class='results'></div>
<div class="emoji-picker-emoji-area" role="button" {{on "click" this.onEmojiSelection}} {{on "mouseover" this.onEmojiHover}}>
<div class="results"></div>
{{#conditional-loading-spinner condition=isLoading}}
<div class="emojis-container">
{{#if recentEmojis.length}}
<div class='section recent' data-section='recent'>
<div class='section-header'>
<span class="title">{{i18n 'emoji_picker.recent'}}</span>
<div class="section recent" data-section="recent">
<div class="section-header">
<span class="title">{{i18n "emoji_picker.recent"}}</span>
{{d-button icon="trash-alt" action=(action "onClearRecents") class="trash-recent"}}
</div>
<div class='section-group'>
<div class="section-group">
{{#each recentEmojis as |emoji|}}
{{replace-emoji (concat ":" emoji ":") (hash lazy=true)}}
{{/each}}
@ -54,30 +50,19 @@
</div>
{{/if}}
<% JSON.parse(File.read("lib/emoji/groups.json")).each.with_index do |group, group_index| %>
<div class='section' data-section='<%= group["name"] %>'>
<div class='section-header'>
<span class="title">{{i18n 'emoji_picker.<%= group["name"] %>'}}</span>
</div>
<div class='section-group'>
<% group["icons"].each do |icon| %>
{{replace-emoji ":<%= icon['name'] %>:" (hash lazy=true class="<%= "diversity" if icon["diversity"] %>")}}
<% end %>
</div>
</div>
<% end %>
{{emoji-group-sections}}
{{#each-in customEmojis as |group emojis|}}
<div class='section' data-section='custom-{{group}}'>
<div class='section-header'>
<div class="section" data-section="custom-{{group}}">
<div class="section-header">
<span class="title">
{{i18n (concat 'emoji_picker.' group)}}
{{i18n (concat "emoji_picker." group)}}
</span>
</div>
{{#if emojis.length}}
<div class='section-group'>
<div class="section-group">
{{#each emojis as |emoji|}}
<img title="{{emoji.code}}" width="20" height="20" loading="lazy" class="emoji" src="{{emoji.src}}">
<img title={{emoji.code}} width="20" height="20" loading="lazy" class="emoji" src={{emoji.src}}>
{{/each}}
</div>
{{/if}}
@ -109,6 +94,6 @@
</div>
{{#if site.mobileView}}
<div class="emoji-picker-modal-overlay" {{on "click" this.onClose}}></div>
<div role="button" class="emoji-picker-modal-overlay" {{on "click" this.onClose}}></div>
{{/if}}
{{/if}}

View File

@ -5,9 +5,9 @@
{{d-icon "envelope" class="private-message-glyph"}}
</span>
</a>
{{else}}
{{~else}}
<span class="private-message-glyph-wrapper">
{{d-icon "envelope" class="private-message-glyph"}}
</span>
{{/if}}
{{~/if}}
{{/if}}

View File

@ -26,9 +26,13 @@
</ul>
</div>
{{#each panels as |tab|}}
{{component tab selectedTab=selectedTab category=model registerValidator=(action "registerValidator")}}
{{/each}}
<div class="edit-category-content">
<h3>{{selectedTabTitle}}</h3>
{{#each panels as |tab|}}
{{component tab selectedTab=selectedTab category=model registerValidator=(action "registerValidator")}}
{{/each}}
</div>
<div class="edit-category-footer">
{{d-button id="save-category" class="btn-primary" disabled=disabled action=(action "saveCategory") label=saveLabel}}

View File

@ -17,7 +17,7 @@
{{#topic-title cancelled=(action "cancelEditingTopic") save=(action "finishedEditingTopic") model=model}}
{{#if editingTopic}}
<div class="edit-topic-title">
{{private-message-glyph shouldShow=model.isPrivateMessage}}
{{private-message-glyph shouldShow=model.isPrivateMessage tagName=""}}
{{text-field id="edit-title" value=buffered.title maxlength=siteSettings.max_topic_title_length autofocus="true"}}
@ -65,9 +65,10 @@
href=pmPath
title="topic_statuses.personal_message.title"
ariaLabel="user.messages.inbox"
tagName=""
}}
{{else}}
{{private-message-glyph shouldShow=model.isPrivateMessage}}
{{private-message-glyph shouldShow=model.isPrivateMessage tagName=""}}
{{/if}}
{{/unless}}
@ -299,7 +300,7 @@
removeTopicTimer=(action "removeTopicTimer" model.topic_timer.status_type "topic_timer")}}
{{#if showSelectedPostsAtBottom}}
<div class="selected-posts {{unless multiSelect "hidden"}}">
<div class="selected-posts {{unless multiSelect "hidden"}} {{if showSelectedPostsAtBottom "hidden"}}">
{{selected-posts
selectedPostsCount=selectedPostsCount
canSelectAll=canSelectAll

View File

@ -3,7 +3,6 @@ import { isEmpty } from "@ember/utils";
import { wantsNewWindow } from "discourse/lib/intercept-click";
import RawHtml from "discourse/widgets/raw-html";
import { createWidget } from "discourse/widgets/widget";
import DiscourseURL from "discourse/lib/url";
import { h } from "virtual-dom";
import { emojiUnescape } from "discourse/lib/text";
import {
@ -11,10 +10,9 @@ import {
escapeExpression,
formatUsername,
} from "discourse/lib/utilities";
import { setTransientHeader } from "discourse/lib/ajax";
import { userPath } from "discourse/lib/url";
import DiscourseURL, { userPath } from "discourse/lib/url";
import { iconNode } from "discourse-common/lib/icon-library";
import { ajax } from "discourse/lib/ajax";
import { ajax, setTransientHeader } from "discourse/lib/ajax";
import getURL from "discourse-common/lib/get-url";
export const DefaultNotificationItem = createWidget(

View File

@ -3,9 +3,8 @@ import I18n from "I18n";
import { later } from "@ember/runloop";
import { createWidget, applyDecorators } from "discourse/widgets/widget";
import { h } from "virtual-dom";
import DiscourseURL from "discourse/lib/url";
import DiscourseURL, { userPath } from "discourse/lib/url";
import { ajax } from "discourse/lib/ajax";
import { userPath } from "discourse/lib/url";
import { wantsNewWindow } from "discourse/lib/intercept-click";
import { NotificationLevels } from "discourse/lib/notification-levels";

View File

@ -141,7 +141,7 @@ export function buildManageButtons(attrs, currentUser, siteSettings) {
if (attrs.canManage) {
contents.push({
icon: "cog",
icon: "sync-alt",
label: "post.controls.rebake",
action: "rebakePost",
className: "popup-menu-button rebuild-html",

View File

@ -4,10 +4,7 @@ import { ajax } from "discourse/lib/ajax";
import { isValidLink } from "discourse/lib/click-track";
import { number } from "discourse/lib/formatter";
import highlightSearch from "discourse/lib/highlight-search";
import {
default as highlightHTML,
unhighlightHTML,
} from "discourse/lib/highlight-html";
import highlightHTML, { unhighlightHTML } from "discourse/lib/highlight-html";
import { spinnerHTML } from "discourse/helpers/loading-spinner";
let _beforeAdoptDecorators = [];

View File

@ -1,4 +1,4 @@
import { default as getURL, getURLWithCDN } from "discourse-common/lib/get-url";
import getURL, { getURLWithCDN } from "discourse-common/lib/get-url";
import I18n from "I18n";
import PostCooked from "discourse/widgets/post-cooked";
import DecoratorHelper from "discourse/widgets/decorator-helper";

View File

@ -44,7 +44,7 @@ createWidget("quick-access-item", {
}
}
return h("a", { attributes: { href } }, [
return h("a", { attributes: this._linkAttributes(href) }, [
iconNode(icon),
new RawHtml({
html: `<div>${this._usernameHtml()}${content}</div>`,
@ -60,6 +60,10 @@ createWidget("quick-access-item", {
}
},
_linkAttributes(href) {
return { href };
},
_contentHtml() {
const content =
this.attrs.escapedContent || escapeExpression(this.attrs.content);

View File

@ -1,7 +1,6 @@
import { exists } from "discourse/tests/helpers/qunit-helpers";
import { exists, acceptance } from "discourse/tests/helpers/qunit-helpers";
import { visit } from "@ember/test-helpers";
import { test } from "qunit";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
acceptance("About", function () {
test("viewing", async function (assert) {

View File

@ -1,8 +1,10 @@
import { queryAll } from "discourse/tests/helpers/qunit-helpers";
import { exists } from "discourse/tests/helpers/qunit-helpers";
import {
queryAll,
exists,
acceptance,
} from "discourse/tests/helpers/qunit-helpers";
import { visit, click, fillIn, currentRouteName } from "@ember/test-helpers";
import { test } from "qunit";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
import PreloadStore from "discourse/lib/preload-store";
acceptance("Account Created", function () {

View File

@ -1,7 +1,6 @@
import { queryAll } from "discourse/tests/helpers/qunit-helpers";
import { queryAll, acceptance } from "discourse/tests/helpers/qunit-helpers";
import { fillIn, click, visit } from "@ember/test-helpers";
import { test } from "qunit";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
const EMAIL = `
From: "somebody" <somebody@example.com>

View File

@ -1,7 +1,6 @@
import { exists } from "discourse/tests/helpers/qunit-helpers";
import { exists, acceptance } from "discourse/tests/helpers/qunit-helpers";
import { visit } from "@ember/test-helpers";
import { test } from "qunit";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
acceptance("Admin - Search Log Term", function (needs) {
needs.user();

View File

@ -1,7 +1,6 @@
import { exists } from "discourse/tests/helpers/qunit-helpers";
import { exists, acceptance } from "discourse/tests/helpers/qunit-helpers";
import { click, visit } from "@ember/test-helpers";
import { test } from "qunit";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
acceptance("Admin - Search Logs", function (needs) {
needs.user();

View File

@ -1,5 +1,3 @@
import { queryAll } from "discourse/tests/helpers/qunit-helpers";
import { exists } from "discourse/tests/helpers/qunit-helpers";
import {
fillIn,
click,
@ -8,7 +6,12 @@ import {
currentURL,
} from "@ember/test-helpers";
import { test } from "qunit";
import { acceptance, count } from "discourse/tests/helpers/qunit-helpers";
import {
acceptance,
count,
queryAll,
exists,
} from "discourse/tests/helpers/qunit-helpers";
import siteSettingFixture from "discourse/tests/fixtures/site-settings";
acceptance("Admin - Site Settings", function (needs) {

View File

@ -1,8 +1,10 @@
import { queryAll } from "discourse/tests/helpers/qunit-helpers";
import { exists } from "discourse/tests/helpers/qunit-helpers";
import {
queryAll,
exists,
acceptance,
} from "discourse/tests/helpers/qunit-helpers";
import { fillIn, click, visit, currentURL } from "@ember/test-helpers";
import { test } from "qunit";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
acceptance("Admin - Site Texts", function (needs) {
needs.user();

View File

@ -1,9 +1,11 @@
import { queryAll } from "discourse/tests/helpers/qunit-helpers";
import { exists } from "discourse/tests/helpers/qunit-helpers";
import {
queryAll,
exists,
acceptance,
} from "discourse/tests/helpers/qunit-helpers";
import { visit, click, fillIn } from "@ember/test-helpers";
import { test } from "qunit";
import selectKit from "discourse/tests/helpers/select-kit-helper";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
acceptance("Admin - Suspend User", function (needs) {
needs.user();
@ -42,7 +44,7 @@ acceptance("Admin - Suspend User", function (needs) {
assert.equal(queryAll(".suspend-user-modal:visible").length, 1);
await fillIn(".suspend-reason", "for breaking the rules");
await fillIn("input.suspend-reason", "for breaking the rules");
await fillIn(".suspend-message", "this is an email reason why");
await click(".d-modal-cancel");

View File

@ -1,7 +1,6 @@
import { exists } from "discourse/tests/helpers/qunit-helpers";
import { exists, acceptance } from "discourse/tests/helpers/qunit-helpers";
import { visit } from "@ember/test-helpers";
import { test } from "qunit";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
acceptance("Admin - Users Badges", function (needs) {
needs.user();

View File

@ -1,8 +1,7 @@
import { queryAll } from "discourse/tests/helpers/qunit-helpers";
import { queryAll, acceptance } from "discourse/tests/helpers/qunit-helpers";
import { click, visit } from "@ember/test-helpers";
import { test } from "qunit";
import I18n from "I18n";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
function assertNoSecondary(assert) {
assert.equal(

View File

@ -1,8 +1,7 @@
import { queryAll } from "discourse/tests/helpers/qunit-helpers";
import { queryAll, acceptance } from "discourse/tests/helpers/qunit-helpers";
import { click, fillIn, visit } from "@ember/test-helpers";
import { test } from "qunit";
import selectKit from "discourse/tests/helpers/select-kit-helper";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
acceptance("Admin - User Index", function (needs) {
needs.user();

View File

@ -1,9 +1,11 @@
import { queryAll } from "discourse/tests/helpers/qunit-helpers";
import { exists } from "discourse/tests/helpers/qunit-helpers";
import {
queryAll,
exists,
acceptance,
} from "discourse/tests/helpers/qunit-helpers";
import { click, visit } from "@ember/test-helpers";
import { test } from "qunit";
import I18n from "I18n";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
acceptance("Admin - Users List", function (needs) {
needs.user();
@ -12,7 +14,7 @@ acceptance("Admin - Users List", function (needs) {
await visit("/admin/users/list/active");
assert.ok(exists(".users-list .user"));
assert.ok(!exists(".user:eq(0) .email small"), "escapes email");
assert.ok(!exists(".user:nth-of-type(1) .email small"), "escapes email");
});
test("sorts users", async function (assert) {

View File

@ -1,8 +1,10 @@
import { queryAll } from "discourse/tests/helpers/qunit-helpers";
import { exists } from "discourse/tests/helpers/qunit-helpers";
import {
queryAll,
exists,
acceptance,
} from "discourse/tests/helpers/qunit-helpers";
import { fillIn, click, visit } from "@ember/test-helpers";
import { test } from "qunit";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
acceptance("Admin - Watched Words", function (needs) {
needs.user();

View File

@ -1,7 +1,6 @@
import { exists } from "discourse/tests/helpers/qunit-helpers";
import { exists, acceptance } from "discourse/tests/helpers/qunit-helpers";
import { visit, currentRouteName } from "@ember/test-helpers";
import { test } from "qunit";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
acceptance("Auth Complete", function (needs) {
needs.hooks.beforeEach(() => {

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