Using the PluginAPI in Site Customizations

plugins
api
themes

(Robin Ward) #1

Using the client side Plugin API is the safest way to build Discourse Javascript plugins and themes while respecting backwards compatibility.

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.

In the latest tests-passed build of Discourse I’ve added the ability to use the pluginAPI via a site customation.

To use it, you just need to add some attributes to a script tag in your </HEAD> customization:

<script type="text/discourse-plugin" version="0.1">
  // you can use the `api` object here!
  api.decorateCooked($elem => $elem.css({ backgroundColor: 'yellow' }));
</script>

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.

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.


Adding text below avatar or next to username
Link custom user field to external website
Custom User fields on Usercard
How to increase Discourse avatar resolution with JS?
Customization always loads PluginAPI version 0.1
GoSquared People integration - how to get user name and email in Javascript at <head> customization?
Change link on logo in header?
How to develop custom themes
How to redirect /categories to /latest
How do you force a script to refire on every page load in Discourse?
Using the 'Discourse' javascript class in a hosted forum
How to modify the header HTML, but still remaining the default founctions
(Dean Taylor) #2

What’s the best way to “import” as a Site Customisation? - is it just to use require?

Whilst this works:

<script type="text/discourse-plugin" version="0.1">
var HamburgerMenuComponent = require('discourse/components/hamburger-menu').default;
</script>

This does not:

<script type="text/discourse-plugin" version="0.1">
import {default as HamburgerMenuComponent2 } from 'discourse/components/hamburger-menu';

</script>

Where I get this error:

<script type="text/discourse-js-error">unknown: 'import' and 'export' may only appear at the top level (3:0)
  1 | Discourse._registerPluginCode('0.1', api => {
  2 |   
> 3 | import {default as HamburgerMenuComponent2 } from 'discourse/components/hamburger-menu';
    | ^
  4 | 
  5 | 
  6 | }); at <eval>:8695:14</script>

(Robin Ward) #3

Instead of importing it, you can look it up with

api.container.lookupFactory('component:hamburger-menu')

(Emma Lejeck) #4

Is there a way to get access to the h helper from virtual-dom in a site customization? I’m trying to add a simple dropdown widget to use in our header, and I can’t get that darned h, even though I can get createWidget


(Sam) #5

Does:

h = require('virtual-dom').h;

not work?


(Emma Lejeck) #6

Yes it does! Works perfectly, thanks a ton!


(Robin Ward) #7

I wouldn’t recommend doing it that way as it would likely break future compatibility. You can use this for now but I’ll try to introduce a workaround shortly that will be safer long term.


(Emma Lejeck) #8

Makes sense, it’s kinda circumventing the whole Plugin API thing and relying on implementation details of the ES6 compilation output, both things that are Dangerous™

Anyways, I’ll definitely keep an eye out for a better solution


(Robin Ward) #9

I’ll try to get to it soon and I’ll reply in this topic, so watch it and you’ll see :slight_smile:


(Robin Ward) #10

Actually thinking about it, the decorateWidget helper gets called with an object that has the h method. How are you inserting your widget if not via a decorator?

If you could post code that would be helpful.


(Emma Lejeck) #11

{{mount-widget}} in a template for a plugin outlet.


(Robin Ward) #12

Ah that’s clever – I didn’t think people would try that. Okay, let me try something out.


(Robin Ward) #13

Okay, I’ve added h to the pluginApi object as long as you request plugin api v0.3:

<script type="text/discourse-plugin" version="0.3">
  console.log(api.h('b', ['hello', 'world']));
</script>

That should work for you!