Version bump
This commit is contained in:
commit
6893e72593
@ -25,3 +25,6 @@ c4644c61d97c823b7dd940ffaf0967a104f4b58c
|
||||
|
||||
# Migrate to app directory
|
||||
7a2e8d3ead63c7d99e1069fc7823e933f931ba85
|
||||
|
||||
# DEV: Fix indentation for routes.rb
|
||||
985900818ff985b04def6aa4c5d99c1aa6dbd45c
|
||||
|
||||
25
.github/workflows/ci.yml
vendored
25
.github/workflows/ci.yml
vendored
@ -6,7 +6,7 @@ on:
|
||||
- master
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- 'tests-passed'
|
||||
- "tests-passed"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@ -28,12 +28,12 @@ jobs:
|
||||
fail-fast: false
|
||||
|
||||
matrix:
|
||||
build_types: [ 'BACKEND', 'FRONTEND', 'LINT' ]
|
||||
target: [ 'PLUGINS', 'CORE' ]
|
||||
os: [ ubuntu-latest ]
|
||||
ruby: [ '2.6' ]
|
||||
postgres: [ '10' ]
|
||||
redis: [ '4.x' ]
|
||||
build_types: ["BACKEND", "FRONTEND", "LINT"]
|
||||
target: ["PLUGINS", "CORE"]
|
||||
os: [ubuntu-latest]
|
||||
ruby: ["2.6"]
|
||||
postgres: ["10"]
|
||||
redis: ["4.x"]
|
||||
|
||||
services:
|
||||
postgres:
|
||||
@ -77,7 +77,6 @@ jobs:
|
||||
uses: actions/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby }}
|
||||
architecture: 'x64'
|
||||
|
||||
- name: Setup bundler
|
||||
run: |
|
||||
@ -145,6 +144,14 @@ jobs:
|
||||
yarn prettier -v
|
||||
yarn prettier --list-different "app/assets/stylesheets/**/*.scss" "app/assets/javascripts/**/*.js" "app/assets/javascripts/**/*.es6" "test/javascripts/**/*.es6" "plugins/**/*.scss" "plugins/**/*.es6"
|
||||
|
||||
- name: Core English locale
|
||||
if: env.BUILD_TYPE == 'LINT' && env.TARGET == 'CORE'
|
||||
run: bundle exec ruby script/i18n_lint.rb "config/**/locales/{client,server}.en.yml"
|
||||
|
||||
- name: Plugin English locale
|
||||
if: env.BUILD_TYPE == 'LINT' && env.TARGET == 'PLUGINS'
|
||||
run: bundle exec ruby script/i18n_lint.rb "plugins/**/locales/{client,server}.en.yml"
|
||||
|
||||
- name: Core RSpec
|
||||
if: env.BUILD_TYPE == 'BACKEND' && env.TARGET == 'CORE'
|
||||
run: |
|
||||
@ -167,5 +174,5 @@ jobs:
|
||||
|
||||
- name: Plugin QUnit # Tests core plugins in TARGET=CORE, and all plugins in TARGET=PLUGINS
|
||||
if: env.BUILD_TYPE == 'FRONTEND'
|
||||
run: bundle exec rake plugin:qunit
|
||||
run: bundle exec rake plugin:qunit['*','1200000']
|
||||
timeout-minutes: 30
|
||||
|
||||
1
Gemfile
1
Gemfile
@ -112,6 +112,7 @@ gem 'oj'
|
||||
gem 'pg'
|
||||
gem 'mini_sql'
|
||||
gem 'pry-rails', require: false
|
||||
gem 'pry-byebug', require: false
|
||||
gem 'r2', require: false
|
||||
gem 'rake'
|
||||
|
||||
|
||||
21
Gemfile.lock
21
Gemfile.lock
@ -1,10 +1,9 @@
|
||||
GIT
|
||||
remote: https://github.com/discourse/rails_failover
|
||||
revision: 0e668eba86591c20aa7a43f47b0cd36d4eacaeb6
|
||||
revision: 66602aa73785851b81c506f0023d3c2a2e40de0a
|
||||
specs:
|
||||
rails_failover (0.4.0)
|
||||
activerecord (~> 6.0)
|
||||
listen (~> 3.2)
|
||||
railties (~> 6.0)
|
||||
|
||||
GEM
|
||||
@ -94,7 +93,7 @@ GEM
|
||||
coderay (1.1.3)
|
||||
colored2 (3.1.2)
|
||||
concurrent-ruby (1.1.6)
|
||||
connection_pool (2.2.2)
|
||||
connection_pool (2.2.3)
|
||||
cose (1.0.0)
|
||||
cbor (~> 0.5.9)
|
||||
openssl-signature_algorithm (~> 0.4.0)
|
||||
@ -115,7 +114,7 @@ GEM
|
||||
in_threads (~> 1.3)
|
||||
progress (~> 3.0, >= 3.0.1)
|
||||
docile (1.3.2)
|
||||
email_reply_trimmer (0.1.12)
|
||||
email_reply_trimmer (0.1.13)
|
||||
ember-data-source (3.0.2)
|
||||
ember-source (>= 2, < 3.0)
|
||||
ember-handlebars-template (0.8.0)
|
||||
@ -156,7 +155,7 @@ GEM
|
||||
hkdf (0.3.0)
|
||||
htmlentities (4.3.4)
|
||||
http_accept_language (2.1.1)
|
||||
i18n (1.8.2)
|
||||
i18n (1.8.3)
|
||||
concurrent-ruby (~> 1.0)
|
||||
image_size (1.5.0)
|
||||
in_threads (1.5.4)
|
||||
@ -192,7 +191,7 @@ GEM
|
||||
mini_mime (>= 0.1.1)
|
||||
maxminddb (0.1.22)
|
||||
memory_profiler (0.9.14)
|
||||
message_bus (3.3.0)
|
||||
message_bus (3.3.1)
|
||||
rack (>= 1.1.3)
|
||||
method_source (1.0.0)
|
||||
mini_mime (1.0.2)
|
||||
@ -268,6 +267,9 @@ GEM
|
||||
pry (0.13.1)
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
pry-byebug (3.9.0)
|
||||
byebug (~> 11.0)
|
||||
pry (~> 0.13.0)
|
||||
pry-rails (0.3.9)
|
||||
pry (>= 0.10.4)
|
||||
public_suffix (4.0.5)
|
||||
@ -286,7 +288,7 @@ GEM
|
||||
nokogiri (>= 1.6)
|
||||
rails-html-sanitizer (1.3.0)
|
||||
loofah (~> 2.3)
|
||||
rails_multisite (2.1.2)
|
||||
rails_multisite (2.3.0)
|
||||
activerecord (> 5.0, < 7)
|
||||
railties (> 5.0, < 7)
|
||||
railties (6.0.3.1)
|
||||
@ -311,6 +313,7 @@ GEM
|
||||
redis (4.1.4)
|
||||
redis-namespace (1.7.0)
|
||||
redis (>= 3.0.4)
|
||||
regexp_parser (1.7.1)
|
||||
request_store (1.5.0)
|
||||
rack (>= 1.4)
|
||||
rexml (3.2.4)
|
||||
@ -350,10 +353,11 @@ GEM
|
||||
json-schema (~> 2.2)
|
||||
railties (>= 3.1, < 7.0)
|
||||
rtlit (0.0.5)
|
||||
rubocop (0.84.0)
|
||||
rubocop (0.85.1)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.7.0.1)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 1.7)
|
||||
rexml
|
||||
rubocop-ast (>= 0.0.3)
|
||||
ruby-progressbar (~> 1.7)
|
||||
@ -515,6 +519,7 @@ DEPENDENCIES
|
||||
onebox
|
||||
parallel_tests
|
||||
pg
|
||||
pry-byebug
|
||||
pry-rails
|
||||
puma
|
||||
r2
|
||||
|
||||
@ -9,8 +9,8 @@ import ReportLoader from "discourse/lib/reports-loader";
|
||||
import { exportEntity } from "discourse/lib/export-csv";
|
||||
import { outputExportResult } from "discourse/lib/export-result";
|
||||
import Report, { SCHEMA_VERSION } from "admin/models/report";
|
||||
import ENV from "discourse-common/config/environment";
|
||||
import { isPresent } from "@ember/utils";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
|
||||
const TABLE_OPTIONS = {
|
||||
perPage: 8,
|
||||
@ -167,8 +167,8 @@ export default Component.extend({
|
||||
let reportKey = "reports:";
|
||||
reportKey += [
|
||||
dataSourceName,
|
||||
ENV.environment === "test" ? "start" : startDate.replace(/-/g, ""),
|
||||
ENV.environment === "test" ? "end" : endDate.replace(/-/g, ""),
|
||||
isTesting() ? "start" : startDate.replace(/-/g, ""),
|
||||
isTesting() ? "end" : endDate.replace(/-/g, ""),
|
||||
"[:prev_period]",
|
||||
this.get("reportOptions.table.limit"),
|
||||
// Convert all filter values to strings to ensure unique serialization
|
||||
@ -226,14 +226,18 @@ export default Component.extend({
|
||||
|
||||
@action
|
||||
exportCsv() {
|
||||
const customFilters = this.get("filters.customFilters") || {};
|
||||
exportEntity("report", {
|
||||
const args = {
|
||||
name: this.get("model.type"),
|
||||
start_date: this.startDate.toISOString(true).split("T")[0],
|
||||
end_date: this.endDate.toISOString(true).split("T")[0],
|
||||
category_id: customFilters.category,
|
||||
group_id: customFilters.group
|
||||
}).then(outputExportResult);
|
||||
end_date: this.endDate.toISOString(true).split("T")[0]
|
||||
};
|
||||
|
||||
const customFilters = this.get("filters.customFilters");
|
||||
if (customFilters) {
|
||||
Object.assign(args, customFilters);
|
||||
}
|
||||
|
||||
exportEntity("report", args).then(outputExportResult);
|
||||
},
|
||||
|
||||
@action
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import { later, schedule } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
@ -31,7 +32,7 @@ export default Component.extend({
|
||||
@on("init")
|
||||
_initialize() {
|
||||
this.resumable = new Resumable({
|
||||
target: Discourse.getURL(this.target),
|
||||
target: getURL(this.target),
|
||||
maxFiles: 1, // only 1 file at a time
|
||||
headers: {
|
||||
"X-CSRF-Token": document.querySelector("meta[name='csrf-token']")
|
||||
|
||||
57
app/assets/javascripts/admin/components/simple-list.js
Normal file
57
app/assets/javascripts/admin/components/simple-list.js
Normal file
@ -0,0 +1,57 @@
|
||||
import { empty } from "@ember/object/computed";
|
||||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
import { on } from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
classNameBindings: [":simple-list", ":value-list"],
|
||||
inputEmpty: empty("newValue"),
|
||||
inputDelimiter: null,
|
||||
newValue: "",
|
||||
collection: null,
|
||||
values: null,
|
||||
|
||||
@on("didReceiveAttrs")
|
||||
_setupCollection() {
|
||||
this.set("collection", this._splitValues(this.values, this.inputDelimiter));
|
||||
},
|
||||
|
||||
keyDown(event) {
|
||||
if (event.which === 13) {
|
||||
this.addValue(this.newValue);
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
@action
|
||||
changeValue(index, newValue) {
|
||||
this.collection.replace(index, 1, [newValue]);
|
||||
this.collection.arrayContentDidChange(index);
|
||||
this._onChange();
|
||||
},
|
||||
|
||||
@action
|
||||
addValue(newValue) {
|
||||
if (this.inputEmpty) return;
|
||||
|
||||
this.set("newValue", null);
|
||||
this.collection.addObject(newValue);
|
||||
this._onChange();
|
||||
},
|
||||
|
||||
@action
|
||||
removeValue(value) {
|
||||
this.collection.removeObject(value);
|
||||
this._onChange();
|
||||
},
|
||||
|
||||
_onChange() {
|
||||
this.attrs.onChange && this.attrs.onChange(this.collection);
|
||||
},
|
||||
|
||||
_splitValues(values, delimiter) {
|
||||
return values && values.length
|
||||
? values.split(delimiter || "\n").filter(Boolean)
|
||||
: [];
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,11 @@
|
||||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
|
||||
export default Component.extend({
|
||||
inputDelimiter: "|",
|
||||
|
||||
@action
|
||||
onChange(value) {
|
||||
this.set("value", value.join(this.inputDelimiter || "\n"));
|
||||
}
|
||||
});
|
||||
@ -4,7 +4,7 @@ import Component from "@ember/component";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import { escape } from "pretty-text/sanitizer";
|
||||
import ENV from "discourse-common/config/environment";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
|
||||
const MAX_COMPONENTS = 4;
|
||||
|
||||
@ -41,7 +41,7 @@ export default Component.extend({
|
||||
animate(isInitial) {
|
||||
const $container = $(this.element);
|
||||
const $list = $(this.element.querySelector(".components-list"));
|
||||
if ($list.length === 0 || ENV.environment === "test") {
|
||||
if ($list.length === 0 || isTesting()) {
|
||||
return;
|
||||
}
|
||||
const duration = 300;
|
||||
|
||||
@ -8,6 +8,7 @@ import AdminDashboard from "admin/models/admin-dashboard";
|
||||
import Report from "admin/models/report";
|
||||
import PeriodComputationMixin from "admin/mixins/period-computation";
|
||||
import { computed } from "@ember/object";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
|
||||
function staticReport(reportType) {
|
||||
return computed("reports.[]", function() {
|
||||
@ -20,7 +21,6 @@ export default Controller.extend(PeriodComputationMixin, {
|
||||
dashboardFetchedAt: null,
|
||||
exceptionController: inject("exception"),
|
||||
logSearchQueriesEnabled: setting("log_search_queries"),
|
||||
basePath: Discourse.BaseUri,
|
||||
|
||||
@discourseComputed("siteSettings.dashboard_general_tab_activity_metrics")
|
||||
activityMetrics(metrics) {
|
||||
@ -107,7 +107,7 @@ export default Controller.extend(PeriodComputationMixin, {
|
||||
@discourseComputed
|
||||
trendingSearchDisabledLabel() {
|
||||
return I18n.t("admin.dashboard.reports.trending_search.disabled", {
|
||||
basePath: Discourse.BaseUri
|
||||
basePath: getURL("/")
|
||||
});
|
||||
},
|
||||
|
||||
@ -150,6 +150,6 @@ export default Controller.extend(PeriodComputationMixin, {
|
||||
},
|
||||
|
||||
_reportsForPeriodURL(period) {
|
||||
return Discourse.getURL(`/admin?period=${period}`);
|
||||
return getURL(`/admin?period=${period}`);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import Controller from "@ember/controller";
|
||||
import PeriodComputationMixin from "admin/mixins/period-computation";
|
||||
@ -45,6 +46,6 @@ export default Controller.extend(PeriodComputationMixin, {
|
||||
},
|
||||
|
||||
_reportsForPeriodURL(period) {
|
||||
return Discourse.getURL(`/admin/dashboard/moderation?period=${period}`);
|
||||
return getURL(`/admin/dashboard/moderation?period=${period}`);
|
||||
}
|
||||
});
|
||||
|
||||
@ -6,6 +6,7 @@ import Controller, { inject as controller } from "@ember/controller";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { extractDomainFromUrl } from "discourse/lib/utilities";
|
||||
import EmberObject from "@ember/object";
|
||||
import { isAbsoluteURL } from "discourse-common/lib/get-url";
|
||||
|
||||
export default Controller.extend({
|
||||
adminWebHooks: controller(),
|
||||
@ -109,7 +110,7 @@ export default Controller.extend({
|
||||
domain === "localhost" ||
|
||||
domain.match(/192\.168\.\d+\.\d+/) ||
|
||||
domain.match(/127\.\d+\.\d+\.\d+/) ||
|
||||
url.startsWith(Discourse.BaseUrl)
|
||||
isAbsoluteURL(url)
|
||||
) {
|
||||
return bootbox.confirm(
|
||||
I18n.t("admin.web_hooks.warn_local_payload_url"),
|
||||
|
||||
@ -25,7 +25,8 @@ const CUSTOM_TYPES = [
|
||||
"upload",
|
||||
"group_list",
|
||||
"tag_list",
|
||||
"color"
|
||||
"color",
|
||||
"simple_list"
|
||||
];
|
||||
|
||||
const AUTO_REFRESH_ON_SAVE = ["logo", "logo_small", "large_icon"];
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { filter, or, gt, lt, not } from "@ember/object/computed";
|
||||
@ -43,7 +44,7 @@ const AdminUser = User.extend({
|
||||
|
||||
@discourseComputed
|
||||
bounceLink() {
|
||||
return Discourse.getURL("/admin/email/bounced");
|
||||
return getURL("/admin/email/bounced");
|
||||
},
|
||||
|
||||
canResetBounceScore: gt("bounce_score", 0),
|
||||
@ -306,7 +307,7 @@ const AdminUser = User.extend({
|
||||
type: "POST",
|
||||
data: { username_or_email: this.username }
|
||||
})
|
||||
.then(() => (document.location = Discourse.getURL("/")))
|
||||
.then(() => (document.location = getURL("/")))
|
||||
.catch(e => {
|
||||
if (e.status === 404) {
|
||||
bootbox.alert(I18n.t("admin.impersonate.not_found"));
|
||||
@ -395,11 +396,11 @@ const AdminUser = User.extend({
|
||||
.then(function(data) {
|
||||
if (data.success) {
|
||||
if (data.username) {
|
||||
document.location = Discourse.getURL(
|
||||
document.location = getURL(
|
||||
`/admin/users/${user.get("id")}/${data.username}`
|
||||
);
|
||||
} else {
|
||||
document.location = Discourse.getURL("/admin/users/list/active");
|
||||
document.location = getURL("/admin/users/list/active");
|
||||
}
|
||||
} else {
|
||||
bootbox.alert(I18n.t("admin.user.anonymize_failed"));
|
||||
@ -456,7 +457,7 @@ const AdminUser = User.extend({
|
||||
if (/^\/admin\/users\/list\//.test(location)) {
|
||||
document.location = location;
|
||||
} else {
|
||||
document.location = Discourse.getURL("/admin/users/list/active");
|
||||
document.location = getURL("/admin/users/list/active");
|
||||
}
|
||||
} else {
|
||||
bootbox.alert(I18n.t("admin.user.delete_failed"));
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { extractError } from "discourse/lib/ajax-error";
|
||||
@ -66,7 +67,7 @@ Backup.reopenClass({
|
||||
bootbox.alert(result.message);
|
||||
} else {
|
||||
// redirect to homepage (session might be lost)
|
||||
window.location = Discourse.getURL("/");
|
||||
window.location = getURL("/");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import AdminUser from "admin/models/admin-user";
|
||||
import EmberObject from "@ember/object";
|
||||
@ -13,7 +14,7 @@ EmailLog.reopenClass({
|
||||
}
|
||||
|
||||
if (attrs.post_url) {
|
||||
attrs.post_url = Discourse.getURL(attrs.post_url);
|
||||
attrs.post_url = getURL(attrs.post_url);
|
||||
}
|
||||
|
||||
return this._super(attrs);
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { makeArray } from "discourse-common/lib/helpers";
|
||||
@ -37,7 +38,7 @@ const Report = EmberObject.extend({
|
||||
.locale("en")
|
||||
.format("YYYY-MM-DD");
|
||||
|
||||
return Discourse.getURL(
|
||||
return getURL(
|
||||
`/admin/reports/${type}?start_date=${start_date}&end_date=${end_date}`
|
||||
);
|
||||
},
|
||||
@ -335,7 +336,7 @@ const Report = EmberObject.extend({
|
||||
avatar_template: row[properties.avatar]
|
||||
});
|
||||
|
||||
const href = Discourse.getURL(`/admin/users/${userId}/${username}`);
|
||||
const href = getURL(`/admin/users/${userId}/${username}`);
|
||||
|
||||
const avatarImg = renderAvatar(user, {
|
||||
imageSize: "tiny",
|
||||
@ -356,7 +357,7 @@ const Report = EmberObject.extend({
|
||||
|
||||
const formatedValue = () => {
|
||||
const topicId = row[properties.id];
|
||||
const href = Discourse.getURL(`/t/-/${topicId}`);
|
||||
const href = getURL(`/t/-/${topicId}`);
|
||||
return `<a href='${href}'>${escapeExpression(topicTitle)}</a>`;
|
||||
};
|
||||
|
||||
@ -370,7 +371,7 @@ const Report = EmberObject.extend({
|
||||
const postTitle = row[properties.truncated_raw];
|
||||
const postNumber = row[properties.number];
|
||||
const topicId = row[properties.topic_id];
|
||||
const href = Discourse.getURL(`/t/-/${topicId}/${postNumber}`);
|
||||
const href = getURL(`/t/-/${topicId}/${postNumber}`);
|
||||
|
||||
return {
|
||||
property: properties.title,
|
||||
@ -434,7 +435,7 @@ const Report = EmberObject.extend({
|
||||
|
||||
_linkLabel(properties, row) {
|
||||
const property = properties[0];
|
||||
const value = Discourse.getURL(row[property]);
|
||||
const value = getURL(row[property]);
|
||||
const formatedValue = (href, anchor) => {
|
||||
return `<a href="${escapeExpression(href)}">${escapeExpression(
|
||||
anchor
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import EmberObject from "@ember/object";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
@ -40,7 +41,7 @@ export default DiscourseRoute.extend({
|
||||
);
|
||||
if (log.operation === "restore") {
|
||||
// redirect to homepage when the restore is done (session might be lost)
|
||||
window.location = Discourse.getURL("/");
|
||||
window.location = getURL("/");
|
||||
}
|
||||
} else {
|
||||
this.controllerFor("adminBackupsLogs")
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
{{#if collection}}
|
||||
<div class="values">
|
||||
{{#each collection as |value index|}}
|
||||
<div data-index={{index}} class="value">
|
||||
{{d-button
|
||||
action=(action "removeValue")
|
||||
actionParam=value
|
||||
icon="times"
|
||||
class="remove-value-btn btn-small"
|
||||
}}
|
||||
|
||||
{{input
|
||||
title=value
|
||||
value=value
|
||||
class="value-input"
|
||||
focus-out=(action "changeValue" index)
|
||||
}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="simple-list-input">
|
||||
{{input
|
||||
type="text"
|
||||
value=newValue
|
||||
placeholderKey="admin.site_settings.simple_list.add_item"
|
||||
class="add-value-input"
|
||||
autocomplete="discourse"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"}}
|
||||
|
||||
{{d-button
|
||||
action=(action "addValue")
|
||||
actionParam=newValue
|
||||
disabled=inputEmpty
|
||||
icon="plus"
|
||||
class="add-value-btn btn-small"
|
||||
}}
|
||||
</div>
|
||||
@ -0,0 +1,3 @@
|
||||
{{simple-list values=value inputDelimiter=inputDelimiter onChange=(action "onChange")}}
|
||||
{{setting-validation-message message=validationMessage}}
|
||||
<div class="desc">{{html-safe setting.description}}</div>
|
||||
@ -147,7 +147,7 @@
|
||||
filters=trendingSearchFilters
|
||||
isEnabled=logSearchQueriesEnabled
|
||||
disabledLabel=trendingSearchDisabledLabel}}
|
||||
{{html-safe (i18n "admin.dashboard.reports.trending_search.more" basePath=basePath)}}
|
||||
{{html-safe (i18n "admin.dashboard.reports.trending_search.more" basePath=(base-url))}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
@ -153,6 +153,7 @@
|
||||
{{i18n "admin.user.tl3_requirements.locked_will_not_be_promoted"}}
|
||||
{{else}}
|
||||
{{d-icon "times"}}
|
||||
{{i18n "admin.user.tl3_requirements.does_not_qualify"}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
@ -1,9 +1,23 @@
|
||||
export const INPUT_DELAY = 250;
|
||||
|
||||
let environment = Ember.testing ? "test" : "development";
|
||||
let environment = "unknown";
|
||||
|
||||
export function isTesting() {
|
||||
return environment === "test";
|
||||
export function setEnvironment(e) {
|
||||
if (isTesting()) {
|
||||
environment = "testing";
|
||||
} else {
|
||||
environment = e;
|
||||
}
|
||||
}
|
||||
|
||||
export default { environment };
|
||||
export function isTesting() {
|
||||
return Ember.testing;
|
||||
}
|
||||
|
||||
export function isDevelopment() {
|
||||
return environment === "development";
|
||||
}
|
||||
|
||||
export function isProduction() {
|
||||
return environment === "production";
|
||||
}
|
||||
|
||||
@ -2,3 +2,4 @@ import { registerUnbound } from "discourse-common/lib/helpers";
|
||||
import getUrl from "discourse-common/lib/get-url";
|
||||
|
||||
registerUnbound("get-url", value => getUrl(value));
|
||||
registerUnbound("base-url", () => getUrl(""));
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
let baseUri;
|
||||
let cdn, baseUrl, baseUri;
|
||||
let S3BaseUrl, S3CDN;
|
||||
|
||||
export default function getURL(url) {
|
||||
if (!url) return url;
|
||||
|
||||
if (!baseUri) {
|
||||
if (baseUri === undefined) {
|
||||
baseUri = $('meta[name="discourse-base-uri"]').attr("content") || "";
|
||||
}
|
||||
|
||||
@ -17,3 +18,42 @@ export default function getURL(url) {
|
||||
|
||||
return baseUri + url;
|
||||
}
|
||||
|
||||
export function getURLWithCDN(url) {
|
||||
url = getURL(url);
|
||||
// only relative urls
|
||||
if (cdn && /^\/[^\/]/.test(url)) {
|
||||
url = cdn + url;
|
||||
} else if (S3CDN) {
|
||||
url = url.replace(S3BaseUrl, S3CDN);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
export function getAbsoluteURL(path) {
|
||||
return baseUrl + path;
|
||||
}
|
||||
|
||||
export function isAbsoluteURL(url) {
|
||||
return url.startsWith(baseUrl);
|
||||
}
|
||||
|
||||
export function withoutPrefix(path) {
|
||||
const rootURL = (!baseUri ? "/" : baseUri).replace(/\/$/, "");
|
||||
return path.replace(rootURL, "");
|
||||
}
|
||||
|
||||
export function setPrefix(configBaseUri) {
|
||||
baseUri = configBaseUri;
|
||||
}
|
||||
|
||||
export function setupURL(configCdn, configBaseUrl, configBaseUri) {
|
||||
cdn = configCdn;
|
||||
baseUrl = configBaseUrl;
|
||||
baseUri = configBaseUri;
|
||||
}
|
||||
|
||||
export function setupS3CDN(configS3BaseUrl, configS3CDN) {
|
||||
S3BaseUrl = configS3BaseUrl;
|
||||
S3CDN = configS3CDN;
|
||||
}
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
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 = [];
|
||||
|
||||
let warnMissingIcons = true;
|
||||
|
||||
const REPLACEMENTS = {
|
||||
"d-tracking": "bell",
|
||||
"d-muted": "discourse-bell-slash",
|
||||
@ -45,6 +48,14 @@ export function replaceIcon(source, destination) {
|
||||
REPLACEMENTS[source] = destination;
|
||||
}
|
||||
|
||||
export function disableMissingIconWarning() {
|
||||
warnMissingIcons = false;
|
||||
}
|
||||
|
||||
export function enableMissingIconWarning() {
|
||||
warnMissingIcons = false;
|
||||
}
|
||||
|
||||
export function renderIcon(renderType, id, params) {
|
||||
for (let i = 0; i < _renderers.length; i++) {
|
||||
let renderer = _renderers[i];
|
||||
@ -105,8 +116,8 @@ function iconClasses(icon, params) {
|
||||
function warnIfMissing(id) {
|
||||
if (
|
||||
typeof Discourse !== "undefined" &&
|
||||
Discourse.Environment === "development" &&
|
||||
!Discourse.disableMissingIconWarning &&
|
||||
isDevelopment() &&
|
||||
warnMissingIcons &&
|
||||
Discourse.SvgIconList &&
|
||||
Discourse.SvgIconList.indexOf(id) === -1
|
||||
) {
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import RestAdapter from "discourse/adapters/rest";
|
||||
import PreloadStore from "discourse/lib/preload-store";
|
||||
|
||||
export function finderFor(filter, params) {
|
||||
return function() {
|
||||
let url = Discourse.getURL("/") + filter + ".json";
|
||||
let url = getURL("/") + filter + ".json";
|
||||
|
||||
if (params) {
|
||||
const keys = Object.keys(params),
|
||||
|
||||
@ -4,6 +4,8 @@ import { computed } from "@ember/object";
|
||||
import { buildResolver } from "discourse-common/resolver";
|
||||
import { bind } from "@ember/runloop";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import { default as getURL, getURLWithCDN } from "discourse-common/lib/get-url";
|
||||
import deprecated from "discourse-common/lib/deprecated";
|
||||
|
||||
const _pluginCallbacks = [];
|
||||
|
||||
@ -42,26 +44,19 @@ const Discourse = Application.extend({
|
||||
},
|
||||
|
||||
getURL(url) {
|
||||
if (!url) return url;
|
||||
|
||||
// if it's a non relative URL, return it.
|
||||
if (url !== "/" && !/^\/[^\/]/.test(url)) return url;
|
||||
|
||||
if (url[0] !== "/") url = "/" + url;
|
||||
if (url.startsWith(this.BaseUri)) return url;
|
||||
|
||||
return this.BaseUri + url;
|
||||
deprecated(
|
||||
"Import `getURL` from `discourse-common/lib/get-url` instead of `Discourse.getURL`",
|
||||
{ since: "2.5", dropFrom: "2.6" }
|
||||
);
|
||||
return getURL(url);
|
||||
},
|
||||
|
||||
getURLWithCDN(url) {
|
||||
url = this.getURL(url);
|
||||
// only relative urls
|
||||
if (this.CDN && /^\/[^\/]/.test(url)) {
|
||||
url = this.CDN + url;
|
||||
} else if (this.S3CDN) {
|
||||
url = url.replace(this.S3BaseUrl, this.S3CDN);
|
||||
}
|
||||
return url;
|
||||
deprecated(
|
||||
"Import `getURLWithCDN` from `discourse-common/lib/get-url` instead of `Discourse.getURLWithCDN`",
|
||||
{ since: "2.5", dropFrom: "2.6" }
|
||||
);
|
||||
return getURLWithCDN(url);
|
||||
},
|
||||
|
||||
Resolver: buildResolver("discourse"),
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { getURLWithCDN } from "discourse-common/lib/get-url";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import Component from "@ember/component";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
@ -7,7 +8,7 @@ export default Component.extend({
|
||||
|
||||
@discourseComputed("src")
|
||||
cdnSrc(src) {
|
||||
return Discourse.getURLWithCDN(src);
|
||||
return getURLWithCDN(src);
|
||||
},
|
||||
|
||||
@discourseComputed("width", "height")
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import { debounce, later, next, schedule, throttle } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
@ -44,7 +45,7 @@ import {
|
||||
cacheShortUploadUrl,
|
||||
resolveAllShortUrls
|
||||
} from "pretty-text/upload-short-url";
|
||||
import ENV from "discourse-common/config/environment";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
|
||||
const REBUILD_SCROLL_MAP_EVENTS = ["composer:resized", "composer:typed-reply"];
|
||||
|
||||
@ -650,9 +651,7 @@ export default Component.extend({
|
||||
const $element = $(this.element);
|
||||
|
||||
$element.fileupload({
|
||||
url: Discourse.getURL(
|
||||
`/uploads.json?client_id=${this.messageBus.clientId}`
|
||||
),
|
||||
url: getURL(`/uploads.json?client_id=${this.messageBus.clientId}`),
|
||||
dataType: "json",
|
||||
pasteZone: $element
|
||||
});
|
||||
@ -847,7 +846,7 @@ export default Component.extend({
|
||||
// need to wait a bit for the "slide down" transition of the composer
|
||||
later(
|
||||
() => this.appEvents.trigger("composer:closed"),
|
||||
ENV.environment === "test" ? 0 : 400
|
||||
isTesting() ? 0 : 400
|
||||
);
|
||||
});
|
||||
|
||||
@ -1001,7 +1000,7 @@ export default Component.extend({
|
||||
);
|
||||
|
||||
// Short upload urls need resolution
|
||||
resolveAllShortUrls(ajax, this.siteSettings, ".d-editor-preview-wrapper");
|
||||
resolveAllShortUrls(ajax, this.siteSettings, $preview[0]);
|
||||
|
||||
if (this._enableAdvancedEditorPreviewSync()) {
|
||||
this._syncScroll(
|
||||
|
||||
@ -6,7 +6,7 @@ import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import { load } from "pretty-text/oneboxer";
|
||||
import { lookupCache } from "pretty-text/oneboxer-cache";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import ENV from "discourse-common/config/environment";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
import EmberObject from "@ember/object";
|
||||
import putCursorAtEnd from "discourse/lib/put-cursor-at-end";
|
||||
|
||||
@ -76,7 +76,7 @@ export default Component.extend({
|
||||
return;
|
||||
}
|
||||
|
||||
if (ENV.environment === "test") {
|
||||
if (isTesting()) {
|
||||
next(() =>
|
||||
// not ideal but we don't want to run this in current
|
||||
// runloop to avoid an error in console
|
||||
|
||||
@ -16,7 +16,7 @@ const CookText = Component.extend({
|
||||
next(() =>
|
||||
window
|
||||
.requireModule("pretty-text/upload-short-url")
|
||||
.resolveAllShortUrls(ajax, this.siteSettings)
|
||||
.resolveAllShortUrls(ajax, this.siteSettings, this.element)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ 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 { cookAsync } from "discourse/lib/text";
|
||||
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";
|
||||
@ -28,7 +28,7 @@ import { emojiSearch, isSkinTonableEmoji } from "pretty-text/emoji";
|
||||
import { emojiUrlFor } from "discourse/lib/text";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
import { Promise } from "rsvp";
|
||||
import ENV from "discourse-common/config/environment";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
|
||||
// Our head can be a static string or a function that returns a string
|
||||
// based on input (like for numbered lists).
|
||||
@ -344,18 +344,32 @@ export default Component.extend({
|
||||
return toolbar;
|
||||
},
|
||||
|
||||
cachedCookAsync(text) {
|
||||
if (this._cachedCookFunction) {
|
||||
return Promise.resolve(this._cachedCookFunction(text));
|
||||
}
|
||||
|
||||
const markdownOptions = this.markdownOptions || {};
|
||||
return generateCookFunction(markdownOptions).then(cook => {
|
||||
this._cachedCookFunction = cook;
|
||||
return cook(text);
|
||||
});
|
||||
},
|
||||
|
||||
_updatePreview() {
|
||||
if (this._state !== "inDOM") {
|
||||
return;
|
||||
}
|
||||
|
||||
const value = this.value;
|
||||
const markdownOptions = this.markdownOptions || {};
|
||||
|
||||
cookAsync(value, markdownOptions).then(cooked => {
|
||||
this.cachedCookAsync(value).then(cooked => {
|
||||
if (this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.preview === cooked) return;
|
||||
|
||||
this.set("preview", cooked);
|
||||
schedule("afterRender", () => {
|
||||
if (this._state !== "inDOM") {
|
||||
@ -378,7 +392,7 @@ export default Component.extend({
|
||||
}
|
||||
|
||||
// Debouncing in test mode is complicated
|
||||
if (ENV.environment === "test") {
|
||||
if (isTesting()) {
|
||||
this._updatePreview();
|
||||
} else {
|
||||
debounce(this, this._updatePreview, 30);
|
||||
@ -425,7 +439,7 @@ export default Component.extend({
|
||||
return false;
|
||||
}
|
||||
|
||||
const matches = /(?:^|[^a-z])(:(?!:).?[\w-]*:?(?!:)(?:t\d?)?:?) ?$/gi.exec(
|
||||
const matches = /(?:^|[>.,\/#!$%^&*;:{}=\-_`~()])(:(?!:).?[\w-]*:?(?!:)(?:t\d?)?:?) ?$/gi.exec(
|
||||
text.substring(0, cp)
|
||||
);
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import Component from "@ember/component";
|
||||
|
||||
@ -13,6 +14,6 @@ export default Component.extend({
|
||||
|
||||
@discourseComputed("tagRecord.id")
|
||||
href(tagRecordId) {
|
||||
return Discourse.getURL("/tag/" + tagRecordId);
|
||||
return getURL("/tag/" + tagRecordId);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { not } from "@ember/object/computed";
|
||||
@ -27,10 +28,10 @@ export default buildCategoryPanel("general", {
|
||||
},
|
||||
|
||||
canSelectParentCategory: not("category.isUncategorizedCategory"),
|
||||
uncategorizedSiteSettingLink: Discourse.getURL(
|
||||
uncategorizedSiteSettingLink: getURL(
|
||||
"/admin/site_settings/category/all_results?filter=allow_uncategorized_topics"
|
||||
),
|
||||
customizeTextContentLink: Discourse.getURL(
|
||||
customizeTextContentLink: getURL(
|
||||
"/admin/customize/site_texts?q=uncategorized"
|
||||
),
|
||||
|
||||
|
||||
@ -68,6 +68,13 @@ export default buildCategoryPanel("settings", {
|
||||
);
|
||||
},
|
||||
|
||||
@discourseComputed
|
||||
availableListFilters() {
|
||||
return ["all", "none"].map(p => {
|
||||
return { name: I18n.t(`category.list_filters.${p}`), value: p };
|
||||
});
|
||||
},
|
||||
|
||||
@discourseComputed
|
||||
searchPrioritiesOptions() {
|
||||
const options = [];
|
||||
@ -101,11 +108,18 @@ export default buildCategoryPanel("settings", {
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
},
|
||||
|
||||
@discourseComputed("category.sort_ascending")
|
||||
sortAscendingOption(sortAscending) {
|
||||
if (sortAscending === "false") return false;
|
||||
if (sortAscending === "true") return true;
|
||||
return sortAscending;
|
||||
},
|
||||
|
||||
@discourseComputed
|
||||
sortAscendingOptions() {
|
||||
return [
|
||||
{ name: I18n.t("category.sort_ascending"), value: "true" },
|
||||
{ name: I18n.t("category.sort_descending"), value: "false" }
|
||||
{ name: I18n.t("category.sort_ascending"), value: true },
|
||||
{ name: I18n.t("category.sort_descending"), value: false }
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
@ -0,0 +1,63 @@
|
||||
import { action, computed } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import I18n from "I18n";
|
||||
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
|
||||
|
||||
export default DropdownSelectBoxComponent.extend({
|
||||
router: service(),
|
||||
|
||||
classNames: ["email-dropdown"],
|
||||
|
||||
selectKitOptions: {
|
||||
icon: "wrench",
|
||||
showFullTitle: false
|
||||
},
|
||||
|
||||
content: computed("email", function() {
|
||||
const content = [];
|
||||
|
||||
if (this.email.primary) {
|
||||
content.push({
|
||||
id: "updateEmail",
|
||||
icon: "pencil-alt",
|
||||
name: I18n.t("user.email.update_email"),
|
||||
description: ""
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.email.primary && this.email.confirmed) {
|
||||
content.push({
|
||||
id: "setPrimaryEmail",
|
||||
icon: "star",
|
||||
name: I18n.t("user.email.set_primary"),
|
||||
description: ""
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.email.primary) {
|
||||
content.push({
|
||||
id: "destroyEmail",
|
||||
icon: "times",
|
||||
name: I18n.t("user.email.destroy"),
|
||||
description: ""
|
||||
});
|
||||
}
|
||||
|
||||
return content;
|
||||
}),
|
||||
|
||||
@action
|
||||
onChange(id) {
|
||||
switch (id) {
|
||||
case "updateEmail":
|
||||
this.router.transitionTo("preferences.email");
|
||||
break;
|
||||
case "setPrimaryEmail":
|
||||
this.setPrimaryEmail(this.email.email);
|
||||
break;
|
||||
case "destroyEmail":
|
||||
this.destroyEmail(this.email.email);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -10,7 +10,7 @@ import {
|
||||
emojiSearch
|
||||
} from "pretty-text/emoji";
|
||||
import { safariHacksDisabled } from "discourse/lib/utilities";
|
||||
import ENV, { INPUT_DELAY } from "discourse-common/config/environment";
|
||||
import { isTesting, INPUT_DELAY } from "discourse-common/config/environment";
|
||||
|
||||
const PER_ROW = 11;
|
||||
function customEmojis() {
|
||||
@ -525,7 +525,7 @@ export default Component.extend({
|
||||
this.$picker.css(_.merge(attributes, options));
|
||||
};
|
||||
|
||||
if (ENV.environment === "test" || !this.automaticPositioning) {
|
||||
if (isTesting() || !this.automaticPositioning) {
|
||||
desktopPositioning();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { notEmpty, not } from "@ember/object/computed";
|
||||
import { notEmpty } from "@ember/object/computed";
|
||||
import { action } from "@ember/object";
|
||||
import Component from "@ember/component";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
@ -11,7 +11,6 @@ export default Component.extend(UploadMixin, {
|
||||
uploadUrl: "/admin/customize/emojis",
|
||||
hasName: notEmpty("name"),
|
||||
hasGroup: notEmpty("group"),
|
||||
addDisabled: not("hasName"),
|
||||
group: "default",
|
||||
emojiGroups: null,
|
||||
newEmojiGroups: null,
|
||||
@ -23,6 +22,11 @@ export default Component.extend(UploadMixin, {
|
||||
this.set("newEmojiGroups", this.emojiGroups);
|
||||
},
|
||||
|
||||
@discourseComputed("hasName", "uploading")
|
||||
addDisabled() {
|
||||
return !this.hasName || this.uploading;
|
||||
},
|
||||
|
||||
uploadOptions() {
|
||||
return { sequentialUploads: true };
|
||||
},
|
||||
|
||||
@ -20,7 +20,7 @@ export default Component.extend({
|
||||
)
|
||||
formattedName(name, nameKey, isCustomFlag, username) {
|
||||
if (isCustomFlag) {
|
||||
return name.replace("{{username}}", username);
|
||||
return name.replace(/{{username}}|%{username}/, username);
|
||||
} else {
|
||||
return I18n.t("flagging.formatted_name." + nameKey);
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import { bind, cancel } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
@ -105,7 +106,7 @@ export default Component.extend({
|
||||
|
||||
if (this.site.wizard_required) {
|
||||
const requiredText = I18n.t("wizard_required", {
|
||||
url: Discourse.getURL("/wizard")
|
||||
url: getURL("/wizard")
|
||||
});
|
||||
notices.push(Notice.create({ text: requiredText, id: "alert-wizard" }));
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import Component from "@ember/component";
|
||||
@ -10,6 +11,6 @@ export default Component.extend({
|
||||
|
||||
@discourseComputed
|
||||
siteUrl() {
|
||||
return `${location.protocol}//${location.host}${Discourse.getURL("/")}`;
|
||||
return `${location.protocol}//${location.host}${getURL("/")}`;
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { debounce } from "@ember/runloop";
|
||||
@ -14,7 +15,7 @@ export default Component.extend({
|
||||
|
||||
@discourseComputed
|
||||
demoAvatarUrl() {
|
||||
return Discourse.getURL("/images/avatar.png");
|
||||
return getURL("/images/avatar.png");
|
||||
},
|
||||
|
||||
@discourseComputed("model.flair_type")
|
||||
@ -105,7 +106,7 @@ export default Component.extend({
|
||||
@action
|
||||
setFlairImage(upload) {
|
||||
this.model.setProperties({
|
||||
flair_url: Discourse.getURL(upload.url),
|
||||
flair_url: getURL(upload.url),
|
||||
flair_upload_id: upload.id
|
||||
});
|
||||
},
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import Component from "@ember/component";
|
||||
|
||||
export default Component.extend({
|
||||
@discourseComputed("post.url")
|
||||
postUrl(url) {
|
||||
return Discourse.getURL(url);
|
||||
return getURL(url);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { getURLWithCDN } from "discourse-common/lib/get-url";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { next } from "@ember/runloop";
|
||||
@ -43,7 +44,7 @@ export default Component.extend(UploadMixin, {
|
||||
return "".htmlSafe();
|
||||
}
|
||||
|
||||
return Discourse.getURLWithCDN(url);
|
||||
return getURLWithCDN(url);
|
||||
},
|
||||
|
||||
@discourseComputed("imageCDNURL")
|
||||
|
||||
@ -0,0 +1,98 @@
|
||||
import I18n from "I18n";
|
||||
import Component from "@ember/component";
|
||||
import Group from "discourse/models/group";
|
||||
import { readOnly } from "@ember/object/computed";
|
||||
import { action } from "@ember/object";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import Invite from "discourse/models/invite";
|
||||
|
||||
export default Component.extend({
|
||||
inviteModel: readOnly("panel.model.inviteModel"),
|
||||
userInvitedShow: readOnly("panel.model.userInvitedShow"),
|
||||
isStaff: readOnly("currentUser.staff"),
|
||||
maxRedemptionAllowed: 5,
|
||||
inviteExpiresAt: moment()
|
||||
.add(1, "month")
|
||||
.format("YYYY-MM-DD"),
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.reset();
|
||||
},
|
||||
|
||||
@discourseComputed("isStaff", "inviteModel.saving", "maxRedemptionAllowed")
|
||||
disabled(isStaff, saving, canInviteTo, maxRedemptionAllowed) {
|
||||
if (saving) return true;
|
||||
if (!isStaff) return true;
|
||||
if (maxRedemptionAllowed < 2) return true;
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
groupFinder(term) {
|
||||
return Group.findAll({ term, ignore_automatic: true });
|
||||
},
|
||||
|
||||
errorMessage: I18n.t("user.invited.invite_link.error"),
|
||||
|
||||
reset() {
|
||||
this.set("maxRedemptionAllowed", 5);
|
||||
|
||||
this.inviteModel.setProperties({
|
||||
groupNames: null,
|
||||
error: false,
|
||||
saving: false,
|
||||
finished: false,
|
||||
inviteLink: null
|
||||
});
|
||||
},
|
||||
|
||||
@action
|
||||
generateMultipleUseInviteLink() {
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const groupNames = this.get("inviteModel.groupNames");
|
||||
const maxRedemptionAllowed = this.maxRedemptionAllowed;
|
||||
const inviteExpiresAt = this.inviteExpiresAt;
|
||||
const userInvitedController = this.userInvitedShow;
|
||||
const model = this.inviteModel;
|
||||
model.setProperties({ saving: true, error: false });
|
||||
|
||||
return model
|
||||
.generateMultipleUseInviteLink(
|
||||
groupNames,
|
||||
maxRedemptionAllowed,
|
||||
inviteExpiresAt
|
||||
)
|
||||
.then(result => {
|
||||
model.setProperties({
|
||||
saving: false,
|
||||
finished: true,
|
||||
inviteLink: result
|
||||
});
|
||||
|
||||
if (userInvitedController) {
|
||||
Invite.findInvitedBy(
|
||||
this.currentUser,
|
||||
userInvitedController.filter
|
||||
).then(inviteModel => {
|
||||
userInvitedController.setProperties({
|
||||
model: inviteModel,
|
||||
totalInvites: inviteModel.invites.length
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
if (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) {
|
||||
this.set("errorMessage", e.jqXHR.responseJSON.errors[0]);
|
||||
} else {
|
||||
this.set("errorMessage", I18n.t("user.invited.invite_link.error"));
|
||||
}
|
||||
model.setProperties({ saving: false, error: true });
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import Component from "@ember/component";
|
||||
|
||||
@ -23,7 +24,7 @@ export default Component.extend({
|
||||
|
||||
@discourseComputed
|
||||
searchLink() {
|
||||
return Discourse.getURL(
|
||||
return getURL(
|
||||
`/search?expanded=true&q=%40${this.targetUser.username}%20in%3Apersonal-direct`
|
||||
);
|
||||
},
|
||||
|
||||
@ -10,7 +10,10 @@ export default Component.extend({
|
||||
@discourseComputed("rs.score_type.title", "reviewable.target_created_by")
|
||||
title(title, targetCreatedBy) {
|
||||
if (title && targetCreatedBy) {
|
||||
return title.replace("{{username}}", targetCreatedBy.username);
|
||||
return title.replace(
|
||||
/{{username}}|%{username}/,
|
||||
targetCreatedBy.username
|
||||
);
|
||||
}
|
||||
|
||||
return title;
|
||||
|
||||
@ -333,6 +333,13 @@ export default MountWidget.extend({
|
||||
});
|
||||
|
||||
this.appEvents.on("post-stream:refresh", this, "_refresh");
|
||||
|
||||
// restore scroll position on browsers with aggressive BFCaches (like Safari)
|
||||
window.onpageshow = function(event) {
|
||||
if (event.persisted) {
|
||||
DiscourseURL.routeTo(this.location.pathname);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { computed, get } from "@ember/object";
|
||||
@ -25,7 +26,7 @@ export default Component.extend({
|
||||
}
|
||||
|
||||
const opts = {
|
||||
latestLink: `<a href="${Discourse.getURL("/latest")}">${I18n.t(
|
||||
latestLink: `<a href="${getURL("/latest")}">${I18n.t(
|
||||
"topic.view_latest_topics"
|
||||
)}</a>`
|
||||
};
|
||||
@ -43,7 +44,7 @@ export default Component.extend({
|
||||
} else {
|
||||
opts.catLink =
|
||||
'<a href="' +
|
||||
Discourse.getURL("/categories") +
|
||||
getURL("/categories") +
|
||||
'">' +
|
||||
I18n.t("topic.browse_all_categories") +
|
||||
"</a>";
|
||||
|
||||
@ -8,7 +8,7 @@ import {
|
||||
REMINDER_TYPE,
|
||||
DELETE_REPLIES_TYPE
|
||||
} from "discourse/controllers/edit-topic-timer";
|
||||
import ENV from "discourse-common/config/environment";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ["topic-status-info"],
|
||||
@ -92,7 +92,7 @@ export default Component.extend({
|
||||
});
|
||||
|
||||
// TODO Sam: concerned this can cause a heavy rerender loop
|
||||
if (ENV.environment !== "test") {
|
||||
if (!isTesting()) {
|
||||
this._delayedRerender = later(() => {
|
||||
this.renderTopicTimer();
|
||||
}, rerenderDelay);
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { getURLWithCDN } from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { alias, gte, and, gt, not, or } from "@ember/object/computed";
|
||||
@ -153,7 +154,7 @@ export default Component.extend(CardContentsBase, CanCheckEmails, CleansUp, {
|
||||
}
|
||||
|
||||
const url = this.get("user.card_background_upload_url");
|
||||
const bg = isEmpty(url) ? "" : `url(${Discourse.getURLWithCDN(url)})`;
|
||||
const bg = isEmpty(url) ? "" : `url(${getURLWithCDN(url)})`;
|
||||
thisElem.style.backgroundImage = bg;
|
||||
},
|
||||
|
||||
|
||||
@ -270,11 +270,6 @@ export default Controller.extend(ModalFunctionality, {
|
||||
return this.nextMonth().format(I18n.t("dates.long_no_year"));
|
||||
},
|
||||
|
||||
@discourseComputed()
|
||||
basePath() {
|
||||
return Discourse.BaseUri;
|
||||
},
|
||||
|
||||
@discourseComputed("userTimezone")
|
||||
userHasTimezoneSet(userTimezone) {
|
||||
return !_.isEmpty(userTimezone);
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
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";
|
||||
@ -23,7 +24,7 @@ import { emojiUnescape } from "discourse/lib/text";
|
||||
import { shortDate } from "discourse/lib/formatter";
|
||||
import { SAVE_LABELS, SAVE_ICONS } from "discourse/models/composer";
|
||||
import { Promise } from "rsvp";
|
||||
import ENV from "discourse-common/config/environment";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
import EmberObject, { computed, action } from "@ember/object";
|
||||
import deprecated from "discourse-common/lib/deprecated";
|
||||
|
||||
@ -71,7 +72,7 @@ function loadDraft(store, opts) {
|
||||
|
||||
const _popupMenuOptionsCallbacks = [];
|
||||
|
||||
let _checkDraftPopup = ENV.environment !== "test";
|
||||
let _checkDraftPopup = !isTesting();
|
||||
|
||||
export function toggleCheckDraftPopup(enabled) {
|
||||
_checkDraftPopup = enabled;
|
||||
@ -561,7 +562,7 @@ export default Controller.extend({
|
||||
) {
|
||||
groups.forEach(group => {
|
||||
let body;
|
||||
const groupLink = Discourse.getURL(`/g/${group.name}/members`);
|
||||
const groupLink = getURL(`/g/${group.name}/members`);
|
||||
|
||||
if (group.max_mentions < group.user_count) {
|
||||
body = I18n.t("composer.group_mentioned_limit", {
|
||||
@ -1109,7 +1110,7 @@ export default Controller.extend({
|
||||
if (model.draftSaving) {
|
||||
// in test debounce is Ember.run, this will cause
|
||||
// an infinite loop
|
||||
if (ENV.environment !== "test") {
|
||||
if (!isTesting()) {
|
||||
this._saveDraftDebounce = debounce(this, this._saveDraft, 2000);
|
||||
}
|
||||
} else {
|
||||
@ -1157,6 +1158,7 @@ export default Controller.extend({
|
||||
const tagsArray = tags || [];
|
||||
if (
|
||||
this.site.can_tag_topics &&
|
||||
!this.currentUser.staff &&
|
||||
category &&
|
||||
category.minimum_required_tags > tagsArray.length
|
||||
) {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import { A } from "@ember/array";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
@ -86,10 +87,9 @@ export default Controller.extend(
|
||||
@discourseComputed
|
||||
disclaimerHtml() {
|
||||
return I18n.t("create_account.disclaimer", {
|
||||
tos_link: this.get("siteSettings.tos_url") || Discourse.getURL("/tos"),
|
||||
tos_link: this.get("siteSettings.tos_url") || getURL("/tos"),
|
||||
privacy_link:
|
||||
this.get("siteSettings.privacy_policy_url") ||
|
||||
Discourse.getURL("/privacy")
|
||||
this.get("siteSettings.privacy_policy_url") || getURL("/privacy")
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import Controller from "@ember/controller";
|
||||
import { SECOND_FACTOR_METHODS } from "discourse/models/user";
|
||||
@ -7,7 +8,7 @@ import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { getWebauthnCredential } from "discourse/lib/webauthn";
|
||||
|
||||
export default Controller.extend({
|
||||
lockImageUrl: Discourse.getURL("/images/lock.svg"),
|
||||
lockImageUrl: getURL("/images/lock.svg"),
|
||||
|
||||
@discourseComputed("model")
|
||||
secondFactorRequired(model) {
|
||||
|
||||
@ -6,6 +6,7 @@ import { ajax } from "discourse/lib/ajax";
|
||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||
import { escapeExpression } from "discourse/lib/utilities";
|
||||
import { extractError } from "discourse/lib/ajax-error";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
|
||||
export default Controller.extend(ModalFunctionality, {
|
||||
offerHelp: null,
|
||||
@ -30,7 +31,7 @@ export default Controller.extend(ModalFunctionality, {
|
||||
help() {
|
||||
this.setProperties({
|
||||
offerHelp: I18n.t("forgot_password.help", {
|
||||
basePath: Discourse.BaseUri
|
||||
basePath: getURL("/")
|
||||
}),
|
||||
helpSeen: true
|
||||
});
|
||||
|
||||
@ -1,16 +1,18 @@
|
||||
import I18n from "I18n";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { alias, notEmpty } from "@ember/object/computed";
|
||||
import { alias, notEmpty, or, readOnly } from "@ember/object/computed";
|
||||
import Controller from "@ember/controller";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import getUrl from "discourse-common/lib/get-url";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { emailValid } from "discourse/lib/utilities";
|
||||
import PasswordValidation from "discourse/mixins/password-validation";
|
||||
import UsernameValidation from "discourse/mixins/username-validation";
|
||||
import NameValidation from "discourse/mixins/name-validation";
|
||||
import UserFieldsValidation from "discourse/mixins/user-fields-validation";
|
||||
import { findAll as findLoginMethods } from "discourse/models/login-method";
|
||||
import EmberObject from "@ember/object";
|
||||
|
||||
export default Controller.extend(
|
||||
PasswordValidation,
|
||||
@ -18,7 +20,7 @@ export default Controller.extend(
|
||||
NameValidation,
|
||||
UserFieldsValidation,
|
||||
{
|
||||
invitedBy: alias("model.invited_by"),
|
||||
invitedBy: readOnly("model.invited_by"),
|
||||
email: alias("model.email"),
|
||||
accountUsername: alias("model.username"),
|
||||
passwordRequired: notEmpty("accountPassword"),
|
||||
@ -26,6 +28,21 @@ export default Controller.extend(
|
||||
errorMessage: null,
|
||||
userFields: null,
|
||||
inviteImageUrl: getUrl("/images/envelope.svg"),
|
||||
isInviteLink: readOnly("model.is_invite_link"),
|
||||
submitDisabled: or(
|
||||
"emailValidation.failed",
|
||||
"usernameValidation.failed",
|
||||
"passwordValidation.failed",
|
||||
"nameValidation.failed",
|
||||
"userFieldsValidation.failed"
|
||||
),
|
||||
rejectedEmails: null,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.rejectedEmails = [];
|
||||
},
|
||||
|
||||
@discourseComputed
|
||||
welcomeTitle() {
|
||||
@ -44,21 +61,6 @@ export default Controller.extend(
|
||||
return findLoginMethods().length > 0;
|
||||
},
|
||||
|
||||
@discourseComputed(
|
||||
"usernameValidation.failed",
|
||||
"passwordValidation.failed",
|
||||
"nameValidation.failed",
|
||||
"userFieldsValidation.failed"
|
||||
)
|
||||
submitDisabled(
|
||||
usernameFailed,
|
||||
passwordFailed,
|
||||
nameFailed,
|
||||
userFieldsFailed
|
||||
) {
|
||||
return usernameFailed || passwordFailed || nameFailed || userFieldsFailed;
|
||||
},
|
||||
|
||||
@discourseComputed
|
||||
fullnameRequired() {
|
||||
return (
|
||||
@ -66,6 +68,35 @@ export default Controller.extend(
|
||||
);
|
||||
},
|
||||
|
||||
@discourseComputed("email", "rejectedEmails.[]")
|
||||
emailValidation(email, rejectedEmails) {
|
||||
// If blank, fail without a reason
|
||||
if (isEmpty(email)) {
|
||||
return EmberObject.create({
|
||||
failed: true
|
||||
});
|
||||
}
|
||||
|
||||
if (rejectedEmails.includes(email)) {
|
||||
return EmberObject.create({
|
||||
failed: true,
|
||||
reason: I18n.t("user.email.invalid")
|
||||
});
|
||||
}
|
||||
|
||||
if (emailValid(email)) {
|
||||
return EmberObject.create({
|
||||
ok: true,
|
||||
reason: I18n.t("user.email.ok")
|
||||
});
|
||||
}
|
||||
|
||||
return EmberObject.create({
|
||||
failed: true,
|
||||
reason: I18n.t("user.email.invalid")
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
submit() {
|
||||
const userFields = this.userFields;
|
||||
@ -80,6 +111,7 @@ export default Controller.extend(
|
||||
url: `/invites/show/${this.get("model.token")}.json`,
|
||||
type: "PUT",
|
||||
data: {
|
||||
email: this.email,
|
||||
username: this.accountUsername,
|
||||
name: this.accountName,
|
||||
password: this.accountPassword,
|
||||
@ -97,6 +129,14 @@ export default Controller.extend(
|
||||
DiscourseURL.redirectTo(result.redirect_to);
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
result.errors &&
|
||||
result.errors.email &&
|
||||
result.errors.email.length > 0 &&
|
||||
result.values
|
||||
) {
|
||||
this.rejectedEmails.pushObject(result.values.email);
|
||||
}
|
||||
if (
|
||||
result.errors &&
|
||||
result.errors.password &&
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
@ -372,8 +373,8 @@ export default Controller.extend(ModalFunctionality, {
|
||||
// redirect client to the original URL
|
||||
$.removeCookie("destination_url");
|
||||
window.location.href = destinationUrl;
|
||||
} else if (window.location.pathname === Discourse.getURL("/login")) {
|
||||
window.location = Discourse.getURL("/");
|
||||
} else if (window.location.pathname === getURL("/login")) {
|
||||
window.location = getURL("/");
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import { alias, or, readOnly } from "@ember/object/computed";
|
||||
import Controller from "@ember/controller";
|
||||
@ -41,10 +42,10 @@ export default Controller.extend(PasswordValidation, {
|
||||
|
||||
@discourseComputed("redirectTo")
|
||||
redirectHref(redirectTo) {
|
||||
return Discourse.getURL(redirectTo || "/");
|
||||
return getURL(redirectTo || "/");
|
||||
},
|
||||
|
||||
lockImageUrl: Discourse.getURL("/images/lock.svg"),
|
||||
lockImageUrl: getURL("/images/lock.svg"),
|
||||
|
||||
actions: {
|
||||
submit() {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import { not, or, gt } from "@ember/object/computed";
|
||||
import Controller from "@ember/controller";
|
||||
@ -11,6 +12,7 @@ import { findAll } from "discourse/models/login-method";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { userPath } from "discourse/lib/url";
|
||||
import logout from "discourse/lib/logout";
|
||||
import EmberObject from "@ember/object";
|
||||
|
||||
// Number of tokens shown by default.
|
||||
const DEFAULT_AUTH_TOKENS_COUNT = 2;
|
||||
@ -94,6 +96,39 @@ export default Controller.extend(CanCheckEmails, {
|
||||
|
||||
disableConnectButtons: propertyNotEqual("model.id", "currentUser.id"),
|
||||
|
||||
@discourseComputed(
|
||||
"model.email",
|
||||
"model.secondary_emails.[]",
|
||||
"model.unconfirmed_emails.[]"
|
||||
)
|
||||
emails(primaryEmail, secondaryEmails, unconfirmedEmails) {
|
||||
const emails = [];
|
||||
|
||||
if (primaryEmail) {
|
||||
emails.push(
|
||||
EmberObject.create({
|
||||
email: primaryEmail,
|
||||
primary: true,
|
||||
confirmed: true
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (secondaryEmails) {
|
||||
secondaryEmails.forEach(email => {
|
||||
emails.push(EmberObject.create({ email, confirmed: true }));
|
||||
});
|
||||
}
|
||||
|
||||
if (unconfirmedEmails) {
|
||||
unconfirmedEmails.forEach(email => {
|
||||
emails.push(EmberObject.create({ email }));
|
||||
});
|
||||
}
|
||||
|
||||
return emails.sort((a, b) => a.email.localeCompare(b.email));
|
||||
},
|
||||
|
||||
@discourseComputed(
|
||||
"model.second_factor_enabled",
|
||||
"canCheckEmails",
|
||||
@ -148,6 +183,26 @@ export default Controller.extend(CanCheckEmails, {
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
|
||||
setPrimaryEmail(email) {
|
||||
this.model.setPrimaryEmail(email).catch(popupAjaxError);
|
||||
},
|
||||
|
||||
destroyEmail(email) {
|
||||
this.model.destroyEmail(email);
|
||||
},
|
||||
|
||||
resendConfirmationEmail(email) {
|
||||
email.set("resending", true);
|
||||
this.model
|
||||
.addEmail(email.email)
|
||||
.then(() => {
|
||||
email.set("resent", true);
|
||||
})
|
||||
.finally(() => {
|
||||
email.set("resending", false);
|
||||
});
|
||||
},
|
||||
|
||||
changePassword() {
|
||||
if (!this.passwordProgress) {
|
||||
this.set(
|
||||
@ -195,7 +250,7 @@ export default Controller.extend(CanCheckEmails, {
|
||||
() => {
|
||||
bootbox.alert(
|
||||
I18n.t("user.deleted_yourself"),
|
||||
() => (window.location = Discourse.getURL("/"))
|
||||
() => (window.location = getURL("/"))
|
||||
);
|
||||
},
|
||||
() => {
|
||||
|
||||
@ -7,10 +7,13 @@ import EmberObject from "@ember/object";
|
||||
import { emailValid } from "discourse/lib/utilities";
|
||||
|
||||
export default Controller.extend({
|
||||
queryParams: ["new"],
|
||||
|
||||
taken: false,
|
||||
saving: false,
|
||||
error: false,
|
||||
success: false,
|
||||
oldEmail: null,
|
||||
newEmail: null,
|
||||
|
||||
newEmailEmpty: empty("newEmail"),
|
||||
@ -23,16 +26,17 @@ export default Controller.extend({
|
||||
"invalidEmail"
|
||||
),
|
||||
|
||||
unchanged: propertyEqual("newEmailLower", "currentUser.email"),
|
||||
unchanged: propertyEqual("newEmailLower", "oldEmail"),
|
||||
|
||||
@discourseComputed("newEmail")
|
||||
newEmailLower(newEmail) {
|
||||
return newEmail.toLowerCase().trim();
|
||||
},
|
||||
|
||||
@discourseComputed("saving")
|
||||
saveButtonText(saving) {
|
||||
@discourseComputed("saving", "new")
|
||||
saveButtonText(saving, isNew) {
|
||||
if (saving) return I18n.t("saving");
|
||||
if (isNew) return I18n.t("user.add_email.add");
|
||||
return I18n.t("user.change");
|
||||
},
|
||||
|
||||
@ -41,9 +45,9 @@ export default Controller.extend({
|
||||
return !emailValid(newEmail);
|
||||
},
|
||||
|
||||
@discourseComputed("invalidEmail")
|
||||
emailValidation(invalidEmail) {
|
||||
if (invalidEmail) {
|
||||
@discourseComputed("invalidEmail", "oldEmail", "newEmail")
|
||||
emailValidation(invalidEmail, oldEmail, newEmail) {
|
||||
if (invalidEmail && (oldEmail || newEmail)) {
|
||||
return EmberObject.create({
|
||||
failed: true,
|
||||
reason: I18n.t("user.email.invalid")
|
||||
@ -62,10 +66,13 @@ export default Controller.extend({
|
||||
},
|
||||
|
||||
actions: {
|
||||
changeEmail() {
|
||||
saveEmail() {
|
||||
this.set("saving", true);
|
||||
|
||||
return this.model.changeEmail(this.newEmail).then(
|
||||
return (this.new
|
||||
? this.model.addEmail(this.newEmail)
|
||||
: this.model.changeEmail(this.newEmail)
|
||||
).then(
|
||||
() => this.set("success", true),
|
||||
e => {
|
||||
this.setProperties({ error: true, saving: false });
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import I18n from "I18n";
|
||||
import { equal, reads, gte } from "@ember/object/computed";
|
||||
import { equal, reads } from "@ember/object/computed";
|
||||
import Controller from "@ember/controller";
|
||||
import Invite from "discourse/models/invite";
|
||||
import discourseDebounce from "discourse/lib/debounce";
|
||||
@ -35,21 +35,30 @@ export default Controller.extend({
|
||||
}, INPUT_DELAY),
|
||||
|
||||
inviteRedeemed: equal("filter", "redeemed"),
|
||||
invitePending: equal("filter", "pending"),
|
||||
|
||||
@discourseComputed("filter")
|
||||
inviteLinks(filter) {
|
||||
return filter === "links" && this.currentUser.staff;
|
||||
},
|
||||
|
||||
@discourseComputed("filter")
|
||||
showBulkActionButtons(filter) {
|
||||
return (
|
||||
filter === "pending" &&
|
||||
this.model.invites.length > 4 &&
|
||||
this.currentUser.get("staff")
|
||||
this.currentUser.staff
|
||||
);
|
||||
},
|
||||
|
||||
canInviteToForum: reads("currentUser.can_invite_to_forum"),
|
||||
|
||||
canBulkInvite: reads("currentUser.admin"),
|
||||
canSendInviteLink: reads("currentUser.staff"),
|
||||
|
||||
showSearch: gte("totalInvites", 10),
|
||||
@discourseComputed("totalInvites", "inviteLinks")
|
||||
showSearch(totalInvites, inviteLinks) {
|
||||
return totalInvites >= 10 && !inviteLinks;
|
||||
},
|
||||
|
||||
@discourseComputed("invitesCount.total", "invitesCount.pending")
|
||||
pendingLabel(invitesCountTotal, invitesCountPending) {
|
||||
@ -73,6 +82,17 @@ export default Controller.extend({
|
||||
}
|
||||
},
|
||||
|
||||
@discourseComputed("invitesCount.total", "invitesCount.links")
|
||||
linksLabel(invitesCountTotal, invitesCountLinks) {
|
||||
if (invitesCountTotal > 50) {
|
||||
return I18n.t("user.invited.links_tab_with_count", {
|
||||
count: invitesCountLinks
|
||||
});
|
||||
} else {
|
||||
return I18n.t("user.invited.links_tab");
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
rescind(invite) {
|
||||
invite.rescind();
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import { get } from "@ember/object";
|
||||
import { registerUnbound } from "discourse-common/lib/helpers";
|
||||
@ -104,9 +105,7 @@ function defaultCategoryLinkRenderer(category, opts) {
|
||||
let restricted = get(category, "read_restricted");
|
||||
let url = opts.url
|
||||
? opts.url
|
||||
: Discourse.getURL(
|
||||
`/c/${Category.slugFor(category)}/${get(category, "id")}`
|
||||
);
|
||||
: getURL(`/c/${Category.slugFor(category)}/${get(category, "id")}`);
|
||||
let href = opts.link === false ? "" : url;
|
||||
let tagName = opts.link === false || opts.link === "false" ? "span" : "a";
|
||||
let extraClasses = opts.extraClasses ? " " + opts.extraClasses : "";
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import { currentThemeIds, refreshCSS } from "discourse/lib/theme-selector";
|
||||
import ENV from "discourse-common/config/environment";
|
||||
import { isDevelopment } from "discourse-common/config/environment";
|
||||
import Handlebars from "handlebars";
|
||||
|
||||
// Use the message bus for live reloading of components for faster development.
|
||||
@ -48,7 +48,7 @@ export default {
|
||||
});
|
||||
|
||||
// Useful to export this for debugging purposes
|
||||
if (Discourse.Environment === "development" && ENV.environment !== "test") {
|
||||
if (isDevelopment()) {
|
||||
window.DiscourseURL = DiscourseURL;
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
// Initialize the message bus to receive messages.
|
||||
import userPresent from "discourse/lib/user-presence";
|
||||
import { handleLogoff } from "discourse/lib/ajax";
|
||||
import { isProduction } from "discourse-common/config/environment";
|
||||
|
||||
const LONG_POLL_AFTER_UNSEEN_TIME = 1200000; // 20 minutes
|
||||
|
||||
@ -32,7 +34,7 @@ export default {
|
||||
user = container.lookup("current-user:main"),
|
||||
siteSettings = container.lookup("site-settings:main");
|
||||
|
||||
messageBus.alwaysLongPoll = Discourse.Environment === "development";
|
||||
messageBus.alwaysLongPoll = !isProduction();
|
||||
messageBus.shouldLongPollCallback = () =>
|
||||
userPresent(LONG_POLL_AFTER_UNSEEN_TIME);
|
||||
|
||||
@ -83,7 +85,7 @@ export default {
|
||||
return ajax(opts);
|
||||
};
|
||||
|
||||
messageBus.baseUrl = Discourse.getURL("/");
|
||||
messageBus.baseUrl = getURL("/");
|
||||
}
|
||||
|
||||
if (user) {
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import { isAbsoluteURL } from "discourse-common/lib/get-url";
|
||||
import getAbsoluteURL from "discourse-common/lib/get-url";
|
||||
|
||||
export default {
|
||||
name: "register-service-worker",
|
||||
|
||||
@ -30,7 +33,7 @@ export default {
|
||||
});
|
||||
|
||||
navigator.serviceWorker
|
||||
.register(`${Discourse.BaseUri}/${Discourse.ServiceWorkerURL}`)
|
||||
.register(getAbsoluteURL(`/${Discourse.ServiceWorkerURL}`))
|
||||
.catch(error => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.info(`Failed to register Service Worker: ${error}`);
|
||||
@ -46,7 +49,7 @@ export default {
|
||||
},
|
||||
|
||||
unregister(registration) {
|
||||
if (registration.scope.startsWith(Discourse.BaseUrl)) {
|
||||
if (isAbsoluteURL(registration.scope)) {
|
||||
registration.unregister();
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ import {
|
||||
unsubscribe as unsubscribePushNotifications,
|
||||
isPushNotificationsEnabled
|
||||
} from "discourse/lib/push-notifications";
|
||||
import ENV from "discourse-common/config/environment";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
|
||||
export default {
|
||||
name: "subscribe-user-notifications",
|
||||
@ -130,7 +130,7 @@ export default {
|
||||
Discourse.set("assetVersion", data)
|
||||
);
|
||||
|
||||
if (ENV.environment !== "test") {
|
||||
if (!isTesting()) {
|
||||
bus.subscribe(alertChannel(user), data => onNotification(data, user));
|
||||
initDesktopNotifications(bus, appEvents);
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import { run } from "@ember/runloop";
|
||||
import userPresent from "discourse/lib/user-presence";
|
||||
@ -159,7 +160,7 @@ export function ajax() {
|
||||
args.cache = true; // Disable JQuery cache busting param, which was created to deal with IE8
|
||||
}
|
||||
|
||||
ajaxObj = $.ajax(Discourse.getURL(url), args);
|
||||
ajaxObj = $.ajax(getURL(url), args);
|
||||
}
|
||||
|
||||
let promise;
|
||||
@ -169,7 +170,7 @@ export function ajax() {
|
||||
if (
|
||||
args.type &&
|
||||
args.type.toUpperCase() !== "GET" &&
|
||||
url !== Discourse.getURL("/clicks/track") &&
|
||||
url !== getURL("/clicks/track") &&
|
||||
!Session.currentProp("csrfToken")
|
||||
) {
|
||||
promise = new Promise((resolve, reject) => {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import discourseDebounce from "discourse/lib/debounce";
|
||||
import { CANCELLED_STATUS } from "discourse/lib/autocomplete";
|
||||
import Category from "discourse/models/category";
|
||||
@ -27,7 +28,7 @@ function searchTags(term, categories, limit) {
|
||||
);
|
||||
|
||||
const debouncedSearch = discourseDebounce((q, cats, resultFunc) => {
|
||||
oldSearch = $.ajax(Discourse.getURL("/tags/filter/search"), {
|
||||
oldSearch = $.ajax(getURL("/tags/filter/search"), {
|
||||
type: "GET",
|
||||
cache: true,
|
||||
data: { limit: limit, q }
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import { later } from "@ember/runloop";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
@ -5,7 +6,7 @@ import DiscourseURL from "discourse/lib/url";
|
||||
import { wantsNewWindow } from "discourse/lib/intercept-click";
|
||||
import { selectedText } from "discourse/lib/utilities";
|
||||
import { Promise } from "rsvp";
|
||||
import ENV from "discourse-common/config/environment";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
import User from "discourse/models/user";
|
||||
|
||||
export function isValidLink($link) {
|
||||
@ -101,14 +102,14 @@ export default {
|
||||
|
||||
let trackPromise = Promise.resolve();
|
||||
if (tracking) {
|
||||
if (ENV.environment !== "test" && navigator.sendBeacon) {
|
||||
if (!isTesting() && navigator.sendBeacon) {
|
||||
const data = new FormData();
|
||||
data.append("url", href);
|
||||
data.append("post_id", postId);
|
||||
data.append("topic_id", topicId);
|
||||
navigator.sendBeacon(Discourse.getURL("/clicks/track"), data);
|
||||
navigator.sendBeacon(getURL("/clicks/track"), data);
|
||||
} else {
|
||||
trackPromise = ajax(Discourse.getURL("/clicks/track"), {
|
||||
trackPromise = ajax(getURL("/clicks/track"), {
|
||||
type: "POST",
|
||||
data: {
|
||||
url: href,
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import { computed } from "@ember/object";
|
||||
import { htmlSafe as htmlSafeTemplateHelper } from "@ember/template";
|
||||
@ -126,7 +127,7 @@ export function fmt(...args) {
|
||||
export function url(...args) {
|
||||
const format = args.pop();
|
||||
return computed(...args, function() {
|
||||
return Discourse.getURL(addonFmt(format, ...args.map(a => this.get(a))));
|
||||
return getURL(addonFmt(format, ...args.map(a => this.get(a))));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import EmberObject from "@ember/object";
|
||||
import { defaultHomepage } from "discourse/lib/utilities";
|
||||
import { guidFor } from "@ember/object/internals";
|
||||
import { withoutPrefix } from "discourse-common/lib/get-url";
|
||||
let popstateFired = false;
|
||||
const supportsHistoryState = window.history && "state" in window.history;
|
||||
const popstateCallbacks = [];
|
||||
@ -62,10 +63,7 @@ const DiscourseLocation = EmberObject.extend({
|
||||
@method getURL
|
||||
*/
|
||||
getURL() {
|
||||
let url = this.location.pathname;
|
||||
|
||||
url = url.replace(new RegExp(`^${Discourse.BaseUri}`), "");
|
||||
|
||||
let url = withoutPrefix(this.location.pathname);
|
||||
const search = this.location.search || "";
|
||||
url += search;
|
||||
return url;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import ENV from "discourse-common/config/environment";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
import AppEvents from "discourse/services/app-events";
|
||||
|
||||
let _skipUpdate;
|
||||
@ -9,7 +9,7 @@ export function configureEyeline(opts) {
|
||||
_skipUpdate = opts.skipUpdate;
|
||||
_rootElement = opts.rootElement;
|
||||
} else {
|
||||
_skipUpdate = ENV.environment === "test";
|
||||
_skipUpdate = isTesting();
|
||||
_rootElement = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -471,9 +471,7 @@ export default {
|
||||
},
|
||||
|
||||
_bindToPath(path, key) {
|
||||
this.keyTrapper.bind(key, () =>
|
||||
DiscourseURL.routeTo(Discourse.BaseUri + path)
|
||||
);
|
||||
this.keyTrapper.bind(key, () => DiscourseURL.routeTo(path));
|
||||
},
|
||||
|
||||
_bindToClick(selector, binding) {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import { schedule } from "@ember/runloop";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { userPath } from "discourse/lib/url";
|
||||
@ -15,7 +16,7 @@ function replaceSpan($e, username, opts) {
|
||||
extraClass = "notify";
|
||||
}
|
||||
$e.replaceWith(
|
||||
`<a href='${Discourse.getURL("/g/") +
|
||||
`<a href='${getURL("/g/") +
|
||||
username}' class='mention-group ${extraClass}' ${extra}>@${username}</a>`
|
||||
);
|
||||
} else {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { default as getURL, getURLWithCDN } from "discourse-common/lib/get-url";
|
||||
import { run } from "@ember/runloop";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { Promise } from "rsvp";
|
||||
@ -51,9 +52,7 @@ export default function loadScript(url, opts) {
|
||||
|
||||
// Scripts should always load from CDN
|
||||
// CSS is type text, to accept it from a CDN we would need to handle CORS
|
||||
const fullUrl = opts.css
|
||||
? Discourse.getURL(url)
|
||||
: Discourse.getURLWithCDN(url);
|
||||
const fullUrl = opts.css ? getURL(url) : getURLWithCDN(url);
|
||||
|
||||
$("script").each((i, tag) => {
|
||||
const src = tag.getAttribute("src");
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { findAll } from "discourse/models/login-method";
|
||||
|
||||
@ -23,9 +24,9 @@ export default function logout(siteSettings, keyValueStore) {
|
||||
if (siteSettings.login_required && (sso || oneAuthenticator)) {
|
||||
// In this situation visiting most URLs will start the auth process again
|
||||
// Go to the `/login` page to avoid an immediate redirect
|
||||
window.location.href = Discourse.getURL("/login");
|
||||
window.location.href = getURL("/login");
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.href = Discourse.getURL("/");
|
||||
window.location.href = getURL("/");
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import ENV from "discourse-common/config/environment";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
|
||||
let mobileForced = false;
|
||||
|
||||
@ -12,7 +12,7 @@ const Mobile = {
|
||||
this.isMobileDevice = mobileForced || $html.hasClass("mobile-device");
|
||||
this.mobileView = mobileForced || $html.hasClass("mobile-view");
|
||||
|
||||
if (ENV.environment === "test" || mobileForced) {
|
||||
if (isTesting() || mobileForced) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import { next } from "@ember/runloop";
|
||||
let _started = false;
|
||||
let cache = {};
|
||||
@ -28,7 +29,7 @@ export function startPageTracking(router, appEvents) {
|
||||
transition.urlMethod === "replace" && transition.queryParamsOnly;
|
||||
|
||||
router.send("refreshTitle");
|
||||
const url = Discourse.getURL(router.get("url"));
|
||||
const url = getURL(router.get("url"));
|
||||
|
||||
// Refreshing the title is debounced, so we need to trigger this in the
|
||||
// next runloop to have the correct title.
|
||||
|
||||
@ -28,7 +28,10 @@ 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 } from "discourse/widgets/header";
|
||||
import {
|
||||
attachAdditionalPanel,
|
||||
addToHeaderIcons
|
||||
} from "discourse/widgets/header";
|
||||
import {
|
||||
registerIconRenderer,
|
||||
replaceIcon
|
||||
@ -1141,6 +1144,19 @@ class PluginApi {
|
||||
addCategoryLinkIcon(renderer) {
|
||||
addExtraIconRenderer(renderer);
|
||||
}
|
||||
/**
|
||||
* Adds a widget to the header-icon ul. The widget must already be created. You can create new widgets
|
||||
* in a theme or plugin via an initializer prior to calling this function.
|
||||
*
|
||||
* ```
|
||||
* api.addToHeaderIcons(
|
||||
* createWidget('some-widget')
|
||||
* ```
|
||||
*
|
||||
**/
|
||||
addToHeaderIcons(icon) {
|
||||
addToHeaderIcons(icon);
|
||||
}
|
||||
}
|
||||
|
||||
let _pluginv01;
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import User from "discourse/models/user";
|
||||
import { escapeExpression } from "discourse/lib/utilities";
|
||||
|
||||
@ -24,7 +25,7 @@ export function defaultRenderTag(tag, params) {
|
||||
path = `/tag/${tag}`;
|
||||
}
|
||||
}
|
||||
const href = path ? ` href='${Discourse.getURL(path)}' ` : "";
|
||||
const href = path ? ` href='${getURL(path)}' ` : "";
|
||||
|
||||
if (Discourse.SiteSettings.tag_style || params.style) {
|
||||
classes.push(params.style || Discourse.SiteSettings.tag_style);
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import EmberObject from "@ember/object";
|
||||
@ -67,7 +68,7 @@ export function translateResults(results, opts) {
|
||||
flairBgColor,
|
||||
fullName,
|
||||
name,
|
||||
url: Discourse.getURL(`/g/${name}`)
|
||||
url: getURL(`/g/${name}`)
|
||||
};
|
||||
})
|
||||
.compact();
|
||||
@ -77,7 +78,7 @@ export function translateResults(results, opts) {
|
||||
const tagName = escapeExpression(tag.name);
|
||||
return EmberObject.create({
|
||||
id: tagName,
|
||||
url: Discourse.getURL("/tag/" + tagName)
|
||||
url: getURL("/tag/" + tagName)
|
||||
});
|
||||
})
|
||||
.compact();
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { getURLWithCDN } from "discourse-common/lib/get-url";
|
||||
import PrettyText, { buildOptions } from "pretty-text/pretty-text";
|
||||
import { performEmojiUnescape, buildEmojiUrl } from "pretty-text/emoji";
|
||||
import WhiteLister from "pretty-text/white-lister";
|
||||
@ -7,8 +8,6 @@ import { formatUsername } from "discourse/lib/utilities";
|
||||
import { Promise } from "rsvp";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
|
||||
const getURLWithCDN = url => Discourse.getURLWithCDN(url);
|
||||
|
||||
function getOpts(opts) {
|
||||
const siteSettings = Discourse.__container__.lookup("site-settings:main"),
|
||||
site = Discourse.__container__.lookup("site:main");
|
||||
@ -39,6 +38,15 @@ export function cookAsync(text, options) {
|
||||
return loadMarkdownIt().then(() => cook(text, options));
|
||||
}
|
||||
|
||||
// Warm up pretty text with a set of options and return a function
|
||||
// which can be used to cook without rebuilding prettytext every time
|
||||
export function generateCookFunction(options) {
|
||||
return loadMarkdownIt().then(() => {
|
||||
const prettyText = createPrettyText(options);
|
||||
return text => prettyText.cook(text);
|
||||
});
|
||||
}
|
||||
|
||||
export function sanitize(text, options) {
|
||||
return textSanitize(text, new WhiteLister(options));
|
||||
}
|
||||
|
||||
@ -47,6 +47,7 @@ export function transformBasicPost(post) {
|
||||
new_user: post.trust_level === 0,
|
||||
name: post.name,
|
||||
user_title: post.user_title,
|
||||
title_is_group: post.title_is_group,
|
||||
created_at: post.created_at,
|
||||
updated_at: post.updated_at,
|
||||
canDelete: post.can_delete,
|
||||
|
||||
@ -5,6 +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";
|
||||
|
||||
const rewrites = [];
|
||||
const TOPIC_REGEXP = /\/t\/([^\/]+)\/(\d+)\/?(\d+)?/;
|
||||
@ -55,11 +56,11 @@ export function clearRewrites() {
|
||||
}
|
||||
|
||||
export function userPath(subPath) {
|
||||
return Discourse.getURL(subPath ? `/u/${subPath}` : "/u");
|
||||
return getURL(subPath ? `/u/${subPath}` : "/u");
|
||||
}
|
||||
|
||||
export function groupPath(subPath) {
|
||||
return Discourse.getURL(subPath ? `/g/${subPath}` : "/g");
|
||||
return getURL(subPath ? `/g/${subPath}` : "/g");
|
||||
}
|
||||
|
||||
let _jumpScheduled = false;
|
||||
@ -210,11 +211,10 @@ const DiscourseURL = EmberObject.extend({
|
||||
}
|
||||
|
||||
if (Discourse.get("requiresRefresh")) {
|
||||
return redirectTo(Discourse.getURL(path));
|
||||
return redirectTo(getURL(path));
|
||||
}
|
||||
|
||||
const pathname = path.replace(/(https?\:)?\/\/[^\/]+/, "");
|
||||
const baseUri = Discourse.BaseUri;
|
||||
|
||||
if (!DiscourseURL.isInternal(path)) {
|
||||
return redirectTo(path);
|
||||
@ -241,13 +241,13 @@ const DiscourseURL = EmberObject.extend({
|
||||
path = path.replace(/(https?\:)?\/\/[^\/]+/, "");
|
||||
|
||||
// Rewrite /my/* urls
|
||||
let myPath = `${baseUri}/my/`;
|
||||
let myPath = getURL("/my");
|
||||
if (path.indexOf(myPath) === 0) {
|
||||
const currentUser = User.current();
|
||||
if (currentUser) {
|
||||
path = path.replace(
|
||||
myPath,
|
||||
userPath(currentUser.get("username_lower") + "/")
|
||||
userPath(currentUser.get("username_lower"))
|
||||
);
|
||||
} else {
|
||||
return redirectTo("/login-preferences");
|
||||
@ -256,11 +256,7 @@ const DiscourseURL = EmberObject.extend({
|
||||
|
||||
// handle prefixes
|
||||
if (path.indexOf("/") === 0) {
|
||||
const rootURL = (baseUri === undefined ? "/" : baseUri).replace(
|
||||
/\/$/,
|
||||
""
|
||||
);
|
||||
path = path.replace(rootURL, "");
|
||||
path = withoutPrefix(path);
|
||||
}
|
||||
|
||||
path = rewritePath(path);
|
||||
@ -293,7 +289,7 @@ const DiscourseURL = EmberObject.extend({
|
||||
},
|
||||
|
||||
routeToUrl(url, opts = {}) {
|
||||
this.routeTo(Discourse.getURL(url), opts);
|
||||
this.routeTo(getURL(url), opts);
|
||||
},
|
||||
|
||||
rewrite(regexp, replacement, opts) {
|
||||
@ -301,7 +297,7 @@ const DiscourseURL = EmberObject.extend({
|
||||
},
|
||||
|
||||
redirectTo(url) {
|
||||
window.location = Discourse.getURL(url);
|
||||
window.location = getURL(url);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -420,10 +416,8 @@ const DiscourseURL = EmberObject.extend({
|
||||
|
||||
// This has been extracted so it can be tested.
|
||||
origin() {
|
||||
return (
|
||||
window.location.origin +
|
||||
(Discourse.BaseUri === "/" ? "" : Discourse.BaseUri)
|
||||
);
|
||||
let prefix = getURL("/");
|
||||
return window.location.origin + (prefix === "/" ? "" : prefix);
|
||||
},
|
||||
|
||||
// TODO: These container calls can be replaced eventually if we migrate this to a service
|
||||
|
||||
@ -2,6 +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";
|
||||
|
||||
const homepageSelector = "meta[name=discourse_current_homepage]";
|
||||
|
||||
@ -55,18 +56,15 @@ export function getRawSize(size) {
|
||||
return size * Math.min(3, Math.max(1, Math.round(pixelRatio)));
|
||||
}
|
||||
|
||||
const getURLWithCDN = url => Discourse.getURLWithCDN(url);
|
||||
|
||||
export function avatarImg(options, getURL) {
|
||||
getURL = getURL || getURLWithCDN;
|
||||
|
||||
export function avatarImg(options, customGetURL) {
|
||||
const size = translateSize(options.size);
|
||||
const url = avatarUrl(options.avatarTemplate, size);
|
||||
let path = avatarUrl(options.avatarTemplate, size);
|
||||
|
||||
// We won't render an invalid url
|
||||
if (!url || url.length === 0) {
|
||||
if (!path || path.length === 0) {
|
||||
return "";
|
||||
}
|
||||
path = (customGetURL || getURLWithCDN)(path);
|
||||
|
||||
const classes =
|
||||
"avatar" + (options.extraClasses ? " " + options.extraClasses : "");
|
||||
@ -77,19 +75,7 @@ export function avatarImg(options, getURL) {
|
||||
title = ` title='${escaped}' aria-label='${escaped}'`;
|
||||
}
|
||||
|
||||
return (
|
||||
"<img alt='' width='" +
|
||||
size +
|
||||
"' height='" +
|
||||
size +
|
||||
"' src='" +
|
||||
getURL(url) +
|
||||
"' class='" +
|
||||
classes +
|
||||
"'" +
|
||||
title +
|
||||
">"
|
||||
);
|
||||
return `<img alt='' width='${size}' height='${size}' src='${path}' class='${classes}'${title}>`;
|
||||
}
|
||||
|
||||
export function tinyAvatar(avatarTemplate, options) {
|
||||
@ -99,7 +85,7 @@ export function tinyAvatar(avatarTemplate, options) {
|
||||
}
|
||||
|
||||
export function postUrl(slug, topicId, postNumber) {
|
||||
var url = Discourse.getURL("/t/");
|
||||
var url = getURL("/t/");
|
||||
if (slug) {
|
||||
url += slug + "/";
|
||||
} else {
|
||||
@ -430,7 +416,7 @@ function reportToLogster(name, error) {
|
||||
stacktrace: error.stack
|
||||
};
|
||||
|
||||
Ember.$.ajax(`${Discourse.BaseUri}/logs/report_js_error`, {
|
||||
Ember.$.ajax(getURL("/logs/report_js_error"), {
|
||||
data,
|
||||
type: "POST",
|
||||
cache: false
|
||||
@ -447,7 +433,7 @@ export function rescueThemeError(name, error, api) {
|
||||
return;
|
||||
}
|
||||
|
||||
const path = `${Discourse.BaseUri}/admin/customize/themes`;
|
||||
const path = getURL(`/admin/customize/themes`);
|
||||
const message = I18n.t("themes.broken_theme_alert", {
|
||||
theme: name,
|
||||
path: `<a href="${path}">${path}</a>`
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
import EmberRouter from "@ember/routing/router";
|
||||
import { defaultHomepage } from "discourse/lib/utilities";
|
||||
import { rewritePath } from "discourse/lib/url";
|
||||
import ENV from "discourse-common/config/environment";
|
||||
import Site from "discourse/models/site";
|
||||
|
||||
const rootURL = Discourse.BaseUri;
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
|
||||
const BareRouter = EmberRouter.extend({
|
||||
rootURL,
|
||||
location: ENV.environment === "test" ? "none" : "discourse-location",
|
||||
location: isTesting() ? "none" : "discourse-location",
|
||||
|
||||
handleURL(url) {
|
||||
url = rewritePath(url);
|
||||
@ -136,10 +134,10 @@ export function mapRoutes() {
|
||||
}
|
||||
});
|
||||
|
||||
return BareRouter.extend().map(function() {
|
||||
return BareRouter.extend({
|
||||
rootURL: getURL("/")
|
||||
}).map(function() {
|
||||
tree.mapRoutes(this);
|
||||
this.route("unknown", { path: "*path" });
|
||||
});
|
||||
}
|
||||
|
||||
export default BareRouter;
|
||||
|
||||
@ -2,11 +2,11 @@ import { scheduleOnce } from "@ember/runloop";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import { deprecated } from "discourse/mixins/scroll-top";
|
||||
import Mixin from "@ember/object/mixin";
|
||||
import ENV from "discourse-common/config/environment";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
|
||||
const context = {
|
||||
_scrollTop() {
|
||||
if (ENV.environment === "test") {
|
||||
if (isTesting()) {
|
||||
return;
|
||||
}
|
||||
$(document).scrollTop(0);
|
||||
|
||||
@ -39,8 +39,10 @@ export default Mixin.create({
|
||||
|
||||
_initialize: on("didInsertElement", function() {
|
||||
const $upload = $(this.element);
|
||||
const reset = () =>
|
||||
const reset = () => {
|
||||
this.setProperties({ uploading: false, uploadProgress: 0 });
|
||||
document.getElementsByClassName("hidden-upload-field")[0].value = "";
|
||||
};
|
||||
const maxFiles = this.getWithDefault(
|
||||
"maxFiles",
|
||||
this.siteSettings.simultaneous_uploads
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { none } from "@ember/object/computed";
|
||||
import EmberObject from "@ember/object";
|
||||
@ -11,7 +12,7 @@ const Badge = RestModel.extend({
|
||||
|
||||
@discourseComputed
|
||||
url() {
|
||||
return Discourse.getURL(`/badges/${this.id}/${this.slug}`);
|
||||
return getURL(`/badges/${this.id}/${this.slug}`);
|
||||
},
|
||||
|
||||
updateFromJson(json) {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import Category from "discourse/models/category";
|
||||
import User from "discourse/models/user";
|
||||
@ -19,7 +20,7 @@ const Bookmark = RestModel.extend({
|
||||
|
||||
@computed
|
||||
get url() {
|
||||
return Discourse.getURL(`/bookmarks/${this.id}`);
|
||||
return getURL(`/bookmarks/${this.id}`);
|
||||
},
|
||||
|
||||
destroy() {
|
||||
@ -37,7 +38,7 @@ const Bookmark = RestModel.extend({
|
||||
|
||||
// Helper to build a Url with a post number
|
||||
urlForPostNumber(postNumber) {
|
||||
let url = Discourse.getURL(`/t/${this.topic_id}`);
|
||||
let url = getURL(`/t/${this.topic_id}`);
|
||||
if (postNumber > 0) {
|
||||
url += `/${postNumber}`;
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { get } from "@ember/object";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
@ -96,7 +97,7 @@ const Category = RestModel.extend({
|
||||
|
||||
@discourseComputed("name")
|
||||
url() {
|
||||
return Discourse.getURL(`/c/${Category.slugFor(this)}/${this.id}`);
|
||||
return getURL(`/c/${Category.slugFor(this)}/${this.id}`);
|
||||
},
|
||||
|
||||
@discourseComputed
|
||||
@ -187,7 +188,8 @@ const Category = RestModel.extend({
|
||||
),
|
||||
search_priority: this.search_priority,
|
||||
reviewable_by_group_name: this.reviewable_by_group_name,
|
||||
read_only_banner: this.read_only_banner
|
||||
read_only_banner: this.read_only_banner,
|
||||
default_list_filter: this.default_list_filter
|
||||
},
|
||||
type: id ? "PUT" : "POST"
|
||||
});
|
||||
|
||||
@ -374,7 +374,7 @@ const Composer = RestModel.extend({
|
||||
"tags",
|
||||
"topicFirstPost",
|
||||
"minimumRequiredTags",
|
||||
"isStaffUser"
|
||||
"user.staff"
|
||||
)
|
||||
cantSubmitPost(
|
||||
loading,
|
||||
|
||||
@ -10,7 +10,7 @@ const Invite = EmberObject.extend({
|
||||
rescind() {
|
||||
ajax("/invites", {
|
||||
type: "DELETE",
|
||||
data: { email: this.email }
|
||||
data: { id: this.id }
|
||||
});
|
||||
this.set("rescinded", true);
|
||||
},
|
||||
@ -42,7 +42,14 @@ Invite.reopenClass({
|
||||
if (!isNone(search)) data.search = search;
|
||||
data.offset = offset || 0;
|
||||
|
||||
return ajax(userPath(`${user.username_lower}/invited.json`), {
|
||||
let path;
|
||||
if (filter === "links") {
|
||||
path = userPath(`${user.username_lower}/invite_links.json`);
|
||||
} else {
|
||||
path = userPath(`${user.username_lower}/invited.json`);
|
||||
}
|
||||
|
||||
return ajax(path, {
|
||||
data
|
||||
}).then(result => {
|
||||
result.invites = result.invites.map(i => Invite.create(i));
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import EmberObject from "@ember/object";
|
||||
@ -33,7 +34,7 @@ const LoginMethod = EmberObject.extend({
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let authUrl = Discourse.getURL(`/auth/${this.name}`);
|
||||
let authUrl = getURL(`/auth/${this.name}`);
|
||||
|
||||
if (reconnect) {
|
||||
params["reconnect"] = true;
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n from "I18n";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { emojiUnescape } from "discourse/lib/text";
|
||||
@ -105,7 +106,7 @@ NavItem.reopenClass({
|
||||
extraNavItemDescriptors: [],
|
||||
|
||||
pathFor(filterType, context) {
|
||||
let path = Discourse.getURL("");
|
||||
let path = getURL("");
|
||||
let includesCategoryContext = false;
|
||||
let includesTagContext = false;
|
||||
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import RestModel from "discourse/models/rest";
|
||||
import { computed } from "@ember/object";
|
||||
import { getAbsoluteURL } from "discourse-common/lib/get-url";
|
||||
|
||||
export default RestModel.extend({
|
||||
url: computed("slug", function() {
|
||||
return `${Discourse.BaseUrl}/pub/${this.slug}`;
|
||||
return getAbsoluteURL(`/pub/${this.slug}`);
|
||||
})
|
||||
});
|
||||
|
||||
@ -80,11 +80,6 @@ const Site = RestModel.extend({
|
||||
return result;
|
||||
},
|
||||
|
||||
@discourseComputed
|
||||
baseUri() {
|
||||
return Discourse.baseUri;
|
||||
},
|
||||
|
||||
// Returns it in the correct order, by setting
|
||||
@discourseComputed("categories.[]")
|
||||
categoriesList() {
|
||||
|
||||
@ -3,6 +3,8 @@ import discourseComputed from "discourse-common/utils/decorators";
|
||||
import EmberObject from "@ember/object";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import User from "discourse/models/user";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
|
||||
/**
|
||||
A model representing a Topic's details that aren't always present, such as a list of participants.
|
||||
When showing topics in lists and such this information should not be required.
|
||||
@ -56,7 +58,7 @@ const TopicDetails = RestModel.extend({
|
||||
} else {
|
||||
return I18n.t(localeString, {
|
||||
username: User.currentProp("username_lower"),
|
||||
basePath: Discourse.BaseUri
|
||||
basePath: getURL("/")
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user