The Bootbox library for dialogs in Discourse is now deprecated as of pmusaraj, with removal planned for 2023. Developers are advised to migrate to the dialog service. TimJensen sought help converting a theme script using bootbox.alert, encountering syntax errors with ES6 imports in <script> tags. pmusaraj recommended moving such logic into an initializer JS file within a theme component, using require and getOwner to access the dialog service. A complete working example was provided here, including proper use of htmlSafe and schedule to ensure the dialog service is ready. TimJensen confirmed the solution worked. The deprecation is now fully resolved as of kelv’s PR.
the bootbox library for dialogs is now deprecated.
Developers, please use the dialog service in core instead of bootbox in your custom themes and plugins. The bootbox library will be removed from core Discourse entirely sometime in 2023.
Here are some example PRs from plugins and themes that show how to transition to the dialog service:
The easiest option would be to load the service using the lookup. Here’s an example code (this is untested, but we use something like this in core, outside of Ember components/controllers/routes):
Thanks. I tried this but am getting an error: SyntaxError: /discourse/theme-11/initializers/theme-field-36-common-html-script-2: 'import' and 'export' may only appear at the top level. (13:4)
Ah, right, we need to use requires in these script tags. If it’s ok to share, what’s the full code of what you are doing here? I’d like to propose an alternative to this <script> tag, we’ve moved most theme code to standalone JS files in the theme components that we maintain ourselves and it might be easier to do a more general refactor here for you.
Thanks for the offer! Here is the full script (with some text slightly modified), which was working fine until we noticed the Bootbox popup no longer rendered HTML tags correctly.
<script type="text/discourse-plugin" version="0.8">
if (typeof bootbox === 'undefined') {
console.log('Cannot trigger undefined "bootbox"');
return;
}
let currentUser = api.getCurrentUser();
if (currentUser) {
api.container.lookup('store:main').find('user', currentUser.username).then((user) => {
let userGroups = user.groups.map(group => group.name);
let bodyClasses = userGroups.map(group => `user-group--${group}`);
document.querySelector('body').classList.add(...bodyClasses)
let showPopup;
switch(true) {
case userGroups.includes('restricted_member'):
showPopup = true;
break;
case userGroups.includes('users_all'):
case userGroups.includes('officers'):
showPopup = false;
break;
default:
showPopup = true;
break;
}
if (!showPopup) {
return;
}
let alertHeading = '<h2>Oh no!</h2>';
let alertBody = '<p>Your account is currently restricted and you no longer have access to valuable resources. <a href="https://meta.discourse.org">Click here</a> for more information.</p>';
bootbox.alert(alertHeading + alertBody);
});
}
</script>
Hi Tim,
Here’s the alternative, note that the contents here needs to go in an initializer JS file in a component. You can see a simple example of a full component structure here.
import { withPluginApi } from "discourse/lib/plugin-api";
import { getOwner } from "discourse-common/lib/get-owner";
import { schedule } from "@ember/runloop";
import { htmlSafe } from "@ember/template";
export default {
name: "tester-initializer",
initialize() {
withPluginApi("0.8", (api) => {
const currentUser = api.getCurrentUser();
if (currentUser) {
const userGroups = currentUser.groups.map((group) => group.name);
let showPopup = false;
switch (true) {
case userGroups.includes("admins"):
showPopup = true;
break;
}
if (!showPopup) {
return;
}
const alertHeading = "Oh no!";
const alertBody =
'Your account is currently restricted and you no longer have access to valuable resources. <a href="https://meta.discourse.org">Click here</a> for more information.';
schedule("afterRender", () => {
// delay needed so that the dialog service is loaded
const dialog = getOwner(this).lookup("service:dialog");
dialog.alert({
message: htmlSafe(alertBody),
title: alertHeading,
});
});
}
});
},
};