mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
FEATURE: allows plugins to add a global notice (#8552)
* FEATURE: allows plugins to add a global notice
Usage:
```
api.addGlobalNotice(id, text, options = {});
```
Options can be:
```
dismissable // Will display a button to hide the notice if true
html // will prepend html to the next if present
level // alert level, will usee css class of alert component
persistentDismiss // if true won't show notice again on reload
onDismiss // execute a custom action on dismiss
visibility // defines custom logic for notice visibility
```
Co-authored-by: Robin Ward <robin.ward@gmail.com>
This commit is contained in:
@@ -1,119 +1,208 @@
|
||||
import { bind } from "@ember/runloop";
|
||||
import { bind, cancel } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import { on } from "discourse-common/utils/decorators";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import LogsNotice from "discourse/services/logs-notice";
|
||||
import { bufferedRender } from "discourse-common/lib/buffered-render";
|
||||
import EmberObject from "@ember/object";
|
||||
|
||||
export default Component.extend(
|
||||
bufferedRender({
|
||||
rerenderTriggers: ["site.isReadOnly", "siteSettings.disable_emails"],
|
||||
const _pluginNotices = [];
|
||||
|
||||
buildBuffer(buffer) {
|
||||
export function addGlobalNotice(text, id, options = {}) {
|
||||
_pluginNotices.push(Notice.create({ text, id, options }));
|
||||
}
|
||||
|
||||
const GLOBAL_NOTICE_DISMISSED_PROMPT_KEY = "dismissed-global-notice";
|
||||
|
||||
const Notice = EmberObject.extend({
|
||||
text: null,
|
||||
id: null,
|
||||
options: null,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
const defaults = {
|
||||
dismissable: false,
|
||||
html: null,
|
||||
level: "info",
|
||||
persistentDismiss: true,
|
||||
onDismiss: null,
|
||||
visibility: null
|
||||
};
|
||||
|
||||
this.options = this.set(
|
||||
"options",
|
||||
Object.assign(defaults, this.options || {})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default Component.extend({
|
||||
logNotice: null,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
this._setupObservers();
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this._tearDownObservers();
|
||||
},
|
||||
|
||||
notices: Ember.computed(
|
||||
"site.isReadOnly",
|
||||
"siteSettings.disable_emails",
|
||||
"logNotice.{id,text,hidden}",
|
||||
function() {
|
||||
let notices = [];
|
||||
|
||||
if ($.cookie("dosp") === "1") {
|
||||
$.removeCookie("dosp", { path: "/" });
|
||||
notices.push([I18n.t("forced_anonymous"), "forced-anonymous"]);
|
||||
notices.push(
|
||||
Notice.create({
|
||||
text: I18n.t("forced_anonymous"),
|
||||
id: "forced-anonymous"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (this.session.get("safe_mode")) {
|
||||
notices.push([I18n.t("safe_mode.enabled"), "safe-mode"]);
|
||||
if (this.session && this.session.safe_mode) {
|
||||
notices.push(
|
||||
Notice.create({ text: I18n.t("safe_mode.enabled"), id: "safe-mode" })
|
||||
);
|
||||
}
|
||||
|
||||
if (this.site.get("isReadOnly")) {
|
||||
notices.push([I18n.t("read_only_mode.enabled"), "alert-read-only"]);
|
||||
if (this.site.isReadOnly) {
|
||||
notices.push(
|
||||
Notice.create({
|
||||
text: I18n.t("read_only_mode.enabled"),
|
||||
id: "alert-read-only"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
this.siteSettings.disable_emails === "yes" ||
|
||||
this.siteSettings.disable_emails === "non-staff"
|
||||
) {
|
||||
notices.push([I18n.t("emails_are_disabled"), "alert-emails-disabled"]);
|
||||
notices.push(
|
||||
Notice.create({
|
||||
text: I18n.t("emails_are_disabled"),
|
||||
id: "alert-emails-disabled"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (this.site.get("wizard_required")) {
|
||||
if (this.site.wizard_required) {
|
||||
const requiredText = I18n.t("wizard_required", {
|
||||
url: Discourse.getURL("/wizard")
|
||||
});
|
||||
notices.push([requiredText, "alert-wizard"]);
|
||||
notices.push(Notice.create({ text: requiredText, id: "alert-wizard" }));
|
||||
}
|
||||
|
||||
if (
|
||||
this.currentUser &&
|
||||
this.currentUser.get("staff") &&
|
||||
this.get("currentUser.staff") &&
|
||||
this.siteSettings.bootstrap_mode_enabled
|
||||
) {
|
||||
if (this.siteSettings.bootstrap_mode_min_users > 0) {
|
||||
notices.push([
|
||||
I18n.t("bootstrap_mode_enabled", {
|
||||
min_users: this.siteSettings.bootstrap_mode_min_users
|
||||
}),
|
||||
"alert-bootstrap-mode"
|
||||
]);
|
||||
notices.push(
|
||||
Notice.create({
|
||||
text: I18n.t("bootstrap_mode_enabled", {
|
||||
min_users: this.siteSettings.bootstrap_mode_min_users
|
||||
}),
|
||||
id: "alert-bootstrap-mode"
|
||||
})
|
||||
);
|
||||
} else {
|
||||
notices.push([
|
||||
I18n.t("bootstrap_mode_disabled"),
|
||||
"alert-bootstrap-mode"
|
||||
]);
|
||||
notices.push(
|
||||
Notice.create({
|
||||
text: I18n.t("bootstrap_mode_disabled"),
|
||||
id: "alert-bootstrap-mode"
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_.isEmpty(this.siteSettings.global_notice)) {
|
||||
notices.push([this.siteSettings.global_notice, "alert-global-notice"]);
|
||||
}
|
||||
|
||||
if (!LogsNotice.currentProp("hidden")) {
|
||||
notices.push([
|
||||
LogsNotice.currentProp("message"),
|
||||
"alert-logs-notice",
|
||||
`<button class='btn btn-flat close'>${iconHTML("times")}</button>`
|
||||
]);
|
||||
}
|
||||
|
||||
if (notices.length > 0) {
|
||||
buffer.push(
|
||||
notices
|
||||
.map(n => {
|
||||
var html = `<div class='row'><div class='alert alert-info ${n[1]}'>`;
|
||||
if (n[2]) html += n[2];
|
||||
html += `${n[0]}</div></div>`;
|
||||
return html;
|
||||
})
|
||||
.join("")
|
||||
if (
|
||||
this.siteSettings.global_notice &&
|
||||
this.siteSettings.global_notice.length
|
||||
) {
|
||||
notices.push(
|
||||
Notice.create({
|
||||
text: this.siteSettings.global_notice,
|
||||
id: "alert-global-notice"
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@on("didInsertElement")
|
||||
_setupLogsNotice() {
|
||||
this._boundRerenderBuffer = bind(this, this.rerenderBuffer);
|
||||
LogsNotice.current().addObserver("hidden", this._boundRerenderBuffer);
|
||||
|
||||
this._boundResetCurrentProp = bind(this, this._resetCurrentProp);
|
||||
$(this.element).on(
|
||||
"click.global-notice",
|
||||
".alert-logs-notice .close",
|
||||
this._boundResetCurrentProp
|
||||
);
|
||||
},
|
||||
|
||||
@on("willDestroyElement")
|
||||
_teardownLogsNotice() {
|
||||
if (this._boundResetCurrentProp) {
|
||||
$(this.element).off("click.global-notice", this._boundResetCurrentProp);
|
||||
if (this.logNotice) {
|
||||
notices.push(this.logNotice);
|
||||
}
|
||||
|
||||
if (this._boundRerenderBuffer) {
|
||||
LogsNotice.current().removeObserver(
|
||||
"hidden",
|
||||
this._boundRerenderBuffer
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
_resetCurrentProp() {
|
||||
LogsNotice.currentProp("text", "");
|
||||
return notices.concat(_pluginNotices).filter(notice => {
|
||||
if (notice.options.visibility) {
|
||||
return notice.options.visibility(notice);
|
||||
} else {
|
||||
const key = `${GLOBAL_NOTICE_DISMISSED_PROMPT_KEY}-${notice.id}`;
|
||||
return !this.keyValueStore.get(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
),
|
||||
|
||||
actions: {
|
||||
dismissNotice(notice) {
|
||||
if (notice.options.onDismiss) {
|
||||
notice.options.onDismiss(notice);
|
||||
}
|
||||
|
||||
if (notice.options.persistentDismiss) {
|
||||
this.keyValueStore.set({
|
||||
key: `${GLOBAL_NOTICE_DISMISSED_PROMPT_KEY}-${notice.id}`,
|
||||
value: true
|
||||
});
|
||||
}
|
||||
|
||||
const alert = document.getElementById(`global-notice-${notice.id}`);
|
||||
if (alert) alert.style.display = "none";
|
||||
}
|
||||
},
|
||||
|
||||
_setupObservers() {
|
||||
this._boundLogsNoticeHandler = bind(this, this._handleLogsNoticeUpdate);
|
||||
LogsNotice.current().addObserver("hidden", this._boundLogsNoticeHandler);
|
||||
LogsNotice.current().addObserver("text", this._boundLogsNoticeHandler);
|
||||
},
|
||||
|
||||
_tearDownObservers() {
|
||||
if (this._boundLogsNoticeHandler) {
|
||||
LogsNotice.current().removeObserver("text", this._boundLogsNoticeHandler);
|
||||
LogsNotice.current().removeObserver(
|
||||
"hidden",
|
||||
this._boundLogsNoticeHandler
|
||||
);
|
||||
cancel(this._boundLogsNoticeHandler);
|
||||
}
|
||||
},
|
||||
|
||||
_handleLogsNoticeUpdate() {
|
||||
const logNotice = Notice.create({
|
||||
text: LogsNotice.currentProp("message"),
|
||||
id: "alert-logs-notice",
|
||||
options: {
|
||||
dismissable: true,
|
||||
persistentDismiss: false,
|
||||
visibility() {
|
||||
return !LogsNotice.currentProp("hidden");
|
||||
},
|
||||
onDismiss() {
|
||||
LogsNotice.currentProp("hidden", true);
|
||||
LogsNotice.currentProp("text", "");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.set("logNotice", logNotice);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ import { includeAttributes } from "discourse/lib/transform-post";
|
||||
import { registerHighlightJSLanguage } from "discourse/lib/highlight-syntax";
|
||||
import { addToolbarCallback } from "discourse/components/d-editor";
|
||||
import { addWidgetCleanCallback } from "discourse/components/mount-widget";
|
||||
import { addGlobalNotice } from "discourse/components/global-notice";
|
||||
import {
|
||||
createWidget,
|
||||
reopenWidget,
|
||||
@@ -49,7 +50,7 @@ import { queryRegistry } from "discourse/widgets/widget";
|
||||
import Composer from "discourse/models/composer";
|
||||
|
||||
// If you add any methods to the API ensure you bump up this number
|
||||
const PLUGIN_API_VERSION = "0.8.36";
|
||||
const PLUGIN_API_VERSION = "0.8.37";
|
||||
|
||||
class PluginApi {
|
||||
constructor(version, container) {
|
||||
@@ -969,6 +970,18 @@ class PluginApi {
|
||||
registerHighlightJSLanguage(name, fn) {
|
||||
registerHighlightJSLanguage(name, fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds global notices to display.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* api.addGlobalNotice("text", "foo", { html: "<p>bar</p>" })
|
||||
*
|
||||
**/
|
||||
addGlobalNotice(id, text, options) {
|
||||
addGlobalNotice(id, text, options);
|
||||
}
|
||||
}
|
||||
|
||||
let _pluginv01;
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
{{#each notices as |notice|}}
|
||||
<div class="row">
|
||||
<div id="global-notice-{{notice.id}}" class="alert alert-{{notice.options.level}} {{notice.id}}">
|
||||
{{#if notice.options.html}}
|
||||
{{{notice.options.html}}}
|
||||
{{/if}}
|
||||
<span class="text">{{notice.text}}</span>
|
||||
|
||||
{{#if notice.options.dismissable}}
|
||||
{{d-button
|
||||
class="btn-flat close"
|
||||
icon="times"
|
||||
action=(action "dismissNotice")
|
||||
actionParam=notice
|
||||
}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
@@ -1,19 +1,28 @@
|
||||
.alert {
|
||||
padding: 8px 32px 8px 16px;
|
||||
padding: 0.5em 1em;
|
||||
background-color: $danger-low;
|
||||
color: $primary;
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
font-size: $font-up-3;
|
||||
align-self: flex-start;
|
||||
margin-left: auto;
|
||||
|
||||
.d-icon {
|
||||
color: $primary-low-mid;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
flex: 1 1 auto;
|
||||
|
||||
& + .close {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
&.alert-success {
|
||||
background-color: $success-low;
|
||||
color: $primary;
|
||||
|
||||
Reference in New Issue
Block a user