Version bump
This commit is contained in:
commit
d8f0379931
@ -1,6 +1,6 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
lang_map = el_GR: el, es_ES: es, fr_FR: fr, ko_KR: ko, pt_PT: pt, sk_SK: sk, vi_VN: vi
|
||||
lang_map = el_GR: el, es_ES: es, fr_FR: fr, hu_HU: hu, ko_KR: ko, pt_PT: pt, sk_SK: sk, vi_VN: vi
|
||||
|
||||
[discourse-org.core-client-yml]
|
||||
file_filter = config/locales/client.<lang>.yml
|
||||
|
||||
@ -184,7 +184,7 @@ GEM
|
||||
logstash-event (1.2.02)
|
||||
logstash-logger (0.26.1)
|
||||
logstash-event (~> 1.2)
|
||||
logster (1.2.9)
|
||||
logster (1.2.11)
|
||||
loofah (2.2.2)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { registerTooltip, unregisterTooltip } from "discourse/lib/tooltip";
|
||||
|
||||
const PAGES_LIMIT = 8;
|
||||
|
||||
@ -11,19 +10,6 @@ export default Ember.Component.extend({
|
||||
perPage: Ember.computed.alias("options.perPage"),
|
||||
page: 0,
|
||||
|
||||
didRender() {
|
||||
this._super(...arguments);
|
||||
|
||||
unregisterTooltip($(".text[data-tooltip]"));
|
||||
registerTooltip($(".text[data-tooltip]"));
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
unregisterTooltip($(".text[data-tooltip]"));
|
||||
},
|
||||
|
||||
@computed("model.computedLabels.length")
|
||||
twoColumns(labelsLength) {
|
||||
return labelsLength === 2;
|
||||
@ -52,7 +38,12 @@ export default Ember.Component.extend({
|
||||
|
||||
@computed("totalsForSampleRow", "model.computedLabels")
|
||||
totalsForSample(row, labels) {
|
||||
return labels.map(label => label.compute(row));
|
||||
return labels.map(label => {
|
||||
const computedLabel = label.compute(row);
|
||||
computedLabel.type = label.type;
|
||||
computedLabel.property = label.mainProperty;
|
||||
return computedLabel;
|
||||
});
|
||||
},
|
||||
|
||||
@computed("model.data", "model.computedLabels")
|
||||
@ -119,7 +110,7 @@ export default Ember.Component.extend({
|
||||
return {
|
||||
page: v + 1,
|
||||
index: v,
|
||||
class: v === page ? "current" : null
|
||||
class: v === page ? "is-current" : null
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@ -4,7 +4,10 @@ import { outputExportResult } from "discourse/lib/export-result";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { SCHEMA_VERSION, default as Report } from "admin/models/report";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { registerTooltip, unregisterTooltip } from "discourse/lib/tooltip";
|
||||
import {
|
||||
registerHoverTooltip,
|
||||
unregisterHoverTooltip
|
||||
} from "discourse/lib/tooltip";
|
||||
|
||||
const TABLE_OPTIONS = {
|
||||
perPage: 8,
|
||||
@ -35,12 +38,7 @@ function collapseWeekly(data, average) {
|
||||
}
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNameBindings: [
|
||||
"isEnabled",
|
||||
"isLoading",
|
||||
"dasherizedDataSourceName",
|
||||
"currentMode"
|
||||
],
|
||||
classNameBindings: ["isEnabled", "isLoading", "dasherizedDataSourceName"],
|
||||
classNames: ["admin-report"],
|
||||
isEnabled: true,
|
||||
disabledLabel: "admin.dashboard.disabled",
|
||||
@ -69,6 +67,7 @@ export default Ember.Component.extend({
|
||||
"showDatesOptions",
|
||||
"showGroupOptions"
|
||||
),
|
||||
shouldDisplayTrend: Ember.computed.and("showTrend", "model.prev_period"),
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
@ -80,6 +79,7 @@ export default Ember.Component.extend({
|
||||
this._super(...arguments);
|
||||
|
||||
const state = this.get("filters") || {};
|
||||
|
||||
this.setProperties({
|
||||
category: Category.findById(state.categoryId),
|
||||
groupId: state.groupId,
|
||||
@ -101,14 +101,13 @@ export default Ember.Component.extend({
|
||||
didRender() {
|
||||
this._super(...arguments);
|
||||
|
||||
unregisterTooltip($(".info[data-tooltip]"));
|
||||
registerTooltip($(".info[data-tooltip]"));
|
||||
registerHoverTooltip($(".info[data-tooltip]"));
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
unregisterTooltip($(".info[data-tooltip]"));
|
||||
unregisterHoverTooltip($(".info[data-tooltip]"));
|
||||
},
|
||||
|
||||
showError: Ember.computed.or("showTimeoutError", "showExceptionError"),
|
||||
@ -140,8 +139,8 @@ export default Ember.Component.extend({
|
||||
const modes = forcedModes ? forcedModes.split(",") : reportModes;
|
||||
|
||||
return Ember.makeArray(modes).map(mode => {
|
||||
const base = `mode-button ${mode}`;
|
||||
const cssClass = currentMode === mode ? `${base} current` : base;
|
||||
const base = `mode-btn ${mode}`;
|
||||
const cssClass = currentMode === mode ? `${base} is-current` : base;
|
||||
|
||||
return {
|
||||
mode,
|
||||
@ -157,7 +156,7 @@ export default Ember.Component.extend({
|
||||
{ name: I18n.t("admin.dashboard.reports.groups"), value: "all" }
|
||||
];
|
||||
return arr.concat(
|
||||
this.site.groups.map(i => {
|
||||
(this.site.groups || []).map(i => {
|
||||
return { name: i["name"], value: i["id"] };
|
||||
})
|
||||
);
|
||||
@ -171,15 +170,25 @@ export default Ember.Component.extend({
|
||||
@computed("startDate")
|
||||
normalizedStartDate(startDate) {
|
||||
return startDate && typeof startDate.isValid === "function"
|
||||
? startDate.format("YYYYMMDD")
|
||||
: startDate;
|
||||
? moment
|
||||
.utc(startDate.toISOString())
|
||||
.locale("en")
|
||||
.format("YYYYMMDD")
|
||||
: moment(startDate)
|
||||
.locale("en")
|
||||
.format("YYYYMMDD");
|
||||
},
|
||||
|
||||
@computed("endDate")
|
||||
normalizedEndDate(endDate) {
|
||||
return endDate && typeof endDate.isValid === "function"
|
||||
? endDate.format("YYYYMMDD")
|
||||
: endDate;
|
||||
? moment
|
||||
.utc(endDate.toISOString())
|
||||
.locale("en")
|
||||
.format("YYYYMMDD")
|
||||
: moment(endDate)
|
||||
.locale("en")
|
||||
.format("YYYYMMDD");
|
||||
},
|
||||
|
||||
@computed(
|
||||
@ -317,16 +326,15 @@ export default Ember.Component.extend({
|
||||
let payload = { data: { cache: true, facets } };
|
||||
|
||||
if (this.get("startDate")) {
|
||||
payload.data.start_date = moment(
|
||||
this.get("startDate"),
|
||||
"YYYY-MM-DD"
|
||||
).format("YYYY-MM-DD[T]HH:mm:ss.SSSZZ");
|
||||
payload.data.start_date = moment
|
||||
.utc(this.get("startDate"), "YYYY-MM-DD")
|
||||
.toISOString();
|
||||
}
|
||||
|
||||
if (this.get("endDate")) {
|
||||
payload.data.end_date = moment(this.get("endDate"), "YYYY-MM-DD").format(
|
||||
"YYYY-MM-DD[T]HH:mm:ss.SSSZZ"
|
||||
);
|
||||
payload.data.end_date = moment
|
||||
.utc(this.get("endDate"), "YYYY-MM-DD")
|
||||
.toISOString();
|
||||
}
|
||||
|
||||
if (this.get("groupId") && this.get("groupId") !== "all") {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import AdminUser from "admin/models/admin-user";
|
||||
import copyText from "discourse/lib/copy-text";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ["ip-lookup"],
|
||||
@ -62,6 +63,38 @@ export default Ember.Component.extend({
|
||||
this.set("show", false);
|
||||
},
|
||||
|
||||
copy: function() {
|
||||
let text = `IP: ${this.get("ip")}\n`;
|
||||
const location = this.get("location");
|
||||
if (location) {
|
||||
if (location.hostname) {
|
||||
text += `${I18n.t("ip_lookup.hostname")}: ${location.hostname}\n`;
|
||||
}
|
||||
|
||||
text += I18n.t("ip_lookup.location");
|
||||
if (location.loc) {
|
||||
text += `: ${location.loc} ${this.get("city")}\n`;
|
||||
} else {
|
||||
text += `: ${I18n.t("ip_lookup.location_not_found")}\n`;
|
||||
}
|
||||
|
||||
if (location.org) {
|
||||
text += I18n.t("ip_lookup.organisation");
|
||||
text += `: ${location.org}\n`;
|
||||
}
|
||||
|
||||
if (location.phone) {
|
||||
text += I18n.t("ip_lookup.phone");
|
||||
text += `: ${location.phone}\n`;
|
||||
}
|
||||
}
|
||||
const copyRange = $(`<textarea id="copy-range"></textarea>`);
|
||||
copyRange.text(text);
|
||||
$(document.body).append(copyRange);
|
||||
copyText(text, copyRange[0]);
|
||||
copyRange.remove();
|
||||
},
|
||||
|
||||
deleteOtherAccounts: function() {
|
||||
var self = this;
|
||||
bootbox.confirm(
|
||||
|
||||
@ -59,18 +59,14 @@ export default Ember.Component.extend({
|
||||
screenedIpAddress
|
||||
.save()
|
||||
.then(result => {
|
||||
if (result.success) {
|
||||
this.setProperties({ ip_address: "", formSubmitted: false });
|
||||
this.sendAction(
|
||||
"action",
|
||||
ScreenedIpAddress.create(result.screened_ip_address)
|
||||
);
|
||||
Ember.run.schedule("afterRender", () =>
|
||||
this.$(".ip-address-input").focus()
|
||||
);
|
||||
} else {
|
||||
bootbox.alert(result.errors);
|
||||
}
|
||||
this.setProperties({ ip_address: "", formSubmitted: false });
|
||||
this.sendAction(
|
||||
"action",
|
||||
ScreenedIpAddress.create(result.screened_ip_address)
|
||||
);
|
||||
Ember.run.schedule("afterRender", () =>
|
||||
this.$(".ip-address-input").focus()
|
||||
);
|
||||
})
|
||||
.catch(e => {
|
||||
this.set("formSubmitted", false);
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
import { default as computed } from "ember-addons/ember-computed-decorators";
|
||||
import {
|
||||
default as computed,
|
||||
observes
|
||||
} from "ember-addons/ember-computed-decorators";
|
||||
import { url } from "discourse/lib/computed";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
@ -9,6 +12,18 @@ const THEME_UPLOAD_VAR = 2;
|
||||
export default Ember.Controller.extend({
|
||||
editRouteName: "adminCustomizeThemes.edit",
|
||||
|
||||
@observes("allowChildThemes")
|
||||
setSelectedThemeId() {
|
||||
const available = this.get("selectableChildThemes");
|
||||
if (
|
||||
!this.get("selectedChildThemeId") &&
|
||||
available &&
|
||||
available.length > 0
|
||||
) {
|
||||
this.set("selectedChildThemeId", available[0].get("id"));
|
||||
}
|
||||
},
|
||||
|
||||
@computed("model", "allThemes")
|
||||
parentThemes(model, allThemes) {
|
||||
let parents = allThemes.filter(theme =>
|
||||
@ -64,16 +79,21 @@ export default Ember.Controller.extend({
|
||||
|
||||
let themes = [];
|
||||
available.forEach(t => {
|
||||
if (!childThemes || childThemes.indexOf(t) === -1) {
|
||||
if (
|
||||
(!childThemes || childThemes.indexOf(t) === -1) &&
|
||||
Em.isEmpty(t.get("childThemes")) &&
|
||||
!t.get("user_selectable") &&
|
||||
!t.get("default")
|
||||
) {
|
||||
themes.push(t);
|
||||
}
|
||||
});
|
||||
return themes.length === 0 ? null : themes;
|
||||
},
|
||||
|
||||
@computed("allThemes", "allThemes.length", "model")
|
||||
@computed("allThemes", "allThemes.length", "model", "parentThemes")
|
||||
availableChildThemes(allThemes, count) {
|
||||
if (count === 1) {
|
||||
if (count === 1 || this.get("parentThemes")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@ -46,19 +46,14 @@ export default Ember.Controller.extend({
|
||||
record.set("editing", false);
|
||||
record
|
||||
.save()
|
||||
.then(saved => {
|
||||
if (saved.success) {
|
||||
this.set("savedIpAddress", null);
|
||||
} else {
|
||||
bootbox.alert(saved.errors);
|
||||
if (wasEditing) record.set("editing", true);
|
||||
}
|
||||
.then(() => {
|
||||
this.set("savedIpAddress", null);
|
||||
})
|
||||
.catch(e => {
|
||||
if (e.responseJSON && e.responseJSON.errors) {
|
||||
if (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) {
|
||||
bootbox.alert(
|
||||
I18n.t("generic_error_with_reason", {
|
||||
error: e.responseJSON.errors.join(". ")
|
||||
error: e.jqXHR.responseJSON.errors.join(". ")
|
||||
})
|
||||
);
|
||||
} else {
|
||||
|
||||
@ -11,6 +11,10 @@ EmailLog.reopenClass({
|
||||
attrs.user = AdminUser.create(attrs.user);
|
||||
}
|
||||
|
||||
if (attrs.post_url) {
|
||||
attrs.post_url = Discourse.getURL(attrs.post_url);
|
||||
}
|
||||
|
||||
return this._super(attrs);
|
||||
},
|
||||
|
||||
|
||||
@ -285,7 +285,7 @@ const Report = Discourse.Model.extend({
|
||||
value,
|
||||
type,
|
||||
property: mainProperty,
|
||||
formatedValue: value ? escapeExpression(value) : "-"
|
||||
formatedValue: value ? escapeExpression(value) : "—"
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -318,7 +318,7 @@ const Report = Discourse.Model.extend({
|
||||
|
||||
return {
|
||||
value: username,
|
||||
formatedValue: username ? formatedValue(username) : "-"
|
||||
formatedValue: username ? formatedValue(username) : "—"
|
||||
};
|
||||
},
|
||||
|
||||
@ -333,7 +333,7 @@ const Report = Discourse.Model.extend({
|
||||
|
||||
return {
|
||||
value: topicTitle,
|
||||
formatedValue: topicTitle ? formatedValue() : "-"
|
||||
formatedValue: topicTitle ? formatedValue() : "—"
|
||||
};
|
||||
},
|
||||
|
||||
@ -360,7 +360,7 @@ const Report = Discourse.Model.extend({
|
||||
_percentLabel(value) {
|
||||
return {
|
||||
value,
|
||||
formatedValue: value ? `${value}%` : "-"
|
||||
formatedValue: value ? `${value}%` : "—"
|
||||
};
|
||||
},
|
||||
|
||||
@ -373,14 +373,14 @@ const Report = Discourse.Model.extend({
|
||||
|
||||
return {
|
||||
value,
|
||||
formatedValue: value ? formatedValue() : "-"
|
||||
formatedValue: value ? formatedValue() : "—"
|
||||
};
|
||||
},
|
||||
|
||||
_dateLabel(value, date) {
|
||||
return {
|
||||
value,
|
||||
formatedValue: value ? date.format("LL") : "-"
|
||||
formatedValue: value ? date.format("LL") : "—"
|
||||
};
|
||||
},
|
||||
|
||||
@ -389,7 +389,7 @@ const Report = Discourse.Model.extend({
|
||||
|
||||
return {
|
||||
value,
|
||||
formatedValue: value ? escaped : "-"
|
||||
formatedValue: value ? escaped : "—"
|
||||
};
|
||||
},
|
||||
|
||||
@ -404,7 +404,7 @@ const Report = Discourse.Model.extend({
|
||||
|
||||
return {
|
||||
value,
|
||||
formatedValue: value ? formatedValue(value, row[properties[1]]) : "-"
|
||||
formatedValue: value ? formatedValue(value, row[properties[1]]) : "—"
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import RestModel from "discourse/models/rest";
|
||||
import { default as computed } from "ember-addons/ember-computed-decorators";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
const THEME_UPLOAD_VAR = 2;
|
||||
|
||||
@ -150,7 +151,9 @@ const Theme = RestModel.extend({
|
||||
|
||||
saveChanges() {
|
||||
const hash = this.getProperties.apply(this, arguments);
|
||||
return this.save(hash).then(() => this.set("changed", false));
|
||||
return this.save(hash)
|
||||
.finally(() => this.set("changed", false))
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
|
||||
saveSettings(name, value) {
|
||||
|
||||
@ -5,14 +5,23 @@ export default Discourse.Route.extend({
|
||||
if (!controller.get("start_date")) {
|
||||
controller.set(
|
||||
"start_date",
|
||||
moment()
|
||||
.subtract("30", "day")
|
||||
moment
|
||||
.utc()
|
||||
.subtract(1, "day")
|
||||
.subtract(1, "month")
|
||||
.startOf("day")
|
||||
.format("YYYY-MM-DD")
|
||||
);
|
||||
}
|
||||
|
||||
if (!controller.get("end_date")) {
|
||||
controller.set("end_date", moment().format("YYYY-MM-DD"));
|
||||
controller.set(
|
||||
"end_date",
|
||||
moment()
|
||||
.utc()
|
||||
.endOf("day")
|
||||
.format("YYYY-MM-DD")
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{{#if showSortingUI}}
|
||||
{{d-button action=sortByLabel icon=sortIcon class="sort-button"}}
|
||||
{{d-button action=sortByLabel icon=sortIcon class="sort-btn"}}
|
||||
{{/if}}
|
||||
|
||||
<span>{{label.title}}</span>
|
||||
<span class="title">{{label.title}}</span>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<table class="report-table">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
{{#if model.computedLabels}}
|
||||
@ -30,7 +30,7 @@
|
||||
</tr>
|
||||
<tr class="admin-report-table-row">
|
||||
{{#each totalsForSample as |total|}}
|
||||
<td class="{{total.type}} {{total.property}}">
|
||||
<td class="admin-report-table-cell {{total.type}} {{total.property}}">
|
||||
{{total.formatedValue}}
|
||||
</td>
|
||||
{{/each}}
|
||||
@ -44,8 +44,8 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="admin-report-table-row">
|
||||
<td class="date x">-</td>
|
||||
<td class="number y">{{number model.total}}</td>
|
||||
<td class="admin-report-table-cell date x">—</td>
|
||||
<td class="admin-report-table-cell number y">{{number model.total}}</td>
|
||||
</tr>
|
||||
{{/if}}
|
||||
</tbody>
|
||||
|
||||
@ -1,50 +1,99 @@
|
||||
{{#if isEnabled}}
|
||||
{{#conditional-loading-section isLoading=isLoading}}
|
||||
{{#if showHeader}}
|
||||
<div class="report-header">
|
||||
{{#if showTitle}}
|
||||
<div class="report-title">
|
||||
<h3 class="title">
|
||||
{{#if showAllReportsLink}}
|
||||
{{#link-to "adminReports" class="all-report-link"}}
|
||||
{{i18n "admin.dashboard.all_reports"}}
|
||||
{{/link-to}}
|
||||
<span class="separator">|</span>
|
||||
{{/if}}
|
||||
{{#conditional-loading-section isLoading=isLoading}}
|
||||
{{#if showHeader}}
|
||||
<div class="header">
|
||||
{{#if showTitle}}
|
||||
<ul class="breadcrumb">
|
||||
{{#if showAllReportsLink}}
|
||||
<li class="item all-reports">
|
||||
{{#link-to "adminReports" class="report-url"}}
|
||||
{{i18n "admin.dashboard.all_reports"}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
<li class="item separator">|</li>
|
||||
{{/if}}
|
||||
|
||||
<a href="{{model.reportUrl}}" class="report-link">
|
||||
{{model.title}}
|
||||
</a>
|
||||
</h3>
|
||||
<li class="item report">
|
||||
<a href="{{model.reportUrl}}" class="report-url">
|
||||
{{model.title}}
|
||||
</a>
|
||||
|
||||
{{#if model.description}}
|
||||
<span class="info" data-tooltip="{{model.description}}">
|
||||
{{d-icon "question-circle"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</li>
|
||||
</ul>
|
||||
{{/if}}
|
||||
|
||||
{{#if showTrend}}
|
||||
{{#if model.prev_period}}
|
||||
<div class="trend {{model.trend}}">
|
||||
<span class="trend-value" title="{{model.trendTitle}}">
|
||||
{{#if model.average}}
|
||||
{{number model.currentAverage}}{{#if model.percent}}%{{/if}}
|
||||
{{else}}
|
||||
{{number model.currentTotal noTitle="true"}}{{#if model.percent}}%{{/if}}
|
||||
{{/if}}
|
||||
</span>
|
||||
{{#if shouldDisplayTrend}}
|
||||
<div class="trend {{model.trend}}">
|
||||
<span class="value" title="{{model.trendTitle}}">
|
||||
{{#if model.average}}
|
||||
{{number model.currentAverage}}{{#if model.percent}}%{{/if}}
|
||||
{{else}}
|
||||
{{number model.currentTotal noTitle="true"}}{{#if model.percent}}%{{/if}}
|
||||
{{/if}}
|
||||
</span>
|
||||
|
||||
{{#if model.trendIcon}}
|
||||
{{d-icon model.trendIcon class="trend-icon"}}
|
||||
{{#if model.trendIcon}}
|
||||
{{d-icon model.trendIcon class="icon"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="body">
|
||||
<div class="main">
|
||||
{{#unless showError}}
|
||||
{{#if hasData}}
|
||||
{{#if currentMode}}
|
||||
{{component modeComponent model=model options=options}}
|
||||
|
||||
{{#if model.relatedReport}}
|
||||
{{admin-report showFilteringUI=false dataSourceName=model.relatedReport.type}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<div class="alert alert-info report-alert no-data">
|
||||
{{d-icon "pie-chart"}}
|
||||
{{#if model.reportUrl}}
|
||||
<a href="{{model.reportUrl}}" class="report-url">
|
||||
<span>
|
||||
{{#if model.title}}
|
||||
{{model.title}} —
|
||||
{{/if}}
|
||||
{{i18n "admin.dashboard.reports.no_data"}}
|
||||
</span>
|
||||
</a>
|
||||
{{else}}
|
||||
<span>{{i18n "admin.dashboard.reports.no_data"}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{#if showTimeoutError}}
|
||||
<div class="alert alert-error report-alert timeout">
|
||||
{{d-icon "exclamation-triangle"}}
|
||||
<span>{{i18n "admin.dashboard.timeout_error"}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showExceptionError}}
|
||||
<div class="alert alert-error report-alert exception">
|
||||
{{d-icon "exclamation-triangle"}}
|
||||
<span>{{i18n "admin.dashboard.exception_error"}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
</div>
|
||||
|
||||
{{#if showFilteringUI}}
|
||||
<div class="filters">
|
||||
{{#if showModes}}
|
||||
<ul class="mode-switch">
|
||||
<ul class="modes">
|
||||
{{#each displayedModes as |displayedMode|}}
|
||||
<li class="mode">
|
||||
{{d-button
|
||||
@ -56,118 +105,84 @@
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
|
||||
{{#if showDatesOptions}}
|
||||
<div class="control">
|
||||
<span class="label">
|
||||
{{i18n 'admin.dashboard.reports.start_date'}}
|
||||
</span>
|
||||
|
||||
<div class="input">
|
||||
{{date-picker-past
|
||||
value=startDate
|
||||
defaultDate=startDate}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control">
|
||||
<span class="label">
|
||||
{{i18n 'admin.dashboard.reports.end_date'}}
|
||||
</span>
|
||||
|
||||
<div class="input">
|
||||
{{date-picker-past
|
||||
value=endDate
|
||||
defaultDate=endDate}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showCategoryOptions}}
|
||||
<div class="control">
|
||||
<div class="input">
|
||||
{{search-advanced-category-chooser
|
||||
filterable=true
|
||||
value=category
|
||||
castInteger=true}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showGroupOptions}}
|
||||
<div class="control">
|
||||
<div class="input">
|
||||
{{combo-box
|
||||
castInteger=true
|
||||
filterable=true
|
||||
valueAttribute="value"
|
||||
content=groupOptions
|
||||
value=groupId}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showExport}}
|
||||
<div class="control">
|
||||
<div class="input">
|
||||
{{d-button
|
||||
class="export-csv-btn"
|
||||
action="exportCsv"
|
||||
label="admin.export_csv.button_text"
|
||||
icon="download"}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showRefresh}}
|
||||
<div class="control">
|
||||
<div class="input">
|
||||
{{d-button
|
||||
class="refresh-report-btn btn-primary"
|
||||
action="refreshReport"
|
||||
label="admin.dashboard.reports.refresh_report"
|
||||
icon="refresh"}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="report-body">
|
||||
{{#unless showError}}
|
||||
{{#if hasData}}
|
||||
{{#if currentMode}}
|
||||
{{component modeComponent model=model options=options}}
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<div class="alert alert-info no-data">
|
||||
{{d-icon "pie-chart"}}
|
||||
<span>{{i18n "admin.dashboard.reports.no_data"}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{#if showTimeoutError}}
|
||||
<div class="alert alert-error report-error timeout">
|
||||
<span>{{i18n "admin.dashboard.timeout_error"}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showExceptionError}}
|
||||
<div class="alert alert-error report-error exception">
|
||||
{{i18n "admin.dashboard.exception_error"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
|
||||
{{#if showFilteringUI}}
|
||||
<div class="report-filters">
|
||||
{{#if showDatesOptions}}
|
||||
<div class="filtering-control">
|
||||
<span class="filtering-label">
|
||||
{{i18n 'admin.dashboard.reports.start_date'}}
|
||||
</span>
|
||||
|
||||
<div class="filtering-input">
|
||||
{{date-picker-past
|
||||
value=startDate
|
||||
defaultDate=startDate}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filtering-control">
|
||||
<span class="filtering-label">
|
||||
{{i18n 'admin.dashboard.reports.end_date'}}
|
||||
</span>
|
||||
|
||||
<div class="filtering-input">
|
||||
{{date-picker-past
|
||||
value=endDate
|
||||
defaultDate=endDate}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showCategoryOptions}}
|
||||
<div class="filtering-control">
|
||||
<div class="filtering-input">
|
||||
{{search-advanced-category-chooser
|
||||
filterable=true
|
||||
value=category
|
||||
castInteger=true}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showGroupOptions}}
|
||||
<div class="filtering-control">
|
||||
<div class="filtering-input">
|
||||
{{combo-box
|
||||
castInteger=true
|
||||
filterable=true
|
||||
valueAttribute="value"
|
||||
content=groupOptions
|
||||
value=groupId}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showExport}}
|
||||
<div class="filtering-control">
|
||||
<div class="filtering-input">
|
||||
{{d-button
|
||||
class="export-csv-btn"
|
||||
action="exportCsv"
|
||||
label="admin.export_csv.button_text"
|
||||
icon="download"}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showRefresh}}
|
||||
<div class="filtering-control">
|
||||
<div class="filtering-input">
|
||||
{{d-button
|
||||
class="refresh-report-btn btn-primary"
|
||||
action="refreshReport"
|
||||
label="admin.dashboard.reports.refresh_report"
|
||||
icon="refresh"}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if model.relatedReport}}
|
||||
{{admin-report dataSourceName=model.relatedReport.type}}
|
||||
{{/if}}
|
||||
{{/conditional-loading-section}}
|
||||
</div>
|
||||
{{/conditional-loading-section}}
|
||||
{{else}}
|
||||
<div class="alert alert-info">
|
||||
{{{i18n disabledLabel}}}
|
||||
|
||||
@ -137,7 +137,7 @@
|
||||
{{/unless}}
|
||||
{{#if selectableChildThemes}}
|
||||
<p>
|
||||
{{combo-box content=selectableChildThemes value=selectedChildThemeId}}
|
||||
{{combo-box filterable=true content=selectableChildThemes value=selectedChildThemeId}}
|
||||
{{#d-button action="addChildTheme" icon="plus"}}{{i18n "admin.customize.theme.add"}}{{/d-button}}
|
||||
</p>
|
||||
{{/if}}
|
||||
|
||||
@ -57,23 +57,23 @@
|
||||
<div class="section-columns">
|
||||
<div class="section-column">
|
||||
<div class="admin-report activity-metrics">
|
||||
<div class="report-header">
|
||||
<div class="report-title">
|
||||
<h3 class="title">
|
||||
{{#link-to "adminReports" class="report-link"}}
|
||||
<div class="header">
|
||||
<ul class="breadcrumb">
|
||||
<li class="item report">
|
||||
{{#link-to "adminReports" class="report-url"}}
|
||||
{{i18n "admin.dashboard.activity_metrics"}}
|
||||
{{/link-to}}
|
||||
</h3>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="report-body">
|
||||
<div class="admin-report-counters-list">
|
||||
<div class="counters-list">
|
||||
<div class="counters-header">
|
||||
<div class="header"></div>
|
||||
<div class="header">{{i18n 'admin.dashboard.reports.today'}}</div>
|
||||
<div class="header">{{i18n 'admin.dashboard.reports.yesterday'}}</div>
|
||||
<div class="header">{{i18n 'admin.dashboard.reports.last_7_days'}}</div>
|
||||
<div class="header">{{i18n 'admin.dashboard.reports.last_30_days'}}</div>
|
||||
<div class="counters-cell"></div>
|
||||
<div class="counters-cell">{{i18n 'admin.dashboard.reports.today'}}</div>
|
||||
<div class="counters-cell">{{i18n 'admin.dashboard.reports.yesterday'}}</div>
|
||||
<div class="counters-cell">{{i18n 'admin.dashboard.reports.last_7_days'}}</div>
|
||||
<div class="counters-cell">{{i18n 'admin.dashboard.reports.last_30_days'}}</div>
|
||||
</div>
|
||||
|
||||
{{#each activityMetrics as |metric|}}
|
||||
@ -84,10 +84,11 @@
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#link-to "adminReports"}}
|
||||
{{i18n "admin.dashboard.all_reports"}}
|
||||
{{/link-to}}
|
||||
</div>
|
||||
{{#link-to "adminReports"}}
|
||||
{{i18n "admin.dashboard.all_reports"}}
|
||||
{{/link-to}}
|
||||
|
||||
<div class="user-metrics">
|
||||
{{#conditional-loading-section isLoading=isLoading}}
|
||||
|
||||
@ -38,6 +38,14 @@ import {
|
||||
|
||||
const REBUILD_SCROLL_MAP_EVENTS = ["composer:resized", "composer:typed-reply"];
|
||||
|
||||
const uploadHandlers = [];
|
||||
export function addComposerUploadHandler(extensions, method) {
|
||||
uploadHandlers.push({
|
||||
extensions,
|
||||
method
|
||||
});
|
||||
}
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNameBindings: ["showToolbar:toolbar-visible", ":wmd-controls"],
|
||||
|
||||
@ -587,6 +595,19 @@ export default Ember.Component.extend({
|
||||
});
|
||||
|
||||
$element.on("fileuploadsubmit", (e, data) => {
|
||||
// Look for a matching file upload handler contributed from a plugin
|
||||
const matcher = handler => {
|
||||
const ext = handler.extensions.join("|");
|
||||
const regex = new RegExp(`\\.(${ext})$`, "i");
|
||||
return regex.test(data.files[0].name);
|
||||
};
|
||||
const matchingHandler = uploadHandlers.find(matcher);
|
||||
if (data.files.length === 1 && matchingHandler) {
|
||||
matchingHandler.method(data.files[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If no plugin, continue as normal
|
||||
const isPrivateMessage = this.get("composer.privateMessage");
|
||||
|
||||
data.formData = { type: "composer" };
|
||||
|
||||
@ -81,7 +81,7 @@ export default Ember.Component.extend({
|
||||
if (!this.site.mobileView) {
|
||||
$this.css({ left: "" + x + "px" });
|
||||
}
|
||||
this.set("link", url);
|
||||
this.set("link", encodeURI(url));
|
||||
this.set("visible", true);
|
||||
|
||||
Ember.run.scheduleOnce("afterRender", this, this._focusUrl);
|
||||
|
||||
@ -61,7 +61,11 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||
this.set("gravatarFailed", true);
|
||||
} else {
|
||||
this.set("gravatarFailed", false);
|
||||
this.get("user").setProperties(result);
|
||||
|
||||
this.get("user").setProperties({
|
||||
gravatar_avatar_upload_id: result.gravatar_upload_id,
|
||||
gravatar_avatar_template: result.gravatar_avatar_template
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => this.set("gravatarRefreshDisabled", false));
|
||||
|
||||
@ -53,7 +53,8 @@ function loadDraft(store, opts) {
|
||||
composerTime: draft.composerTime,
|
||||
typingTime: draft.typingTime,
|
||||
whisper: draft.whisper,
|
||||
tags: draft.tags
|
||||
tags: draft.tags,
|
||||
noBump: draft.noBump
|
||||
});
|
||||
return composer;
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ export default Ember.Controller.extend(PreferencesTabController, {
|
||||
@observes("themeId")
|
||||
themeIdChanged() {
|
||||
const id = this.get("themeId");
|
||||
previewTheme(id);
|
||||
previewTheme([id]);
|
||||
},
|
||||
|
||||
homeChanged() {
|
||||
|
||||
@ -930,6 +930,10 @@ export default Ember.Controller.extend(BufferedContent, {
|
||||
|
||||
removeFeaturedLink() {
|
||||
this.set("buffered.featured_link", null);
|
||||
},
|
||||
|
||||
resetBumpDate() {
|
||||
this.get("content").resetBumpDate();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import { currentThemeId, refreshCSS } from "discourse/lib/theme-selector";
|
||||
import { currentThemeIds, refreshCSS } from "discourse/lib/theme-selector";
|
||||
|
||||
// Use the message bus for live reloading of components for faster development.
|
||||
export default {
|
||||
@ -58,12 +58,16 @@ export default {
|
||||
// Refresh if necessary
|
||||
document.location.reload(true);
|
||||
} else {
|
||||
let themeId = currentThemeId();
|
||||
|
||||
const themeIds = currentThemeIds();
|
||||
$("link").each(function() {
|
||||
if (me.hasOwnProperty("theme_id") && me.new_href) {
|
||||
let target = $(this).data("target");
|
||||
if (me.theme_id === themeId && target === me.target) {
|
||||
const target = $(this).data("target");
|
||||
const themeId = $(this).data("theme-id");
|
||||
if (
|
||||
themeIds.indexOf(me.theme_id) !== -1 &&
|
||||
target === me.target &&
|
||||
(!themeId || themeId === me.theme_id)
|
||||
) {
|
||||
refreshCSS(this, null, me.new_href);
|
||||
}
|
||||
} else if (this.href.match(me.name) && (me.hash || me.new_href)) {
|
||||
|
||||
@ -29,6 +29,17 @@ export function handleLogoff(xhr) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleRedirect(data) {
|
||||
if (
|
||||
data &&
|
||||
data.getResponseHeader &&
|
||||
data.getResponseHeader("Discourse-Xhr-Redirect")
|
||||
) {
|
||||
window.location.replace(data.responseText);
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Our own $.ajax method. Makes sure the .then method executes in an Ember runloop
|
||||
for performance reasons. Also automatically adjusts the URL to support installs
|
||||
@ -76,6 +87,7 @@ export function ajax() {
|
||||
}
|
||||
|
||||
args.success = (data, textStatus, xhr) => {
|
||||
handleRedirect(data);
|
||||
handleLogoff(xhr);
|
||||
|
||||
Ember.run(() => {
|
||||
|
||||
@ -66,7 +66,8 @@ const DiscourseLocation = Ember.Object.extend({
|
||||
getURL() {
|
||||
const location = get(this, "location");
|
||||
let url = location.pathname;
|
||||
url = url.replace(Discourse.BaseUri, "");
|
||||
|
||||
url = url.replace(new RegExp(`^${Discourse.BaseUri}`), "");
|
||||
|
||||
const search = location.search || "";
|
||||
url += search;
|
||||
|
||||
@ -35,9 +35,10 @@ import { registerCustomAvatarHelper } from "discourse/helpers/user-avatar";
|
||||
import { disableNameSuppression } from "discourse/widgets/poster-name";
|
||||
import { registerCustomPostMessageCallback as registerCustomPostMessageCallback1 } from "discourse/controllers/topic";
|
||||
import Sharing from "discourse/lib/sharing";
|
||||
import { addComposerUploadHandler } from "discourse/components/composer-editor";
|
||||
|
||||
// If you add any methods to the API ensure you bump up this number
|
||||
const PLUGIN_API_VERSION = "0.8.23";
|
||||
const PLUGIN_API_VERSION = "0.8.24";
|
||||
|
||||
class PluginApi {
|
||||
constructor(version, container) {
|
||||
@ -753,6 +754,22 @@ class PluginApi {
|
||||
Sharing.addSharingId(options.id);
|
||||
Sharing.addSource(options);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Registers a function to handle uploads for specified file types
|
||||
* The normal uploading functionality will be bypassed
|
||||
* This only for uploads of individual files
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* addComposerUploadHandler(["mp4", "mov"], (file) => {
|
||||
* console.log("Handling upload for", file.name);
|
||||
* })
|
||||
*/
|
||||
addComposerUploadHandler(extensions, method) {
|
||||
addComposerUploadHandler(extensions, method);
|
||||
}
|
||||
}
|
||||
|
||||
let _pluginv01;
|
||||
|
||||
@ -34,6 +34,10 @@ export default function(page) {
|
||||
this.controllerFor("static").set("model", model);
|
||||
},
|
||||
|
||||
titleToken() {
|
||||
return I18n.t(page);
|
||||
},
|
||||
|
||||
actions: {
|
||||
didTransition() {
|
||||
this.controllerFor("application").set("showFooter", true);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import deprecated from "discourse-common/lib/deprecated";
|
||||
|
||||
const keySelector = "meta[name=discourse_theme_id]";
|
||||
const keySelector = "meta[name=discourse_theme_ids]";
|
||||
|
||||
export function currentThemeKey() {
|
||||
if (console && console.warn && console.trace) {
|
||||
@ -12,21 +12,26 @@ export function currentThemeKey() {
|
||||
}
|
||||
}
|
||||
|
||||
export function currentThemeId() {
|
||||
let themeId = null;
|
||||
let elem = _.first($(keySelector));
|
||||
export function currentThemeIds() {
|
||||
const themeIds = [];
|
||||
const elem = _.first($(keySelector));
|
||||
if (elem) {
|
||||
themeId = elem.content;
|
||||
if (_.isEmpty(themeId)) {
|
||||
themeId = null;
|
||||
} else {
|
||||
themeId = parseInt(themeId);
|
||||
}
|
||||
elem.content.split(",").forEach(num => {
|
||||
num = parseInt(num, 10);
|
||||
if (!isNaN(num)) {
|
||||
themeIds.push(num);
|
||||
}
|
||||
});
|
||||
}
|
||||
return themeId;
|
||||
return themeIds;
|
||||
}
|
||||
|
||||
export function currentThemeId() {
|
||||
return currentThemeIds()[0];
|
||||
}
|
||||
|
||||
export function setLocalTheme(ids, themeSeq) {
|
||||
ids = ids.reject(id => !id);
|
||||
if (ids && ids.length > 0) {
|
||||
$.cookie("theme_ids", `${ids.join(",")}|${themeSeq}`, {
|
||||
path: "/",
|
||||
@ -76,23 +81,28 @@ export function refreshCSS(node, hash, newHref, options) {
|
||||
$orig.data("copy", reloaded);
|
||||
}
|
||||
|
||||
export function previewTheme(id) {
|
||||
if (currentThemeId() !== id) {
|
||||
export function previewTheme(ids = []) {
|
||||
ids = ids.reject(id => !id);
|
||||
if (!ids.includes(currentThemeId())) {
|
||||
Discourse.set("assetVersion", "forceRefresh");
|
||||
|
||||
ajax(`/themes/assets/${id ? id : "default"}`).then(results => {
|
||||
let elem = _.first($(keySelector));
|
||||
if (elem) {
|
||||
elem.content = id;
|
||||
}
|
||||
|
||||
results.themes.forEach(theme => {
|
||||
let node = $(`link[rel=stylesheet][data-target=${theme.target}]`)[0];
|
||||
if (node) {
|
||||
refreshCSS(node, null, theme.url, { force: true });
|
||||
ajax(`/themes/assets/${ids.length > 0 ? ids.join("-") : "default"}`).then(
|
||||
results => {
|
||||
const elem = _.first($(keySelector));
|
||||
if (elem) {
|
||||
elem.content = ids.join(",");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
results.themes.forEach(theme => {
|
||||
const node = $(
|
||||
`link[rel=stylesheet][data-target=${theme.target}]`
|
||||
)[0];
|
||||
if (node) {
|
||||
refreshCSS(node, null, theme.new_href, { force: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { escapeExpression } from "discourse/lib/utilities";
|
||||
|
||||
const fadeSpeed = 300;
|
||||
const tooltipID = "#discourse-tooltip";
|
||||
|
||||
export function showTooltip() {
|
||||
const fadeSpeed = 300;
|
||||
const tooltipID = "#discourse-tooltip";
|
||||
const $this = $(this);
|
||||
const $parent = $this.offsetParent();
|
||||
const content = escapeExpression($this.attr("data-tooltip"));
|
||||
@ -16,9 +17,7 @@ export function showTooltip() {
|
||||
pos.top -= delta.top;
|
||||
pos.left -= delta.left;
|
||||
|
||||
$(tooltipID)
|
||||
.fadeOut(fadeSpeed)
|
||||
.remove();
|
||||
hideTooltip(tooltipID);
|
||||
|
||||
$(this).after(`
|
||||
<div id="discourse-tooltip" ${retina}>
|
||||
@ -67,9 +66,24 @@ export function showTooltip() {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function hideTooltip() {
|
||||
$(tooltipID)
|
||||
.fadeOut(fadeSpeed)
|
||||
.remove();
|
||||
}
|
||||
|
||||
export function registerTooltip(jqueryContext) {
|
||||
if (jqueryContext.length) {
|
||||
jqueryContext.on("click", showTooltip);
|
||||
jqueryContext.off("click").on("click", showTooltip);
|
||||
}
|
||||
}
|
||||
|
||||
export function registerHoverTooltip(jqueryContext) {
|
||||
if (jqueryContext.length) {
|
||||
jqueryContext
|
||||
.off("mouseenter mouseleave click")
|
||||
.on("mouseenter click", showTooltip)
|
||||
.on("mouseleave", hideTooltip);
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,3 +92,9 @@ export function unregisterTooltip(jqueryContext) {
|
||||
jqueryContext.off("click");
|
||||
}
|
||||
}
|
||||
|
||||
export function unregisterHoverTooltip(jqueryContext) {
|
||||
if (jqueryContext.length) {
|
||||
jqueryContext.off("mouseenter mouseleave click");
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +41,8 @@ const CLOSED = "closed",
|
||||
composer_open_duration_msecs: "composerTime",
|
||||
tags: "tags",
|
||||
featured_link: "featuredLink",
|
||||
shared_draft: "sharedDraft"
|
||||
shared_draft: "sharedDraft",
|
||||
no_bump: "noBump"
|
||||
},
|
||||
_edit_topic_serializer = {
|
||||
title: "topic.title",
|
||||
@ -71,6 +72,7 @@ const SAVE_ICONS = {
|
||||
const Composer = RestModel.extend({
|
||||
_categoryId: null,
|
||||
unlistTopic: false,
|
||||
noBump: false,
|
||||
|
||||
archetypes: function() {
|
||||
return this.site.get("archetypes");
|
||||
@ -608,7 +610,8 @@ const Composer = RestModel.extend({
|
||||
composerTotalOpened: opts.composerTime,
|
||||
typingTime: opts.typingTime,
|
||||
whisper: opts.whisper,
|
||||
tags: opts.tags
|
||||
tags: opts.tags,
|
||||
noBump: opts.noBump
|
||||
});
|
||||
|
||||
if (opts.post) {
|
||||
@ -714,7 +717,8 @@ const Composer = RestModel.extend({
|
||||
typingTime: 0,
|
||||
composerOpened: null,
|
||||
composerTotalOpened: 0,
|
||||
featuredLink: null
|
||||
featuredLink: null,
|
||||
noBump: false
|
||||
});
|
||||
},
|
||||
|
||||
@ -964,7 +968,8 @@ const Composer = RestModel.extend({
|
||||
usernames: this.get("targetUsernames"),
|
||||
composerTime: this.get("composerTime"),
|
||||
typingTime: this.get("typingTime"),
|
||||
tags: this.get("tags")
|
||||
tags: this.get("tags"),
|
||||
noBump: this.get("noBump")
|
||||
};
|
||||
|
||||
this.set("draftStatus", I18n.t("composer.saving_draft_tip"));
|
||||
|
||||
@ -86,7 +86,10 @@ export function findAll(siteSettings, capabilities, isMobileDevice) {
|
||||
});
|
||||
|
||||
// On Mobile, Android or iOS always go with full screen
|
||||
if (isMobileDevice || capabilities.isIOS || capabilities.isAndroid) {
|
||||
if (
|
||||
isMobileDevice ||
|
||||
(capabilities && (capabilities.isIOS || capabilities.isAndroid))
|
||||
) {
|
||||
methods.forEach(m => m.set("full_screen_login", true));
|
||||
}
|
||||
|
||||
|
||||
@ -565,6 +565,12 @@ const Topic = RestModel.extend({
|
||||
window.location.reload();
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
|
||||
resetBumpDate() {
|
||||
return ajax(`/t/${this.get("id")}/reset-bump-date`, { type: "PUT" }).catch(
|
||||
popupAjaxError
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
{{else}}
|
||||
<li class="nav-item-faq">{{#link-to 'faq'}}{{i18n 'faq'}}{{/link-to}}</li>
|
||||
{{/if}}
|
||||
<li class="nav-item-tos">{{#link-to 'tos'}}{{i18n 'terms_of_service'}}{{/link-to}}</li>
|
||||
<li class="nav-item-tos">{{#link-to 'tos'}}{{i18n 'tos'}}{{/link-to}}</li>
|
||||
<li class="nav-item-privacy">{{#link-to 'privacy'}}{{i18n 'privacy'}}{{/link-to}}</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@ -13,14 +13,12 @@
|
||||
{{#each categories as |c|}}
|
||||
<tr data-category-id={{c.id}} class="{{if c.description_excerpt 'has-description' 'no-description'}} {{if c.uploaded_logo.url 'has-logo' 'no-logo'}}">
|
||||
<td class="category" style={{border-color c.color}}>
|
||||
<div>
|
||||
{{category-title-link category=c}}
|
||||
<div class="category-description">
|
||||
{{{dir-span c.description_excerpt}}}
|
||||
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
{{#if c.subcategories}}
|
||||
{{#if c.subcategories}}
|
||||
<div class='subcategories'>
|
||||
{{#each c.subcategories as |s|}}
|
||||
<span class='subcategory'>
|
||||
@ -31,6 +29,7 @@
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
</td>
|
||||
<td class="topics">
|
||||
<div title={{c.statTitle}}>{{{c.stat}}}</div>
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
{{#if show}}
|
||||
<div class="location-box">
|
||||
<a class="close pull-right" {{action "hide"}}>{{d-icon "times"}}</a>
|
||||
<a class="btn pull-right" {{action "copy"}}>{{d-icon "copy"}}</a>
|
||||
<h4>{{i18n 'ip_lookup.title'}}</h4>
|
||||
<p class='powered-by'>{{{i18n 'ip_lookup.powered_by'}}}</p>
|
||||
<dl>
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
showTopicStatusUpdate=showTopicStatusUpdate
|
||||
showFeatureTopic=showFeatureTopic
|
||||
showChangeTimestamp=showChangeTimestamp
|
||||
resetBumpDate=resetBumpDate
|
||||
convertToPublicTopic=convertToPublicTopic
|
||||
convertToPrivateMessage=convertToPrivateMessage}}
|
||||
{{/if}}
|
||||
|
||||
@ -21,6 +21,9 @@
|
||||
{{#if whisperOrUnlistTopicText}}
|
||||
<span class='whisper'>({{whisperOrUnlistTopicText}})</span>
|
||||
{{/if}}
|
||||
{{#if model.noBump}}
|
||||
<span class="no-bump">{{d-icon "anchor"}}</span>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
|
||||
{{#if canEdit}}
|
||||
@ -120,6 +123,9 @@
|
||||
{{d-icon "eye-slash"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
{{#if model.noBump}}
|
||||
<span class="no-bump">{{d-icon "anchor"}}</span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
|
||||
|
||||
@ -15,7 +15,13 @@
|
||||
<div class="avatar-choice">
|
||||
{{radio-button id="gravatar" name="avatar" value="gravatar" selection=selected}}
|
||||
<label class="radio" for="gravatar">{{bound-avatar-template user.gravatar_avatar_template "large"}} {{{i18n 'user.change_avatar.gravatar'}}} {{user.email}}</label>
|
||||
{{d-button action="refreshGravatar" title="user.change_avatar.refresh_gravatar_title" disabled=gravatarRefreshDisabled icon="refresh"}}
|
||||
|
||||
{{d-button action="refreshGravatar"
|
||||
title="user.change_avatar.refresh_gravatar_title"
|
||||
disabled=gravatarRefreshDisabled
|
||||
icon="refresh"
|
||||
class="avatar-selector-refresh-gravatar"}}
|
||||
|
||||
{{#if gravatarFailed}}
|
||||
<p class="error">{{I18n 'user.change_avatar.gravatar_failed'}}</p>
|
||||
{{/if}}
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
{{d-button class="no-text" action="moveUp" actionParam=cat icon="arrow-up"}}
|
||||
{{d-button class="no-text" action="moveDown" actionParam=cat icon="arrow-down"}}
|
||||
{{#if cat.hasBufferedChanges}}
|
||||
{{d-button class="no-text" action="commit" icon="check"}}
|
||||
{{d-button class="no-text ok" action="commit" icon="check"}}
|
||||
{{/if}}
|
||||
</td>
|
||||
<td>{{category-badge cat allowUncategorized="true"}}</td>
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
</ul>
|
||||
</p>
|
||||
<p><span>{{i18n "user.api_approved"}}</span> {{bound-date key.created_at}}</p>
|
||||
<p><span>{{i18n "user.api_last_used_at"}}</span> {{bound-date key.last_used_at}}</p>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
@ -1,11 +1,3 @@
|
||||
{{#d-section pageClass="tags"}}
|
||||
<div class="container list-container">
|
||||
<div class="row">
|
||||
<div class="full-width">
|
||||
<div id='list-area'>
|
||||
{{outlet}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/d-section}}
|
||||
{{#d-section pageClass="tags" tagName=""}}
|
||||
{{outlet}}
|
||||
{{/d-section}}
|
||||
@ -4,23 +4,7 @@
|
||||
|
||||
<div class="list-controls">
|
||||
<div class="container">
|
||||
{{#if tagNotification}}
|
||||
{{#unless additionalTags}}
|
||||
{{tag-notifications-button action="changeTagNotification"
|
||||
notificationLevel=tagNotification.notification_level}}
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
|
||||
{{#if showAdminControls}}
|
||||
{{d-button action="deleteTag" icon="trash-o" class="admin-tag btn-danger"}}
|
||||
{{d-button action="renameTag" actionParam=tag icon="pencil" class="admin-tag"}}
|
||||
{{/if}}
|
||||
|
||||
{{create-topic-button
|
||||
canCreateTopic=canCreateTopic
|
||||
disabled=createTopicDisabled
|
||||
label=createTopicLabel
|
||||
action=(route-action "createTopic")}}
|
||||
<section class="navigation-container">
|
||||
|
||||
{{#if showTagFilter}}
|
||||
{{bread-crumbs categories=categories
|
||||
@ -40,35 +24,61 @@
|
||||
{{/each}}
|
||||
</h2>
|
||||
{{/if}}
|
||||
|
||||
|
||||
{{#if tagNotification}}
|
||||
{{#unless additionalTags}}
|
||||
{{tag-notifications-button action="changeTagNotification"
|
||||
notificationLevel=tagNotification.notification_level}}
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
|
||||
{{create-topic-button
|
||||
canCreateTopic=canCreateTopic
|
||||
disabled=createTopicDisabled
|
||||
label=createTopicLabel
|
||||
action=(route-action "createTopic")}}
|
||||
|
||||
{{#if showAdminControls}}
|
||||
{{d-button action="renameTag" actionParam=tag icon="pencil" class="admin-tag"}}
|
||||
{{d-button action="deleteTag" icon="trash-o" class="admin-tag btn-danger"}}
|
||||
{{/if}}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{plugin-outlet name="discovery-list-container-top"}}
|
||||
<div class="container list-container">
|
||||
<div class="row">
|
||||
<div class="full-width">
|
||||
<div id='list-area'>
|
||||
{{conditional-loading-spinner condition=loading}}
|
||||
|
||||
{{conditional-loading-spinner condition=loading}}
|
||||
{{#unless loading}}
|
||||
{{#if list.topics}}
|
||||
{{#discovery-topics-list model=list refresh="refresh"}}
|
||||
{{bulk-select-button selected=selected action="refresh"}}
|
||||
|
||||
{{#unless loading}}
|
||||
{{#if list.topics}}
|
||||
{{#discovery-topics-list model=list refresh="refresh"}}
|
||||
{{bulk-select-button selected=selected action="refresh"}}
|
||||
|
||||
{{topic-list topics=list.topics
|
||||
canBulkSelect=canBulkSelect
|
||||
toggleBulkSelect="toggleBulkSelect"
|
||||
bulkSelectEnabled=bulkSelectEnabled
|
||||
selected=selected
|
||||
showPosters=true
|
||||
order=order
|
||||
ascending=ascending
|
||||
changeSort="changeSort"}}
|
||||
|
||||
{{/discovery-topics-list}}
|
||||
{{else}}
|
||||
<footer class='topic-list-bottom'>
|
||||
<h3>
|
||||
{{footerMessage}}{{#link-to "discovery.categories"}} {{i18n 'topic.browse_all_categories'}}{{/link-to}} {{i18n 'or'}} {{#link-to 'discovery.latest'}}{{i18n 'topic.view_latest_topics'}}{{/link-to}}.
|
||||
</h3>
|
||||
</footer>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
{{topic-list topics=list.topics
|
||||
canBulkSelect=canBulkSelect
|
||||
toggleBulkSelect="toggleBulkSelect"
|
||||
bulkSelectEnabled=bulkSelectEnabled
|
||||
selected=selected
|
||||
showPosters=true
|
||||
order=order
|
||||
ascending=ascending
|
||||
changeSort="changeSort"}}
|
||||
|
||||
{{/discovery-topics-list}}
|
||||
{{else}}
|
||||
<footer class='topic-list-bottom'>
|
||||
<h3>
|
||||
{{footerMessage}}{{#link-to "discovery.categories"}} {{i18n 'topic.browse_all_categories'}}{{/link-to}} {{i18n 'or'}} {{#link-to 'discovery.latest'}}{{i18n 'topic.view_latest_topics'}}{{/link-to}}.
|
||||
</h3>
|
||||
</footer>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -94,6 +94,7 @@
|
||||
showTopicStatusUpdate=(action "topicRouteAction" "showTopicStatusUpdate")
|
||||
showFeatureTopic=(action "topicRouteAction" "showFeatureTopic")
|
||||
showChangeTimestamp=(action "topicRouteAction" "showChangeTimestamp")
|
||||
resetBumpDate=(action "resetBumpDate")
|
||||
convertToPublicTopic=(action "convertToPublicTopic")
|
||||
convertToPrivateMessage=(action "convertToPrivateMessage")}}
|
||||
{{/if}}
|
||||
@ -121,6 +122,7 @@
|
||||
showTopicStatusUpdate=(action "topicRouteAction" "showTopicStatusUpdate")
|
||||
showFeatureTopic=(action "topicRouteAction" "showFeatureTopic")
|
||||
showChangeTimestamp=(action "topicRouteAction" "showChangeTimestamp")
|
||||
resetBumpDate=(action "resetBumpDate")
|
||||
convertToPublicTopic=(action "convertToPublicTopic")
|
||||
convertToPrivateMessage=(action "convertToPrivateMessage")}}
|
||||
{{else}}
|
||||
@ -144,6 +146,7 @@
|
||||
showTopicStatusUpdate=(action "topicRouteAction" "showTopicStatusUpdate")
|
||||
showFeatureTopic=(action "topicRouteAction" "showFeatureTopic")
|
||||
showChangeTimestamp=(action "topicRouteAction" "showChangeTimestamp")
|
||||
resetBumpDate=(action "resetBumpDate")
|
||||
convertToPublicTopic=(action "convertToPublicTopic")
|
||||
convertToPrivateMessage=(action "convertToPrivateMessage")}}
|
||||
{{/if}}
|
||||
@ -256,6 +259,7 @@
|
||||
showTopicStatusUpdate=(action "topicRouteAction" "showTopicStatusUpdate")
|
||||
showFeatureTopic=(action "topicRouteAction" "showFeatureTopic")
|
||||
showChangeTimestamp=(action "topicRouteAction" "showChangeTimestamp")
|
||||
resetBumpDate=(action "resetBumpDate")
|
||||
convertToPublicTopic=(action "convertToPublicTopic")
|
||||
convertToPrivateMessage=(action "convertToPrivateMessage")
|
||||
toggleBookmark=(action "toggleBookmark")
|
||||
|
||||
@ -182,7 +182,6 @@ export default createWidget("search-menu", {
|
||||
},
|
||||
|
||||
html(attrs) {
|
||||
searchData.contextEnabled = attrs.contextEnabled;
|
||||
const searchContext = this.searchContext();
|
||||
|
||||
const shouldTriggerSearch =
|
||||
@ -196,6 +195,8 @@ export default createWidget("search-menu", {
|
||||
this.triggerSearch();
|
||||
}
|
||||
|
||||
searchData.contextEnabled = attrs.contextEnabled;
|
||||
|
||||
return this.attach("menu-panel", {
|
||||
maxWidth: 500,
|
||||
contents: () => this.panelContents()
|
||||
|
||||
@ -203,6 +203,15 @@ export default createWidget("topic-admin-menu", {
|
||||
});
|
||||
}
|
||||
|
||||
if (this.currentUser.get("staff")) {
|
||||
buttons.push({
|
||||
className: "topic-admin-reset-bump-date",
|
||||
action: "resetBumpDate",
|
||||
icon: "anchor",
|
||||
label: "actions.reset_bump_date"
|
||||
});
|
||||
}
|
||||
|
||||
if (!isPrivateMessage) {
|
||||
buttons.push({
|
||||
className: "topic-admin-archive",
|
||||
|
||||
3
app/assets/javascripts/locales/hu.js.erb
Normal file
3
app/assets/javascripts/locales/hu.js.erb
Normal file
@ -0,0 +1,3 @@
|
||||
//= depend_on 'client.hu.yml'
|
||||
//= require locales/i18n
|
||||
<%= JsLocaleHelper.output_locale(:hu) %>
|
||||
@ -192,6 +192,17 @@ export default DropdownSelectBoxComponent.extend({
|
||||
});
|
||||
}
|
||||
|
||||
const currentUser = Discourse.User.current();
|
||||
|
||||
if (action === REPLY && currentUser && currentUser.get("staff")) {
|
||||
items.push({
|
||||
name: I18n.t("composer.composer_actions.toggle_topic_bump.label"),
|
||||
description: I18n.t("composer.composer_actions.toggle_topic_bump.desc"),
|
||||
icon: "anchor",
|
||||
id: "toggle_topic_bump"
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
|
||||
@ -234,6 +245,10 @@ export default DropdownSelectBoxComponent.extend({
|
||||
model.toggleProperty("whisper");
|
||||
},
|
||||
|
||||
toggleTopicBumpSelected(options, model) {
|
||||
model.toggleProperty("noBump");
|
||||
},
|
||||
|
||||
replyToTopicSelected(options) {
|
||||
options.action = REPLY;
|
||||
options.topic = _topicSnapshot;
|
||||
|
||||
@ -45,21 +45,26 @@ export default ComboBox.extend(Tags, {
|
||||
);
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.$(".selected-name").off("touchend.select-kit pointerup.select-kit");
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.$(".selected-name").on(
|
||||
"touchend.select-kit pointerup.select-kit",
|
||||
() => this.focusFilterOrHeader()
|
||||
this.$(".select-kit-body").on(
|
||||
"mousedown touchstart",
|
||||
".selected-tag",
|
||||
event => {
|
||||
const $button = $(event.target);
|
||||
this._destroyEvent(event);
|
||||
this.destroyTags(this.computeContentItem($button.attr("data-value")));
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.$(".select-kit-body").off("mousedown touchstart");
|
||||
},
|
||||
|
||||
@computed("hasReachedMaximum")
|
||||
caretIcon(hasReachedMaximum) {
|
||||
return hasReachedMaximum ? null : "plus fa-fw";
|
||||
@ -74,37 +79,6 @@ export default ComboBox.extend(Tags, {
|
||||
return computedContent;
|
||||
},
|
||||
|
||||
didRender() {
|
||||
this._super();
|
||||
|
||||
this.$(".select-kit-body").on(
|
||||
"click.mini-tag-chooser",
|
||||
".selected-tag",
|
||||
event => {
|
||||
event.stopImmediatePropagation();
|
||||
this.destroyTags(
|
||||
this.computeContentItem($(event.target).attr("data-value"))
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
this.$(".select-kit-header").on(
|
||||
"focus.mini-tag-chooser",
|
||||
".selected-name",
|
||||
event => {
|
||||
event.stopImmediatePropagation();
|
||||
this.focus(event);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super();
|
||||
|
||||
this.$(".select-kit-body").off("click.mini-tag-chooser");
|
||||
this.$(".select-kit-header").off("focus.mini-tag-chooser");
|
||||
},
|
||||
|
||||
// we are directly mutatings tags to define the current selection
|
||||
mutateValue() {},
|
||||
|
||||
|
||||
@ -18,6 +18,6 @@ export default NotificationOptionsComponent.extend({
|
||||
|
||||
@computed("iconForSelectedDetails")
|
||||
headerIcon(iconForSelectedDetails) {
|
||||
return [iconForSelectedDetails, "caret-down"];
|
||||
return iconForSelectedDetails;
|
||||
}
|
||||
});
|
||||
|
||||
@ -91,14 +91,25 @@ export default Ember.Mixin.create({
|
||||
// next so we are sure it finised expand/collapse
|
||||
Ember.run.next(() => {
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
if (!context.$filterInput() || !context.$filterInput().is(":visible")) {
|
||||
if (
|
||||
!context.$filterInput() ||
|
||||
!context.$filterInput().is(":visible") ||
|
||||
context
|
||||
.$filterInput()
|
||||
.parent()
|
||||
.hasClass("is-hidden")
|
||||
) {
|
||||
if (context.$header()) {
|
||||
context.$header().focus();
|
||||
} else {
|
||||
$(context.element).focus();
|
||||
}
|
||||
} else {
|
||||
context.$filterInput().focus();
|
||||
if (this.site && this.site.isMobileDevice) {
|
||||
this.expand();
|
||||
} else {
|
||||
context.$filterInput().focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,3 +1,2 @@
|
||||
{{input class="selected-name" value=label readonly="readonly" tabindex="-1"}}
|
||||
|
||||
<span class="selected-name">{{label}}</span>
|
||||
{{d-icon caretIcon class="caret-icon"}}
|
||||
|
||||
@ -1,64 +1,36 @@
|
||||
.admin-report {
|
||||
.report-error,
|
||||
.no-data {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
align-self: flex-start;
|
||||
text-align: center;
|
||||
padding: 3em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.report-error {
|
||||
color: $danger;
|
||||
border: 1px solid $danger;
|
||||
}
|
||||
|
||||
.no-data {
|
||||
background: $secondary;
|
||||
border: 1px solid $primary-low;
|
||||
color: $primary-low-mid;
|
||||
|
||||
.d-icon-pie-chart {
|
||||
color: currentColor;
|
||||
margin-bottom: 0.25em;
|
||||
font-size: $font-up-5;
|
||||
display: block;
|
||||
.conditional-loading-section {
|
||||
&.is-loading {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.conditional-loading-section {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid $primary-low;
|
||||
margin-bottom: 0.5em;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.report-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1em;
|
||||
.header .breadcrumb {
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
|
||||
.report-title {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.item {
|
||||
display: inline;
|
||||
font-size: $font-up-1;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: $font-up-1;
|
||||
.separator {
|
||||
font-weight: normal;
|
||||
}
|
||||
.all-reports .report-url {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.report-link {
|
||||
color: $primary;
|
||||
}
|
||||
.report {
|
||||
font-weight: 700;
|
||||
|
||||
.separator + .report-link {
|
||||
font-weight: normal;
|
||||
}
|
||||
.report-url {
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
.info {
|
||||
@ -71,167 +43,134 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.trend {
|
||||
align-items: center;
|
||||
|
||||
&.trending-down,
|
||||
&.high-trending-down {
|
||||
color: $danger;
|
||||
}
|
||||
|
||||
&.trending-up,
|
||||
&.high-trending-up {
|
||||
color: $success;
|
||||
}
|
||||
|
||||
&.no-change {
|
||||
color: $primary-medium;
|
||||
}
|
||||
|
||||
.trend-value {
|
||||
font-size: $font-up-1;
|
||||
}
|
||||
|
||||
.trend-icon {
|
||||
font-size: $font-up-1;
|
||||
font-weight: 700;
|
||||
}
|
||||
.header .trend {
|
||||
margin-left: auto;
|
||||
&.trending-down,
|
||||
&.high-trending-down {
|
||||
color: $danger;
|
||||
}
|
||||
|
||||
.mode-switch {
|
||||
&.trending-up,
|
||||
&.high-trending-up {
|
||||
color: $success;
|
||||
}
|
||||
|
||||
&.no-change {
|
||||
color: $primary-medium;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: $font-up-1;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: $font-up-1;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.main .report-alert {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
padding: 3em;
|
||||
border: 1px solid transparent;
|
||||
|
||||
a {
|
||||
color: $primary-medium;
|
||||
}
|
||||
|
||||
.d-icon {
|
||||
color: currentColor;
|
||||
margin-bottom: 0.25em;
|
||||
font-size: $font-up-5;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&.no-data {
|
||||
background: $secondary;
|
||||
border-color: $primary-low;
|
||||
color: $primary-low-mid;
|
||||
}
|
||||
|
||||
&.timeout,
|
||||
&.exception {
|
||||
border-color: $danger-low;
|
||||
color: $danger;
|
||||
}
|
||||
}
|
||||
|
||||
.filters {
|
||||
display: flex;
|
||||
margin-left: 1em;
|
||||
flex-direction: column;
|
||||
width: 220px;
|
||||
|
||||
.modes {
|
||||
margin: 0 0 1em 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
display: flex;
|
||||
margin: 0;
|
||||
|
||||
.mode {
|
||||
display: inline;
|
||||
display: inline-flex;
|
||||
flex: 1;
|
||||
|
||||
.mode-button.current {
|
||||
.mode-btn.is-current {
|
||||
color: $tertiary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.report-body {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.control {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.admin-report-table,
|
||||
.admin-report-chart {
|
||||
.control .label {
|
||||
font-weight: 700;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.report-filters {
|
||||
margin-left: 1em;
|
||||
min-width: 250px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.control .input,
|
||||
.control .select-kit {
|
||||
width: 100%;
|
||||
|
||||
.filtering-control {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.filtering-label {
|
||||
}
|
||||
.filtering-input {
|
||||
.export-csv-btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.date-picker-wrapper,
|
||||
.combo-box,
|
||||
.export-csv-btn,
|
||||
.refresh-report-btn {
|
||||
.refresh-report-btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.date-picker-wrapper {
|
||||
width: 100%;
|
||||
.date-picker {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.date-picker-wrapper {
|
||||
.date-picker {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
}
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.report-filters:only-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report.activity-metrics {
|
||||
table {
|
||||
table-layout: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report.users-by-type {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.admin-report.users-by-type,
|
||||
.admin-report.users-by-trust-level {
|
||||
margin-bottom: 1em;
|
||||
flex: 1;
|
||||
.report-header {
|
||||
border-bottom: 1px solid $primary-medium;
|
||||
padding-bottom: 0.5em;
|
||||
border-bottom: 1px solid #e9e9e9;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report.moderators-activity {
|
||||
tbody tr td.username,
|
||||
thead tr th.username {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report.trending-search {
|
||||
tbody tr td.term,
|
||||
thead tr th.term {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report.top-traffic-sources {
|
||||
tbody tr td.domain,
|
||||
thead tr th.domain {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report.post-edits {
|
||||
.report-table {
|
||||
table-layout: auto;
|
||||
|
||||
tbody tr td,
|
||||
thead tr th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
thead tr th.edit_reason,
|
||||
tbody tr td.edit_reason {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report.flags-status {
|
||||
.admin-report-table {
|
||||
table-layout: auto;
|
||||
|
||||
tbody tr td,
|
||||
thead tr th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
tbody tr td.response_time,
|
||||
thead tr th.response_time {
|
||||
text-align: center;
|
||||
}
|
||||
.rtl .admin-report {
|
||||
.filters {
|
||||
margin-left: 0;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.trend {
|
||||
margin-left: unset;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,70 +1,21 @@
|
||||
.admin-report-counters-list {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
|
||||
.counters-header {
|
||||
display: grid;
|
||||
flex: 1;
|
||||
grid-template-columns: 33% repeat(auto-fit, minmax(20px, 1fr));
|
||||
border: 1px solid $primary-low;
|
||||
border-bottom: 0;
|
||||
padding: 0.25em;
|
||||
font-weight: 700;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.conditional-loading-section.is-loading {
|
||||
padding: 0.5em;
|
||||
margin: 0;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
|
||||
.title {
|
||||
font-weight: normal;
|
||||
font-size: $font-down-1;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
margin: 0 0 0 0.5em;
|
||||
height: 5px;
|
||||
width: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report.counters {
|
||||
&:last-child .admin-report-counters {
|
||||
border-bottom: 1px solid $primary-low;
|
||||
}
|
||||
|
||||
.admin-report {
|
||||
.admin-report-counters {
|
||||
display: grid;
|
||||
flex: 1;
|
||||
grid-template-columns: 33% repeat(auto-fit, minmax(20px, 1fr));
|
||||
grid-template-rows: repeat(auto-fit, minmax(32px, 1fr));
|
||||
border: 1px solid $primary-low;
|
||||
align-items: center;
|
||||
border-bottom: 0;
|
||||
|
||||
.cell {
|
||||
padding: 0.25em;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
padding: 8px 21px 8px 8px; // accounting for negative right caret margin
|
||||
&:nth-of-type(2) {
|
||||
padding: 8px 12px 8px;
|
||||
}
|
||||
i {
|
||||
margin-right: -12px; // align on caret
|
||||
@media screen and (max-width: 650px) {
|
||||
margin-right: -9px;
|
||||
}
|
||||
}
|
||||
|
||||
&.title {
|
||||
text-align: left;
|
||||
padding: 8px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
.d-icon {
|
||||
color: $primary-low-mid;
|
||||
@ -78,13 +29,8 @@
|
||||
}
|
||||
|
||||
@media screen and (max-width: 400px) {
|
||||
&.title {
|
||||
padding: 8px 0 8px 4px;
|
||||
font-size: $font-down-1;
|
||||
|
||||
.d-icon {
|
||||
display: none;
|
||||
}
|
||||
&.title .d-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,28 +48,18 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.no-data {
|
||||
margin: 0;
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-size: $font-0;
|
||||
border: 0;
|
||||
background: $primary-low;
|
||||
color: $primary-medium;
|
||||
.d-icon {
|
||||
font-size: $font-up-1;
|
||||
margin: 0 0.25em 0 0;
|
||||
color: $primary-low-mid;
|
||||
.rtl .counters-list .counters-header .counters-cell {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.rtl .counters-list {
|
||||
.cell {
|
||||
text-align: left;
|
||||
|
||||
&.title {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
text-align: left;
|
||||
padding: 0.5em;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,113 +1,193 @@
|
||||
.admin-report-table {
|
||||
@media screen and (max-width: 650px) {
|
||||
table {
|
||||
tbody tr td {
|
||||
font-size: $font-down-1;
|
||||
&.two-columns {
|
||||
.table .admin-report-table-cell:first-child,
|
||||
.table .admin-report-table-header:first-child {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.table .admin-report-table-cell:last-child,
|
||||
.table .admin-report-table-header:last-child {
|
||||
display: inline-block;
|
||||
width: 80%;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.table {
|
||||
margin: 0;
|
||||
border: 1px solid $primary-low;
|
||||
table-layout: fixed;
|
||||
|
||||
tbody {
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.table .admin-report-table-header {
|
||||
.sort-btn {
|
||||
outline: none;
|
||||
background: none;
|
||||
padding: 3px 8px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&.is-current-sort {
|
||||
.d-icon {
|
||||
color: $tertiary;
|
||||
}
|
||||
|
||||
.sort-btn:hover {
|
||||
color: $primary-medium;
|
||||
background: $primary-low;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.is-current-sort) .sort-btn {
|
||||
background: none;
|
||||
|
||||
&:hover {
|
||||
color: $primary-medium;
|
||||
background: $primary-low;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report-table-cell {
|
||||
&.user .username {
|
||||
margin-left: 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
.total-row {
|
||||
background: $primary-very-low;
|
||||
|
||||
td {
|
||||
font-weight: 700;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 0.5em;
|
||||
margin-top: 0.25em;
|
||||
|
||||
button {
|
||||
margin-left: 0.5em;
|
||||
|
||||
&.current {
|
||||
&.is-current {
|
||||
color: $tertiary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.two-columns {
|
||||
.report-table tbody tr td:first-child,
|
||||
.report-table thead tr th:first-child {
|
||||
text-align: left;
|
||||
.admin-report.top-referred-topics {
|
||||
.admin-report-table-header.topic_title {
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report.trending-search {
|
||||
.admin-report-table-header.ctr,
|
||||
.admin-report-table-header.unique_searches,
|
||||
.admin-report-table-cell.ctr,
|
||||
.admin-report-table-cell.unique_searches {
|
||||
text-align: center;
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.admin-report-table-cell.term {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report.moderators-activity {
|
||||
.admin-report-table-header.seconds,
|
||||
.admin-report-table-header.number,
|
||||
.admin-report-table-cell.seconds,
|
||||
.admin-report-table-cell.number {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.admin-report-table-header.user {
|
||||
width: 20%;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report.post-edits {
|
||||
.admin-report-table-header.user {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.admin-report-table-cell.post,
|
||||
.admin-report-table-cell.edit_reason {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report.flags-status {
|
||||
.admin-report-table-header.response_time,
|
||||
.admin-report-table-cell.response_time {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.rtl {
|
||||
.admin-report-table {
|
||||
&.two-columns {
|
||||
.table .admin-report-table-cell:first-child,
|
||||
.table .admin-report-table-header:first-child {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.table .admin-report-table-cell:last-child,
|
||||
.table .admin-report-table-header:last-child {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.report-table {
|
||||
table-layout: auto;
|
||||
.total-row {
|
||||
td {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.report-table {
|
||||
table-layout: fixed;
|
||||
border: 1px solid $primary-low;
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
|
||||
tbody {
|
||||
border: none;
|
||||
|
||||
.total-row {
|
||||
td {
|
||||
font-weight: 700;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
td {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
|
||||
&.user {
|
||||
text-align: left;
|
||||
|
||||
.username {
|
||||
margin-left: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.admin-report-table-cell {
|
||||
&.user .username {
|
||||
margin-left: 0;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
thead {
|
||||
border: 1px solid $primary-low;
|
||||
.admin-report.trending-search {
|
||||
.admin-report-table-header.term,
|
||||
.admin-report-table-cell.term {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report-table-header {
|
||||
.sort-button {
|
||||
outline: none;
|
||||
background: none;
|
||||
padding: 3px 7px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.pagination {
|
||||
button {
|
||||
margin-left: 0;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-current-sort {
|
||||
.d-icon {
|
||||
color: $tertiary;
|
||||
}
|
||||
|
||||
.sort-button:hover {
|
||||
color: $primary-medium;
|
||||
background: $primary-low;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.is-current-sort) .sort-button {
|
||||
background: none;
|
||||
|
||||
&:hover {
|
||||
color: $primary-medium;
|
||||
background: $primary-low;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
th {
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
.admin-report.moderators-activity {
|
||||
.admin-report-table-header.user,
|
||||
.admin-report-table-cell.user {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,6 +333,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.permalink-form .select-kit {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.permalink-title {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
@ -7,28 +7,33 @@
|
||||
|
||||
.dashboard-next {
|
||||
.section-top {
|
||||
margin-bottom: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.navigation {
|
||||
display: flex;
|
||||
margin: 0 0 1em 0;
|
||||
margin: 0 0 2.5em 0;
|
||||
border-bottom: 1px solid $primary-low-mid;
|
||||
|
||||
.navigation-item {
|
||||
display: inline;
|
||||
background: $secondary;
|
||||
padding: 0.5em 1em;
|
||||
&:hover {
|
||||
background: $primary-very-low;
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-link {
|
||||
font-weight: 700;
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
padding: 0.6em 1em 0.5em 1em;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin active-navigation-item {
|
||||
border-radius: 3px 3px 0 0;
|
||||
border: 1px solid $primary-low;
|
||||
border-bottom: 10px solid $secondary;
|
||||
.navigation-link {
|
||||
border-bottom: 0.4em solid $tertiary;
|
||||
}
|
||||
}
|
||||
|
||||
&.dashboard-next-moderation .navigation-item.moderation {
|
||||
@ -57,11 +62,11 @@
|
||||
max-width: 100%;
|
||||
|
||||
&:last-child {
|
||||
margin-left: 1em;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-right: 1em;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
@include breakpoint(medium) {
|
||||
@ -108,26 +113,31 @@
|
||||
|
||||
.section-body {
|
||||
padding: 1em 0 0;
|
||||
> p {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.charts {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
.admin-report .header {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.chart {
|
||||
max-width: calc(100% * 1 / 3.2);
|
||||
width: 100%;
|
||||
flex-grow: 1;
|
||||
flex-basis: 100%;
|
||||
display: flex;
|
||||
margin-bottom: 1em;
|
||||
.charts {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
grid-column-gap: 1em;
|
||||
grid-row-gap: 1em;
|
||||
|
||||
.admin-report {
|
||||
grid-column: span 4;
|
||||
}
|
||||
|
||||
@include breakpoint(medium) {
|
||||
.chart {
|
||||
max-width: 100%;
|
||||
.admin-report {
|
||||
grid-column: span 12;
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,6 +168,7 @@
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
border-right: 1px solid $primary-low;
|
||||
|
||||
.backups,
|
||||
.uploads {
|
||||
@ -189,7 +200,6 @@
|
||||
}
|
||||
}
|
||||
.last-dashboard-update {
|
||||
border-left: 1px solid $primary-low;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@ -202,6 +212,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.top-referred-topics {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.top-referred-topics,
|
||||
.trending-search {
|
||||
th:first-of-type {
|
||||
@ -209,12 +223,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.top-referred-topics {
|
||||
.dashboard-table table {
|
||||
table-layout: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.section {
|
||||
.period-chooser .period-chooser-header {
|
||||
.selected-name,
|
||||
@ -229,7 +237,7 @@
|
||||
}
|
||||
|
||||
.dashboard-problems {
|
||||
margin-bottom: 2em;
|
||||
margin-bottom: 2.5em;
|
||||
|
||||
.d-icon-exclamation-triangle {
|
||||
color: $danger;
|
||||
@ -241,29 +249,82 @@
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report-table {
|
||||
margin-bottom: 1em;
|
||||
.counters-list {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
|
||||
&.is-disabled {
|
||||
background: $primary-low;
|
||||
padding: 1em;
|
||||
.counters-header {
|
||||
display: grid;
|
||||
flex: 1;
|
||||
grid-template-columns: 33% repeat(auto-fit, minmax(20px, 1fr));
|
||||
border: 1px solid $primary-low;
|
||||
border-bottom: 0;
|
||||
font-weight: 700;
|
||||
text-align: right;
|
||||
align-items: center;
|
||||
padding: 0.65em 0.25em;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 650px) {
|
||||
table {
|
||||
tbody tr td {
|
||||
font-size: $font-down-1;
|
||||
}
|
||||
.admin-report .main {
|
||||
border: 1px solid $primary-low;
|
||||
|
||||
&:hover {
|
||||
background-color: $primary-very-low;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-loading {
|
||||
height: 150px;
|
||||
.admin-report:not(:last-child) {
|
||||
.main {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.conditional-loading-section.is-loading {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report .conditional-loading-section.is-loading {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 0.5em 0.25em;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
border: 1px solid $primary-low;
|
||||
|
||||
.title {
|
||||
font-size: $font-0;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
margin: 0;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report .main .report-alert {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 0.5em 0.25em;
|
||||
align-items: center;
|
||||
border: 0;
|
||||
|
||||
&:hover {
|
||||
background-color: $primary-very-low;
|
||||
}
|
||||
|
||||
.d-icon {
|
||||
font-size: $font-up-1;
|
||||
margin: 0 0.25em 0 0;
|
||||
color: $primary-low-mid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.activity-metrics {
|
||||
margin-bottom: 1em;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.user-metrics {
|
||||
@ -363,21 +424,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
.community-health.section {
|
||||
margin-bottom: 1em;
|
||||
.users-by-trust-level,
|
||||
.users-by-type {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.dashboard-next.moderation {
|
||||
.community-health.section {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.dashboard-next-moderation {
|
||||
.admin-dashboard-moderation-top {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
grid-column-gap: 1em;
|
||||
grid-row-gap: 1em;
|
||||
}
|
||||
|
||||
.section-body {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.main-section {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
grid-column-gap: 1em;
|
||||
grid-row-gap: 1em;
|
||||
|
||||
> * {
|
||||
grid-column: span 12;
|
||||
@ -387,6 +459,7 @@
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
grid-column-gap: 1em;
|
||||
grid-row-gap: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
|
||||
.list-controls {
|
||||
clear: both;
|
||||
margin-bottom: 5px;
|
||||
.combo-box .combo-box-header {
|
||||
background: $primary-low;
|
||||
color: $primary;
|
||||
@ -35,6 +36,9 @@
|
||||
&.category-notifications-button,
|
||||
&.tag-notifications-button {
|
||||
float: right;
|
||||
button {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,31 +1,31 @@
|
||||
.reorder-categories {
|
||||
thead {
|
||||
border-bottom: 1px solid $primary-low;
|
||||
th {
|
||||
padding-bottom: 0.5em;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
input {
|
||||
width: 4em;
|
||||
}
|
||||
.th-pos {
|
||||
width: calc(4em + 150px);
|
||||
}
|
||||
tbody tr {
|
||||
background-color: transparent;
|
||||
transition: background 0s ease;
|
||||
&.highlighted {
|
||||
background-color: rgba($highlight, 0.4);
|
||||
&.done {
|
||||
background-color: transparent;
|
||||
transition-duration: 1s;
|
||||
}
|
||||
@include breakpoint(mobile) {
|
||||
width: 2em;
|
||||
}
|
||||
&:first-child td {
|
||||
padding-top: 7px;
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
border-bottom: 1px solid blend-primary-secondary(50%);
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
padding-bottom: 150px;
|
||||
td {
|
||||
padding: 0.5em 0.5em 0.5em 0;
|
||||
@include breakpoint(mobile, min-width) {
|
||||
min-width: 15em;
|
||||
}
|
||||
}
|
||||
}
|
||||
.badge-wrapper span.badge-category {
|
||||
max-width: 20em;
|
||||
@include breakpoint(mobile) {
|
||||
max-width: 30vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
.category-admin-menu ul {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
@ -437,4 +437,10 @@ div.ac-wrap {
|
||||
|
||||
.md-table {
|
||||
overflow-y: auto;
|
||||
margin: 1em 0;
|
||||
.mobile-view & {
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,63 +1,8 @@
|
||||
.edit-topic-timer-modal {
|
||||
.modal-body {
|
||||
max-height: none;
|
||||
overflow: visible !important; /* inline JS styles */
|
||||
}
|
||||
|
||||
.select-kit {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
input.date-picker,
|
||||
input[type="time"] {
|
||||
width: 150px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.radios {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
label {
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
padding-right: 5px;
|
||||
|
||||
input {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.btn.pull-right {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.future-date-input {
|
||||
input {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
margin: 0 -15px -15px -15px;
|
||||
}
|
||||
|
||||
.pika-single {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
.topic-status-info {
|
||||
border: none;
|
||||
padding: 0;
|
||||
|
||||
h3 {
|
||||
font-weight: normal;
|
||||
font-size: $font-up-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mobile styles
|
||||
.mobile-view .edit-topic-timer-modal {
|
||||
.control-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -66,20 +11,55 @@
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.select-kit {
|
||||
width: 50%;
|
||||
}
|
||||
input.date-picker,
|
||||
input[type="time"] {
|
||||
width: 150px;
|
||||
text-align: left;
|
||||
}
|
||||
.radios {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
label {
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
padding-right: 5px;
|
||||
input {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
.btn.pull-right {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.future-date-input {
|
||||
input {
|
||||
margin: 0;
|
||||
}
|
||||
.alert-info {
|
||||
margin: 0 -15px -15px -15px;
|
||||
}
|
||||
.btn-clear {
|
||||
display: none;
|
||||
}
|
||||
.topic-status-info {
|
||||
border: none;
|
||||
padding: 0;
|
||||
h3 {
|
||||
font-weight: normal;
|
||||
font-size: $font-up-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.pika-single {
|
||||
position: absolute !important; /* inline JS styles */
|
||||
}
|
||||
}
|
||||
|
||||
// mobile styles
|
||||
.mobile-view .edit-topic-timer-modal {
|
||||
.select-kit.combo-box {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.future-date-input-selector-header .btn-clear {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
overflow: visible !important; /* inline styles from JS */
|
||||
}
|
||||
|
||||
.pika-single {
|
||||
position: absolute !important; /* inline styles from JS */
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,6 +78,7 @@
|
||||
float: left;
|
||||
background-color: transparent;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.25em 0.5em;
|
||||
width: 50%;
|
||||
box-sizing: border-box;
|
||||
@ -122,9 +123,6 @@
|
||||
font-weight: normal;
|
||||
font-size: $font-down-1;
|
||||
}
|
||||
.box + b.topics-count {
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
span.badge-category {
|
||||
max-width: 90px;
|
||||
@ -275,6 +273,9 @@
|
||||
|
||||
a {
|
||||
padding: 0;
|
||||
> div {
|
||||
overflow: hidden; // clears the text from wrapping below icons
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
|
||||
@ -29,6 +29,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
.tag-show-heading {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
.d-icon {
|
||||
margin: 0 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
.topic-header-extra .discourse-tag {
|
||||
-webkit-animation: fadein 0.7s;
|
||||
animation: fadein 0.7s;
|
||||
@ -201,12 +209,6 @@ header .discourse-tag {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
.admin-tag {
|
||||
position: relative;
|
||||
float: right;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.tag-sort-options {
|
||||
margin-bottom: 20px;
|
||||
a {
|
||||
|
||||
@ -140,10 +140,12 @@
|
||||
}
|
||||
.clearfix > .topic-meta-data > .names {
|
||||
span.user-title {
|
||||
background-color: dark-light-choose($highlight-low, $highlight-medium);
|
||||
color: dark-light-choose($primary-high, $secondary-low);
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
a {
|
||||
background-color: dark-light-choose($highlight-low, $highlight-medium);
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +75,6 @@
|
||||
.d-editor-button-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: none;
|
||||
min-height: 30px;
|
||||
padding-left: 3px;
|
||||
border-bottom: 1px solid $primary-low;
|
||||
|
||||
@ -50,6 +50,8 @@
|
||||
}
|
||||
|
||||
.subcategories {
|
||||
margin-top: 0.25em;
|
||||
clear: both;
|
||||
.badge-notification.new-posts {
|
||||
padding: 0;
|
||||
margin: 0 10px 0 0;
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
.list-controls {
|
||||
.nav {
|
||||
float: left;
|
||||
margin-bottom: 15px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
@ -223,8 +223,7 @@ button.dismiss-read {
|
||||
.category-logo {
|
||||
max-height: 150px;
|
||||
float: left;
|
||||
margin-bottom: 15px;
|
||||
margin-right: 15px;
|
||||
margin: 0.25em 1em 0.5em 0;
|
||||
}
|
||||
|
||||
/* Tablet (portrait) ----------- */
|
||||
|
||||
@ -22,9 +22,11 @@
|
||||
@import "mobile/ring";
|
||||
@import "mobile/group";
|
||||
@import "mobile/groups";
|
||||
@import "mobile/dashboard_next";
|
||||
@import "mobile/admin_reports";
|
||||
@import "mobile/admin_report";
|
||||
@import "mobile/admin_report_table";
|
||||
@import "mobile/admin_report_counters";
|
||||
|
||||
// Import all component-specific files
|
||||
@import "mobile/components/*";
|
||||
|
||||
@ -9,27 +9,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report.top-referred-topics {
|
||||
.admin-report-table {
|
||||
.report-table {
|
||||
table-layout: fixed;
|
||||
|
||||
thead tr th.topic_title,
|
||||
tbody tr td.topic_title {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.admin-report.trending-search {
|
||||
.admin-report-table {
|
||||
.report-table {
|
||||
table-layout: fixed;
|
||||
|
||||
thead tr th.term,
|
||||
tbody tr td.term {
|
||||
width: 50%;
|
||||
|
||||
5
app/assets/stylesheets/mobile/admin_report_counters.scss
Normal file
5
app/assets/stylesheets/mobile/admin_report_counters.scss
Normal file
@ -0,0 +1,5 @@
|
||||
.counters-list {
|
||||
.counters-header .counters-cell {
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,28 @@
|
||||
.admin-report-table {
|
||||
.report-table {
|
||||
table-layout: fixed;
|
||||
.table {
|
||||
font-size: $font-down-1;
|
||||
}
|
||||
|
||||
thead tr th {
|
||||
font-weight: normal;
|
||||
font-size: $font-down-2;
|
||||
white-space: unset;
|
||||
.sort-button {
|
||||
display: none;
|
||||
}
|
||||
.table .admin-report-table-header {
|
||||
font-weight: 500;
|
||||
border-right: 1px solid $primary-low;
|
||||
padding: auto;
|
||||
|
||||
.title {
|
||||
writing-mode: vertical-rl;
|
||||
text-orientation: mixed;
|
||||
text-align: right;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.sort-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.table tbody tr td {
|
||||
&.user .username {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,24 +1,24 @@
|
||||
.admin-reports {
|
||||
.admin-report {
|
||||
.report-body {
|
||||
.body {
|
||||
flex-direction: column;
|
||||
|
||||
.report-filters {
|
||||
.filters {
|
||||
order: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.alert {
|
||||
.main {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.report-alert {
|
||||
margin: 0;
|
||||
order: 1;
|
||||
flex: 1;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.admin-report-table,
|
||||
.admin-report-chart {
|
||||
order: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,6 +158,7 @@
|
||||
flex: 1;
|
||||
margin-left: 5px;
|
||||
margin-bottom: 5px;
|
||||
z-index: z("base");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
5
app/assets/stylesheets/mobile/dashboard_next.scss
Normal file
5
app/assets/stylesheets/mobile/dashboard_next.scss
Normal file
@ -0,0 +1,5 @@
|
||||
.dashboard-next {
|
||||
.activity-metrics .counters-list {
|
||||
font-size: $font-down-1;
|
||||
}
|
||||
}
|
||||
@ -442,7 +442,7 @@ span.highlighted {
|
||||
}
|
||||
.user-title {
|
||||
order: 4;
|
||||
margin-right: auto;
|
||||
flex-basis: 100%;
|
||||
}
|
||||
span {
|
||||
margin-right: 4px;
|
||||
@ -456,7 +456,6 @@ span.highlighted {
|
||||
|
||||
.user-title {
|
||||
color: #aaa;
|
||||
padding-top: 2px;
|
||||
overflow: hidden;
|
||||
margin-right: 50px;
|
||||
}
|
||||
|
||||
@ -23,8 +23,8 @@ class Admin::ReportsController < Admin::AdminController
|
||||
|
||||
raise Discourse::NotFound unless report_type =~ /^[a-z0-9\_]+$/
|
||||
|
||||
start_date = (params[:start_date].present? ? params[:start_date].to_date : 30.days.ago).beginning_of_day
|
||||
end_date = (params[:end_date].present? ? params[:end_date].to_date : start_date + 30.days).end_of_day
|
||||
start_date = (params[:start_date].present? ? Time.parse(params[:start_date]).to_date : 1.days.ago).beginning_of_day
|
||||
end_date = (params[:end_date].present? ? Time.parse(params[:end_date]).to_date : start_date + 30.days).end_of_day
|
||||
|
||||
if params.has_key?(:category_id) && params[:category_id].to_i > 0
|
||||
category_id = params[:category_id].to_i
|
||||
|
||||
@ -33,7 +33,7 @@ class Admin::ScreenedIpAddressesController < Admin::AdminController
|
||||
|
||||
def update
|
||||
if @screened_ip_address.update_attributes(allowed_params)
|
||||
render json: success_json
|
||||
render_serialized(@screened_ip_address, ScreenedIpAddressSerializer)
|
||||
else
|
||||
render_json_error(@screened_ip_address)
|
||||
end
|
||||
|
||||
@ -182,11 +182,13 @@ class Admin::ThemesController < Admin::AdminController
|
||||
log_theme_change(original_json, @theme)
|
||||
format.json { render json: @theme, status: :ok }
|
||||
else
|
||||
format.json {
|
||||
format.json do
|
||||
error = @theme.errors.full_messages.join(", ").presence
|
||||
error = I18n.t("themes.bad_color_scheme") if @theme.errors[:color_scheme].present?
|
||||
error ||= I18n.t("themes.other_error")
|
||||
|
||||
error = @theme.errors[:color_scheme] ? I18n.t("themes.bad_color_scheme") : I18n.t("themes.other_error")
|
||||
render json: { errors: [ error ] }, status: :unprocessable_entity
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -22,7 +22,7 @@ class ApplicationController < ActionController::Base
|
||||
include GlobalPath
|
||||
include Hijack
|
||||
|
||||
attr_reader :theme_id
|
||||
attr_reader :theme_ids
|
||||
|
||||
serialization_scope :guardian
|
||||
|
||||
@ -62,8 +62,8 @@ class ApplicationController < ActionController::Base
|
||||
after_action :remember_theme_id
|
||||
|
||||
def remember_theme_id
|
||||
if @theme_id
|
||||
Stylesheet::Watcher.theme_id = @theme_id if defined? Stylesheet::Watcher
|
||||
if @theme_ids.present?
|
||||
Stylesheet::Watcher.theme_id = @theme_ids.first if defined? Stylesheet::Watcher
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -129,6 +129,14 @@ class ApplicationController < ActionController::Base
|
||||
)
|
||||
end
|
||||
|
||||
rescue_from ActiveRecord::RecordInvalid do |e|
|
||||
if request.format && request.format.json?
|
||||
render_json_error e, type: :record_invalid, status: 422
|
||||
else
|
||||
raise e
|
||||
end
|
||||
end
|
||||
|
||||
# If they hit the rate limiter
|
||||
rescue_from RateLimiter::LimitExceeded do |e|
|
||||
render_rate_limit_error(e)
|
||||
@ -173,7 +181,16 @@ class ApplicationController < ActionController::Base
|
||||
end
|
||||
end
|
||||
|
||||
rescue_from Discourse::NotFound, PluginDisabled, ActionController::RoutingError do
|
||||
rescue_from Discourse::NotFound do |e|
|
||||
rescue_discourse_actions(
|
||||
:not_found,
|
||||
e.status,
|
||||
check_permalinks: e.check_permalinks,
|
||||
original_path: e.original_path
|
||||
)
|
||||
end
|
||||
|
||||
rescue_from PluginDisabled, ActionController::RoutingError do
|
||||
rescue_discourse_actions(:not_found, 404)
|
||||
end
|
||||
|
||||
@ -194,12 +211,34 @@ class ApplicationController < ActionController::Base
|
||||
render_json_error I18n.t('read_only_mode_enabled'), type: :read_only, status: 503
|
||||
end
|
||||
|
||||
def redirect_with_client_support(url, options)
|
||||
if request.xhr?
|
||||
response.headers['Discourse-Xhr-Redirect'] = 'true'
|
||||
render plain: url
|
||||
else
|
||||
redirect_to url, options
|
||||
end
|
||||
end
|
||||
|
||||
def rescue_discourse_actions(type, status_code, opts = nil)
|
||||
opts ||= {}
|
||||
show_json_errors = (request.format && request.format.json?) ||
|
||||
(request.xhr?) ||
|
||||
((params[:external_id] || '').ends_with? '.json')
|
||||
|
||||
if type == :not_found && opts[:check_permalinks]
|
||||
url = opts[:original_path] || request.fullpath
|
||||
permalink = Permalink.find_by_url(url)
|
||||
|
||||
# there are some cases where we have a permalink but no url
|
||||
# cause category / topic was deleted
|
||||
if permalink.present? && permalink.target_url
|
||||
# permalink present, redirect to that URL
|
||||
redirect_with_client_support permalink.target_url, status: :moved_permanently
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
message = opts[:custom_message_translated] || I18n.t(opts[:custom_message] || type)
|
||||
|
||||
if show_json_errors
|
||||
@ -331,28 +370,33 @@ class ApplicationController < ActionController::Base
|
||||
resolve_safe_mode
|
||||
return if request.env[NO_CUSTOM]
|
||||
|
||||
theme_id = request[:preview_theme_id]&.to_i
|
||||
theme_ids = []
|
||||
|
||||
if preview_theme_id = request[:preview_theme_id]&.to_i
|
||||
theme_ids << preview_theme_id
|
||||
end
|
||||
|
||||
user_option = current_user&.user_option
|
||||
|
||||
unless theme_id
|
||||
if theme_ids.blank?
|
||||
ids, seq = cookies[:theme_ids]&.split("|")
|
||||
ids = ids&.split(",")&.map(&:to_i)
|
||||
if ids && ids.size > 0 && seq && seq.to_i == user_option&.theme_key_seq.to_i
|
||||
theme_id = ids.first
|
||||
if ids.present? && seq && seq.to_i == user_option&.theme_key_seq.to_i
|
||||
theme_ids = ids if guardian.allow_themes?(ids)
|
||||
end
|
||||
end
|
||||
|
||||
theme_id ||= user_option&.theme_ids&.first
|
||||
theme_ids = user_option&.theme_ids || [] if theme_ids.blank?
|
||||
|
||||
if theme_id && !guardian.allow_themes?(theme_id)
|
||||
theme_id = nil
|
||||
unless guardian.allow_themes?(theme_ids)
|
||||
theme_ids = []
|
||||
end
|
||||
|
||||
theme_id ||= SiteSetting.default_theme_id
|
||||
theme_id = nil if theme_id.blank? || theme_id == -1
|
||||
if theme_ids.blank? && SiteSetting.default_theme_id != -1
|
||||
theme_ids << SiteSetting.default_theme_id
|
||||
end
|
||||
|
||||
@theme_id = request.env[:resolved_theme_id] = theme_id
|
||||
@theme_ids = request.env[:resolved_theme_ids] = theme_ids
|
||||
end
|
||||
|
||||
def guardian
|
||||
@ -439,25 +483,6 @@ class ApplicationController < ActionController::Base
|
||||
request.session_options[:skip] = true
|
||||
end
|
||||
|
||||
def permalink_redirect_or_not_found
|
||||
url = request.fullpath
|
||||
permalink = Permalink.find_by_url(url)
|
||||
|
||||
if permalink.present?
|
||||
# permalink present, redirect to that URL
|
||||
if permalink.external_url
|
||||
redirect_to permalink.external_url, status: :moved_permanently
|
||||
elsif permalink.target_url
|
||||
redirect_to "#{Discourse::base_uri}#{permalink.target_url}", status: :moved_permanently
|
||||
else
|
||||
raise Discourse::NotFound
|
||||
end
|
||||
else
|
||||
# redirect to 404
|
||||
raise Discourse::NotFound
|
||||
end
|
||||
end
|
||||
|
||||
def secure_session
|
||||
SecureSession.new(session["secure_session_id"] ||= SecureRandom.hex)
|
||||
end
|
||||
@ -502,10 +527,10 @@ class ApplicationController < ActionController::Base
|
||||
target = view_context.mobile_view? ? :mobile : :desktop
|
||||
|
||||
data =
|
||||
if @theme_id
|
||||
if @theme_ids.present?
|
||||
{
|
||||
top: Theme.lookup_field(@theme_id, target, "after_header"),
|
||||
footer: Theme.lookup_field(@theme_id, target, "footer")
|
||||
top: Theme.lookup_field(@theme_ids, target, "after_header"),
|
||||
footer: Theme.lookup_field(@theme_ids, target, "footer")
|
||||
}
|
||||
else
|
||||
{}
|
||||
@ -685,7 +710,7 @@ class ApplicationController < ActionController::Base
|
||||
@slug = params[:slug].class == String ? params[:slug] : ''
|
||||
@slug = (params[:id].class == String ? params[:id] : '') if @slug.blank?
|
||||
@slug.tr!('-', ' ')
|
||||
@hide_google = true if SiteSetting.login_required
|
||||
@hide_search = true if SiteSetting.login_required
|
||||
render_to_string status: status, layout: layout, formats: [:html], template: '/exceptions/not_found'
|
||||
end
|
||||
|
||||
|
||||
@ -48,11 +48,13 @@ class CategoriesController < ApplicationController
|
||||
|
||||
if style == "categories_and_latest_topics".freeze
|
||||
@topic_list = TopicQuery.new(current_user, topic_options).list_latest
|
||||
@topic_list.more_topics_url = url_for(public_send("latest_path"))
|
||||
elsif style == "categories_and_top_topics".freeze
|
||||
@topic_list = TopicQuery.new(nil, topic_options).list_top_for(SiteSetting.top_page_default_timeframe.to_sym)
|
||||
@topic_list.more_topics_url = url_for(public_send("top_path"))
|
||||
end
|
||||
|
||||
if @topic_list.present?
|
||||
if @topic_list.present? && @topic_list.topics.present?
|
||||
store_preloaded(
|
||||
@topic_list.preload_key,
|
||||
MultiJson.dump(TopicListSerializer.new(@topic_list, scope: guardian))
|
||||
|
||||
@ -112,7 +112,7 @@ class EmbedController < ApplicationController
|
||||
end
|
||||
|
||||
response.headers['X-Frame-Options'] = "ALLOWALL"
|
||||
rescue URI::InvalidURIError
|
||||
rescue URI::Error
|
||||
raise Discourse::InvalidAccess.new('invalid referer host')
|
||||
end
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
class ExceptionsController < ApplicationController
|
||||
skip_before_action :check_xhr, :preload_json
|
||||
before_action :hide_google
|
||||
before_action :hide_search
|
||||
|
||||
def not_found
|
||||
# centralize all rendering of 404 into app controller
|
||||
@ -14,8 +14,8 @@ class ExceptionsController < ApplicationController
|
||||
|
||||
private
|
||||
|
||||
def hide_google
|
||||
@hide_google = true if SiteSetting.login_required
|
||||
def hide_search
|
||||
@hide_search = true if SiteSetting.login_required
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -98,7 +98,13 @@ class GroupsController < ApplicationController
|
||||
type_filters: type_filters
|
||||
},
|
||||
total_rows_groups: count,
|
||||
load_more_groups: groups_path(page: page + 1, type: type),
|
||||
load_more_groups: groups_path(
|
||||
page: page + 1,
|
||||
type: type,
|
||||
order: order,
|
||||
asc: params[:asc],
|
||||
filter: filter
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@ -344,7 +344,7 @@ class ListController < ApplicationController
|
||||
parent_category_id = nil
|
||||
if parent_slug_or_id.present?
|
||||
parent_category_id = Category.query_parent_category(parent_slug_or_id)
|
||||
permalink_redirect_or_not_found && (return) if parent_category_id.blank? && !id
|
||||
raise Discourse::NotFound.new("category not found", check_permalinks: true) if parent_category_id.blank? && !id
|
||||
end
|
||||
|
||||
@category = Category.query_category(slug_or_id, parent_category_id)
|
||||
@ -355,7 +355,7 @@ class ListController < ApplicationController
|
||||
(redirect_to category.url, status: 301) && return if category
|
||||
end
|
||||
|
||||
permalink_redirect_or_not_found && (return) if !@category
|
||||
raise Discourse::NotFound.new("category not found", check_permalinks: true) if !@category
|
||||
|
||||
@description_meta = @category.description_text
|
||||
raise Discourse::NotFound unless guardian.can_see?(@category)
|
||||
@ -371,7 +371,12 @@ class ListController < ApplicationController
|
||||
params[:tags] = [params[:tag_id].parameterize] if params[:tag_id].present? && guardian.can_tag_pms?
|
||||
|
||||
TopicQuery.public_valid_options.each do |key|
|
||||
options[key] = params[key]
|
||||
if params.key?(key)
|
||||
val = options[key] = params[key]
|
||||
if !TopicQuery.validate?(key, val)
|
||||
raise Discourse::InvalidParameters.new key
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# hacky columns get special handling
|
||||
|
||||
@ -19,10 +19,12 @@ class MetadataController < ApplicationController
|
||||
end
|
||||
file_info = get_file_info(logo)
|
||||
|
||||
display = request.user_agent =~ /iPad|iPhone/ ? 'browser' : 'standalone'
|
||||
|
||||
manifest = {
|
||||
name: SiteSetting.title,
|
||||
short_name: SiteSetting.title,
|
||||
display: 'standalone',
|
||||
display: display,
|
||||
orientation: 'any',
|
||||
start_url: Discourse.base_uri.present? ? "#{Discourse.base_uri}/" : '.',
|
||||
background_color: "##{ColorScheme.hex_for_name('secondary')}",
|
||||
|
||||
@ -8,9 +8,7 @@ class PermalinksController < ApplicationController
|
||||
|
||||
raise Discourse::NotFound unless permalink
|
||||
|
||||
if permalink.external_url
|
||||
redirect_to permalink.external_url, status: :moved_permanently
|
||||
elsif permalink.target_url
|
||||
if permalink.target_url
|
||||
redirect_to permalink.target_url, status: :moved_permanently
|
||||
else
|
||||
raise Discourse::NotFound
|
||||
|
||||
@ -653,6 +653,11 @@ class PostsController < ApplicationController
|
||||
result[:is_warning] = false
|
||||
end
|
||||
|
||||
if params[:no_bump] == "true"
|
||||
raise Discourse::InvalidParameters.new(:no_bump) unless guardian.can_skip_bump?
|
||||
result[:no_bump] = true
|
||||
end
|
||||
|
||||
if params[:shared_draft] == 'true'
|
||||
raise Discourse::InvalidParameters.new(:shared_draft) unless guardian.can_create_shared_draft?
|
||||
|
||||
|
||||
@ -4,13 +4,7 @@ class RobotsTxtController < ApplicationController
|
||||
|
||||
# NOTE: order is important!
|
||||
DISALLOWED_PATHS ||= %w{
|
||||
/auth/cas
|
||||
/auth/facebook/callback
|
||||
/auth/twitter/callback
|
||||
/auth/google/callback
|
||||
/auth/yahoo/callback
|
||||
/auth/github/callback
|
||||
/auth/cas/callback
|
||||
/auth/
|
||||
/assets/browser-update*.js
|
||||
/users/
|
||||
/u/
|
||||
|
||||
@ -88,7 +88,7 @@ class StaticController < ApplicationController
|
||||
destination = uri.path
|
||||
destination = "#{uri.path}?#{uri.query}" if uri.path =~ /new-topic/ || uri.path =~ /new-message/ || uri.path =~ /user-api-key/
|
||||
end
|
||||
rescue URI::InvalidURIError
|
||||
rescue URI::Error
|
||||
# Do nothing if the URI is invalid
|
||||
end
|
||||
end
|
||||
|
||||
@ -29,7 +29,7 @@ class StylesheetsController < ApplicationController
|
||||
# we hold of re-compilation till someone asks for asset
|
||||
if target.include?("theme")
|
||||
split_target, theme_id = target.split(/_(-?[0-9]+)/)
|
||||
theme = Theme.find(theme_id) if theme_id
|
||||
theme = Theme.find_by(id: theme_id) if theme_id.present?
|
||||
else
|
||||
split_target, color_scheme_id = target.split(/_(-?[0-9]+)/)
|
||||
theme = Theme.find_by(color_scheme_id: color_scheme_id)
|
||||
|
||||
@ -90,7 +90,7 @@ class TagsController < ::ApplicationController
|
||||
canonical_url "#{Discourse.base_url_no_prefix}#{public_send(path_name, *(params.slice(:parent_category, :category, :tag_id).values.map { |t| t.force_encoding("UTF-8") }))}"
|
||||
|
||||
if @list.topics.size == 0 && params[:tag_id] != 'none' && !Tag.where(name: @tag_id).exists?
|
||||
permalink_redirect_or_not_found
|
||||
raise Discourse::NotFound.new("tag not found", check_permalinks: true)
|
||||
else
|
||||
respond_with_list(@list)
|
||||
end
|
||||
|
||||
@ -1,27 +1,26 @@
|
||||
class ThemesController < ::ApplicationController
|
||||
def assets
|
||||
theme_id = params[:id].to_i
|
||||
theme_ids = params[:ids].to_s.split("-").map(&:to_i)
|
||||
|
||||
if params[:id] == "default"
|
||||
theme_id = nil
|
||||
if params[:ids] == "default"
|
||||
theme_ids = nil
|
||||
else
|
||||
raise Discourse::NotFound unless Theme.where(id: theme_id).exists?
|
||||
raise Discourse::NotFound unless guardian.allow_themes?(theme_ids)
|
||||
end
|
||||
|
||||
object = [:mobile, :desktop, :desktop_theme, :mobile_theme].map do |target|
|
||||
link = Stylesheet::Manager.stylesheet_link_tag(target, 'all', params[:id])
|
||||
if link
|
||||
href = link.split(/["']/)[1]
|
||||
if Rails.env.development?
|
||||
href << (href.include?("?") ? "&" : "?")
|
||||
href << SecureRandom.hex
|
||||
end
|
||||
{
|
||||
target: target,
|
||||
url: href
|
||||
}
|
||||
targets = view_context.mobile_view? ? [:mobile, :mobile_theme] : [:desktop, :desktop_theme]
|
||||
targets << :admin if guardian.is_staff?
|
||||
|
||||
object = targets.map do |target|
|
||||
Stylesheet::Manager.stylesheet_data(target, theme_ids).map do |hash|
|
||||
next hash unless Rails.env.development?
|
||||
|
||||
dup_hash = hash.dup
|
||||
dup_hash[:new_href] << (dup_hash[:new_href].include?("?") ? "&" : "?")
|
||||
dup_hash[:new_href] << SecureRandom.hex
|
||||
dup_hash
|
||||
end
|
||||
end.compact
|
||||
end.flatten
|
||||
|
||||
render json: object.as_json
|
||||
end
|
||||
|
||||
@ -33,7 +33,8 @@ class TopicsController < ApplicationController
|
||||
:move_to_inbox,
|
||||
:convert_topic,
|
||||
:bookmark,
|
||||
:publish
|
||||
:publish,
|
||||
:reset_bump_date
|
||||
]
|
||||
|
||||
before_action :consider_user_for_promotion, only: :show
|
||||
@ -135,8 +136,12 @@ class TopicsController < ApplicationController
|
||||
end
|
||||
|
||||
if ex.obj && Topic === ex.obj && guardian.can_see_topic_if_not_deleted?(ex.obj)
|
||||
rescue_discourse_actions(:not_found, 410)
|
||||
return
|
||||
raise Discourse::NotFound.new(
|
||||
"topic was deleted",
|
||||
status: 410,
|
||||
check_permalinks: true,
|
||||
original_path: ex.obj.relative_url
|
||||
)
|
||||
end
|
||||
|
||||
raise ex
|
||||
@ -716,6 +721,17 @@ class TopicsController < ApplicationController
|
||||
render_json_error(ex)
|
||||
end
|
||||
|
||||
def reset_bump_date
|
||||
params.require(:id)
|
||||
guardian.ensure_can_update_bumped_at!
|
||||
|
||||
topic = Topic.find_by(id: params[:id])
|
||||
raise Discourse::NotFound.new unless topic
|
||||
|
||||
topic.reset_bumped_at
|
||||
render body: nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def topic_params
|
||||
|
||||
@ -179,14 +179,15 @@ class UserAvatarsController < ApplicationController
|
||||
send_file path, disposition: nil
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# consider removal of hacks some time in 2019
|
||||
|
||||
def get_optimized_image(upload, size)
|
||||
OptimizedImage.create_for(
|
||||
upload,
|
||||
size,
|
||||
size,
|
||||
filename: upload.original_filename,
|
||||
allow_animation: SiteSetting.allow_animated_avatars,
|
||||
)
|
||||
return if !upload
|
||||
|
||||
upload.get_optimized_image(size, size, allow_animation: SiteSetting.allow_animated_avatars)
|
||||
# TODO decide if we want to detach here
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -60,7 +60,7 @@ class UserBadgesController < ApplicationController
|
||||
if params[:reason].present?
|
||||
path = begin
|
||||
URI.parse(params[:reason]).path
|
||||
rescue URI::InvalidURIError
|
||||
rescue URI::Error
|
||||
end
|
||||
|
||||
route = Rails.application.routes.recognize_path(path) if path
|
||||
|
||||
@ -41,7 +41,7 @@ class Users::OmniauthCallbacksController < ApplicationController
|
||||
if origin.present?
|
||||
parsed = begin
|
||||
URI.parse(origin)
|
||||
rescue URI::InvalidURIError
|
||||
rescue URI::Error
|
||||
end
|
||||
|
||||
if parsed
|
||||
|
||||
@ -581,7 +581,6 @@ class UsersController < ApplicationController
|
||||
|
||||
email_token_user = EmailToken.confirmable(token)&.user
|
||||
totp_enabled = email_token_user&.totp_enabled?
|
||||
backup_enabled = email_token_user&.backup_codes_enabled?
|
||||
second_factor_token = params[:second_factor_token]
|
||||
second_factor_method = params[:second_factor_method].to_i
|
||||
confirm_email = false
|
||||
@ -1079,7 +1078,7 @@ class UsersController < ApplicationController
|
||||
|
||||
# Using Discourse.authenticators rather than Discourse.enabled_authenticators so users can
|
||||
# revoke permissions even if the admin has temporarily disabled that type of login
|
||||
authenticator = Discourse.authenticators.find { |authenticator| authenticator.name == provider_name }
|
||||
authenticator = Discourse.authenticators.find { |a| a.name == provider_name }
|
||||
raise Discourse::NotFound if authenticator.nil? || !authenticator.can_revoke?
|
||||
|
||||
skip_remote = params.permit(:skip_remote)
|
||||
@ -1088,9 +1087,9 @@ class UsersController < ApplicationController
|
||||
hijack do
|
||||
result = authenticator.revoke(user, skip_remote: skip_remote)
|
||||
if result
|
||||
return render json: success_json
|
||||
render json: success_json
|
||||
else
|
||||
return render json: {
|
||||
render json: {
|
||||
success: false,
|
||||
message: I18n.t("associated_accounts.revoke_failed", provider_name: provider_name)
|
||||
}
|
||||
|
||||
@ -177,10 +177,8 @@ module ApplicationHelper
|
||||
["ar", "ur", "fa_IR", "he"].include? I18n.locale.to_s
|
||||
end
|
||||
|
||||
def user_locale
|
||||
locale = current_user.locale if current_user && SiteSetting.allow_user_locale
|
||||
# changing back to default shoves a blank string there
|
||||
locale.present? ? locale : SiteSetting.default_locale
|
||||
def html_lang
|
||||
SiteSetting.default_locale.sub("_", "-")
|
||||
end
|
||||
|
||||
# Creates open graph and twitter card meta data
|
||||
@ -350,11 +348,11 @@ module ApplicationHelper
|
||||
end
|
||||
end
|
||||
|
||||
def theme_id
|
||||
def theme_ids
|
||||
if customization_disabled?
|
||||
nil
|
||||
else
|
||||
request.env[:resolved_theme_id]
|
||||
request.env[:resolved_theme_ids]
|
||||
end
|
||||
end
|
||||
|
||||
@ -378,17 +376,17 @@ module ApplicationHelper
|
||||
end
|
||||
|
||||
def theme_lookup(name)
|
||||
lookup = Theme.lookup_field(theme_id, mobile_view? ? :mobile : :desktop, name)
|
||||
lookup = Theme.lookup_field(theme_ids, mobile_view? ? :mobile : :desktop, name)
|
||||
lookup.html_safe if lookup
|
||||
end
|
||||
|
||||
def discourse_stylesheet_link_tag(name, opts = {})
|
||||
if opts.key?(:theme_id)
|
||||
id = opts[:theme_id] unless customization_disabled?
|
||||
if opts.key?(:theme_ids)
|
||||
ids = opts[:theme_ids] unless customization_disabled?
|
||||
else
|
||||
id = theme_id
|
||||
ids = theme_ids
|
||||
end
|
||||
|
||||
Stylesheet::Manager.stylesheet_link_tag(name, 'all', id)
|
||||
Stylesheet::Manager.stylesheet_link_tag(name, 'all', ids)
|
||||
end
|
||||
end
|
||||
|
||||
@ -106,7 +106,7 @@ module UserNotificationsHelper
|
||||
|
||||
def url_for_email(href)
|
||||
URI(href).host.present? ? href : UrlHelper.absolute("#{Discourse.base_uri}#{href}")
|
||||
rescue URI::InvalidURIError, URI::InvalidComponentError
|
||||
rescue URI::Error
|
||||
href
|
||||
end
|
||||
|
||||
|
||||
@ -119,6 +119,11 @@ module Jobs
|
||||
RailsMultisite::ConnectionManagement.all_dbs
|
||||
end
|
||||
|
||||
logster_env = {}
|
||||
Logster.add_to_env(logster_env, :current_db, 'default')
|
||||
Logster.add_to_env(logster_env, :job, self.class.to_s)
|
||||
Thread.current[Logster::Logger::LOGSTER_ENV] = logster_env
|
||||
|
||||
exceptions = []
|
||||
dbs.each do |db|
|
||||
begin
|
||||
@ -129,6 +134,7 @@ module Jobs
|
||||
I18n.locale = SiteSetting.default_locale || "en"
|
||||
I18n.ensure_all_loaded!
|
||||
begin
|
||||
Logster.add_to_env(logster_env, :db, db)
|
||||
execute(opts)
|
||||
rescue => e
|
||||
exception[:ex] = e
|
||||
@ -140,6 +146,7 @@ module Jobs
|
||||
exception[:other] = { problem_db: db }
|
||||
ensure
|
||||
total_db_time += Instrumenter.stats.duration_ms
|
||||
Thread.current[Logster::Logger::LOGSTER_ENV] = nil
|
||||
end
|
||||
end
|
||||
|
||||
@ -147,6 +154,8 @@ module Jobs
|
||||
end
|
||||
end
|
||||
|
||||
Thread.current[Logster::Logger::LOGSTER_ENV] = nil
|
||||
|
||||
if exceptions.length > 0
|
||||
exceptions.each do |exception_hash|
|
||||
Discourse.handle_job_exception(exception_hash[:ex], error_context(opts, exception_hash[:code], exception_hash[:other]))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user