Skip to content

Commit 46c8220

Browse files
Update outdated references to <script>-based js/hbs themes (#64)
1 parent 697f150 commit 46c8220

File tree

9 files changed

+92
-402
lines changed

9 files changed

+92
-402
lines changed
Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
11
---
2-
title: Using the PluginAPI in Site Customizations
3-
short_title: PluginAPI
2+
title: Using the JS API
3+
short_title: JS API
44
id: pluginapi
55
---
66

7-
Using the [client side Plugin API](https://meta.discourse.org/t/a-new-versioned-api-for-client-side-plugins/40051) is the safest way to build Discourse Javascript plugins and themes while respecting backwards compatibility.
7+
Discourse's JavaScript API allows themes and plugins to make extensive customizations to the user experience. The simplest way to use it is to create a new theme from the admin panel, click "Edit Code", and then head to the JS tab.
88

9-
However, some people have made simple customizations using the Admin > Customization > CSS/HTML and dropping some Javascript into a `<script>` tag. Previously, it was very difficult to use the `withPluginApi` to access objects using the Discourse container.
9+
For file-based themes, the API can be used by creating a file in the `api-initializers` directory. For theme's that's `{theme}/javascripts/api-initializers/init-theme.gjs`, and for plugins, it's `{plugin}/assets/javascripts/discourse/api-initializers/init-plugin.js`. The content should be:
1010

11-
In the latest tests-passed build of Discourse I've added the ability to use the pluginAPI via a site customization.
11+
```gjs
12+
import { apiInitializer } from "discourse/lib/api";
1213
13-
To use it, you just need to add some attributes to a script tag in your `</HEAD>` customization:
14-
15-
```html
16-
<script type="text/discourse-plugin" version="0.1">
17-
// you can use the `api` object here!
18-
api.decorateCooked($elem => $elem.css({ backgroundColor: 'yellow' }));
19-
</script>
14+
export default apiInitializer((api) => {
15+
// Your code here
16+
});
2017
```
2118

22-
When you save the customization, Discourse will transpile the ES2015 code within the tag so you can use the latest Javascript features. Additionally, it wraps the code in an initializer and runs it through `withPluginApi` so you don't have to bother with that. Just specify the version of the `api` object you want and Discourse will give it to you, providing safe backwards compatibility.
19+
All the available APIs are listed in the [`plugin-api.gjs` source code](https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/lib/plugin-api.gjs) in Discourse core, along with a short description and examples.
20+
21+
For a full tuturial, including examples of JS API usage, check out:
2322

24-
If the compilation of the ES2015 fails for some reason, when you view source on your page you'll see the error in a `<script type='text/discourse-js-error'>` block. Fix what it says and re-save your customization and you'll be good to go.
23+
https://meta.discourse.org/t/theme-developer-tutorial-1-introduction/357796

docs/03-code-internals/13-plugin-outlet-connectors.md

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -62,22 +62,6 @@ If you need custom logic as well, see the other ℹ️ sections below.
6262
[/details]
6363
[/quote]
6464

65-
[quote]
66-
[details=ℹ️ Defining template via theme </head>]
67-
We recommend using a dedicated file for your connector template. However, Discourse does still support defining a template via a `<script>` tag in your theme's `</head>` section. In that case, a definition would look like
68-
69-
```html
70-
<script
71-
type="text/x-handlebars"
72-
data-template-name="/connectors/{outlet-name}/{connector-name}"
73-
>
74-
Template content here
75-
</script>
76-
```
77-
78-
[/details]
79-
[/quote]
80-
8165
# Using outlet arguments
8266

8367
Plugin Outlets provide information about the surrounding context via `@outletArgs`. The arguments passed to each outlet vary. An easy way to view the arguments is to add this to your template:

docs/05-themes-components/02-quick-reference.md

Lines changed: 20 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -70,38 +70,19 @@ javascripts/
7070

7171
### Javascript <small>[read more](https://meta.discourse.org/t/using-the-pluginapi-in-site-customizations/41281)</small>
7272

73-
```html
74-
<!-- Define in </head> section of theme-->
75-
<script type="text/discourse-plugin" version="0.8">
76-
console.log("I am ", api.getCurrentUser().username);
77-
</script>
73+
```gjs
74+
// {theme}/javascripts/api-initializers/init-theme.gjs
75+
import { apiInitializer } from "discourse/lib/api";
76+
77+
export default apiInitializer((api) => {
78+
// Your code here
79+
});
7880
```
7981

8082
[:link: JS Plugin API](https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/lib/plugin-api.gjs)
8183

8284
[:link: Multi-file Javascript](https://meta.discourse.org/t/splitting-up-theme-javascript-into-multiple-files/119369)
8385

84-
### Templates <small>[read more](https://meta.discourse.org/t/adding-plugin-outlets-using-a-theme/32727)</small>
85-
86-
```html
87-
<!-- Define in </head> section of theme-->
88-
89-
<!-- Plugin Outlet -->
90-
<script
91-
type="text/x-handlebars"
92-
data-template-name="/connectors/below-footer/arbitrary-unique-name"
93-
>
94-
This is displayed below the footer
95-
</script>
96-
97-
<!-- Override Core -->
98-
<script type="text/x-handlebars" data-template-name="list/topic-list-item.raw">
99-
This used to be a topic list item
100-
</script>
101-
```
102-
103-
[:link: Locate outlets](https://meta.discourse.org/t/plugin-outlet-locations-theme-component/100673)
104-
10586
### Settings <small>[read more](https://meta.discourse.org/t/how-to-add-settings-to-your-discourse-theme/82557)</small>
10687

10788
`settings.yml`:
@@ -121,10 +102,10 @@ Access from JavaScript:
121102
console.log(settings.fruit);
122103
```
123104

124-
Access from templates:
105+
Access from gjs templates:
125106

126-
```hbs
127-
{{theme-setting "fruit"}}
107+
```gjs
108+
<template>{{settings.fruit}}</template>
128109
```
129110

130111
Access from scss:
@@ -152,12 +133,17 @@ en:
152133
Access from JavaScript:
153134
154135
```js
155-
I18n.t(themePrefix("my_translation_key"));
136+
import { i18n } from "discourse-i18n";
137+
i18n(themePrefix("my_translation_key"));
156138
```
157139

158-
Access from templates:
140+
Access from gjs templates:
141+
142+
```gjs
143+
import { i18n } from "discourse-i18n";
159144
160-
```hbs
161-
{{theme-i18n "my_translation_key"}}
162-
{{d-button label=(theme-prefix "my_translation_key")}}
145+
<template>
146+
{{i18n (themePrefix "my_translation_key")}}
147+
<DButton @label={{theme-prefix "my_translation_key"}} />
148+
</template>
163149
```

docs/05-themes-components/07-multiple-js-files.md

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,52 +6,40 @@ id: multiple-js-files
66

77
Complex theme javascript can be split into multiple files, to keep things nicely organised.
88

9-
To use this functionality, simply add files to the `/javascripts` folder in your theme directory. Currently, these files can not be edited from the Discourse UI, so you must use the [Theme CLI](https://meta.discourse.org/t/discourse-theme-cli-console-app-to-help-you-build-themes/82950) or [source the theme from git](https://meta.discourse.org/t/how-to-source-a-theme-from-a-private-git-repository/82584).
9+
To use this functionality, simply add files to the `/javascripts` folder in your theme directory. These files can not be edited from the Discourse UI, so you must use the [Theme CLI](https://meta.discourse.org/t/discourse-theme-cli-console-app-to-help-you-build-themes/82950) or [source the theme from git](https://meta.discourse.org/t/how-to-source-a-theme-from-a-private-git-repository/82584).
1010

1111
Javascript files are treated exactly the same as they are in core/plugins, so you should follow the same file/folder structure. Theme files are loaded after core/plugins, so if the filenames match, the theme version will take precedence.
1212

1313
---
1414

1515
As an example, you can now accomplish https://meta.discourse.org/t/adding-to-plugin-outlets-using-a-theme/32727 by adding a single file to your theme:
1616

17-
**`/javascripts/my-theme/connectors/discovery-list-container-top/add-header-message.hbs`**
17+
**`/javascripts/my-theme/connectors/discovery-list-container-top/add-header-message.gjs`**
1818

1919
```hbs
20-
Welcome
21-
{{currentUser.username}}. Please visit
22-
<a class="nav-link" href="http://google.com" target="_blank">My Site</a>
23-
```
24-
25-
To add a connector class, add another file
26-
27-
**`/javascripts/my-theme/connectors/discovery-list-container-top/add-header-message.js`**
28-
29-
```js
30-
import { isAppleDevice } from "discourse/lib/utilities";
31-
32-
export default {
33-
shouldRender(args, component) {
34-
return isAppleDevice();
35-
},
36-
};
20+
import Component from "@glimmer/component"; import { service } from
21+
"@ember/service"; export default class HeaderMessage extends Component {
22+
@service currentUser;
23+
24+
<template>
25+
Welcome
26+
{{this.currentUser.username}}
27+
</template>
28+
}
3729
```
3830

3931
---
4032

41-
If you want to simply move some existing theme javascript out of a `<script type="text/discourse-plugin"` block, you should wrap it in an initializer like this:
33+
To use the JS API, create an initializer:
4234

43-
**`/javascripts/my-theme/initializers/initialize-stuff.js`**
35+
**`/javascripts/discourse/api-initializers/init-theme.gjs`**
4436

4537
```js
46-
import { withPluginApi } from "discourse/lib/plugin-api";
47-
export default {
48-
name: "my-initializer",
49-
initialize() {
50-
withPluginApi("0.8.7", (api) => {
51-
// Do something with the API here
52-
});
53-
},
54-
};
38+
import { apiInitializer } from "discourse/lib/api";
39+
40+
export default apiInitializer((api) => {
41+
// Your code here
42+
});
5543
```
5644

5745
---

docs/05-themes-components/09-theme-settings.md

Lines changed: 10 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -205,14 +205,19 @@ If your user tries to enter a value that's not within the allowed range, they'll
205205

206206
<h3 id='heading--settings-js-css'>Access to settings in your JS/CSS/Handlebars</h3>
207207

208-
To have access to setting in your theme JS code, the `script` tag that wraps your code must have a `type="text/discourse-plugin"` attribute as well as `version` specified like so:
208+
Theme settings are made available globally as a `settings` variable in theme JavaScript files. For example:
209209

210-
```hbs
211-
<script type="text/discourse-plugin" version="0.8.13">
212-
alert(settings.integer_setting + 1); console.log(settings.string_setting);
213-
</script>
210+
```gjs
211+
// {theme}/javascripts/discourse/api-initializers/init-theme.gjs
212+
import { apiInitializer } from "discourse/lib/api";
213+
214+
export default apiInitializer((api) => {
215+
console.log("settings are", settings);
216+
});
214217
```
215218

219+
This `settings` object is also usable as normal within `.gjs` `<template>` tags.
220+
216221
In CSS, you'll get a variable created for every setting of your theme and each variable will have the same name as the setting it represents.
217222

218223
So if you had a float setting called `global_font_size` and a string setting called `site_background`, you could do something like this in your theme CSS:
@@ -224,137 +229,6 @@ html {
224229
}
225230
```
226231

227-
Similarly, theme settings are available in handlebars templates that you define in your theme whether you're overriding a core template, or creating your own. For example if you have something like this in your theme:
228-
229-
```hbs
230-
<script type="text/x-handlebars" data-template-name="my-template">
231-
<h1>{{theme-setting "your_setting_key"}}</h1>
232-
</script>
233-
```
234-
235-
It'll render with your setting value.
236-
237-
You may want to use a boolean setting as a condition for an `{{#if}}` block in your template, this is how you can do that:
238-
239-
```hbs
240-
<script type="text/x-handlebars" data-template-name="my-template">
241-
{{#if (theme-setting "my_boolean_setting")}}
242-
<h1>Value is true!</h1>
243-
{{else}}
244-
<h1>Value is false!</h1>
245-
{{/if}}
246-
</script>
247-
```
248-
249-
---
250-
251-
If you have a question about this or there is something unclear, feel free to ask - I'll try to answer/clarify as much as I can. Also this is a wiki post, so contributions to improve this are greatly appreciated! :sunflower:
252-
253-
---
254-
255-
## :question: Frequently Asked Questions
256-
257-
[quote="p0fi, post:27, topic:82557"]
258-
Can I combine javascript and handlebars in there somehow too? I would like to get the current year using javascript to put it in the string too.
259-
[/quote]
260-
261-
Not directly; you’ll need to use the `registerConnectorClass` plugin API to add an attribute that has the current year to the connector instance behind your connector template. See an [example](https://github.com/OsamaSayegh/discourse-tab-bar-theme/blob/c05adce3274ffee821eadac8f81ffb54b85e5045/mobile/head_tag.html#L91) from my theme.
262-
263-
My theme sets the `tabs` attributes which is then referenced in the Handlebars template at the end of the file. You can do something like `this.set("year", compute current year here)` in the `setupComponent` method and then in your template you can access the year value like this `{{year}}`.
264-
265-
[quote="Jay Pfaffman, post:29, topic:82557, full:true, username:pfaffman"]
266-
What if I have a setting like
267-
268-
```yaml
269-
my_text:
270-
type: string
271-
default: "<a href='https://google.com/'>Google!</a>"
272-
```
273-
274-
and then want to do
275-
276-
```hbs
277-
<script type="text/x-handlebars" data-template-name="my-template">
278-
{{theme-setting "my_text"}}
279-
</script>
280-
```
281-
282-
It doesn’t give me my link but instead displays all the HTML. Is there a way to fix that?
283-
[/quote]
284-
285-
You can use the Ember's `html-safe` helper here and it will render the HTML instead of the text.
286-
287-
```hbs
288-
{{html-safe (theme-setting "my_text")}}
289-
```
290-
291-
[quote="Alex P., post:33, topic:82557, full:true, username:Alex_P"]
292-
[quote]
293-
the `script` tag that wraps your code must have a `type="text/discourse-plugin"` attribute as well as `version` specified like so:
294-
[/quote]
295-
296-
What’s that version value? Is it supposed to be the version of my plugin?
297-
[/quote]
298-
299-
No, that’s the version of our [Plugin API](https://github.com/discourse/discourse/blob/e6b5b6eae348aa0f6148589a07e9ade0f08bae59/app/assets/javascripts/discourse/app/lib/plugin-api.js#L112)
300-
301-
We bump that every time a new method is added to the API so that themes / plugins which relay on methods that were recently added to the plugin API don’t end up breaking sites which haven’t been updated.
302-
303-
You don’t really need to worry about this a lot because:
304-
305-
1. We don’t add new methods very often
306-
2. Most sites that use Discourse are updated very frequently.
307-
308-
[quote="Marcus Baw, post:44, topic:82557, username:pacharanero"]
309-
Is it possible to access the theme settings from within the Ruby code as opposed to the JS?
310-
[/quote]
311-
312-
To access theme settings in Ruby you need to call the `settings` method on a theme like so: `Theme.find(<id>).settings`. It will return an array which contains a [`ThemeSettingsManager`](https://github.com/discourse/discourse/blob/66151d805609839a333500248149da4cc5e9cae3/lib/theme_settings_manager.rb#L1) instance for each setting and from it you can get the setting name and value by calling the `name` and `value` methods respectively.
313-
314-
[quote="Heddson, post:47, topic:82557"]
315-
Could things break if I don’t prefix my setting names with something like my theme name?
316-
[/quote]
317-
318-
In JavaScript and hbs templates, there is no way this can happen. The `settings` variable that you use to access your theme settings is local to your theme and only contains your theme settings. In hbs templates, the settings of each theme are namespaced with their theme’s primary key in the database, so conflicts are impossible.
319-
320-
[quote="Manuel, post:50, topic:82557, full:true, username:nolo"]
321-
When I use a list in settings, I get the values as:
322-
323-
```
324-
value1,value2,value3|value1,value2,value3
325-
```
326-
327-
Is it possible to modify this output by declaring a different list_type? I’d like to use values from a list in Scss, but I think I could only de-structure the list if the output is formatted as
328-
329-
```
330-
value1 value2 value3,
331-
value1 value2 value3;
332-
```
333-
334-
[/quote]
335-
336-
You can create custom SCSS functions to transform settings values into whatever format you want. E.g., in your case I think all you need is a string replace function that replaces commas with whitespace and pipes with commas? Here is an implementation of a string replace function in SCSS: [Str-replace Function | CSS-Tricks](https://css-tricks.com/snippets/sass/str-replace-function/)
337-
338-
[quote="Jonathan Shaw, post:60, topic:82557, full:true, username:JonathanShaw"]
339-
Is it possible to access settings from another theme or component. e.g. if you have the category icons theme component installed, can you access information about the icons defined in its settings in a different custom theme component?
340-
[/quote]
341-
342-
There is not a supported way to access the settings of another theme/component.
343-
344-
[quote="Alex, post:62, topic:82557, username:daemon"]
345-
Is it possible to divide the settings into sections, e.g. with horizontal lines in between?
346-
347-
And is it possible to integrate some kind of heading?
348-
[/quote]
349-
350-
No, neither of those things are possible at the moment.
351-
352-
---
353-
354232
## :link: Related Topics
355233

356234
- https://meta.discourse.org/t/developer-s-guide-to-discourse-themes/93648
357-
358-
---
359-
360-
_Last Reviewed by @keegan on [date=2022-10-06 timezone="America/Vancouver"]_

0 commit comments

Comments
 (0)