Brand new plugin interface


(Sam Saffron) #1

I have been working on a heavily simplified plugin interface so its a lot easier for people to get started with extending Discourse.

For example:

Say you wanted to extend Discourse and add Ubuntu SSO to the dialog.

Place a file called plugin.rb in the directory plugins/ubuntu_sso

###plugins/ubuntu_sso/plugin.rb

# name: discourse-ubuntu
# about: ubuntu login support for Discourse
# version: 0.1
# authors: Sam Saffron, Marco Ceppi

auth_provider :title => 'with Ubuntu',
              :authenticator => Auth::OpenIdAuthenticator.new('ubuntu','https://login.ubuntu.com', trusted: true),
              :message => 'Authenticating with Ubuntu (make sure pop up blockers are not enbaled)',
              :frame_width => 1000,   # the frame size used for the pop up window, overrides default
              :frame_height => 800

register_css <<CSS

.btn-social.ubuntu {
  background: #dd4814;
}

.btn-social.ubuntu:before {
  font-family: Ubuntu;
  content: "U";
}

CSS

Run bundle exec rake assets:precompile
Restart you services.

Done, you have working Ubuntu SSO.

This interface is still not complete, as I finalize it I will be adding more examples here.

The /plugins directory is ignored by git so you can place your extensions there safely.

###Longer term plans

  1. Possibly use a Rails::Engine for greater flexibility
  2. More hooks
  3. Admin UI to enable or disable plugins (with multisite support)
  4. Central plugin repo
  5. External gem dependencies

If you are interested in extending the interface etc, let me know.

cc @marcoceppi


Admin menu feature: view installed plugins
Discourse plugin for static site generators like Jekyll or Octopress
Single sign-on with self-hosted Wordpress install
YtLight: a lightweight YouTube Oneboxer
Global Gmail style keyboard shortcuts
Login with Vk.com Open API
Syntax highlighting of code blocks
Tmp/cache needs to be manually cleared when developing plugins
What's the current state of LDAP support in Discourse?
Has anyone built something for informal status updates - Chat?
Adding additional Omniauth strategies via plugin?
Adding additional Omniauth strategies via plugin?
Clicking arrows in progress bar not working right
Comrades let's join our efforts on ukrainian and russian translations
(Sam Saffron) #2

The next plugin I wrote demonstrates some other features:

###plugins/all_caps/plugin.rb

# name: ALL_CAPS
# about: ALL TEXT SHOULD ALWAYS BE CAPS AS GOD INTENDED
# version: 0.1
# authors: Sam Saffron

# uses server side to ensure markdown pipeline on the server knows about it
register_asset "javascripts/all_caps.js", :server_side

###plugins/all_caps/assets/javascripts/all_caps.js

Discourse.Dialect.postProcessText(function (text) {
  return text.toUpperCase();
});

Screenshot:

I CALL THIS PROGRESS


Plugin Tutorial #1 - How to manipulate the text in the composer?
What is the most awesome plugin for Discourse, that does not yet exist?
Converting keywords in posts on the fly
Auto convert keywords to hyperlinks
(zspencer) #3

Is there a recommended way to manage your plugin installs? Or should I just copy folders into the plugins directory directly on release?


Discourse + HipChat
(Robin Ward) #4

It definitely is. Before we tell people to use it we should provide a cleaner version. I’ve added it to my list.

I know it’s early yet but this looks very good. Might I suggest putting the meta data in a ruby structure rather than comments? I think the last thing we’ll want to do is parse those out.


(Sam Saffron) #5

There is a very strong reason I kept metadata in Ruby comments as opposed to the file.

I would like to parse out the metadata prior to enabling a plugin. That way you can decide which plugins are on or off in without needing to load the file in Ruby which could impose a security risk.


(Sam Saffron) #6

I think a very clean way is the way rbenv does it.

Keep your plugins in github, clone the project into a plugin dir.


(zspencer) #7

Would it make sense for plugins to include a manifest file, similar to package.json or a gemspec? The comments metadata seems ‘Good enough for now.’


(Sam Saffron) #8

possibly, but I am of the philosophy that “mo files mo problems”, trying to go for super simple here.


(Jeff Atwood) #9


(Adam Baxter) #10

This seems strange to me, does that mean you’re always following git HEAD? What about doing it the same way as git submodules?


(Sam Saffron) #11

I would say that at the moment it is all cutting edge, you probably do want to be on github latest for your plugins (note none exist at the moment so this is a bit imagineering)

Longer term I want us to maintain a repo just like WordPress do.


(Sam Saffron) #12

###Onebox whitelist extension

###plugins/onebox_stuff/plugin.rb

# name: onebox_stuff
# about: show off onebox customisation
# version: 0.1
# authors: Sam Saffron

require 'oneboxer'

Oneboxer::Whitelist.entries << Oneboxer::Whitelist::Entry.new(/^https?:\/\/(?:www\.)?markshuttleworth\.com\/.+/)

Result:


(George Kaplan) #13

@sam, this is fantastic.

I want to add one button to the editor for input language selection. Any tips where to start?


(Brentley Jones) #14

Would there be a way to change which nodes are looked at for a onebox? For example, I would like to pull the first video/screenshot from store.steampowered.com like google does for it’s search results (similar to how the github onebox pulls the avatar and the wiki one pulls the first image).


(Marco Ceppi) #15

We did something similar by creating a custom onebox module for Launchpad code. I’d love to see how @sam works custom oneboxes in to the plugin infrastructure.


(Robin Ward) #16

In that case, how about a structured format within the comment, like JSON? It’s easier to parse, and is more extensible. For example how would you handle multiple authors?

I was thinking something like this:

=begin
{ 
  name: "ALL_CAPS",
  about: "ALL TEXT SHOULD ALWAYS BE CAPS AS GOD INTENDED",
  version: 0.1,
  authors: ['Sam Saffron', 'Upper McCasey']
}  
=end

register_asset "javascripts/all_caps.js", :server_side  

(rands) #17

This is my favorite plug-in ever.


(Brentley Jones) #18

Fix it for you :smile:


(NM Discourse) #19

I’d like to do some custom processing after my custom OpenID plugin finishes processing (e.g., fetch some other data from external, automatically create the relevant discourse User without asking for email and username (I’d populate that automatically)).

I previously tried to do it hacking the omniauth_callbacks_controller.rb - I wonder if there is a better more proper way with the plugins mechanism?


(adam8797) #20

@sam I tried your auth plugin (with a different service) but when I run the bundle command it complains about the CSS.

It says

(eval):20: can't find string "CSS" anywhere before EOF
(eval):9: syntax error, unexpected end-of-input, expecting tSTRING_CONTENT or tSTRING_DBEG or tSTRING_DVAR or tSTRING_END

and my plugin:

auth_provider :open_id,  
    :name => 'IYA',
    :title => 'with IYA',
    :identifier => 'https://www.google.com/accounts/o8/site-xrds?hd=internationalyouthcongress.org',
    :message => 'Authenticating with IYA Google Apps accounts. (Disable popup blockers)',
    :frame_width => 1000,   
    :frame_height => 800

register_css <<CSS

.btn-social.ubuntu {
  background: #dd4814;
}

.btn-social.ubuntu:before {
  font-family: Ubuntu;
  content: "U";
}

CSS

any ideas? I left the css the same after it failed the first time.

and then on top of that (which it may be the google apps provider) it throws me into a redirect loop.