Plugin Development: Beyond the Basics?


(Neil) #1

Apologies if this is in the wrong section. I couldn’t start a new topic in Plugins or Howto.

I’m currently trying my hand at a new (simple) plugin.
A folder of images will be ‘globbed’ at server startup and an array of filenames will be generated.
The user will then be able to see this list via a combo-box in preferences and then select whichever one they like. the resulting image will then be displayed on the user-card/profile.

That’s it! :wink:

However, I’m really stumped. passing the array from the server to the client is where I’m getting stuck. There seems to be a rather large gulf between @eviltrout 's great beginners guides and tackling Ember proper.

I’m pretty sure I need to read the folder contents in plugin.rb and init the array. do I pass the variable with an initializer? Do I need a controller? should I reopen the Preferences Controller and add the variable there? I’ve downloaded a few Discourse plugins to see how other people do it, but I feel that they’re probably way more advanced than what I need to do.

Can anyone give me any pointers or a good general Ember guide?
Cheers!


(James Kiesel) #2

If you haven’t found the Ember docs yet, they’re probably the best place to start if the question is ‘WTF are you, Ember?’

A rough outline of how you could tackle this plugin:

  • Write a serializer which will output the data you need, given a file:
class FileSerializer < ActiveModel::Serializer
  attributes :filename, :href
end
  • Write a rails controller method which use that serializer to return the files in a json format:
class Globber::FilesController < ApplicationController
  def index
    @files = Dir.glob("files/**.*.rb")
    render json: ActiveModel::ArraySerializer.new(@files, each_serializer: FileSerializer)
  end
end
  • Expose an endpoint so that Ember can access the controller method:
Discourse::Application.routes.append do
  get '/globber/files' => 'globber/files#index'
end
  • On the ember side, make an ajax call which hits that endpoint and returns the json data you’re looking for:
import { ajax } from 'discourse/lib/ajax'
ajax('/globber/files').then((data) => { this.set('files', data) })
# ^^ 'files' is now a property that's accessible in the view
  • Then, in the ember template of the javascript controller, you can iterate through the files and display them seasoned to taste:
{{#each file as |files|}}
  <a href={{file.href}}>{{file.name}}</a>
{{/each}}

(NB: all code in this post is untested and subject to crappiness)

The only way I ever learned anything about writing plugins for Discourse was reading other people’s code (well, and asking questions on here ;)), so likely that’s the best way to go if you’re looking to expand your knowledge further.


(Neil) #3

This is great. Just what I needed. Thanks so much for taking the time out to explain.

I’ve managed to glob the files from the plugin subdirectory and serve the list as JSON accessible from the browser. I’d like the AJAX call to fire when the user opens their preferences. There’s a combo-box ready and waiting but I still don’t get how the app knows when to trigger the call. Do I need to extend the ‘RestrictedUserRoute’ in the controller? how does the app know that this particular controller is linked to that plugin outlet?


(James Kiesel) #4

This might be a bit hand-wavey, but you could probably tap into the Ember component lifecycle here to perform requests once the page has loaded its components.

Something like

# discourse/components/some-component.js.es6
export default Ember.Component.extend({
  didInsertElement() {
    ajax('/globber/files').then((response) => {
      this.set('files', response.files)
    }
  }
}

(Neil) #5

Thanks a lot James. All of your advice is great.

I understand the system a bit more and I think I should generate the file array at application start and place it in some global application store. It’s ‘static’ enough that It shouldn’t have to be refreshed until the application restarts again.

That way, it’s just a list available via user preferences. The custom field is then turned into a URL to display the image everywhere.

Armed with the Ember docs link you provided, I should be able to figure the rest out myself. Thanks again and all the best!


(Erlend Sogge Heggen) #6

p.s. have you already read @angus’ excellent general beginner’s guide?

Always a worthwhile read for new developers who are unfamiliar with most of our stack.