mirror of
https://github.com/codeninjasllc/discourse.git
synced 2025-05-02 08:54:02 -04:00
working plugin interface for custom openid auth, custom css and custom js
This commit is contained in:
parent
61b330abb4
commit
160107a712
12 changed files with 422 additions and 95 deletions
app
assets/javascripts/discourse
controllers/users
config
lib
spec
|
@ -16,19 +16,12 @@ Discourse.LoginController = Discourse.Controller.extend(Discourse.ModalFunctiona
|
||||||
return Discourse.Site.instance();
|
return Discourse.Site.instance();
|
||||||
}.property(),
|
}.property(),
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Determines whether at least one login button is enabled
|
Determines whether at least one login button is enabled
|
||||||
**/
|
**/
|
||||||
hasAtLeastOneLoginButton: function() {
|
hasAtLeastOneLoginButton: function() {
|
||||||
return Discourse.SiteSettings.enable_google_logins ||
|
return Em.get("Discourse.LoginMethod.all").length > 0;
|
||||||
Discourse.SiteSettings.enable_facebook_logins ||
|
}.property("Discourse.LoginMethod.all.@each"),
|
||||||
Discourse.SiteSettings.enable_cas_logins ||
|
|
||||||
Discourse.SiteSettings.enable_twitter_logins ||
|
|
||||||
Discourse.SiteSettings.enable_yahoo_logins ||
|
|
||||||
Discourse.SiteSettings.enable_github_logins ||
|
|
||||||
Discourse.SiteSettings.enable_persona_logins;
|
|
||||||
}.property(),
|
|
||||||
|
|
||||||
loginButtonText: function() {
|
loginButtonText: function() {
|
||||||
return this.get('loggingIn') ? I18n.t('login.logging_in') : I18n.t('login.title');
|
return this.get('loggingIn') ? I18n.t('login.logging_in') : I18n.t('login.title');
|
||||||
|
@ -78,54 +71,30 @@ Discourse.LoginController = Discourse.Controller.extend(Discourse.ModalFunctiona
|
||||||
|
|
||||||
authMessage: (function() {
|
authMessage: (function() {
|
||||||
if (this.blank('authenticate')) return "";
|
if (this.blank('authenticate')) return "";
|
||||||
return I18n.t("login." + (this.get('authenticate')) + ".message");
|
var method = Discourse.get('LoginMethod.all').findProperty("name", this.get("authenticate"));
|
||||||
|
if(method){
|
||||||
|
return method.get('message');
|
||||||
|
}
|
||||||
}).property('authenticate'),
|
}).property('authenticate'),
|
||||||
|
|
||||||
twitterLogin: function() {
|
externalLogin: function(loginMethod){
|
||||||
this.set('authenticate', 'twitter');
|
var name = loginMethod.get("name");
|
||||||
var left = this.get('lastX') - 400;
|
var customLogin = loginMethod.get("customLogin");
|
||||||
var top = this.get('lastY') - 200;
|
|
||||||
return window.open(Discourse.getURL("/auth/twitter"), "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top);
|
|
||||||
},
|
|
||||||
|
|
||||||
facebookLogin: function() {
|
if(customLogin){
|
||||||
this.set('authenticate', 'facebook');
|
customLogin();
|
||||||
var left = this.get('lastX') - 400;
|
|
||||||
var top = this.get('lastY') - 200;
|
|
||||||
return window.open(Discourse.getURL("/auth/facebook"), "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top);
|
|
||||||
},
|
|
||||||
|
|
||||||
casLogin: function() {
|
|
||||||
var left, top;
|
|
||||||
this.set('authenticate', 'cas');
|
|
||||||
left = this.get('lastX') - 400;
|
|
||||||
top = this.get('lastY') - 200;
|
|
||||||
return window.open("/auth/cas", "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top);
|
|
||||||
},
|
|
||||||
|
|
||||||
openidLogin: function(provider) {
|
|
||||||
var left = this.get('lastX') - 400;
|
|
||||||
var top = this.get('lastY') - 200;
|
|
||||||
if (provider === "yahoo") {
|
|
||||||
this.set("authenticate", 'yahoo');
|
|
||||||
return window.open(Discourse.getURL("/auth/yahoo"), "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top);
|
|
||||||
} else {
|
} else {
|
||||||
window.open(Discourse.getURL("/auth/google"), "_blank", "menubar=no,status=no,height=500,width=850,left=" + left + ",top=" + top);
|
this.set('authenticate', name);
|
||||||
return this.set("authenticate", 'google');
|
var left = this.get('lastX') - 400;
|
||||||
|
var top = this.get('lastY') - 200;
|
||||||
|
|
||||||
|
var height = loginMethod.get("frameHeight") || 400;
|
||||||
|
var width = loginMethod.get("frameWidth") || 800;
|
||||||
|
window.open(Discourse.getURL("/auth/" + name), "_blank",
|
||||||
|
"menubar=no,status=no,height=" + height + ",width=" + width + ",left=" + left + ",top=" + top);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
githubLogin: function() {
|
|
||||||
this.set('authenticate', 'github');
|
|
||||||
var left = this.get('lastX') - 400;
|
|
||||||
var top = this.get('lastY') - 200;
|
|
||||||
return window.open(Discourse.getURL("/auth/github"), "_blank", "menubar=no,status=no,height=400,width=800,left=" + left + ",top=" + top);
|
|
||||||
},
|
|
||||||
|
|
||||||
personaLogin: function() {
|
|
||||||
navigator.id.request();
|
|
||||||
},
|
|
||||||
|
|
||||||
authenticationComplete: function(options) {
|
authenticationComplete: function(options) {
|
||||||
if (options.awaiting_approval) {
|
if (options.awaiting_approval) {
|
||||||
this.flash(I18n.t('login.awaiting_approval'), 'success');
|
this.flash(I18n.t('login.awaiting_approval'), 'success');
|
||||||
|
|
69
app/assets/javascripts/discourse/models/login_method.js
Normal file
69
app/assets/javascripts/discourse/models/login_method.js
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
Discourse.LoginMethod = Ember.Object.extend({
|
||||||
|
title: function(){
|
||||||
|
return this.get("titleOverride") || I18n.t("login." + this.get("name") + ".title");
|
||||||
|
}.property(),
|
||||||
|
|
||||||
|
message: function(){
|
||||||
|
return this.get("messageOverride") || I18n.t("login." + this.get("name") + ".message");
|
||||||
|
}.property()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Note, you can add login methods by adding to the list
|
||||||
|
// just Em.get("Discourse.LoginMethod.all") and then
|
||||||
|
// pushObject for any new methods
|
||||||
|
Discourse.LoginMethod.reopenClass({
|
||||||
|
register: function(method){
|
||||||
|
if(this.methods){
|
||||||
|
this.methods.pushObject(method);
|
||||||
|
} else {
|
||||||
|
this.preRegister = this.preRegister || [];
|
||||||
|
this.preRegister.push(method);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
all: function(){
|
||||||
|
if (this.methods) { return this.methods; }
|
||||||
|
|
||||||
|
var methods = this.methods = Em.A();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* enable_google_logins etc.
|
||||||
|
* */
|
||||||
|
|
||||||
|
[ "google",
|
||||||
|
"facebook",
|
||||||
|
"cas",
|
||||||
|
"twitter",
|
||||||
|
"yahoo",
|
||||||
|
"github",
|
||||||
|
"persona"
|
||||||
|
].forEach(function(name){
|
||||||
|
if(Discourse.SiteSettings["enable_" + name + "_logins"]){
|
||||||
|
|
||||||
|
var params = {name: name};
|
||||||
|
|
||||||
|
if(name === "persona") {
|
||||||
|
params.customLogin = function(){
|
||||||
|
navigator.id.request();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(name === "google") {
|
||||||
|
params.frameWidth = 850;
|
||||||
|
params.frameHeight = 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
methods.pushObject(Discourse.LoginMethod.create(params));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.preRegister){
|
||||||
|
this.preRegister.forEach(function(method){
|
||||||
|
methods.pushObject(method);
|
||||||
|
});
|
||||||
|
delete this.preRegister;
|
||||||
|
}
|
||||||
|
return methods;
|
||||||
|
}.property()
|
||||||
|
});
|
||||||
|
|
|
@ -1,27 +1,9 @@
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
{{#if hasAtLeastOneLoginButton}}
|
{{#if hasAtLeastOneLoginButton}}
|
||||||
<div id="login-buttons">
|
<div id="login-buttons">
|
||||||
{{#if Discourse.SiteSettings.enable_google_logins}}
|
{{#each Discourse.LoginMethod.all}}
|
||||||
<button class="btn btn-social google" title="{{i18n login.google.title}}" {{action openidLogin "google"}}>{{i18n login.google.title}}</button>
|
<button class="btn btn-social {{unbound name}}" {{action externalLogin this}}>{{title}}</button>
|
||||||
{{/if}}
|
{{/each}}
|
||||||
{{#if Discourse.SiteSettings.enable_facebook_logins}}
|
|
||||||
<button class="btn btn-social facebook" title="{{i18n login.facebook.title}}" {{action "facebookLogin"}}>{{i18n login.facebook.title}}</button>
|
|
||||||
{{/if}}
|
|
||||||
{{#if Discourse.SiteSettings.enable_cas_logins}}
|
|
||||||
<button class="btn btn-social cas" title="{{i18n login.cas.title}}" {{action "casLogin"}}>{{i18n login.cas.title}}</button>
|
|
||||||
{{/if}}
|
|
||||||
{{#if Discourse.SiteSettings.enable_twitter_logins}}
|
|
||||||
<button class="btn btn-social twitter" title="{{i18n login.twitter.title}}" {{action "twitterLogin"}}>{{i18n login.twitter.title}}</button>
|
|
||||||
{{/if}}
|
|
||||||
{{#if Discourse.SiteSettings.enable_yahoo_logins}}
|
|
||||||
<button class="btn btn-social yahoo" title="{{i18n login.yahoo.title}}" {{action openidLogin "yahoo"}}>{{i18n login.yahoo.title}}</button>
|
|
||||||
{{/if}}
|
|
||||||
{{#if Discourse.SiteSettings.enable_github_logins}}
|
|
||||||
<button class="btn btn-social github" title="{{i18n login.github.title}}" {{action "githubLogin"}}>{{i18n login.github.title}}</button>
|
|
||||||
{{/if}}
|
|
||||||
{{#if Discourse.SiteSettings.enable_persona_logins}}
|
|
||||||
<button class="btn btn-social persona" title="{{i18n login.persona.title}}" {{action "personaLogin"}}>{{i18n login.persona.title}}</button>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if Discourse.SiteSettings.enable_local_logins}}
|
{{#if Discourse.SiteSettings.enable_local_logins}}
|
||||||
|
|
|
@ -20,15 +20,28 @@ class Users::OmniauthCallbacksController < ApplicationController
|
||||||
skip_before_filter :verify_authenticity_token, only: :complete
|
skip_before_filter :verify_authenticity_token, only: :complete
|
||||||
|
|
||||||
def complete
|
def complete
|
||||||
# Make sure we support that provider
|
|
||||||
provider = params[:provider]
|
provider = params[:provider]
|
||||||
raise Discourse::InvalidAccess.new unless self.class.types.keys.map(&:to_s).include?(provider)
|
|
||||||
|
|
||||||
# Check if the provider is enabled
|
# If we are a plugin, then try to login with it
|
||||||
raise Discourse::InvalidAccess.new("provider is not enabled") unless SiteSetting.send("enable_#{provider}_logins?")
|
found = false
|
||||||
|
Discourse.auth_providers.each do |p|
|
||||||
|
if p.name == provider && p.type == :open_id
|
||||||
|
create_or_sign_on_user_using_openid request.env["omniauth.auth"]
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Call the appropriate logic
|
unless found
|
||||||
send("create_or_sign_on_user_using_#{provider}", request.env["omniauth.auth"])
|
# Make sure we support that provider
|
||||||
|
raise Discourse::InvalidAccess.new unless self.class.types.keys.map(&:to_s).include?(provider)
|
||||||
|
|
||||||
|
# Check if the provider is enabled
|
||||||
|
raise Discourse::InvalidAccess.new("provider is not enabled") unless SiteSetting.send("enable_#{provider}_logins?")
|
||||||
|
|
||||||
|
# Call the appropriate logic
|
||||||
|
send("create_or_sign_on_user_using_#{provider}", request.env["omniauth.auth"])
|
||||||
|
end
|
||||||
|
|
||||||
@data[:awaiting_approval] = true if invite_only?
|
@data[:awaiting_approval] = true if invite_only?
|
||||||
|
|
||||||
|
|
|
@ -119,6 +119,11 @@ module Discourse
|
||||||
# attr_accessible.
|
# attr_accessible.
|
||||||
config.active_record.whitelist_attributes = false
|
config.active_record.whitelist_attributes = false
|
||||||
|
|
||||||
|
unless Rails.env.test?
|
||||||
|
require 'plugin'
|
||||||
|
Discourse.activate_plugins!
|
||||||
|
end
|
||||||
|
|
||||||
# So open id logs somewhere sane
|
# So open id logs somewhere sane
|
||||||
config.after_initialize do
|
config.after_initialize do
|
||||||
OpenID::Util.logger = Rails.logger
|
OpenID::Util.logger = Rails.logger
|
||||||
|
@ -131,9 +136,6 @@ module Discourse
|
||||||
Clockwork.run
|
Clockwork.run
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,8 +18,18 @@ Rails.application.config.middleware.use OmniAuth::Builder do
|
||||||
:identifier => 'https://me.yahoo.com',
|
:identifier => 'https://me.yahoo.com',
|
||||||
:require => 'omniauth-openid'
|
:require => 'omniauth-openid'
|
||||||
|
|
||||||
# lambda is required for proper multisite support,
|
Discourse.auth_providers.each do |p|
|
||||||
# without it subdomains will not function correctly
|
if p.type == :open_id
|
||||||
|
provider :open_id, {
|
||||||
|
:name => p.name,
|
||||||
|
:store => OpenID::Store::Redis.new($redis),
|
||||||
|
:require => 'omniauth-openid'
|
||||||
|
}.merge(p.options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# lambda is required for proper multisite support,
|
||||||
|
# without it subdomains will not function correctly
|
||||||
provider :facebook,
|
provider :facebook,
|
||||||
:setup => lambda { |env|
|
:setup => lambda { |env|
|
||||||
strategy = env['omniauth.strategy']
|
strategy = env['omniauth.strategy']
|
||||||
|
|
4
lib/auth_provider.rb
Normal file
4
lib/auth_provider.rb
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
class AuthProvider
|
||||||
|
attr_accessor :type, :glyph, :background_color, :name, :title,
|
||||||
|
:message, :frame_width, :frame_height, :options
|
||||||
|
end
|
|
@ -23,6 +23,31 @@ module Discourse
|
||||||
# Cross site request forgery
|
# Cross site request forgery
|
||||||
class CSRF < Exception; end
|
class CSRF < Exception; end
|
||||||
|
|
||||||
|
def self.activate_plugins!
|
||||||
|
@plugins = Plugin.find_all("#{Rails.root}/plugins")
|
||||||
|
@plugins.each do |plugin|
|
||||||
|
plugin.activate!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.plugins
|
||||||
|
@plugins
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.auth_providers
|
||||||
|
providers = nil
|
||||||
|
if plugins
|
||||||
|
plugins.each do |p|
|
||||||
|
next unless p.auth_providers
|
||||||
|
p.auth_providers.each do |prov|
|
||||||
|
providers ||= []
|
||||||
|
providers << prov
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
providers
|
||||||
|
end
|
||||||
|
|
||||||
def self.cache
|
def self.cache
|
||||||
@cache ||= Cache.new
|
@cache ||= Cache.new
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,21 +7,22 @@ class DiscoursePluginRegistry
|
||||||
attr_accessor :javascripts
|
attr_accessor :javascripts
|
||||||
attr_accessor :server_side_javascripts
|
attr_accessor :server_side_javascripts
|
||||||
attr_accessor :stylesheets
|
attr_accessor :stylesheets
|
||||||
|
|
||||||
|
# Default accessor values
|
||||||
|
#
|
||||||
|
def stylesheets
|
||||||
|
@stylesheets ||= Set.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def javascripts
|
||||||
|
@javascripts ||= Set.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def server_side_javascripts
|
||||||
|
@server_side_javascripts ||= Set.new
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Default accessor values
|
|
||||||
#
|
|
||||||
def self.stylesheets
|
|
||||||
@stylesheets ||= Set.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.javascripts
|
|
||||||
@javascripts ||= Set.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.server_side_javascripts
|
|
||||||
@server_side_javascripts ||= Set.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def register_js(filename, options={})
|
def register_js(filename, options={})
|
||||||
# If we have a server side option, add that too.
|
# If we have a server side option, add that too.
|
||||||
|
|
177
lib/plugin.rb
Normal file
177
lib/plugin.rb
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
require_dependency 'auth_provider'
|
||||||
|
require 'digest/sha1'
|
||||||
|
require 'fileutils'
|
||||||
|
|
||||||
|
class Plugin
|
||||||
|
|
||||||
|
METADATA = [:name, :about, :version, :authors]
|
||||||
|
|
||||||
|
attr_accessor :path
|
||||||
|
attr_accessor *METADATA
|
||||||
|
attr_reader :auth_providers
|
||||||
|
attr_reader :assets
|
||||||
|
|
||||||
|
def self.find_all(parent_path)
|
||||||
|
plugins = []
|
||||||
|
Dir["#{parent_path}/**/plugin.rb"].each do |path|
|
||||||
|
plugin = parse(File.read(path))
|
||||||
|
plugin.path = path
|
||||||
|
plugins << plugin
|
||||||
|
end
|
||||||
|
|
||||||
|
plugins
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse(text)
|
||||||
|
plugin = self.new
|
||||||
|
|
||||||
|
text.each_line do |line|
|
||||||
|
break unless plugin.parse_line(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
plugin
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@assets = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_line(line)
|
||||||
|
line = line.strip
|
||||||
|
|
||||||
|
unless line.empty?
|
||||||
|
return false unless line[0] == "#"
|
||||||
|
attribute, *description = line[1..-1].split(":")
|
||||||
|
|
||||||
|
description = description.join(":")
|
||||||
|
attribute = attribute.strip.to_sym
|
||||||
|
|
||||||
|
if METADATA.include?(attribute)
|
||||||
|
self.send("#{attribute}=", description.strip)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
# will make sure all the assets this plugin needs are registered
|
||||||
|
def generate_automatic_assets!
|
||||||
|
paths = []
|
||||||
|
automatic_assets.each do |path, contents|
|
||||||
|
unless File.exists? path
|
||||||
|
ensure_directory path
|
||||||
|
File.open(path,"w") do |f|
|
||||||
|
f.write(contents)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
paths << path
|
||||||
|
end
|
||||||
|
|
||||||
|
delete_extra_automatic_assets(paths)
|
||||||
|
|
||||||
|
paths
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_extra_automatic_assets(good_paths)
|
||||||
|
filenames = good_paths.map{|f| File.basename(f)}
|
||||||
|
# nuke old files
|
||||||
|
Dir.foreach(auto_generated_path) do |p|
|
||||||
|
next if [".", ".."].include?(p)
|
||||||
|
next if filenames.include?(p)
|
||||||
|
File.delete(auto_generated_path + "/#{p}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensure_directory(path)
|
||||||
|
dirname = File.dirname(path)
|
||||||
|
unless File.directory?(dirname)
|
||||||
|
FileUtils.mkdir_p(dirname)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def auto_generated_path
|
||||||
|
File.dirname(path) << "/auto_generated"
|
||||||
|
end
|
||||||
|
|
||||||
|
def register_css(style)
|
||||||
|
@styles ||= []
|
||||||
|
@styles << style
|
||||||
|
end
|
||||||
|
|
||||||
|
def register_javascript(js)
|
||||||
|
@javascripts ||= []
|
||||||
|
@javascripts << js
|
||||||
|
end
|
||||||
|
|
||||||
|
def automatic_assets
|
||||||
|
css = ""
|
||||||
|
js = "(function(){"
|
||||||
|
|
||||||
|
css = @styles.join("\n") if @styles
|
||||||
|
js = @javascripts.join("\n") if @javascripts
|
||||||
|
|
||||||
|
unless auth_providers.blank?
|
||||||
|
auth_providers.each do |auth|
|
||||||
|
overrides = ""
|
||||||
|
overrides = ", titleOverride: '#{auth.title}'" if auth.title
|
||||||
|
overrides << ", messageOverride: '#{auth.message}'" if auth.message
|
||||||
|
overrides << ", frameWidth: '#{auth.frame_width}'" if auth.frame_width
|
||||||
|
overrides << ", frameHeight: '#{auth.frame_height}'" if auth.frame_height
|
||||||
|
|
||||||
|
js << "Discourse.LoginMethod.register(Discourse.LoginMethod.create({name: '#{auth.name}'#{overrides}}));\n"
|
||||||
|
|
||||||
|
if auth.glyph
|
||||||
|
css << ".btn-social.#{auth.name}:before{ content: '#{auth.glyph}'; }\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
if auth.background_color
|
||||||
|
css << ".btn-social.#{auth.name}{ background: #{auth.background_color}; }\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
js << "})();"
|
||||||
|
|
||||||
|
# TODO don't serve blank assets
|
||||||
|
[[css,"css"],[js,"js"]].map do |asset, extension|
|
||||||
|
hash = Digest::SHA1.hexdigest asset
|
||||||
|
["#{auto_generated_path}/plugin_#{hash}.#{extension}", asset]
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# note, we need to be able to parse seperately to activation.
|
||||||
|
# this allows us to present information about a plugin in the UI
|
||||||
|
# prior to activations
|
||||||
|
def activate!
|
||||||
|
self.instance_eval File.read(path)
|
||||||
|
if auto_assets = generate_automatic_assets!
|
||||||
|
assets.concat auto_assets
|
||||||
|
end
|
||||||
|
unless assets.blank?
|
||||||
|
paths = []
|
||||||
|
assets.each do |asset|
|
||||||
|
if asset =~ /\.js$/
|
||||||
|
DiscoursePluginRegistry.javascripts << asset
|
||||||
|
elsif asset =~ /\.css$|\.scss$/
|
||||||
|
DiscoursePluginRegistry.stylesheets << asset
|
||||||
|
end
|
||||||
|
paths << File.dirname(asset)
|
||||||
|
end
|
||||||
|
# TODO possibly amend this to a rails engine
|
||||||
|
Rails.configuration.assets.paths << auto_generated_path
|
||||||
|
Rails.configuration.assets.paths << File.dirname(path) + "/assets"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def auth_provider(type, opts)
|
||||||
|
@auth_providers ||= []
|
||||||
|
provider = AuthProvider.new
|
||||||
|
provider.type = type
|
||||||
|
[:name, :glyph, :background_color, :title, :message, :frame_width, :frame_height].each do |sym|
|
||||||
|
provider.send "#{sym}=", opts.delete(sym)
|
||||||
|
end
|
||||||
|
provider.options = opts
|
||||||
|
@auth_providers << provider
|
||||||
|
end
|
||||||
|
end
|
65
spec/components/plugin_spec.rb
Normal file
65
spec/components/plugin_spec.rb
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
require_dependency 'plugin'
|
||||||
|
|
||||||
|
describe Plugin do
|
||||||
|
context "parse" do
|
||||||
|
it "correctly parses plugin info" do
|
||||||
|
plugin = Plugin.parse <<TEXT
|
||||||
|
# name: plugin-name
|
||||||
|
# about: about: my plugin
|
||||||
|
# version: 0.1
|
||||||
|
# authors: Frank Zappa
|
||||||
|
|
||||||
|
some_ruby
|
||||||
|
TEXT
|
||||||
|
|
||||||
|
plugin.name.should == "plugin-name"
|
||||||
|
plugin.about.should == "about: my plugin"
|
||||||
|
plugin.version.should == "0.1"
|
||||||
|
plugin.authors.should == "Frank Zappa"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "find_all" do
|
||||||
|
it "can find plugins correctly" do
|
||||||
|
plugins = Plugin.find_all("#{Rails.root}/spec/fixtures/plugins")
|
||||||
|
plugins.count.should == 1
|
||||||
|
plugin = plugins[0]
|
||||||
|
|
||||||
|
plugin.name.should == "plugin-name"
|
||||||
|
plugin.path.should == "#{Rails.root}/spec/fixtures/plugins/my_plugin/plugin.rb"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not blow up on missing directory" do
|
||||||
|
plugins = Plugin.find_all("#{Rails.root}/frank_zappa")
|
||||||
|
plugins.count.should == 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "activate!" do
|
||||||
|
it "can activate plugins correctly" do
|
||||||
|
plugin = Plugin.new
|
||||||
|
plugin.path = "#{Rails.root}/spec/fixtures/plugins/my_plugin/plugin.rb"
|
||||||
|
plugin.ensure_directory(plugin.auto_generated_path)
|
||||||
|
|
||||||
|
junk_file = "#{plugin.auto_generated_path}/junk"
|
||||||
|
File.open("#{plugin.auto_generated_path}/junk", "w") {|f| f.write("junk")}
|
||||||
|
plugin.activate!
|
||||||
|
|
||||||
|
plugin.auth_providers.count.should == 1
|
||||||
|
auth_provider = plugin.auth_providers[0]
|
||||||
|
auth_provider.options.should == {:identifier => 'https://zappa.com'}
|
||||||
|
auth_provider.type.should == :open_id
|
||||||
|
|
||||||
|
# calls ensure_assets! make sure they are there
|
||||||
|
plugin.assets.count.should == 2
|
||||||
|
plugin.assets.each do |a|
|
||||||
|
File.exists?(a).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
# ensure it cleans up all crap in autogenerated directory
|
||||||
|
File.exists?(junk_file).should be_false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
10
spec/fixtures/plugins/my_plugin/plugin.rb
vendored
Normal file
10
spec/fixtures/plugins/my_plugin/plugin.rb
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# name: plugin-name
|
||||||
|
# about: about: my plugin
|
||||||
|
# version: 0.1
|
||||||
|
# authors: Frank Zappa
|
||||||
|
|
||||||
|
auth_provider :open_id,
|
||||||
|
:name => 'zappa',
|
||||||
|
:identifier => 'https://zappa.com',
|
||||||
|
:background_color => '#dd4814',
|
||||||
|
:glyph => 'B'
|
Loading…
Add table
Add a link
Reference in a new issue