diff --git a/.gitignore b/.gitignore index edaed0ceb..a4b586cd6 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ .DS_Store .vagrant + +/plugins/* diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js.erb similarity index 97% rename from app/assets/javascripts/application.js rename to app/assets/javascripts/application.js.erb index c9f57b7f9..e9a58fa5d 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js.erb @@ -72,3 +72,7 @@ //= require_tree ./services //= require_tree ./directives //= require_tree ./filters + +<% +PluginRegistry.javascripts.each { |js| require_asset(js) } +%> diff --git a/app/assets/javascripts/controllers/main_nav.coffee.erb b/app/assets/javascripts/controllers/main_nav.coffee.erb index 96b1c5775..8b9a49217 100644 --- a/app/assets/javascripts/controllers/main_nav.coffee.erb +++ b/app/assets/javascripts/controllers/main_nav.coffee.erb @@ -44,7 +44,8 @@ Application.Controllers.controller "MainNavController", ["$scope", "$location", }) - $scope.adminNavLinks = [ + Fablab.adminNavLinks = Fablab.adminNavLinks || [] + Fablab.adminNavLinks = [ { state: 'app.admin.trainings' linkText: 'trainings_monitoring' @@ -95,5 +96,7 @@ Application.Controllers.controller "MainNavController", ["$scope", "$location", linkText: 'customization' linkIcon: 'gear' } - ] + ].concat(Fablab.adminNavLinks) + + $scope.adminNavLinks = Fablab.adminNavLinks ] diff --git a/app/assets/javascripts/controllers/members.coffee b/app/assets/javascripts/controllers/members.coffee index 6261c15a1..440cf5e43 100644 --- a/app/assets/javascripts/controllers/members.coffee +++ b/app/assets/javascripts/controllers/members.coffee @@ -261,7 +261,7 @@ Application.Controllers.controller "EditProfileController", ["$scope", "$rootSco Application.Controllers.controller "ShowProfileController", ["$scope", 'memberPromise', 'SocialNetworks', ($scope, memberPromise, SocialNetworks) -> ## Selected user's informations - $scope.user = memberPromise + $scope.user = memberPromise # DEPENDENCY WITH NAVINUM GAMIFICATION PLUGIN !!!! ## List of social networks associated with this user and toggle 'show all' state $scope.social = diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss.erb similarity index 86% rename from app/assets/stylesheets/application.scss rename to app/assets/stylesheets/application.scss.erb index bb4619d2b..2ae151239 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss.erb @@ -34,3 +34,8 @@ @import "modules/invoice"; @import "app.responsive"; + +<% PluginRegistry.stylesheets.each do |stylesheet| %> + <% basename = File.basename(stylesheet,'.scss') %> + <%= "@import '#{basename}';" %> +<% end %> diff --git a/app/assets/templates/shared/publicProfile.html.erb b/app/assets/templates/shared/publicProfile.html.erb index 28a8b938a..51773df6a 100644 --- a/app/assets/templates/shared/publicProfile.html.erb +++ b/app/assets/templates/shared/publicProfile.html.erb @@ -37,6 +37,7 @@
+ <%= PluginRegistry.insert_code('html.user.profile') %>
@@ -126,4 +127,4 @@ - \ No newline at end of file + diff --git a/app/views/api/members/show.json.jbuilder b/app/views/api/members/show.json.jbuilder index 5c23588dd..bdc715b02 100644 --- a/app/views/api/members/show.json.jbuilder +++ b/app/views/api/members/show.json.jbuilder @@ -1,4 +1,4 @@ -json.extract! @member, :id, :username, :email, :group_id, :slug, :invoicing_disabled, :is_allow_contact +json.extract! @member, :id, :uid, :username, :email, :group_id, :slug, :invoicing_disabled, :is_allow_contact json.role @member.roles.first.name json.name @member.profile.full_name json.need_completion @member.need_completion? diff --git a/config/application.rb b/config/application.rb index a48a2d29e..e18661a56 100644 --- a/config/application.rb +++ b/config/application.rb @@ -13,12 +13,14 @@ require "rails/all" require 'elasticsearch/rails/instrumentation' require 'elasticsearch/persistence/model' + # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) module Fablab class Application < Rails::Application + require 'fab_manager' # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. @@ -62,5 +64,20 @@ module Fablab config.web_console.whitelisted_ips << '10.0.2.2' #vagrant end + # enable the app to find locales in plugins locales directory + config.i18n.load_path += Dir["#{Rails.root}/plugins/*/config/locales/*.yml"] + + # enable the app to find views in plugins views directory + Dir["#{Rails.root}/plugins/*/views"].each do |path| + Rails.application.config.paths['app/views'] << path + end + + FabManager.activate_plugins! + + config.after_initialize do + if plugins = FabManager.plugins + plugins.each { |plugin| plugin.notify_after_initialize } + end + end end end diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index f27f1b87d..180690249 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -14,7 +14,8 @@ Sidekiq.configure_server do |config| schedule_file = "config/schedule.yml" if File.exists?(schedule_file) - Sidekiq::Cron::Job.load_from_hash YAML.load_file(schedule_file) + rendered_schedule_file = ERB.new(File.read(schedule_file)).result + Sidekiq::Cron::Job.load_from_hash YAML.load(rendered_schedule_file) end end diff --git a/config/schedule.yml b/config/schedule.yml index 09e819987..fdc5438fe 100644 --- a/config/schedule.yml +++ b/config/schedule.yml @@ -14,3 +14,5 @@ generate_statistic: cron: "0 1 * * *" class: "StatisticWorker" queue: default + +<%= PluginRegistry.insert_code('yml.schedule') %> diff --git a/config/secrets.yml b/config/secrets.yml index df471202e..4425cc5fc 100644 --- a/config/secrets.yml +++ b/config/secrets.yml @@ -31,6 +31,8 @@ development: openlab_app_secret: <%= ENV["OPENLAB_APP_SECRET"] %> openlab_app_id: <%= ENV["OPENLAB_APP_ID"] %> openlab_base_uri: <%= ENV["OPENLAB_BASE_URI"] %> + navinum_api_login: <%= ENV["NAVINUM_API_LOGIN"] %> + navinum_api_password: <%= ENV["NAVINUM_API_PASSWORD"] %> test: secret_key_base: 83daf5e7b80d990f037407bab78dff9904aaf3c195a50f84fa8695a22287e707dfbd9524b403b1dcf116ae1d8c06844c3d7ed942564e5b46be6ae3ead93a9d30 @@ -53,6 +55,8 @@ test: openlab_app_secret: openlab_app_id: openlab_base_uri: + navinum_api_login: + navinum_api_password: staging: secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> @@ -82,6 +86,8 @@ staging: openlab_app_secret: <%= ENV["OPENLAB_APP_SECRET"] %> openlab_app_id: <%= ENV["OPENLAB_APP_ID"] %> openlab_base_uri: <%= ENV["OPENLAB_BASE_URI"] %> + navinum_api_login: <%= ENV["NAVINUM_API_LOGIN"] %> + navinum_api_password: <%= ENV["NAVINUM_API_PASSWORD"] %> # Do not keep production secrets in the repository, # instead read values from the environment. @@ -114,3 +120,5 @@ production: openlab_app_id: <%= ENV["OPENLAB_APP_ID"] %> openlab_base_uri: <%= ENV["OPENLAB_BASE_URI"] %> google_analytics_id: <%= ENV["GA_ID"] %> + navinum_api_login: <%= ENV["NAVINUM_API_LOGIN"] %> + navinum_api_password: <%= ENV["NAVINUM_API_PASSWORD"] %> diff --git a/db/schema.rb b/db/schema.rb index 70830f169..470636d5b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160613093842) do +ActiveRecord::Schema.define(version: 20160526102307) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -269,6 +269,24 @@ ActiveRecord::Schema.define(version: 20160613093842) do add_index "offer_days", ["subscription_id"], name: "index_offer_days_on_subscription_id", using: :btree + create_table "open_api_calls_count_tracings", force: :cascade do |t| + t.integer "open_api_client_id" + t.integer "calls_count", null: false + t.datetime "at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "open_api_calls_count_tracings", ["open_api_client_id"], name: "index_open_api_calls_count_tracings_on_open_api_client_id", using: :btree + + create_table "open_api_clients", force: :cascade do |t| + t.string "name" + t.integer "calls_count", default: 0 + t.string "token" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "plans", force: :cascade do |t| t.string "name", limit: 255 t.integer "amount" @@ -368,7 +386,7 @@ ActiveRecord::Schema.define(version: 20160613093842) do t.datetime "published_at" end - add_index "projects", ["slug"], name: "index_projects_on_slug", unique: true, using: :btree + add_index "projects", ["slug"], name: "index_projects_on_slug", using: :btree create_table "projects_components", force: :cascade do |t| t.integer "project_id" @@ -661,6 +679,7 @@ ActiveRecord::Schema.define(version: 20160613093842) do add_foreign_key "availability_tags", "availabilities" add_foreign_key "availability_tags", "tags" add_foreign_key "o_auth2_mappings", "o_auth2_providers" + add_foreign_key "open_api_calls_count_tracings", "open_api_clients" add_foreign_key "prices", "groups" add_foreign_key "prices", "plans" add_foreign_key "user_tags", "tags" diff --git a/docker/env.example b/docker/env.example index c4af63ab9..f81ef266d 100644 --- a/docker/env.example +++ b/docker/env.example @@ -47,3 +47,7 @@ UIB_DATE_FORMAT=dd/MM/yyyy OPENLAB_APP_SECRET=fSF9jZEWxjHyqjAzzg34jd92 OPENLAB_APP_ID=xLn9CmryyURNNHZiDRYVRXbv + + +NAVINUM_API_LOGIN: +NAVINUM_API_PASSWORD: diff --git a/lib/fab_manager.rb b/lib/fab_manager.rb new file mode 100644 index 000000000..96370fbea --- /dev/null +++ b/lib/fab_manager.rb @@ -0,0 +1,17 @@ +require_dependency 'plugin/instance' + +module FabManager + class << self + attr_reader :plugins + end + + def self.activate_plugins! + all_plugins = Plugin::Instance.find_all("#{Rails.root}/plugins") + + @plugins = [] + all_plugins.each do |p| + p.activate! + @plugins << p + end + end +end diff --git a/lib/plugin/instance.rb b/lib/plugin/instance.rb new file mode 100644 index 000000000..965e16028 --- /dev/null +++ b/lib/plugin/instance.rb @@ -0,0 +1,98 @@ +require 'fileutils' +require 'plugin_registry' + +module Plugin + class Instance + attr_accessor :path#, :directory + + [:assets, :initializers, :javascripts, :styles].each do |att| + class_eval %Q{ + def #{att} + @#{att} ||= [] + end + } + end + + def self.find_all(parent_path) + [].tap { |plugins| + # also follows symlinks - http://stackoverflow.com/q/357754 + Dir["#{parent_path}/**/*/**/plugin.rb"].sort.each do |path| + + source = File.read(path) + # metadata = Plugin::Metadata.parse(source) + plugins << self.new(nil, path) + end + } + end + + def initialize(metadata=nil, path=nil) + @metadata = metadata + @path = path + #@directory = path.match(/(.*)\/plugin.rb/)[1] + @idx = 0 + end + + def activate! + if @path + root_path = "#{File.dirname(@path)}/assets/javascripts" + PluginRegistry.register_glob(root_path, 'coffee.erb') + end + + self.instance_eval File.read(path), path # execute all code of the plugin main file ! (named plugin.rb) + + register_assets! unless assets.blank? + + Rails.configuration.assets.paths << File.dirname(path) + "/assets" + + Rails.configuration.assets.precompile += [lambda do |filename, path| + (Dir["plugins/*/assets/templates"].any? { |p| path.include?(p) }) # useless because already included in application.css/js || (%w(.js).include?(File.extname(filename)) && Dir["plugins/*/assets/javascripts"].any? { |p| path.include?(p) }) || (%w(.css).include?(File.extname(filename)) && Dir["plugins/*/assets/stylesheets"].any? { |p| path.include?(p) }) + end] # + + Rails.configuration.sass.load_paths += Dir["plugins/*/assets/stylesheets"] + + + # Automatically include rake tasks + Rake.add_rakelib(File.dirname(path) + "/lib/tasks") + + # Automatically include migrations + Rails.configuration.paths["db/migrate"] << File.dirname(path) + "/db/migrate" + end + + def register_asset(file, opts=nil) # to be used by the plugin ! + full_path = File.dirname(path) << "/assets/" << file + assets << [full_path, opts] + end + + def register_code_insertion(key, code) + PluginRegistry.code_insertions[key] ||= [] + PluginRegistry.code_insertions[key] << code + end + + def register_css(style) # useless ? + styles << style + end + + def after_initialize(&block) + initializers << block + end + + def notify_after_initialize + initializers.each do |callback| + begin + callback.call(self) + rescue ActiveRecord::StatementInvalid => e + # When running db:migrate for the first time on a new database, plugin initializers might + # try to use models. Tolerate it. + raise e unless e.message.try(:include?, "PG::UndefinedTable") + end + end + end + + protected + def register_assets! + assets.each do |asset, opts| + PluginRegistry.register_asset(asset, opts) + end + end + end +end diff --git a/lib/plugin_registry.rb b/lib/plugin_registry.rb new file mode 100644 index 000000000..58495ae46 --- /dev/null +++ b/lib/plugin_registry.rb @@ -0,0 +1,58 @@ +class PluginRegistry + class << self + attr_writer :javascripts + attr_writer :stylesheets + + def asset_globs + @asset_globs ||= Set.new + end + + def javascripts + @javascripts ||= Set.new + end + + def stylesheets + @stylesheets ||= Set.new + end + + def code_insertions + @code_insertions ||= {} + end + end + + def self.register_glob(root, extension, options=nil) + self.asset_globs << [root, extension, options || {}] + end + + def self.register_asset(asset, opts=nil) + if asset =~ /\.js$|\.js\.erb$|\.js\.es6$|\.coffee$|\.coffee\.erb/ + # if opts == :admin + # self.admin_javascripts << asset + # else + # if opts == :server_side + # self.server_side_javascripts << asset + # end + self.javascripts << asset + # end + elsif asset =~ /\.css$|\.scss$/ + # if opts == :mobile + # self.mobile_stylesheets << asset + # elsif opts == :desktop + # self.desktop_stylesheets << asset + # elsif opts == :variables + # self.sass_variables << asset + # else + self.stylesheets << asset + # end + + # elsif asset =~ /\.hbs$/ + # self.handlebars << asset + # elsif asset =~ /\.js\.handlebars$/ + # self.handlebars << asset + end + + def self.insert_code(key) + self.code_insertions[key].join('\n') + end + end +end diff --git a/plugins/.keep b/plugins/.keep new file mode 100644 index 000000000..e69de29bb